<commit_before>define('TimelineController', [
    'timelineService',
    'audioConfig',
    'AudioController'
], function(timelineService, audioConfig, AudioController) {
    'use strict';

    var $timeline = document.querySelector('#animalBand .timeline'),
        isPlaying = false;

    return {
        init: init,
        renderSequencer: renderSequencer,
        isPlaying: isPlaying
    };

    function init() {
        renderSequencer();
        togglePlaying();
        toggleActiveNotes();
        AudioController.init();
        updateGainValue();
        highlightRhytm();
    }

    function renderSequencer() {
        var $sequencer = $timeline.querySelector('.sequencer'),
            matrix = audioConfig.matrix;

            //example1
            matrix.entities.basskick=[true,false,true,false,true,false,true,false];
            matrix.entities.cowbell=[false,true,false,true,false,true,false,true];
            matrix.entities.hihat=[true,true,true,true,false,true,true,true];
            matrix.entities.snare=[false,true,false,true,false,true,false,true];
            matrix.entities.tom1=[false,true,false,false,false,true,false,false];
            matrix.entities.tom2=[true,true,false,true,false,true,false,true];

        matrix.result.map(function(instrument, index) {
            var $instrument = document.createElement('div');
            $instrument.className = 'instrument';
            $instrument.setAttribute('data-id', instrument);

            matrix.entities[instrument].map(function(note, index) {
                var $note = document.createElement('div'),
                    $noteSymbol = document.createElement('div');

                if(note){
                    $noteSymbol.className = 'note__symbol active';
                }
                else
                    $noteSymbol.className = 'note__symbol';

                $note.setAttribute('ondragstart','return false;');
                $noteSymbol.setAttribute('data-index', index);
                $noteSymbol.setAttribute('data-value', note);
                $note.className = 'note';
                $note.appendChild($noteSymbol);
                $instrument.appendChild($note);
            });
            $sequencer.appendChild($instrument);
        });
    }

    function togglePlaying() {
        $timeline.querySelector('.btn--play')
            .addEventListener('click', function() {
                isPlaying = !isPlaying;
                if(isPlaying) {
                    this.querySelector('.icon')
                        .classList.remove('icon--play');
                    this.querySelector('.icon')
                        .classList.add('icon--stop');
                    AudioController.startPlaying();
                } else {
                    this.querySelector('.icon')
                        .classList.remove('icon--stop');
                    this.querySelector('.icon')
                        .classList.add('icon--play');
                    AudioController.stopPlaying();
                }
            });
    }

    function toggleActiveNotes() {
        var $noteSymbols = $timeline.querySelectorAll('.note__symbol'),
        i;
        var target = null;
        $noteSymbols
        for(i = 0; i < $noteSymbols.length; i++) {
            $noteSymbols[i]
                .addEventListener(
                    'mouseover',
                    function(e) {
                        if(e.which==1) {
                        timelineService.toggleActiveNote.call(
                            this,
                            audioConfig.matrix
                        );
                        }
                    }
                );
            //take care of touch screens
             $timeline
                .addEventListener(
                    'touchmove',
                    function(e) {
                        var newTarget = document.elementFromPoint(e.touches[0].pageX, e.touches[0].pageY);
                        if(newTarget!=target && newTarget.classList.contains('note__symbol'))
                        {
                            target = newTarget;
                            timelineService.toggleActiveNote.call(
                            newTarget,
                            audioConfig.matrix
                            );
                        }
                    }
                );

            $noteSymbols[i]
                .addEventListener(
                    'mousedown',
                    function() {

                        timelineService.toggleActiveNote.call(
                            this,
                            audioConfig.matrix
                        );

                    }
                );
        }
    }

    function updateGainValue() {
        $timeline.querySelector('.timeline .gain')
            .addEventListener('input', function() {
                AudioController.setGainValue(this.value/100);
            });
    }

    function highlightRhytm() {
        document.querySelector('.timeline')
            .addEventListener('animalBand.audio.rhythmIndex', function(e) {
                audioConfig.matrix.result.map(function(instrument, index) {
                    var rhythmIndex = e.detail.rhythmIndex;
                    if(audioConfig.matrix.entities[instrument][rhythmIndex]) {
                        timelineService.highlightNote(
                            instrument,
                            rhythmIndex,
                            60000 / audioConfig.tempo
                        );

                    }
                });
            });
    }

});
<commit_msg>Updated toggleActiveNotes to bigger container<commit_after>define('TimelineController', [
    'timelineService',
    'audioConfig',
    'AudioController'
], function(timelineService, audioConfig, AudioController) {
    'use strict';

    var $timeline = document.querySelector('#animalBand .timeline'),
        isPlaying = false;

    return {
        init: init,
        renderSequencer: renderSequencer,
        isPlaying: isPlaying
    };

    function init() {
        renderSequencer();
        togglePlaying();
        toggleActiveNotes();
        AudioController.init();
        updateGainValue();
        highlightRhytm();
    }

    function renderSequencer() {
        var $sequencer = $timeline.querySelector('.sequencer'),
            matrix = audioConfig.matrix;

            //example1
            matrix.entities.basskick=[true,false,true,false,true,false,true,false];
            matrix.entities.cowbell=[false,true,false,true,false,true,false,true];
            matrix.entities.hihat=[true,true,true,true,false,true,true,true];
            matrix.entities.snare=[false,true,false,true,false,true,false,true];
            matrix.entities.tom1=[false,true,false,false,false,true,false,false];
            matrix.entities.tom2=[true,true,false,true,false,true,false,true];

        matrix.result.map(function(instrument, index) {
            var $instrument = document.createElement('div');
            $instrument.className = 'instrument';
            $instrument.setAttribute('data-id', instrument);

            matrix.entities[instrument].map(function(note, index) {
                var $note = document.createElement('div'),
                    $noteSymbol = document.createElement('div');

                if(note){
                    $noteSymbol.className = 'note__symbol active';
                }
                else
                    $noteSymbol.className = 'note__symbol';

                $note.setAttribute('ondragstart','return false;');
                $noteSymbol.setAttribute('data-index', index);
                $noteSymbol.setAttribute('data-value', note);
                $note.className = 'note';
                $note.appendChild($noteSymbol);
                $instrument.appendChild($note);
            });
            $sequencer.appendChild($instrument);
        });
    }

    function togglePlaying() {
        $timeline.querySelector('.btn--play')
            .addEventListener('click', function() {
                isPlaying = !isPlaying;
                if(isPlaying) {
                    this.querySelector('.icon')
                        .classList.remove('icon--play');
                    this.querySelector('.icon')
                        .classList.add('icon--stop');
                    AudioController.startPlaying();
                } else {
                    this.querySelector('.icon')
                        .classList.remove('icon--stop');
                    this.querySelector('.icon')
                        .classList.add('icon--play');
                    AudioController.stopPlaying();
                }
            });
    }

    function toggleActiveNotes() {
        var $noteSymbols = $timeline.querySelectorAll('.note__symbol'),
            $notes = $timeline.querySelectorAll('.note'),
        i;
        var target = null;
        $noteSymbols
        for(i = 0; i < $noteSymbols.length; i++) {
            $notes[i]
                .addEventListener(
                    'mouseover',
                    function(e) {
                        if(e.which==1) {
                        timelineService.toggleActiveNote.call(
                            this.children[0],
                            audioConfig.matrix
                        );
                        }
                    }
                );
            //take care of touch screens
             $timeline
                .addEventListener(
                    'touchmove',
                    function(e) {
                        var newTarget = document.elementFromPoint(e.touches[0].pageX, e.touches[0].pageY);
                        if(newTarget!=target && newTarget.classList.contains('note'))
                        {
                            target = newTarget;
                            timelineService.toggleActiveNote.call(
                            newTarget.children[0],
                            audioConfig.matrix
                            );
                        }
                    }
                );

            $notes[i]
                .addEventListener(
                    'mousedown',
                    function() {

                        timelineService.toggleActiveNote.call(
                            this.children[0],
                            audioConfig.matrix
                        );

                    }
                );
        }
    }

    function updateGainValue() {
        $timeline.querySelector('.timeline .gain')
            .addEventListener('input', function() {
                AudioController.setGainValue(this.value/100);
            });
    }

    function highlightRhytm() {
        document.querySelector('.timeline')
            .addEventListener('animalBand.audio.rhythmIndex', function(e) {
                audioConfig.matrix.result.map(function(instrument, index) {
                    var rhythmIndex = e.detail.rhythmIndex;
                    if(audioConfig.matrix.entities[instrument][rhythmIndex]) {
                        timelineService.highlightNote(
                            instrument,
                            rhythmIndex,
                            60000 / audioConfig.tempo
                        );

                    }
                });
            });
    }

});
<|endoftext|>


<commit_before>/*
Copyright 2018 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

import MImageBody from './MImageBody';
import sdk from '../../../index';
import TintableSVG from '../elements/TintableSvg';

export default class MStickerBody extends MImageBody {
    displayName: 'MStickerBody'

    constructor(props) {
      super(props);

      this._onMouseEnter = this._onMouseEnter.bind(this);
      this._onMouseLeave = this._onMouseLeave.bind(this);
      this._onImageLoad = this._onImageLoad.bind(this);
    }

    _onMouseEnter() {
        this.setState({showTooltip: true});
    }

    _onMouseLeave() {
        this.setState({showTooltip: false});
    }

    _onImageLoad() {
        this.fixupHeight();
        this.setState({
            placeholderClasses: 'mx_MStickerBody_placeholder_invisible',
        });
        const hidePlaceholderTimer = setTimeout(() => {
            this.setState({
                placeholderVisible: false,
                imageClasses: 'mx_MStickerBody_image_visible',
            });
        }, 500);
        this.setState({hidePlaceholderTimer});
        if (this.props.onWidgetLoad) {
            this.props.onWidgetLoad();
        }
    }

    _afterComponentDidMount() {
        if (this.refs.image.complete) {
            // Image already loaded
            this.setState({
                placeholderVisible: false,
                placeholderClasses: '.mx_MStickerBody_placeholder_invisible',
                imageClasses: 'mx_MStickerBody_image_visible',
            });
        } else {
            // Image not already loaded
            this.setState({
                placeholderVisible: true,
                placeholderClasses: '',
                imageClasses: '',
            });
        }
    }

    _afterComponentWillUnmount() {
        if (this.state.hidePlaceholderTimer) {
            clearTimeout(this.state.hidePlaceholderTimer);
            this.setState({hidePlaceholderTimer: null});
        }
    }

    _messageContent(contentUrl, thumbUrl, content) {
        let tooltip;
        const tooltipBody = (
            this.props.mxEvent &&
            this.props.mxEvent.getContent() &&
            this.props.mxEvent.getContent().body) ?
            this.props.mxEvent.getContent().body : null;
        if (this.state.showTooltip && tooltipBody) {
            const RoomTooltip = sdk.getComponent('rooms.RoomTooltip');
            tooltip = <RoomTooltip
                className='mx_RoleButton_tooltip'
                label={tooltipBody} />;
        }

        const gutterSize = 0;
        let placeholderSize = 75;
        let placeholderFixupHeight = '100px';
        let placeholderTop = 0;
        let placeholderLeft = 0;

        if (content.info) {
            placeholderTop = Math.floor((content.info.h/2) - (placeholderSize/2)) + 'px';
            placeholderLeft = Math.floor((content.info.w/2) - (placeholderSize/2) + gutterSize) + 'px';
            placeholderFixupHeight = content.info.h + 'px';
        }

        // The pixel size of sticker images is generally larger than their intended display
        // size so they render at native reolution on HiDPI displays. We therefore need to
        // explicity set the size so they render at the intended size.
        // XXX: This will be clobberred when we run fixupHeight(), but we need to do it
        // here otherwise the stickers are momentarily displayed at the pixel size.
        const imageStyle = {
            height: content.info.h,
            // leave the browser the calculate the width automatically
        };

        placeholderSize = placeholderSize + 'px';

        // Body 'ref' required by MImageBody
        return (

                  { this.state.placeholderVisible &&

                        <TintableSVG
                            src={'img/icons-show-stickers.svg'}
                            width={placeholderSize}
                            height={placeholderSize} />

 }

                    { tooltip }

        );
    }

    // Empty to prevent default behaviour of MImageBody
    onClick() {
    }
}
<commit_msg>Remove fixupHeight call from MStickerBody<commit_after>/*
Copyright 2018 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

import MImageBody from './MImageBody';
import sdk from '../../../index';
import TintableSVG from '../elements/TintableSvg';

export default class MStickerBody extends MImageBody {
    displayName: 'MStickerBody'

    constructor(props) {
      super(props);

      this._onMouseEnter = this._onMouseEnter.bind(this);
      this._onMouseLeave = this._onMouseLeave.bind(this);
      this._onImageLoad = this._onImageLoad.bind(this);
    }

    _onMouseEnter() {
        this.setState({showTooltip: true});
    }

    _onMouseLeave() {
        this.setState({showTooltip: false});
    }

    _onImageLoad() {
        this.setState({
            placeholderClasses: 'mx_MStickerBody_placeholder_invisible',
        });
        const hidePlaceholderTimer = setTimeout(() => {
            this.setState({
                placeholderVisible: false,
                imageClasses: 'mx_MStickerBody_image_visible',
            });
        }, 500);
        this.setState({hidePlaceholderTimer});
        if (this.props.onWidgetLoad) {
            this.props.onWidgetLoad();
        }
    }

    _afterComponentDidMount() {
        if (this.refs.image.complete) {
            // Image already loaded
            this.setState({
                placeholderVisible: false,
                placeholderClasses: '.mx_MStickerBody_placeholder_invisible',
                imageClasses: 'mx_MStickerBody_image_visible',
            });
        } else {
            // Image not already loaded
            this.setState({
                placeholderVisible: true,
                placeholderClasses: '',
                imageClasses: '',
            });
        }
    }

    _afterComponentWillUnmount() {
        if (this.state.hidePlaceholderTimer) {
            clearTimeout(this.state.hidePlaceholderTimer);
            this.setState({hidePlaceholderTimer: null});
        }
    }

    _messageContent(contentUrl, thumbUrl, content) {
        let tooltip;
        const tooltipBody = (
            this.props.mxEvent &&
            this.props.mxEvent.getContent() &&
            this.props.mxEvent.getContent().body) ?
            this.props.mxEvent.getContent().body : null;
        if (this.state.showTooltip && tooltipBody) {
            const RoomTooltip = sdk.getComponent('rooms.RoomTooltip');
            tooltip = <RoomTooltip
                className='mx_RoleButton_tooltip'
                label={tooltipBody} />;
        }

        const gutterSize = 0;
        let placeholderSize = 75;
        let placeholderFixupHeight = '100px';
        let placeholderTop = 0;
        let placeholderLeft = 0;

        if (content.info) {
            placeholderTop = Math.floor((content.info.h/2) - (placeholderSize/2)) + 'px';
            placeholderLeft = Math.floor((content.info.w/2) - (placeholderSize/2) + gutterSize) + 'px';
            placeholderFixupHeight = content.info.h + 'px';
        }

        // The pixel size of sticker images is generally larger than their intended display
        // size so they render at native reolution on HiDPI displays. We therefore need to
        // explicity set the size so they render at the intended size.
        const imageStyle = {
            height: content.info.h,
            // leave the browser the calculate the width automatically
        };

        placeholderSize = placeholderSize + 'px';

        // Body 'ref' required by MImageBody
        return (

                  { this.state.placeholderVisible &&

                        <TintableSVG
                            src={'img/icons-show-stickers.svg'}
                            width={placeholderSize}
                            height={placeholderSize} />

 }

                    { tooltip }

        );
    }

    // Empty to prevent default behaviour of MImageBody
    onClick() {
    }
}
<|endoftext|>


<commit_before>/*
Copyright 2018 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import MImageBody from './MImageBody';
import * as sdk from '../../../index';
import {replaceableComponent} from "../../../utils/replaceableComponent";

@replaceableComponent("views.messages.MStickerBody")
export default class MStickerBody extends MImageBody {
    // Mostly empty to prevent default behaviour of MImageBody
    onClick(ev) {
        ev.preventDefault();
        if (!this.state.showImage) {
            this.showImage();
        }
    }

    // MStickerBody doesn't need a wrapping `<a href=...>`, but it does need extra padding
    // which is added by mx_MStickerBody_wrapper
    wrapImage(contentUrl, children) {
        let onClick = null;
        if (!this.state.showImage) {
            onClick = this.onClick;
        }
        return
 { children }
;
    }

    // Placeholder to show in place of the sticker image if
    // img onLoad hasn't fired yet.
    getPlaceholder() {
        const TintableSVG = sdk.getComponent('elements.TintableSvg');
        return <TintableSVG src={require("../../../../res/img/icons-show-stickers.svg")} width="75" height="75" />;
    }

    // Tooltip to show on mouse over
    getTooltip() {
        const content = this.props.mxEvent && this.props.mxEvent.getContent();

        if (!content || !content.body || !content.info || !content.info.w) return null;

        const Tooltip = sdk.getComponent('elements.Tooltip');
        return

            <Tooltip label={content.body} />

;
    }

    // Don't show "Download this_file.png ..."
    getFileBody() {
        return null;
    }
}
<commit_msg>Remove Tinter reference<commit_after>/*
Copyright 2018 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import MImageBody from './MImageBody';
import * as sdk from '../../../index';
import {replaceableComponent} from "../../../utils/replaceableComponent";

@replaceableComponent("views.messages.MStickerBody")
export default class MStickerBody extends MImageBody {
    // Mostly empty to prevent default behaviour of MImageBody
    onClick(ev) {
        ev.preventDefault();
        if (!this.state.showImage) {
            this.showImage();
        }
    }

    // MStickerBody doesn't need a wrapping `<a href=...>`, but it does need extra padding
    // which is added by mx_MStickerBody_wrapper
    wrapImage(contentUrl, children) {
        let onClick = null;
        if (!this.state.showImage) {
            onClick = this.onClick;
        }
        return
 { children }
;
    }

    // Placeholder to show in place of the sticker image if
    // img onLoad hasn't fired yet.
    getPlaceholder() {
        return ;
    }

    // Tooltip to show on mouse over
    getTooltip() {
        const content = this.props.mxEvent && this.props.mxEvent.getContent();

        if (!content || !content.body || !content.info || !content.info.w) return null;

        const Tooltip = sdk.getComponent('elements.Tooltip');
        return

            <Tooltip label={content.body} />

;
    }

    // Don't show "Download this_file.png ..."
    getFileBody() {
        return null;
    }
}
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

var React = require('react');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var sdk = require('../../../index');

module.exports = React.createClass({
    displayName: 'ChangeAvatar',
    propTypes: {
        initialAvatarUrl: React.PropTypes.string,
        room: React.PropTypes.object,
        // if false, you need to call changeAvatar.onFileSelected yourself.
        showUploadSection: React.PropTypes.bool,
        width: React.PropTypes.number,
        height: React.PropTypes.number,
        className: React.PropTypes.string
    },

    Phases: {
        Display: "display",
        Uploading: "uploading",
        Error: "error",
    },

    getDefaultProps: function() {
        return {
            showUploadSection: true,
            className: "",
            width: 80,
            height: 80,
        };
    },

    getInitialState: function() {
        return {
            avatarUrl: this.props.initialAvatarUrl,
            phase: this.Phases.Display,
        }
    },

    componentWillReceiveProps: function(newProps) {
        if (this.avatarSet) {
            // don't clobber what the user has just set
            return;
        }
        this.setState({
            avatarUrl: newProps.initialAvatarUrl
        });
    },

    setAvatarFromFile: function(file) {
        var newUrl = null;

        this.setState({
            phase: this.Phases.Uploading
        });
        var self = this;
        var httpPromise = MatrixClientPeg.get().uploadContent(file).then(function(url) {
            newUrl = url;
            if (self.props.room) {
                return MatrixClientPeg.get().sendStateEvent(
                    self.props.room.roomId,
                    'm.room.avatar',
                    {url: url},
                    ''
                );
            } else {
                return MatrixClientPeg.get().setAvatarUrl(url);
            }
        });

        httpPromise.done(function() {
            self.setState({
                phase: self.Phases.Display,
                avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(newUrl)
            });
        }, function(error) {
            self.setState({
                phase: self.Phases.Error
            });
            self.onError(error);
        });

        return httpPromise;
    },

    onFileSelected: function(ev) {
        this.avatarSet = true;
        return this.setAvatarFromFile(ev.target.files[0]);
    },

    onError: function(error) {
        this.setState({
            errorText: "Failed to upload profile picture!"
        });
    },

    render: function() {
        var RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
        var avatarImg;
        // Having just set an avatar we just display that since it will take a little
        // time to propagate through to the RoomAvatar.
        if (this.props.room && !this.avatarSet) {
            avatarImg = <RoomAvatar room={this.props.room} width={ this.props.width } height={ this.props.height } resizeMethod='crop' />;
        } else {
            var style = {
                width: this.props.width,
                height: this.props.height,
                objectFit: 'cover',
            };
            avatarImg = ;
        }

        var uploadSection;
        if (this.props.showUploadSection) {
            uploadSection = (

                    Upload new:
                    <input type="file" onChange={this.onFileSelected}/>
                    {this.state.errorText}

            );
        }

        switch (this.state.phase) {
            case this.Phases.Display:
            case this.Phases.Error:
                return (

                            {avatarImg}

                        {uploadSection}

                );
            case this.Phases.Uploading:
                var Loader = sdk.getComponent("elements.Spinner");
                return (
                    <Loader />
                );
        }
    }
});
<commit_msg>oops, use right class<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

var React = require('react');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var sdk = require('../../../index');

module.exports = React.createClass({
    displayName: 'ChangeAvatar',
    propTypes: {
        initialAvatarUrl: React.PropTypes.string,
        room: React.PropTypes.object,
        // if false, you need to call changeAvatar.onFileSelected yourself.
        showUploadSection: React.PropTypes.bool,
        width: React.PropTypes.number,
        height: React.PropTypes.number,
        className: React.PropTypes.string
    },

    Phases: {
        Display: "display",
        Uploading: "uploading",
        Error: "error",
    },

    getDefaultProps: function() {
        return {
            showUploadSection: true,
            className: "",
            width: 80,
            height: 80,
        };
    },

    getInitialState: function() {
        return {
            avatarUrl: this.props.initialAvatarUrl,
            phase: this.Phases.Display,
        }
    },

    componentWillReceiveProps: function(newProps) {
        if (this.avatarSet) {
            // don't clobber what the user has just set
            return;
        }
        this.setState({
            avatarUrl: newProps.initialAvatarUrl
        });
    },

    setAvatarFromFile: function(file) {
        var newUrl = null;

        this.setState({
            phase: this.Phases.Uploading
        });
        var self = this;
        var httpPromise = MatrixClientPeg.get().uploadContent(file).then(function(url) {
            newUrl = url;
            if (self.props.room) {
                return MatrixClientPeg.get().sendStateEvent(
                    self.props.room.roomId,
                    'm.room.avatar',
                    {url: url},
                    ''
                );
            } else {
                return MatrixClientPeg.get().setAvatarUrl(url);
            }
        });

        httpPromise.done(function() {
            self.setState({
                phase: self.Phases.Display,
                avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(newUrl)
            });
        }, function(error) {
            self.setState({
                phase: self.Phases.Error
            });
            self.onError(error);
        });

        return httpPromise;
    },

    onFileSelected: function(ev) {
        this.avatarSet = true;
        return this.setAvatarFromFile(ev.target.files[0]);
    },

    onError: function(error) {
        this.setState({
            errorText: "Failed to upload profile picture!"
        });
    },

    render: function() {
        var RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
        var avatarImg;
        // Having just set an avatar we just display that since it will take a little
        // time to propagate through to the RoomAvatar.
        if (this.props.room && !this.avatarSet) {
            avatarImg = <RoomAvatar room={this.props.room} width={ this.props.width } height={ this.props.height } resizeMethod='crop' />;
        } else {
            var style = {
                width: this.props.width,
                height: this.props.height,
                objectFit: 'cover',
            };
            avatarImg = ;
        }

        var uploadSection;
        if (this.props.showUploadSection) {
            uploadSection = (

                    Upload new:
                    <input type="file" onChange={this.onFileSelected}/>
                    {this.state.errorText}

            );
        }

        switch (this.state.phase) {
            case this.Phases.Display:
            case this.Phases.Error:
                return (

                            {avatarImg}

                        {uploadSection}

                );
            case this.Phases.Uploading:
                var Loader = sdk.getComponent("elements.Spinner");
                return (
                    <Loader />
                );
        }
    }
});
<|endoftext|>


<commit_before>import noop from 'lodash/noop';
import { checkRendering, parseAroundLatLngFromString } from '../../lib/utils';

const usage = `Usage:

var customGeoSearch = connectGeoSearch(function render(params, isFirstRendering) {
  // params = {
  //   items,
  //   position,
  //   refine,
  //   clearMapRefinement,
  //   isRefinedWithMap,
  //   toggleRefineOnMapMove,
  //   isRefineOnMapMove,
  //   setMapMoveSinceLastRefine,
  //   hasMapMoveSinceLastRefine,
  //   hasMapMoveSinceLastRefine,
  //   widgetParams,
  //   instantSearchInstance,
  // }
});

search.addWidget(
  customGeoSearch({
    [ enableRefineOnMapMove = true ],
    [ enableGeolocationWithIP = true ],
    [ position ],
    [ radius ],
    [ precision ],
    [ transformItems ],
  })
);

Full documentation available at https://community.algolia.com/instantsearch.js/v2/connectors/connectGeoSearch.html
`;

/**
 * @typedef {Object} LatLng
 * @property {number} lat The latitude in degrees.
 * @property {number} lng The longitude in degrees.
 */

/**
 * @typedef {Object} Bounds
 * @property {LatLng} northEast The top right corner of the map view.
 * @property {LatLng} southWest The bottom left corner of the map view.
 */

/**
 * @typedef {Object} CustomGeoSearchWidgetOptions
 * @property {boolean} [enableRefineOnMapMove=true] If true, refine will be triggered as you move the map.
 * @property {boolean} [enableGeolocationWithIP=true] If true, the IP will be use for the geolocation. When the `position` option is provided this option will be ignored. See [the documentation](https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLngViaIP) for more information.
 * @property {LatLng} [position] Position that will be use to search around.

 * See [the documentation](https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLng) for more information.
 * @property {number} [radius] Maximum radius to search around the position (in meters).

 * See [the documentation](https://www.algolia.com/doc/api-reference/api-parameters/aroundRadius) for more information.
 * @property {number} [precision] Precision of geo search (in meters).

 * See [the documentation](https://www.algolia.com/doc/api-reference/api-parameters/aroundPrecision) for more information.
 * @property {function(object[]):object[]} [transformItems] Function to transform the items passed to the templates.
 */

/**
 * @typedef {Object} GeoSearchRenderingOptions
 * @property {Object[]} items The matched hits from Algolia API.
 * @property {function(Bounds)} refine Sets a bounding box to filter the results from the given map bounds.
 * @property {function()} clearMapRefinement Reset the current bounding box refinement.
 * @property {function(): boolean} isRefinedWithMap Return true if the current refinement is set with the map bounds.
 * @property {function()} toggleRefineOnMapMove Toggle the fact that the user is able to refine on map move.
 * @property {function(): boolean} isRefineOnMapMove Return true if the user is able to refine on map move.
 * @property {function()} setMapMoveSinceLastRefine Set the fact that the map has moved since the last refinement, should be call on each map move. The call to the function triggers a new rendering only when the value change.
 * @property {function(): boolean} hasMapMoveSinceLastRefine Return true if the map has move since the last refinement.
 * @property {Object} widgetParams All original `CustomGeoSearchWidgetOptions` forwarded to the `renderFn`.
 * @property {LatLng} [position] The current position of the search.
 */

/**
 * The **GeoSearch** connector provides the logic to build a widget that will display the results on a map. It also provides a way to search for results based on their position. The connector provides functions to manage the search experience (search on map interaction or control the interaction for example).
 *
 * @requirements
 *
 * Note that the GeoSearch connector uses the [geosearch](https://www.algolia.com/doc/guides/searching/geo-search) capabilities of Algolia. Your hits **must** have a `_geoloc` attribute in order to be passed to the rendering function.
 *
 * Currently, the feature is not compatible with multiple values in the _geoloc attribute.
 *
 * @type {Connector}
 * @param {function(GeoSearchRenderingOptions, boolean)} renderFn Rendering function for the custom **GeoSearch** widget.
 * @param {function} unmountFn Unmount function called when the widget is disposed.
 * @return {function(CustomGeoSearchWidgetOptions)} Re-usable widget factory for a custom **GeoSearch** widget.
 * @staticExample
 * // This example use Leaflet for the rendering, be sure to have the library correctly setup
 * // before trying the demo. You can find more details in their documentation (link below).
 * // We choose Leaflet for the example but you can use any libraries that you want.
 * // See: http://leafletjs.com/examples/quick-start
 *
 * let map = null;
 * let markers = [];
 *
 * // custom `renderFn` to render the custom GeoSearch widget
 * function renderFn(GeoSearchRenderingOptions, isFirstRendering) {
 *   const { items, widgetParams } = GeoSearchRenderingOptions;
 *
 *   if (isFirstRendering) {
 *     map = L.map(widgetParams.container);
 *
 *     L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
 *       attribution:
 *         '© OpenStreetMap contributors',
 *     }).addTo(map);
 *   }
 *
 *   markers.forEach(marker => marker.remove());
 *
 *   markers = items.map(({ _geoloc }) =>
 *     L.marker([_geoloc.lat, _geoloc.lng]).addTo(map)
 *   );
 *
 *   if (markers.length) {
 *     map.fitBounds(L.featureGroup(markers).getBounds());
 *   }
 * }
 *
 * // connect `renderFn` to GeoSearch logic
 * const customGeoSearch = instantsearch.connectors.connectGeoSearch(renderFn);
 *
 * // mount widget on the page
 * search.addWidget(
 *   customGeoSearch({
 *     container: document.getElementById('custom-geo-search'),
 *   })
 * );
 */
const connectGeoSearch = (renderFn, unmountFn) => {
  checkRendering(renderFn, usage);

  return (widgetParams = {}) => {
    const {
      enableRefineOnMapMove = true,
      enableGeolocationWithIP = true,
      position,
      radius,
      precision,
      transformItems = items => items,
    } = widgetParams;

    const widgetState = {
      isRefineOnMapMove: enableRefineOnMapMove,
      hasMapMoveSinceLastRefine: false,
      lastRefinePosition: '',
      lastRefineBoundingBox: '',
      internalToggleRefineOnMapMove: noop,
      internalSetMapMoveSinceLastRefine: noop,
    };

    const getPositionFromState = state =>
      state.aroundLatLng && parseAroundLatLngFromString(state.aroundLatLng);

    const refine = helper => ({ northEast: ne, southWest: sw }) => {
      const boundingBox = [ne.lat, ne.lng, sw.lat, sw.lng].join();

      helper.setQueryParameter('insideBoundingBox', boundingBox).search();

      widgetState.hasMapMoveSinceLastRefine = false;
      widgetState.lastRefineBoundingBox = boundingBox;
    };

    const clearMapRefinement = helper => () => {
      helper.setQueryParameter('insideBoundingBox').search();
    };

    const isRefinedWithMap = state => () => Boolean(state.insideBoundingBox);

    const toggleRefineOnMapMove = () =>
      widgetState.internalToggleRefineOnMapMove();
    const createInternalToggleRefinementonMapMove = (render, args) => () => {
      widgetState.isRefineOnMapMove = !widgetState.isRefineOnMapMove;

      render(args);
    };

    const isRefineOnMapMove = () => widgetState.isRefineOnMapMove;

    const setMapMoveSinceLastRefine = () =>
      widgetState.internalSetMapMoveSinceLastRefine();
    const createInternalSetMapMoveSinceLastRefine = (render, args) => () => {
      const shouldTriggerRender =
        widgetState.hasMapMoveSinceLastRefine !== true;

      widgetState.hasMapMoveSinceLastRefine = true;

      if (shouldTriggerRender) {
        render(args);
      }
    };

    const hasMapMoveSinceLastRefine = () =>
      widgetState.hasMapMoveSinceLastRefine;

    const init = initArgs => {
      const { state, helper, instantSearchInstance } = initArgs;
      const isFirstRendering = true;

      widgetState.internalToggleRefineOnMapMove = createInternalToggleRefinementonMapMove(
        noop,
        initArgs
      );

      widgetState.internalSetMapMoveSinceLastRefine = createInternalSetMapMoveSinceLastRefine(
        noop,
        initArgs
      );

      renderFn(
        {
          items: [],
          position: getPositionFromState(state),
          refine: refine(helper),
          clearMapRefinement: clearMapRefinement(helper),
          isRefinedWithMap: isRefinedWithMap(state),
          toggleRefineOnMapMove,
          isRefineOnMapMove,
          setMapMoveSinceLastRefine,
          hasMapMoveSinceLastRefine,
          widgetParams,
          instantSearchInstance,
        },
        isFirstRendering
      );
    };

    const render = renderArgs => {
      const { results, helper, instantSearchInstance } = renderArgs;
      const isFirstRendering = false;
      // We don't use the state provided by the render function because we need
      // to be sure that the state is the latest one for the following condition
      const state = helper.getState();

      const positionChangedSinceLastRefine =
        Boolean(state.aroundLatLng) &&
        Boolean(widgetState.lastRefinePosition) &&
        state.aroundLatLng !== widgetState.lastRefinePosition;

      const boundingBoxChangedSinceLastRefine =
        !state.insideBoundingBox &&
        Boolean(widgetState.lastRefineBoundingBox) &&
        state.insideBoundingBox !== widgetState.lastRefineBoundingBox;

      if (positionChangedSinceLastRefine || boundingBoxChangedSinceLastRefine) {
        widgetState.hasMapMoveSinceLastRefine = false;
      }

      widgetState.lastRefinePosition = state.aroundLatLng || '';
      widgetState.lastRefineBoundingBox = state.insideBoundingBox || '';

      widgetState.internalToggleRefineOnMapMove = createInternalToggleRefinementonMapMove(
        render,
        renderArgs
      );

      widgetState.internalSetMapMoveSinceLastRefine = createInternalSetMapMoveSinceLastRefine(
        render,
        renderArgs
      );

      const items = transformItems(results.hits.filter(hit => hit._geoloc));

      renderFn(
        {
          items,
          position: getPositionFromState(state),
          refine: refine(helper),
          clearMapRefinement: clearMapRefinement(helper),
          isRefinedWithMap: isRefinedWithMap(state),
          toggleRefineOnMapMove,
          isRefineOnMapMove,
          setMapMoveSinceLastRefine,
          hasMapMoveSinceLastRefine,
          widgetParams,
          instantSearchInstance,
        },
        isFirstRendering
      );
    };

    return {
      init,
      render,

      getConfiguration(previous) {
        const configuration = {};

        if (
          enableGeolocationWithIP &&
          !position &&
          !previous.aroundLatLng &&
          previous.aroundLatLngViaIP === undefined
        ) {
          configuration.aroundLatLngViaIP = true;
        }

        if (position && !previous.aroundLatLng && !previous.aroundLatLngViaIP) {
          configuration.aroundLatLng = `${position.lat}, ${position.lng}`;
        }

        if (radius && !previous.aroundRadius) {
          configuration.aroundRadius = radius;
        }

        if (precision && !previous.aroundPrecision) {
          configuration.aroundPrecision = precision;
        }

        return configuration;
      },

      dispose({ state }) {
        unmountFn();

        let nextState = state;

        if (enableGeolocationWithIP && !position) {
          nextState = nextState.setQueryParameter('aroundLatLngViaIP');
        }

        if (position) {
          nextState = nextState.setQueryParameter('aroundLatLng');
        }

        if (radius) {
          nextState = nextState.setQueryParameter('aroundRadius');
        }

        if (precision) {
          nextState = nextState.setQueryParameter('aroundPrecision');
        }

        nextState = nextState.setQueryParameter('insideBoundingBox');

        return nextState;
      },
    };
  };
};

export default connectGeoSearch;
<commit_msg>docs(GeoSearch): fix typo in name (#3304)<commit_after>import noop from 'lodash/noop';
import { checkRendering, parseAroundLatLngFromString } from '../../lib/utils';

const usage = `Usage:

var customGeoSearch = connectGeoSearch(function render(params, isFirstRendering) {
  // params = {
  //   items,
  //   position,
  //   refine,
  //   clearMapRefinement,
  //   isRefinedWithMap,
  //   toggleRefineOnMapMove,
  //   isRefineOnMapMove,
  //   setMapMoveSinceLastRefine,
  //   hasMapMoveSinceLastRefine,
  //   hasMapMoveSinceLastRefine,
  //   widgetParams,
  //   instantSearchInstance,
  // }
});

search.addWidget(
  customGeoSearch({
    [ enableRefineOnMapMove = true ],
    [ enableGeolocationWithIP = true ],
    [ position ],
    [ radius ],
    [ precision ],
    [ transformItems ],
  })
);

Full documentation available at https://community.algolia.com/instantsearch.js/v2/connectors/connectGeoSearch.html
`;

/**
 * @typedef {Object} LatLng
 * @property {number} lat The latitude in degrees.
 * @property {number} lng The longitude in degrees.
 */

/**
 * @typedef {Object} Bounds
 * @property {LatLng} northEast The top right corner of the map view.
 * @property {LatLng} southWest The bottom left corner of the map view.
 */

/**
 * @typedef {Object} CustomGeoSearchWidgetOptions
 * @property {boolean} [enableRefineOnMapMove=true] If true, refine will be triggered as you move the map.
 * @property {boolean} [enableGeolocationWithIP=true] If true, the IP will be use for the geolocation. When the `position` option is provided this option will be ignored. See [the documentation](https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLngViaIP) for more information.
 * @property {LatLng} [position] Position that will be use to search around.

 * See [the documentation](https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLng) for more information.
 * @property {number} [radius] Maximum radius to search around the position (in meters).

 * See [the documentation](https://www.algolia.com/doc/api-reference/api-parameters/aroundRadius) for more information.
 * @property {number} [precision] Precision of geo search (in meters).

 * See [the documentation](https://www.algolia.com/doc/api-reference/api-parameters/aroundPrecision) for more information.
 * @property {function(object[]):object[]} [transformItems] Function to transform the items passed to the templates.
 */

/**
 * @typedef {Object} GeoSearchRenderingOptions
 * @property {Object[]} items The matched hits from Algolia API.
 * @property {function(Bounds)} refine Sets a bounding box to filter the results from the given map bounds.
 * @property {function()} clearMapRefinement Reset the current bounding box refinement.
 * @property {function(): boolean} isRefinedWithMap Return true if the current refinement is set with the map bounds.
 * @property {function()} toggleRefineOnMapMove Toggle the fact that the user is able to refine on map move.
 * @property {function(): boolean} isRefineOnMapMove Return true if the user is able to refine on map move.
 * @property {function()} setMapMoveSinceLastRefine Set the fact that the map has moved since the last refinement, should be call on each map move. The call to the function triggers a new rendering only when the value change.
 * @property {function(): boolean} hasMapMoveSinceLastRefine Return true if the map has move since the last refinement.
 * @property {Object} widgetParams All original `CustomGeoSearchWidgetOptions` forwarded to the `renderFn`.
 * @property {LatLng} [position] The current position of the search.
 */

/**
 * The **GeoSearch** connector provides the logic to build a widget that will display the results on a map. It also provides a way to search for results based on their position. The connector provides functions to manage the search experience (search on map interaction or control the interaction for example).
 *
 * @requirements
 *
 * Note that the GeoSearch connector uses the [geosearch](https://www.algolia.com/doc/guides/searching/geo-search) capabilities of Algolia. Your hits **must** have a `_geoloc` attribute in order to be passed to the rendering function.
 *
 * Currently, the feature is not compatible with multiple values in the _geoloc attribute.
 *
 * @type {Connector}
 * @param {function(GeoSearchRenderingOptions, boolean)} renderFn Rendering function for the custom **GeoSearch** widget.
 * @param {function} unmountFn Unmount function called when the widget is disposed.
 * @return {function(CustomGeoSearchWidgetOptions)} Re-usable widget factory for a custom **GeoSearch** widget.
 * @staticExample
 * // This example use Leaflet for the rendering, be sure to have the library correctly setup
 * // before trying the demo. You can find more details in their documentation (link below).
 * // We choose Leaflet for the example but you can use any libraries that you want.
 * // See: http://leafletjs.com/examples/quick-start
 *
 * let map = null;
 * let markers = [];
 *
 * // custom `renderFn` to render the custom GeoSearch widget
 * function renderFn(GeoSearchRenderingOptions, isFirstRendering) {
 *   const { items, widgetParams } = GeoSearchRenderingOptions;
 *
 *   if (isFirstRendering) {
 *     map = L.map(widgetParams.container);
 *
 *     L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
 *       attribution:
 *         '© OpenStreetMap contributors',
 *     }).addTo(map);
 *   }
 *
 *   markers.forEach(marker => marker.remove());
 *
 *   markers = items.map(({ _geoloc }) =>
 *     L.marker([_geoloc.lat, _geoloc.lng]).addTo(map)
 *   );
 *
 *   if (markers.length) {
 *     map.fitBounds(L.featureGroup(markers).getBounds());
 *   }
 * }
 *
 * // connect `renderFn` to GeoSearch logic
 * const customGeoSearch = instantsearch.connectors.connectGeoSearch(renderFn);
 *
 * // mount widget on the page
 * search.addWidget(
 *   customGeoSearch({
 *     container: document.getElementById('custom-geo-search'),
 *   })
 * );
 */
const connectGeoSearch = (renderFn, unmountFn) => {
  checkRendering(renderFn, usage);

  return (widgetParams = {}) => {
    const {
      enableRefineOnMapMove = true,
      enableGeolocationWithIP = true,
      position,
      radius,
      precision,
      transformItems = items => items,
    } = widgetParams;

    const widgetState = {
      isRefineOnMapMove: enableRefineOnMapMove,
      hasMapMoveSinceLastRefine: false,
      lastRefinePosition: '',
      lastRefineBoundingBox: '',
      internalToggleRefineOnMapMove: noop,
      internalSetMapMoveSinceLastRefine: noop,
    };

    const getPositionFromState = state =>
      state.aroundLatLng && parseAroundLatLngFromString(state.aroundLatLng);

    const refine = helper => ({ northEast: ne, southWest: sw }) => {
      const boundingBox = [ne.lat, ne.lng, sw.lat, sw.lng].join();

      helper.setQueryParameter('insideBoundingBox', boundingBox).search();

      widgetState.hasMapMoveSinceLastRefine = false;
      widgetState.lastRefineBoundingBox = boundingBox;
    };

    const clearMapRefinement = helper => () => {
      helper.setQueryParameter('insideBoundingBox').search();
    };

    const isRefinedWithMap = state => () => Boolean(state.insideBoundingBox);

    const toggleRefineOnMapMove = () =>
      widgetState.internalToggleRefineOnMapMove();
    const createInternalToggleRefinementOnMapMove = (render, args) => () => {
      widgetState.isRefineOnMapMove = !widgetState.isRefineOnMapMove;

      render(args);
    };

    const isRefineOnMapMove = () => widgetState.isRefineOnMapMove;

    const setMapMoveSinceLastRefine = () =>
      widgetState.internalSetMapMoveSinceLastRefine();
    const createInternalSetMapMoveSinceLastRefine = (render, args) => () => {
      const shouldTriggerRender =
        widgetState.hasMapMoveSinceLastRefine !== true;

      widgetState.hasMapMoveSinceLastRefine = true;

      if (shouldTriggerRender) {
        render(args);
      }
    };

    const hasMapMoveSinceLastRefine = () =>
      widgetState.hasMapMoveSinceLastRefine;

    const init = initArgs => {
      const { state, helper, instantSearchInstance } = initArgs;
      const isFirstRendering = true;

      widgetState.internalToggleRefineOnMapMove = createInternalToggleRefinementOnMapMove(
        noop,
        initArgs
      );

      widgetState.internalSetMapMoveSinceLastRefine = createInternalSetMapMoveSinceLastRefine(
        noop,
        initArgs
      );

      renderFn(
        {
          items: [],
          position: getPositionFromState(state),
          refine: refine(helper),
          clearMapRefinement: clearMapRefinement(helper),
          isRefinedWithMap: isRefinedWithMap(state),
          toggleRefineOnMapMove,
          isRefineOnMapMove,
          setMapMoveSinceLastRefine,
          hasMapMoveSinceLastRefine,
          widgetParams,
          instantSearchInstance,
        },
        isFirstRendering
      );
    };

    const render = renderArgs => {
      const { results, helper, instantSearchInstance } = renderArgs;
      const isFirstRendering = false;
      // We don't use the state provided by the render function because we need
      // to be sure that the state is the latest one for the following condition
      const state = helper.getState();

      const positionChangedSinceLastRefine =
        Boolean(state.aroundLatLng) &&
        Boolean(widgetState.lastRefinePosition) &&
        state.aroundLatLng !== widgetState.lastRefinePosition;

      const boundingBoxChangedSinceLastRefine =
        !state.insideBoundingBox &&
        Boolean(widgetState.lastRefineBoundingBox) &&
        state.insideBoundingBox !== widgetState.lastRefineBoundingBox;

      if (positionChangedSinceLastRefine || boundingBoxChangedSinceLastRefine) {
        widgetState.hasMapMoveSinceLastRefine = false;
      }

      widgetState.lastRefinePosition = state.aroundLatLng || '';
      widgetState.lastRefineBoundingBox = state.insideBoundingBox || '';

      widgetState.internalToggleRefineOnMapMove = createInternalToggleRefinementOnMapMove(
        render,
        renderArgs
      );

      widgetState.internalSetMapMoveSinceLastRefine = createInternalSetMapMoveSinceLastRefine(
        render,
        renderArgs
      );

      const items = transformItems(results.hits.filter(hit => hit._geoloc));

      renderFn(
        {
          items,
          position: getPositionFromState(state),
          refine: refine(helper),
          clearMapRefinement: clearMapRefinement(helper),
          isRefinedWithMap: isRefinedWithMap(state),
          toggleRefineOnMapMove,
          isRefineOnMapMove,
          setMapMoveSinceLastRefine,
          hasMapMoveSinceLastRefine,
          widgetParams,
          instantSearchInstance,
        },
        isFirstRendering
      );
    };

    return {
      init,
      render,

      getConfiguration(previous) {
        const configuration = {};

        if (
          enableGeolocationWithIP &&
          !position &&
          !previous.aroundLatLng &&
          previous.aroundLatLngViaIP === undefined
        ) {
          configuration.aroundLatLngViaIP = true;
        }

        if (position && !previous.aroundLatLng && !previous.aroundLatLngViaIP) {
          configuration.aroundLatLng = `${position.lat}, ${position.lng}`;
        }

        if (radius && !previous.aroundRadius) {
          configuration.aroundRadius = radius;
        }

        if (precision && !previous.aroundPrecision) {
          configuration.aroundPrecision = precision;
        }

        return configuration;
      },

      dispose({ state }) {
        unmountFn();

        let nextState = state;

        if (enableGeolocationWithIP && !position) {
          nextState = nextState.setQueryParameter('aroundLatLngViaIP');
        }

        if (position) {
          nextState = nextState.setQueryParameter('aroundLatLng');
        }

        if (radius) {
          nextState = nextState.setQueryParameter('aroundRadius');
        }

        if (precision) {
          nextState = nextState.setQueryParameter('aroundPrecision');
        }

        nextState = nextState.setQueryParameter('insideBoundingBox');

        return nextState;
      },
    };
  };
};

export default connectGeoSearch;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { push } from 'react-router-redux'
import { isEqual, pick } from 'lodash'
import { openAlert, closeAlert } from '../actions/modals'
import { saveAvatar, saveCover } from '../actions/profile'
import {
  selectAvatar,
  selectCoverImage,
  selectIsAvatarBlank,
  selectIsCoverImageBlank,
  selectIsInfoFormBlank,
} from '../selectors/profile'
import OnboardingSettings from '../components/onboarding/OnboardingSettings'

function shouldContainerUpdate(thisProps, nextProps) {
  const pickProps = ['avatar', 'coverImage', 'isNextDisabled']
  const thisCompare = pick(thisProps, pickProps)
  const nextCompare = pick(nextProps, pickProps)
  return !isEqual(thisCompare, nextCompare)
}

function mapStateToProps(state) {
  const avatar = selectAvatar(state)
  const coverImage = selectCoverImage(state)
  const isAvatarBlank = selectIsAvatarBlank(state)
  const isCoverImageBlank = selectIsCoverImageBlank(state)
  const isInfoFormBlank = selectIsInfoFormBlank(state)
  const isNextDisabled = isAvatarBlank && isCoverImageBlank && isInfoFormBlank
  return {
    avatar,
    coverImage,
    isAvatarBlank,
    isCoverImageBlank,
    isNextDisabled,
  }
}

class OnboardingSettingsContainer extends Component {

  static propTypes = {
    avatar: PropTypes.object,
    coverImage: PropTypes.object,
    dispatch: PropTypes.func.isRequired,
    isAvatarBlank: PropTypes.bool.isRequired,
    isCoverImageBlank: PropTypes.bool.isRequired,
    isNextDisabled: PropTypes.bool.isRequired,
  }

  static childContextTypes = {
    avatar: PropTypes.object,
    closeAlert: PropTypes.func,
    coverImage: PropTypes.object,
    isAvatarBlank: PropTypes.bool,
    isCoverImageBlank: PropTypes.bool,
    nextLabel: PropTypes.string,
    onDoneClick: PropTypes.func,
    onNextClick: PropTypes.func,
    openAlert: PropTypes.func,
    saveAvatar: PropTypes.func,
    saveCover: PropTypes.func,
  }

  getChildContext() {
    const {
      avatar, dispatch, coverImage, isAvatarBlank, isCoverImageBlank, isNextDisabled,
    } = this.props
    return {
      avatar,
      closeAlert: bindActionCreators(closeAlert, dispatch),
      coverImage,
      isAvatarBlank,
      isCoverImageBlank,
      nextLabel: 'Invite Cool People',
      onDoneClick: isNextDisabled ? null : this.onDoneClick,
      onNextClick: this.onNextClick,
      openAlert: bindActionCreators(openAlert, dispatch),
      saveAvatar: bindActionCreators(saveAvatar, dispatch),
      saveCover: bindActionCreators(saveCover, dispatch),
    }
  }

  shouldComponentUpdate(nextProps) {
    console.log(this.props.coverImage, nextProps.coverImage)
    return shouldContainerUpdate(this.props, nextProps)
  }

  onDoneClick = () => {
    const { dispatch } = this.props
    dispatch(push('/following'))
  }

  onNextClick = () => {
    const { dispatch } = this.props
    dispatch(push('/onboarding/invitations'))
  }

  render() {
    return <OnboardingSettings isNextDisabled={this.props.isNextDisabled} />
  }
}

export default connect(mapStateToProps)(OnboardingSettingsContainer)

<commit_msg>Remove an unnecessary console.<commit_after>import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { push } from 'react-router-redux'
import { isEqual, pick } from 'lodash'
import { openAlert, closeAlert } from '../actions/modals'
import { saveAvatar, saveCover } from '../actions/profile'
import {
  selectAvatar,
  selectCoverImage,
  selectIsAvatarBlank,
  selectIsCoverImageBlank,
  selectIsInfoFormBlank,
} from '../selectors/profile'
import OnboardingSettings from '../components/onboarding/OnboardingSettings'

function shouldContainerUpdate(thisProps, nextProps) {
  const pickProps = ['avatar', 'coverImage', 'isNextDisabled']
  const thisCompare = pick(thisProps, pickProps)
  const nextCompare = pick(nextProps, pickProps)
  return !isEqual(thisCompare, nextCompare)
}

function mapStateToProps(state) {
  const avatar = selectAvatar(state)
  const coverImage = selectCoverImage(state)
  const isAvatarBlank = selectIsAvatarBlank(state)
  const isCoverImageBlank = selectIsCoverImageBlank(state)
  const isInfoFormBlank = selectIsInfoFormBlank(state)
  const isNextDisabled = isAvatarBlank && isCoverImageBlank && isInfoFormBlank
  return {
    avatar,
    coverImage,
    isAvatarBlank,
    isCoverImageBlank,
    isNextDisabled,
  }
}

class OnboardingSettingsContainer extends Component {

  static propTypes = {
    avatar: PropTypes.object,
    coverImage: PropTypes.object,
    dispatch: PropTypes.func.isRequired,
    isAvatarBlank: PropTypes.bool.isRequired,
    isCoverImageBlank: PropTypes.bool.isRequired,
    isNextDisabled: PropTypes.bool.isRequired,
  }

  static childContextTypes = {
    avatar: PropTypes.object,
    closeAlert: PropTypes.func,
    coverImage: PropTypes.object,
    isAvatarBlank: PropTypes.bool,
    isCoverImageBlank: PropTypes.bool,
    nextLabel: PropTypes.string,
    onDoneClick: PropTypes.func,
    onNextClick: PropTypes.func,
    openAlert: PropTypes.func,
    saveAvatar: PropTypes.func,
    saveCover: PropTypes.func,
  }

  getChildContext() {
    const {
      avatar, dispatch, coverImage, isAvatarBlank, isCoverImageBlank, isNextDisabled,
    } = this.props
    return {
      avatar,
      closeAlert: bindActionCreators(closeAlert, dispatch),
      coverImage,
      isAvatarBlank,
      isCoverImageBlank,
      nextLabel: 'Invite Cool People',
      onDoneClick: isNextDisabled ? null : this.onDoneClick,
      onNextClick: this.onNextClick,
      openAlert: bindActionCreators(openAlert, dispatch),
      saveAvatar: bindActionCreators(saveAvatar, dispatch),
      saveCover: bindActionCreators(saveCover, dispatch),
    }
  }

  shouldComponentUpdate(nextProps) {
    return shouldContainerUpdate(this.props, nextProps)
  }

  onDoneClick = () => {
    const { dispatch } = this.props
    dispatch(push('/following'))
  }

  onNextClick = () => {
    const { dispatch } = this.props
    dispatch(push('/onboarding/invitations'))
  }

  render() {
    return <OnboardingSettings isNextDisabled={this.props.isNextDisabled} />
  }
}

export default connect(mapStateToProps)(OnboardingSettingsContainer)

<|endoftext|>


<commit_before>/*
  This pulls block-elements such as `<fig>` which are
  wrapped in a `<p>` one level up.
  In the opposite direction only those elements are wrapped
  which would otherwise violate JATS
*/
export default class UnwrapBlockLevelElements {

  import(dom) {
    dom.findAll('p').forEach(_pBlock)
  }

  export() {
    console.error('TODO: implement UnwrapBlockLevelElements.export()')
  }

}

// TODO: add all of them
const BLOCKS = ['fig', 'fig-group', 'media']
const isBlock = BLOCKS.reduce((m, n) => { m[n] = true; return m}, {})

function _pBlock(p) {
  let parent = p.parentNode
  let children = p.children
  let L = children.length
  // doing it reverse so that we don't miss elements due to the ongoing tranformations
  for (var i = L - 1; i >= 0; i--) {
    let child = children[i]
    if (isBlock[child.tagName]) {
      // create a new

      let newP = parent.createElement('p')
      let childPos = p.getChildIndex(child)
      let siblings = p.childNodes.slice(childPos+1)
      // move all subsequent siblings to the new

      // and insert the block element and the new one after the current

      let pos = parent.getChildIndex(p)+1
      parent.insertAt(pos, child)
      if (siblings.length > 0 && _needsP(siblings)) {
        newP.append(siblings)
        parent.insertAt(pos+1, newP)
      }
    }
  }
  // if the original
 is now empty, remove it
  if (!_needsP(p.childNodes)) {
    p.parentNode.removeChild(p)
  }
}

function _needsP(nodes) {
  for (let i = 0; i < nodes.length; i++) {
    let child = nodes[i]
    // don't prune if there is something else left
    if (!child.isTextNode() || !(/^\s*$/.exec(child.textContent)) ) return true
  }
  return false
}
<commit_msg>Unwrap more block-level elements inside
<commit_after>/*
  This pulls block-elements such as `<fig>` which are
  wrapped in a `<p>` one level up.
  In the opposite direction only those elements are wrapped
  which would otherwise violate JATS
*/
export default class UnwrapBlockLevelElements {

  import(dom) {
    dom.findAll('p').forEach(_pBlock)
  }

  export() {
    console.error('TODO: implement UnwrapBlockLevelElements.export()')
  }

}

// TODO: add all of them
const BLOCKS = ['fig', 'fig-group', 'media', 'list', 'disp-formula', 'disp-quote']
const isBlock = BLOCKS.reduce((m, n) => { m[n] = true; return m}, {})

function _pBlock(p) {
  let parent = p.parentNode
  let children = p.children
  let L = children.length
  // doing it reverse so that we don't miss elements due to the ongoing tranformations
  for (var i = L - 1; i >= 0; i--) {
    let child = children[i]
    if (isBlock[child.tagName]) {
      // create a new

      let newP = parent.createElement('p')
      let childPos = p.getChildIndex(child)
      let siblings = p.childNodes.slice(childPos+1)
      // move all subsequent siblings to the new

      // and insert the block element and the new one after the current

      let pos = parent.getChildIndex(p)+1
      parent.insertAt(pos, child)
      if (siblings.length > 0 && _needsP(siblings)) {
        newP.append(siblings)
        parent.insertAt(pos+1, newP)
      }
    }
  }
  // if the original
 is now empty, remove it
  if (!_needsP(p.childNodes)) {
    p.parentNode.removeChild(p)
  }
}

function _needsP(nodes) {
  for (let i = 0; i < nodes.length; i++) {
    let child = nodes[i]
    // don't prune if there is something else left
    if (!child.isTextNode() || !(/^\s*$/.exec(child.textContent)) ) return true
  }
  return false
}
<|endoftext|>


<commit_before>import * as ColumnListSort from './column.list.sort';
import {modelFactory} from '../test/model.factory';
import {ColumnModel} from '../column-type/column.model';

describe('sortIndexFactory', function () {
	it('sorting Test array based on ColumnList.index values', () => { //[(right-10,1),(left-5,2),(center-6,3)]  =>
		let leftPinColumn1 = new ColumnModel();								//[(left-2,5),(center-3,6),(right-1,10)]
		leftPinColumn1.pin = 'right';
		leftPinColumn1.key = 10;
		let leftPinColumn2 = new ColumnModel();
		leftPinColumn2.pin = 'right';
		leftPinColumn2.key = 1;
		let rightPinColumn1 = new ColumnModel();
		rightPinColumn1.pin = 'left';
		rightPinColumn1.key = 5;
		let rightPinColumn2 = new ColumnModel();
		rightPinColumn2.pin = 'left';
		rightPinColumn2.key = 2;
		let centerPinColumn1 = new ColumnModel();
		centerPinColumn1.pin = null;
		centerPinColumn1.key = 6;
		let centerPinColumn2 = new ColumnModel();
		centerPinColumn2.pin = null;
		centerPinColumn2.key = 3;

		let column1 = new ColumnModel();
		column1.key = 0;
		let column2 = new ColumnModel();
		column2.key = 0;
		let column3 = new ColumnModel();
		column3.key = 0;
		let model = modelFactory();
		let test = [leftPinColumn1, rightPinColumn1, centerPinColumn1, leftPinColumn2, rightPinColumn2, centerPinColumn2];
		model.columnList({
			index: [1, 2, 3, 5, 6, 10],
			columns: [column1, column2, column3]
		});

		let sort = ColumnListSort.sortIndexFactory(model);
		let result = sort(test);
		expect(JSON.stringify(result.index)).to.equals('[2,5,3,6,1,10]');
		expect(result.hasChanges).to.equals(true);
	});
});
<commit_msg>Update column.list.sort.spec.js<commit_after>import * as ColumnListSort from './column.list.sort';
import {modelFactory} from '../test/model.factory';
import {ColumnModel} from '../column-type/column.model';

describe('sortIndexFactory', function () {
	it('sorting Test array based on the ColumnList.index values', () => { //[(right-10,1),(left-5,2),(center-6,3)]  =>
		let leftPinColumn1 = new ColumnModel();									 //[(left-2,5),(center-3,6),(right-1,10)]
		leftPinColumn1.pin = 'right';
		leftPinColumn1.key = 10;
		let leftPinColumn2 = new ColumnModel();
		leftPinColumn2.pin = 'right';
		leftPinColumn2.key = 1;
		let rightPinColumn1 = new ColumnModel();
		rightPinColumn1.pin = 'left';
		rightPinColumn1.key = 5;
		let rightPinColumn2 = new ColumnModel();
		rightPinColumn2.pin = 'left';
		rightPinColumn2.key = 2;
		let centerPinColumn1 = new ColumnModel();
		centerPinColumn1.pin = null;
		centerPinColumn1.key = 6;
		let centerPinColumn2 = new ColumnModel();
		centerPinColumn2.pin = null;
		centerPinColumn2.key = 3;

		let column1 = new ColumnModel();
		column1.key = 0;
		let column2 = new ColumnModel();
		column2.key = 0;
		let column3 = new ColumnModel();
		column3.key = 0;
		let model = modelFactory();
		let test = [leftPinColumn1, rightPinColumn1, centerPinColumn1, leftPinColumn2, rightPinColumn2, centerPinColumn2];
		model.columnList({
			index: [1, 2, 3, 5, 6, 10],
			columns: [column1, column2, column3]
		});

		let sort = ColumnListSort.sortIndexFactory(model);
		let result = sort(test);
		expect(JSON.stringify(result.index)).to.equals('[2,5,3,6,1,10]');
		expect(result.hasChanges).to.equals(true);
	});
});
<|endoftext|>


<commit_before>var EasyAssess = require('../../easyassess.application');

EasyAssess.directives["esFormSubmit"]
    = EasyAssess.app.directive("esFormSubmit", function (esRequestService,ngDialog,Upload) {
    return {
        restrict: 'E',
        replace: true,
        transclude: false,
        template:'
'
                     +'<es-form-page>'
                         +'
'
                            +'<button type="button" class="btn btn-primary" ng-click="submit()">提交</button>'
                            +'<button type="button" class="btn btn-primary" ng-click="manageAttachment()">上传附件</button>'
                            //+'<button ng-if="esType!=\'plan\'" type="button" class="btn btn-primary" ng-click="save()">保存</button>'
                         +'
'
                         +'<es-app-tab-pane>'
                         + '<es-app-tab es-active="true" es-ref="formView" es-title="表单">'
                            +'
'
                            +'<es-form-header es-header="formHeader" es-editable="false"></es-form-header>'
                            +'
'
                            +'
'
                            +'
'
                            +'
'
                            +'<es-form-group-edit es-type="{{esType}}" es-group="group" es-data="helpData"></es-form-group-edit>'
                            + '
'
                            +'
'
                            +   '
'
                            +      '填报说明:'
                            +      '<es-form-footer ng-if="template" es-editable="false" es-footer="template.footer"></es-form-footer>'
                            +   '
'
                            +'
'
                         + '</es-app-tab>'
                         + '<es-app-tab es-ref="preView" es-title="预览">'
                         +    '<es-form-result ng-if="template && previewModel" es-form="previewModel" es-template="template"></es-form-result>'
                         + '</es-app-tab>'
                        +'</es-app-tab-pane>'
                     +'</es-form-page>'
                 +'
',
        scope: {
            esForm: "=",
            esType: "@"
        },

        controller: ["$scope","ngDialog", function($scope,ngDialog){
            if ($scope.esType == 'assess'){
                var url = EasyAssess.activeEnv['assess']() + 'template/' + $scope.esForm.securedAssessment.templateGuid;
                esRequestService.esGet(url).then(function(data){
                    for (var code in $scope.esForm.securedAssessment.specimenCodes) {
                        data.data.groups.forEach(function (group) {
                            group.specimens.forEach(function (specimen) {
                               if (specimen.number === code) {
                                   specimen.isPlainNumber = true;
                                   break;
                               }
                            });
                        })
                    }
                    $scope.template = data.data;
                });
            }else if($scope.esType == 'plan'){
                $scope.template = $scope.esForm.template;
            }

            $scope.formHeader = {
                name:$scope.esForm.formName
            };

            if ($scope.esType =='assess'){
                $scope.helpData =  $scope.esForm.securedAssessment.id;
            }else{
                $scope.helpData = null;
            }

            $scope.manageAttachment = function () {
                ngDialog.open({
                    template: '
'
                            +	'
'
                            +  '注意: 每个表单只能上传一个附件，重复上传会相互覆盖.且文件大小不能大于50MB'
                            +  '
'
                            +  '<es-spinner ng-if="loading"></es-spinner>'
                            +	'
'
                             +      '选择文件'
                             +      '查看附件'
                             +  '
'

                             +	'
'
                             +      '<button type="submit" class="btn btn-primary" ng-click="submit()">上传</button>'
                             +  '
'
                             + '
',
                    plain: true,
                    controller: ['$scope', function ($dialogScope) {
                        $dialogScope.attachment = $scope.esForm.attachment;
                        $dialogScope.submit = function () {
                            if (!$dialogScope.file) {
                                EasyAssess.QuickMessage.error("请选择文件");
                            } else {
                                $dialogScope.loading = true;
                                $dialogScope.upload($dialogScope.file);
                            }
                        };

                        $dialogScope.upload = function (file) {
                            $dialogScope.fileInfo = file;
                            Upload.upload({
                                url: EasyAssess.activeEnv['assess']() + 'form/' + $scope.esForm.id + "/attachment",
                                data: {
                                    'attachment': file
                                },
                                withCredentials: true
                            }).success(function (data, status, headers, config) {
                                $scope.esForm.attachment = data.data;
                                $dialogScope.attachment = data.data;
                                EasyAssess.QuickMessage.message("上传成功");
                                $dialogScope.closeThisDialog();
                            }).error(function (data, status, headers, config) {
                                EasyAssess.QuickMessage.error("上传失败");
                            });
                        };
                    }]
                });
            };

            $scope.answer = {
                values:[],
                codes:[],
                details:[],
                signatures: {}
            };

            $scope.save = function(){
                if($scope.esType != 'plan'){
                    angular.forEach($scope.rawCodeList,function(rawCode){
                        delete rawCode['codeGuid']
                    });
                    $scope.answer.codes = $scope.rawCodeList;

                    var url = EasyAssess.activeEnv['assess']() + 'form/' + $scope.esForm.id;
                    $scope.esForm.status = "S";
                    $scope.esForm.values = $scope.answer.values;
                    $scope.esForm.codes = $scope.answer.codes;
                    $scope.esForm.details = $scope.answer.details;
                    $scope.esForm.signatures = $scope.answer.signatures;

                    esRequestService.esPut(url, $scope.esForm).then(function(res){
                        EasyAssess.QuickMessage.message("保存成功");
                    });
                }
            };

            $scope.submit = function(){
                ngDialog.openConfirm({
                    template:   '
答案一旦提交无法修改,是否确定提交?
'
                    + '
<button ng-click="confirm()" class="btn btn-primary">确定</button><button ng-click="closeThisDialog()" class="btn btn-primary">取消</button>
',
                    plain: true
                }).then(
                    function(){
                        angular.forEach($scope.rawCodeList,function(rawCode){
                            delete rawCode['codeGuid']
                        });

                        $scope.answer.codes = $scope.rawCodeList;

                        if($scope.esType == 'plan'){
                            $scope.$emit('submitted',{"values":$scope.answer.values,"codes":$scope.answer.codes, "details":$scope.answer.details, "signatures":$scope.answer.signatures});
                        }else{
                            var url = EasyAssess.activeEnv['assess']() + 'form/submit/' + $scope.esForm.id;
                            esRequestService.esPut(url, {"values":$scope.answer.values,"codes":$scope.answer.codes,"details":$scope.answer.details, "signatures":$scope.answer.signatures}).then(function(res){
                                $scope.$emit('submitted');
                            });
                        }
                    }
                );

            };

            $scope.rawCodeList = [];

            $scope.$on('$codeItemLookup_selected',function(e, data, attrs){
                var subjectGuid = attrs.subjectGuid;
                var codeGroupGuid = attrs.codeGroupGuid

                addCodeIntoList(e.targetScope, data, subjectGuid, codeGroupGuid);
            });

            $scope.$on('$tabSelected',function(e, ref, attrs){
                if (ref=="preView") {
                    $scope.previewModel = {
                        values:$scope.answer.values,
                        codes:$scope.rawCodeList,
                        details:$scope.answer.details,
                        signatures: $scope.answer.signatures
                    };
                } else {
                    $scope.previewModel = null;
                }
            });

            $scope.$on('removeSpecimen',function(e,data){
                removeFromList(data);
            });

            $scope.$on("$rowguidLookup_selected",function(e,data){
            });

            $scope.$on('valueChanged',function(e,data){
                updateTheValueList(data);
            });

            $scope.$on('batchNumberChanged',function(e,data){
                updateTheDetailList(data, 'batchNumber');
            });

            $scope.$on('expireChanged',function(e,data){
                updateTheDetailList(data, 'expire');
            });

            $scope.$on('signatureChanged',function(e, data){
                if (!$scope.answer.signatures[data.groupGuid]) {
                    $scope.answer.signatures[data.groupGuid] = {};
                }
                $scope.answer.signatures[data.groupGuid][data.field] = data.value;
            });

            function updateTheDetailList(data, field) {
                var exists = $scope.answer.details.find(function(detail){
                   return detail.subjectGuid == data.subjectGuid;
                });

                if (exists) {
                    exists[field] = data.value;
                } else {
                    var detail = {
                        'subjectGuid': data.subjectGuid
                    }
                    detail[field] = data.value;
                    $scope.answer.details.push(detail);
                }
            }

            function updateTheValueList(data){
                var updateCode = $scope.answer.values.find(function(item){
                    return (item.subjectGuid === data.subjectGuid) && (item.specimenCode === data.specimenCode);
                });

                if (updateCode){
                    updateCode.value = data.value
                }else{
                    $scope.answer.values.push(data);
                }
            }

            function removeFromList(data){
                $scope.answer.values = $scope.answer.values.filter(function(item){
                    return item.specimenCode != data;
                });
            }

            function addCodeIntoList(t, data, subjectGuid, codeGroupGuid){
                var codeItem = {
                    subject:{
                        subject: t.esSubject.item.subject,
                        unit: t.esSubject.item.unit
                    },
                    subjectGuid: t.esSubject.guid,
                    codeNumber:data.codeNumber,
                    codeName:data.name,
                    codeGroup: {
                        "name":data.group.name,
                        "id":data.group.id,
                        "guid":codeGroupGuid
                    },
                    codeGuid: t.esLookupGuid
                };

                var updatedItem = $scope.rawCodeList.find(function(item){
                    return item.codeGuid  == codeItem.codeGuid
                });

                if(updatedItem){
                    updatedItem.subject = codeItem.subject;
                    updatedItem.subjectGuid = codeItem.subjectGuid;
                    updatedItem.codeNumber = codeItem.codeNumber;
                    updatedItem.codeName = codeItem.codeName;
                    updatedItem.codeGroup = {
                        "name": codeItem.codeGroup.name,
                        "id": codeItem.codeGroup.id,
                        "guid":codeGroupGuid
                    }
                }else{
                    $scope.rawCodeList.push(codeItem);
                }
            }
        }],

    }
});
<commit_msg>Fix wrong break<commit_after>var EasyAssess = require('../../easyassess.application');

EasyAssess.directives["esFormSubmit"]
    = EasyAssess.app.directive("esFormSubmit", function (esRequestService,ngDialog,Upload) {
    return {
        restrict: 'E',
        replace: true,
        transclude: false,
        template:'
'
                     +'<es-form-page>'
                         +'
'
                            +'<button type="button" class="btn btn-primary" ng-click="submit()">提交</button>'
                            +'<button type="button" class="btn btn-primary" ng-click="manageAttachment()">上传附件</button>'
                            //+'<button ng-if="esType!=\'plan\'" type="button" class="btn btn-primary" ng-click="save()">保存</button>'
                         +'
'
                         +'<es-app-tab-pane>'
                         + '<es-app-tab es-active="true" es-ref="formView" es-title="表单">'
                            +'
'
                            +'<es-form-header es-header="formHeader" es-editable="false"></es-form-header>'
                            +'
'
                            +'
'
                            +'
'
                            +'
'
                            +'<es-form-group-edit es-type="{{esType}}" es-group="group" es-data="helpData"></es-form-group-edit>'
                            + '
'
                            +'
'
                            +   '
'
                            +      '填报说明:'
                            +      '<es-form-footer ng-if="template" es-editable="false" es-footer="template.footer"></es-form-footer>'
                            +   '
'
                            +'
'
                         + '</es-app-tab>'
                         + '<es-app-tab es-ref="preView" es-title="预览">'
                         +    '<es-form-result ng-if="template && previewModel" es-form="previewModel" es-template="template"></es-form-result>'
                         + '</es-app-tab>'
                        +'</es-app-tab-pane>'
                     +'</es-form-page>'
                 +'
',
        scope: {
            esForm: "=",
            esType: "@"
        },

        controller: ["$scope","ngDialog", function($scope,ngDialog){
            if ($scope.esType == 'assess'){
                var url = EasyAssess.activeEnv['assess']() + 'template/' + $scope.esForm.securedAssessment.templateGuid;
                esRequestService.esGet(url).then(function(data){
                    for (var code in $scope.esForm.securedAssessment.specimenCodes) {
                        data.data.groups.forEach(function (group) {
                            group.specimens.forEach(function (specimen) {
                               if (specimen.number === code) {
                                   specimen.isPlainNumber = true;
                               }
                            });
                        })
                    }
                    $scope.template = data.data;
                });
            }else if($scope.esType == 'plan'){
                $scope.template = $scope.esForm.template;
            }

            $scope.formHeader = {
                name:$scope.esForm.formName
            };

            if ($scope.esType =='assess'){
                $scope.helpData =  $scope.esForm.securedAssessment.id;
            }else{
                $scope.helpData = null;
            }

            $scope.manageAttachment = function () {
                ngDialog.open({
                    template: '
'
                            +	'
'
                            +  '注意: 每个表单只能上传一个附件，重复上传会相互覆盖.且文件大小不能大于50MB'
                            +  '
'
                            +  '<es-spinner ng-if="loading"></es-spinner>'
                            +	'
'
                             +      '选择文件'
                             +      '查看附件'
                             +  '
'

                             +	'
'
                             +      '<button type="submit" class="btn btn-primary" ng-click="submit()">上传</button>'
                             +  '
'
                             + '
',
                    plain: true,
                    controller: ['$scope', function ($dialogScope) {
                        $dialogScope.attachment = $scope.esForm.attachment;
                        $dialogScope.submit = function () {
                            if (!$dialogScope.file) {
                                EasyAssess.QuickMessage.error("请选择文件");
                            } else {
                                $dialogScope.loading = true;
                                $dialogScope.upload($dialogScope.file);
                            }
                        };

                        $dialogScope.upload = function (file) {
                            $dialogScope.fileInfo = file;
                            Upload.upload({
                                url: EasyAssess.activeEnv['assess']() + 'form/' + $scope.esForm.id + "/attachment",
                                data: {
                                    'attachment': file
                                },
                                withCredentials: true
                            }).success(function (data, status, headers, config) {
                                $scope.esForm.attachment = data.data;
                                $dialogScope.attachment = data.data;
                                EasyAssess.QuickMessage.message("上传成功");
                                $dialogScope.closeThisDialog();
                            }).error(function (data, status, headers, config) {
                                EasyAssess.QuickMessage.error("上传失败");
                            });
                        };
                    }]
                });
            };

            $scope.answer = {
                values:[],
                codes:[],
                details:[],
                signatures: {}
            };

            $scope.save = function(){
                if($scope.esType != 'plan'){
                    angular.forEach($scope.rawCodeList,function(rawCode){
                        delete rawCode['codeGuid']
                    });
                    $scope.answer.codes = $scope.rawCodeList;

                    var url = EasyAssess.activeEnv['assess']() + 'form/' + $scope.esForm.id;
                    $scope.esForm.status = "S";
                    $scope.esForm.values = $scope.answer.values;
                    $scope.esForm.codes = $scope.answer.codes;
                    $scope.esForm.details = $scope.answer.details;
                    $scope.esForm.signatures = $scope.answer.signatures;

                    esRequestService.esPut(url, $scope.esForm).then(function(res){
                        EasyAssess.QuickMessage.message("保存成功");
                    });
                }
            };

            $scope.submit = function(){
                ngDialog.openConfirm({
                    template:   '
答案一旦提交无法修改,是否确定提交?
'
                    + '
<button ng-click="confirm()" class="btn btn-primary">确定</button><button ng-click="closeThisDialog()" class="btn btn-primary">取消</button>
',
                    plain: true
                }).then(
                    function(){
                        angular.forEach($scope.rawCodeList,function(rawCode){
                            delete rawCode['codeGuid']
                        });

                        $scope.answer.codes = $scope.rawCodeList;

                        if($scope.esType == 'plan'){
                            $scope.$emit('submitted',{"values":$scope.answer.values,"codes":$scope.answer.codes, "details":$scope.answer.details, "signatures":$scope.answer.signatures});
                        }else{
                            var url = EasyAssess.activeEnv['assess']() + 'form/submit/' + $scope.esForm.id;
                            esRequestService.esPut(url, {"values":$scope.answer.values,"codes":$scope.answer.codes,"details":$scope.answer.details, "signatures":$scope.answer.signatures}).then(function(res){
                                $scope.$emit('submitted');
                            });
                        }
                    }
                );

            };

            $scope.rawCodeList = [];

            $scope.$on('$codeItemLookup_selected',function(e, data, attrs){
                var subjectGuid = attrs.subjectGuid;
                var codeGroupGuid = attrs.codeGroupGuid

                addCodeIntoList(e.targetScope, data, subjectGuid, codeGroupGuid);
            });

            $scope.$on('$tabSelected',function(e, ref, attrs){
                if (ref=="preView") {
                    $scope.previewModel = {
                        values:$scope.answer.values,
                        codes:$scope.rawCodeList,
                        details:$scope.answer.details,
                        signatures: $scope.answer.signatures
                    };
                } else {
                    $scope.previewModel = null;
                }
            });

            $scope.$on('removeSpecimen',function(e,data){
                removeFromList(data);
            });

            $scope.$on("$rowguidLookup_selected",function(e,data){
            });

            $scope.$on('valueChanged',function(e,data){
                updateTheValueList(data);
            });

            $scope.$on('batchNumberChanged',function(e,data){
                updateTheDetailList(data, 'batchNumber');
            });

            $scope.$on('expireChanged',function(e,data){
                updateTheDetailList(data, 'expire');
            });

            $scope.$on('signatureChanged',function(e, data){
                if (!$scope.answer.signatures[data.groupGuid]) {
                    $scope.answer.signatures[data.groupGuid] = {};
                }
                $scope.answer.signatures[data.groupGuid][data.field] = data.value;
            });

            function updateTheDetailList(data, field) {
                var exists = $scope.answer.details.find(function(detail){
                   return detail.subjectGuid == data.subjectGuid;
                });

                if (exists) {
                    exists[field] = data.value;
                } else {
                    var detail = {
                        'subjectGuid': data.subjectGuid
                    }
                    detail[field] = data.value;
                    $scope.answer.details.push(detail);
                }
            }

            function updateTheValueList(data){
                var updateCode = $scope.answer.values.find(function(item){
                    return (item.subjectGuid === data.subjectGuid) && (item.specimenCode === data.specimenCode);
                });

                if (updateCode){
                    updateCode.value = data.value
                }else{
                    $scope.answer.values.push(data);
                }
            }

            function removeFromList(data){
                $scope.answer.values = $scope.answer.values.filter(function(item){
                    return item.specimenCode != data;
                });
            }

            function addCodeIntoList(t, data, subjectGuid, codeGroupGuid){
                var codeItem = {
                    subject:{
                        subject: t.esSubject.item.subject,
                        unit: t.esSubject.item.unit
                    },
                    subjectGuid: t.esSubject.guid,
                    codeNumber:data.codeNumber,
                    codeName:data.name,
                    codeGroup: {
                        "name":data.group.name,
                        "id":data.group.id,
                        "guid":codeGroupGuid
                    },
                    codeGuid: t.esLookupGuid
                };

                var updatedItem = $scope.rawCodeList.find(function(item){
                    return item.codeGuid  == codeItem.codeGuid
                });

                if(updatedItem){
                    updatedItem.subject = codeItem.subject;
                    updatedItem.subjectGuid = codeItem.subjectGuid;
                    updatedItem.codeNumber = codeItem.codeNumber;
                    updatedItem.codeName = codeItem.codeName;
                    updatedItem.codeGroup = {
                        "name": codeItem.codeGroup.name,
                        "id": codeItem.codeGroup.id,
                        "guid":codeGroupGuid
                    }
                }else{
                    $scope.rawCodeList.push(codeItem);
                }
            }
        }],

    }
});
<|endoftext|>