<commit_before>import React from 'react'
import {InputGroup, Radio, Table, Glyphicon, Button} from 'react-bootstrap'

import DatePicker from 'react-bootstrap-date-picker'

import {ContentForHeader} from '../../components/Header'
import {Form, HorizontalFormField} from '../../components/FormField'
import RadioGroup from '../../components/RadioGroup'
import Breadcrumbs from '../../components/Breadcrumbs'
import Autocomplete from '../../components/Autocomplete'
import TextEditor from '../../components/TextEditor'

export default class ReportNew extends React.Component {
	static useNavigation = false

	constructor(props) {
		super(props)
		this.state = {
			report: {engagementIntent: '', location: ''},
			reportAtmosphere: 'positive',
			attendees: [],
			poams: [],
		}

		this.onFormChange = this.onFormChange.bind(this)
		this.onAtmosphereChange = this.onAtmosphereChange.bind(this)
		this.addAttendee = this.addAttendee.bind(this)
		this.addPoam = this.addPoam.bind(this)
	}

	render() {
		return (

				<ContentForHeader>

Create a new Report

				</ContentForHeader>

				<Breadcrumbs items={[['EF4', '/organizations/ef4'], ['Submit a report', '/reports/new']]} />

				<Form formFor={this.state.report} onChange={this.onFormChange} horizontal>
					<fieldset>
						<legend>Engagement details Required</legend>

						<HorizontalFormField id="engagementIntent" label="Meeting subject" placeholder="What happened?" data-focus>
							<HorizontalFormField.Col>{this.subjectCharactersRemaining()}</HorizontalFormField.Col>
						</HorizontalFormField>

						<HorizontalFormField id="engagementDate">
							<DatePicker placeholder="When did it happen?">
								<InputGroup.Addon>📆</InputGroup.Addon>
							</DatePicker>
						</HorizontalFormField>

						<HorizontalFormField id="engagementLocation" addon="📍">
							<Autocomplete value={this.state.report.location} placeholder="Where did it happen?" url="/api/locations/search" />
						</HorizontalFormField>

						<HorizontalFormField id="atmosphere">
							<RadioGroup onChange={this.onAtmosphereChange}>
								<Radio value="positive">👍</Radio>
								<Radio value="neutral">😐</Radio>
								<Radio value="negative">👎</Radio>
							</RadioGroup>
						</HorizontalFormField>

						<HorizontalFormField id="atmosphereDetails" className={this.state.reportAtmosphere === 'positive' && 'hide'} />
					</fieldset>

					<fieldset>
						<legend>Meeting attendance Required</legend>

						<HorizontalFormField id="addAttendee">
							<Autocomplete value="" placeholder="Who was there?" url="/api/people/search" onChange={this.addAttendee} template={person =>
								{person.name} {person.rank.toUpperCase()}
							} clearOnSelect={true} />

										Name
										Position

									{this.state.attendees.map(person =>

											<Glyphicon glyph="remove-sign" style={{cursor: 'pointer'}} />

										{person.name} {person.rank.toUpperCase()}
										{person.role}

)}

							<HorizontalFormField.Col style={{marginTop: '-28px'}}>

Shortcuts

								<Button bsStyle="link">Add myself</Button>
								<Button bsStyle="link">Add my principal (Aarash Aarif)</Button>
								<Button bsStyle="link">Mohammad Aaron (Recent)</Button>
							</HorizontalFormField.Col>
						</HorizontalFormField>
					</fieldset>

					<fieldset>
						<legend>Milestones</legend>

						<HorizontalFormField id="poams">
							<Autocomplete value="" url="/api/poams/search" onChange={this.addPoam} template={poam =>
								{poam.shortName} - {poam.longName}
							} clearOnSelect={true} />

										Name
										AO

									{this.state.poams.map(poam =>

											<Glyphicon glyph="remove-sign" style={{cursor: 'pointer'}} />

										{poam.longName}
										{poam.shortName}

)}

							<HorizontalFormField.Col style={{marginTop: '-28px'}}>

Shortcuts

								<Button bsStyle="link">Add "Tracking your expenses"</Button>
								<Button bsStyle="link">Add "Getting a budget in place"</Button>
							</HorizontalFormField.Col>
						</HorizontalFormField>
					</fieldset>

					<fieldset>
						<legend>Meeting discussion Required</legend>

						<TextEditor label="Discussion outcome" />
						<TextEditor label="Next steps" style={{marginTop: '5rem'}} />
					</fieldset>
				</Form>

		)
	}

	onFormChange(event) {
		this.setState({report: this.state.report})
	}

	onAtmosphereChange(atmosphere) {
		this.setState({reportAtmosphere: atmosphere})
	}

	subjectCharactersRemaining() {
		let charactersRemaining = 250 - this.state.report.engagementIntent.length
		return charactersRemaining + " characters remaining"
	}

	addAttendee(attendee) {
		let attendees = this.state.attendees.slice(0)
		attendees.push(attendee)
		this.setState({attendees})
	}

	removeAttendee(attendee) {
		let attendees = this.state.attendees.slice(0)
		attendees.splice(attendees.indexOf(attendee), 1)
		this.setState({attendees})
	}

	addPoam(poam) {
		let poams = this.state.poams.slice(0)
		poams.push(poam)
		this.setState({poams})
	}

	removePoam(poam) {
		let poams = this.state.poams.slice(0)
		poams.splice(poams.indexOf(poam), 1)
		this.setState({poams})
	}
}
<commit_msg>Don't mark "Meeting discussion" section as required<commit_after>import React from 'react'
import {InputGroup, Radio, Table, Glyphicon, Button} from 'react-bootstrap'

import DatePicker from 'react-bootstrap-date-picker'

import {ContentForHeader} from '../../components/Header'
import {Form, HorizontalFormField} from '../../components/FormField'
import RadioGroup from '../../components/RadioGroup'
import Breadcrumbs from '../../components/Breadcrumbs'
import Autocomplete from '../../components/Autocomplete'
import TextEditor from '../../components/TextEditor'

export default class ReportNew extends React.Component {
	static useNavigation = false

	constructor(props) {
		super(props)
		this.state = {
			report: {engagementIntent: '', location: ''},
			reportAtmosphere: 'positive',
			attendees: [],
			poams: [],
		}

		this.onFormChange = this.onFormChange.bind(this)
		this.onAtmosphereChange = this.onAtmosphereChange.bind(this)
		this.addAttendee = this.addAttendee.bind(this)
		this.addPoam = this.addPoam.bind(this)
	}

	render() {
		return (

				<ContentForHeader>

Create a new Report

				</ContentForHeader>

				<Breadcrumbs items={[['EF4', '/organizations/ef4'], ['Submit a report', '/reports/new']]} />

				<Form formFor={this.state.report} onChange={this.onFormChange} horizontal>
					<fieldset>
						<legend>Engagement details Required</legend>

						<HorizontalFormField id="engagementIntent" label="Meeting subject" placeholder="What happened?" data-focus>
							<HorizontalFormField.Col>{this.subjectCharactersRemaining()}</HorizontalFormField.Col>
						</HorizontalFormField>

						<HorizontalFormField id="engagementDate">
							<DatePicker placeholder="When did it happen?">
								<InputGroup.Addon>📆</InputGroup.Addon>
							</DatePicker>
						</HorizontalFormField>

						<HorizontalFormField id="engagementLocation" addon="📍">
							<Autocomplete value={this.state.report.location} placeholder="Where did it happen?" url="/api/locations/search" />
						</HorizontalFormField>

						<HorizontalFormField id="atmosphere">
							<RadioGroup onChange={this.onAtmosphereChange}>
								<Radio value="positive">👍</Radio>
								<Radio value="neutral">😐</Radio>
								<Radio value="negative">👎</Radio>
							</RadioGroup>
						</HorizontalFormField>

						<HorizontalFormField id="atmosphereDetails" className={this.state.reportAtmosphere === 'positive' && 'hide'} />
					</fieldset>

					<fieldset>
						<legend>Meeting attendance Required</legend>

						<HorizontalFormField id="addAttendee">
							<Autocomplete value="" placeholder="Who was there?" url="/api/people/search" onChange={this.addAttendee} template={person =>
								{person.name} {person.rank.toUpperCase()}
							} clearOnSelect={true} />

										Name
										Position

									{this.state.attendees.map(person =>

											<Glyphicon glyph="remove-sign" style={{cursor: 'pointer'}} />

										{person.name} {person.rank.toUpperCase()}
										{person.role}

)}

							<HorizontalFormField.Col style={{marginTop: '-28px'}}>

Shortcuts

								<Button bsStyle="link">Add myself</Button>
								<Button bsStyle="link">Add my principal (Aarash Aarif)</Button>
								<Button bsStyle="link">Mohammad Aaron (Recent)</Button>
							</HorizontalFormField.Col>
						</HorizontalFormField>
					</fieldset>

					<fieldset>
						<legend>Milestones</legend>

						<HorizontalFormField id="poams">
							<Autocomplete value="" url="/api/poams/search" onChange={this.addPoam} template={poam =>
								{poam.shortName} - {poam.longName}
							} clearOnSelect={true} />

										Name
										AO

									{this.state.poams.map(poam =>

											<Glyphicon glyph="remove-sign" style={{cursor: 'pointer'}} />

										{poam.longName}
										{poam.shortName}

)}

							<HorizontalFormField.Col style={{marginTop: '-28px'}}>

Shortcuts

								<Button bsStyle="link">Add "Tracking your expenses"</Button>
								<Button bsStyle="link">Add "Getting a budget in place"</Button>
							</HorizontalFormField.Col>
						</HorizontalFormField>
					</fieldset>

					<fieldset>
						<legend>Meeting discussion</legend>

						<TextEditor label="Discussion outcome" />
						<TextEditor label="Next steps" style={{marginTop: '5rem'}} />
					</fieldset>
				</Form>

		)
	}

	onFormChange(event) {
		this.setState({report: this.state.report})
	}

	onAtmosphereChange(atmosphere) {
		this.setState({reportAtmosphere: atmosphere})
	}

	subjectCharactersRemaining() {
		let charactersRemaining = 250 - this.state.report.engagementIntent.length
		return charactersRemaining + " characters remaining"
	}

	addAttendee(attendee) {
		let attendees = this.state.attendees.slice(0)
		attendees.push(attendee)
		this.setState({attendees})
	}

	removeAttendee(attendee) {
		let attendees = this.state.attendees.slice(0)
		attendees.splice(attendees.indexOf(attendee), 1)
		this.setState({attendees})
	}

	addPoam(poam) {
		let poams = this.state.poams.slice(0)
		poams.push(poam)
		this.setState({poams})
	}

	removePoam(poam) {
		let poams = this.state.poams.slice(0)
		poams.splice(poams.indexOf(poam), 1)
		this.setState({poams})
	}
}
<|endoftext|>


<commit_before>/**
 (c) Sneezry, 2015
 MIT License

 lizhe@lizhe.org
**/

(function(window, undefined) {
'use strict';

var isDelayStart = false, startCount = 0;

var timeout = document.getElementsByTagName('html')[0].getAttribute('data-delay-timeout');

var batch = document.getElementsByTagName('html')[0].getAttribute('data-delay-batch');

timeout = (!timeout || isNaN(timeout)) ? 10 : Number(timeout);

var getBgImageUrl = function(element) {
    var bgImageRawCss = getComputedStyle(element, null).getPropertyValue('background-image'),
        bgImageRawCssURL = bgImageRawCss.match(/^\s*\-webkit\-image\-set\((.*?)\)\s*$/),
        i, bgImageURL, dpr = 1;

    bgImageRawCssURL = (bgImageRawCssURL ? bgImageRawCssURL[1] : bgImageRawCss).split(',');

    for (i = 0; i < bgImageRawCssURL.length; i++) {
        if (bgImageRawCssURL[i].match(/1x\s*$/) && dpr === 1) {
            bgImageURL = bgImageRawCssURL[i].match(/url\([\s"']*(.*?)[\s"']*\)/);
        }

        if (bgImageRawCssURL[i].match(/2x\s*$/) && window.devicePixelRatio > 1 && dpr < 2) {
            dpr = 2;
            bgImageURL = bgImageRawCssURL[i].match(/url\([\s"']*(.*?)[\s"']*\)/);
        }

        if (bgImageRawCssURL[i].match(/3x\s*$/) && window.devicePixelRatio > 2 && dpr < 3) {
            dpr = 3;
            bgImageURL = bgImageRawCssURL[i].match(/url\([\s"']*(.*?)[\s"']*\)/);
        }
    }

    if (!bgImageURL) {
        bgImageURL = bgImageRawCssURL[0].match(/url\([\s"']*(.*?)[\s"']*\)/);
    }

    return bgImageURL ? bgImageURL[1] : null;
};

var listenBgLoaded = function(element, callback) {
    var src, isLoaded = false,
        timeout = element.getAttribute('data-image-delay-wait'),
        img = document.createElement('img');

    timeout = (!timeout || isNaN(timeout)) ? 5 : Number(timeout);

    element.removeAttribute('data-delay-index');
    src = getBgImageUrl(element);

    img.src = src;
    img.onload = function() {
        if (!isLoaded) {
            isLoaded = true;
            element.removeAttribute('data-image-delay-wait');
            callback();
        }
    };

    img.onerror = function() {
        if (!isLoaded) {
            isLoaded = true;
            element.removeAttribute('data-image-delay-wait');
            element.setAttribute('data-image-delay-error', 'true');
            callback();
        }
    };

    setTimeout(function() {
        if (!isLoaded) {
            isLoaded = true;
            element.removeAttribute('data-image-delay-wait');
            callback();
        }
    }, timeout * 1000);
};

var listenLoaded = function(element, callback) {
    if (element.getAttribute('data-delay-src') || element.getAttribute('data-delay-setsrc')) {
        var setsrc, src, isLoaded = false,
            timeout = element.getAttribute('data-image-delay-wait');

        timeout = (!timeout || isNaN(timeout)) ? 5 : Number(timeout);

        if (setsrc = element.getAttribute('data-delay-setsrc')) {
            element.setsrc = setsrc;
        }

        if (src = element.getAttribute('data-delay-src')) {
            element.src = src;
        }

        element.onload = function() {
            if (!isLoaded) {
                isLoaded = true;
                element.removeAttribute('data-image-delay-wait');
                callback();
            }
        };

        element.onerror = function() {
            if (!isLoaded) {
                isLoaded = true;
                element.removeAttribute('data-image-delay-wait');
                element.setAttribute('data-image-delay-error', 'true');
                callback();
            }
        };

        setTimeout(function() {
            if (!isLoaded) {
                isLoaded = true;
                element.removeAttribute('data-image-delay-wait');
                callback();
            }
        }, timeout * 1000);
    } else {
        listenBgLoaded(element, callback);
    }
};

var sortDelayList = function(list) {
    var i, currentIndex, group = [], newList = [];

    list.sort(function(a, b) {
        return a.getAttribute('data-delay-index') - b.getAttribute('data-delay-index');
    });

    currentIndex = list[0].getAttribute('data-delay-index');

    for (i = 0; i < list.length; i++) {
        if (currentIndex !== list[i].getAttribute('data-delay-index')) {
            newList.push(group);
            group = [];
            currentIndex = list[i].getAttribute('data-delay-index');
        }

        group.push(list[i]);
    }

    newList.push(group);

    return newList;
};

var loadQueue = function(sortList, index) {
    index = index || 0;

    if (index >= sortList.length) {
        document.getElementsByTagName('html')[0].setAttribute('data-delay-end', 'true');
        document.getElementsByTagName('html')[0].removeAttribute('data-delay-batch');
        isDelayStart = false;
        startCount = 0;
        return;
    }

    var i, loadedLen = 0;
    for (i = 0; i < sortList[index].length; i++) {
        sortList[index][i].removeAttribute('data-delay-index');
        sortList[index][i].setAttribute('data-delay-start', 'true');
        listenLoaded(sortList[index][i], function() {
            loadedLen++;
            if (loadedLen === sortList[index].length) {
                loadQueue(sortList, index + 1);
            }
        });
    }
};

var delayStart = function() {
    var num, len, src, setsrc, batchUnitNum = 0, batchUnit = [], sortList = [], indexList = [], allElements = document.all;

    document.getElementsByTagName('html')[0].setAttribute('data-delay-start', 'true');

    for (num = 0, len = allElements.length; num < len; num++) {
        if (allElements[num].getAttribute('ng-delay-src') && !allElements[num].getAttribute('data-delay-src') && startCount < 10) {
            setTimeout(delayStart, 500);
            startCount++;
            return;
        }
    }

    if (isDelayStart) {
        return;
    } else {
        isDelayStart = true;
    }

    if (batch && !isNaN(batch) && Number(batch) > 0) {
        batch = Number(batch);
        for (num = 0, len = allElements.length; num < len; num++) {
            if (allElements[num].getAttribute('data-delay-src') ||
                allElements[num].getAttribute('data-delay-setsrc') ||
                allElements[num].getAttribute('data-image-delay') !== null ||
                allElements[num].getAttribute('data-image-delay-wait') !== null) {
                batchUnit.push(allElements[num]);
                batchUnitNum++;
                if (batchUnitNum % batch === 0) {
                    sortList.push(batchUnit);
                    batchUnit = [];
                    batchUnitNum = 0;
                }
            }
        }
        if (batchUnit.length > 0) {
            sortList.push(batchUnit);
        }
    } else {
        for (num = 0, len = allElements.length; num < len; num++) {
            if ((allElements[num].getAttribute('data-delay-src') ||
                allElements[num].getAttribute('data-delay-setsrc') ||
                allElements[num].getAttribute('data-image-delay') !== null ||
                allElements[num].getAttribute('data-image-delay-wait') !== null) &&
                !allElements[num].getAttribute('data-delay-index')) {
                var index, parent = allElements[num].parentNode;

                while(parent && parent.getAttribute) {
                    if (index = parent.getAttribute('data-delay-index')) {
                        allElements[num].setAttribute('data-delay-index', index);
                        break;
                    }

                    parent = parent.parentNode;
                }
            }
        }

        for (num = 0, len = allElements.length; num < len; num++) {
            if (allElements[num].getAttribute('data-delay-src') === null &&
                allElements[num].getAttribute('data-delay-setsrc') === null &&
                allElements[num].getAttribute('data-image-delay') === null &&
                allElements[num].getAttribute('data-image-delay-wait') === null &&
                allElements[num].getAttribute('data-delay-index')) {

                allElements[num].removeAttribute('data-delay-index');
            }
        }

        for (num = 0, len = allElements.length; num < len; num++) {
            if ((allElements[num].getAttribute('data-image-delay') !== null ||
                allElements[num].getAttribute('data-delay-src')) &&
                (!allElements[num].getAttribute('data-delay-index') ||
                isNaN(allElements[num].getAttribute('data-delay-index')))) {

                sortList.push(allElements[num]);
            } else if (allElements[num].getAttribute('data-delay-index') &&
                !isNaN(allElements[num].getAttribute('data-delay-index')) &&
                (allElements[num].getAttribute('data-image-delay') !== null ||
                allElements[num].getAttribute('data-image-delay-wait') !== null ||
                allElements[num].getAttribute('data-delay-src') ||
                allElements[num].getAttribute('data-delay-setsrc'))) {

                indexList.push(allElements[num]);
            }
        }

        if (sortList.length > 0 && indexList.length > 0) {
            sortList = [sortList].concat(sortDelayList(indexList));
        } else if (sortList.length > 0) {
            sortList = [sortList];
        } else if (indexList.length > 0) {
            sortList = sortDelayList(indexList);
        }
    }

    if (sortList.length > 0) {
        loadQueue(sortList);
    }
};

var delayInit = function() {
    document.getElementsByTagName('html')[0].removeAttribute('data-delay-start');
    document.getElementsByTagName('html')[0].removeAttribute('data-delay-end');

    if (batch) {
        document.getElementsByTagName('html')[0].setAttribute('data-delay-batch', batch);
    }
};

if (document.getElementsByTagName('html')[0].getAttribute('data-delay-passive') === null) {
    window.onload = function() {
        setTimeout(delayStart, 0);
    };

    setTimeout(delayStart, timeout * 1000);
}

window.imageDelayStart = delayStart;
window.imageDelayInit = delayInit;

})(window);<commit_msg>refactor sortDelayList for better readability<commit_after>/**
 (c) Sneezry, 2015
 MIT License

 lizhe@lizhe.org
**/

(function(window, undefined) {
'use strict';

var isDelayStart = false, startCount = 0;

var timeout = document.getElementsByTagName('html')[0].getAttribute('data-delay-timeout');

var batch = document.getElementsByTagName('html')[0].getAttribute('data-delay-batch');

timeout = (!timeout || isNaN(timeout)) ? 10 : Number(timeout);

var getBgImageUrl = function(element) {
    var bgImageRawCss = getComputedStyle(element, null).getPropertyValue('background-image'),
        bgImageRawCssURL = bgImageRawCss.match(/^\s*\-webkit\-image\-set\((.*?)\)\s*$/),
        i, bgImageURL, dpr = 1;

    bgImageRawCssURL = (bgImageRawCssURL ? bgImageRawCssURL[1] : bgImageRawCss).split(',');

    for (i = 0; i < bgImageRawCssURL.length; i++) {
        if (bgImageRawCssURL[i].match(/1x\s*$/) && dpr === 1) {
            bgImageURL = bgImageRawCssURL[i].match(/url\([\s"']*(.*?)[\s"']*\)/);
        }

        if (bgImageRawCssURL[i].match(/2x\s*$/) && window.devicePixelRatio > 1 && dpr < 2) {
            dpr = 2;
            bgImageURL = bgImageRawCssURL[i].match(/url\([\s"']*(.*?)[\s"']*\)/);
        }

        if (bgImageRawCssURL[i].match(/3x\s*$/) && window.devicePixelRatio > 2 && dpr < 3) {
            dpr = 3;
            bgImageURL = bgImageRawCssURL[i].match(/url\([\s"']*(.*?)[\s"']*\)/);
        }
    }

    if (!bgImageURL) {
        bgImageURL = bgImageRawCssURL[0].match(/url\([\s"']*(.*?)[\s"']*\)/);
    }

    return bgImageURL ? bgImageURL[1] : null;
};

var listenBgLoaded = function(element, callback) {
    var src, isLoaded = false,
        timeout = element.getAttribute('data-image-delay-wait'),
        img = document.createElement('img');

    timeout = (!timeout || isNaN(timeout)) ? 5 : Number(timeout);

    element.removeAttribute('data-delay-index');
    src = getBgImageUrl(element);

    img.src = src;
    img.onload = function() {
        if (!isLoaded) {
            isLoaded = true;
            element.removeAttribute('data-image-delay-wait');
            callback();
        }
    };

    img.onerror = function() {
        if (!isLoaded) {
            isLoaded = true;
            element.removeAttribute('data-image-delay-wait');
            element.setAttribute('data-image-delay-error', 'true');
            callback();
        }
    };

    setTimeout(function() {
        if (!isLoaded) {
            isLoaded = true;
            element.removeAttribute('data-image-delay-wait');
            callback();
        }
    }, timeout * 1000);
};

var listenLoaded = function(element, callback) {
    if (element.getAttribute('data-delay-src') || element.getAttribute('data-delay-setsrc')) {
        var setsrc, src, isLoaded = false,
            timeout = element.getAttribute('data-image-delay-wait');

        timeout = (!timeout || isNaN(timeout)) ? 5 : Number(timeout);

        if (setsrc = element.getAttribute('data-delay-setsrc')) {
            element.setsrc = setsrc;
        }

        if (src = element.getAttribute('data-delay-src')) {
            element.src = src;
        }

        element.onload = function() {
            if (!isLoaded) {
                isLoaded = true;
                element.removeAttribute('data-image-delay-wait');
                callback();
            }
        };

        element.onerror = function() {
            if (!isLoaded) {
                isLoaded = true;
                element.removeAttribute('data-image-delay-wait');
                element.setAttribute('data-image-delay-error', 'true');
                callback();
            }
        };

        setTimeout(function() {
            if (!isLoaded) {
                isLoaded = true;
                element.removeAttribute('data-image-delay-wait');
                callback();
            }
        }, timeout * 1000);
    } else {
        listenBgLoaded(element, callback);
    }
};

var groupNSortDelayList = function(list) {
    var groups = {};
    list.forEach( function(o)
    {
        var group = o.getAttribute('data-delay-index');
        groups[group] = groups[group] || [];
        groups[group].push(o);
    });

    return Object.keys(groups).sort(function (a, b) {
        return a - b;
    }).map( function( group ) {
        return groups[group];
    });
};

var loadQueue = function(sortList, index) {
    index = index || 0;

    if (index >= sortList.length) {
        document.getElementsByTagName('html')[0].setAttribute('data-delay-end', 'true');
        document.getElementsByTagName('html')[0].removeAttribute('data-delay-batch');
        isDelayStart = false;
        startCount = 0;
        return;
    }

    var i, loadedLen = 0;
    for (i = 0; i < sortList[index].length; i++) {
        sortList[index][i].removeAttribute('data-delay-index');
        sortList[index][i].setAttribute('data-delay-start', 'true');
        listenLoaded(sortList[index][i], function() {
            loadedLen++;
            if (loadedLen === sortList[index].length) {
                loadQueue(sortList, index + 1);
            }
        });
    }
};

var delayStart = function() {
    var num, len, src, setsrc, batchUnitNum = 0, batchUnit = [], sortList = [], indexList = [], allElements = document.all;

    document.getElementsByTagName('html')[0].setAttribute('data-delay-start', 'true');

    for (num = 0, len = allElements.length; num < len; num++) {
        if (allElements[num].getAttribute('ng-delay-src') && !allElements[num].getAttribute('data-delay-src') && startCount < 10) {
            setTimeout(delayStart, 500);
            startCount++;
            return;
        }
    }

    if (isDelayStart) {
        return;
    } else {
        isDelayStart = true;
    }

    if (batch && !isNaN(batch) && Number(batch) > 0) {
        batch = Number(batch);
        for (num = 0, len = allElements.length; num < len; num++) {
            if (allElements[num].getAttribute('data-delay-src') ||
                allElements[num].getAttribute('data-delay-setsrc') ||
                allElements[num].getAttribute('data-image-delay') !== null ||
                allElements[num].getAttribute('data-image-delay-wait') !== null) {
                batchUnit.push(allElements[num]);
                batchUnitNum++;
                if (batchUnitNum % batch === 0) {
                    sortList.push(batchUnit);
                    batchUnit = [];
                    batchUnitNum = 0;
                }
            }
        }
        if (batchUnit.length > 0) {
            sortList.push(batchUnit);
        }
    } else {
        for (num = 0, len = allElements.length; num < len; num++) {
            if ((allElements[num].getAttribute('data-delay-src') ||
                allElements[num].getAttribute('data-delay-setsrc') ||
                allElements[num].getAttribute('data-image-delay') !== null ||
                allElements[num].getAttribute('data-image-delay-wait') !== null) &&
                !allElements[num].getAttribute('data-delay-index')) {
                var index, parent = allElements[num].parentNode;

                while(parent && parent.getAttribute) {
                    if (index = parent.getAttribute('data-delay-index')) {
                        allElements[num].setAttribute('data-delay-index', index);
                        break;
                    }

                    parent = parent.parentNode;
                }
            }
        }

        for (num = 0, len = allElements.length; num < len; num++) {
            if (allElements[num].getAttribute('data-delay-src') === null &&
                allElements[num].getAttribute('data-delay-setsrc') === null &&
                allElements[num].getAttribute('data-image-delay') === null &&
                allElements[num].getAttribute('data-image-delay-wait') === null &&
                allElements[num].getAttribute('data-delay-index')) {

                allElements[num].removeAttribute('data-delay-index');
            }
        }

        for (num = 0, len = allElements.length; num < len; num++) {
            if ((allElements[num].getAttribute('data-image-delay') !== null ||
                allElements[num].getAttribute('data-delay-src')) &&
                (!allElements[num].getAttribute('data-delay-index') ||
                isNaN(allElements[num].getAttribute('data-delay-index')))) {

                sortList.push(allElements[num]);
            } else if (allElements[num].getAttribute('data-delay-index') &&
                !isNaN(allElements[num].getAttribute('data-delay-index')) &&
                (allElements[num].getAttribute('data-image-delay') !== null ||
                allElements[num].getAttribute('data-image-delay-wait') !== null ||
                allElements[num].getAttribute('data-delay-src') ||
                allElements[num].getAttribute('data-delay-setsrc'))) {

                indexList.push(allElements[num]);
            }
        }

        if (sortList.length > 0 && indexList.length > 0) {
            sortList = [sortList].concat(groupNSortDelayList(indexList));
        } else if (sortList.length > 0) {
            sortList = [sortList];
        } else if (indexList.length > 0) {
            sortList = groupNSortDelayList(indexList);
        }
    }

    if (sortList.length > 0) {
        loadQueue(sortList);
    }
};

var delayInit = function() {
    document.getElementsByTagName('html')[0].removeAttribute('data-delay-start');
    document.getElementsByTagName('html')[0].removeAttribute('data-delay-end');

    if (batch) {
        document.getElementsByTagName('html')[0].setAttribute('data-delay-batch', batch);
    }
};

if (document.getElementsByTagName('html')[0].getAttribute('data-delay-passive') === null) {
    window.onload = function() {
        setTimeout(delayStart, 0);
    };

    setTimeout(delayStart, timeout * 1000);
}

window.imageDelayStart = delayStart;
window.imageDelayInit = delayInit;

})(window);
<|endoftext|>


<commit_before>import React from "react";
import { connect } from "react-redux";
import ChooseDatasetSelect from "./choose-dataset-select";

const renderBareDataPath = (source, fields) => (

    {`Source: ${source || "unknown"}`}

    {`Datapath: ${fields ? fields.join("/") : "unknown"}`}

);

@connect((state) => {
  return {
    available: state.controls.available,
    datasetFields: state.controls.datasetFields,
    source: state.controls.source
  };
})
class ChooseDataset extends React.Component {

  render() {
    /* If the server hasn't yet returned the available datasets, show the
       source & raw datapath if we have one, otherwise don't render anything.
       This helps the user know what they're looking at.
     */
    if (!this.props.available) {
      return renderBareDataPath(this.props.source, this.props.datasetFields);
    }

    const selected = this.props.datasetFields;
    const options = [[]];

    this.props.available.forEach((d) => {
      if (options[0].indexOf(d[0]) === -1) options[0].push(d[0]);
    })

    for (let idx=1; idx<selected.length; idx++) {
      /* going through the fields which comprise the current dataset
      in order to create available alternatives for each field */
      options[idx] = [];
      this.props.available.forEach((ds) => {
        if (ds[idx-1] === selected[idx-1] && options[idx].indexOf(ds[idx]) === -1) {
          options[idx].push(ds[idx]);
        }
      })
    }

    const selectors = [];

    for (let i=0; i<options.length; i++) {
      selectors.push((

          <ChooseDatasetSelect
            dispatch={this.props.dispatch}
            source={this.props.source}
            choice_tree={selected.slice(0, i)}
            selected={selected[i]}
            options={options[i]}
          />

      ));
    }

    return (

        {selectors}

    );
  }
}

export default ChooseDataset;
<commit_msg>fix bug in available dataset parsing<commit_after>import React from "react";
import { connect } from "react-redux";
import ChooseDatasetSelect from "./choose-dataset-select";

const renderBareDataPath = (source, fields) => (

    {`Source: ${source || "unknown"}`}

    {`Datapath: ${fields ? fields.join("/") : "unknown"}`}

);

const checkEqualityOfArrays = (arr1, arr2, upToIdx) => {
  return arr1.slice(0, upToIdx).every((value, index) => value === arr2[index]);
};

@connect((state) => {
  return {
    available: state.controls.available,
    datasetFields: state.controls.datasetFields,
    source: state.controls.source
  };
})
class ChooseDataset extends React.Component {

  render() {
    /* If the server hasn't yet returned the available datasets, show the
       source & raw datapath if we have one, otherwise don't render anything.
       This helps the user know what they're looking at.
     */
    if (!this.props.available) {
      return renderBareDataPath(this.props.source, this.props.datasetFields);
    }

    const selected = this.props.datasetFields;
    const options = [[]];

    this.props.available.forEach((d) => {
      if (options[0].indexOf(d[0]) === -1) options[0].push(d[0]);
    });

    for (let idx=1; idx<selected.length; idx++) {
      /* going through the fields which comprise the current dataset
      in order to create available alternatives for each field */
      options[idx] = [];
      this.props.available.forEach((query) => {
        /* if the parents (and their parents etc) of this choice match,
        then we add that as a valid option */
        if (checkEqualityOfArrays(query, selected, idx) && options[idx].indexOf(query[idx]) === -1) {
          options[idx].push(query[idx]);
        }
      });
    }

    const selectors = [];

    for (let i=0; i<options.length; i++) {
      selectors.push((

          <ChooseDatasetSelect
            dispatch={this.props.dispatch}
            source={this.props.source}
            choice_tree={selected.slice(0, i)}
            selected={selected[i]}
            options={options[i]}
          />

      ));
    }

    return (

        {selectors}

    );
  }
}

export default ChooseDataset;
<|endoftext|>


<commit_before>var fs = require("fs"),
    path = require("path"),
    Mocha = require("mocha"),
    chai = require('chai'),
    should = chai.should(),
    expect = chai.expect,
    WebDriver = require("wd"),
    flow = require("..").flow,
    argv = require("optimist")
        .default("test-group", "all")
        .default("host", "127.0.0.1")
        .default("port", "4444")
        .default("timeout", "30000")
        .default("browser", "firefox")
        .default("baseUrl", "http://localhost:8080")
        .default("username", process.env["SAUCE_USERNAME"])
        .default("password", process.env["SAUCE_ACCESS_KEY"])
        .argv;

process.stdin.resume();
process.on('uncaughtException', function (err) {
    console.error('Caught exception: ' + err);
    end(err);
});

process.on('SIGINT', function () {
    console.error('Got SIGINT.');
    process.exit(2);
});

var testGroup = argv["test-group"],
    groups,
    browser,
    tests,
    desired,
    failedTests = [],
    skippedTests = [],
    passedTests = [],
    errorTests = [];

groups = JSON.parse(fs.readFileSync(path.join(__dirname + "/groups.json"), "utf8"));
tests = testGroup in groups ? groups[testGroup] : groups["all"];

browser = new WebDriver.remote(argv.host, argv.port, argv.username, argv.password);

browser.setHTTPInactivityTimeout(argv.timeout);
browser.on('status', function (info) {
    console.log('\x1b[36m%s\x1b[0m', info);
});
browser.on('command', function (meth, path) {
    console.log(' > \x1b[33m%s\x1b[0m: %s', meth, path);
});

desired = {
    browserName: "firefox",
    name: "rAppid.js [" + testGroup + "]"
};

global.browser = browser;
global.expect = expect;
global.should = should;
global.flow = flow;
global.SkipError = function (message) {
    this.message = message;
};

flow()
    .seq(function (cb) {
        browser.init(desired, cb);
    })
    .seq(function (cb) {
        // run browser tests

        flow()
            .seq(function(cb) {
                browser.get(argv.baseUrl + "/webtest/?testgroup=" + argv["test-group"], cb);
            })
            .seq("testResults", function(cb) {

                waitForComplete();

                function waitForComplete() {

                    setTimeout(function() {
                        // TODO: better make use of execute_async
                        browser.execute("return window.testResults", function(err, testResults) {

                            if (err) {
                                cb(err);
                            } else {
                                if (testResults) {
                                    cb(null, testResults);
                                } else {
                                    // another round trip
                                    waitForComplete();
                                }
                            }
                        });
                    }, 1000);
                }
            })
            .exec(function(err, results) {
                if (err) {
                    errorTests.push({
                        fileName: "browser tests",
                        error: err
                    });
                } else {
                    var testResults = results["testResults"];
                    errorTests = errorTests.concat(testResults.errorTests);
                    failedTests = failedTests.concat(testResults.failedTests);
                    skippedTests = skippedTests.concat(testResults.skippedTests);
                    passedTests = passedTests.concat(testResults.passedTests);
                }

                cb();
            });
    })
    .seqEach(tests, function (test, cb) {

        if (!test instanceof Object) {
            // TODO:
        }

        if (test.type === "wb") {
            var fileName = path.join(__dirname, "test", test.test);

            // start mocha
            var reporter,
                mocha = new Mocha({
                    timeout: argv.timeout,
                    reporter: function (r) {
                        reporter = r;

                        r.on('pass', function (test) {
                            test.fileName = test;
                            passedTests.push(test);
                        });

                        r.on('fail', function (test, err) {
                            test.fileName = fileName;
                            test.error = err;

                            if (err instanceof global.SkipError) {
                                skippedTests.push(test);
                            } else {
                                failedTests.push(test);
                            }
                        });
                    }
                });

            mocha.addFile(fileName);

            flow()
                .seq(function (cb) {
                    browser.get(argv.baseUrl + "/webtest/test/wd.html?application=" + test.app, cb);
                })
                .seq(function (cb) {
                    mocha.run(function () {
                        cb();
                    });
                })
                .exec(function (err) {
                    if (err){
                        errorTests.push({
                            fileName: fileName,
                            error: err
                        });
                    }

                    cb();
                });
        } else {
            cb();
        }

    })
    .seq(function () {

        console.log(failedTests.length === 0 && errorTests.length === 0 ? "SUCCESS" : "FAILED");

        console.log("Error Tests: " + errorTests.length);
        console.log("Failed tests: " + failedTests.length);
        console.log("Skipped tests: " + skippedTests.length);
        console.log("Passed tests: " + passedTests.length);

        console.log();
        for (var i = 0; i < failedTests.length; i++) {
            var obj = failedTests[i];

            console.log(obj.fileName);
            console.log(obj.title);
            console.log(obj.error.message || obj.error);
            console.log();

        }

    })
    .exec(function (err) {
        err && console.error(err);
        err = err || (failedTests.length > 0 ? 100 : 0);

        end(err);
    });

function end(err) {

    flow()
        .seq(function (cb) {

            if (browser) {
                browser.quit(cb);
            } else {
                cb();
            }

        })
        .seq(function (cb) {
            setTimeout(cb, 1000);
        })
        .exec(function () {
            process.exit(err || 0);
        });
}
<commit_msg>removed RunWebTests which has been moved to lib and rappidjs webtest command<commit_after><|endoftext|>


<commit_before>import Prism from "prismjs";
import marked from "marked";
import getSlug from "speakingurl";
import urlJoin from "url-join";
import uriTemplate from "uritemplate";
import store from "store2";
import axios from "axios";
import oauth from "axios-oauth-client";
import qs from "querystringify";
import urlParse from "url-parse";
import createAuthRefreshInterceptor from "axios-auth-refresh";
import { token } from "./store";

Prism.languages.json = {
  property: {
    pattern: /"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,
    greedy: true
  },
  string: {
    pattern: /"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,
    greedy: true
  },
  comment: /\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,
  number: /-?\d+\.?\d*(e[+-]?\d+)?/i,
  punctuation: /[{}[\],]/,
  operator: /:/,
  boolean: /\b(?:true|false)\b/,
  null: {
    pattern: /\bnull\b/,
    alias: "keyword"
  }
};

const highlight = function(code, lang) {
  const supported = ["xml", "json"];

  if (!supported.includes(lang)) {
    lang = "markup";
  }

  return Prism.highlight(code, Prism.languages[lang], lang);
};

marked.setOptions({
  highlight
});

const renderer = new marked.Renderer();

renderer.pre = renderer.code;
renderer.code = function(code, infostring, escaped) {
  const out = this.pre(code, infostring, escaped);
  return out.replace("<pre>", `<pre class="language-${infostring}">`);
};

const markdown = function(source) {
  return source ? marked(source, { renderer: renderer }) : "";
};

const toc = function(source) {
  if (!source) {
    return [];
  }

  const tokens = marked.lexer(source);
  const headings = tokens.filter(elem => elem.type === "heading");
  const depths = headings.map(head => head.depth);
  const minDepth = Math.min(...depths);

  return headings.map(head => ({
    text: head.text,
    level: head.depth - minDepth
  }));
};

const colorize = function(str, prefix = "is-") {
  switch (str) {
    case "get":
      return `${prefix}success`;
    case "post":
      return `${prefix}link`;
    case "put":
      return `${prefix}primary`;
    case "patch":
      return `${prefix}info`;
    case "delete":
      return `${prefix}danger`;
    case 200:
    case 201:
    case 202:
    case 204:
      return `${prefix}info`;
    case 401:
    case 403:
    case 404:
    case 422:
      return `${prefix}warning`;
    case 500:
      return `${prefix}danger`;
  }
};

const slugify = function(str) {
  return getSlug(str, "-");
};

const alias = str => {
  return str && str.match("json") ? "json" : "markup";
};

const stringify = obj => {
  if (obj) {
    return JSON.stringify(obj, null, "  ");
  }

  return "";
};

const expandUrl = (uri, obj) => {
  const tpl = uriTemplate.parse(uri);
  return tpl.expand(obj);
};

const actionFilter = (act, regex) => {
  return (
    act.path.match(regex) || act.method.match(regex) || act.title.match(regex)
  );
};

const filteredItem = (title, key, items) => {
  if (items.length === 0) {
    return false;
  }

  return { title: title, [key]: items };
};

const filterActions = (tagActions, regex) => {
  return tagActions
    .map(tag => {
      const children = tag.children.map(child => {
        const actions = child.actions.filter(act => actionFilter(act, regex));
        return filteredItem(child.title, "actions", actions);
      });

      return filteredItem(tag.title, "children", children.filter(Boolean));
    })
    .filter(Boolean);
};

const basePath = config => {
  if (config.basePath.endsWith("/")) {
    return config.basePath;
  } else {
    return config.basePath + "/";
  }
};

const tokenName = env => `token:${env}`;
const setToken = (env, token) => store.session.set(tokenName(env), token);
const getToken = env => store.session.get(tokenName(env));
const removeToken = env => store.session.remove(tokenName(env));

const refreshTokenName = env => `refresh-token:${env}`;
const setRefreshToken = (env, token) =>
  store.session.set(refreshTokenName(env), token);
const getRefreshToken = env => store.session.get(refreshTokenName(env));
const removeRefreshToken = env => store.session.remove(refreshTokenName(env));

const isAuth = (environment, name) => {
  return environment.auth && environment.auth.name === name;
};

const pushHistory = href => history.pushState(history.state, "", href);

const requestToken = async (client, options) => {
  const authRequest = oauth.client(client, options);
  const authCode = await authRequest();

  if (typeof authCode === "string") {
    const authParsed = qs.parse(authCode);
    return {
      accessToken: authParsed.access_token,
      refreshToken: authParsed.refresh_token
    };
  }

  return {
    accessToken: authCode.access_token,
    refreshToken: authCode.refresh_token
  };
};

const exchangeToken = async (code, options) => {
  return requestToken(axios.create(), {
    url: options.tokenUrl,
    grant_type: "authorization_code",
    client_id: options.clientId,
    client_secret: options.clientSecret,
    redirect_uri: options.callbackUrl,
    code: code
  });
};

const populate = arr => {
  return arr
    .filter(obj => obj.used)
    .reduce((prev, cur) => {
      prev[cur.name] = cur.value;
      return prev;
    }, {});
};

const allowBody = action => {
  return ["put", "post", "patch"].includes(action.method);
};

const refreshInterceptor = (env, options) => {
  const refreshToken = getRefreshToken(env);

  return async failedRequest => {
    const {
      accessToken: newAccessToken,
      refreshToken: newRefreshToken
    } = await requestToken(axios, {
      url: options.tokenUrl,
      grant_type: "refresh_token",
      client_id: options.clientId,
      client_secret: options.clientSecret,
      refresh_token: refreshToken
    });

    if (newAccessToken) {
      token.set(newAccessToken);
      setToken(env, newAccessToken);
    }

    if (newRefreshToken) {
      setRefreshToken(env, newRefreshToken);
    }

    failedRequest.response.config.headers[
      "Authorization"
    ] = `Bearer ${newAccessToken}`;
  };
};

const sendRequest = (
  env,
  environment,
  action,
  { headers, parameters, body }
) => {
  const options = {
    baseURL: environment.url,
    method: action.method,
    headers: populate(headers)
  };

  if (environment.auth) {
    switch (environment.auth.name) {
      case "basic":
        options.auth = environment.auth.options;
        break;
      case "apikey":
        options.headers[environment.auth.options.header] =
          environment.auth.options.key;
        break;
      case "oauth2":
        options.headers["Authorization"] = `Bearer ${getToken(env)}`;
        break;
    }
  }

  const expandedUrl = expandUrl(action.pathTemplate, populate(parameters));
  const destUrl = urlParse(expandedUrl, true);

  options.params = destUrl.query;

  if (allowBody(action)) {
    options.data = body;
  }

  const client = axios.create(options);

  if (isAuth(environment, "oauth2")) {
    createAuthRefreshInterceptor(
      client,
      refreshInterceptor(env, environment.auth.options)
    );
  }

  return client.request({
    url: destUrl.pathname
  });
};

const getEnv = () => store.get("env");

export {
  alias,
  allowBody,
  basePath,
  colorize,
  exchangeToken,
  expandUrl,
  filterActions,
  getEnv,
  getToken,
  highlight,
  isAuth,
  markdown,
  pushHistory,
  removeToken,
  sendRequest,
  setToken,
  slugify,
  stringify,
  toc,
  urlJoin,
  urlParse,
  setRefreshToken,
  getRefreshToken,
  removeRefreshToken
};
<commit_msg>fix missing axios options<commit_after>import Prism from "prismjs";
import marked from "marked";
import getSlug from "speakingurl";
import urlJoin from "url-join";
import uriTemplate from "uritemplate";
import store from "store2";
import axios from "axios";
import oauth from "axios-oauth-client";
import qs from "querystringify";
import urlParse from "url-parse";
import createAuthRefreshInterceptor from "axios-auth-refresh";
import { token } from "./store";

Prism.languages.json = {
  property: {
    pattern: /"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,
    greedy: true
  },
  string: {
    pattern: /"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,
    greedy: true
  },
  comment: /\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,
  number: /-?\d+\.?\d*(e[+-]?\d+)?/i,
  punctuation: /[{}[\],]/,
  operator: /:/,
  boolean: /\b(?:true|false)\b/,
  null: {
    pattern: /\bnull\b/,
    alias: "keyword"
  }
};

const highlight = function(code, lang) {
  const supported = ["xml", "json"];

  if (!supported.includes(lang)) {
    lang = "markup";
  }

  return Prism.highlight(code, Prism.languages[lang], lang);
};

marked.setOptions({
  highlight
});

const renderer = new marked.Renderer();

renderer.pre = renderer.code;
renderer.code = function(code, infostring, escaped) {
  const out = this.pre(code, infostring, escaped);
  return out.replace("<pre>", `<pre class="language-${infostring}">`);
};

const markdown = function(source) {
  return source ? marked(source, { renderer: renderer }) : "";
};

const toc = function(source) {
  if (!source) {
    return [];
  }

  const tokens = marked.lexer(source);
  const headings = tokens.filter(elem => elem.type === "heading");
  const depths = headings.map(head => head.depth);
  const minDepth = Math.min(...depths);

  return headings.map(head => ({
    text: head.text,
    level: head.depth - minDepth
  }));
};

const colorize = function(str, prefix = "is-") {
  switch (str) {
    case "get":
      return `${prefix}success`;
    case "post":
      return `${prefix}link`;
    case "put":
      return `${prefix}primary`;
    case "patch":
      return `${prefix}info`;
    case "delete":
      return `${prefix}danger`;
    case 200:
    case 201:
    case 202:
    case 204:
      return `${prefix}info`;
    case 401:
    case 403:
    case 404:
    case 422:
      return `${prefix}warning`;
    case 500:
      return `${prefix}danger`;
  }
};

const slugify = function(str) {
  return getSlug(str, "-");
};

const alias = str => {
  return str && str.match("json") ? "json" : "markup";
};

const stringify = obj => {
  if (obj) {
    return JSON.stringify(obj, null, "  ");
  }

  return "";
};

const expandUrl = (uri, obj) => {
  const tpl = uriTemplate.parse(uri);
  return tpl.expand(obj);
};

const actionFilter = (act, regex) => {
  return (
    act.path.match(regex) || act.method.match(regex) || act.title.match(regex)
  );
};

const filteredItem = (title, key, items) => {
  if (items.length === 0) {
    return false;
  }

  return { title: title, [key]: items };
};

const filterActions = (tagActions, regex) => {
  return tagActions
    .map(tag => {
      const children = tag.children.map(child => {
        const actions = child.actions.filter(act => actionFilter(act, regex));
        return filteredItem(child.title, "actions", actions);
      });

      return filteredItem(tag.title, "children", children.filter(Boolean));
    })
    .filter(Boolean);
};

const basePath = config => {
  if (config.basePath.endsWith("/")) {
    return config.basePath;
  } else {
    return config.basePath + "/";
  }
};

const tokenName = env => `token:${env}`;
const setToken = (env, token) => store.session.set(tokenName(env), token);
const getToken = env => store.session.get(tokenName(env));
const removeToken = env => store.session.remove(tokenName(env));

const refreshTokenName = env => `refresh-token:${env}`;
const setRefreshToken = (env, token) =>
  store.session.set(refreshTokenName(env), token);
const getRefreshToken = env => store.session.get(refreshTokenName(env));
const removeRefreshToken = env => store.session.remove(refreshTokenName(env));

const isAuth = (environment, name) => {
  return environment.auth && environment.auth.name === name;
};

const pushHistory = href => history.pushState(history.state, "", href);

const requestToken = async (client, options) => {
  const authRequest = oauth.client(client, options);
  const authCode = await authRequest();

  if (typeof authCode === "string") {
    const authParsed = qs.parse(authCode);
    return {
      accessToken: authParsed.access_token,
      refreshToken: authParsed.refresh_token
    };
  }

  return {
    accessToken: authCode.access_token,
    refreshToken: authCode.refresh_token
  };
};

const exchangeToken = async (code, options) => {
  return requestToken(axios.create(), {
    url: options.tokenUrl,
    grant_type: "authorization_code",
    client_id: options.clientId,
    client_secret: options.clientSecret,
    redirect_uri: options.callbackUrl,
    code: code
  });
};

const populate = arr => {
  return arr
    .filter(obj => obj.used)
    .reduce((prev, cur) => {
      prev[cur.name] = cur.value;
      return prev;
    }, {});
};

const allowBody = action => {
  return ["put", "post", "patch"].includes(action.method);
};

const refreshInterceptor = (env, options) => {
  const refreshToken = getRefreshToken(env);

  return async failedRequest => {
    const {
      accessToken: newAccessToken,
      refreshToken: newRefreshToken
    } = await requestToken(axios, {
      url: options.tokenUrl,
      grant_type: "refresh_token",
      client_id: options.clientId,
      client_secret: options.clientSecret,
      refresh_token: refreshToken
    });

    if (newAccessToken) {
      token.set(newAccessToken);
      setToken(env, newAccessToken);
    }

    if (newRefreshToken) {
      setRefreshToken(env, newRefreshToken);
    }

    failedRequest.response.config.headers[
      "Authorization"
    ] = `Bearer ${newAccessToken}`;
  };
};

const sendRequest = (
  env,
  environment,
  action,
  { headers, parameters, body }
) => {
  const client = axios.create({
    baseURL: environment.url
  });

  const options = {
    method: action.method,
    headers: populate(headers)
  };

  if (environment.auth) {
    switch (environment.auth.name) {
      case "basic":
        options.auth = environment.auth.options;
        break;
      case "apikey":
        options.headers[environment.auth.options.header] =
          environment.auth.options.key;
        break;
      case "oauth2":
        options.headers["Authorization"] = `Bearer ${getToken(env)}`;
        break;
    }
  }

  const expandedUrl = expandUrl(action.pathTemplate, populate(parameters));
  const destUrl = urlParse(expandedUrl, true);

  options.params = destUrl.query;
  options.url = destUrl.pathname;

  if (allowBody(action)) {
    options.data = body;
  }

  if (isAuth(environment, "oauth2")) {
    createAuthRefreshInterceptor(
      client,
      refreshInterceptor(env, environment.auth.options)
    );
  }

  return client.request(options);
};

const getEnv = () => store.get("env");

export {
  alias,
  allowBody,
  basePath,
  colorize,
  exchangeToken,
  expandUrl,
  filterActions,
  getEnv,
  getToken,
  highlight,
  isAuth,
  markdown,
  pushHistory,
  removeToken,
  sendRequest,
  setToken,
  slugify,
  stringify,
  toc,
  urlJoin,
  urlParse,
  setRefreshToken,
  getRefreshToken,
  removeRefreshToken
};
<|endoftext|>


<commit_before>/**
 * @author Eberhard Graether / http://egraether.com/
 */

THREE.LandscapeControls = function ( camera ) {

    THREE.EventTarget.call( this );

    var _this = this;
    STATE = { NONE : -1, ROTATE : 0, PAN : 1, ZOOM : 2 };
    var statefunc = {
        0: this.rotate.bind(this),
        1: this.pan.bind(this),
        2: this.zoom.bind(this),
    }

    this.keystate = null;
    this.camera = camera;
    this.domElement = document.getElementById("braincover");

    // API
    this.enabled = true;

    this.rotateSpeed = .4;
    this.zoomSpeed = .002;
    this.panSpeed = 0.3;
    this.clickTimeout = 200; // milliseconds

    // Picker
    this.projector = new THREE.Projector();

    // internals
    this.target = new THREE.Vector3();
    this.azimuth = 45;
    this.altitude = 45;
    this.radius = 200;

    var _state = STATE.NONE,
        _start = new THREE.Vector3(),
        _end = new THREE.Vector3();

    var _mousedowntime = 0;

    // events

    var changeEvent = { type: 'change' };

    this.update = function (flatmix) {
        var mouseChange = _end.clone().subSelf(_start);
        this.flatmix = flatmix;

        this.azlim = flatmix * 180;
        this.altlim = flatmix * 90;

        if (mouseChange.length() > 0 && statefunc[_state]) {
            if (statefunc[_state])
                statefunc[_state](mouseChange);

            this.setCamera();
            if (typeof(this.onmove) == "function")
                this.onmove();
        }
        if (this.picker)
            this.picker.setMix();

        _start = _end;
    };

    // listeners
    function keydown( event ) {
        if ( ! this.enabled ) return;

        if (event.keyCode == 17) {
            this.keystate = STATE.ZOOM;
        } else if (event.keyCode == 16) {
            this.keystate = STATE.PAN;
        } else {
            this.keystate = null;
        }

    };

    function keyup( event ) {
        if ( ! this.enabled ) return;
        this.keystate = null;
    };

    function mousedown( event ) {
        event.preventDefault();
        event.stopPropagation();

        if ( _state === STATE.NONE ) {
            _state = this.keystate ? this.keystate : event.button;
            _start = _end = this.getMouse(event);
            _mousedowntime = new Date().getTime();
        }
    };

    function mousemove( event ) {
        if ( ! this.enabled ) return;

        if ( _state === STATE.NONE ) {
            return;
        } else {
            _end = this.getMouse(event);
        }
        this.dispatchEvent( changeEvent );
    };

    function mouseup( event ) {
        if ( ! this.enabled ) return;

        event.preventDefault();
        event.stopPropagation();

        _state = STATE.NONE;
        var ctime = new Date().getTime();

        // Run picker if time since mousedown is short enough
        if ( ctime - _mousedowntime < this.clickTimeout && event.button == 0) {
            var mouse2D = this.getMouse(event).clone();
            if (this.picker !== undefined)
                this.picker.pick(mouse2D.x, mouse2D.y, this.keystate == STATE.ZOOM);
        }
    };

    function mousewheel( event ) {
        if ( ! this.enabled ) return;

        event.preventDefault();
        event.stopPropagation();

        this.wheelzoom( event );
        this.setCamera();
        this.dispatchEvent( changeEvent );

    };

    this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );

    this.domElement.addEventListener( 'mousemove', mousemove.bind(this), false );
    this.domElement.addEventListener( 'mousedown', mousedown.bind(this), false );
    this.domElement.addEventListener( 'mouseup', mouseup.bind(this), false );
    this.domElement.addEventListener( 'mousewheel', mousewheel.bind(this), false);
    this.domElement.addEventListener( 'mouseout', mouseup.bind(this), false );

    window.addEventListener( 'keydown', keydown.bind(this), false );
    window.addEventListener( 'keyup', keyup.bind(this), false );

    this.resize($("#brain").width(), $("#brain").height() );
    this.setCamera();

};

THREE.LandscapeControls.prototype = {
    _limitview: function() {
        this.altitude = this.altitude > 179.9999-this.altlim ? 179.9999-this.altlim : this.altitude;
        this.altitude = this.altitude < 0.0001+this.altlim ? 0.0001+this.altlim : this.altitude;

        if (this.azlim > this.azimuth || this.azimuth > (360 - this.azlim)) {
            var d1 = this.azlim - this.azimuth;
            var d2 = 360 - this.azlim - this.azimuth;
            this.azimuth = Math.abs(d1) > Math.abs(d2) ? 360-this.azlim : this.azlim;
        }

        var rad = this.radius, target = this.target.clone();
        if ($("#zlockwhole")[0].checked) {
            rad  = this.flatsize / 2 / this.camera.aspect;
            rad /= Math.tan(this.camera.fov / 2 * Math.PI / 180);
            rad -= this.flatoff;
            rad = this.flatmix * rad + (1 - this.flatmix) * this.radius;
        } else if (!$("#zlocknone")[0].checked) {
            rad  = this.flatsize / 4 / this.camera.aspect;
            rad /= Math.tan(this.camera.fov / 2 * Math.PI / 180);
            rad -= this.flatoff;
            rad = this.flatmix * rad + (1 - this.flatmix) * this.radius;
            if ($("#zlockleft")[0].checked) {
                target.x = this.flatmix * (-this.flatsize / 4) + (1 - this.flatmix) * target.x;
            } else if ($("#zlockright")[0].checked) {
                target.x = this.flatmix * ( this.flatsize / 4) + (1 - this.flatmix) * target.x;
            }
        }

        return {radius:rad, target:target};
    },
    handleEvent: function ( event ) {
        if ( typeof this[ event.type ] == 'function' ) {
            this[ event.type ]( event );
        }
    },
    resize: function(w, h) {
        this.domElement.style.width = w+"px";
        this.domElement.style.height = h+"px";
        if (this.picker !== undefined)
            this.picker.resize(w, h);
    },

    getMouse: function ( event ) {
        return new THREE.Vector2( event.clientX, event.clientY);
    },

    setCamera: function(az, alt, rad) {
        if (az !== undefined)
            this.azimuth = az;
        if (alt !== undefined)
            this.altitude = alt;
        if (rad !== undefined)
            this.radius = rad;

        var tar_rad = this._limitview();

        var altrad = this.altitude*Math.PI / 180;
        var azirad = (this.azimuth+90)*Math.PI / 180;

        var eye = new THREE.Vector3(
            tar_rad.radius*Math.sin(altrad)*Math.cos(azirad),
            tar_rad.radius*Math.sin(altrad)*Math.sin(azirad),
            tar_rad.radius*Math.cos(altrad)
        );

        this.camera.position.add( tar_rad.target, eye );
        this.camera.lookAt( tar_rad.target );
        if (this.picker !== undefined)
            this.picker._valid = false;
    },

    rotate: function ( mouseChange ) {
        var azdiff = -this.rotateSpeed*mouseChange.x;
        var az = this.azimuth + azdiff;
        if ( this.azlim > 0 ) {
            if ( this.azlim > az || az > (360-this.azlim)) {
                this.azimuth = azdiff < 0 ? this.azlim : 360-this.azlim;
            } else {
                this.azimuth = az - 360*Math.floor(az / 360);
            }
        } else {
            this.azimuth = az - 360*Math.floor(az / 360);
        }

        this.altitude -= this.rotateSpeed*mouseChange.y;
    },

    pan: function( mouseChange ) {
        var eye = this.camera.position.clone().subSelf(this.target);
        var right = eye.clone().crossSelf( this.camera.up );
        var up = right.clone().crossSelf(eye);
        var pan = (new THREE.Vector3()).add(
            right.setLength( this.panSpeed*mouseChange.x ),
            up.setLength( this.panSpeed*mouseChange.y ));
        this.camera.position.addSelf( pan );
        this.target.addSelf( pan );
    },

    zoom: function( mouseChange ) {
        var factor = 1.0 + mouseChange.y*this.zoomSpeed;
        this.radius *= factor;
    },

    wheelzoom: function( wheelEvent ) {
        var factor = 1.0 + this.zoomSpeed * -1 * wheelEvent.wheelDelta/10.0;
        this.radius *= factor;
    }

}
<commit_msg>made LMB pan in flat view<commit_after>/**
 * @author Eberhard Graether / http://egraether.com/
 */

THREE.LandscapeControls = function ( camera ) {

    THREE.EventTarget.call( this );

    var _this = this;
    STATE = { NONE : -1, ROTATE : 0, PAN : 1, ZOOM : 2 };
    var statefunc = {
        0: this.rotate.bind(this),
        1: this.pan.bind(this),
        2: this.zoom.bind(this),
    }

    this.keystate = null;
    this.camera = camera;
    this.domElement = document.getElementById("braincover");

    // API
    this.enabled = true;

    // Constants
    this.rotateSpeed = .4;
    this.zoomSpeed = .002;
    this.panSpeed = 0.3;
    this.clickTimeout = 200; // milliseconds

    // Picker
    this.projector = new THREE.Projector();

    // internals
    this.target = new THREE.Vector3();
    this.azimuth = 45;
    this.altitude = 45;
    this.radius = 200;

    var _state = STATE.NONE,
        _start = new THREE.Vector3(),
        _end = new THREE.Vector3();

    var _mousedowntime = 0;

    // events

    var changeEvent = { type: 'change' };

    this.update = function (flatmix) {
        var mouseChange = _end.clone().subSelf(_start);
        this.flatmix = flatmix;

        this.azlim = flatmix * 180;
        this.altlim = flatmix * 90;

        if (mouseChange.length() > 0 && statefunc[_state]) {
            if (statefunc[_state])
                statefunc[_state](mouseChange);

            this.setCamera();
            if (typeof(this.onmove) == "function")
                this.onmove();
        }
        if (this.picker)
            this.picker.setMix();

        _start = _end;
    };

    // listeners
    function keydown( event ) {
        if ( ! this.enabled ) return;

        if (event.keyCode == 17) {
            this.keystate = STATE.ZOOM;
        } else if (event.keyCode == 16) {
            this.keystate = STATE.PAN;
        } else {
            this.keystate = null;
        }

    };

    function keyup( event ) {
        if ( ! this.enabled ) return;
        this.keystate = null;
    };

    function mousedown( event ) {
        event.preventDefault();
        event.stopPropagation();

        if ( _state === STATE.NONE ) {
            _state = this.keystate ? this.keystate : event.button;
            _start = _end = this.getMouse(event);
            _mousedowntime = new Date().getTime();
        }
    };

    function mousemove( event ) {
        if ( ! this.enabled ) return;

        if ( _state === STATE.NONE ) {
            return;
        } else {
            _end = this.getMouse(event);
        }
        this.dispatchEvent( changeEvent );
    };

    function mouseup( event ) {
        if ( ! this.enabled ) return;

        event.preventDefault();
        event.stopPropagation();

        _state = STATE.NONE;
        var ctime = new Date().getTime();

        // Run picker if time since mousedown is short enough
        if ( ctime - _mousedowntime < this.clickTimeout && event.button == 0) {
            var mouse2D = this.getMouse(event).clone();
            if (this.picker !== undefined)
                this.picker.pick(mouse2D.x, mouse2D.y, this.keystate == STATE.ZOOM);
        }
    };

    function mousewheel( event ) {
        if ( ! this.enabled ) return;

        event.preventDefault();
        event.stopPropagation();

        this.wheelzoom( event );
        this.setCamera();
        this.dispatchEvent( changeEvent );

    };

    this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );

    this.domElement.addEventListener( 'mousemove', mousemove.bind(this), false );
    this.domElement.addEventListener( 'mousedown', mousedown.bind(this), false );
    this.domElement.addEventListener( 'mouseup', mouseup.bind(this), false );
    this.domElement.addEventListener( 'mousewheel', mousewheel.bind(this), false);
    this.domElement.addEventListener( 'mouseout', mouseup.bind(this), false );

    window.addEventListener( 'keydown', keydown.bind(this), false );
    window.addEventListener( 'keyup', keyup.bind(this), false );

    this.resize($("#brain").width(), $("#brain").height() );
    this.setCamera();

};

THREE.LandscapeControls.prototype = {
    _limitview: function() {
        this.altitude = this.altitude > 179.9999-this.altlim ? 179.9999-this.altlim : this.altitude;
        this.altitude = this.altitude < 0.0001+this.altlim ? 0.0001+this.altlim : this.altitude;

        if (this.azlim > this.azimuth || this.azimuth > (360 - this.azlim)) {
            var d1 = this.azlim - this.azimuth;
            var d2 = 360 - this.azlim - this.azimuth;
            this.azimuth = Math.abs(d1) > Math.abs(d2) ? 360-this.azlim : this.azlim;
        }

        var rad = this.radius, target = this.target.clone();
        if ($("#zlockwhole")[0].checked) {
            rad  = this.flatsize / 2 / this.camera.aspect;
            rad /= Math.tan(this.camera.fov / 2 * Math.PI / 180);
            rad -= this.flatoff;
            rad = this.flatmix * rad + (1 - this.flatmix) * this.radius;
        } else if (!$("#zlocknone")[0].checked) {
            rad  = this.flatsize / 4 / this.camera.aspect;
            rad /= Math.tan(this.camera.fov / 2 * Math.PI / 180);
            rad -= this.flatoff;
            rad = this.flatmix * rad + (1 - this.flatmix) * this.radius;
            if ($("#zlockleft")[0].checked) {
                target.x = this.flatmix * (-this.flatsize / 4) + (1 - this.flatmix) * target.x;
            } else if ($("#zlockright")[0].checked) {
                target.x = this.flatmix * ( this.flatsize / 4) + (1 - this.flatmix) * target.x;
            }
        }

        return {radius:rad, target:target};
    },
    handleEvent: function ( event ) {
        if ( typeof this[ event.type ] == 'function' ) {
            this[ event.type ]( event );
        }
    },
    resize: function(w, h) {
        this.domElement.style.width = w+"px";
        this.domElement.style.height = h+"px";
        if (this.picker !== undefined)
            this.picker.resize(w, h);
    },

    getMouse: function ( event ) {
        return new THREE.Vector2( event.clientX, event.clientY);
    },

    setCamera: function(az, alt, rad) {
        if (az !== undefined)
            this.azimuth = az;
        if (alt !== undefined)
            this.altitude = alt;
        if (rad !== undefined)
            this.radius = rad;

        var tar_rad = this._limitview();

        var altrad = this.altitude*Math.PI / 180;
        var azirad = (this.azimuth+90)*Math.PI / 180;

        var eye = new THREE.Vector3(
            tar_rad.radius*Math.sin(altrad)*Math.cos(azirad),
            tar_rad.radius*Math.sin(altrad)*Math.sin(azirad),
            tar_rad.radius*Math.cos(altrad)
        );

        this.camera.position.add( tar_rad.target, eye );
        this.camera.lookAt( tar_rad.target );
        if (this.picker !== undefined)
            this.picker._valid = false;
    },

    rotate: function ( mouseChange ) {
        var azdiff = -this.rotateSpeed*mouseChange.x;
        var az = this.azimuth + azdiff;
        if ( this.azlim > 0 ) {
            if ( this.azlim > az || az > (360-this.azlim)) {
                this.azimuth = azdiff < 0 ? this.azlim : 360-this.azlim;
            } else {
                this.azimuth = az - 360*Math.floor(az / 360);
            }
        } else {
            this.azimuth = az - 360*Math.floor(az / 360);
        }

        this.altitude -= this.rotateSpeed*mouseChange.y;

	// Add panning depending on flatmix
	var panMouseChange = new Object;
	panMouseChange.x = mouseChange.x * Math.pow(this.flatmix, 2);
	panMouseChange.y = mouseChange.y * Math.pow(this.flatmix, 2);
	this.pan(panMouseChange);
    },

    pan: function( mouseChange ) {
        var eye = this.camera.position.clone().subSelf(this.target);
        var right = eye.clone().crossSelf( this.camera.up );
        var up = right.clone().crossSelf(eye);
        var pan = (new THREE.Vector3()).add(
            right.setLength( this.panSpeed*mouseChange.x ),
            up.setLength( this.panSpeed*mouseChange.y ));
        this.camera.position.addSelf( pan );
        this.target.addSelf( pan );
    },

    zoom: function( mouseChange ) {
        var factor = 1.0 + mouseChange.y*this.zoomSpeed;
        this.radius *= factor;
    },

    wheelzoom: function( wheelEvent ) {
        var factor = 1.0 + this.zoomSpeed * -1 * wheelEvent.wheelDelta/10.0;
        this.radius *= factor;
    }

}
<|endoftext|>


<commit_before>import {expect} from 'chai';

describe('RequestParser', () => {

	describe("#extractRequestData", () => {

		describe("when a request don't has id in path", () => {
			it("parses to an object with an endpoint without id property");
		});

		describe("when a request has id in path", () => {
			it("parses to an object with an endpoint cointaining id property");
		});

		describe("when a request has body", () => {
			it("parses to an object with non-null body property");
		}

		describe("when a request don't has body", () => {
			it("parses to an object without body property");
		}

		it("parses to an object with non-null method property");

	});

});<commit_msg>Add Request Parser Specs<commit_after>import {expect} from 'chai';
import RequestParser from '../../src/request/request-parser';

describe('RequestParser', () => {

	describe("#extractRequestData", () => {
		const request = {
			url: '/collection',
			method: 'GET'
		};

		describe("when a request don't has id in path", () => {
			const requestData = RequestParser.extractRequestData(request);

			it("parses to an object with an endpoint without id property", () => {
				expect(requestData).to.exist;
				expect(requestData.endpoint).to.exist;
				expect(requestData.endpoint.id).to.be.undefined;
			});
		});

		describe("when a request has id in path", () => {
			request.url = "/collection/15";
			const requestData = RequestParser.extractRequestData(request);

			it("parses to an object with an endpoint cointaining id property", () => {
				expect(requestData.endpoint.id).to.exist;
			});
		});

		describe("when a request has body", () => {
			request.body = '{ "name" : "object" }';
			const requestData = RequestParser.extractRequestData(request);

			it("parses to an object with non-null body property", () => {
				expect(requestData.body).to.exist;
			});
		});

		describe("when a request don't has body", () => {
			request.body = undefined;
			const requestData = RequestParser.extractRequestData(request);

			it("parses to an object without body property", () => {
				expect(requestData.body).to.be.undefined;
			});
		});

		it("parses to an object with non-null method property", () => {
			const requestData = RequestParser.extractRequestData(request);
			expect(requestData.method).to.exist;
		});

	});

});<|endoftext|>


<commit_before>import React from 'react'
import Page from 'components/Page'
import autobind from 'autobind-decorator'
import moment from 'moment'

import {DropdownButton, MenuItem, Modal, Alert, Button} from 'react-bootstrap'

import Breadcrumbs from 'components/Breadcrumbs'
import ReportCollection from 'components/ReportCollection'
import ReportSummary from 'components/ReportSummary'
import CalendarButton from 'components/CalendarButton'
import Form from 'components/Form'

import API from 'api'
import {Report} from 'models'

var d3 = null/* required later */

const graphCss = {
	width: '100%',
	height: '300px'
}

const barColors = {
	verified: '#9CBDA4',
	unverified: '#F5D98C',
	incoming: '#DA9795',
}

const calendarButtonCss = {
	marginRight: '20px',
}

export default class RollupShow extends Page {
	static propTypes = {
		date: React.PropTypes.object,
	}

	static defaultProps = {
		date: moment(),
	}

	static contextTypes = {
		app: React.PropTypes.object.isRequired,
	}

	get dateStr() { return this.state.date.format('DD MMM YYYY') }
	get dateLongStr() { return this.state.date.format('DD MMMM YYYY') }
	get rollupStart() { return moment(this.state.date).startOf('day') }
	get rollupEnd() { return moment(this.state.date).endOf('day') }

	constructor(props) {
		super(props)
		this.state = {
			date: props.date,
			reports: {list: []},
			graphData: {},
			showEmailModal: false,
			email: {},
		}
	}

	componentDidMount() {
		super.componentDidMount()

		if (d3) {
			return
		}

		require.ensure([], () => {
			d3 = require('d3')
			this.forceUpdate()
		})
	}

	fetchData(props) {
		let rollupQuery = {
			releasedAtStart: this.rollupStart.valueOf(),
			releasedAtEnd: this.rollupEnd.valueOf(),
			engagementDateStart: moment(this.rollupStart).subtract(14, 'days').valueOf()
		}
		API.query(/* GraphQL */`
			reportList(f:search, query:$rollupQuery) {
				pageNum, pageSize, totalCount, list {
					id, state, intent, engagementDate, intent, keyOutcomes, nextSteps, cancelledReason
					author { id, name }
					location { id, name, lat, lng }
					poams { id, longName }
					comments { id }
					primaryAdvisor {
						id, name, rank
						position { organization { id, shortName, longName }}
					}
					primaryPrincipal {
						id, name, rank
						position { organization { id, shortName, longName }}
					}
					advisorOrg {
						id, shortName
						parentOrg { id, shortName }
					}
					principalOrg {
						id, shortName
						parentOrg { id, shortName }
					}
				}
			}
		`, {rollupQuery}, '($rollupQuery: ReportSearchQuery)')
		.then(data => {
			data.reportList.list = Report.fromArray(data.reportList.list)
			if (data.reportList.pageSize == null) {
				data.reportList.pageSize = 1
				data.reportList.pageNum = 1
				data.reportList.totalCount = data.reportList.list.length
			}
			this.setState({reports: data.reportList})
		})

		API.fetch(`/api/reports/rollupGraph?startDate=${this.rollupStart.valueOf()}&endDate=${this.rollupEnd.valueOf()}`
		).then(data => {
			this.setState({graphData: data})
		})
	}

	componentDidUpdate() {
		let graphData = this.state.graphData
		if (!graphData || !d3) {
			return
		}

		// Sets up the data
		var step1// = d3.nest()
		//		.key(function(d){return (d.advisorOrg && d.advisorOrg.shortName)})
		//		.rollup(function(d){return {l:d.length,s:d[0].state,r:d}})
		//		.entries(reports)

		step1 = d3.nest()
			.key((entry) => (entry.org && entry.org.shortName) || "Unknown")
			.rollup(entry => entry[0] )
			.entries(graphData)

		var svg = d3.select(this.graph),
			margin = {top: 20, right: 20, bottom: 20, left: 20},
			width = this.graph.clientWidth - margin.left - margin.right,
			height = this.graph.clientHeight - margin.top - margin.bottom,
			padding = 22,
			g = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')

		var x = d3.scaleBand().rangeRound([0,width])

		var y = d3.scaleLinear()
			.rangeRound([height, 0])

		x.domain(step1.map(d => d.key))
		y.domain([0,d3.max(step1.map(d => d.value.released))])
		d3.line()
			.x(function(d,i) { return x(d.key) })
			.y(function(d,i) { return y(d.value.released) })

		var xAxis = d3.axisBottom()
			.scale(x)
		var maxValue = d3.max(step1.map(d => d.value.released))
		var yAxis = d3.axisLeft()
			.scale(y).ticks(Math.min(maxValue+1,10))

		g.append('g')
			.attr('transform', 'translate(0,' + height + ')')
			.attr('fill', '#00f')
			.call(xAxis)

		g.append('g')
			.attr('fill', '#400')
			.call(yAxis)
			.append('text')
			.attr('fill', '#000')
			.attr('transform', 'rotate(-90)')
			.attr('y', 6)
			.attr('dy', '0.71em')
			.style('text-anchor', 'end')
			.text('# of Reports')

		g.selectAll('.bar')
			.data(step1)
			.enter().append('rect')
			.attr('class', 'line')
			.attr('width', width / step1.length - padding)
			.attr('x',function(d,i){return x(d.key) + (width / (step1.length + padding))})
			.attr('height', (d,i) => height - y((d.value.released || 0)))
			.attr('y', (d,i) => y((d.value.released || 0)) )
			.attr('fill', function(d){return barColors.verified})
	}

	render() {
		// let reports = this.state.reports.list
		let reportOTD = null //reports[0]

		// Admins can edit poams, or super users if this poam is assigned to their org.
		let currentUser = this.context.app.state.currentUser
		let canEdit = currentUser && currentUser.isAdmin()

		return (

				<Breadcrumbs items={[['Rollup', ''],[this.dateStr, 'rollup/' +this.dateStr]]} />

				{canEdit &&

						<CalendarButton onChange={this.changeRollupDate} value={this.state.date.toISOString()} style={calendarButtonCss} />

						<DropdownButton bsStyle="primary" title="Actions" id="actions" className="pull-right" onSelect={this.actionSelect}>
							<MenuItem eventKey="email">Email rollup</MenuItem>
							<MenuItem eventKey="print">Print</MenuItem>
						</DropdownButton>

				}

					Daily Rollup - {this.dateLongStr}

				<fieldset>
					<legend>Summary of Report Input</legend>
					<svg ref={el => this.graph = el} style={graphCss} />
				</fieldset>

				{reportOTD && <fieldset>
					<legend>Report of the Day</legend>

					<ReportSummary report={reportOTD} />
				</fieldset>}

				<fieldset>
					<legend>Reports - {this.dateLongStr}</legend>

					<ReportCollection paginatedReports={this.state.reports} />
				</fieldset>

				{this.renderEmailModal()}

		)
	}

	@autobind
	actionSelect(eventKey, event) {
		if (eventKey === 'email') {
			this.toggleEmailModal()
		} else {
			console.log('Unimplemented Action: ' + eventKey)
		}
	}

	@autobind
	changeRollupDate(newDate) {
		this.setState({date: moment(newDate)}, () => {
			this.loadData()
		})
	}

	@autobind
	renderEmailModal() {
		let email = this.state.email
		return <Modal show={this.state.showEmailModal} onHide={this.toggleEmailModal}>
			<Form formFor={email} onChange={this.onChange} submitText={false} >
				<Modal.Header closeButton>
					<Modal.Title>Email rollup</Modal.Title>
				</Modal.Header>

				<Modal.Body>
					{email.errors &&
						<Alert bsStyle="danger">{email.errors}</Alert>
					}

					<Form.Field id="to" />
					<Form.Field componentClass="textarea" id="comment" />
				</Modal.Body>
				<Modal.Footer>
					<Button bsStyle="primary" onClick={this.emailRollup}>Send email</Button>
				</Modal.Footer>
			</Form>
		</Modal>
	}

	@autobind
	toggleEmailModal() {
		this.setState({showEmailModal: !this.state.showEmailModal})
	}

	@autobind
	emailRollup() {
		let email = this.state.email
		if (!email.to) {
			email.errors = 'You must select a person to send this to'
			this.setState({email})
			return
		}

		email = {
			toAddresses: email.to.replace(/\s/g, '').split(/[,;]/),
		}
		API.send(`/api/reports/rollup/email?startDate=${this.rollupStart.valueOf()}&endDate=${this.rollupEnd.valueOf()}&comment=${email.comment}`, email).then (() =>
			this.setState({
				success: 'Email successfully sent',
				showEmailModal: false,
				email: {}
			})
		)
	}
}
<commit_msg>Moved Rollup Date Select out of permissions check. Closes #295<commit_after>import React from 'react'
import Page from 'components/Page'
import autobind from 'autobind-decorator'
import moment from 'moment'

import {DropdownButton, MenuItem, Modal, Alert, Button} from 'react-bootstrap'

import Breadcrumbs from 'components/Breadcrumbs'
import ReportCollection from 'components/ReportCollection'
import ReportSummary from 'components/ReportSummary'
import CalendarButton from 'components/CalendarButton'
import Form from 'components/Form'

import API from 'api'
import {Report} from 'models'

var d3 = null/* required later */

const graphCss = {
	width: '100%',
	height: '300px'
}

const barColors = {
	verified: '#9CBDA4',
	unverified: '#F5D98C',
	incoming: '#DA9795',
}

const calendarButtonCss = {
	marginRight: '20px',
}

export default class RollupShow extends Page {
	static propTypes = {
		date: React.PropTypes.object,
	}

	static defaultProps = {
		date: moment(),
	}

	static contextTypes = {
		app: React.PropTypes.object.isRequired,
	}

	get dateStr() { return this.state.date.format('DD MMM YYYY') }
	get dateLongStr() { return this.state.date.format('DD MMMM YYYY') }
	get rollupStart() { return moment(this.state.date).startOf('day') }
	get rollupEnd() { return moment(this.state.date).endOf('day') }

	constructor(props) {
		super(props)
		this.state = {
			date: props.date,
			reports: {list: []},
			graphData: {},
			showEmailModal: false,
			email: {},
		}
	}

	componentDidMount() {
		super.componentDidMount()

		if (d3) {
			return
		}

		require.ensure([], () => {
			d3 = require('d3')
			this.forceUpdate()
		})
	}

	fetchData(props) {
		let rollupQuery = {
			releasedAtStart: this.rollupStart.valueOf(),
			releasedAtEnd: this.rollupEnd.valueOf(),
			engagementDateStart: moment(this.rollupStart).subtract(14, 'days').valueOf()
		}
		API.query(/* GraphQL */`
			reportList(f:search, query:$rollupQuery) {
				pageNum, pageSize, totalCount, list {
					id, state, intent, engagementDate, intent, keyOutcomes, nextSteps, cancelledReason
					author { id, name }
					location { id, name, lat, lng }
					poams { id, longName }
					comments { id }
					primaryAdvisor {
						id, name, rank
						position { organization { id, shortName, longName }}
					}
					primaryPrincipal {
						id, name, rank
						position { organization { id, shortName, longName }}
					}
					advisorOrg {
						id, shortName
						parentOrg { id, shortName }
					}
					principalOrg {
						id, shortName
						parentOrg { id, shortName }
					}
				}
			}
		`, {rollupQuery}, '($rollupQuery: ReportSearchQuery)')
		.then(data => {
			data.reportList.list = Report.fromArray(data.reportList.list)
			if (data.reportList.pageSize == null) {
				data.reportList.pageSize = 1
				data.reportList.pageNum = 1
				data.reportList.totalCount = data.reportList.list.length
			}
			this.setState({reports: data.reportList})
		})

		API.fetch(`/api/reports/rollupGraph?startDate=${this.rollupStart.valueOf()}&endDate=${this.rollupEnd.valueOf()}`
		).then(data => {
			this.setState({graphData: data})
		})
	}

	componentDidUpdate() {
		let graphData = this.state.graphData
		if (!graphData || !d3) {
			return
		}

		// Sets up the data
		var step1// = d3.nest()
		//		.key(function(d){return (d.advisorOrg && d.advisorOrg.shortName)})
		//		.rollup(function(d){return {l:d.length,s:d[0].state,r:d}})
		//		.entries(reports)

		step1 = d3.nest()
			.key((entry) => (entry.org && entry.org.shortName) || "Unknown")
			.rollup(entry => entry[0] )
			.entries(graphData)

		var svg = d3.select(this.graph),
			margin = {top: 20, right: 20, bottom: 20, left: 20},
			width = this.graph.clientWidth - margin.left - margin.right,
			height = this.graph.clientHeight - margin.top - margin.bottom,
			padding = 22,
			g = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')

		var x = d3.scaleBand().rangeRound([0,width])

		var y = d3.scaleLinear()
			.rangeRound([height, 0])

		x.domain(step1.map(d => d.key))
		y.domain([0,d3.max(step1.map(d => d.value.released))])
		d3.line()
			.x(function(d,i) { return x(d.key) })
			.y(function(d,i) { return y(d.value.released) })

		var xAxis = d3.axisBottom()
			.scale(x)
		var maxValue = d3.max(step1.map(d => d.value.released))
		var yAxis = d3.axisLeft()
			.scale(y).ticks(Math.min(maxValue+1,10))

		g.append('g')
			.attr('transform', 'translate(0,' + height + ')')
			.attr('fill', '#00f')
			.call(xAxis)

		g.append('g')
			.attr('fill', '#400')
			.call(yAxis)
			.append('text')
			.attr('fill', '#000')
			.attr('transform', 'rotate(-90)')
			.attr('y', 6)
			.attr('dy', '0.71em')
			.style('text-anchor', 'end')
			.text('# of Reports')

		g.selectAll('.bar')
			.data(step1)
			.enter().append('rect')
			.attr('class', 'line')
			.attr('width', width / step1.length - padding)
			.attr('x',function(d,i){return x(d.key) + (width / (step1.length + padding))})
			.attr('height', (d,i) => height - y((d.value.released || 0)))
			.attr('y', (d,i) => y((d.value.released || 0)) )
			.attr('fill', function(d){return barColors.verified})
	}

	render() {
		// let reports = this.state.reports.list
		let reportOTD = null //reports[0]

		// Only admins can email the Rollup out.
		let currentUser = this.context.app.state.currentUser
		let canEdit = currentUser && currentUser.isAdmin()

		return (

				<Breadcrumbs items={[['Rollup', ''],[this.dateStr, 'rollup/' +this.dateStr]]} />

					<CalendarButton onChange={this.changeRollupDate} value={this.state.date.toISOString()} style={calendarButtonCss} />

					{canEdit &&
						<DropdownButton bsStyle="primary" title="Actions" id="actions" className="pull-right" onSelect={this.actionSelect}>
							<MenuItem eventKey="email">Email rollup</MenuItem>
							<MenuItem eventKey="print">Print</MenuItem>
						</DropdownButton>
					}

					Daily Rollup - {this.dateLongStr}

				<fieldset>
					<legend>Summary of Report Input</legend>
					<svg ref={el => this.graph = el} style={graphCss} />
				</fieldset>

				{reportOTD && <fieldset>
					<legend>Report of the Day</legend>

					<ReportSummary report={reportOTD} />
				</fieldset>}

				<fieldset>
					<legend>Reports - {this.dateLongStr}</legend>

					<ReportCollection paginatedReports={this.state.reports} />
				</fieldset>

				{this.renderEmailModal()}

		)
	}

	@autobind
	actionSelect(eventKey, event) {
		if (eventKey === 'email') {
			this.toggleEmailModal()
		} else {
			console.log('Unimplemented Action: ' + eventKey)
		}
	}

	@autobind
	changeRollupDate(newDate) {
		this.setState({date: moment(newDate)}, () => {
			this.loadData()
		})
	}

	@autobind
	renderEmailModal() {
		let email = this.state.email
		return <Modal show={this.state.showEmailModal} onHide={this.toggleEmailModal}>
			<Form formFor={email} onChange={this.onChange} submitText={false} >
				<Modal.Header closeButton>
					<Modal.Title>Email rollup</Modal.Title>
				</Modal.Header>

				<Modal.Body>
					{email.errors &&
						<Alert bsStyle="danger">{email.errors}</Alert>
					}

					<Form.Field id="to" />
					<Form.Field componentClass="textarea" id="comment" />
				</Modal.Body>
				<Modal.Footer>
					<Button bsStyle="primary" onClick={this.emailRollup}>Send email</Button>
				</Modal.Footer>
			</Form>
		</Modal>
	}

	@autobind
	toggleEmailModal() {
		this.setState({showEmailModal: !this.state.showEmailModal})
	}

	@autobind
	emailRollup() {
		let email = this.state.email
		if (!email.to) {
			email.errors = 'You must select a person to send this to'
			this.setState({email})
			return
		}

		email = {
			toAddresses: email.to.replace(/\s/g, '').split(/[,;]/),
		}
		API.send(`/api/reports/rollup/email?startDate=${this.rollupStart.valueOf()}&endDate=${this.rollupEnd.valueOf()}&comment=${email.comment}`, email).then (() =>
			this.setState({
				success: 'Email successfully sent',
				showEmailModal: false,
				email: {}
			})
		)
	}
}
<|endoftext|>