<commit_before>import { translate } from '../../../common/i18n';
import { tradeOptionToProposal, doUntilDone } from '../tools';
import { proposalsReady, clearProposals } from './state/actions';
import { TrackJSError } from '../../view/logger';

export default Engine =>
    class Proposal extends Engine {
        makeProposals(tradeOption) {
            if (!this.isNewTradeOption(tradeOption)) {
                return;
            }
            this.tradeOption = tradeOption;
            this.proposalTemplates = tradeOptionToProposal(tradeOption);
            this.renewProposalsOnPurchase();
        }
        selectProposal(contractType) {
            let toBuy;

            if (!this.data.has('proposals')) {
                throw Error(translate('Proposals are not ready'));
            }

            this.data.get('proposals').forEach(proposal => {
                if (proposal.contractType === contractType) {
                    if (proposal.error) {
                        const { error } = proposal.error;
                        throw new TrackJSError(error.error.code, error.error.message, error);
                    } else {
                        toBuy = proposal;
                    }
                }
            });

            if (!toBuy) {
                throw new TrackJSError(
                    'CustomInvalidProposal',
                    translate('Selected proposal does not exist'),
                    Array.from(this.data.get('proposals')).map(proposal => proposal[1])
                );
            }

            return {
                proposal: toBuy,
                currency: this.tradeOption.currency,
            };
        }
        renewProposalsOnPurchase() {
            this.unsubscribeProposals().then(() => this.requestProposals());
        }
        clearProposals() {
            this.data = this.data.set('proposals', new Map());
            this.store.dispatch(clearProposals());
        }
        requestProposals() {
            this.proposalTemplates.map(proposal =>
                doUntilDone(() =>
                    this.api
                        .subscribeToPriceForContractProposal(proposal)
                        // eslint-disable-next-line consistent-return
                        .catch(e => {
                            if (e && e.name === 'RateLimit') {
                                return Promise.reject(e);
                            }

                            const errorCode = e.error && e.error.error && e.error.error.code;

                            if (errorCode === 'ContractBuyValidationError') {
                                const { uuid } = e.error.echo_req.passthrough;

                                if (!this.data.hasIn(['forgetProposals', uuid])) {
                                    this.data = this.data.setIn(['proposals', uuid], {
                                        ...proposal,
                                        contractType: proposal.contract_type,
                                        error       : e,
                                    });
                                }
                            } else {
                                this.$scope.observer.emit('Error', e);
                            }
                        })
                )
            );
        }
        observeProposals() {
            this.listen('proposal', r => {
                const { proposal, passthrough } = r;
                const id = passthrough.uuid;

                if (!this.data.hasIn(['forgetProposals', id])) {
                    this.data = this.data.setIn(['proposals', id], {
                        ...proposal,
                        ...passthrough,
                    });
                    this.checkProposalReady();
                }
            });
        }
        unsubscribeProposals() {
            const proposalObj = this.data.get('proposals');

            if (!proposalObj) {
                return Promise.resolve();
            }

            const proposals = Array.from(proposalObj.values());

            this.clearProposals();

            return Promise.all(
                proposals.map(proposal => {
                    const { uuid: id } = proposal;
                    const removeProposal = () => {
                        this.data = this.data.deleteIn(['forgetProposals', id]);
                    };

                    this.data = this.data.setIn(['forgetProposals', id], true);

                    if (proposal.error) {
                        removeProposal();
                        return Promise.resolve();
                    }

                    return doUntilDone(() => this.api.unsubscribeByID(proposal.id)).then(() => removeProposal());
                })
            );
        }
        checkProposalReady() {
            const proposals = this.data.get('proposals');

            if (proposals && proposals.size) {
                const isSameWithTemplate = this.proposalTemplates.every(p =>
                    this.data.hasIn(['proposals', p.passthrough.uuid])
                );

                if (isSameWithTemplate) {
                    this.startPromise.then(() => this.store.dispatch(proposalsReady()));
                }
            }
        }
        isNewTradeOption(tradeOption) {
            if (!this.tradeOption) {
                this.tradeOption = tradeOption;
                return true;
            }

            const isNotEqual = key => this.tradeOption[key] !== tradeOption[key];

            return (
                isNotEqual('duration') ||
                isNotEqual('duration_unit') ||
                isNotEqual('amount') ||
                isNotEqual('prediction') ||
                isNotEqual('barrierOffset') ||
                isNotEqual('secondBarrierOffset') ||
                isNotEqual('symbol')
            );
        }
    };
<commit_msg>Promise all subscriptions to be executed<commit_after>import { translate } from '../../../common/i18n';
import { tradeOptionToProposal, doUntilDone } from '../tools';
import { proposalsReady, clearProposals } from './state/actions';
import { TrackJSError } from '../../view/logger';

export default Engine =>
    class Proposal extends Engine {
        makeProposals(tradeOption) {
            if (!this.isNewTradeOption(tradeOption)) {
                return;
            }
            this.tradeOption = tradeOption;
            this.proposalTemplates = tradeOptionToProposal(tradeOption);
            this.renewProposalsOnPurchase();
        }
        selectProposal(contractType) {
            let toBuy;

            if (!this.data.has('proposals')) {
                throw Error(translate('Proposals are not ready'));
            }

            this.data.get('proposals').forEach(proposal => {
                if (proposal.contractType === contractType) {
                    if (proposal.error) {
                        const { error } = proposal.error;
                        throw new TrackJSError(error.error.code, error.error.message, error);
                    } else {
                        toBuy = proposal;
                    }
                }
            });

            if (!toBuy) {
                throw new TrackJSError(
                    'CustomInvalidProposal',
                    translate('Selected proposal does not exist'),
                    Array.from(this.data.get('proposals')).map(proposal => proposal[1])
                );
            }

            return {
                proposal: toBuy,
                currency: this.tradeOption.currency,
            };
        }
        renewProposalsOnPurchase() {
            this.unsubscribeProposals().then(() => this.requestProposals());
        }
        clearProposals() {
            this.data = this.data.set('proposals', new Map());
            this.store.dispatch(clearProposals());
        }
        requestProposals() {
            Promise.all(
                this.proposalTemplates.map(proposal =>
                    doUntilDone(() =>
                        this.api
                            .subscribeToPriceForContractProposal(proposal)
                            // eslint-disable-next-line consistent-return
                            .catch(e => {
                                if (e && e.name === 'RateLimit') {
                                    return Promise.reject(e);
                                }

                                const errorCode = e.error && e.error.error && e.error.error.code;

                                if (errorCode === 'ContractBuyValidationError') {
                                    const { uuid } = e.error.echo_req.passthrough;

                                    if (!this.data.hasIn(['forgetProposals', uuid])) {
                                        // Add to proposals map with error. Will later be shown to user, see selectProposal.
                                        this.data = this.data.setIn(['proposals', uuid], {
                                            ...proposal,
                                            contractType: proposal.contract_type,
                                            error       : e,
                                        });
                                    }
                                } else {
                                    this.$scope.observer.emit('Error', e);
                                }
                            })
                    )
                )
            ).catch(e => this.$scope.observer.emit('Error', e));
        }
        observeProposals() {
            this.listen('proposal', r => {
                const { proposal, passthrough } = r;
                const id = passthrough.uuid;

                if (!this.data.hasIn(['forgetProposals', id])) {
                    this.data = this.data.setIn(['proposals', id], {
                        ...proposal,
                        ...passthrough,
                    });
                    this.checkProposalReady();
                }
            });
        }
        unsubscribeProposals() {
            const proposalObj = this.data.get('proposals');

            if (!proposalObj) {
                return Promise.resolve();
            }

            const proposals = Array.from(proposalObj.values());

            this.clearProposals();

            return Promise.all(
                proposals.map(proposal => {
                    const { uuid: id } = proposal;
                    const removeProposal = () => {
                        this.data = this.data.deleteIn(['forgetProposals', id]);
                    };

                    this.data = this.data.setIn(['forgetProposals', id], true);

                    if (proposal.error) {
                        removeProposal();
                        return Promise.resolve();
                    }

                    return doUntilDone(() => this.api.unsubscribeByID(proposal.id)).then(() => removeProposal());
                })
            );
        }
        checkProposalReady() {
            const proposals = this.data.get('proposals');

            if (proposals && proposals.size) {
                const isSameWithTemplate = this.proposalTemplates.every(p =>
                    this.data.hasIn(['proposals', p.passthrough.uuid])
                );

                if (isSameWithTemplate) {
                    this.startPromise.then(() => this.store.dispatch(proposalsReady()));
                }
            }
        }
        isNewTradeOption(tradeOption) {
            if (!this.tradeOption) {
                this.tradeOption = tradeOption;
                return true;
            }

            const isNotEqual = key => this.tradeOption[key] !== tradeOption[key];

            return (
                isNotEqual('duration') ||
                isNotEqual('duration_unit') ||
                isNotEqual('amount') ||
                isNotEqual('prediction') ||
                isNotEqual('barrierOffset') ||
                isNotEqual('secondBarrierOffset') ||
                isNotEqual('symbol')
            );
        }
    };
<|endoftext|>


<commit_before>(function() {

    "use strict";

    /** This controller takes a camera target and provides animation using
     *  points of interest. POIs can be added and removed. One can navigate
     *  to next, previous or specific POIs.
     *
     *  @constructor
     */
    XMOT.CameraAnimationController = new XMOT.Class({

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {Element|Transformable} targetViewGroup
         *  @param {Object=} options
         *
         *  Options:
         *      o moveToTime: time in milliseconds used for camera pose animation, default: 3000
         */
        initialize: function(targetViewGroup, options) {

            if(!options) {
                options = {};
            }
            if(!options.moveToTime) {
                options.moveToTime = 3000;
            }

            this.target = XMOT.util.getOrCreateTransformable(targetViewGroup);

            /** @private */
            this._idGenerator = new XMOT.util.IDGenerator();
            /** @private */
            this._pointOfInterests = new XMOT.util.Map();
            /** @private */
            this._moveToTime = options.moveToTime;

            /** @private
             *  @type {string}
             */
            this._currentPointOfInterestID = null;

            /** @private */
            this._movementInProgress = false;
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @return {boolean}
         */
        movementInProgress: function() {
            return this._movementInProgress;
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {window.XML3DVec3} position
         *  @param {window.XML3DRotation} orientation
         *  @param {number=} moveToTime. Default: class instance's moveToTime
         *  @return {string} id of the newly added point of interest
         */
        addPOI: function(position, orientation, moveToTime) {

            var id = this._idGenerator.newID();
            if(moveToTime === undefined)
                moveToTime = this._moveToTime;

            var poi = {
                id: id,
                position: position,
                orientation: orientation,
                moveToTime: moveToTime
            };
            this._pointOfInterests.add(id, poi);

            if(this._currentPointOfInterestID == null) {
                this._currentPointOfInterestID = id;
            }

            return id;
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {string} id of the point of interest to remove
         */
        removePOI: function(id) {
            this._pointOfInterests.remove(id);
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         */
        clearPOIs: function() {
            this.stopMovementToPOI();

            this._pointOfInterests.clear();
            this._currentPointOfInterestID = null;
            this._idGenerator.reset();
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {function()=} moveToFinishedCallback
         */
        moveToNextPOI: function(moveToFinishedCallback) {

            var nextPOI = this._pointOfInterests.getNext(this._currentPointOfInterestID)[0].id;
            this.moveToPOI(nextPOI, moveToFinishedCallback);
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {function()=} moveToFinishedCallback
         */
        moveToPreviousPOI: function(moveToFinishedCallback) {

            var previousPOI = this._pointOfInterests.getPrevious(this._currentPointOfInterestID)[0].id;
            this.moveToPOI(previousPOI, moveToFinishedCallback);
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {Object} poi
         *  @return {boolean} true if movement has actually been started
         *
         *  Attributes of poi:
         *  o id: id of the point of interest to move to
         *  o moveToFinishedCallback: invoked when the animation is finished
         *  o moveToTime: default: the moved-to POI's moveToTime
         */
        moveToPOI: function(poi) {

            var nextPOI = this._pointOfInterests.get(poi.id)[0];

            this._currentPointOfInterestID = poi.id;
            this._movementInProgress = true;

            var internalFinishedCallback = function() {
                this._moveToFinished(poi.moveToFinishedCallback);
            }.bind(this);

            this.target.moveTo(nextPOI.position, nextPOI.orientation, nextPOI.moveToTime, {
                queueing: false,
                callback: internalFinishedCallback
            });
        },

        /** Sequentially move to the given POIs, starting with the one at index 0 and
         *  continuing until the end of the array. If the whole action is finished
         *  invokes the optional callback.
         *
         *  @this {XMOT.CameraAnimationController}
         *  @param {Array.<{id:string,moveToTime:number}>} POIs, moveToTime is optional
         *  @param {function()=} moveToFinishedCallback
         *  @return {boolean} true if movement has actually been started
         */
        moveAlongPOIPath: function(POIs, moveToFinishedCallback) {

            if(this._pointOfInterests.size() < 1 ||
                this._movementInProgress || this.target.movementInProgress()) {
                return false;
            }

            var numPOIs = POIs.length;

            // Moving to a POI is asynchronous and will invoke a callback when the movement
            // is done. Thus, we construct a callback chain, that will trigger the movement
            // to the next POI. The last invokation of the callback will trigger the given
            // moveToFinishedCallback.
            var lastCallback = moveToFinishedCallback;
            var fn = function() {};
            var that = this;

            for(var i = numPOIs-1; i >= 0; i--) {

                (function(){
                    var opts = {};
                    opts.id = POIs[i].id;
                    if(POIs[i].moveToTime !== undefined)
                        opts.moveToTime = POIs[i].moveToTime;
                    if(lastCallback !== undefined)
                        opts.moveToFinishedCallback = lastCallback;

                    fn = function() {that.moveToPOI(opts);}

                    lastCallback = fn;
                }());
            }

            fn();

            return true;
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         */
        stopMovementToPOI: function() {

            this.target.stop();
            this._moveToFinished();
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @private
         *  @param {function()=} moveToFinishedCallback
         */
        _moveToFinished: function(moveToFinishedCallback) {
            this._movementInProgress = false;
            if(moveToFinishedCallback !== undefined)
                moveToFinishedCallback();
        }
    });
}());
<commit_msg>CameraAnimationCtrl: added callback argument to moveToPOI()<commit_after>(function() {

    "use strict";

    /** This controller takes a camera target and provides animation using
     *  points of interest. POIs can be added and removed. One can navigate
     *  to next, previous or specific POIs.
     *
     *  @constructor
     */
    XMOT.CameraAnimationController = new XMOT.Class({

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {Element|Transformable} targetViewGroup
         *  @param {Object=} options
         *
         *  Options:
         *      o moveToTime: time in milliseconds used for camera pose animation, default: 3000
         */
        initialize: function(targetViewGroup, options) {

            if(!options) {
                options = {};
            }
            if(!options.moveToTime) {
                options.moveToTime = 3000;
            }

            this.target = XMOT.util.getOrCreateTransformable(targetViewGroup);

            /** @private */
            this._idGenerator = new XMOT.util.IDGenerator();
            /** @private */
            this._pointOfInterests = new XMOT.util.Map();
            /** @private */
            this._moveToTime = options.moveToTime;

            /** @private
             *  @type {string}
             */
            this._currentPointOfInterestID = null;

            /** @private */
            this._movementInProgress = false;
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @return {boolean}
         */
        movementInProgress: function() {
            return this._movementInProgress;
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {window.XML3DVec3} position
         *  @param {window.XML3DRotation} orientation
         *  @param {number=} moveToTime. Default: class instance's moveToTime
         *  @return {string} id of the newly added point of interest
         */
        addPOI: function(position, orientation, moveToTime) {

            var id = this._idGenerator.newID();
            if(moveToTime === undefined)
                moveToTime = this._moveToTime;

            var poi = {
                id: id,
                position: position,
                orientation: orientation,
                moveToTime: moveToTime
            };
            this._pointOfInterests.add(id, poi);

            if(this._currentPointOfInterestID == null) {
                this._currentPointOfInterestID = id;
            }

            return id;
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {string} id of the point of interest to remove
         */
        removePOI: function(id) {
            this._pointOfInterests.remove(id);
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         */
        clearPOIs: function() {
            this.stopMovementToPOI();

            this._pointOfInterests.clear();
            this._currentPointOfInterestID = null;
            this._idGenerator.reset();
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {function()=} moveToFinishedCallback
         */
        moveToNextPOI: function(moveToFinishedCallback) {

            var nextPOI = this._pointOfInterests.getNext(this._currentPointOfInterestID)[0].id;
            this.moveToPOI(nextPOI, moveToFinishedCallback);
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {function()=} moveToFinishedCallback
         */
        moveToPreviousPOI: function(moveToFinishedCallback) {

            var previousPOI = this._pointOfInterests.getPrevious(this._currentPointOfInterestID)[0].id;
            this.moveToPOI(previousPOI, moveToFinishedCallback);
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @param {Object} poi
         *  @return {boolean} true if movement has actually been started
         *
         *  Attributes of poi:
         *  o id: id of the point of interest to move to
         *  o moveToTime: default: the moved-to POI's moveToTime
         */
        moveToPOI: function(poi, moveToFinishedCallback) {

            var nextPOI = this._pointOfInterests.get(poi.id)[0];

            this._currentPointOfInterestID = poi.id;
            this._movementInProgress = true;

            var internalFinishedCallback = function() {
                this._moveToFinished(moveToFinishedCallback);
            }.bind(this);

            this.target.moveTo(nextPOI.position, nextPOI.orientation, nextPOI.moveToTime, {
                queueing: false,
                callback: internalFinishedCallback
            });
        },

        /** Sequentially move to the given POIs, starting with the one at index 0 and
         *  continuing until the end of the array. If the whole action is finished
         *  invokes the optional callback.
         *
         *  @this {XMOT.CameraAnimationController}
         *  @param {Array.<{id:string,moveToTime:number}>} POIs, moveToTime is optional
         *  @param {function()=} moveToFinishedCallback
         *  @return {boolean} true if movement has actually been started
         */
        moveAlongPOIPath: function(POIs, moveToFinishedCallback) {

            if(this._pointOfInterests.size() < 1 ||
                this._movementInProgress || this.target.movementInProgress()) {
                return false;
            }

            var numPOIs = POIs.length;

            // Moving to a POI is asynchronous and will invoke a callback when the movement
            // is done. Thus, we construct a callback chain, that will trigger the movement
            // to the next POI. The last invokation of the callback will trigger the given
            // moveToFinishedCallback.
            var lastCallback = moveToFinishedCallback;
            var fn = function() {};
            var that = this;

            for(var i = numPOIs-1; i >= 0; i--) {

                (function(){
                    var opts = {};
                    opts.id = POIs[i].id;
                    if(POIs[i].moveToTime !== undefined)
                        opts.moveToTime = POIs[i].moveToTime;

                    var finishedCallback = lastCallback;

                    fn = function() {that.moveToPOI(opts, finishedCallback);}

                    lastCallback = fn;
                }());
            }

            fn();

            return true;
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         */
        stopMovementToPOI: function() {

            this.target.stop();
            this._moveToFinished();
        },

        /**
         *  @this {XMOT.CameraAnimationController}
         *  @private
         *  @param {function()=} moveToFinishedCallback
         */
        _moveToFinished: function(moveToFinishedCallback) {
            this._movementInProgress = false;
            if(moveToFinishedCallback !== undefined)
                moveToFinishedCallback();
        }
    });
}());
<|endoftext|>


<commit_before>export default {
	'CDU':'#33302E',
	'SPD':'#F34D5B',
	'FDP':'#fcc83c',
	'Linke':'#B3325D',
	'AfD':'#1E8FCC',
  'Grune':'#AECC70',
	'Other':'#CEC6B9',
};
<commit_msg>change cdu name<commit_after>export default {
	'CDU/CSU':'#33302E',
	'SPD':'#F34D5B',
	'FDP':'#fcc83c',
	'Linke':'#B3325D',
	'AfD':'#1E8FCC',
  'Grune':'#AECC70',
	'Other':'#CEC6B9',
};
<|endoftext|>


<commit_before>var React = require('react/addons');
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var AltText = require('./AltText');
var Toolbar = require('./Toolbar');

var { Button, FormIconField, FormInput } = require('elemental');

var Header = React.createClass({

	displayName: 'ItemViewHeader',

	getInitialState () {
		return {
			searchIsVisible: false,
			searchIsFocused: false,
			searchString: ''
		};
	},

	componentDidUpdate (prevProps, prevState) {
		if (this.state.searchIsVisible && !prevState.searchIsVisible) {
			this.refs.searchField.getDOMNode().focus();
		}
	},

	toggleCreate (visible) {
		this.props.toggleCreate(visible);
	},

	toggleSearch (visible) {
		this.setState({
			searchIsVisible: visible,
			searchIsFocused: visible,
			searchString: ''
		});
	},

	searchFocusChanged (focused) {
		this.setState({
			searchIsFocused: focused
		});
	},

	searchStringChanged (event) {
		this.setState({
			searchString: event.target.value
		});
	},

	renderDrilldown () {
		if (this.state.searchIsVisible) return null;
		/* eslint-disable no-script-url */
		return (
			<Toolbar.Section left>
				{this.renderDrilldownItems()}
				{this.renderSearch()}
			</Toolbar.Section>
		);
		/* eslint-enable */
	},

	renderDrilldownItems () {

		var list = this.props.list;
		var items = this.props.data.drilldown ? this.props.data.drilldown.items : [];

		var els = items.map(function(dd) {

			var links = [];

			dd.items.forEach(function(el, i) {
				links.push({el.label});
				if (i < dd.items.length - 1) {
					links.push(,);//eslint-disable-line comma-spacing
				}
			});

			var more = dd.more ? ... : '';

			return (

					{links}
					{more}

			);

		});

		if (!els.length) {
			return (
				<Button type="link" href={'/keystone/' + list.path}>

					{list.plural}
				</Button>
			);
		} else {
			// add the current list
			els.push(

					{list.plural}

			);
			return
{els}
;
		}

	},

	renderSearch () {
		var list = this.props.list;
		return (
			<form action={'/keystone/' + list.path} className="EditForm__header__search hidden-xs">
				<FormIconField iconPosition="left" iconColor="primary" iconKey="search" className="EditForm__header__search-field">
					<FormInput
						ref="searchField"
						type="search"
						name="search"
						value={this.state.searchString}
						onChange={this.searchStringChanged}
						onFocus={this.searchFocusChanged.bind(this, true)}
						onBlur={this.searchFocusChanged.bind(this, false)}
						placeholder="Search"
						className="EditForm__header__search-input" />
				</FormIconField>
			</form>
		);
	},

	renderInfo () {
		return (
			<Toolbar.Section right>
				{this.renderCreateButton()}
			</Toolbar.Section>
		);
	},

	renderCreateButton () {
		if (this.props.list.nocreate) return null;
		/* eslint-disable no-script-url */
		return (
			<Button type="success" onClick={this.toggleCreate.bind(this, true)}>

				New {this.props.list.singular}
			</Button>
		);
		/* eslint-enable */
	},

	render () {
		return (
			<Toolbar>
				{this.renderDrilldown()}
				{this.renderInfo()}
			</Toolbar>
		);
	}

});

module.exports = Header;
<commit_msg>I think this should be a chevron-left<commit_after>var React = require('react/addons');
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var AltText = require('./AltText');
var Toolbar = require('./Toolbar');

var { Button, FormIconField, FormInput } = require('elemental');

var Header = React.createClass({

	displayName: 'ItemViewHeader',

	getInitialState () {
		return {
			searchIsVisible: false,
			searchIsFocused: false,
			searchString: ''
		};
	},

	componentDidUpdate (prevProps, prevState) {
		if (this.state.searchIsVisible && !prevState.searchIsVisible) {
			this.refs.searchField.getDOMNode().focus();
		}
	},

	toggleCreate (visible) {
		this.props.toggleCreate(visible);
	},

	toggleSearch (visible) {
		this.setState({
			searchIsVisible: visible,
			searchIsFocused: visible,
			searchString: ''
		});
	},

	searchFocusChanged (focused) {
		this.setState({
			searchIsFocused: focused
		});
	},

	searchStringChanged (event) {
		this.setState({
			searchString: event.target.value
		});
	},

	renderDrilldown () {
		if (this.state.searchIsVisible) return null;
		/* eslint-disable no-script-url */
		return (
			<Toolbar.Section left>
				{this.renderDrilldownItems()}
				{this.renderSearch()}
			</Toolbar.Section>
		);
		/* eslint-enable */
	},

	renderDrilldownItems () {

		var list = this.props.list;
		var items = this.props.data.drilldown ? this.props.data.drilldown.items : [];

		var els = items.map(function(dd) {

			var links = [];

			dd.items.forEach(function(el, i) {
				links.push({el.label});
				if (i < dd.items.length - 1) {
					links.push(,);//eslint-disable-line comma-spacing
				}
			});

			var more = dd.more ? ... : '';

			return (

					{links}
					{more}

			);

		});

		if (!els.length) {
			return (
				<Button type="link" href={'/keystone/' + list.path}>

					{list.plural}
				</Button>
			);
		} else {
			// add the current list
			els.push(

					{list.plural}

			);
			return
{els}
;
		}

	},

	renderSearch () {
		var list = this.props.list;
		return (
			<form action={'/keystone/' + list.path} className="EditForm__header__search hidden-xs">
				<FormIconField iconPosition="left" iconColor="primary" iconKey="search" className="EditForm__header__search-field">
					<FormInput
						ref="searchField"
						type="search"
						name="search"
						value={this.state.searchString}
						onChange={this.searchStringChanged}
						onFocus={this.searchFocusChanged.bind(this, true)}
						onBlur={this.searchFocusChanged.bind(this, false)}
						placeholder="Search"
						className="EditForm__header__search-input" />
				</FormIconField>
			</form>
		);
	},

	renderInfo () {
		return (
			<Toolbar.Section right>
				{this.renderCreateButton()}
			</Toolbar.Section>
		);
	},

	renderCreateButton () {
		if (this.props.list.nocreate) return null;
		/* eslint-disable no-script-url */
		return (
			<Button type="success" onClick={this.toggleCreate.bind(this, true)}>

				New {this.props.list.singular}
			</Button>
		);
		/* eslint-enable */
	},

	render () {
		return (
			<Toolbar>
				{this.renderDrilldown()}
				{this.renderInfo()}
			</Toolbar>
		);
	}

});

module.exports = Header;
<|endoftext|>


<commit_before>/**
 * Created by yangm11 on 8/30/2017.
 */
'use strict';

class BinaryTree {
  constructor(list) {
    list = list || [null];
    let entry = BinaryTree.createNode(list[0]);
    for (let i = 1; i < list.length; i++) {
      BinaryTree.insert(list[i], entry);
    }
    this._entry = entry;
  }

  toSortedList(node) {
    node = node || this._entry;
    if (node.left === null && node.right === null) {
      return BinaryTree.nodeToList(node);
    }
    if (node.left === null && node.right !== null) {
      return BinaryTree.nodeToList(node).concat(this.toSortedList(node.right));
    }
    if (node.left !== null && node.right === null) {
      return this.toSortedList(node.left).concat(BinaryTree.nodeToList(node));
    }
    if (node.left !== null && node.right !== null) {
      return this.toSortedList(node.left).concat(BinaryTree.nodeToList(node), this.toSortedList(node.right));
    }
  }

  static nodeToList(node) {
    let res = [];
    for (let i = 0; i < node.copies; i++) {
      res.push(node.value);
    }
    return res;
  }

  static createNode(v) {
    return {
      value: v,
      copies: 1,
      left: null,
      right: null
    }
  }

  static insert(v, node) {
    if (v > node.value) {
      if (node.right === null) {
        let t = BinaryTree.createNode(v);
        node.right = t;
        return t;
      } else {
        BinaryTree.insert(v, node.right);
      }
    }
    if (v < node.value) {
      if (node.left === null) {
        let t = BinaryTree.createNode(v);
        node.left = t;
        return t;
      } else {
        BinaryTree.insert(v, node.left);
      }
    }
    if (v === node.value) {
      node.copies += 1;
    }
  }
}

if (typeof module !== 'undefined' && module.parent) {
  module.exports = BinaryTree;
} else if (typeof window !== 'undefined') {
  //
} else {
  let s = [10, 2, 7, 9, 3, 1, 13, 17, 3, 5, 6, 9, 8, 11, 27];

  let t = new BinaryTree(s);
  console.dir(t._entry, {depth: null});
  console.log(t.toSortedList());

  s = 'EFGDCBAABCDEFGH';
  t = new BinaryTree(s);
  console.dir(t._entry, {depth: null});
}<commit_msg>simplified binary tree<commit_after>/**
 * Created by yangm11 on 8/30/2017.
 */
'use strict';

class BinaryTree {
  constructor(list) {
    list = list || [null];
    let entry = BinaryTree.createNode(list[0]);
    for (let i = 1; i < list.length; i++) {
      BinaryTree.insert(list[i], entry);
    }
    this._entry = entry;
  }

  toSortedList(node) {
    node = node || this._entry;
    if (node.left === null && node.right === null) {
      return BinaryTree.nodeToList(node);
    }
    if (node.left === null && node.right !== null) {
      return BinaryTree.nodeToList(node).concat(this.toSortedList(node.right));
    }
    if (node.left !== null && node.right === null) {
      return this.toSortedList(node.left).concat(BinaryTree.nodeToList(node));
    }
    if (node.left !== null && node.right !== null) {
      return this.toSortedList(node.left).concat(BinaryTree.nodeToList(node), this.toSortedList(node.right));
    }
  }

  static nodeToList(node) {
    let res = [];
    for (let i = 0; i < node.copies; i++) {
      res.push(node.value);
    }
    return res;
  }

  static createNode(v) {
    return {
      value: v,
      copies: 1,
      left: null,
      right: null
    }
  }

  static insert(v, node) {
    if (v > node.value) {
      if (node.right === null) {
        let t = BinaryTree.createNode(v);
        node.right = t;
        return t;
      } else {
        BinaryTree.insert(v, node.right);
      }
    }
    if (v < node.value) {
      if (node.left === null) {
        let t = BinaryTree.createNode(v);
        node.left = t;
        return t;
      } else {
        BinaryTree.insert(v, node.left);
      }
    }
    if (v === node.value) {
      node.copies += 1;
    }
  }
}

class Node {
  constructor(v) {
    if (!v) {
      throw 'a value expected';
    }
    this.value = v;
    this.left = null;
    this.right = null;
  }
  insert(v) {
    if (this.value > v) {
      if (this.left) {
        this.left.insert(v);
      } else {
        this.left = new Node(v);
      }
    } else {
      if (this.right) {
        this.right.insert(v);
      } else {
        this.right = new Node(v);
      }
    }
  }
  toArray() {
    if (!this.left && !this.right) {
      return [this.value];
    }
    if (this.left && !this.right) {
      return [...this.left.toArray(), this.value];
    }
    if (!this.left && this.right) {
      return [this.value, ...this.right.toArray()];
    }
    if (this.left && this.right) {
      return [...this.left.toArray(), this.value, ...this.right.toArray()];
    }
  }
}

if (typeof module !== 'undefined' && module.parent) {
  module.exports = BinaryTree;
} else if (typeof window !== 'undefined') {
  //
} else {
  let s = [10, 2, 7, 9, 3, 1, 13, 17, 3, 5, 6, 9, 8, 11, 27];

  // let t = new BinaryTree(s);
  // console.dir(t._entry, {depth: null});
  // console.log(t.toSortedList());
  //
  // s = 'EFGDCBAABCDEFGH';
  // t = new BinaryTree(s);
  // console.dir(t._entry, {depth: null});

  let n = new Node(10);
  for (let v of s) {
    n.insert(v);
  }
  console.log(n.toArray());

}<|endoftext|>


<commit_before>/**
 * Created by epotignano on 13/4/16.
 */

import React, { Component, PropTypes} from 'react';
import moment from 'moment';
import { connect } from "react-redux";
import _ from 'lodash';
import Loader from '../components/Loader';
import { GetAppointments, SelectNewDate, GetClosestAppointments } from '../actions/Appointments';

import AppointmentDayPicker from './AppointmentDayPicker';
import WeekDisplayer from './WeekDisplayer';

const currentYear = (new Date()).getFullYear();
const fromMonth = new Date(currentYear, 0, 1, 0, 0);
const toMonth = new Date(currentYear + 10, 11, 31, 23, 59);
import 'moment/locale/es';

//Pasar esto a un state si se requiere poder cambiar de idioma.
const locale = 'es';
function getFirstAppointmentAvailableForLocation(appointment, idLocalidad) {
  if(appointment){
    var _appointment = {'fecha_hora_inicio': moment(appointment)};
  }

  return _appointment;
}

function filterAppointmentsForWeek(selectedDay, appointments, range) {
  return _.filter(appointments, function ProcessAppointment(appointment) {
    if( (range.min.hour(0).minutes(0).seconds(0) <= moment(appointment.fecha_hora_inicio).utc())  &&  (moment(appointment.fecha_hora_inicio).utc() <= range.max.hour(23).minutes(59).seconds(59))) {
      return appointment
    }
  });
}

function smallerThanToday(day) {
    return new Date() >= day;
}

class AvailabilityDisplayer extends Component {

  constructor(props) {
    super(props);

    this.state = {
      displayDay : moment(),
      appointmentsForWeek : this.calculateAvailableAppointmentsForWeek(moment(), this.props.availability, true),
      selectedDate: moment()
    };
  }

  componentDidMount(){

  }

  componentWillMount(){

  }

  componentDidUpdate(){

  }

  renderWeekDisplayer(selectedDate, appointments){

    const { idLocalidad, institution, availability, appointment, doctor, doctorUsername, dispatch } = this.props;
    const { loadingAppointmentsForLocation, loadingAppointments } = appointment;

    if(loadingAppointmentsForLocation == idLocalidad || loadingAppointments){
      return (<Loader inverted={true} loaderText="Leyendo..."/>);
    }
    var _datesToUse;

    appointment.responsable_servicio.localidades.some((localidad) =>{
      if(idLocalidad == localidad.localidad.id){
        _datesToUse = localidad.turnos
      }
    });

    var _appointmentsForWeek = this.calculateAvailableAppointmentsForWeek(selectedDate, _datesToUse, true);
    if(!this.state.showClosestAppointment){
      if(_appointmentsForWeek.length) {
        return (<WeekDisplayer appointmentsForWeek={_appointmentsForWeek}
                         selectedDay={selectedDate}
                         month={this.state.month}
                         institution={institution}
                         idLocalidad={idLocalidad}
                         onDateChange={this.setNewDate.bind(this)}
                         doctorUsername={doctorUsername}
                         doctor={doctor}
                         languageJson={this.props.languageJson}/>

        )
      } else if(!appointment.loadingAppointments &&
        !appointment.loadingDoctorData &&
        !_appointmentsForWeek.length && appointment.readSuccess
      ) {

        GetClosestAppointments(appointment.responsable_servicio.id, idLocalidad, selectedDate).then((data) => {
          let _closest =  getFirstAppointmentAvailableForLocation(data.data, idLocalidad);
          this.setState({
            showClosestAppointment: true,
            closestAppointment : _closest,
            availability : _closest
          });
        });

        return (<Loader inverted={true} loaderText="Leyendo..."></Loader>)
      }
    }else{
      if(this.state.closestAppointment && this.state.closestAppointment.fecha_hora_inicio){
        return (

 {
            this.setNewDate(this.state.closestAppointment.fecha_hora_inicio)}}>

                  Próximo turno disponible {moment(this.state.closestAppointment.fecha_hora_inicio).format("dddd DD/MMMM/YYYY")}

          )

      } else {
        return (

                  No existen turnos disponibles para esta localidad

          )
      }
    }

  }

  calculateAvailableAppointmentsForWeek(selectedDate, appointments, initial) {
     const { dispatch } = this.props;
      let min = selectedDate;
      let max = moment(selectedDate).add(6, 'd');
      if(initial) {
        return filterAppointmentsForWeek(selectedDate, appointments ,{min, max})
      }
  }

  setNewDate(day) {
    var _day = moment(day);
    const { appointment, idLocalidad, dispatch } = this.props;

    this.setState({
      selectedDate : _day,
      month: _day.toDate(),
      showClosestAppointment: false
    });

    dispatch(SelectNewDate(_day, appointment.responsable_servicio.id, idLocalidad));
  }

  render() {
    const { availability, appointment, idLocalidad, doctor } = this.props;
    const { selectedDate } = this.state;
    // (this.state.closestAppointment && this.state.closestAppointment.fecha_hora_inicio)
    return(

              <AppointmentDayPicker
              onClick={this.setNewDate.bind(this)}
              month={this.state.month}
              selectedDate={this.state.selectedDate}/>

            { this.renderWeekDisplayer(this.state.selectedDate, availability)}

    )}

}

AvailabilityDisplayer.propTypes = {
  availability : PropTypes.any,
  institution: PropTypes.object,
  idLocalidad: PropTypes.any,
  doctorUsername: PropTypes.string,
  doctor: PropTypes.any,
  languageJson: PropTypes.any
};

AvailabilityDisplayer.contextTypes = {
  router : PropTypes.any
};

function mapStateToProps(state) {
  const { appointment } = state;
  return {
    appointment
  }
}

export default connect(mapStateToProps)(AvailabilityDisplayer);

//

//

//               { this.renderWeekDisplayer(this.state.selectedDate, availability)}
//

//

<commit_msg>Mas avances<commit_after>/**
 * Created by epotignano on 13/4/16.
 */

import React, { Component, PropTypes} from 'react';
import moment from 'moment';
import { connect } from "react-redux";
import _ from 'lodash';
import Loader from '../components/Loader';
import { GetAppointments, SelectNewDate, GetClosestAppointments } from '../actions/Appointments';

import AppointmentDayPicker from './AppointmentDayPicker';
import WeekDisplayer from './WeekDisplayer';

const currentYear = (new Date()).getFullYear();
const fromMonth = new Date(currentYear, 0, 1, 0, 0);
const toMonth = new Date(currentYear + 10, 11, 31, 23, 59);
import 'moment/locale/es';

//Pasar esto a un state si se requiere poder cambiar de idioma.
const locale = 'es';
function getFirstAppointmentAvailableForLocation(appointment, idLocalidad) {
  if(appointment){
    var _appointment = {'fecha_hora_inicio': moment(appointment)};
  }

  return _appointment;
}

function filterAppointmentsForWeek(selectedDay, appointments, range) {
  return _.filter(appointments, function ProcessAppointment(appointment) {
    if( (range.min.hour(0).minutes(0).seconds(0) <= moment(appointment.fecha_hora_inicio).utc())  &&  (moment(appointment.fecha_hora_inicio).utc() <= range.max.hour(23).minutes(59).seconds(59))) {
      return appointment
    }
  });
}

function smallerThanToday(day) {
    return new Date() >= day;
}

class AvailabilityDisplayer extends Component {

  constructor(props) {
    super(props);

    this.state = {
      displayDay : moment(),
      appointmentsForWeek : this.calculateAvailableAppointmentsForWeek(moment(), this.props.availability, true),
      selectedDate: moment()
    };
  }

  componentDidMount(){

  }

  componentWillMount(){

  }

  componentDidUpdate(){

  }

  renderWeekDisplayer(selectedDate, appointments){

    const { idLocalidad, institution, availability, appointment, doctor, doctorUsername, dispatch, user } = this.props;
    const { loadingAppointmentsForLocation, loadingAppointments } = appointment;

    if(loadingAppointmentsForLocation == idLocalidad || loadingAppointments){
      return (<Loader inverted={true} loaderText="Leyendo..."/>);
    }
    var _datesToUse;

    appointment.responsable_servicio.localidades.some((localidad) =>{
      if(idLocalidad == localidad.localidad.id){
        _datesToUse = localidad.turnos
      }
    });

    var _appointmentsForWeek = this.calculateAvailableAppointmentsForWeek(selectedDate, _datesToUse, true);
    if(!this.state.showClosestAppointment){
      if(_appointmentsForWeek.length) {
        return (<WeekDisplayer appointmentsForWeek={_appointmentsForWeek}
                         selectedDay={selectedDate}
                         month={this.state.month}
                         institution={institution}
                         idLocalidad={idLocalidad}
                         onDateChange={this.setNewDate.bind(this)}
                         doctorUsername={doctorUsername}
                         doctor={doctor}
                         languageJson={this.props.user.languageJson}/>

        )
      } else if(!appointment.loadingAppointments &&
        !appointment.loadingDoctorData &&
        !_appointmentsForWeek.length && appointment.readSuccess
      ) {

        GetClosestAppointments(appointment.responsable_servicio.id, idLocalidad, selectedDate).then((data) => {
          let _closest =  getFirstAppointmentAvailableForLocation(data.data, idLocalidad);
          this.setState({
            showClosestAppointment: true,
            closestAppointment : _closest,
            availability : _closest
          });
        });

        return (<Loader inverted={true} loaderText="Leyendo..."></Loader>)
      }
    }else{
      if(this.state.closestAppointment && this.state.closestAppointment.fecha_hora_inicio){
        return (

 {
            this.setNewDate(this.state.closestAppointment.fecha_hora_inicio)}}>

                    {this.props.user.languageJson.closest_available_appointment} {moment(this.state.closestAppointment.fecha_hora_inicio).format("dddd DD/MMMM/YYYY")}

          )

      } else {
        return (

                  Span

          )
      }
    }

  }

  calculateAvailableAppointmentsForWeek(selectedDate, appointments, initial) {
     const { dispatch } = this.props;
      let min = selectedDate;
      let max = moment(selectedDate).add(6, 'd');
      if(initial) {
        return filterAppointmentsForWeek(selectedDate, appointments ,{min, max})
      }
  }

  setNewDate(day) {
    var _day = moment(day);
    const { appointment, idLocalidad, dispatch } = this.props;

    this.setState({
      selectedDate : _day,
      month: _day.toDate(),
      showClosestAppointment: false
    });

    dispatch(SelectNewDate(_day, appointment.responsable_servicio.id, idLocalidad));
  }

  render() {
    const { availability, appointment, idLocalidad, doctor } = this.props;
    const { selectedDate } = this.state;
    // (this.state.closestAppointment && this.state.closestAppointment.fecha_hora_inicio)
    return(

              <AppointmentDayPicker
              onClick={this.setNewDate.bind(this)}
              month={this.state.month}
              selectedDate={this.state.selectedDate}/>

            { this.renderWeekDisplayer(this.state.selectedDate, availability)}

    )}

}

AvailabilityDisplayer.propTypes = {
  availability : PropTypes.any,
  institution: PropTypes.object,
  idLocalidad: PropTypes.any,
  doctorUsername: PropTypes.string,
  doctor: PropTypes.any,
  languageJson: PropTypes.any
};

AvailabilityDisplayer.contextTypes = {
  router : PropTypes.any
};

function mapStateToProps(state) {
  const { appointment, user } = state;
  return {
    appointment, user
  }
}

export default connect(mapStateToProps)(AvailabilityDisplayer);

//

//

//               { this.renderWeekDisplayer(this.state.selectedDate, availability)}
//

//

<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { Router } from 'react-router-dom'

const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'

class ConnectedRouter extends Component {
  constructor (props) {
    super(props)

    this.handleLocationChange = this.handleLocationChange.bind(this)
  }

  componentWillMount () {
    const { history } = this.props
    this.unsubscribe = history.listen(this.handleLocationChange)
    this.handleLocationChange(history.location)
  }

  componentWillUnmount () {
    this.unsubscribe()
  }

  handleLocationChange (location) {
    this.props.store.dispatch({
      type: LOCATION_CHANGE,
      payload: location
    })
  }

  render () {
    return <Router {...this.props} />
  }
}

export default ConnectedRouter
<commit_msg>Add scroll to top on router update<commit_after>import React, { Component } from 'react'
import { Router } from 'react-router-dom'

const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'

class ConnectedRouter extends Component {
  constructor (props) {
    super(props)

    this.handleLocationChange = this.handleLocationChange.bind(this)
  }

  componentWillMount () {
    const { history } = this.props
    this.unsubscribe = history.listen(this.handleLocationChange)
    this.handleLocationChange(history.location)
  }

  componentWillUnmount () {
    this.unsubscribe()
  }

  handleLocationChange (location) {
    this.props.store.dispatch({
      type: LOCATION_CHANGE,
      payload: location
    })
  }

  render () {
    return <Router
      onUpdate={() => window.scrollTo(0, 0)}
      {...this.props}
    />
  }
}

export default ConnectedRouter
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { Editor } from 'slate-react';
import { Value } from 'slate';
import Html from 'slate-html-serializer';
import rules from './rules';
import Button from '../Button';
import { ICONS } from '../../constants';
import Box from '../Box';

// plugin for tables https://github.com/GitbookIO/slate-edit-table

const html = new Html({ rules });

const DEFAULT_NODE = 'paragraph';
function unwrapLink(change) {
  change.unwrapInline('link');
}

function insertImage(change, src, target) {
  if (target) {
    change.select(target);
  }

  change.insertBlock({
    type: 'image',
    isVoid: true,
    data: { src },
  });
}
const getType = chars => {
  switch (chars) {
    case '*':
    case '-':
    case '+':
      return 'list-item';
    case '>':
      return 'block-quote';
    case '#':
      return 'heading-one';
    case '##':
      return 'heading-two';
    case '###':
      return 'heading-three';
    case '####':
      return 'heading-four';
    case '#####':
      return 'heading-five';
    case '######':
      return 'heading-six';
    default:
      return null;
  }
};

function wrapLink(change, href) {
  change.wrapInline({
    type: 'link',
    data: { href },
  });

  change.collapseToEnd();
}

class MainEditor extends React.Component {
  static propTypes = {
    onChange: PropTypes.func.isRequired,
    className: PropTypes.string,
  };
  static defaultProps = { className: null };
  constructor(props) {
    super(props);
    this.state = {
      value: Value.fromJSON({
        document: {
          nodes: [
            {
              object: 'block',
              type: 'paragraph',
              nodes: [
                {
                  object: 'text',
                  leaves: [
                    {
                      text: '',
                    },
                  ],
                },
              ],
            },
          ],
        },
      }),
    };

    this.onClickImage = this.onClickImage.bind(this);
  }

  componentDidMount() {
    // eslint-disable-next-line react/no-did-mount-set-state
    /*  this.setState({
      value: html.deserialize(this.props.initialValue),
    }); */
  }

  onChange = ({ value }) => {
    // TODO check if localstorage is available
    if (value.document !== this.state.value.document) {
      const string = html.serialize(value);
      this.props.onChange(string);
      // localStorage.setItem('content', string);
    }
    this.setState({ value });
  };

  onClickMark(event, type) {
    event.preventDefault();
    const { value } = this.state;
    const change = value.change().toggleMark(type);
    this.onChange(change);
  }
  onClickBlock(event, type) {
    event.preventDefault();
    const { value } = this.state;
    const change = value.change();
    const { document } = value;

    // Handle everything but list buttons.
    if (type !== 'bulleted-list' && type !== 'numbered-list') {
      const isActive = this.hasBlock(type);
      const isList = this.hasBlock('list-item');

      if (isList) {
        change
          .setBlocks(isActive ? DEFAULT_NODE : type)
          .unwrapBlock('bulleted-list')
          .unwrapBlock('numbered-list');
      } else {
        change.setBlocks(isActive ? DEFAULT_NODE : type);
      }
    } else {
      // Handle the extra wrapping required for list buttons.
      const isList = this.hasBlock('list-item');
      const isType = value.blocks.some(
        block =>
          !!document.getClosest(block.key, parent => parent.type === type),
      );

      if (isList && isType) {
        change
          .setBlocks(DEFAULT_NODE)
          .unwrapBlock('bulleted-list')
          .unwrapBlock('numbered-list');
      } else if (isList) {
        change
          .unwrapBlock(
            type === 'bulleted-list' ? 'numbered-list' : 'bulleted-list',
          )
          .wrapBlock(type);
      } else {
        change.setBlocks('list-item').wrapBlock(type);
      }
    }

    this.onChange(change);
  }

  onClickLink = event => {
    event.preventDefault();
    const { value } = this.state;
    const hasLinks = this.hasLinks();
    const change = value.change();

    if (hasLinks) {
      change.call(unwrapLink);
    } else if (value.isExpanded) {
      const href = window.prompt('Enter the URL of the link:', 'https://');
      if (href) {
        change.call(wrapLink, href);
      }
    } else {
      const href = window.prompt('Enter the URL of the link:', 'https://');
      const text = window.prompt('Enter the text for the link:');
      if (href && text) {
        change
          .insertText(text)
          .extend(0 - text.length)
          .call(wrapLink, href);
      }
    }

    this.onChange(change);
  };

  onKeyDown = (event, change) => {
    switch (event.key) {
      case ' ':
        return this.onSpace(event, change);
      case 'Backspace':
        return this.onBackspace(event, change);
      case 'Enter':
        return this.onEnter(event, change);
      default:
        return undefined;
    }
  };

  onSpace = (event, change) => {
    const { value } = change;
    if (value.isExpanded) return false;

    const { startBlock, startOffset } = value;
    const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '');
    const type = getType(chars);

    if (!type) return false;
    if (type === 'list-item' && startBlock.type === 'list-item') return false;
    event.preventDefault();

    change.setBlocks(type);

    if (type === 'list-item') {
      change.wrapBlock('bulleted-list');
    }

    change.extendToStartOf(startBlock).delete();
    return true;
  };

  onBackspace = (event, change) => {
    const { value } = change;
    if (value.isExpanded) return undefined;
    if (value.startOffset !== 0) return undefined;

    const { startBlock } = value;
    if (startBlock.type === 'paragraph') return undefined;

    event.preventDefault();
    change.setBlocks('paragraph');

    if (startBlock.type === 'list-item') {
      change.unwrapBlock('bulleted-list');
    }

    return true;
  };

  onEnter = (event, change) => {
    const { value } = change;
    if (value.isExpanded) return undefined;

    const { startBlock, startOffset, endOffset } = value;
    if (startOffset === 0 && startBlock.text.length === 0)
      return this.onBackspace(event, change);
    if (endOffset !== startBlock.text.length) return undefined;

    if (
      startBlock.type !== 'heading-one' &&
      startBlock.type !== 'heading-two' &&
      startBlock.type !== 'heading-three' &&
      startBlock.type !== 'heading-four' &&
      startBlock.type !== 'heading-five' &&
      startBlock.type !== 'heading-six' &&
      startBlock.type !== 'block-quote'
    ) {
      return undefined;
    }

    event.preventDefault();
    change.splitBlock().setBlocks('paragraph');
    return true;
  };

  onClickImage(event) {
    event.preventDefault();
    const src = window.prompt('Enter the URL of the image:');
    if (!src) return;

    const change = this.state.value.change().call(insertImage, src);

    this.onChange(change);
  }

  hasLinks = () => {
    const { value } = this.state;
    return value.inlines.some(inline => inline.type === 'link');
  };

  hasMark = type => {
    const { value } = this.state;
    return value.activeMarks.some(mark => mark.type === type);
  };
  hasBlock = type => {
    const { value } = this.state;
    return value.blocks.some(node => node.type === type);
  };

  reset() {
    this.setState({ value: html.deserialize('

') });
  }
  setInitialState(value) {
    this.setState({ value: html.deserialize(value) });
  }

  renderToolbar = () => (
    <Box>
      {this.renderMarkButton('bold')}
      {this.renderMarkButton('italic')}
      {this.renderMarkButton('link')}
      {this.renderMarkButton('underline')}
      {this.renderImageButton()}
      {/* {this.renderMarkButton('underline', 'format_underline')}
      {this.renderMarkButton('code', 'code')}
      {this.renderBlockButton('heading-one', 'looks_one')}
      {this.renderBlockButton('heading-two', 'looks_two')}
      {this.renderBlockButton('block-quote', 'format_quote')}
      {this.renderBlockButton('numbered-list', 'format_list_numbered')}
  {this.renderBlockButton('bulleted-list', 'format_list_bulleted')} */}
    </Box>
  );

  renderImageButton() {
    return (
      <Button
        plain
        onClick={this.onClickImage}
        icon={
          <svg
            viewBox="0 0 24 24"
            width="24px"
            height="24px"
            role="img"
            aria-label="link"
          >
            <path fill="none" stroke="#000" strokeWidth="2" d={ICONS.image} />
          </svg>
        }
      />
    );
  }

  renderMarkButton(type) {
    const onMouseDown = event => this.onClickMark(event, type);
    // const isActive = this.hasMark(type);

    switch (type) {
      case 'bold':
        return <Button onClick={onMouseDown} plain icon={A} />;
      case 'italic':
        return <Button onClick={onMouseDown} plain icon={A} />;
      case 'underline':
        return <Button onClick={onMouseDown} plain icon={U} />;
      case 'link':
        return (
          <Button
            plain
            onClick={this.onClickLink}
            icon={
              <svg
                viewBox="0 0 24 24"
                width="24px"
                height="24px"
                role="img"
                aria-label="link"
              >
                <path
                  fill="none"
                  stroke="#000"
                  strokeWidth="2"
                  d={ICONS.link}
                />
              </svg>
            }
          />
        );
      default:
        throw new Error(`Type not recognized: ${type}`);
    }
  }

  renderBlockButton(type, icon) {
    let isActive = this.hasBlock(type);

    if (['numbered-list', 'bulleted-list'].includes(type)) {
      const { value } = this.state;
      const parent = value.document.getParent(value.blocks.first().key);
      isActive = this.hasBlock('list-item') && parent && parent.type === type;
    }
    const onMouseDown = event => this.onClickBlock(event, type);
    /* eslint-disable jsx-a11y/no-static-element-interactions */

    // eslint-disable-next-line react/jsx-no-bind
    return (

        {icon}

    );
    /* eslint-enable jsx-a11y/no-static-element-interactions */
  }
  /*
  onPaste = (event, change) => {
    if (change.value.isCollapsed) return;

    const transfer = getEventTransfer(event);
    const { type, text } = transfer;
    if (type != 'text' && type != 'html') return;
    if (!isUrl(text)) return;

    if (this.hasLinks()) {
      change.call(unwrapLink);
    }

    change.call(wrapLink, text);
    return true;
  };
*/
  /* eslint-disable class-methods-use-this  */
  renderMark(props) {
    const { children, mark, attributes } = props;
    switch (mark.type) {
      case 'bold':
        return {children};
      case 'code':
        return {children};
      case 'italic':
        return {children};
      case 'underline':
        return {children};
      default:
        throw new Error(`Type not recognized: ${mark.type}`);
    }
  }

  renderNode = props => {
    const { attributes, children, node } = props;
    switch (node.type) {
      case 'paragraph':
        return
{children}
;
      case 'block-quote':
        return
{children}
;
      case 'bulleted-list':
        return
{children}
;
      case 'heading-one':
        return
{children}
;
      case 'heading-two':
        return
{children}
;
      case 'heading-three':
        return
{children}
;
      case 'heading-four':
        return
{children}
;
      case 'heading-five':
        return
{children}
;
      case 'heading-six':
        return
{children}
;
      case 'list-item':
        return
{children}
;
      case 'numbered-list':
        return
{children}
;
      case 'link': {
        const { data } = node;
        const href = data.get('href');
        return (

            {children}

        );
      }
      case 'image': {
        const src = node.data.get('src');
        const className = ''; // isSelected ? 'active' : null;
        const style = { display: 'block', maxWidth: '100%', margin: '0 auto' };
        return (

        );
      }
      default:
        throw new Error(`Type not recognized: ${node.type}`);
    }
  };

  /* eslint-enable class-methods-use-this */
  renderEditor() {
    return (
      <Editor
        placeholder="Enter some text..."
        value={this.state.value}
        onChange={this.onChange}
        onKeyDown={this.onKeyDown}
        renderNode={this.renderNode}
        renderMark={this.renderMark}
        autoFocus
      />
    );
  }

  render() {
    return (

        {this.renderToolbar()}
        {this.renderEditor()}

    );
  }
}

export default MainEditor;
<commit_msg>fixed some  breaking changes of slatejs<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { Editor } from 'slate-react';
import { Value } from 'slate';
import Html from 'slate-html-serializer';
import rules from './rules';
import Button from '../Button';
import { ICONS } from '../../constants';
import Box from '../Box';

// plugin for tables https://github.com/GitbookIO/slate-edit-table

const html = new Html({ rules });

const DEFAULT_NODE = 'paragraph';
function unwrapLink(change) {
  change.unwrapInline('link');
}

function insertImage(change, src, target) {
  if (target) {
    change.select(target);
  }

  change.insertBlock({
    type: 'image',
    isVoid: true,
    data: { src },
  });
}

function wrapLink(change, href) {
  change.wrapInline({ type: 'link', data: { href } });

  change.moveToEnd();
}

class MainEditor extends React.Component {
  static propTypes = {
    onChange: PropTypes.func.isRequired,
    className: PropTypes.string,
  };

  static defaultProps = { className: null };

  constructor(props) {
    super(props);
    this.state = {
      value: Value.fromJSON({
        document: {
          nodes: [
            {
              object: 'block',
              type: 'paragraph',
              nodes: [
                {
                  object: 'text',
                  leaves: [
                    {
                      text: '',
                    },
                  ],
                },
              ],
            },
          ],
        },
      }),
    };

    this.onClickImage = this.onClickImage.bind(this);
  }

  componentDidMount() {
    // eslint-disable-next-line react/no-did-mount-set-state
    /*  this.setState({
      value: html.deserialize(this.props.initialValue),
    }); */
  }

  onChange = ({ value }) => {
    // TODO check if localstorage is available
    const { onChange } = this.props;
    // eslint-disable-next-line
    if (value.document !== this.state.value.document) {
      const string = html.serialize(value);
      onChange(string);
      // localStorage.setItem('content', string);
    }
    this.setState({ value });
  };

  onClickMark(event, type) {
    event.preventDefault();
    this.editor.change(change => {
      const { value } = change;
      value.change().toggleMark(type);
    });
  }

  onClickBlock(event, type) {
    event.preventDefault();

    this.editor.change(change => {
      const { value } = change;
      const { document } = value;
      // Handle everything but list buttons.
      if (type !== 'bulleted-list' && type !== 'numbered-list') {
        const isActive = this.hasBlock(type);
        const isList = this.hasBlock('list-item');

        if (isList) {
          change
            .setBlocks(isActive ? DEFAULT_NODE : type)
            .unwrapBlock('bulleted-list')
            .unwrapBlock('numbered-list');
        } else {
          change.setBlocks(isActive ? DEFAULT_NODE : type);
        }
      } else {
        // Handle the extra wrapping required for list buttons.
        const isList = this.hasBlock('list-item');
        const isType = value.blocks.some(
          block =>
            !!document.getClosest(block.key, parent => parent.type === type),
        );

        if (isList && isType) {
          change
            .setBlocks(DEFAULT_NODE)
            .unwrapBlock('bulleted-list')
            .unwrapBlock('numbered-list');
        } else if (isList) {
          change
            .unwrapBlock(
              type === 'bulleted-list' ? 'numbered-list' : 'bulleted-list',
            )
            .wrapBlock(type);
        } else {
          change.setBlocks('list-item').wrapBlock(type);
        }
      }
    });
  }

  onClickLink = event => {
    event.preventDefault();

    this.editor.change(change => {
      const { value } = change;
      const hasLinks = this.hasLinks();
      if (hasLinks) {
        change.call(unwrapLink);
      } else if (value.selection.isExpanded) {
        const href = window.prompt('Enter the URL of the link:', 'https://');
        if (href) {
          change.call(wrapLink, href);
        }
      } else {
        const href = window.prompt('Enter the URL of the link:', 'https://');
        if (href === null) {
          return;
        }
        const text = window.prompt('Enter the text for the link:');
        if (text === null) {
          return;
        }
        change
          .insertText(text)
          .moveFocusBackward(text.length)
          .call(wrapLink, href);
      }
    });
  };

  onKeyDown = (event, change, next) => {
    switch (event.key) {
      case ' ':
        return this.onSpace(event, change, next);
      case 'Backspace':
        return this.onBackspace(event, change, next);
      case 'Enter':
        return this.onEnter(event, change, next);
      default:
        return next();
    }
  };

  onSpace = (event, change, next) => {
    const { value } = change;
    const { selection } = value;
    if (value.isExpanded) return next();

    const { startBlock } = value;
    const { start } = selection;
    const chars = startBlock.text.slice(0, start.offset).replace(/\s*/g, '');
    const type = this.getType(chars);

    if (!type) return next();
    if (type === 'list-item' && startBlock.type === 'list-item') return next();
    event.preventDefault();

    change.setBlocks(type);

    if (type === 'list-item') {
      change.wrapBlock('bulleted-list');
    }

    change.moveFocusToStartOfNode(startBlock).delete();
    return next();
  };

  onBackspace = (event, change, next) => {
    const { value } = change;
    const { selection } = value;
    if (selection.isExpanded) return next();
    if (selection.start.offset !== 0) return next();

    const { startBlock } = value;
    if (startBlock.type === 'paragraph') return next();

    event.preventDefault();
    change.setBlocks('paragraph');

    if (startBlock.type === 'list-item') {
      change.unwrapBlock('bulleted-list');
    }
    return next();
  };

  onEnter = (event, change, next) => {
    const { value } = change;
    const { selection } = value;
    const { start, end, isExpanded } = selection;
    if (isExpanded) return next();

    const { startBlock } = value;
    if (start.offset === 0 && startBlock.text.length === 0)
      return this.onBackspace(event, change, next);
    if (end.offset !== startBlock.text.length) return next();

    if (
      startBlock.type !== 'heading-one' &&
      startBlock.type !== 'heading-two' &&
      startBlock.type !== 'heading-three' &&
      startBlock.type !== 'heading-four' &&
      startBlock.type !== 'heading-five' &&
      startBlock.type !== 'heading-six' &&
      startBlock.type !== 'block-quote'
    ) {
      return next();
    }

    event.preventDefault();
    change.splitBlock().setBlocks('paragraph');
    return next();
  };

  onClickImage(event) {
    event.preventDefault();
    const src = window.prompt('Enter the URL of the image:');
    if (!src) return;

    this.editor.change(insertImage, src);
  }

  getType = chars => {
    switch (chars) {
      case '*':
      case '-':
      case '+':
        return 'list-item';
      case '>':
        return 'block-quote';
      case '#':
        return 'heading-one';
      case '##':
        return 'heading-two';
      case '###':
        return 'heading-three';
      case '####':
        return 'heading-four';
      case '#####':
        return 'heading-five';
      case '######':
        return 'heading-six';
      default:
        return null;
    }
  };

  hasLinks = () => {
    const { value } = this.state;
    return value.inlines.some(inline => inline.type === 'link');
  };

  hasMark = type => {
    const { value } = this.state;
    return value.activeMarks.some(mark => mark.type === type);
  };

  hasBlock = type => {
    const { value } = this.state;
    return value.blocks.some(node => node.type === type);
  };

  ref = editor => {
    this.editor = editor;
  };

  reset() {
    this.setState({ value: html.deserialize('

') });
  }

  setInitialState(value) {
    this.setState({ value: html.deserialize(value) });
  }

  renderToolbar = () => (
    <Box>
      {this.renderMarkButton('bold')}
      {this.renderMarkButton('italic')}
      {this.renderMarkButton('link')}
      {this.renderMarkButton('underline')}
      {this.renderImageButton()}
      {/* {this.renderMarkButton('underline', 'format_underline')}
      {this.renderMarkButton('code', 'code')}
      {this.renderBlockButton('heading-one', 'looks_one')}
      {this.renderBlockButton('heading-two', 'looks_two')}
      {this.renderBlockButton('block-quote', 'format_quote')}
      {this.renderBlockButton('numbered-list', 'format_list_numbered')}
  {this.renderBlockButton('bulleted-list', 'format_list_bulleted')} */}
    </Box>
  );

  renderImageButton() {
    return (
      <Button
        plain
        onClick={this.onClickImage}
        icon={
          <svg
            viewBox="0 0 24 24"
            width="24px"
            height="24px"
            role="img"
            aria-label="link"
          >
            <path fill="none" stroke="#000" strokeWidth="2" d={ICONS.image} />
          </svg>
        }
      />
    );
  }

  renderMarkButton(type) {
    const onMouseDown = event => this.onClickMark(event, type);
    // const isActive = this.hasMark(type);

    switch (type) {
      case 'bold':
        return <Button onClick={onMouseDown} plain icon={A} />;
      case 'italic':
        return <Button onClick={onMouseDown} plain icon={A} />;
      case 'underline':
        return <Button onClick={onMouseDown} plain icon={U} />;
      case 'link':
        return (
          <Button
            plain
            onClick={this.onClickLink}
            icon={
              <svg
                viewBox="0 0 24 24"
                width="24px"
                height="24px"
                role="img"
                aria-label="link"
              >
                <path
                  fill="none"
                  stroke="#000"
                  strokeWidth="2"
                  d={ICONS.link}
                />
              </svg>
            }
          />
        );
      default:
        throw new Error(`Type not recognized: ${type}`);
    }
  }

  renderBlockButton(type, icon) {
    let isActive = this.hasBlock(type);

    if (['numbered-list', 'bulleted-list'].includes(type)) {
      const { value } = this.state;
      const parent = value.document.getParent(value.blocks.first().key);
      isActive = this.hasBlock('list-item') && parent && parent.type === type;
    }
    const onMouseDown = event => this.onClickBlock(event, type);
    /* eslint-disable jsx-a11y/no-static-element-interactions */

    // eslint-disable-next-line react/jsx-no-bind
    return (

        {icon}

    );
    /* eslint-enable jsx-a11y/no-static-element-interactions */
  }
  /*
  onPaste = (event, change) => {
    if (change.value.isCollapsed) return;

    const transfer = getEventTransfer(event);
    const { type, text } = transfer;
    if (type != 'text' && type != 'html') return;
    if (!isUrl(text)) return;

    if (this.hasLinks()) {
      change.call(unwrapLink);
    }

    change.call(wrapLink, text);
    return true;
  };
*/
  /* eslint-disable class-methods-use-this  */

  renderMark(props, next) {
    const { children, mark, attributes } = props;
    switch (mark.type) {
      case 'bold':
        return {children};
      case 'code':
        return {children};
      case 'italic':
        return {children};
      case 'underline':
        return {children};
      default:
        return next;
    }
  }

  renderNode = (props, next) => {
    const { attributes, children, node } = props;
    switch (node.type) {
      case 'paragraph':
        return
{children}
;
      case 'block-quote':
        return
{children}
;
      case 'bulleted-list':
        return
{children}
;
      case 'heading-one':
        return
{children}
;
      case 'heading-two':
        return
{children}
;
      case 'heading-three':
        return
{children}
;
      case 'heading-four':
        return
{children}
;
      case 'heading-five':
        return
{children}
;
      case 'heading-six':
        return
{children}
;
      case 'list-item':
        return
{children}
;
      case 'numbered-list':
        return
{children}
;
      case 'link': {
        const { data } = node;
        const href = data.get('href');
        return (

            {children}

        );
      }
      case 'image': {
        const src = node.data.get('src');
        const className = ''; // isSelected ? 'active' : null;
        const style = { display: 'block', maxWidth: '100%', margin: '0 auto' };
        return (

        );
      }
      default:
        return next();
    }
  };

  /* eslint-enable class-methods-use-this */
  renderEditor() {
    return (
      <Editor
        placeholder="Enter some text..."
        value={this.state.value} // eslint-disable-line
        ref={this.ref}
        onChange={this.onChange}
        onKeyDown={this.onKeyDown}
        renderNode={this.renderNode}
        renderMark={this.renderMark}
        autoFocus
      />
    );
  }

  render() {
    const { className } = this.props;
    return (

        {this.renderToolbar()}
        {this.renderEditor()}

    );
  }
}

export default MainEditor;
<|endoftext|>


<commit_before>( function( window, define, requirejs, undefined ) {
    'use strict';

    define( [
        'jquery'
    ], function( $ ) {

        describe( 'Base Module', function() {

            before( function() {
                requirejs.undef( 'utils/module' );
            } );

            afterEach( function() {
                requirejs.undef( 'utils/module' );
            } );

            it( 'should extend from an object literal', function( done ) {

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( {
                            test: 'test',
                            testFunction: function() {}
                        } ),
                        instance = new FakeClass();

                    expect( instance ).to.have.property( 'test' );
                    expect( instance.testFunction ).to.be.a( 'function' );
                    done();
                } );

            } );

            it( 'should call the default constructor methods', function( done ) {
                var fakeModule = {
                        ready: function() {},
                        events: function() {}
                    },
                    mock = sinon.mock( fakeModule );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        instance = new FakeClass();

                    mock.expects( 'ready' ).once();
                    mock.expects( 'events' ).once();
                    done();
                } );
            } );

            it( 'should call custom constructor methods', function( done ) {
                var fakeModule = {
                        constructors: [ 'testConstructor' ],
                        testConstructor: function() {}
                    },
                    mock = sinon.mock( fakeModule );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        instance = new FakeClass();

                    mock.expects( 'testConstructor' ).once();
                    done();
                } );
            } );

            it( 'should call constructor methods with arguments', function( done ) {
                var fakeModule = {
                        ready: function() {}
                    },
                    mock = sinon.mock( fakeModule ),
                    testArgumentOne = 'test',
                    testArgumentTwo = {};

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        instance = new FakeClass( testArgumentOne, testArgumentTwo );

                    mock.expects( 'ready' ).once().withArgs( testArgumentOne, testArgumentTwo );
                    done();
                } );
            } );

            it( 'should extend an existing class', function( done ) {
                var fakeModule = {
                        ready: function() {},
                        test: function() {}
                    },
                    fakeExtension = {
                        ready: function() {}
                    };

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        SecondClass = FakeClass.extend( fakeExtension );

                    expect( FakeClass ).not.to.be.equal( SecondClass );
                    expect( FakeClass.prototype.ready ).not.to.be.equal( SecondClass.prototype.ready );
                    expect( FakeClass.prototype.test ).to.be.equal( SecondClass.prototype.test );

                    done();
                } );
            } );

            it( 'should trigger an event before initialized', function( done ) {
                var fakeModule = {
                        ready: function() {}
                    },
                    fakeElement = $( '

' );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule );

                    fakeElement.one( 'beforeInit', function() {
                        done();
                    } );

                    var instance = new FakeClass( fakeElement );
                } );
            } );

            it( 'should trigger an event after being initialized', function( done ) {
                var fakeModule = {
                        ready: function() {}
                    },
                    fakeElement = $( '

' );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule );

                    fakeElement.one( 'afterInit', function() {
                        done();
                    } );

                    var instance = new FakeClass( fakeElement );
                } );
            } );

        } );

    } );

} )( this, this.define, this.requirejs );
<commit_msg>Add some specs to base module<commit_after>( function( window, define, requirejs, undefined ) {
    'use strict';

    define( [
        'jquery'
    ], function( $ ) {

        describe( 'Base Module', function() {

            before( function() {
                requirejs.undef( 'utils/module' );
            } );

            afterEach( function() {
                requirejs.undef( 'utils/module' );
            } );

            it( 'should extend from an object literal', function( done ) {

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( {
                            test: 'test',
                            testFunction: function() {}
                        } ),
                        instance = new FakeClass();

                    expect( instance ).to.have.property( 'test' );
                    expect( instance.testFunction ).to.be.a( 'function' );
                    done();
                } );

            } );

            it( 'should call the default constructor methods', function( done ) {
                var fakeModule = {
                        ready: function() {},
                        events: function() {}
                    },
                    mock = sinon.mock( fakeModule );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        instance = new FakeClass();

                    mock.expects( 'ready' ).once();
                    mock.expects( 'events' ).once();
                    done();
                } );
            } );

            it( 'should call custom constructor methods', function( done ) {
                var fakeModule = {
                        constructors: [ 'testConstructor' ],
                        testConstructor: function() {}
                    },
                    mock = sinon.mock( fakeModule );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        instance = new FakeClass();

                    mock.expects( 'testConstructor' ).once();
                    done();
                } );
            } );

            it( 'should call constructor methods with arguments', function( done ) {
                var fakeModule = {
                        ready: function() {}
                    },
                    mock = sinon.mock( fakeModule ),
                    testArgumentOne = 'test',
                    testArgumentTwo = {};

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        instance = new FakeClass( testArgumentOne, testArgumentTwo );

                    mock.expects( 'ready' ).once().withArgs( testArgumentOne, testArgumentTwo );
                    done();
                } );
            } );

            it( 'should extend an existing class', function( done ) {
                var fakeModule = {
                        ready: function() {},
                        test: function() {}
                    },
                    fakeExtension = {
                        ready: function() {}
                    };

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        SecondClass = FakeClass.extend( fakeExtension );

                    expect( FakeClass ).not.to.be.equal( SecondClass );
                    expect( FakeClass.prototype.ready ).not.to.be.equal( SecondClass.prototype.ready );
                    expect( FakeClass.prototype.test ).to.be.equal( SecondClass.prototype.test );

                    done();
                } );
            } );

            it( 'should be named "module" by default', function ( done ) {
                var fakeModule = {};

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        instance = new FakeClass();

                    expect( instance.name ).to.be.equal( 'module' );
                    done();
                } );
            } );

            it( 'should have a name given in the constructor', function( done ) {
                var fakeModule = {},
                    testname = 'test';

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule ),
                        instance = new FakeClass( null, {}, testname);

                    expect( instance.name ).to.be.equal( testname );
                    done();
                } );
            } );

            it( 'should trigger an event before initialized', function( done ) {
                var fakeModule = {
                        ready: function() {}
                    },
                    fakeElement = $( '

' );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule );

                    fakeElement.one( 'beforeInit', function() {
                        done();
                    } );

                    var instance = new FakeClass( fakeElement );
                } );
            } );

            it( 'should trigger a namespaced event before initialized', function( done ) {
                var fakeModule = {
                        ready: function() {}
                    },
                    fakeModuleName = 'test',
                    fakeElement = $( '

' );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule );

                    fakeElement.one( 'beforeInit.' + fakeModuleName, function() {
                        done();
                    } );

                    var instance = new FakeClass( fakeElement, {}, fakeModuleName );
                } );
            } );

            it( 'should trigger an event after being initialized', function( done ) {
                var fakeModule = {
                        ready: function() {}
                    },
                    fakeElement = $( '

' );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule );

                    fakeElement.one( 'afterInit', function() {
                        done();
                    } );

                    var instance = new FakeClass( fakeElement );
                } );
            } );

            it( 'should trigger a namespaced event after being initialized', function( done ) {
                var fakeModule = {
                        ready: function() {}
                    },
                    fakeModuleName = 'test',
                    fakeElement = $( '

' );

                require( [ 'utils/module' ], function( Module ) {
                    var FakeClass = Module.extend( fakeModule );

                    fakeElement.one( 'afterInit.' + fakeModuleName, function() {
                        done();
                    } );

                    var instance = new FakeClass( fakeElement, {}, fakeModuleName );
                } );
            } );

        } );

    } );

} )( this, this.define, this.requirejs );
<|endoftext|>


<commit_before>/*eslint no-unused-vars:0*/
'use strict'
import React, { Component } from 'react'
import classNames from 'classnames'
import { Link } from 'react-router'
import { authorTypes, LINK_PREFIX } from '../../constants/index'
import commonStyles from './Common.scss'
import styles from './HeadingAuthor.scss'

// lodash
import forIn from 'lodash/forIn'
import groupBy from 'lodash/groupBy'
import map from 'lodash/map'

export const HeadingAuthor = ({ authors, children, extendByline }) => {

  function _groupAuthor(authors) {
    return groupBy(authors, 'type')
  }

  function _renderAuthor(author, key) {
    return (

        <Link to={`${LINK_PREFIX.AUTHOR}${author.id}`}>
          {author.name}
        </Link>

      )
  }

  function _renderAuthors(authors) {
    return map(authors, (author, index) => _renderAuthor(author, index))
  }

  const groupedAuthors = _groupAuthor(authors)
  let count = 0
  const authorRows = []
  forIn(groupedAuthors, (authors, type) => {
    let _authors = _renderAuthors(authors)
    authorRows.push(

        {authorTypes[type]}
        {_authors}

    )
  })

  return (

      { authorRows }
      {extendByline}
      { children }

  )
}
<commit_msg>Display author title in articles<commit_after>/*eslint no-unused-vars:0*/
'use strict'

import { LINK_PREFIX, authorTypes } from '../../constants/index'
import React, { Component } from 'react'

import { Link } from 'react-router'
import classNames from 'classnames'
import commonStyles from './Common.scss'
// lodash
import forIn from 'lodash/forIn'
import groupBy from 'lodash/groupBy'
import map from 'lodash/map'
import styles from './HeadingAuthor.scss'

export const HeadingAuthor = ({ authors, children, extendByline }) => {

  function _groupAuthor(authors) {
    return groupBy(authors, 'type')
  }

  function _renderAuthor(author, key) {
    const title = author.jobTitle ? `（${author.jobTitle}）` : ''
    return (

        <Link to={`${LINK_PREFIX.AUTHOR}${author.id}`}>
          {author.name+title}
        </Link>

      )
  }

  function _renderAuthors(authors) {
    return map(authors, (author, index) => _renderAuthor(author, index))
  }

  const groupedAuthors = _groupAuthor(authors)
  let count = 0
  const authorRows = []
  forIn(groupedAuthors, (authors, type) => {
    let _authors = _renderAuthors(authors)
    authorRows.push(

        {authorTypes[type]}
        {_authors}

    )
  })

  return (

      { authorRows }
      {extendByline}
      { children }

  )
}
<|endoftext|>


<commit_before>import React, { useState, useEffect } from 'react'
import styled from 'styled-components'

import Popup from './popup'
import { Button } from '../formElements'

import keepItSecret from '../../icons/keepitsecret.png'

const Container = styled.form`
  display: flex;
  flex-direction: column;
  text-align: center;
`

const Text = styled.p`
  line-height: 1.5;
`

const KeysList = styled.ul`
  font-family: monospace;
  list-style: none;
  padding: 0px;

  & > li {
    line-height: 2;

    & > span {
      padding: .3em;
      background-color: rgba(0, 0, 0, .1);
      border-radius: .3em;
    }
  }
`

const DownloadLink = styled(Button)`
  display: block;
  text-decoration: none;
  margin-bottom: 1em;
`

const GandalfImg = styled.img`
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 1em;
`

export default function ResetKeysPopup ({ resetKeys, onClose }) {
  const [fileURL, setFileURL] = useState('')
  useEffect(() => {
    const fileText = resetKeys.reduce((sum, key) => `${sum}\r\n${key}`) + '\r\n'

    const blob = new window.Blob([fileText], { type: 'text/plain' })
    const objURL = window.URL.createObjectURL(blob)

    setFileURL(objURL)
    return () => {
      window.URL.revokeObjectURL(objURL)
    }
  }, resetKeys)

  return <Popup title='Password reset keys' onClose={onClose}>
    <Container>
      <Text>
        Those are your encryption reset-keys.

        You need them, when you did forget your encryption-password!

        Please save them!

        Save them some where secure!

        There is no other way to get your data back!
      </Text>

      <KeysList>
        {resetKeys.map((key, index) => <li key={`reset-key-${index}`}>
          {key}

)}
      </KeysList>

You can also download them:

      <DownloadLink
        className='primary'
        as='a'
        href={fileURL}
        download='andromeda-viewer-reset-keys.txt'
      >
        Download as a file
      </DownloadLink>

      <GandalfImg
        src={keepItSecret}
        height='200'
        width='200'
        alt='Gandalf saying: Keep it secret, keep it safe!'
      />

      <Text>
        Remember: If you lose your encryption password and the reset-keys, you lose your data!
      </Text>

      <Button className='ok' onClick={onClose}>OK, I did save them!</Button>
    </Container>
  </Popup>
}
<commit_msg>fix(resetKeysPopup): change resetKeys back to be in dependency-array<commit_after>import React, { useState, useEffect } from 'react'
import styled from 'styled-components'

import Popup from './popup'
import { Button } from '../formElements'

import keepItSecret from '../../icons/keepitsecret.png'

const Container = styled.form`
  display: flex;
  flex-direction: column;
  text-align: center;
`

const Text = styled.p`
  line-height: 1.5;
`

const KeysList = styled.ul`
  font-family: monospace;
  list-style: none;
  padding: 0px;

  & > li {
    line-height: 2;

    & > span {
      padding: .3em;
      background-color: rgba(0, 0, 0, .1);
      border-radius: .3em;
    }
  }
`

const DownloadLink = styled(Button)`
  display: block;
  text-decoration: none;
  margin-bottom: 1em;
`

const GandalfImg = styled.img`
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 1em;
`

export default function ResetKeysPopup ({ resetKeys, onClose }) {
  const [fileURL, setFileURL] = useState('')
  useEffect(() => {
    const fileText = resetKeys.reduce((sum, key) => `${sum}\r\n${key}`) + '\r\n'

    const blob = new window.Blob([fileText], { type: 'text/plain' })
    const objURL = window.URL.createObjectURL(blob)

    setFileURL(objURL)
    return () => {
      window.URL.revokeObjectURL(objURL)
    }
  }, [resetKeys])

  return <Popup title='Password reset keys' onClose={onClose}>
    <Container>
      <Text>
        Those are your encryption reset-keys.

        You need them, when you did forget your encryption-password!

        Please save them!

        Save them some where secure!

        There is no other way to get your data back!
      </Text>

      <KeysList>
        {resetKeys.map((key, index) => <li key={`reset-key-${index}`}>
          {key}

)}
      </KeysList>

You can also download them:

      <DownloadLink
        className='primary'
        as='a'
        href={fileURL}
        download='andromeda-viewer-reset-keys.txt'
      >
        Download as a file
      </DownloadLink>

      <GandalfImg
        src={keepItSecret}
        height='200'
        width='200'
        alt='Gandalf saying: Keep it secret, keep it safe!'
      />

      <Text>
        Remember: If you lose your encryption password and the reset-keys, you lose your data!
      </Text>

      <Button className='ok' onClick={onClose}>OK, I did save them!</Button>
    </Container>
  </Popup>
}
<|endoftext|>