<commit_before>console.log( "==== simpread options page load ====" )

import '../assets/css/options_page.css';
import '../assets/css/option.css';
import '../vender/notify/notify.css';

import Velocity   from 'velocity';
import Notify     from 'notify';

import Tabs       from 'tabs';
import * as waves from 'waves';
import * as tt    from 'tooltip';
import Button     from 'button';
import * as side  from 'sidebar';

import { storage, STORAGE_MODE as mode } from 'storage';
import * as ss    from 'stylesheet';

import FocusOpt   from 'focusopt';
import ReadOpt    from 'readopt';

let tabsItemID = 0;

const tabsItem = [{
        name: "共通",
        value: "common",
        active : true,
        route: "#common",
    },{
        name: "聚焦模式",
        value: "focus",
        route: "#focus",
    },{
        name: "阅读模式",
        value: "read",
        route: "#read",
    },{
        name: "稍后读",
        value: "later",
        route: "#later",
}],
    headerColors = [
        "#64B5F6",
        "#81C784",
        "#9575CD",
        "#BA68C8"
],
    topColors = [
        "#2196F3",
        "#4CAF50",
        "#673AB7",
        "#9C27B0"
],
    tooltip_options = {
        target   : "name",
        position : "bottom",
        delay    : 50,
},
    menuItem = tabsItem.map( ( item, idx ) => {
       const menu = { ...item };
       switch ( idx ) {
            case 0:
                delete menu.active;
                menu.icon = ss.IconPath( "common_icon" );
                break;
            case 1:
                menu.icon = ss.IconPath( "focus_mode_icon" );
                break;
            case 2:
                menu.icon = ss.IconPath( "read_mode_icon" );
                menu.items = [
                    { name: "AAA", value: "AAA" },
                    { name: "BBB", value: "BBB" },
                    { name: "CCC", value: "CCC" }
                ]
                break;
            case 3:
                menu.icon = ss.IconPath( "read_later_icon" );
                break;
       }
       return menu;
});

console.log( menuItem );

/**
 * Add parallax scroll
 */
$( window ).scroll( (event) => {
    const $target = $( event.target ),
          scroll  = $target.scrollTop(),
          offset  = ( 0 - scroll ) / 2;
    scroll >  200 && ( $( ".header" ).css( "opacity", 1 ) );
    scroll <= 200 && ( $( ".header" ).css( "opacity", 0 ) );
    $( ".top" ).css( "transform", `translate3d(0px, ${offset}px, 0px)` );
});

/**
 * Save simpread data
 *
 * @param {string} simpread mode
 */
function save( mode ) {
    storage.Write( ()=> {
        new Notify().Render( 0, "保存成功，页面刷新后生效！" );
    });
}

/**
 * Tabs onChange event handler
 *
 * @param {jquery} prev jquery object
 * @param {event} current event
 */
function tabsOnChange( $prev, event ) {
    let $target = $( event.target );
    while ( !$target.is( "tab-label" ) ) { $target = $target.parent(); }
    const idx = $target.find( "a" ).attr( "id" );
    Render( idx );
    tabsItem.forEach( ( item, index ) => item.active = idx == index ? true : false );
}

/**
 * Tabs render
 *
 * @param {string} header background color
 */
function tabsRender( color ) {
    const tabs = <Tabs waves="sr-tabs waves-effect waves-light"
                    headerStyle={{ transition: 'all 1000ms cubic-bezier(0.23, 1, 0.32, 1) 0ms' }}
                    bgColor={ color }
                    items={ tabsItem }
                    onChange={ ( $p, evt )=>tabsOnChange( $p, evt ) }>

aaa

                        <FocusOpt option={ storage.focus } />
                        <Button type="raised" backgroundColor={ topColors[1] } width="100%" waves="sr-button waves-effect waves-button" text="保 存" onClick={ ()=>save( mode.focus ) } />

                        <ReadOpt option={ storage.read } />
                        <Button type="raised" backgroundColor={ topColors[2] } width="100%" waves="sr-button waves-effect waves-button" text="保 存" onClick={ ()=>save( mode.read ) } />

ddd

                </Tabs>;
    ReactDOM.render( tabs, $( ".tabscontainer" )[0] );
}

/**
 * navigation Render
 */
function navRender() {
    const navClick = () => {
        side.Open();
    };
    const button = <Button waves="sr-tabs waves-effect waves-circle" hoverColor="transparent" icon={ ss.IconPath( "sidebar_icon" ) } onClick={ ()=>navClick() } />;
    ReactDOM.render( button, $( ".header .nav" )[0] );
}

/**
 * sidebar Render
 */
function sidebarRender() {
    const sidebarClick = ( $target, items ) => {
        const idx = tabsItem.findIndex( item => item.value == items.value );
        tabsItem.forEach( ( item, index ) => item.active = idx == index ? true : false );
        Render( idx );
    };
    const sidebar = <side.Sidebar items={ menuItem }
                             waves="sr-tabs waves-effect waves-button"
                             tooltip={ tooltip_options }
                             header="设定" footer="关于 简悦" onClick={ ($t,o)=>sidebarClick($t,o) } />;
    ReactDOM.render( sidebar, $( ".sidebar" )[0] );
}

/**
 * Set options page style and tabs.Render()
 *
 * @param {number} headerColors index
 */
function Render( idx ) {
    $( ".top" ).css( "background-color", topColors[idx] );
    $( ".header" ).css( "background-color", topColors[idx] ).find( ".title" ).text( tabsItem[idx].name );
    tabsRender( headerColors[ idx ] );
}

/**
 * Get tabsItemID from window.location.hash exist
 */
window.location.hash && ( tabsItemID = tabsItem.findIndex( item => item.route == window.location.hash ) );
tabsItemID == -1 || tabsItemID == 0 ? tabsItemID = 0 : tabsItem.forEach( ( item, index ) => item.active = tabsItemID == index ? true : false );

/**
 * Entry:
 * - get data from chrome storage
 * - waves.Render()
 * - tooltip.Render()
 */
storage.Get( function() {
    console.log( "simpread storage get success!", storage.focus, storage.read );
    sidebarRender();
    navRender();
    Render( tabsItemID );
    tt.Render( "body" );
    waves.Render({ root: "simpread-font", name: "sr-tabs" });
});
<commit_msg>Update tabsItem.<commit_after>console.log( "==== simpread options page load ====" )

import '../assets/css/options_page.css';
import '../assets/css/option.css';
import '../vender/notify/notify.css';

import Velocity   from 'velocity';
import Notify     from 'notify';

import Tabs       from 'tabs';
import * as waves from 'waves';
import * as tt    from 'tooltip';
import Button     from 'button';
import * as side  from 'sidebar';

import { storage, STORAGE_MODE as mode } from 'storage';
import * as ss    from 'stylesheet';

import FocusOpt   from 'focusopt';
import ReadOpt    from 'readopt';

let tabsItemID = 0;

const tabsItem = [{
        name: "共通",
        value: "common",
        active : true,
        route: "#common",
    },{
        name: "聚焦模式",
        value: "focus",
        route: "#focus",
    },{
        name: "阅读模式",
        value: "read",
        route: "#read",
    },{
        name: "稍后读",
        value: "later",
        route: "#later",
    },{
        name: "关于",
        value: "about",
        route: "#about",
}],
    headerColors = [
        "#64B5F6",
        "#81C784",
        "#9575CD",
        "#BA68C8",
        "#4DB6AC"
],
    topColors = [
        "#2196F3",
        "#4CAF50",
        "#673AB7",
        "#9C27B0",
        "#009688"
],
    tooltip_options = {
        target   : "name",
        position : "bottom",
        delay    : 50,
},
    menuItem = tabsItem.map( ( item, idx ) => {
       const menu = { ...item };
       switch ( idx ) {
            case 0:
                delete menu.active;
                menu.icon = ss.IconPath( "common_icon" );
                break;
            case 1:
                menu.icon = ss.IconPath( "focus_mode_icon" );
                break;
            case 2:
                menu.icon = ss.IconPath( "read_mode_icon" );
                break;
            case 3:
                menu.icon = ss.IconPath( "read_later_icon" );
                break;
            case 4:
                menu.icon = ss.IconPath( "about_icon" );
                break;
       }
       return menu;
});

console.log( menuItem );

/**
 * Add parallax scroll
 */
$( window ).scroll( (event) => {
    const $target = $( event.target ),
          scroll  = $target.scrollTop(),
          offset  = ( 0 - scroll ) / 2;
    scroll >  200 && ( $( ".header" ).css( "opacity", 1 ) );
    scroll <= 200 && ( $( ".header" ).css( "opacity", 0 ) );
    $( ".top" ).css( "transform", `translate3d(0px, ${offset}px, 0px)` );
});

/**
 * Save simpread data
 *
 * @param {string} simpread mode
 */
function save( mode ) {
    storage.Write( ()=> {
        new Notify().Render( 0, "保存成功，页面刷新后生效！" );
    });
}

/**
 * Tabs onChange event handler
 *
 * @param {jquery} prev jquery object
 * @param {event} current event
 */
function tabsOnChange( $prev, event ) {
    let $target = $( event.target );
    while ( !$target.is( "tab-label" ) ) { $target = $target.parent(); }
    const idx = $target.find( "a" ).attr( "id" );
    Render( idx );
    tabsItem.forEach( ( item, index ) => item.active = idx == index ? true : false );
}

/**
 * Tabs render
 *
 * @param {string} header background color
 */
function tabsRender( color ) {
    const tabs = <Tabs waves="sr-tabs waves-effect waves-light"
                    headerStyle={{ transition: 'all 1000ms cubic-bezier(0.23, 1, 0.32, 1) 0ms' }}
                    bgColor={ color }
                    items={ tabsItem }
                    onChange={ ( $p, evt )=>tabsOnChange( $p, evt ) }>

Common

                        <FocusOpt option={ storage.focus } />
                        <Button type="raised" backgroundColor={ topColors[1] } width="100%" waves="sr-button waves-effect waves-button" text="保 存" onClick={ ()=>save( mode.focus ) } />

                        <ReadOpt option={ storage.read } />
                        <Button type="raised" backgroundColor={ topColors[2] } width="100%" waves="sr-button waves-effect waves-button" text="保 存" onClick={ ()=>save( mode.read ) } />

Later

About

                </Tabs>;
    ReactDOM.render( tabs, $( ".tabscontainer" )[0] );
}

/**
 * navigation Render
 */
function navRender() {
    const navClick = () => {
        side.Open();
    };
    const button = <Button waves="sr-tabs waves-effect waves-circle" hoverColor="transparent" icon={ ss.IconPath( "sidebar_icon" ) } onClick={ ()=>navClick() } />;
    ReactDOM.render( button, $( ".header .nav" )[0] );
}

/**
 * sidebar Render
 */
function sidebarRender() {
    const sidebarClick = ( $target, items ) => {
        const idx = tabsItem.findIndex( item => item.value == items.value );
        tabsItem.forEach( ( item, index ) => item.active = idx == index ? true : false );
        Render( idx );
    };
    const sidebar = <side.Sidebar items={ menuItem }
                             waves="sr-tabs waves-effect waves-button"
                             tooltip={ tooltip_options }
                             header="设定" footer=" 简悦 © 2017" onClick={ ($t,o)=>sidebarClick($t,o) } />;
    ReactDOM.render( sidebar, $( ".sidebar" )[0] );
}

/**
 * Set options page style and tabs.Render()
 *
 * @param {number} headerColors index
 */
function Render( idx ) {
    $( ".top" ).css( "background-color", topColors[idx] );
    $( ".header" ).css( "background-color", topColors[idx] ).find( ".title" ).text( tabsItem[idx].name );
    tabsRender( headerColors[ idx ] );
}

/**
 * Get tabsItemID from window.location.hash exist
 */
window.location.hash && ( tabsItemID = tabsItem.findIndex( item => item.route == window.location.hash ) );
tabsItemID == -1 || tabsItemID == 0 ? tabsItemID = 0 : tabsItem.forEach( ( item, index ) => item.active = tabsItemID == index ? true : false );

/**
 * Entry:
 * - get data from chrome storage
 * - waves.Render()
 * - tooltip.Render()
 */
storage.Get( function() {
    console.log( "simpread storage get success!", storage.focus, storage.read );
    sidebarRender();
    navRender();
    Render( tabsItemID );
    tt.Render( "body" );
    waves.Render({ root: "simpread-font", name: "sr-tabs" });
});
<|endoftext|>


<commit_before>/**
 * LegacyDashboardWidgetPopularKeywordsTable component.
 *
 * Site Kit by Google, Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __, _x } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { getTimeInSeconds, numberFormat } from '../../../../util';
import withData from '../../../../components/higherorder/withdata';
import { TYPE_MODULES } from '../../../../components/data';
import { getDataTableFromData, TableOverflowContainer } from '../../../../components/data-table';
import PreviewTable from '../../../../components/PreviewTable';
import Layout from '../../../../components/layout/layout';
import {
	isDataZeroSearchConsole,
} from '../../util';
import { getCurrentDateRangeDayCount } from '../../../../util/date-range';
import { STORE_NAME } from '../../datastore/constants';
import { STORE_NAME as CORE_SITE } from '../../../../googlesitekit/datastore/site';
const { useSelect } = Data;

const LegacyDashboardWidgetPopularKeywordsTable = ( props ) => {
	const { data } = props;
	const domain = useSelect( ( select ) => select( STORE_NAME ).getPropertyID() );
	const isDomainProperty = useSelect( ( select ) => select( STORE_NAME ).isDomainProperty() );
	let referenceSiteURL = useSelect( ( select ) => select( CORE_SITE ).getReferenceSiteURL() );
	if ( referenceSiteURL.endsWith( '/' ) ) {
		// remove the trailing slash
		referenceSiteURL = referenceSiteURL.slice( 0, -1 );
	}
	const baseServiceArgs = {
		resource_id: domain,
		num_of_days: getCurrentDateRangeDayCount(),
	};
	if ( isDomainProperty ) {
		baseServiceArgs.page = `*${ referenceSiteURL }`;
	}
	const baseServiceURL = useSelect( ( select ) => select( STORE_NAME ).getServiceURL(
		{
			path: '/performance/search-analytics',
			query: baseServiceArgs,
		} ) );

	if ( ! data || ! data.length ) {
		return null;
	}

	const headers = [
		{
			title: __( 'Top search queries for your site', 'google-site-kit' ),
			tooltip: __( 'Most searched for keywords related to your content', 'google-site-kit' ),
			primary: true,
		},
		{
			title: __( 'Clicks', 'google-site-kit' ),
			tooltip: __( 'Number of times users clicked on your content in search results', 'google-site-kit' ),
		},
		{
			title: __( 'Impressions', 'google-site-kit' ),
			tooltip: __( 'Counted each time your content appears in search results', 'google-site-kit' ),
		},
	];
	const links = [];

	const dataMapped = data.map( ( row, i ) => {
		const query = row.keys[ 0 ];
		links[ i ] = addQueryArgs( baseServiceURL, { query: `!${ query }` } );
		return [
			query,
			numberFormat( row.clicks ),
			numberFormat( row.impressions ),
		];
	} );

	const options = {
		hideHeader: false,
		chartsEnabled: false,
		links,
	};

	const dataTable = getDataTableFromData( dataMapped, headers, options );

	return (

			<Layout
				className="googlesitekit-popular-content"
				footer
				footerCTALabel={ _x( 'Search Console', 'Service name', 'google-site-kit' ) }
				footerCTALink={ baseServiceURL }
				fill
			>
				<TableOverflowContainer>
					{ dataTable }
				</TableOverflowContainer>
			</Layout>

	);
};

export default withData(
	LegacyDashboardWidgetPopularKeywordsTable,
	[
		{
			type: TYPE_MODULES,
			identifier: 'search-console',
			datapoint: 'searchanalytics',
			data: {
				dimensions: 'query',
				limit: 10,
			},
			priority: 1,
			maxAge: getTimeInSeconds( 'day' ),
			context: [ 'Dashboard' ],
		},
	],

		<Layout className="googlesitekit-popular-content" fill>
			<PreviewTable padding />
		</Layout>

,
	{
		inGrid: true,
		createGrid: true,
	},
	isDataZeroSearchConsole
);
<commit_msg>Use constants instead of index for code/site datastore.<commit_after>/**
 * LegacyDashboardWidgetPopularKeywordsTable component.
 *
 * Site Kit by Google, Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __, _x } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { getTimeInSeconds, numberFormat } from '../../../../util';
import withData from '../../../../components/higherorder/withdata';
import { TYPE_MODULES } from '../../../../components/data';
import { getDataTableFromData, TableOverflowContainer } from '../../../../components/data-table';
import PreviewTable from '../../../../components/PreviewTable';
import Layout from '../../../../components/layout/layout';
import {
	isDataZeroSearchConsole,
} from '../../util';
import { getCurrentDateRangeDayCount } from '../../../../util/date-range';
import { STORE_NAME } from '../../datastore/constants';
import { STORE_NAME as CORE_SITE } from '../../../../googlesitekit/datastore/site/constants';
const { useSelect } = Data;

const LegacyDashboardWidgetPopularKeywordsTable = ( props ) => {
	const { data } = props;
	const domain = useSelect( ( select ) => select( STORE_NAME ).getPropertyID() );
	const isDomainProperty = useSelect( ( select ) => select( STORE_NAME ).isDomainProperty() );
	let referenceSiteURL = useSelect( ( select ) => select( CORE_SITE ).getReferenceSiteURL() );
	if ( referenceSiteURL.endsWith( '/' ) ) {
		// remove the trailing slash
		referenceSiteURL = referenceSiteURL.slice( 0, -1 );
	}
	const baseServiceArgs = {
		resource_id: domain,
		num_of_days: getCurrentDateRangeDayCount(),
	};
	if ( isDomainProperty ) {
		baseServiceArgs.page = `*${ referenceSiteURL }`;
	}
	const baseServiceURL = useSelect( ( select ) => select( STORE_NAME ).getServiceURL(
		{
			path: '/performance/search-analytics',
			query: baseServiceArgs,
		} ) );

	if ( ! data || ! data.length ) {
		return null;
	}

	const headers = [
		{
			title: __( 'Top search queries for your site', 'google-site-kit' ),
			tooltip: __( 'Most searched for keywords related to your content', 'google-site-kit' ),
			primary: true,
		},
		{
			title: __( 'Clicks', 'google-site-kit' ),
			tooltip: __( 'Number of times users clicked on your content in search results', 'google-site-kit' ),
		},
		{
			title: __( 'Impressions', 'google-site-kit' ),
			tooltip: __( 'Counted each time your content appears in search results', 'google-site-kit' ),
		},
	];
	const links = [];

	const dataMapped = data.map( ( row, i ) => {
		const query = row.keys[ 0 ];
		links[ i ] = addQueryArgs( baseServiceURL, { query: `!${ query }` } );
		return [
			query,
			numberFormat( row.clicks ),
			numberFormat( row.impressions ),
		];
	} );

	const options = {
		hideHeader: false,
		chartsEnabled: false,
		links,
	};

	const dataTable = getDataTableFromData( dataMapped, headers, options );

	return (

			<Layout
				className="googlesitekit-popular-content"
				footer
				footerCTALabel={ _x( 'Search Console', 'Service name', 'google-site-kit' ) }
				footerCTALink={ baseServiceURL }
				fill
			>
				<TableOverflowContainer>
					{ dataTable }
				</TableOverflowContainer>
			</Layout>

	);
};

export default withData(
	LegacyDashboardWidgetPopularKeywordsTable,
	[
		{
			type: TYPE_MODULES,
			identifier: 'search-console',
			datapoint: 'searchanalytics',
			data: {
				dimensions: 'query',
				limit: 10,
			},
			priority: 1,
			maxAge: getTimeInSeconds( 'day' ),
			context: [ 'Dashboard' ],
		},
	],

		<Layout className="googlesitekit-popular-content" fill>
			<PreviewTable padding />
		</Layout>

,
	{
		inGrid: true,
		createGrid: true,
	},
	isDataZeroSearchConsole
);
<|endoftext|>


<commit_before>var Photo        = require('./Photo.js');
var PhotosClient = require('../../Api/PhotosClient');
var PhotoStore   = require('../../Store/PhotoStore');
var React        = require('react');

var PhotoList = React.createClass({
    getState : function () {
        return {
            all_photos                : PhotoStore.getCollection(),
            loading_new_set           : PhotoStore.newSetBeingLoaded(),
            uploading_boooth          : PhotoStore.booothBeingUploaded(),
            complete_catalogue_loaded : PhotoStore.completeCatalogueLoaded(),
            current_page              : this.state
                ? this.state.current_page
                : parseInt(this.props.page),
            loaded_pages              : this.state
                ? this.state.loaded_pages
                : []
        };
    },

    getInitialState : function () {
        return this.getState();
    },

    componentDidMount : function () {
        PhotoStore.addChangeListener(this._onChange);
        PhotosClient.getCollection(this.props.page);

        this.addScrollListener();
    },

    componentWillUnmount : function () {
        PhotoStore.removeChangeListener(this._onChange);
        this.removeScrollListener();
    },

    addScrollListener : function () {
        window.addEventListener('scroll', this.scrollListener);
        window.addEventListener('resize', this.scrollListener);
    },

    removeScrollListener : function () {
        window.removeEventListener('scroll', this.scrollListener);
        window.removeEventListener('resize', this.scrollListener);
    },

    scrollListener : function () {
        this.removeScrollListener();

        var scrolled_height = window.pageYOffset;
        var page_height     = window.innerHeight;
        var body_height     = window.document.body.offsetHeight;
        var bottom_reached  = (scrolled_height + page_height) >= body_height;

        if (bottom_reached && this.shouldLoadNextPage()) {
            var next_page = this.state.current_page + 1;
            PhotosClient.getCollection(next_page);

            var loaded_pages = this.state.loaded_pages;
            loaded_pages.push(this.state.current_page);

            this.setState({
                current_page : next_page,
                loaded_pages : loaded_pages
            });
        }

        this.addScrollListener();
    },

    shouldLoadNextPage : function () {
        var next_page = this.state.current_page + 1;

        return (
            this.state.loaded_pages.indexOf(next_page) === -1
            && !this.state.loading_new_set
            && !this.state.complete_catalogue_loaded
        );
    },

    _onChange : function () {
        this.setState(this.getState());
    },

    render : function () {
        var photos = [];

        this.state.all_photos.map(function (photo) {
            photos.push(<Photo key={photo.id} photo={photo} />);
        })

        return (

{this.state.loading_new_set ? 'Loading' : ''}

{this.state.uploading_boooth ? 'Uploading' : ''}

                {photos}

{this.state.complete_catalogue_loaded ? 'No more boooths to be shown!' : ''}

        );
    }
});

module.exports = PhotoList;<commit_msg>Avoid waiting until the bottom to load the next boooths stack<commit_after>var Photo        = require('./Photo.js');
var PhotosClient = require('../../Api/PhotosClient');
var PhotoStore   = require('../../Store/PhotoStore');
var React        = require('react');

var PhotoList = React.createClass({
    getState : function () {
        return {
            all_photos                : PhotoStore.getCollection(),
            loading_new_set           : PhotoStore.newSetBeingLoaded(),
            uploading_boooth          : PhotoStore.booothBeingUploaded(),
            complete_catalogue_loaded : PhotoStore.completeCatalogueLoaded(),
            current_page              : this.state
                ? this.state.current_page
                : parseInt(this.props.page),
            loaded_pages              : this.state
                ? this.state.loaded_pages
                : []
        };
    },

    getInitialState : function () {
        return this.getState();
    },

    componentDidMount : function () {
        PhotoStore.addChangeListener(this._onChange);
        PhotosClient.getCollection(this.props.page);

        this.addScrollListener();
    },

    componentWillUnmount : function () {
        PhotoStore.removeChangeListener(this._onChange);
        this.removeScrollListener();
    },

    addScrollListener : function () {
        window.addEventListener('scroll', this.scrollListener);
        window.addEventListener('resize', this.scrollListener);
    },

    removeScrollListener : function () {
        window.removeEventListener('scroll', this.scrollListener);
        window.removeEventListener('resize', this.scrollListener);
    },

    scrollListener : function () {
        this.removeScrollListener();

        var scrolled_height = window.pageYOffset;
        var page_height     = window.innerHeight;
        var body_height     = window.document.body.offsetHeight;
        var bottom_reached  = (scrolled_height + page_height) >= (body_height - 3000);

        if (bottom_reached && this.shouldLoadNextPage()) {
            var next_page = this.state.current_page + 1;
            PhotosClient.getCollection(next_page);

            var loaded_pages = this.state.loaded_pages;
            loaded_pages.push(this.state.current_page);

            this.setState({
                current_page : next_page,
                loaded_pages : loaded_pages
            });
        }

        this.addScrollListener();
    },

    shouldLoadNextPage : function () {
        var next_page = this.state.current_page + 1;

        return (
            this.state.loaded_pages.indexOf(next_page) === -1
            && !this.state.loading_new_set
            && !this.state.complete_catalogue_loaded
        );
    },

    _onChange : function () {
        this.setState(this.getState());
    },

    render : function () {
        var photos = [];

        this.state.all_photos.map(function (photo) {
            photos.push(<Photo key={photo.id} photo={photo} />);
        })

        return (

{this.state.loading_new_set ? 'Loading' : ''}

{this.state.uploading_boooth ? 'Uploading' : ''}

                {photos}

{this.state.complete_catalogue_loaded ? 'No more boooths to be shown!' : ''}

        );
    }
});

module.exports = PhotoList;<|endoftext|>


<commit_before>    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    var w = canvas.width;
    var h = canvas.height;
    var angle = 3 * Math.PI / 180;
    var cx = 200;
    var cy = 75;
    var radius = 60;

    ctx.fillStyle = "skyblue";
    ctx.strokeStyle = "lightgray";

    function New_open_win()
    {
    Qwek2=window.open("https://omaniamnotgoodwith.computer/side/WindowPullAlt","datshizzle","location=0,status=1,width=700,height=500");
    Qwek2.focus()
    }
    function draw(x, y) {
        ctx.clearRect(0, 0, w, h);
        ctx.save();
        ctx.beginPath();
        ctx.beginPath();
        ctx.rect(x, y, 50, 30)
        ctx.rect(x - 50 / 2, y - 30 / 2, 50, 30)
        ctx.fill();
        ctx.stroke();
        ctx.restore();
    };

    //keepDrawing();

    function keepDrawing() {

        draw(newX, newY);

        setTimeout(keepDrawing, 250)
    }

    window.requestAnimFrame = (function (callback) {
        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
            window.setTimeout(callback, 1000 / 60);
        };
    })();

    var fps = 60;

    function animate() {
        setTimeout(function () {
            requestAnimationFrame(animate);

            // increase the angle of rotation
            angle += 3 * Math.PI / 180 * 4;

            // calculate the new ball.x / ball.y
            var newX = cx + radius * Math.cos(angle) + 200;
            var newY = cy + radius * Math.sin(angle) + 200;
            // draw
            draw(newX, newY);
            console.log(newX);
	    console.log(newY);
	    Qwek2.moveTo(newX, newY);
//	    Qwek2.resizeTo(newX, newY);
            Qwek2.focus()
	    document.getElementById("iframe").width = "newX + 400";
	    document.getElementById("iframe").height = "newY + 400";

            // draw the centerpoint
            ctx.beginPath();
            ctx.arc(cx, cy, radius, 0, Math.PI * 2, false);
            ctx.closePath();
            ctx.stroke();

        }, 1000 / fps);
    }
    animate();
<commit_msg>Update WinBoundJs.js<commit_after>    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    var w = canvas.width;
    var h = canvas.height;
    var angle = 3 * Math.PI / 180;
    var cx = 200;
    var cy = 75;
    var radius = 60;

    ctx.fillStyle = "skyblue";
    ctx.strokeStyle = "lightgray";

    function New_open_win()
    {
    Qwek2=window.open("https://omaniamnotgoodwith.computer/side/WindowPullAlt","datshizzle","location=0,status=1,width=700,height=500");
    Qwek2.focus()
    }
    function draw(x, y) {
        ctx.clearRect(0, 0, w, h);
        ctx.save();
        ctx.beginPath();
        ctx.beginPath();
        ctx.rect(x, y, 50, 30)
        ctx.rect(x - 50 / 2, y - 30 / 2, 50, 30)
        ctx.fill();
        ctx.stroke();
        ctx.restore();
    };

    //keepDrawing();

    function keepDrawing() {

        draw(newX, newY);

        setTimeout(keepDrawing, 250)
    }

    window.requestAnimFrame = (function (callback) {
        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
            window.setTimeout(callback, 1000 / 60);
        };
    })();

    var fps = 60;

    function animate() {
        setTimeout(function () {
            requestAnimationFrame(animate);

            // increase the angle of rotation
            angle += 3 * Math.PI / 180 * 4;

            // calculate the new ball.x / ball.y
            var newX = cx + radius * Math.cos(angle) + 200;
            var newY = cy + radius * Math.sin(angle) + 200;
            // draw
            draw(newX, newY);
            console.log(newX);
	    console.log(newY);
	    Qwek2.moveTo(newX, newY);
	    Qwek2.resizeTo(newX, newY);
            Qwek2.focus()
            // draw the centerpoint
            ctx.beginPath();
            ctx.arc(cx, cy, radius, 0, Math.PI * 2, false);
            ctx.closePath();
            ctx.stroke();

        }, 1000 / fps);
    }
    animate();
<|endoftext|>


<commit_before>import React from 'react'

var widget = React.createClass({
  getInitialState: function() {
    return {
      hidden: false,
      sent: false
    }
  },
  getPaymentLink: function() {
    // TODO 这里如何根据数据输出不同的HTML
    this.setState({
      sent: true
    })

    setTimeout(function(that) {
      that.setState({
        hidden: true,
        sent: false
      })
    }, 3000, this)
  },
  render: function() {
    return (

        <form className="pure-form">
          <fieldset>
            <legend>Pay with Bitcoin</legend>
            <input className="email-form-input" type="email" placeholder="Enter your email to get payment link" required />
            <button type="button" className="pure-button pure-button-primary" onClick={this.getPaymentLink}>
              Send payment link
            </button>
          </fieldset>
        </form>

    )
  }
})

export default widget
<commit_msg>html output<commit_after>import React from 'react'

var widget = React.createClass({
  getInitialState: function() {
    return {
      hidden: true,
      sent: false
    }
  },
  getPaymentLink: function() {
    this.setState({
      sent: true
    })

    setTimeout(function(that) {
      that.setState({
        hidden: true,
        sent: false
      })
    }, 3000, this)
  },
  render: function() {
    var fragment = this.state.sent ?
ok!
 : (
      <form className="pure-form">
        <fieldset>
          <legend>Pay with Bitcoin</legend>
          <input className="email-form-input" type="email" placeholder="Enter your email to get payment link" required />
          <button type="button" className="pure-button pure-button-primary" onClick={this.getPaymentLink}>
            Send payment link
          </button>
        </fieldset>
      </form>
    )

    return (

        {fragment}

    )
  }
})

export default widget
<|endoftext|>


<commit_before>/* eslint-disable react/no-multi-comp */
import React from 'react';
import moment from 'moment';
import {Link} from 'react-router';

import BuildStore from '../stores/build-store';
import strings from '../strings';
import Actions from '../actions';
import Loading from '../components/loading';
import BuildTitle from '../components/builds/build-title';
import Task from '../components/task';

export default class BuildDetailsPage extends React.Component {

  constructor() {
    super();
    this.state = {build: null, loading: false};
  }

  get() {
    return BuildStore.getBuild(
      this.props.params.owner,
      this.props.params.name,
      this.props.params.buildNumber
    );
  }

  fetch() {
    Actions.getBuild(
      this.props.params.owner + '/' +
      this.props.params.name + '/' +
      this.props.params.buildNumber
    );
    Actions.addAlert({
      message: strings('LOADING'),
      iconClasses: 'fa fa-spinner fa-pulse',
      key: 'loading-data',
    });
  }

  componentDidMount() {
    BuildStore.addChangeListener(this._onChange.bind(this));
    this.setState({build: this.get(), loading: true});
    this.fetch();
  }

  componentWillUnmount() {
    BuildStore.removeChangeListener(this._onChange.bind(this));
  }

  _onChange() {
    this.setState({build: this.get(), loading: BuildStore.isLoading()});
  }

  render() {
    if (!this.state.loading) Actions.removeAlert('loading-data');
    const build = this.state.build;

    if (!build) return (<Loading />);
    if (build.get('color') === 'gray') {
      return (

        <BuildTitle project={build.get('project')} branch={build.get('branch')} buildNumber={build.get('build_number')} size={2}/>

{strings('BUILD_ERRORED')}

      );
    }

    let setupTasks = false;
    let tasks = false;
    let state = 'Pending';

    if (build.get('result')) {
      if (!build.get('result').still_running) {
        state = build.get('result').succeeded ? 'Success' : 'Failure';
      }

      if (build.get('result').setup_tasks) {
        setupTasks = build.get('result').setup_tasks.map(task => {
          task.key = task.task;
          return (<Task {...task} />);
        });
      }

      tasks = build.get('result').tasks.map(task => {
        task.key = task.task;
        return (<Task {...task} />);
      });
    }

    if (!build.get('result') || build.get('result').still_running) {
      clearTimeout(this.fetchTimeout);
      this.fetchTimeout = setTimeout(this.fetch.bind(this), 2000);
    }
    return (

        <BuildTitle project={build.get('project')} branch={build.get('branch')} buildNumber={build.get('build_number')} size={2}/>

          Branch: {build.get('branch')}

          <PullRequestInfo id={build.get('pull_request_id')} url={build.get('pull_request_url')} />
          Commit hash: {build.get('sha')}

          Author: {build.get('author')}

          Timestamp: {moment(build.get('start_time')).fromNow()}

          State: {state}
          <Coverage result={build.get('result')} />
          <DeploymentInfo build={build} {...build.get('deployment')} />

          {build.get('message')}

          {setupTasks.length ? (
Setup tasks:
) : false}
          {setupTasks}

Tasks:

          {tasks}

    );
  }
}

BuildDetailsPage.propTypes = {
  params: React.PropTypes.object,
};

class Coverage extends React.Component {
  render() {
    if (!this.props.result || !this.props.result.coverage) return false;

    return (

        Coverage: {this.props.result.coverage}

    );
  }
}

Coverage.propTypes = {
  result: React.PropTypes.object,
};

class PullRequestInfo extends React.Component {
  render() {
    if (this.props.id === 0) return false;
    return (

        Pull Request: #{this.props.id}

    );
  }
}

PullRequestInfo.propTypes = {
  url: React.PropTypes.string,
  id: React.PropTypes.number,
};

class DeploymentInfo extends React.Component {
  url() {
    return 'http://' + this.props.port + '.pr.frigg.io';
  }

  render() {
    const build = this.props.build;
    if (!build.get('deployment')) return false;

    if (!this.props.succeeded) {
      return (

          Preview:
          {this.props.is_pending ? 'Pending' : 'Could not deploy.'}
          (<Link to="deployment" params={{owner: build.get('project').owner, name: build.get('project').name, buildNumber: build.get('build_number')}}>
            Deploy details
          </Link>)

      );
    }
    return (

        Preview:
        {this.url()}
        (<Link to="deployment" params={{owner: build.get('project').owner, name: build.get('project').name, buildNumber: build.get('build_number')}}>
          Deploy details
        </Link>)

    );
  }
}

DeploymentInfo.propTypes = {
  succeeded: React.PropTypes.bool,
  is_pending: React.PropTypes.bool,
  build: React.PropTypes.object,
  port: React.PropTypes.number,
};
<commit_msg>Add showing of worker host to staff users<commit_after>/* eslint-disable react/no-multi-comp */
import React from 'react';
import moment from 'moment';
import {Link} from 'react-router';

import BuildStore from '../stores/build-store';
import UserStore from '../stores/user-store';
import strings from '../strings';
import Actions from '../actions';
import Loading from '../components/loading';
import BuildTitle from '../components/builds/build-title';
import Task from '../components/task';

export default class BuildDetailsPage extends React.Component {

  constructor() {
    super();
    this.state = {build: null, loading: false};
  }

  get() {
    return BuildStore.getBuild(
      this.props.params.owner,
      this.props.params.name,
      this.props.params.buildNumber
    );
  }

  fetch() {
    Actions.getBuild(
      this.props.params.owner + '/' +
      this.props.params.name + '/' +
      this.props.params.buildNumber
    );
    Actions.addAlert({
      message: strings('LOADING'),
      iconClasses: 'fa fa-spinner fa-pulse',
      key: 'loading-data',
    });
  }

  componentDidMount() {
    BuildStore.addChangeListener(this._onChange.bind(this));
    this.setState({build: this.get(), loading: true});
    this.fetch();
  }

  componentWillUnmount() {
    BuildStore.removeChangeListener(this._onChange.bind(this));
  }

  _onChange() {
    this.setState({build: this.get(), loading: BuildStore.isLoading()});
  }

  render() {
    if (!this.state.loading) Actions.removeAlert('loading-data');
    const build = this.state.build;
    const user = UserStore.getCurrentUser();

    if (!build) return (<Loading />);
    if (build.get('color') === 'gray') {
      return (

        <BuildTitle project={build.get('project')} branch={build.get('branch')} buildNumber={build.get('build_number')} size={2}/>

{strings('BUILD_ERRORED')}

      );
    }

    let setupTasks = false;
    let tasks = false;
    let state = 'Pending';

    if (build.get('result')) {
      if (!build.get('result').still_running) {
        state = build.get('result').succeeded ? 'Success' : 'Failure';
      }

      if (build.get('result').setup_tasks) {
        setupTasks = build.get('result').setup_tasks.map(task => {
          task.key = task.task;
          return (<Task {...task} />);
        });
      }

      tasks = build.get('result').tasks.map(task => {
        task.key = task.task;
        return (<Task {...task} />);
      });
    }

    if (!build.get('result') || build.get('result').still_running) {
      clearTimeout(this.fetchTimeout);
      this.fetchTimeout = setTimeout(this.fetch.bind(this), 2000);
    }

    return (

        <BuildTitle project={build.get('project')} branch={build.get('branch')} buildNumber={build.get('build_number')} size={2}/>

          Branch: {build.get('branch')}

          <PullRequestInfo id={build.get('pull_request_id')} url={build.get('pull_request_url')} />
          Commit hash: {build.get('sha')}

          Author: {build.get('author')}

          Timestamp: {moment(build.get('start_time')).fromNow()}

          State: {state}
          <Coverage result={build.get('result')} />
          {user.get('is_staff') ? (Worker: {build.get('worker_host')}) : false}
          <DeploymentInfo build={build} {...build.get('deployment')} />

          {build.get('message')}

          {setupTasks.length ? (
Setup tasks:
) : false}
          {setupTasks}

Tasks:

          {tasks}

    );
  }
}

BuildDetailsPage.propTypes = {
  params: React.PropTypes.object,
};

class Coverage extends React.Component {
  render() {
    if (!this.props.result || !this.props.result.coverage) return false;

    return (

        Coverage: {this.props.result.coverage}

    );
  }
}

Coverage.propTypes = {
  result: React.PropTypes.object,
};

class PullRequestInfo extends React.Component {
  render() {
    if (this.props.id === 0) return false;
    return (

        Pull Request: #{this.props.id}

    );
  }
}

PullRequestInfo.propTypes = {
  url: React.PropTypes.string,
  id: React.PropTypes.number,
};

class DeploymentInfo extends React.Component {
  url() {
    return 'http://' + this.props.port + '.pr.frigg.io';
  }

  render() {
    const build = this.props.build;
    if (!build.get('deployment')) return false;

    if (!this.props.succeeded) {
      return (

          Preview:
          {this.props.is_pending ? 'Pending' : 'Could not deploy.'}
          (<Link to="deployment" params={{owner: build.get('project').owner, name: build.get('project').name, buildNumber: build.get('build_number')}}>
            Deploy details
          </Link>)

      );
    }
    return (

        Preview:
        {this.url()}
        (<Link to="deployment" params={{owner: build.get('project').owner, name: build.get('project').name, buildNumber: build.get('build_number')}}>
          Deploy details
        </Link>)

    );
  }
}

DeploymentInfo.propTypes = {
  succeeded: React.PropTypes.bool,
  is_pending: React.PropTypes.bool,
  build: React.PropTypes.object,
  port: React.PropTypes.number,
};
<|endoftext|>


<commit_before>/* eslint-disable react/no-multi-comp */
import React from 'react';
import moment from 'moment';
import {Link} from 'react-router';

import BuildStore from '../stores/build-store';
import UserStore from '../stores/user-store';
import strings from '../strings';
import Actions from '../actions';
import Loading from '../components/loading';
import BuildTitle from '../components/builds/build-title';
import Task from '../components/task';

export default class BuildDetailsPage extends React.Component {

  constructor() {
    super();
    this.state = {build: null, loading: false};
  }

  get() {
    return BuildStore.getBuild(
      this.props.params.owner,
      this.props.params.name,
      this.props.params.buildNumber
    );
  }

  fetch() {
    Actions.getBuild(
      this.props.params.owner + '/' +
      this.props.params.name + '/' +
      this.props.params.buildNumber
    );
    Actions.addAlert({
      message: strings('LOADING'),
      iconClasses: 'fa fa-spinner fa-pulse',
      key: 'loading-data',
    });
  }

  componentDidMount() {
    BuildStore.addChangeListener(this._onChange.bind(this));
    this.setState({build: this.get(), loading: true});
    this.fetch();
  }

  componentWillUnmount() {
    BuildStore.removeChangeListener(this._onChange.bind(this));
  }

  _onChange() {
    this.setState({build: this.get(), loading: BuildStore.isLoading()});
  }

  render() {
    if (!this.state.loading) Actions.removeAlert('loading-data');
    const build = this.state.build;
    const user = UserStore.getCurrentUser();

    if (!build) return (<Loading />);
    if (build.get('color') === 'gray') {
      return (

        <BuildTitle project={build.get('project')} branch={build.get('branch')} buildNumber={build.get('build_number')} size={2}/>

{strings('BUILD_ERRORED')}

      );
    }

    let setupTasks = false;
    let tasks = false;
    let state = 'Pending';

    if (build.get('result')) {
      if (!build.get('result').still_running) {
        state = build.get('result').succeeded ? 'Success' : 'Failure';
      }

      if (build.get('result').setup_tasks) {
        setupTasks = build.get('result').setup_tasks.map(task => {
          task.key = task.task;
          return (<Task {...task} />);
        });
      }

      tasks = build.get('result').tasks.map(task => {
        task.key = task.task;
        return (<Task {...task} />);
      });
    }

    if (!build.get('result') || build.get('result').still_running) {
      clearTimeout(this.fetchTimeout);
      this.fetchTimeout = setTimeout(this.fetch.bind(this), 2000);
    }

    return (

        <BuildTitle project={build.get('project')} branch={build.get('branch')} buildNumber={build.get('build_number')} size={2}/>

          Branch: {build.get('branch')}

          <PullRequestInfo id={build.get('pull_request_id')} url={build.get('pull_request_url')} />
          Commit hash: {build.get('sha')}

          Author: {build.get('author')}

          Timestamp: {moment(build.get('start_time')).fromNow()}

          State: {state}

          {user.get('is_staff') ? (Worker: {build.get('result').worker_host}) : false}
          <Coverage result={build.get('result')} />
          <DeploymentInfo build={build} {...build.get('deployment')} />

          {build.get('message')}

          {setupTasks.length ? (
Setup tasks:
) : false}
          {setupTasks}

Tasks:

          {tasks}

    );
  }
}

BuildDetailsPage.propTypes = {
  params: React.PropTypes.object,
};

class Coverage extends React.Component {
  render() {
    if (!this.props.result || !this.props.result.coverage) return false;

    return (

        Coverage: {this.props.result.coverage}

    );
  }
}

Coverage.propTypes = {
  result: React.PropTypes.object,
};

class PullRequestInfo extends React.Component {
  render() {
    if (this.props.id === 0) return false;
    return (

        Pull Request: #{this.props.id}

    );
  }
}

PullRequestInfo.propTypes = {
  url: React.PropTypes.string,
  id: React.PropTypes.number,
};

class DeploymentInfo extends React.Component {
  url() {
    return 'http://' + this.props.port + '.pr.frigg.io';
  }

  render() {
    const build = this.props.build;
    if (!build.get('deployment')) return false;

    if (!this.props.succeeded) {
      return (

          Preview:
          {this.props.is_pending ? 'Pending' : 'Could not deploy.'}
          (<Link to="deployment" params={{owner: build.get('project').owner, name: build.get('project').name, buildNumber: build.get('build_number')}}>
            Deploy details
          </Link>)

      );
    }
    return (

        Preview:
        {this.url()}
        (<Link to="deployment" params={{owner: build.get('project').owner, name: build.get('project').name, buildNumber: build.get('build_number')}}>
          Deploy details
        </Link>)

    );
  }
}

DeploymentInfo.propTypes = {
  succeeded: React.PropTypes.bool,
  is_pending: React.PropTypes.bool,
  build: React.PropTypes.object,
  port: React.PropTypes.number,
};
<commit_msg>Fix issue with cannot read worker_host of undefined<commit_after>/* eslint-disable react/no-multi-comp */
import React from 'react';
import moment from 'moment';
import {Link} from 'react-router';

import BuildStore from '../stores/build-store';
import UserStore from '../stores/user-store';
import strings from '../strings';
import Actions from '../actions';
import Loading from '../components/loading';
import BuildTitle from '../components/builds/build-title';
import Task from '../components/task';

export default class BuildDetailsPage extends React.Component {

  constructor() {
    super();
    this.state = {build: null, loading: false};
  }

  get() {
    return BuildStore.getBuild(
      this.props.params.owner,
      this.props.params.name,
      this.props.params.buildNumber
    );
  }

  fetch() {
    Actions.getBuild(
      this.props.params.owner + '/' +
      this.props.params.name + '/' +
      this.props.params.buildNumber
    );
    Actions.addAlert({
      message: strings('LOADING'),
      iconClasses: 'fa fa-spinner fa-pulse',
      key: 'loading-data',
    });
  }

  componentDidMount() {
    BuildStore.addChangeListener(this._onChange.bind(this));
    this.setState({build: this.get(), loading: true});
    this.fetch();
  }

  componentWillUnmount() {
    BuildStore.removeChangeListener(this._onChange.bind(this));
  }

  _onChange() {
    this.setState({build: this.get(), loading: BuildStore.isLoading()});
  }

  render() {
    if (!this.state.loading) Actions.removeAlert('loading-data');
    const build = this.state.build;
    const user = UserStore.getCurrentUser();

    if (!build) return (<Loading />);
    if (build.get('color') === 'gray') {
      return (

        <BuildTitle project={build.get('project')} branch={build.get('branch')} buildNumber={build.get('build_number')} size={2}/>

{strings('BUILD_ERRORED')}

      );
    }

    let setupTasks = false;
    let tasks = false;
    let state = 'Pending';

    if (build.get('result')) {
      if (!build.get('result').still_running) {
        state = build.get('result').succeeded ? 'Success' : 'Failure';
      }

      if (build.get('result').setup_tasks) {
        setupTasks = build.get('result').setup_tasks.map(task => {
          task.key = task.task;
          return (<Task {...task} />);
        });
      }

      tasks = build.get('result').tasks.map(task => {
        task.key = task.task;
        return (<Task {...task} />);
      });
    }

    if (!build.get('result') || build.get('result').still_running) {
      clearTimeout(this.fetchTimeout);
      this.fetchTimeout = setTimeout(this.fetch.bind(this), 2000);
    }

    return (

        <BuildTitle project={build.get('project')} branch={build.get('branch')} buildNumber={build.get('build_number')} size={2}/>

          Branch: {build.get('branch')}

          <PullRequestInfo id={build.get('pull_request_id')} url={build.get('pull_request_url')} />
          Commit hash: {build.get('sha')}

          Author: {build.get('author')}

          Timestamp: {moment(build.get('start_time')).fromNow()}

          State: {state}

          {user.get('is_staff') && build.get('result') ? (Worker: {build.get('result').worker_host}) : false}
          <Coverage result={build.get('result')} />
          <DeploymentInfo build={build} {...build.get('deployment')} />

          {build.get('message')}

          {setupTasks.length ? (
Setup tasks:
) : false}
          {setupTasks}

Tasks:

          {tasks}

    );
  }
}

BuildDetailsPage.propTypes = {
  params: React.PropTypes.object,
};

class Coverage extends React.Component {
  render() {
    if (!this.props.result || !this.props.result.coverage) return false;

    return (

        Coverage: {this.props.result.coverage}

    );
  }
}

Coverage.propTypes = {
  result: React.PropTypes.object,
};

class PullRequestInfo extends React.Component {
  render() {
    if (this.props.id === 0) return false;
    return (

        Pull Request: #{this.props.id}

    );
  }
}

PullRequestInfo.propTypes = {
  url: React.PropTypes.string,
  id: React.PropTypes.number,
};

class DeploymentInfo extends React.Component {
  url() {
    return 'http://' + this.props.port + '.pr.frigg.io';
  }

  render() {
    const build = this.props.build;
    if (!build.get('deployment')) return false;

    if (!this.props.succeeded) {
      return (

          Preview:
          {this.props.is_pending ? 'Pending' : 'Could not deploy.'}
          (<Link to="deployment" params={{owner: build.get('project').owner, name: build.get('project').name, buildNumber: build.get('build_number')}}>
            Deploy details
          </Link>)

      );
    }
    return (

        Preview:
        {this.url()}
        (<Link to="deployment" params={{owner: build.get('project').owner, name: build.get('project').name, buildNumber: build.get('build_number')}}>
          Deploy details
        </Link>)

    );
  }
}

DeploymentInfo.propTypes = {
  succeeded: React.PropTypes.bool,
  is_pending: React.PropTypes.bool,
  build: React.PropTypes.object,
  port: React.PropTypes.number,
};
<|endoftext|>


<commit_before>/*
 * The main component for the home page.
 */

import React, {Component} from 'react';

import {Blurb, Heading, Link} from '../../Components';

Link.registerPreloadResources('/', () => [
    require('./me.png'),
]);

export default class HomePage extends Component {

    render() {
        const urls = {
            brain: "https://ai.google/research/teams/brain",
            ka: "https://www.khanacademy.org/",
            pgpKey: require("../../shared_files/wchargin_public_key.gpg"),
            sourcecred: "https://sourcecred.io/",
            tensorboard: "https://www.tensorflow.org/tensorboard",
            tensorboardGithub: "https://github.com/tensorflow/tensorboard",
        };
        return

            <Heading level={1}>Hey!</Heading>
            <Blurb
                image={require("./me.png")}
                imagePosition={Blurb.IMAGE_RIGHT}
            >

                    I’m William.
                    I enjoy creating software that is correct, efficient, and useful.

                    I’m currently at <Link href={urls.brain}>Google Brain</Link> working full time on <Link href={urls.tensorboard}>TensorBoard</Link>, which is <Link href={urls.tensorboardGithub}>fully open source</Link>.
                    In my free time, I work on <Link href={urls.sourcecred}>SourceCred</Link>.
                    In the past, I worked at <Link href={urls.ka}>Khan Academy</Link>.

                    Miscellanea about me:

                        My email address is at gmail.com.
                        (You can figure it out.)

                        <Link href={urls.pgpKey}>My PGP key</Link>’s ID is 9ADC5E04FB5CCEE9.

                        Sometimes <Link to="/posts">I write things</Link>.

                    My surname is pronounced /'ʧɑɹgɪn/, with a hard g.
                    It rhymes with bargain, not barge in.

            </Blurb>

;
    }

}
<commit_msg>Make a minor word tweak on home page<commit_after>/*
 * The main component for the home page.
 */

import React, {Component} from 'react';

import {Blurb, Heading, Link} from '../../Components';

Link.registerPreloadResources('/', () => [
    require('./me.png'),
]);

export default class HomePage extends Component {

    render() {
        const urls = {
            brain: "https://ai.google/research/teams/brain",
            ka: "https://www.khanacademy.org/",
            pgpKey: require("../../shared_files/wchargin_public_key.gpg"),
            sourcecred: "https://sourcecred.io/",
            tensorboard: "https://www.tensorflow.org/tensorboard",
            tensorboardGithub: "https://github.com/tensorflow/tensorboard",
        };
        return

            <Heading level={1}>Hey!</Heading>
            <Blurb
                image={require("./me.png")}
                imagePosition={Blurb.IMAGE_RIGHT}
            >

                    I’m William.
                    I enjoy creating software that is correct, efficient, and useful.

                    I’m currently at <Link href={urls.brain}>Google Brain</Link> working full time on <Link href={urls.tensorboard}>TensorBoard</Link>, which is <Link href={urls.tensorboardGithub}>fully open source</Link>.
                    In my free time, I work on <Link href={urls.sourcecred}>SourceCred</Link>.
                    In the past, I worked at <Link href={urls.ka}>Khan Academy</Link>.

                    About me:

                        My email address is at gmail.com.
                        (You can figure it out.)

                        <Link href={urls.pgpKey}>My PGP key</Link>’s ID is 9ADC5E04FB5CCEE9.

                        Sometimes <Link to="/posts">I write things</Link>.

                    My surname is pronounced /'ʧɑɹgɪn/, with a hard g.
                    It rhymes with bargain, not barge in.

            </Blurb>

;
    }

}
<|endoftext|>


<commit_before>/* adminwidget js
----------------------------------------------------------

@package:	Zotonic 2009, 2012
@Author:	Tim Benniks <tim@timbenniks.nl>

Copyright 2009 Tim Benniks
Copyright 2012 Arjan Scherpenisse

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

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

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

---------------------------------------------------------- */

$.widget("z.adminwidget",
{
    _init: function()
    {
	var self = this;
	var id = self.element.attr("id");
	self.element.addClass("widget-active");
	self.item = self.element.find("div.widget-content");
	self.header = self.element.find("h3:first");
        self.icon = $("<i>").appendTo(self.header);
	self.options.minifiedOnInit ? self.hide(true) : self.show(true);
        self.icon
            .attr("title", z_translate("Click to toggle"))
            .bind("mouseover", function(){$(this).addClass('icon-white');})
            .bind("mouseout", function(){$(this).removeClass('icon-white');})
            .click(function()
    		   {
    		       self.toggle();
    		       if (id) z_event("adminwidget_toggle", {id: id, showing: self.showing});
    		   });
    },

    toggle: function() {
	var self = this;
	self.setVisible(!self.showing);
    },

    setVisible: function(v, skipAnim) {
	var self = this;
	v ? self.show(skipAnim) : self.hide(skipAnim);
    },

    hide: function(skipAnim) {
	var self = this;
	if (skipAnim)
	    self.item.hide();
	else
	    self.item.slideUp(200);
	self.icon.attr("class", "pull-right icon-plus");
	self.showing = false;
    },

    show: function(skipAnim) {
	var self = this;
	if (skipAnim)
	    self.item.show();
	else
	    self.item.slideDown(200);
	self.icon.attr("class", "pull-right icon-minus");
	self.showing = true;
    }
});

$.z.adminwidget.defaults = {
    minifiedOnInit: false
};<commit_msg>mod_admin: Make entire admin widget header clickable<commit_after>/* adminwidget js
----------------------------------------------------------

@package:	Zotonic 2009, 2012
@Author:	Tim Benniks <tim@timbenniks.nl>

Copyright 2009 Tim Benniks
Copyright 2012 Arjan Scherpenisse

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

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

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

---------------------------------------------------------- */

$.widget("z.adminwidget",
{
    _init: function()
    {
	    var self = this;
	    self.element.addClass("widget-active");
	    self.item = self.element.find("div.widget-content");
	    self.header = self.element.find("h3:first");
        self.icon = $("<i>").appendTo(self.header);
	    self.options.minifiedOnInit ? self.hide(true) : self.show(true);
        self.header
            .css("cursor", "pointer")
            .bind("mouseover", function(){self.icon.addClass('icon-white');})
            .bind("mouseout", function(){self.icon.removeClass('icon-white');})
            .attr("title", z_translate("Click to toggle"))
            .click(function(){self.toggle();});

    },

    toggle: function() {
	    var self = this;
	    var id = self.element.attr("id");
	    self.setVisible(!self.showing);
    	if (id) z_event("adminwidget_toggle", {id: id, showing: self.showing});
    },

    setVisible: function(v, skipAnim) {
	    var self = this;
	    v ? self.show(skipAnim) : self.hide(skipAnim);
    },

    hide: function(skipAnim) {
	    var self = this;
	    if (skipAnim)
	        self.item.hide();
	    else
	        self.item.slideUp(200);
	    self.icon.attr("class", "pull-right icon-plus");
	    self.showing = false;
    },

    show: function(skipAnim) {
	    var self = this;
	    if (skipAnim)
	        self.item.show();
	    else
	        self.item.slideDown(200);
	    self.icon.attr("class", "pull-right icon-minus");
	    self.showing = true;
    }
});

$.z.adminwidget.defaults = {
    minifiedOnInit: false
};<|endoftext|>


<commit_before>import { reduceApi, getBaseType } from '../../common/api'

import {
  LIST_TODOS,
  VIEW_TODO,
  CREATE_TODO,
  UPDATE_TODO,
  DELETE_TODO,
  MOVE_TODO
} from './actions';

export default function todoReducer(state = {todos: []}, action) {
  switch (getBaseType(action.type)) {
    case LIST_TODOS.BASE:
      state = reduceApi(state, action, LIST_TODOS, (data) => { return {todos: data}; });
    case VIEW_TODO.BASE:
      state = reduceApi(state, action, VIEW_TODO, (data) => { return {todo: data}; });
    case CREATE_TODO.BASE:
      state = reduceApi(state, action, CREATE_TODO, (data) => { return {todo: data}; });
    default:
      return state;
  }
}
<commit_msg>Add new record to list<commit_after>import { reduceApi, getBaseType } from '../../common/api'

import {
  LIST_TODOS,
  VIEW_TODO,
  CREATE_TODO,
  UPDATE_TODO,
  DELETE_TODO,
  MOVE_TODO
} from './actions';

export default function todoReducer(state = {todos: []}, action) {
  switch (getBaseType(action.type)) {
    case LIST_TODOS.BASE:
      state = reduceApi(state, action, LIST_TODOS, (data) => { return {todos: data}; });
    case VIEW_TODO.BASE:
      state = reduceApi(state, action, VIEW_TODO, (data) => { return {todo: data}; });
    case CREATE_TODO.BASE:
      state = reduceApi(state, action, CREATE_TODO, (data) => {
        return {todos: [...state.todos, data]};
      });
    default:
      return state;
  }
}
<|endoftext|>


<commit_before>'use strict';

var mongoose = require ('mongoose');
var Project = mongoose.model ('Project');

var getProjectById = function (id, cb) {
	Project.findById(id).exec(cb);
};

module.exports = function (app) {
	// app.use (require ('cors'));
	// -------------------------------------------------------------------------
	//
	// get a brand new empty project
	//
	// -------------------------------------------------------------------------
	app.get ('/esm/project/new', function (req, res) {
		var project = new Project ();
		project = project.toJSON ();
		res.status(200).send (project);
	});

	app.get ('/esm/project/:projectid', function (req, res) {
		getProjectById (req.params.projectid, function (err, project) {
	        if (err) res.status(500).send(err);
	        else res.status(200).send(project);
		});
	});

	app.post ('/esm/project', function (req, res) {
		console.log ('body = ',req.body);
		var project = new Project (req.body);
		project.save (function (err, doc) {
            if (err) res.status(500).send(err);
            else res.status(200).send(doc);
        });
	});

	app.put ('/esm/project/:projectid', function (req, res) {
		getProjectById (req.body._id, function (err, project) {
			if (project) {
				project.set(req.body);
			}
			project.save (function (err, doc) {
	            if (err) res.status(500).send(err);
	            else res.status(200).send(doc);
	        });
		});
	});
};

<commit_msg>added cross domain maybe<commit_after>'use strict';

var mongoose = require ('mongoose');
var Project = mongoose.model ('Project');

var getProjectById = function (id, cb) {
	Project.findById(id).exec(cb);
};

module.exports = function (app) {
app.use (require ('express-cors')({
	allowedOrigins : ['*']
}));
	// -------------------------------------------------------------------------
	//
	// get a brand new empty project
	//
	// -------------------------------------------------------------------------
	app.get ('/esm/project/new', function (req, res) {
		var project = new Project ();
		project = project.toJSON ();
res.set ({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type'
	});
		res.status(200).send (project);
	});

	app.get ('/esm/project/:projectid', function (req, res) {
		getProjectById (req.params.projectid, function (err, project) {
	        if (err) res.status(500).send(err);
	        else res.status(200).send(project);
		});
	});

	app.post ('/esm/project', function (req, res) {
		console.log ('body = ',req.body);
		var project = new Project (req.body);
		project.save (function (err, doc) {
            if (err) res.status(500).send(err);
            else res.status(200).send(doc);
        });
	});

	app.put ('/esm/project/:projectid', function (req, res) {
		getProjectById (req.body._id, function (err, project) {
			if (project) {
				project.set(req.body);
			}
			project.save (function (err, doc) {
	            if (err) res.status(500).send(err);
	            else res.status(200).send(doc);
	        });
		});
	});
};

<|endoftext|>


<commit_before>/*
 * decaffeinate suggestions:
 * DS101: Remove unnecessary use of Array.from
 * DS102: Remove unnecessary code created because of implicit returns
 * DS207: Consider shorter variations of null checks
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
const VASTUtil = require('../util.js');

class ParserUtils {
    constructor() {
        this.vastUtil = new VASTUtil();
    }

    childByName(node, name) {
        for (let child of Array.from(node.childNodes)) {
            if (child.nodeName === name) {
                return child;
            }
        }
    }

    childrenByName(node, name) {
        const children = [];
        for (let child of Array.from(node.childNodes)) {
            if (child.nodeName === name) {
                children.push(child);
            }
        }
        return children;
    }

    parseBoolean(booleanString) {
        return ['true', 'TRUE', '1'].includes(booleanString);
    }

    // Parsing node text for legacy support
    parseNodeText(node) {
        return node && (node.textContent || node.text || '').trim();
    }

    copyNodeAttribute(attributeName, nodeSource, nodeDestination) {
        const attributeValue = nodeSource.getAttribute(attributeName);
        if (attributeValue) {
            return nodeDestination.setAttribute(attributeName, attributeValue);
        }
    }

    parseDuration(durationString) {
        if (durationString == null) {
            return -1;
        }
        // Some VAST doesn't have an HH:MM:SS duration format but instead jus the number of seconds
        if (this.vastUtil.isNumeric(durationString)) {
            return parseInt(durationString);
        }

        const durationComponents = durationString.split(":");
        if (durationComponents.length !== 3) {
            return -1;
        }

        const secondsAndMS = durationComponents[2].split(".");
        let seconds = parseInt(secondsAndMS[0]);
        if (secondsAndMS.length === 2) {
            seconds += parseFloat(`0.${secondsAndMS[1]}`);
        }

        const minutes = parseInt(durationComponents[1] * 60);
        const hours = parseInt(durationComponents[0] * 60 * 60);

        if (isNaN(hours) || isNaN(minutes) || isNaN(seconds) || (minutes > (60 * 60)) || (seconds > 60)) {
            return -1;
        }
        return hours + minutes + seconds;
    }
}

module.exports = ParserUtils;
<commit_msg>Refactoring fixes after ParserUtils es6 conversion<commit_after>const VASTUtil = require('../util.js');

class ParserUtils {
    constructor() {
        this.vastUtil = new VASTUtil();
    }

    childByName(node, name) {
        const childNodes = node.childNodes;

        for (let childKey in childNodes) {
            const child = childNodes[childKey];

            if (child.nodeName === name) {
                return child;
            }
        }
    }

    childrenByName(node, name) {
        const children = [];
        const childNodes = node.childNodes;

        for (let childKey in childNodes) {
            const child = childNodes[childKey];

            if (child.nodeName === name) {
                children.push(child);
            }
        }
        return children;
    }

    parseBoolean(booleanString) {
        return ['true', 'TRUE', '1'].includes(booleanString);
    }

    // Parsing node text for legacy support
    parseNodeText(node) {
        return node && (node.textContent || node.text || '').trim();
    }

    copyNodeAttribute(attributeName, nodeSource, nodeDestination) {
        const attributeValue = nodeSource.getAttribute(attributeName);
        if (attributeValue) {
            nodeDestination.setAttribute(attributeName, attributeValue);
        }
    }

    parseDuration(durationString) {
        if (durationString == null) {
            return -1;
        }
        // Some VAST doesn't have an HH:MM:SS duration format but instead jus the number of seconds
        if (this.vastUtil.isNumeric(durationString)) {
            return parseInt(durationString);
        }

        const durationComponents = durationString.split(":");
        if (durationComponents.length !== 3) {
            return -1;
        }

        const secondsAndMS = durationComponents[2].split(".");
        let seconds = parseInt(secondsAndMS[0]);
        if (secondsAndMS.length === 2) {
            seconds += parseFloat(`0.${secondsAndMS[1]}`);
        }

        const minutes = parseInt(durationComponents[1] * 60);
        const hours = parseInt(durationComponents[0] * 60 * 60);

        if (isNaN(hours) || isNaN(minutes) || isNaN(seconds) || (minutes > (60 * 60)) || (seconds > 60)) {
            return -1;
        }
        return hours + minutes + seconds;
    }
}

module.exports = ParserUtils;
<|endoftext|>


<commit_before>'use strict';
angular.module('shortcuts').filter('groupBy', function() {
    return function(shortcuts, field) {
        var filtered = {};
        // loop through each item in the list
        angular.forEach(shortcuts, function(shortcut) {
            var value = shortcut[field];
            if(value === ''){
                value = 'unsorted';
            }
            if(filtered[value] === undefined){
                filtered[value] = [];
            }
            filtered[value].push(shortcut);
        });
        return filtered;
    };
});<commit_msg>refactored filter for legibility<commit_after>'use strict';
angular.module('shortcuts').filter('groupBy', function() {
    return function(shortcuts, field) {
        var filtered = {};
        // loop through each shortcut in the list
        angular.forEach(shortcuts, function(shortcut) {
            var key = shortcut[field];
            // no key becomes unsorted
            if(key === ''){
                key = 'unsorted';
            }
            // add keys if they don't exist
            if(filtered[key] === undefined){
                filtered[key] = [];
            }
            // add shortcut to key
            filtered[key].push(shortcut);
        });
        return filtered;
    };
});<|endoftext|>


<commit_before>import AppDispatcher from '../dispatcher/AppDispatcher';
import { ActionTypes } from '../constants/Constants';
import fs from 'fs';
import { remote } from 'electron';
const dialog = remote.dialog;

let EditorActionCreators = {
  readFilepath(filepath) {
    fs.readFile(filepath, 'utf8', function(err, data) {
      if (err) {
        console.log('error');
      } else {
        localStorage.setItem('lastFile', filepath);
        AppDispatcher.dispatch({
          type: ActionTypes.OPEN_FILE,
          payload: {
            filepath: filepath,
            code: data
          }
        });
      }
    });
  },
  openFile() {
    dialog.showOpenDialog({
      filters: [{ name: 'python', extensions: ['py']}]
    }, (filepaths) => {
      if (filepaths === undefined) return;
      // Store as lastFile in localStorage. lastFile is auto-opened on startup.
      localStorage.setItem('lastFile', filepaths[0]);
      this.readFilepath(filepaths[0]);
    });
  },
  saveFile(filepath, code) {
    function writeContents(filepath) {
      fs.writeFile(filepath, code, function(err) {
        if (err) {
          console.log(err);
        } else {
          AppDispatcher.dispatch({
            type: ActionTypes.SAVE_FILE,
            payload: {
              code: code,
              filepath: filepath
            }
          });
        }
      });
    };

    if (filepath === null) {
      dialog.showSaveDialog(function(filepath) {
        if (filepath === undefined) return;
        writeContents(filepath);
      });
    } else {
      writeContents(filepath);
    }
  },
  createNewFile() {
    localStorage.removeItem('lastFile');
    AppDispatcher.dispatch({
      type: ActionTypes.CLEAR_EDITOR
    });
  },
  deleteFile(filepath) {
    fs.unlink(filepath, function() {
      AppDispatcher.dispatch({
        type: ActionTypes.CLEAR_EDITOR
      });
    });
  },
  editorUpdate(newVal) {
    AppDispatcher.dispatch({
      type: ActionTypes.UPDATE_EDITOR,
      payload: {
        code: newVal
      }
    });
  }
};

export default EditorActionCreators;
<commit_msg>Automatically add '.py' extension if they don't do so themselves.<commit_after>import AppDispatcher from '../dispatcher/AppDispatcher';
import { ActionTypes } from '../constants/Constants';
import fs from 'fs';
import { remote } from 'electron';
const dialog = remote.dialog;

let EditorActionCreators = {
  readFilepath(filepath) {
    fs.readFile(filepath, 'utf8', function(err, data) {
      if (err) {
        console.log('error');
      } else {
        localStorage.setItem('lastFile', filepath);
        AppDispatcher.dispatch({
          type: ActionTypes.OPEN_FILE,
          payload: {
            filepath: filepath,
            code: data
          }
        });
      }
    });
  },
  openFile() {
    dialog.showOpenDialog({
      filters: [{ name: 'python', extensions: ['py']}]
    }, (filepaths) => {
      if (filepaths === undefined) return;
      // Store as lastFile in localStorage. lastFile is auto-opened on startup.
      localStorage.setItem('lastFile', filepaths[0]);
      this.readFilepath(filepaths[0]);
    });
  },
  saveFile(filepath, code) {
    function writeContents(filepath) {
      fs.writeFile(filepath, code, function(err) {
        if (err) {
          console.log(err);
        } else {
          AppDispatcher.dispatch({
            type: ActionTypes.SAVE_FILE,
            payload: {
              code: code,
              filepath: filepath
            }
          });
        }
      });
    };

    if (filepath === null) {
      dialog.showSaveDialog({
        filters: [{ name: 'python', extensions: ['py']}]
      }, function(filepath) {
        if (filepath === undefined) return;

        // Automatically append .py extension if they don't have it
        if (filepath.endsWith('.py')) {
          writeContents(filepath);
        } else {
          writeContents(filepath + '.py');
        }
      });
    } else {
      writeContents(filepath);
    }
  },
  createNewFile() {
    localStorage.removeItem('lastFile');
    AppDispatcher.dispatch({
      type: ActionTypes.CLEAR_EDITOR
    });
  },
  deleteFile(filepath) {
    fs.unlink(filepath, function() {
      AppDispatcher.dispatch({
        type: ActionTypes.CLEAR_EDITOR
      });
    });
  },
  editorUpdate(newVal) {
    AppDispatcher.dispatch({
      type: ActionTypes.UPDATE_EDITOR,
      payload: {
        code: newVal
      }
    });
  }
};

export default EditorActionCreators;
<|endoftext|>


<commit_before>'use strict';

const Mailgun = require('mailgun-js');
const cons = require('consolidate');
const packageData = require('../package.json');

const whitelistExact = [
  'from',
  'to',
  'cc',
  'bcc',
  'subject',
  'text',
  'html',
  'attachment',
  'inline',
  'recipient-variables',
  'o:tag',
  'o:campaign',
  'o:dkim',
  'o:deliverytime',
  'o:testmode',
  'o:tracking',
  'o:tracking-clicks',
  'o:tracking-opens',
  'o:require-tls',
  'o:skip-verification',
  'X-Mailgun-Variables'
];
const whitelistPrefix = [
  'h:',
  'v:'
];

const transformList = [
  {
    nodemailerKey: 'replyTo',
    mailgunKey: 'h:Reply-To'
  }
];

module.exports = function (options) {
  return new MailgunTransport(options);
};

function MailgunTransport(options) {
  this.options = options || {};
  this.name = 'Mailgun';
  this.version = packageData.version;

  this.mailgun = Mailgun({
    apiKey: this.options.auth.api_key,
    domain: this.options.auth.domain || '',
    proxy: this.options.proxy || false,
    host: this.options.host || 'api.mailgun.net',
    protocol: this.options.protocol || 'https:',
    port: this.options.port || 443
  });
  this.messages = this.mailgun.messages();
}

MailgunTransport.prototype.send = function send(mail, callback) {
  const self = this;
  const mailData = mail.data;
  const resolveTemplate = () => {
    return new Promise((resolve, reject) => {
      if (mailData.template && mailData.template.name && mailData.template.engine) {
        mailData.template.context = mailData.template.context || {};
        cons[mailData.template.engine](mailData.template.name, mailData.template.context, (err, html) => {
          if (err) {
            reject(err);
          }
          mailData.html = html;
          resolve();
        });
      } else {
        resolve();
      }
    })
  }
  const convertAddressesToStrings = () => {
    // convert address objects or array of objects to strings if present
    const targets = ['from','to','cc','bcc','replyTo'];
    for (const target of targets) {
      const addrsData = mailData[target];
      if (addrsData !== null && (typeof addrsData === 'object' || Array.isArray(addrsData))) {
        const addrs= [];
        const addresses = typeof addrsData === 'object' ? [addrsData] : addrsData;
        for (const addr of addresses ) {
          if (Array.isArray(addr)) {
            for (const add of addr) {
              if (typeof add === 'object' && add.address) {
                const final = add.name ? add.name + ' <' + add.address + '>' : add.address
                addrs.push(final);
              } else if (typeof add === 'string') {
                addrs.push(add);
              }
            }
          } else {
            if (addr.address) {
              const final = addr.name ? addr.name + ' <' + addr.address + '>' : addr.address;
              addrs.push(final);
            }
          }
        }
        mailData[target] = addrs.join();
      }
    }
  }
  const resolveAttachments = () => {
    // convert nodemailer attachments to mailgun-js attachments
    if (mailData.attachments) {
      let mailgunAttachment, data, attachmentList = [], inlineList = [];
      for (const attachment of mailData.attachments) {
        // mailgunjs does not encode content string to a buffer
        if (typeof attachment.content === 'string') {
          data = Buffer.from(attachment.content, attachment.encoding);
        } else {
          data = attachment.content || attachment.path || undefined;
        }
        mailgunAttachment = new self.mailgun.Attachment({
          data: data,
          filename: attachment.cid || attachment.filename || undefined,
          contentType: attachment.contentType || undefined,
          knownLength: attachment.knownLength || undefined
        });

        if (attachment.cid) {
          inlineList.push(mailgunAttachment);
        } else {
          attachmentList.push(mailgunAttachment);
        }
      }

      mailData.attachment = attachmentList;
      mailData.inline = inlineList;
      delete mailData.attachments;
    }
  }
  const transformMailData = () => {
    delete mailData.headers;

    for (const transform of transformList) {
      if (mailData[transform.nodemailerKey]) {
        mailData[transform.mailgunKey] = mailData[transform.nodemailerKey]
        delete mailData[transform.nodemailerKey]
      }
    }
  }
  const sendMail = () => {
    return new Promise((resolve, reject) => {
      const options = Object.keys(mailData)
        .filter(key => whitelistExact.find(whitelistExactKey => whitelistExactKey === key) || whitelistPrefix.find(whitelistPrefixKey => key.startsWith(whitelistPrefixKey)))
        .reduce((obj, key) => {
          obj[key] = mailData[key];
          return obj;
        }, {});

      self.messages.send(options, (err, data) => {
        if (data) {
          data.messageId = data.id;
        }
        if (err) {
          reject(err)
        }
        resolve(data)
      });
    })
  }
  convertAddressesToStrings();
  transformMailData();
  resolveAttachments();
  resolveTemplate()
    .then(sendMail)
    .then((data) => {
      callback(null, data);
    }).catch((err) => {
      callback(err);
    })
};
<commit_msg>linting cleanup<commit_after>'use strict';

const Mailgun = require('mailgun-js');
const cons = require('consolidate');
const packageData = require('../package.json');

const whitelistExact = [
  'from',
  'to',
  'cc',
  'bcc',
  'subject',
  'text',
  'html',
  'attachment',
  'inline',
  'recipient-variables',
  'o:tag',
  'o:campaign',
  'o:dkim',
  'o:deliverytime',
  'o:testmode',
  'o:tracking',
  'o:tracking-clicks',
  'o:tracking-opens',
  'o:require-tls',
  'o:skip-verification',
  'X-Mailgun-Variables'
];
const whitelistPrefix = [
  'h:',
  'v:'
];

const transformList = [
  {
    nodemailerKey: 'replyTo',
    mailgunKey: 'h:Reply-To'
  }
];

module.exports = function (options) {
  return new MailgunTransport(options);
};

function MailgunTransport(options) {
  this.options = options || {};
  this.name = 'Mailgun';
  this.version = packageData.version;

  this.mailgun = Mailgun({
    apiKey: this.options.auth.api_key,
    domain: this.options.auth.domain || '',
    proxy: this.options.proxy || false,
    host: this.options.host || 'api.mailgun.net',
    protocol: this.options.protocol || 'https:',
    port: this.options.port || 443
  });
  this.messages = this.mailgun.messages();
}

MailgunTransport.prototype.send = function send(mail, callback) {
  const self = this;
  const mailData = mail.data;
  const resolveTemplate = () => {
    return new Promise((resolve, reject) => {
      if (mailData.template && mailData.template.name && mailData.template.engine) {
        mailData.template.context = mailData.template.context || {};
        cons[mailData.template.engine](mailData.template.name, mailData.template.context, (err, html) => {
          if (err) {
            reject(err);
          }
          mailData.html = html;
          resolve();
        });
      } else {
        resolve();
      }
    });
  };
  const convertAddressesToStrings = () => {
    // convert address objects or array of objects to strings if present
    const targets = ['from','to','cc','bcc','replyTo'];
    for (const target of targets) {
      const addrsData = mailData[target];
      if (addrsData !== null && (typeof addrsData === 'object' || Array.isArray(addrsData))) {
        const addrs= [];
        const addresses = typeof addrsData === 'object' ? [addrsData] : addrsData;
        for (const addr of addresses) {
          if (Array.isArray(addr)) {
            for (const add of addr) {
              if (typeof add === 'object' && add.address) {
                const final = add.name ? add.name + ' <' + add.address + '>' : add.address;
                addrs.push(final);
              } else if (typeof add === 'string') {
                addrs.push(add);
              }
            }
          } else {
            if (addr.address) {
              const final = addr.name ? addr.name + ' <' + addr.address + '>' : addr.address;
              addrs.push(final);
            }
          }
        }
        mailData[target] = addrs.join();
      }
    }
  };
  const resolveAttachments = () => {
    // convert nodemailer attachments to mailgun-js attachments
    if (mailData.attachments) {
      let mailgunAttachment, data, attachmentList = [], inlineList = [];
      for (const attachment of mailData.attachments) {
        // mailgunjs does not encode content string to a buffer
        if (typeof attachment.content === 'string') {
          data = Buffer.from(attachment.content, attachment.encoding);
        } else {
          data = attachment.content || attachment.path || undefined;
        }
        mailgunAttachment = new self.mailgun.Attachment({
          data: data,
          filename: attachment.cid || attachment.filename || undefined,
          contentType: attachment.contentType || undefined,
          knownLength: attachment.knownLength || undefined
        });

        if (attachment.cid) {
          inlineList.push(mailgunAttachment);
        } else {
          attachmentList.push(mailgunAttachment);
        }
      }

      mailData.attachment = attachmentList;
      mailData.inline = inlineList;
      delete mailData.attachments;
    }
  };
  const transformMailData = () => {
    delete mailData.headers;

    for (const transform of transformList) {
      if (mailData[transform.nodemailerKey]) {
        mailData[transform.mailgunKey] = mailData[transform.nodemailerKey];
        delete mailData[transform.nodemailerKey];
      }
    }
  };
  const sendMail = () => {
    return new Promise((resolve, reject) => {
      const options = Object.keys(mailData)
        .filter(key => whitelistExact.find(whitelistExactKey => whitelistExactKey === key) || whitelistPrefix.find(whitelistPrefixKey => key.startsWith(whitelistPrefixKey)))
        .reduce((obj, key) => {
          obj[key] = mailData[key];
          return obj;
        }, {});

      self.messages.send(options, (err, data) => {
        if (data) {
          data.messageId = data.id;
        }
        if (err) {
          reject(err);
        }
        resolve(data);
      });
    });
  };
  convertAddressesToStrings();
  transformMailData();
  resolveAttachments();
  resolveTemplate()
    .then(sendMail)
    .then((data) => {
      callback(null, data);
    }).catch((err) => {
      callback(err);
    });
};
<|endoftext|>