<commit_before>/**
 * Created by ksinger on 19.02.2016.
 */

import  won from '../won-es6.js';
import Immutable from 'immutable';
import jsonld from 'jsonld'; //import *after* the rdfstore to shadow its custom jsonld

import {
    selectAllByConnectionUri,
    selectOpenConnectionUri,
    selectRemoteEvents,
} from '../selectors.js';

import {
    is,
    urisToLookupMap,
    msStringToDate,
    getIn,
    get,
    jsonld2simpleFormat,
    cloneAsMutable,
} from '../utils.js';

import {
   makeParams,
} from '../configRouting.js';

import {
    actionTypes,
    actionCreators,
} from './actions.js';

import {
    buildOpenMessage,
    buildCloseMessage,
    buildChatMessage,
    buildRateMessage,
    buildConnectMessage,
    getEventsFromMessage,
} from '../won-message-utils.js';

export function connectionsChatMessage(chatMessage, connectionUri) {
   return dispatch => {
       console.log('connectionsChatMessage: ', chatMessage, connectionUri);

       buildChatMessage(chatMessage, connectionUri)
       .then(msgData => {

           /*
            * not sure how elegant it is to build the ld and then parse
            * it again. It uses existing utilities at least, reducing
            * redundant program logic. ^^
             */
           const optimisticEventPromise = getEventsFromMessage(msgData.message)
               .then(optimisticEvent => optimisticEvent['msg:FromOwner']);

           return Promise.all([
               Promise.resolve(msgData.message),
               optimisticEventPromise,
           ]);
       })
       .then( ([ message, optimisticEvent ]) => dispatch({
               type: actionTypes.connections.sendChatMessage,
               payload: {
                   eventUri: optimisticEvent.uri,
                   message,
                   optimisticEvent,
               }
           })
       )
   }
}

export function connectionsFetch(data) {
    return dispatch=> {
        const allConnectionsPromise = won.executeCrawlableQuery(won.queries["getAllConnectionUrisOfNeed"], data.needUri);
        allConnectionsPromise.then(function (connections) {
            console.log("fetching connections");
            dispatch(actionCreators.needs__connectionsReceived({needUri: data.needUri, connections: connections}));
            dispatch(actionCreators.events__fetch({connectionUris: connections}))
        })
    }
}

export function connectionsOpen(connectionUri, message) {
    return (dispatch, getState) => {
        buildOpenMessage(connectionUri, message)
        .then(msgData => {
            const optimisticEventPromise = getEventsFromMessage(msgData.message)
                .then(optimisticEvent => optimisticEvent['msg:FromOwner']);
            return Promise.all([
                Promise.resolve(msgData),
                optimisticEventPromise,
            ]);

            //TODO dispatch connections.open
        })
        .then( ([ msgData, optimisticEvent ]) => {
            // dispatch(actionCreators.messages__send(messageData));
            dispatch({
                type: actionTypes.connections.open,
                payload: {
                    eventUri: optimisticEvent.uri,
                    message: msgData.message,
                    optimisticEvent,
                }
            });

            dispatch(actionCreators.router__stateGoAbs("post", {
                postUri: optimisticEvent.hasSenderNeed,
                connectionType: won.WON.Connected,
                connectionUri: optimisticEvent.hasSender,
            }));

        });
    }
}

export function connectionsConnect(connectionUri,message) {
    return (dispatch, getState) => {
        const state = getState();
        const eventData = selectAllByConnectionUri(state, connectionUri).toJS(); // TODO avoid toJS; UPDATE TO NEW STRUCTURE
        const messageDataP = won
            .getConnectionWithEventUris(eventData.connection.uri)
            .then(connection=> {
                let msgToOpenFor = {event: eventData, connection: connection};
                return buildConnectMessage(msgToOpenFor, message)
            });
        messageDataP.then((action)=> {
            dispatch(actionCreators.messages__send({eventUri: action.eventUri, message: action.message}));

            jsonld.promises.frame(
                action.message,
                {
                    '@id': action.eventUri,
                    '@context': action.message['@context']
                }
            ).then(framed => {
                let event = getIn(framed, ['@graph', 0]);
                if(event) {
                    event['@context'] = framed['@context']; // context is needed by jsonld2simpleFormat for expanding prefixes in values
                    event = jsonld2simpleFormat(event);
                }

                dispatch({
                    type: actionTypes.connections.connect,
                    payload: {
                        connectionUri,
                        message,
                        eventUri: action.eventUri,
                        optimisticEvent: event,
                    }
                });
            });
        })
    }
}

export function connectionsConnectAdHoc(theirNeedUri) {
    return (dispatch, getState) => {
        const state = getState();

        const theirNeed = getIn(state, ['needs', theirNeedUri]);
        const adHocDraft = generateResponseNeedTo(theirNeed);

        dispatch(actionCreators.needs__create(adHocDraft))
        .then(() => {
            console.log('STARTED PUBLISHING AD HOC DRAFT');

        });

        //TODO: CREATE COUNTERPART NEED
        //TODO: connection-request text
        //TODO: CREATE CONNECTION BETWEEN COUNTERPART NEED AND GIVEN NEED
        //TODO: SEND REQUEST FOR CREATED CONNECTION
    }
}

function generateResponseNeedTo(theirNeed) {
    let reNeedType, descriptionPhrase;
    const theirNeedType = get(theirNeed, 'type');
    if(theirNeedType === won.WON.BasicNeedTypeDemandCompacted) {
        reNeedType = won.WON.BasicNeedTypeSupplyCompacted;
        descriptionPhrase = 'I have something similar to: ';
    } else if(theirNeedType === won.WON.BasicNeedTypeSupplyCompacted) {
        reNeedType = won.WON.BasicNeedTypeDemandCompacted;
        descriptionPhrase = 'I want something like: ';
    } else if(theirNeedType === won.WON.BasicNeedTypeDotogetherCompacted) {
        reNeedType = won.WON.BasicNeedTypeDotogetherCompacted;
        descriptionPhrase = 'I\'d like to find people for something like the following: ';
    } else {
        console.error(
            'The need responded to (' + theirNeedUri + ') doesn\'t ' +
            'have a need type recognized by ad-hoc-connect method. Type: ',
            theirNeedType
        );
        reNeedType = undefined;
        descriptionPhrase = 'It\'s a response to: ';
    }

    return {
        title: 'Re: ' + get(theirNeed, 'title'),
        description:
        'This is an automatically generated post. ' +
        descriptionPhrase +
        '"' + get(theirNeed, 'description') +'"',
        type: reNeedType,
        tags: cloneAsMutable(get(theirNeed, 'tags')),
        location: cloneAsMutable(get(theirNeed, 'location')),
    };

}

export function connectionsClose(connectionUri) {
    return (dispatch, getState) => {
        const state = getState();
        const eventData = selectAllByConnectionUri(state, connectionUri).toJS();// TODO avoid toJS; UPDATE TO NEW STRUCTURE
        //let eventData = state.getIn(['connections', 'connectionsDeprecated', connectionData.connection.uri])
        let messageData = null;

        const promise = new Promise((resolve, defer) => {
            won.getConnectionWithEventUris(eventData.connection.uri).then(connection=> {
                let msgToOpenFor = {event: eventData, connection: connection};
                buildCloseMessage(msgToOpenFor).then(messageData=> {
                    resolve(messageData);
                })
            });
        });

        promise.then((action)=> {
            dispatch(actionCreators.messages__send({eventUri: action.eventUri, message: action.message}));
            dispatch({
                type: actionTypes.connections.close,
                payload: { connectionUri }
            });
        })
    }
}

export function connectionsRate(connectionUri,rating) {
    return (dispatch, getState) => {
        console.log(connectionUri);
        console.log(rating);

        const state = getState();
        const eventData = selectAllByConnectionUri(state, connectionUri).toJS();// TODO avoid toJS; UPDATE TO NEW STRUCTURE
        let messageData = null;

        won.getConnectionWithEventUris(eventData.connection.uri)
            .then(connection=> {
                let msgToRateFor = {event: eventData, connection: connection};
                return buildRateMessage(msgToRateFor, rating)
            }).then(action =>
                dispatch(
                    actionCreators.messages__send({
                        eventUri: action.eventUri,
                        message: action.message
                    })
                )
            );
    }
}

/**
 * @param connectionUri
 * @param numberOfEvents
 *   The approximate number of chat-message
 *   that the view needs. Note that the
 *   actual number varies due the varying number
 *   of success-responses the server includes and
 *   because the API only accepts a count of
 *   events that include the latter.
 * @return {Function}
 */
export function showLatestMessages(connectionUri, numberOfEvents){
    return (dispatch, getState) => {
        const state = getState();
        const connectionUri = selectOpenConnectionUri(state);
        const allByConnectionUri = connectionUri && selectAllByConnectionUri(state, connectionUri);

        const connection = allByConnectionUri && allByConnectionUri.get("connection"); //TODO: UPDATE METHOD ACCORDING TO NEW STRUCTURE

        if (!connectionUri || !connection) return;

        const events = allByConnectionUri && allByConnectionUri.get("events");
        if (connection.get('loadingEvents') || !events || events.size > 0) return; // only start loading once. //TODO: PENDING IS CURRENTLY NOT IMPLEMENTED IN THE NEW STATE

        //TODO a `return` here might be a race condition that results in this function never being called.
        //TODO the delay solution is super-hacky (idle-waiting)
        // -----> if(!self.connection__showLatestEvent) delay(100).then(loadStuff); // we tried to call this before the action-creators where attached.

        console.log('connections-actions.js: testing for selective loading. ', connectionUri, connection);
        //TODO determine first if component is actually visible (angular calls the constructor long before that)

        dispatch({
            type: actionTypes.connections.showLatestMessages,
            payload: Immutable.fromJS({connectionUri, pending: true}),
        });

        const requesterWebId = allByConnectionUri && allByConnectionUri.getIn(["ownNeed", "uri"]);

        getEvents(
            connection.get('uri'),
            {
                requesterWebId,
                pagingSize: numOfEvts2pageSize(numberOfEvents),
                deep: true
            }
        )
        .then(events =>
            dispatch({
                type: actionTypes.connections.showLatestMessages,
                payload: Immutable.fromJS({
                    connectionUri: connectionUri,
                    events: events,
                })
            })
        )
        .catch(error => {
            console.error('Failed loading the latest events: ', error);
            dispatch({
                type: actionTypes.connections.showLatestMessages,
                payload: Immutable.fromJS({
                    connectionUri: connectionUri,
                    error: error,
                })
            })
        });
    }
}

//TODO replace the won.getEventsOfConnection with this version (and make sure it works for all previous uses).
/**
 * Gets the events and uses the paging-parameters
 * in a meaningful fashion.
 * @param eventContainerUri
 * @param params
 * @return {*}
 */
function getEvents(connectionUri, params) {
    const eventP = won
        .getNode(connectionUri, params)
        .then(cnct =>
            won.getNode(cnct.hasEventContainer, params)
        )
        .then(eventContainer => is('Array', eventContainer.member) ?
            eventContainer.member :
            [eventContainer.member]
        )
        .then(eventUris => urisToLookupMap(
            eventUris,
            uri => won.getEvent(
                uri,
                { requesterWebId: params.requesterWebId }
            )
        ));

    return eventP;

}
/**
 * @param connectionUri
 * @param numberOfEvents
 *   The approximate number of chat-message
 *   that the view needs. Note that the
 *   actual number varies due the varying number
 *   of success-responses the server includes and
 *   because the API only accepts a count of
 *   events that include the latter.
 * @return {Function}
 */
export function showMoreMessages(connectionUri, numberOfEvents) {
    return (dispatch, getState) => {
        const state = getState();
        const connectionUri = selectOpenConnectionUri(state);
        const allByConnectionUri = connectionUri && selectAllByConnectionUri(state, connectionUri);
        const connection = allByConnectionUri && allByConnectionUri.get("connection"); //TODO: UPDATE METHOD ACCORDING TO NEW STRUCTURE
        const requesterWebId = allByConnectionUri && allByConnectionUri.getIn(["ownNeed", "uri"]);
        const ownEvents = allByConnectionUri.get("events");
        const remoteEvents =
            selectRemoteEvents(state)
            .filter(e =>
                e.get('hasReceiver') === connectionUri ||
                e.get('hasSender') === connectionUri
            );

        /* TODO expand set of uris from latest chat message via daisy-chaining?
         * they have multiple predecessors, i.e. success response and previous chat message
         * and this way we can find holes in the loaded messages.
         * Prob: when daisy-chaining: make sure to look into the correspondingRemoteMessage to get links between their messages
         * store normalized and write a selector to get event+remote? (for old code)
         * or look through *all* events here to find the event we're looking for.
         */
        // determine the oldest loaded event
        //alternative approach sort everything together

        // determine the oldest loaded event
        const sortByTime = (someEvents, pathToMsString) =>
            someEvents.sort((e1, e2) =>
                msStringToDate(e1.getIn(pathToMsString)) -
                msStringToDate(e2.getIn(pathToMsString))
            );

        const sortedOwnEvents = sortByTime(ownEvents, ['hasReceivedTimestamp']);

        const oldestEvent = sortedOwnEvents.first();
        const eventHashValue = oldestEvent
                .get('uri')
                .replace(/.*\/event\/(.*)/, '$1'); // everything following the `/event/`

        // chain is more of a cycle-free graph
        // find all the ones w/o predecessors
        // then take the oldest of these
        // make sure to always take the timestamp on the own node

        dispatch({
            type: actionTypes.connections.showMoreMessages,
            payload: Immutable.fromJS({connectionUri, pending: true}),
        });

        getEvents(
            connection.get('uri'),
            {
                requesterWebId,
                pagingSize: numOfEvts2pageSize(numberOfEvents),
                deep: true,
                resumebefore: eventHashValue,
            }
        )
        .then(events =>
            dispatch({
                type: actionTypes.connections.showMoreMessages,
                payload: Immutable.fromJS({
                    connectionUri: connectionUri,
                    events: events,
                })
            })
        )
        .catch(error => {
            console.error('Failed loading more events: ', error);
            dispatch({
                type: actionTypes.connections.showMoreMessages,
                payload: Immutable.fromJS({
                    connectionUri: connectionUri,
                    error: error,
                })
            })
        });
    }
}

function numOfEvts2pageSize(numberOfEvents) {
     // `*3*` to compensate for the *roughly* 2 additional success events per chat message
    return numberOfEvents * 3;
}
<commit_msg>Printing used draft after successful posting.<commit_after>/**
 * Created by ksinger on 19.02.2016.
 */

import  won from '../won-es6.js';
import Immutable from 'immutable';
import jsonld from 'jsonld'; //import *after* the rdfstore to shadow its custom jsonld

import {
    selectAllByConnectionUri,
    selectOpenConnectionUri,
    selectRemoteEvents,
} from '../selectors.js';

import {
    is,
    urisToLookupMap,
    msStringToDate,
    getIn,
    get,
    jsonld2simpleFormat,
    cloneAsMutable,
} from '../utils.js';

import {
   makeParams,
} from '../configRouting.js';

import {
    actionTypes,
    actionCreators,
} from './actions.js';

import {
    buildOpenMessage,
    buildCloseMessage,
    buildChatMessage,
    buildRateMessage,
    buildConnectMessage,
    getEventsFromMessage,
} from '../won-message-utils.js';

export function connectionsChatMessage(chatMessage, connectionUri) {
   return dispatch => {
       console.log('connectionsChatMessage: ', chatMessage, connectionUri);

       buildChatMessage(chatMessage, connectionUri)
       .then(msgData => {

           /*
            * not sure how elegant it is to build the ld and then parse
            * it again. It uses existing utilities at least, reducing
            * redundant program logic. ^^
             */
           const optimisticEventPromise = getEventsFromMessage(msgData.message)
               .then(optimisticEvent => optimisticEvent['msg:FromOwner']);

           return Promise.all([
               Promise.resolve(msgData.message),
               optimisticEventPromise,
           ]);
       })
       .then( ([ message, optimisticEvent ]) => dispatch({
               type: actionTypes.connections.sendChatMessage,
               payload: {
                   eventUri: optimisticEvent.uri,
                   message,
                   optimisticEvent,
               }
           })
       )
   }
}

export function connectionsFetch(data) {
    return dispatch=> {
        const allConnectionsPromise = won.executeCrawlableQuery(won.queries["getAllConnectionUrisOfNeed"], data.needUri);
        allConnectionsPromise.then(function (connections) {
            console.log("fetching connections");
            dispatch(actionCreators.needs__connectionsReceived({needUri: data.needUri, connections: connections}));
            dispatch(actionCreators.events__fetch({connectionUris: connections}))
        })
    }
}

export function connectionsOpen(connectionUri, message) {
    return (dispatch, getState) => {
        buildOpenMessage(connectionUri, message)
        .then(msgData => {
            const optimisticEventPromise = getEventsFromMessage(msgData.message)
                .then(optimisticEvent => optimisticEvent['msg:FromOwner']);
            return Promise.all([
                Promise.resolve(msgData),
                optimisticEventPromise,
            ]);

            //TODO dispatch connections.open
        })
        .then( ([ msgData, optimisticEvent ]) => {
            // dispatch(actionCreators.messages__send(messageData));
            dispatch({
                type: actionTypes.connections.open,
                payload: {
                    eventUri: optimisticEvent.uri,
                    message: msgData.message,
                    optimisticEvent,
                }
            });

            dispatch(actionCreators.router__stateGoAbs("post", {
                postUri: optimisticEvent.hasSenderNeed,
                connectionType: won.WON.Connected,
                connectionUri: optimisticEvent.hasSender,
            }));

        });
    }
}

export function connectionsConnect(connectionUri,message) {
    return (dispatch, getState) => {
        const state = getState();
        const eventData = selectAllByConnectionUri(state, connectionUri).toJS(); // TODO avoid toJS; UPDATE TO NEW STRUCTURE
        const messageDataP = won
            .getConnectionWithEventUris(eventData.connection.uri)
            .then(connection=> {
                let msgToOpenFor = {event: eventData, connection: connection};
                return buildConnectMessage(msgToOpenFor, message)
            });
        messageDataP.then((action)=> {
            dispatch(actionCreators.messages__send({eventUri: action.eventUri, message: action.message}));

            jsonld.promises.frame(
                action.message,
                {
                    '@id': action.eventUri,
                    '@context': action.message['@context']
                }
            ).then(framed => {
                let event = getIn(framed, ['@graph', 0]);
                if(event) {
                    event['@context'] = framed['@context']; // context is needed by jsonld2simpleFormat for expanding prefixes in values
                    event = jsonld2simpleFormat(event);
                }

                dispatch({
                    type: actionTypes.connections.connect,
                    payload: {
                        connectionUri,
                        message,
                        eventUri: action.eventUri,
                        optimisticEvent: event,
                    }
                });
            });
        })
    }
}

export function connectionsConnectAdHoc(theirNeedUri) {
    return (dispatch, getState) => {
        const state = getState();

        const theirNeed = getIn(state, ['needs', theirNeedUri]);
        const adHocDraft = generateResponseNeedTo(theirNeed);

        dispatch(actionCreators.needs__create(adHocDraft))
        .then(() => {
            console.log('STARTED PUBLISHING AD HOC DRAFT: ', adHocDraft);

        });

        //TODO: CREATE COUNTERPART NEED
        //TODO: connection-request text
        //TODO: CREATE CONNECTION BETWEEN COUNTERPART NEED AND GIVEN NEED
        //TODO: SEND REQUEST FOR CREATED CONNECTION
    }
}

function generateResponseNeedTo(theirNeed) {
    let reNeedType, descriptionPhrase;
    const theirNeedType = get(theirNeed, 'type');
    if(theirNeedType === won.WON.BasicNeedTypeDemandCompacted) {
        reNeedType = won.WON.BasicNeedTypeSupplyCompacted;
        descriptionPhrase = 'I have something similar to: ';
    } else if(theirNeedType === won.WON.BasicNeedTypeSupplyCompacted) {
        reNeedType = won.WON.BasicNeedTypeDemandCompacted;
        descriptionPhrase = 'I want something like: ';
    } else if(theirNeedType === won.WON.BasicNeedTypeDotogetherCompacted) {
        reNeedType = won.WON.BasicNeedTypeDotogetherCompacted;
        descriptionPhrase = 'I\'d like to find people for something like the following: ';
    } else {
        console.error(
            'The need responded to (' + theirNeedUri + ') doesn\'t ' +
            'have a need type recognized by ad-hoc-connect method. Type: ',
            theirNeedType
        );
        reNeedType = undefined;
        descriptionPhrase = 'It\'s a response to: ';
    }

    return {
        title: 'Re: ' + get(theirNeed, 'title'),
        description:
        'This is an automatically generated post. ' +
        descriptionPhrase +
        '"' + get(theirNeed, 'description') +'"',
        type: reNeedType,
        tags: cloneAsMutable(get(theirNeed, 'tags')),
        location: cloneAsMutable(get(theirNeed, 'location')),
    };

}

export function connectionsClose(connectionUri) {
    return (dispatch, getState) => {
        const state = getState();
        const eventData = selectAllByConnectionUri(state, connectionUri).toJS();// TODO avoid toJS; UPDATE TO NEW STRUCTURE
        //let eventData = state.getIn(['connections', 'connectionsDeprecated', connectionData.connection.uri])
        let messageData = null;

        const promise = new Promise((resolve, defer) => {
            won.getConnectionWithEventUris(eventData.connection.uri).then(connection=> {
                let msgToOpenFor = {event: eventData, connection: connection};
                buildCloseMessage(msgToOpenFor).then(messageData=> {
                    resolve(messageData);
                })
            });
        });

        promise.then((action)=> {
            dispatch(actionCreators.messages__send({eventUri: action.eventUri, message: action.message}));
            dispatch({
                type: actionTypes.connections.close,
                payload: { connectionUri }
            });
        })
    }
}

export function connectionsRate(connectionUri,rating) {
    return (dispatch, getState) => {
        console.log(connectionUri);
        console.log(rating);

        const state = getState();
        const eventData = selectAllByConnectionUri(state, connectionUri).toJS();// TODO avoid toJS; UPDATE TO NEW STRUCTURE
        let messageData = null;

        won.getConnectionWithEventUris(eventData.connection.uri)
            .then(connection=> {
                let msgToRateFor = {event: eventData, connection: connection};
                return buildRateMessage(msgToRateFor, rating)
            }).then(action =>
                dispatch(
                    actionCreators.messages__send({
                        eventUri: action.eventUri,
                        message: action.message
                    })
                )
            );
    }
}

/**
 * @param connectionUri
 * @param numberOfEvents
 *   The approximate number of chat-message
 *   that the view needs. Note that the
 *   actual number varies due the varying number
 *   of success-responses the server includes and
 *   because the API only accepts a count of
 *   events that include the latter.
 * @return {Function}
 */
export function showLatestMessages(connectionUri, numberOfEvents){
    return (dispatch, getState) => {
        const state = getState();
        const connectionUri = selectOpenConnectionUri(state);
        const allByConnectionUri = connectionUri && selectAllByConnectionUri(state, connectionUri);

        const connection = allByConnectionUri && allByConnectionUri.get("connection"); //TODO: UPDATE METHOD ACCORDING TO NEW STRUCTURE

        if (!connectionUri || !connection) return;

        const events = allByConnectionUri && allByConnectionUri.get("events");
        if (connection.get('loadingEvents') || !events || events.size > 0) return; // only start loading once. //TODO: PENDING IS CURRENTLY NOT IMPLEMENTED IN THE NEW STATE

        //TODO a `return` here might be a race condition that results in this function never being called.
        //TODO the delay solution is super-hacky (idle-waiting)
        // -----> if(!self.connection__showLatestEvent) delay(100).then(loadStuff); // we tried to call this before the action-creators where attached.

        console.log('connections-actions.js: testing for selective loading. ', connectionUri, connection);
        //TODO determine first if component is actually visible (angular calls the constructor long before that)

        dispatch({
            type: actionTypes.connections.showLatestMessages,
            payload: Immutable.fromJS({connectionUri, pending: true}),
        });

        const requesterWebId = allByConnectionUri && allByConnectionUri.getIn(["ownNeed", "uri"]);

        getEvents(
            connection.get('uri'),
            {
                requesterWebId,
                pagingSize: numOfEvts2pageSize(numberOfEvents),
                deep: true
            }
        )
        .then(events =>
            dispatch({
                type: actionTypes.connections.showLatestMessages,
                payload: Immutable.fromJS({
                    connectionUri: connectionUri,
                    events: events,
                })
            })
        )
        .catch(error => {
            console.error('Failed loading the latest events: ', error);
            dispatch({
                type: actionTypes.connections.showLatestMessages,
                payload: Immutable.fromJS({
                    connectionUri: connectionUri,
                    error: error,
                })
            })
        });
    }
}

//TODO replace the won.getEventsOfConnection with this version (and make sure it works for all previous uses).
/**
 * Gets the events and uses the paging-parameters
 * in a meaningful fashion.
 * @param eventContainerUri
 * @param params
 * @return {*}
 */
function getEvents(connectionUri, params) {
    const eventP = won
        .getNode(connectionUri, params)
        .then(cnct =>
            won.getNode(cnct.hasEventContainer, params)
        )
        .then(eventContainer => is('Array', eventContainer.member) ?
            eventContainer.member :
            [eventContainer.member]
        )
        .then(eventUris => urisToLookupMap(
            eventUris,
            uri => won.getEvent(
                uri,
                { requesterWebId: params.requesterWebId }
            )
        ));

    return eventP;

}
/**
 * @param connectionUri
 * @param numberOfEvents
 *   The approximate number of chat-message
 *   that the view needs. Note that the
 *   actual number varies due the varying number
 *   of success-responses the server includes and
 *   because the API only accepts a count of
 *   events that include the latter.
 * @return {Function}
 */
export function showMoreMessages(connectionUri, numberOfEvents) {
    return (dispatch, getState) => {
        const state = getState();
        const connectionUri = selectOpenConnectionUri(state);
        const allByConnectionUri = connectionUri && selectAllByConnectionUri(state, connectionUri);
        const connection = allByConnectionUri && allByConnectionUri.get("connection"); //TODO: UPDATE METHOD ACCORDING TO NEW STRUCTURE
        const requesterWebId = allByConnectionUri && allByConnectionUri.getIn(["ownNeed", "uri"]);
        const ownEvents = allByConnectionUri.get("events");
        const remoteEvents =
            selectRemoteEvents(state)
            .filter(e =>
                e.get('hasReceiver') === connectionUri ||
                e.get('hasSender') === connectionUri
            );

        /* TODO expand set of uris from latest chat message via daisy-chaining?
         * they have multiple predecessors, i.e. success response and previous chat message
         * and this way we can find holes in the loaded messages.
         * Prob: when daisy-chaining: make sure to look into the correspondingRemoteMessage to get links between their messages
         * store normalized and write a selector to get event+remote? (for old code)
         * or look through *all* events here to find the event we're looking for.
         */
        // determine the oldest loaded event
        //alternative approach sort everything together

        // determine the oldest loaded event
        const sortByTime = (someEvents, pathToMsString) =>
            someEvents.sort((e1, e2) =>
                msStringToDate(e1.getIn(pathToMsString)) -
                msStringToDate(e2.getIn(pathToMsString))
            );

        const sortedOwnEvents = sortByTime(ownEvents, ['hasReceivedTimestamp']);

        const oldestEvent = sortedOwnEvents.first();
        const eventHashValue = oldestEvent
                .get('uri')
                .replace(/.*\/event\/(.*)/, '$1'); // everything following the `/event/`

        // chain is more of a cycle-free graph
        // find all the ones w/o predecessors
        // then take the oldest of these
        // make sure to always take the timestamp on the own node

        dispatch({
            type: actionTypes.connections.showMoreMessages,
            payload: Immutable.fromJS({connectionUri, pending: true}),
        });

        getEvents(
            connection.get('uri'),
            {
                requesterWebId,
                pagingSize: numOfEvts2pageSize(numberOfEvents),
                deep: true,
                resumebefore: eventHashValue,
            }
        )
        .then(events =>
            dispatch({
                type: actionTypes.connections.showMoreMessages,
                payload: Immutable.fromJS({
                    connectionUri: connectionUri,
                    events: events,
                })
            })
        )
        .catch(error => {
            console.error('Failed loading more events: ', error);
            dispatch({
                type: actionTypes.connections.showMoreMessages,
                payload: Immutable.fromJS({
                    connectionUri: connectionUri,
                    error: error,
                })
            })
        });
    }
}

function numOfEvts2pageSize(numberOfEvents) {
     // `*3*` to compensate for the *roughly* 2 additional success events per chat message
    return numberOfEvents * 3;
}
<|endoftext|>


<commit_before>/**
 * Created by ksinger on 22.01.2016.
 */

import { createSelector } from "reselect";

import { decodeUriComponentProperly, getIn, get } from "../utils.js";
import { isPersona, isNeed, isActive, isInactive } from "../need-utils.js";
import Color from "color";

export const selectLastUpdateTime = state => state.get("lastUpdateTime");
export const getRouterParams = state =>
  getIn(state, ["router", "currentParams"]);

export const getNeeds = state => state.get("needs");
export const getOwnedNeeds = state =>
  getNeeds(state).filter(need => need.get("isOwned"));
export const getNonOwnedNeeds = state =>
  getNeeds(state).filter(need => !need.get("isOwned"));

export function getPosts(state) {
  const needs = getNeeds(state);
  return needs.filter(need => {
    if (!need.getIn(["content", "type"])) return true;

    return isNeed(need) && !isPersona(need);
  });
}

export const getOwnedPosts = state =>
  getPosts(state).filter(need => need.get("isOwned"));

export function getOwnedOpenPosts(state) {
  const allOwnedNeeds = getOwnedPosts(state);
  return allOwnedNeeds && allOwnedNeeds.filter(post => isActive(post));
}

export function getOpenPosts(state) {
  const allPosts = getPosts(state);
  return allPosts && allPosts.filter(post => isActive(post));
}

export function getActiveNeeds(state) {
  const allNeeds = getNeeds(state);
  return allNeeds && allNeeds.filter(need => isActive(need));
}

//TODO: METHOD NAME TO ACTUALLY REPRESENT WHAT THE SELECTOR DOES
export function getOwnedClosedPosts(state) {
  const allOwnedNeeds = getOwnedPosts(state);
  return allOwnedNeeds && allOwnedNeeds.filter(post => isInactive(post));
}

export function getOwnedNeedsInCreation(state) {
  const allOwnedNeeds = getOwnedNeeds(state);
  // needs that have been created but are not confirmed by the server yet
  return (
    allOwnedNeeds && allOwnedNeeds.filter(post => post.get("isBeingCreated"))
  );
}

export const selectIsConnected = state =>
  !state.getIn(["messages", "reconnecting"]) &&
  !state.getIn(["messages", "lostConnection"]);

/**
 * Get the need for a given connectionUri
 * @param state to retrieve data from
 * @param connectionUri to find corresponding need for
 */
export function getOwnedNeedByConnectionUri(state, connectionUri) {
  let needs = getOwnedNeeds(state); //we only check own needs as these are the only ones who have connections stored
  return needs.find(need => need.getIn(["connections", connectionUri]));
}

export const getCurrentParamsFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams"]);
  }
);

export const getViewNeedUriFromRoute = createSelector(
  state => state,
  state => {
    const encodedNeedUri = getIn(state, [
      "router",
      "currentParams",
      "viewNeedUri",
    ]);
    return decodeUriComponentProperly(encodedNeedUri);
  }
);

export const getUseCaseFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "useCase"]);
  }
);

export const getUseCaseGroupFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "useCaseGroup"]);
  }
);

export const getPrivateIdFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "privateId"]);
  }
);

export const getVerificationTokenFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "token"]);
  }
);

export const getConnectionUriFromRoute = createSelector(
  getRouterParams,
  routerParams => {
    //de-escaping is lost in transpiling if not done in two steps :|
    const openConnectionUri = decodeUriComponentProperly(
      routerParams["connectionUri"] || routerParams["openConversation"]
    );

    if (openConnectionUri) {
      return openConnectionUri;
    } else {
      return undefined;
    }
  }
);

export const getGroupPostAdminUriFromRoute = createSelector(
  state => state,
  state => {
    const encodedPostUri = getIn(state, [
      "router",
      "currentParams",
      "groupPostAdminUri",
    ]);
    return decodeUriComponentProperly(encodedPostUri);
  }
);

export const getFromNeedUriFromRoute = createSelector(
  state => state,
  state => {
    const encodedNeedUri = getIn(state, [
      "router",
      "currentParams",
      "fromNeedUri",
    ]);
    return decodeUriComponentProperly(encodedNeedUri);
  }
);

export const getModeFromRoute = createSelector(
  state => state,
  state => {
    const mode = getIn(state, ["router", "currentParams", "mode"]);

    if (mode) {
      if (mode === "EDIT") {
        return "EDIT";
      }
      return "DUPLICATE";
    }
    return undefined;
  }
);

export const getPostUriFromRoute = createSelector(
  state => state,
  state => {
    const encodedPostUri = getIn(state, ["router", "currentParams", "postUri"]);
    return decodeUriComponentProperly(encodedPostUri);
  }
);

export const getAboutSectionFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "aboutSection"]);
  }
);

export function getOwnedPersonas(state) {
  const needs = getOwnedNeeds(state);
  const personas = needs.toList().filter(need => isPersona(need));
  return personas.map(persona => {
    return {
      displayName: getIn(persona, ["content", "personaName"]),
      website: getIn(persona, ["content", "website"]),
      aboutMe: getIn(persona, ["content", "description"]),
      url: get(persona, "uri"),
      saved: !get(persona, "isBeingCreated"),
      timestamp: get(persona, "creationDate").toISOString(),
    };
  });
}

//TODO: move this method to a place that makes more sense, its not really a selector function
export function currentSkin() {
  const style = getComputedStyle(document.body);
  const getColor = name => {
    const color = Color(style.getPropertyValue(name).trim());
    return color.rgb().object();
  };
  return {
    primaryColor: getColor("--won-primary-color"),
    lightGray: getColor("--won-light-gray"),
    lineGray: getColor("--won-line-gray"),
    subtitleGray: getColor("--won-subtitle-gray"),
  };
}
<commit_msg>Add function to retrieve viewConnUri from route state<commit_after>/**
 * Created by ksinger on 22.01.2016.
 */

import { createSelector } from "reselect";

import { decodeUriComponentProperly, getIn, get } from "../utils.js";
import { isPersona, isNeed, isActive, isInactive } from "../need-utils.js";
import Color from "color";

export const selectLastUpdateTime = state => state.get("lastUpdateTime");
export const getRouterParams = state =>
  getIn(state, ["router", "currentParams"]);

export const getNeeds = state => state.get("needs");
export const getOwnedNeeds = state =>
  getNeeds(state).filter(need => need.get("isOwned"));
export const getNonOwnedNeeds = state =>
  getNeeds(state).filter(need => !need.get("isOwned"));

export function getPosts(state) {
  const needs = getNeeds(state);
  return needs.filter(need => {
    if (!need.getIn(["content", "type"])) return true;

    return isNeed(need) && !isPersona(need);
  });
}

export const getOwnedPosts = state =>
  getPosts(state).filter(need => need.get("isOwned"));

export function getOwnedOpenPosts(state) {
  const allOwnedNeeds = getOwnedPosts(state);
  return allOwnedNeeds && allOwnedNeeds.filter(post => isActive(post));
}

export function getOpenPosts(state) {
  const allPosts = getPosts(state);
  return allPosts && allPosts.filter(post => isActive(post));
}

export function getActiveNeeds(state) {
  const allNeeds = getNeeds(state);
  return allNeeds && allNeeds.filter(need => isActive(need));
}

//TODO: METHOD NAME TO ACTUALLY REPRESENT WHAT THE SELECTOR DOES
export function getOwnedClosedPosts(state) {
  const allOwnedNeeds = getOwnedPosts(state);
  return allOwnedNeeds && allOwnedNeeds.filter(post => isInactive(post));
}

export function getOwnedNeedsInCreation(state) {
  const allOwnedNeeds = getOwnedNeeds(state);
  // needs that have been created but are not confirmed by the server yet
  return (
    allOwnedNeeds && allOwnedNeeds.filter(post => post.get("isBeingCreated"))
  );
}

export const selectIsConnected = state =>
  !state.getIn(["messages", "reconnecting"]) &&
  !state.getIn(["messages", "lostConnection"]);

/**
 * Get the need for a given connectionUri
 * @param state to retrieve data from
 * @param connectionUri to find corresponding need for
 */
export function getOwnedNeedByConnectionUri(state, connectionUri) {
  let needs = getOwnedNeeds(state); //we only check own needs as these are the only ones who have connections stored
  return needs.find(need => need.getIn(["connections", connectionUri]));
}

export const getCurrentParamsFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams"]);
  }
);

export const getViewNeedUriFromRoute = createSelector(
  state => state,
  state => {
    const encodedNeedUri = getIn(state, [
      "router",
      "currentParams",
      "viewNeedUri",
    ]);
    return decodeUriComponentProperly(encodedNeedUri);
  }
);

export const getViewConnectionUriFromRoute = createSelector(
  state => state,
  state => {
    const encodedConnUri = getIn(state, [
      "router",
      "currentParams",
      "viewConnUri",
    ]);
    return decodeUriComponentProperly(encodedConnUri);
  }
);

export const getUseCaseFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "useCase"]);
  }
);

export const getUseCaseGroupFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "useCaseGroup"]);
  }
);

export const getPrivateIdFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "privateId"]);
  }
);

export const getVerificationTokenFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "token"]);
  }
);

export const getConnectionUriFromRoute = createSelector(
  getRouterParams,
  routerParams => {
    //de-escaping is lost in transpiling if not done in two steps :|
    const openConnectionUri = decodeUriComponentProperly(
      routerParams["connectionUri"] || routerParams["openConversation"]
    );

    if (openConnectionUri) {
      return openConnectionUri;
    } else {
      return undefined;
    }
  }
);

export const getGroupPostAdminUriFromRoute = createSelector(
  state => state,
  state => {
    const encodedPostUri = getIn(state, [
      "router",
      "currentParams",
      "groupPostAdminUri",
    ]);
    return decodeUriComponentProperly(encodedPostUri);
  }
);

export const getFromNeedUriFromRoute = createSelector(
  state => state,
  state => {
    const encodedNeedUri = getIn(state, [
      "router",
      "currentParams",
      "fromNeedUri",
    ]);
    return decodeUriComponentProperly(encodedNeedUri);
  }
);

export const getModeFromRoute = createSelector(
  state => state,
  state => {
    const mode = getIn(state, ["router", "currentParams", "mode"]);

    if (mode) {
      if (mode === "EDIT") {
        return "EDIT";
      }
      return "DUPLICATE";
    }
    return undefined;
  }
);

export const getPostUriFromRoute = createSelector(
  state => state,
  state => {
    const encodedPostUri = getIn(state, ["router", "currentParams", "postUri"]);
    return decodeUriComponentProperly(encodedPostUri);
  }
);

export const getAboutSectionFromRoute = createSelector(
  state => state,
  state => {
    return getIn(state, ["router", "currentParams", "aboutSection"]);
  }
);

export function getOwnedPersonas(state) {
  const needs = getOwnedNeeds(state);
  const personas = needs.toList().filter(need => isPersona(need));
  return personas.map(persona => {
    return {
      displayName: getIn(persona, ["content", "personaName"]),
      website: getIn(persona, ["content", "website"]),
      aboutMe: getIn(persona, ["content", "description"]),
      url: get(persona, "uri"),
      saved: !get(persona, "isBeingCreated"),
      timestamp: get(persona, "creationDate").toISOString(),
    };
  });
}

//TODO: move this method to a place that makes more sense, its not really a selector function
export function currentSkin() {
  const style = getComputedStyle(document.body);
  const getColor = name => {
    const color = Color(style.getPropertyValue(name).trim());
    return color.rgb().object();
  };
  return {
    primaryColor: getColor("--won-primary-color"),
    lightGray: getColor("--won-light-gray"),
    lineGray: getColor("--won-line-gray"),
    subtitleGray: getColor("--won-subtitle-gray"),
  };
}
<|endoftext|>


<commit_before>STSearcher.subClass('STModelSearcher', {

    initWithModelUrl: function(model, url, labelMethod)
    {
        this.init();

        this.model = model;
        this.url = url;
        this.labelMethod = labelMethod || 'label';
        this.async = true;
        this.split = true;
        this.minLength = 3;
        this.cache = {};
    },

    minLength: ST.$property,

    search: function(term)
    {
        var self = this;

        this.searching = true;

        if (term.length < this.minLength) {
            this.clearResults();
        } else if (this.cache[term]) {
            this.setResults(this.cache[term]);
        } else {
            $.ajax({
                url: this.url,
                method: 'get',
                dataType: 'json',
                data: {q: term},
                success: function(data) { self.succeeded(term, data); },
                error:  this.methodFn('failed')
            });
        }
    },

    succeeded: function(term, data)
    {
        var self = this;

        var results = STArray.create();
        if (data && data.length) {
            $.each(data, function()
            {
                if (!STModel.Index[this.uuid]) {
                    STModel.createWithData(this, {temporary: true});
                }
                this.model = STModel.Index[this.uuid];

                this.textLabel = this.model.get(self.labelMethod);
                if (this.textLabel) {
                    this.label = self.hilight(this.textLabel, self.split ? term.split(' ') : term);
                }
                results.push(this);
            });
        }
        this.cache[term] = results;
        this.setResults(results);
    },

    failed: function()
    {
        this.setResults(ST.A());
    },

end
:0});<commit_msg>Changed an STModelSearcher internal variable name to make it more accurate<commit_after>STSearcher.subClass('STModelSearcher', {

    initWithModelUrl: function(model, url, labelField)
    {
        this.init();

        this.model = model;
        this.url = url;
        this.labelField = labelField || 'label';
        this.async = true;
        this.split = true;
        this.minLength = 3;
        this.cache = {};
    },

    minLength: ST.$property,

    search: function(term)
    {
        var self = this;

        this.searching = true;

        if (term.length < this.minLength) {
            this.clearResults();
        } else if (this.cache[term]) {
            this.setResults(this.cache[term]);
        } else {
            $.ajax({
                url: this.url,
                method: 'get',
                dataType: 'json',
                data: {q: term},
                success: function(data) { self.succeeded(term, data); },
                error:  this.methodFn('failed')
            });
        }
    },

    succeeded: function(term, data)
    {
        var self = this;

        var results = STArray.create();
        if (data && data.length) {
            $.each(data, function()
            {
                if (!STModel.Index[this.uuid]) {
                    STModel.createWithData(this, {temporary: true});
                }
                this.model = STModel.Index[this.uuid];

                this.textLabel = this.model.get(self.labelField);
                if (this.textLabel) {
                    this.label = self.hilight(this.textLabel, self.split ? term.split(' ') : term);
                }
                results.push(this);
            });
        }
        this.cache[term] = results;
        this.setResults(results);
    },

    failed: function()
    {
        this.setResults(ST.A());
    },

end
:0});<|endoftext|>


<commit_before>'use strict';
var React = require('react');
var url = require('url');
var _ = require('underscore');
var panel = require('../libs/bootstrap/panel');
var form = require('../libs/bootstrap/form');
var globals = require('./globals');
var curator = require('./curator');

var CurationData = curator.CurationData;
var CurationNav = curator.CurationNav;
var PmidSummary = curator.PmidSummary;
var PanelGroup = panel.PanelGroup;
var Panel = panel.Panel;
var Input = form.Input;
var InputMixin = form.InputMixin;

// Temporary hard-coded data to display
var pmid_items = require('./testdata').pmid_items;
var country_codes = require('./testdata').country_codes;

var GroupCuration = React.createClass({
    render: function() {
        var currPmidItem;

        var queryParsed = this.props.href && url.parse(this.props.href, true).query;
        if (queryParsed && Object.keys(queryParsed).length) {
            // Find the first 'pmid' query string item, if any
            var pmidKey = _(Object.keys(queryParsed)).find(function(key) {
                return key === 'pmid';
            });
            var pmid = parseInt(queryParsed[pmidKey], 10);
            currPmidItem = _(pmid_items).find(function(item) {
                return item.id === pmid;
            });
        }

        return (

                <CurationData />

                    {currPmidItem ?

                            <PmidSummary pmidItem={currPmidItem} />

                    : null}

                            <form className="form-horizontal">
                                <Panel>
                                    <GroupName />
                                </Panel>
                                <PanelGroup accordion>
                                    <Panel title="Common diseases & phenotypes" open>
                                        <GroupCommonDiseases />
                                    </Panel>
                                </PanelGroup>
                                <PanelGroup accordion>
                                    <Panel title="Demographics">
                                        <GroupDemographics />
                                    </Panel>
                                </PanelGroup>
                            </form>

                        {currPmidItem ?

                                <CurationNav currPmidItem={currPmidItem} />

                        : null}

        );
    }
});

globals.curator_page.register(GroupCuration, 'curator_page', 'group-curation');

var GroupName = React.createClass({
    render: function() {
        return (

                <Input type="text" id="group-name" ref="groupname" label="Group name (optional):"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />

        );
    }
});

var GroupCommonDiseases = React.createClass({
    render: function() {
        return (

                <Input type="text" id="orphanet-id" ref="orphanetid" label={<LabelOrphanetId />}
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" required />
                <Input type="text" id="hpo-id" ref="hpoid" label={<LabelHpoId />}
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" inputClassName="uppercase-input" />
                <Input type="textarea" id="pheno-terms" ref="phenoterms" label={<LabelPhenoTerms />} rows="5"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />

Enter phenotypes that are NOT present in Group if they are specifically noted in the paper

                <Input type="text" id="not-hpo-id" ref="nothpoid" label={<LabelHpoId not />}
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" inputClassName="uppercase-input" />
                <Input type="textarea" id="pheno-terms" ref="phenoterms" label={<LabelPhenoTerms not />} rows="5"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />

        );
    }
});

// HTML labels for inputs follow.
var LabelOrphanetId = React.createClass({
    render: function() {
        return Orphanet common diagnosis:;
    }
});

// HTML labels for inputs follow.
var LabelHpoId = React.createClass({
    propTypes: {
        not: React.PropTypes.bool
    },

    render: function() {
        return {this.props.not ? NOT : null} HPO ID(s):;
    }
});

// HTML labels for inputs follow.
var LabelPhenoTerms = React.createClass({
    propTypes: {
        not: React.PropTypes.bool
    },

    render: function() {
        return Free text{this.props.not ?  NOT : null} phenotype terms:;
    }
});

var GroupDemographics = React.createClass({
    render: function() {
        return (

                <Input type="text" id="male-count" ref="malecount" label="# males:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />
                <Input type="text" id="female-count" ref="malecount" label="# females:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />
                <Input type="select" id="country" ref="country" label="Country of Origin:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group">
                    {country_codes.map(function(country_code) {
                        return <option key={country_code.code} value={country_code.code}>{country_code.name}</option>;
                    })}
                </Input>
                <Input type="select" id="ethnicity" ref="ethnicity" label="Ethnicity:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group">
                    <option value="hispanic">Hispanic or Latino</option>
                    <option value="nonhispanic">Not Hispanic or Latino</option>
                </Input>
                <Input type="select" id="race" ref="race" label="Race:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group">
                    <option value="nativeamerican">American Indian or Alaska Native</option>
                    <option value="asian">Asian</option>
                    <option value="black">Black</option>
                    <option value="pacificislander">Native Hawaiian or Other Pacific Islander</option>
                    <option value="white">White</option>
                    <option value="mixed">Mixed</option>
                    <option value="unknown">Unknown</option>
                </Input>

Age Range

                    <Input type="select" id="agerange-type" ref="agerangetype" label="Type:"
                        labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group">
                        <option value="onset">Onset</option>
                        <option value="report">Report</option>
                        <option value="diagnosis">Diagnosis</option>
                        <option value="death">Death</option>
                    </Input>

        );
    }
});
<commit_msg>Update ids to match refs<commit_after>'use strict';
var React = require('react');
var url = require('url');
var _ = require('underscore');
var panel = require('../libs/bootstrap/panel');
var form = require('../libs/bootstrap/form');
var globals = require('./globals');
var curator = require('./curator');

var CurationData = curator.CurationData;
var CurationNav = curator.CurationNav;
var PmidSummary = curator.PmidSummary;
var PanelGroup = panel.PanelGroup;
var Panel = panel.Panel;
var Input = form.Input;
var InputMixin = form.InputMixin;

// Temporary hard-coded data to display
var pmid_items = require('./testdata').pmid_items;
var country_codes = require('./testdata').country_codes;

var GroupCuration = React.createClass({
    render: function() {
        var currPmidItem;

        var queryParsed = this.props.href && url.parse(this.props.href, true).query;
        if (queryParsed && Object.keys(queryParsed).length) {
            // Find the first 'pmid' query string item, if any
            var pmidKey = _(Object.keys(queryParsed)).find(function(key) {
                return key === 'pmid';
            });
            var pmid = parseInt(queryParsed[pmidKey], 10);
            currPmidItem = _(pmid_items).find(function(item) {
                return item.id === pmid;
            });
        }

        return (

                <CurationData />

                    {currPmidItem ?

                            <PmidSummary pmidItem={currPmidItem} />

                    : null}

                            <form className="form-horizontal">
                                <Panel>
                                    <GroupName />
                                </Panel>
                                <PanelGroup accordion>
                                    <Panel title="Common diseases & phenotypes" open>
                                        <GroupCommonDiseases />
                                    </Panel>
                                </PanelGroup>
                                <PanelGroup accordion>
                                    <Panel title="Demographics">
                                        <GroupDemographics />
                                    </Panel>
                                </PanelGroup>
                            </form>

                        {currPmidItem ?

                                <CurationNav currPmidItem={currPmidItem} />

                        : null}

        );
    }
});

globals.curator_page.register(GroupCuration, 'curator_page', 'group-curation');

var GroupName = React.createClass({
    render: function() {
        return (

                <Input type="text" id="groupname" ref="groupname" label="Group name (optional):"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />

        );
    }
});

var GroupCommonDiseases = React.createClass({
    render: function() {
        return (

                <Input type="text" id="orphanetid" ref="orphanetid" label={<LabelOrphanetId />}
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" required />
                <Input type="text" id="hpoid" ref="hpoid" label={<LabelHpoId />}
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" inputClassName="uppercase-input" />
                <Input type="textarea" id="phenoterms" ref="phenoterms" label={<LabelPhenoTerms />} rows="5"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />

Enter phenotypes that are NOT present in Group if they are specifically noted in the paper

                <Input type="text" id="nothpoid" ref="nothpoid" label={<LabelHpoId not />}
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" inputClassName="uppercase-input" />
                <Input type="textarea" id="phenoterms" ref="phenoterms" label={<LabelPhenoTerms not />} rows="5"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />

        );
    }
});

// HTML labels for inputs follow.
var LabelOrphanetId = React.createClass({
    render: function() {
        return Orphanet common diagnosis:;
    }
});

// HTML labels for inputs follow.
var LabelHpoId = React.createClass({
    propTypes: {
        not: React.PropTypes.bool
    },

    render: function() {
        return {this.props.not ? NOT : null} HPO ID(s):;
    }
});

// HTML labels for inputs follow.
var LabelPhenoTerms = React.createClass({
    propTypes: {
        not: React.PropTypes.bool
    },

    render: function() {
        return Free text{this.props.not ?  NOT : null} phenotype terms:;
    }
});

var GroupDemographics = React.createClass({
    render: function() {
        return (

                <Input type="text" id="malecount" ref="malecount" label="# males:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />
                <Input type="text" id="femalecount" ref="malecount" label="# females:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" />
                <Input type="select" id="country" ref="country" label="Country of Origin:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group">
                    {country_codes.map(function(country_code) {
                        return <option key={country_code.code} value={country_code.code}>{country_code.name}</option>;
                    })}
                </Input>
                <Input type="select" id="ethnicity" ref="ethnicity" label="Ethnicity:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group">
                    <option value="hispanic">Hispanic or Latino</option>
                    <option value="nonhispanic">Not Hispanic or Latino</option>
                </Input>
                <Input type="select" id="race" ref="race" label="Race:"
                    labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group">
                    <option value="nativeamerican">American Indian or Alaska Native</option>
                    <option value="asian">Asian</option>
                    <option value="black">Black</option>
                    <option value="pacificislander">Native Hawaiian or Other Pacific Islander</option>
                    <option value="white">White</option>
                    <option value="mixed">Mixed</option>
                    <option value="unknown">Unknown</option>
                </Input>

Age Range

                    <Input type="select" id="agerangetype" ref="agerangetype" label="Type:"
                        labelClassName="col-sm-5 control-label" wrapperClassName="col-sm-7" groupClassName="form-group">
                        <option value="onset">Onset</option>
                        <option value="report">Report</option>
                        <option value="diagnosis">Diagnosis</option>
                        <option value="death">Death</option>
                    </Input>

        );
    }
});
<|endoftext|>


<commit_before>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
      emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
      unorderedListRE = /[*+-]\s/;

  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
    if (cm.getOption("disableInput")) return CodeMirror.Pass;
    var ranges = cm.listSelections(), replacements = [];
    for (var i = 0; i < ranges.length; i++) {
      var pos = ranges[i].head;
      var eolState = cm.getStateAfter(pos.line);
      var inList = eolState.list !== false;
      var inQuote = eolState.quote !== 0;

      var line = cm.getLine(pos.line), match = listRE.exec(line);
      var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch));
      if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
        cm.execCommand("newlineAndIndent");
        return;
      }
      if (emptyListRE.test(line)) {
        if (!/>\s*$/.test(line)) cm.replaceRange("", {
          line: pos.line, ch: 0
        }, {
          line: pos.line, ch: pos.ch + 1
        });
        replacements[i] = "\n";
      } else {
        var indent = match[1], after = match[5];
        var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
          ? match[2].replace("x", " ")
          : (parseInt(match[3], 10) + 1) + match[4];

        replacements[i] = "\n" + indent + bullet + after;
      }
    }

    cm.replaceSelections(replacements);
  };
});
<commit_msg>[continuelist addon] Increment numbers when item is added to the middle<commit_after>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
      emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
      unorderedListRE = /[*+-]\s/;

  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
    if (cm.getOption("disableInput")) return CodeMirror.Pass;
    var ranges = cm.listSelections(), replacements = [];
    for (var i = 0; i < ranges.length; i++) {
      var pos = ranges[i].head;
      var eolState = cm.getStateAfter(pos.line);
      var inList = eolState.list !== false;
      var inQuote = eolState.quote !== 0;

      var line = cm.getLine(pos.line), match = listRE.exec(line);
      var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch));
      if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
        cm.execCommand("newlineAndIndent");
        return;
      }
      if (emptyListRE.test(line)) {
        if (!/>\s*$/.test(line)) cm.replaceRange("", {
          line: pos.line, ch: 0
        }, {
          line: pos.line, ch: pos.ch + 1
        });
        replacements[i] = "\n";
      } else {
        var indent = match[1], after = match[5];
        var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
          ? match[2].replace("x", " ")
          : (parseInt(match[3], 10) + 1) + match[4];

        replacements[i] = "\n" + indent + bullet + after;

        incrementRemainingMarkdownListNumbers(cm, pos);
      }
    }

    cm.replaceSelections(replacements);
  };

  // Auto-updating Markdown list numbers when a new item is added to the
  // middle of a list
  function incrementRemainingMarkdownListNumbers(cm, pos) {
    var startLine = pos.line, lookAhead = 0, skipCount = 0;
    var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1];

    do {
      lookAhead += 1;
      var nextLineNumber = startLine + lookAhead;
      var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine);

      if (nextItem) {
        var nextIndent = nextItem[1];
        var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount);
        var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber;

        if (startIndent === nextIndent) {
          if (newNumber === nextNumber) itemNumber = nextNumber + 1;
          if (newNumber > nextNumber) itemNumber = newNumber + 1;
          cm.replaceRange(
            nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
          {
            line: nextLineNumber, ch: 0
          }, {
            line: nextLineNumber, ch: nextLine.length
          });
        } else {
          if (startIndent.length > nextIndent.length) return;
          // This doesn't run if the next line immediatley indents, as it is
          // not clear of the users intention (new indented item or same level)
          if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return;
          skipCount += 1;
        }
      }
    } while (nextItem);
  }
});
<|endoftext|>


<commit_before>import Ember from 'ember'
const {
  typeOf
} = Ember
import PropTypes, {validators} from '../utils/prop-types'
import config from 'ember-get-config'

const helpers = {
  /* eslint-disable complexity */
  validateProperty (ctx, name, def) {
    const value = ctx.get(name)

    if (value === undefined) {
      if (!def.required) {
        return
      }

      Ember.Logger.warn(`Missing required property ${name}`)

      return
    }

    if (def.type in validators) {
      validators[def.type](ctx, name, value, def, true)
    } else {
      Ember.Logger.warn(`Unknown propType ${def.type}`)
    }
  },
  /* eslint-enable complexity */

  validatePropTypes (ctx) {
    if (!config || config.environment === 'production') {
      return
    }

    const propTypesArray = [].concat(ctx.get('propTypes'))
    propTypesArray.forEach((property) => {
      if (!property) {
        return
      }

      Object.keys(property).forEach(name => {
        const def = property[name]

        if (def === undefined) {
          Ember.Logger.warn(`propType for ${name} is unknown`)
          return
        }

        helpers.validateProperty(ctx, name, def)
      })
    })
  }
}

export default Ember.Mixin.create({
  concatenatedProperties: ['propTypes', 'getDefaultProps'],

  init () {
    helpers.validatePropTypes(this)

    const keys = Object.keys(this)
    const defaults = [].concat(this.get('getDefaultProps'))
    defaults.forEach((propsFunction) => {
      if (typeOf(propsFunction) !== 'function') {
        return
      }

      const defaultProps = propsFunction.apply(this)
      keys.forEach((key) => {
        delete defaultProps[key]
      })

      this.setProperties(defaultProps)
    })
    this._super(...arguments)
  }
})

export {helpers, PropTypes}
<commit_msg>More variable name tweaking<commit_after>import Ember from 'ember'
const {
  typeOf
} = Ember
import PropTypes, {validators} from '../utils/prop-types'
import config from 'ember-get-config'

const helpers = {
  /* eslint-disable complexity */
  validateProperty (ctx, name, def) {
    const value = ctx.get(name)

    if (value === undefined) {
      if (!def.required) {
        return
      }

      Ember.Logger.warn(`Missing required property ${name}`)

      return
    }

    if (def.type in validators) {
      validators[def.type](ctx, name, value, def, true)
    } else {
      Ember.Logger.warn(`Unknown propType ${def.type}`)
    }
  },
  /* eslint-enable complexity */

  validatePropTypes (ctx) {
    if (!config || config.environment === 'production') {
      return
    }

    const propTypesArray = [].concat(ctx.get('propTypes'))
    propTypesArray.forEach((propType) => {
      if (!propType) {
        return
      }

      Object.keys(propType).forEach(name => {
        const def = propType[name]

        if (def === undefined) {
          Ember.Logger.warn(`propType for ${name} is unknown`)
          return
        }

        helpers.validateProperty(ctx, name, def)
      })
    })
  }
}

export default Ember.Mixin.create({
  concatenatedProperties: ['propTypes', 'getDefaultProps'],

  init () {
    helpers.validatePropTypes(this)

    const keys = Object.keys(this)
    const defaults = [].concat(this.get('getDefaultProps'))
    defaults.forEach((propsFunction) => {
      if (typeOf(propsFunction) !== 'function') {
        return
      }

      const defaultProps = propsFunction.apply(this)
      keys.forEach((key) => {
        delete defaultProps[key]
      })

      this.setProperties(defaultProps)
    })
    this._super(...arguments)
  }
})

export {helpers, PropTypes}
<|endoftext|>


<commit_before>/**
 * The PropTypesMixin definition
 */
import Ember from 'ember'
const {Mixin, get, getWithDefault, typeOf} = Ember
import config from 'ember-get-config'

import PropTypes, {getDef, logger, validators} from '../utils/prop-types'

export const settings = {
  requireComponentPropTypes: getWithDefault(
    config, 'ember-prop-types.requireComponentPropTypes', false
  ),
  spreadProperty: get(config, 'ember-prop-types.spreadProperty'),
  throwErrors: getWithDefault(config, 'ember-prop-types.throwErrors', false),
  validate: get(config, 'ember-prop-types.validate'),
  validateOnUpdate: getWithDefault(config, 'ember-prop-types.validateOnUpdate', false)
}

export const helpers = {
  handleError (ctx, message) {
    logger.warn(ctx, message, settings.throwErrors)
  },

  /* eslint-disable complexity */
  validateProperty (ctx, name, def) {
    let value = get(ctx, name)

    if (value === undefined && settings.spreadProperty) {
      value = get(ctx, `${settings.spreadProperty}.${name}`)
    }

    if (value === undefined) {
      if (!def.required) {
        return
      }

      helpers.handleError(ctx, `Missing required property ${name}`)

      return
    }

    if (def.type in validators) {
      validators[def.type](ctx, name, value, def, true, settings.throwErrors)
    } else {
      helpers.handleError(ctx, `Unknown propType ${def.type}`)
    }
  },
  /* eslint-enable complexity */

  validatePropTypes (ctx) {
    const disabledForEnv = settings.validate === false
    const isProduction = !config || config.environment === 'production'
    const settingMissing = settings.validate === undefined

    if (
      disabledForEnv ||
      (settingMissing && isProduction)
    ) {
      return
    }

    const propTypesArray = [].concat(ctx.get('propTypes'))
    propTypesArray.forEach((propType) => {
      if (!propType) {
        return
      }

      Object.keys(propType).forEach(name => {
        const def = getDef(propType[name])

        if (def === undefined) {
          helpers.handleError(ctx, `propType for ${name} is unknown`)
          return
        }

        if (settings.validateOnUpdate) {
          ctx.addObserver(name, ctx, function () {
            if (def.updatable === false) {
              helpers.handleError(ctx, `${name} should not be updated`)
              return
            }

            helpers.validateProperty(this, name, def)
          })
        }

        helpers.validateProperty(ctx, name, def)
      })
    })
  }
}

export default Mixin.create({
  concatenatedProperties: ['propTypes', 'getDefaultProps'],

  getDefaultProps () {
    // Maintain compatibility for 2.x users calling this._super
    return {}
  },

  init () {
    helpers.validatePropTypes(this)

    // Note defaults is a concatenated property so this is actually an array
    // of getDefaultProps() methods all the way up the inheritance chain
    const defaults = this.get('getDefaultProps')

    // Keep a record of any properties that came from a getDefaultProps call
    // - let child getDefaultProps functions take precedence over parents
    // - let child getDefaultProps have _access_ to parent values set by getDefaultProps
    // - don't apply defaults for properties set explicitly on the target object
    const defaultedProps = {}

    defaults.forEach((propsFunction) => {
      // If for some reason getDefaultProps() isn't a function we'll simply
      // ignore it. In the future we might want to actually assert an error
      // here.
      if (typeOf(propsFunction) !== 'function') return

      // Now let's actually call the getDefaultProps() method
      const defaultProps = propsFunction.apply(this)

      // Now iterate defaults and remove any properties that already have
      // values set on this instance _that didn't come from a previous
      // getDefaultProps_
      Object.keys(defaultProps).forEach((key) => {
        if (this.get(key) !== undefined && get(defaultedProps, key) === undefined) {
          delete defaultProps[key]
        }
      })

      // Record the properties that were defaulted
      Object.assign(defaultedProps, defaultProps)

      // Apply the defaults for this layer of the hierarchy immediately
      // @sglanzer 2017-05-29 PR #118 delayed the execution of the setProperties
      // until the end of all the getDefaultProps() calls, which meant that parent
      // layers in the getDefaultProps() hierarchy weren't available for use in
      // child functions.  This broke backwards compatibility
      // concatenated defaults with the results of this method call.
      this.setProperties(defaultProps)
    })

    this._super(...arguments)
  }
})

export {PropTypes}
<commit_msg>fall back to Ember.assign/merge for browsers without Object.assign support<commit_after>/**
 * The PropTypesMixin definition
 */
import Ember from 'ember'
const {Mixin, get, getWithDefault, typeOf} = Ember
import config from 'ember-get-config'

import PropTypes, {getDef, logger, validators} from '../utils/prop-types'

const assign = Object.assign || Ember.assign || Ember.merge // eslint-disable-line

export const settings = {
  requireComponentPropTypes: getWithDefault(
    config, 'ember-prop-types.requireComponentPropTypes', false
  ),
  spreadProperty: get(config, 'ember-prop-types.spreadProperty'),
  throwErrors: getWithDefault(config, 'ember-prop-types.throwErrors', false),
  validate: get(config, 'ember-prop-types.validate'),
  validateOnUpdate: getWithDefault(config, 'ember-prop-types.validateOnUpdate', false)
}

export const helpers = {
  handleError (ctx, message) {
    logger.warn(ctx, message, settings.throwErrors)
  },

  /* eslint-disable complexity */
  validateProperty (ctx, name, def) {
    let value = get(ctx, name)

    if (value === undefined && settings.spreadProperty) {
      value = get(ctx, `${settings.spreadProperty}.${name}`)
    }

    if (value === undefined) {
      if (!def.required) {
        return
      }

      helpers.handleError(ctx, `Missing required property ${name}`)

      return
    }

    if (def.type in validators) {
      validators[def.type](ctx, name, value, def, true, settings.throwErrors)
    } else {
      helpers.handleError(ctx, `Unknown propType ${def.type}`)
    }
  },
  /* eslint-enable complexity */

  validatePropTypes (ctx) {
    const disabledForEnv = settings.validate === false
    const isProduction = !config || config.environment === 'production'
    const settingMissing = settings.validate === undefined

    if (
      disabledForEnv ||
      (settingMissing && isProduction)
    ) {
      return
    }

    const propTypesArray = [].concat(ctx.get('propTypes'))
    propTypesArray.forEach((propType) => {
      if (!propType) {
        return
      }

      Object.keys(propType).forEach(name => {
        const def = getDef(propType[name])

        if (def === undefined) {
          helpers.handleError(ctx, `propType for ${name} is unknown`)
          return
        }

        if (settings.validateOnUpdate) {
          ctx.addObserver(name, ctx, function () {
            if (def.updatable === false) {
              helpers.handleError(ctx, `${name} should not be updated`)
              return
            }

            helpers.validateProperty(this, name, def)
          })
        }

        helpers.validateProperty(ctx, name, def)
      })
    })
  }
}

export default Mixin.create({
  concatenatedProperties: ['propTypes', 'getDefaultProps'],

  getDefaultProps () {
    // Maintain compatibility for 2.x users calling this._super
    return {}
  },

  init () {
    helpers.validatePropTypes(this)

    // Note defaults is a concatenated property so this is actually an array
    // of getDefaultProps() methods all the way up the inheritance chain
    const defaults = this.get('getDefaultProps')

    // Keep a record of any properties that came from a getDefaultProps call
    // - let child getDefaultProps functions take precedence over parents
    // - let child getDefaultProps have _access_ to parent values set by getDefaultProps
    // - don't apply defaults for properties set explicitly on the target object
    const defaultedProps = {}

    defaults.forEach((propsFunction) => {
      // If for some reason getDefaultProps() isn't a function we'll simply
      // ignore it. In the future we might want to actually assert an error
      // here.
      if (typeOf(propsFunction) !== 'function') return

      // Now let's actually call the getDefaultProps() method
      const defaultProps = propsFunction.apply(this)

      // Now iterate defaults and remove any properties that already have
      // values set on this instance _that didn't come from a previous
      // getDefaultProps_
      Object.keys(defaultProps).forEach((key) => {
        if (this.get(key) !== undefined && get(defaultedProps, key) === undefined) {
          delete defaultProps[key]
        }
      })

      // Record the properties that were defaulted
      assign(defaultedProps, defaultProps)

      // Apply the defaults for this layer of the hierarchy immediately
      // @sglanzer 2017-05-29 PR #118 delayed the execution of the setProperties
      // until the end of all the getDefaultProps() calls, which meant that parent
      // layers in the getDefaultProps() hierarchy weren't available for use in
      // child functions.  This broke backwards compatibility
      // concatenated defaults with the results of this method call.
      this.setProperties(defaultProps)
    })

    this._super(...arguments)
  }
})

export {PropTypes}
<|endoftext|>


<commit_before>import DS from 'ember-data';
import BaseModel from './base';

export default BaseModel.extend({
  title: DS.attr('rendered'),
  media_details: DS.attr(),
  source_url: DS.attr('string'),
  caption: DS.attr('rendered')
});
<commit_msg>Add description for media HTML<commit_after>import DS from 'ember-data';
import BaseModel from './base';

export default BaseModel.extend({
  title: DS.attr('rendered'),
  media_details: DS.attr(),
  source_url: DS.attr('string'),
  caption: DS.attr('rendered'),
  description: DS.attr('rendered')
});
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';
import {t} from '../../util.js';

import * as Control from '../../controls.js';
import {Col} from 'react-bootstrap';

export class EditClientRate extends Component {
  static propTypes = {
    rate: PropTypes.shape({
      type: PropTypes.oneOf(Control.invoiceLineTypes).isRequired,
      hoursInDay: PropTypes.number,
      value: PropTypes.number.isRequired, // TODO: rename to price?
      description: PropTypes.string,
    }).isRequired,
    onChange: PropTypes.func.isRequired,
  }
  render() {
    const {rate} = this.props;
    return (

        <Col sm={4}>
          <Control.NumericInput
            prefix="€"
            label={t('client.rate.defaultPrice')}
            value={rate.value}
            onChange={value => this.props.onChange({...rate, value: value})}
            data-tst="client.rate.value"
          />
        </Col>

        <Col sm={4}>
          <Control.NumericInput
            label={t('client.rate.hoursInDay')}
            value={rate.hoursInDay}
            onChange={value => this.props.onChange({...rate, hoursInDay: value})}
            data-tst="client.rate.hoursInDay"
          />
        </Col>
        <Col sm={4}>
          <Control.InvoiceLineTypeSelect
            label={t('config.defaultInvoiceLineType')}
            type={rate.type}
            onChange={value => this.props.onChange({...rate, type: value})}
            data-tst="client.rate.type"
          />
        </Col>

        <Col sm={4}>
          <Control.StringInput
            label={t('client.rate.desc')}
            value={rate.description}
            onChange={value => this.props.onChange({...rate, description: value})}
            data-tst="client.rate.desc"
          />
        </Col>

    );
  }
}
<commit_msg>allow floats for default client rate<commit_after>import React, {Component, PropTypes} from 'react';
import {t} from '../../util.js';

import * as Control from '../../controls.js';
import {Col} from 'react-bootstrap';

export class EditClientRate extends Component {
  static propTypes = {
    rate: PropTypes.shape({
      type: PropTypes.oneOf(Control.invoiceLineTypes).isRequired,
      hoursInDay: PropTypes.number,
      value: PropTypes.number.isRequired, // TODO: rename to price?
      description: PropTypes.string,
    }).isRequired,
    onChange: PropTypes.func.isRequired,
  }
  render() {
    const {rate} = this.props;
    return (

        <Col sm={4}>
          <Control.NumericInput
            prefix="€"
            label={t('client.rate.defaultPrice')}
            float
            value={rate.value}
            onChange={value => this.props.onChange({...rate, value: value})}
            data-tst="client.rate.value"
          />
        </Col>

        <Col sm={4}>
          <Control.NumericInput
            label={t('client.rate.hoursInDay')}
            value={rate.hoursInDay}
            onChange={value => this.props.onChange({...rate, hoursInDay: value})}
            data-tst="client.rate.hoursInDay"
          />
        </Col>
        <Col sm={4}>
          <Control.InvoiceLineTypeSelect
            label={t('config.defaultInvoiceLineType')}
            type={rate.type}
            onChange={value => this.props.onChange({...rate, type: value})}
            data-tst="client.rate.type"
          />
        </Col>

        <Col sm={4}>
          <Control.StringInput
            label={t('client.rate.desc')}
            value={rate.description}
            onChange={value => this.props.onChange({...rate, description: value})}
            data-tst="client.rate.desc"
          />
        </Col>

    );
  }
}
<|endoftext|>