<commit_before>import React, {Component} from 'react';
import QueryWindow from '../SearchWindow/querybuilder';
import MapWindow from '../MapWindow/mapView';
import TableWindow from '../TableWindow/tableView';
import {ButtonToolbar, Dropdown, Glyphicon, MenuItem} from 'react-bootstrap';
import ReactDOM from 'react-dom';

import MapAlert from '../Alerts/MapAlert';
import TableAlert from '../Alerts/TableAlert';
import HistogramAlert from '../Alerts/HistogramAlert';
import CloudAlert from '../Alerts/CloudAlert';
import CardAlert from '../Alerts/CardAlert';

function isAlert(t) {
    return t == 'MapAlert' || t == 'TableAlert' || t == 'HistogramAlert' || t == 'CloudAlert' || t.contains('Alert');
}
const DropdownMenu = Dropdown.Menu;

/* eslint-disable react/no-multi-comp*/
/* eslint-disable react/prop-types*/
const GoldenLayout = require('golden-layout');

class CustomToggle extends React.Component {
    constructor(props, context) {
        super(props, context);

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

    handleClick(e) {
        e.preventDefault();

        this.props.onClick(e);
    }

    render() {
        return (
            <Glyphicon glyph="cog" onClick={this.handleClick} />
        );
    }
}

//configuration for the layout
const layoutConfig = {
    settings: {
        hasHeaders: true
    },
    dimensions: {
        borderWidth: 2,
        headerHeight: 25
    },
    content: [{
        type: 'row',
        content: [{
            type: 'column',
            width: 50,
            content: [
                {
                    type: 'row',
                    height: 50,
                    content: [{
                        type: 'react-component',
                        component: 'Search Criteria',
                        title: 'Search Criteria'
                    }]
                },
                {
                    type: 'row',
                    height: 50,
                    content: [{
                        type: 'column',
                        width: 33,
                        content: [
                            {
                                type: 'react-component',
                                component: 'MapAlert',
                                title: 'Alert1',
                                props: { title: 'Alert 1', color: '#53eb53' }
                            }, {
                                type: 'react-component',
                                component: 'TableAlert',
                                title: 'Alert2',
                                props: { title: 'Alert 2', color: '#eb4f4f' }
                            }
                        ]
                    }, {
                            type: 'column',
                            width: 33,
                            content: [
                                {
                                    type: 'react-component',
                                    component: 'HistogramAlert',
                                    title: 'Alert3',
                                    props: { title: 'Alert 3', color: '#eb9650' }
                                }, {
                                    type: 'react-component',
                                    component: 'CloudAlert',
                                    title: 'Alert4',
                                    props: { title: 'Alert 4', color: '#4febeb' }

                                }
                            ]

                        },
                        {
                            type: 'column',
                            width: 33,
                            content: [

                                {
                                    type: 'react-component',
                                    component: 'CardAlert',
                                    title: 'Alert4',
                                    props: { title: 'Alert 4', color: '#4febeb' }

                                }
                            ]
                        }

                    ]
                }
            ]
        }, {
                type: 'row',
                content: [{
                    type: 'column',
                    width: 50,
                    content: [{
                        type: 'stack',
                        height: 50,
                        content: [{
                            type: 'react-component',
                            component: 'Table',
                            title: 'Table'
                        }, {
                                type: 'react-component',
                                component: 'Map',
                                title: 'Map'
                            }, {
                                type: 'react-component',
                                component: 'Cloud',
                                title: 'Cloud'
                            }, {
                                type: 'react-component',
                                component: 'Histogram',
                                title: 'Histogram'
                            }]
                    }]
                }]
            }]
    }]
};
// eslint-disable-next-line react/no-multi-comp

export default class MainLayout extends Component {

    componentDidMount() {
        const temp = React.createClass({
            render: () => {
                return (
test component 3
)
            }
        });
        const queryWidget = React.createClass({
            render: () => {
                return (<QueryWindow/>)
            }
        });

        var alertCtr = 1;

        const alertWall = React.createClass({
            componentDidMount: function () {

                const color = this.props.color || '#000000';
                this.props.glContainer.tab.titleElement.prevObject.css('background-color', this.props.color);
                this.props.glContainer.tab.setTitle(this.props.title || 'No Title');
                // eslint-disable-next-line react/prop-types
                this.props.glContainer.on('tab', (tab) => {
                    tab.titleElement.prevObject.css('background-color', color);
                });
            },
            render: function () {
                return (
Alert Window {alertCtr++}
)
            }
        })

        const layout = new GoldenLayout(layoutConfig);
        layout.registerComponent('Search Criteria', queryWidget);

        layout.registerComponent('MapAlert', MapAlert);
        layout.registerComponent('TableAlert', TableAlert);
        layout.registerComponent('HistogramAlert', HistogramAlert);
        layout.registerComponent('CloudAlert', CloudAlert);
        layout.registerComponent('Alert Wall', alertWall);
        layout.registerComponent('Histogram', temp);
        layout.registerComponent('CardAlert', CardAlert);

        layout.registerComponent('Cloud', temp);
        layout.registerComponent('Map', MapWindow);
        layout.registerComponent('Table', TableWindow);

        layout.on('stackCreated', function (stack) {
            /*
             * Listening for activeContentItemChanged. This happens initially
             * when the stack is created and everytime the user clicks a tab
             */
            stack.on('activeContentItemChanged', function (contentItem) {
                stack.header.controlsContainer.children('.alertCommands').remove();
                const buttonInstance = (
                    <ButtonToolbar>
                        <Dropdown id="dropdown-custom-1"  >
                            <CustomToggle bsRole="toggle">
                                <Glyphicon glyph="cog" />
                            </CustomToggle>
                            <DropdownMenu className="">
                                <MenuItem eventKey="1">Load in Search Window</MenuItem>
                                <MenuItem eventKey="1">Change perspective ></MenuItem>
                                <MenuItem divider />
                                <MenuItem eventKey="1">Color ></MenuItem>
                                <MenuItem eventKey="1">Audio ></MenuItem>
                                <MenuItem eventKey="1">[x]Active</MenuItem>
                                <MenuItem eventKey="1">[x]Intelligent downsample</MenuItem>
                                <MenuItem divider />
                                <MenuItem eventKey="1">Export to CSV</MenuItem>
                                <MenuItem eventKey="1">Export to PNG</MenuItem>
                                <MenuItem divider />
                                <MenuItem eventKey="1">Save this configuration</MenuItem>
                                <MenuItem eventKey="1">Share this configuration</MenuItem>
                            </DropdownMenu>
                        </Dropdown>
                    </ButtonToolbar>

                );

                const searchWindow = (
                    <ButtonToolbar>
                        <Dropdown id="dropdown-custom-1"  >
                            <CustomToggle bsRole="toggle">
                                <Glyphicon glyph="cog" />
                            </CustomToggle>
                            <DropdownMenu className="">
                                <MenuItem eventKey="1">Save Session</MenuItem>
                                <MenuItem eventKey="1">Restore Session </MenuItem>
                                <MenuItem divider />
                                <MenuItem eventKey="1">Disable Updates</MenuItem>
                            </DropdownMenu>
                        </Dropdown>
                    </ButtonToolbar>

                );

                // interact with the contentItem
                if (isAlert(contentItem.config.component)) {

                    stack.header.controlsContainer.prepend('

');
                    stack.header.controlsContainer.prepend('

');
                    ReactDOM.render(buttonInstance, stack.header.controlsContainer.children('.cogSettings')[0]);
                }
                if (contentItem.config.component == 'Search Criteria') {

                    stack.header.controlsContainer.prepend('

');
                    stack.header.controlsContainer.prepend('

');
                    ReactDOM.render(searchWindow, stack.header.controlsContainer.children('.cogSettings')[0]);
                }
            });

            /*
             * Accessing the container and updating its state
             */
            //stack.getActiveContentItem().container.extendState({ color: '#faa' });
        });

        layout.init()
    }

    render() {
        return (

)
    }
}
<commit_msg>Fixed isAlert function Alert function was using a contains method that does not exist.<commit_after>import React, {Component} from 'react';
import QueryWindow from '../SearchWindow/querybuilder';
import MapWindow from '../MapWindow/mapView';
import TableWindow from '../TableWindow/tableView';
import {ButtonToolbar, Dropdown, Glyphicon, MenuItem} from 'react-bootstrap';
import ReactDOM from 'react-dom';

import MapAlert from '../Alerts/MapAlert';
import TableAlert from '../Alerts/TableAlert';
import HistogramAlert from '../Alerts/HistogramAlert';
import CloudAlert from '../Alerts/CloudAlert';
import CardAlert from '../Alerts/CardAlert';

function isAlert(t) {
    return t == 'MapAlert' || t == 'TableAlert' || t == 'HistogramAlert' || t == 'CloudAlert' ||  (t.indexOf('Alert') !== -1);
}
const DropdownMenu = Dropdown.Menu;

/* eslint-disable react/no-multi-comp*/
/* eslint-disable react/prop-types*/
const GoldenLayout = require('golden-layout');

class CustomToggle extends React.Component {
    constructor(props, context) {
        super(props, context);

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

    handleClick(e) {
        e.preventDefault();

        this.props.onClick(e);
    }

    render() {
        return (
            <Glyphicon glyph="cog" onClick={this.handleClick} />
        );
    }
}

//configuration for the layout
const layoutConfig = {
    settings: {
        hasHeaders: true
    },
    dimensions: {
        borderWidth: 2,
        headerHeight: 25
    },
    content: [{
        type: 'row',
        content: [{
            type: 'column',
            width: 50,
            content: [
                {
                    type: 'row',
                    height: 50,
                    content: [{
                        type: 'react-component',
                        component: 'Search Criteria',
                        title: 'Search Criteria'
                    }]
                },
                {
                    type: 'row',
                    height: 50,
                    content: [{
                        type: 'column',
                        width: 33,
                        content: [
                            {
                                type: 'react-component',
                                component: 'MapAlert',
                                title: 'Alert1',
                                props: { title: 'Alert 1', color: '#53eb53' }
                            }, {
                                type: 'react-component',
                                component: 'TableAlert',
                                title: 'Alert2',
                                props: { title: 'Alert 2', color: '#eb4f4f' }
                            }
                        ]
                    }, {
                            type: 'column',
                            width: 33,
                            content: [
                                {
                                    type: 'react-component',
                                    component: 'HistogramAlert',
                                    title: 'Alert3',
                                    props: { title: 'Alert 3', color: '#eb9650' }
                                }, {
                                    type: 'react-component',
                                    component: 'CloudAlert',
                                    title: 'Alert4',
                                    props: { title: 'Alert 4', color: '#4febeb' }

                                }
                            ]

                        },
                        {
                            type: 'column',
                            width: 33,
                            content: [

                                {
                                    type: 'react-component',
                                    component: 'CardAlert',
                                    title: 'Alert4',
                                    props: { title: 'Alert 4', color: '#4febeb' }

                                }
                            ]
                        }

                    ]
                }
            ]
        }, {
                type: 'row',
                content: [{
                    type: 'column',
                    width: 50,
                    content: [{
                        type: 'stack',
                        height: 50,
                        content: [{
                            type: 'react-component',
                            component: 'Table',
                            title: 'Table'
                        }, {
                                type: 'react-component',
                                component: 'Map',
                                title: 'Map'
                            }, {
                                type: 'react-component',
                                component: 'Cloud',
                                title: 'Cloud'
                            }, {
                                type: 'react-component',
                                component: 'Histogram',
                                title: 'Histogram'
                            }]
                    }]
                }]
            }]
    }]
};
// eslint-disable-next-line react/no-multi-comp

export default class MainLayout extends Component {

    componentDidMount() {
        const temp = React.createClass({
            render: () => {
                return (
test component 3
)
            }
        });
        const queryWidget = React.createClass({
            render: () => {
                return (<QueryWindow/>)
            }
        });

        var alertCtr = 1;

        const alertWall = React.createClass({
            componentDidMount: function () {

                const color = this.props.color || '#000000';
                this.props.glContainer.tab.titleElement.prevObject.css('background-color', this.props.color);
                this.props.glContainer.tab.setTitle(this.props.title || 'No Title');
                // eslint-disable-next-line react/prop-types
                this.props.glContainer.on('tab', (tab) => {
                    tab.titleElement.prevObject.css('background-color', color);
                });
            },
            render: function () {
                return (
Alert Window {alertCtr++}
)
            }
        })

        const layout = new GoldenLayout(layoutConfig);
        layout.registerComponent('Search Criteria', queryWidget);

        layout.registerComponent('MapAlert', MapAlert);
        layout.registerComponent('TableAlert', TableAlert);
        layout.registerComponent('HistogramAlert', HistogramAlert);
        layout.registerComponent('CloudAlert', CloudAlert);
        layout.registerComponent('Alert Wall', alertWall);
        layout.registerComponent('Histogram', temp);
        layout.registerComponent('CardAlert', CardAlert);

        layout.registerComponent('Cloud', temp);
        layout.registerComponent('Map', MapWindow);
        layout.registerComponent('Table', TableWindow);

        layout.on('stackCreated', function (stack) {
            /*
             * Listening for activeContentItemChanged. This happens initially
             * when the stack is created and everytime the user clicks a tab
             */
            stack.on('activeContentItemChanged', function (contentItem) {
                stack.header.controlsContainer.children('.alertCommands').remove();
                const buttonInstance = (
                    <ButtonToolbar>
                        <Dropdown id="dropdown-custom-1"  >
                            <CustomToggle bsRole="toggle">
                                <Glyphicon glyph="cog" />
                            </CustomToggle>
                            <DropdownMenu className="">
                                <MenuItem eventKey="1">Load in Search Window</MenuItem>
                                <MenuItem eventKey="1">Change perspective ></MenuItem>
                                <MenuItem divider />
                                <MenuItem eventKey="1">Color ></MenuItem>
                                <MenuItem eventKey="1">Audio ></MenuItem>
                                <MenuItem eventKey="1">[x]Active</MenuItem>
                                <MenuItem eventKey="1">[x]Intelligent downsample</MenuItem>
                                <MenuItem divider />
                                <MenuItem eventKey="1">Export to CSV</MenuItem>
                                <MenuItem eventKey="1">Export to PNG</MenuItem>
                                <MenuItem divider />
                                <MenuItem eventKey="1">Save this configuration</MenuItem>
                                <MenuItem eventKey="1">Share this configuration</MenuItem>
                            </DropdownMenu>
                        </Dropdown>
                    </ButtonToolbar>

                );

                const searchWindow = (
                    <ButtonToolbar>
                        <Dropdown id="dropdown-custom-1"  >
                            <CustomToggle bsRole="toggle">
                                <Glyphicon glyph="cog" />
                            </CustomToggle>
                            <DropdownMenu className="">
                                <MenuItem eventKey="1">Save Session</MenuItem>
                                <MenuItem eventKey="1">Restore Session </MenuItem>
                                <MenuItem divider />
                                <MenuItem eventKey="1">Disable Updates</MenuItem>
                            </DropdownMenu>
                        </Dropdown>
                    </ButtonToolbar>

                );

                // interact with the contentItem
                if (isAlert(contentItem.config.component)) {

                    stack.header.controlsContainer.prepend('

');
                    stack.header.controlsContainer.prepend('

');
                    ReactDOM.render(buttonInstance, stack.header.controlsContainer.children('.cogSettings')[0]);
                }
                if (contentItem.config.component == 'Search Criteria') {

                    stack.header.controlsContainer.prepend('

');
                    stack.header.controlsContainer.prepend('

');
                    ReactDOM.render(searchWindow, stack.header.controlsContainer.children('.cogSettings')[0]);
                }
            });

            /*
             * Accessing the container and updating its state
             */
            //stack.getActiveContentItem().container.extendState({ color: '#faa' });
        });

        layout.init()
    }

    render() {
        return (

)
    }
}
<|endoftext|>


<commit_before>var gui = window.require('nw.gui');
var platform = require('./platform');
var settings = require('./settings');
var utils = require('./utils');

module.exports = {
  /**
   * Update the behaviour of the given window object.
   */
  set: function(win) {
    // Show the window when the dock icon is pressed
    gui.App.removeAllListeners('reopen');
    gui.App.on('reopen', function() {
      win.show();
    });

    gui.App.on('open', function() {
      win.show();
    });

    // Don't quit the app when the window is closed
      win.removeAllListeners('close');
      win.on('close', function(quit) {
        if (quit) {
          this.saveWindowState(win);
          win.close(true);
        } else {
          win.hide();
        }
      }.bind(this));
  },

  /**
   * Close the window using the ESC key.
   */
  closeWithEscKey: function(win, doc) {
    doc.onkeyup = function(e) {
      if (e.keyCode == 27 && settings.closeWithEscKey) {
        e.preventDefault();
        win.close();
        return false;
      }
    }
  },

  /**
   * Change the new window policy to open links in the browser or another window.
   */
  setNewWinPolicy: function(win) {
    win.removeAllListeners('new-win-policy');
    win.on('new-win-policy', function(frame, url, policy) {
      if (url == 'about:blank') {
        return policy.ignore(); // Ignore about:blank URLs
      }

      if (settings.openLinksInBrowser) {
        url = utils.skipFacebookRedirect(url);
        gui.Shell.openExternal(url);
        policy.ignore();
      } else {
        policy.forceNewWindow();
      }
    });
  },

  /**
   * Listen for window state events.
   */
  bindWindowStateEvents: function(win) {
    win.removeAllListeners('maximize');
    win.on('maximize', function() {
      win.sizeMode = 'maximized';
    });

    win.removeAllListeners('unmaximize');
    win.on('unmaximize', function() {
      win.sizeMode = 'normal';
    });

    win.removeAllListeners('minimize');
    win.on('minimize', function() {
      win.sizeMode = 'minimized';
    });

    win.removeAllListeners('restore');
    win.on('restore', function() {
      win.sizeMode = 'normal';
    });
  },

  /**
   * Bind the events of the node window to the content window.
   */
  bindEvents: function(win, contentWindow) {
    ['focus', 'blur'].forEach(function(name) {
      win.removeAllListeners(name);
      win.on(name, function() {
        if (contentWindow.dispatchEvent && contentWindow.Event) {
          contentWindow.dispatchEvent(new contentWindow.Event(name));
        }
      });
    });
  },

  /**
   * Set an interval to sync the badge and the title.
   */
  syncBadgeAndTitle: function(win, parentDoc, childDoc) {
    var notifCountRegex = /\((\d)\)/;
    var defaultTitle = childDoc.title;

    setInterval(function() {
      parentDoc.title = childDoc.title;
      defaultTitle = defaultTitle || childDoc.title;

      var label = '';

      if (childDoc.title != defaultTitle) {
        var countMatch = notifCountRegex.exec(childDoc.title);
        label = countMatch && countMatch[1] || '';

        if (!label) {
          // Probably it says that someone messaged the user
          // This prevents the badge from blinking at the same time with the title
          return;
        }
      }

      win.setBadgeLabel(label);

      // Update the tray icon too
      if (win.tray) {
        var type = platform.isOSX ? 'menubar' : 'tray';
        var alert = label ? '_alert' : '';
        var extension = platform.isOSX ? '.tiff' : '.png';
        win.tray.icon = 'images/icon_' + type + alert + extension;
      }
    }, 100);
  },

  /**
   * Store the window state.
   */
  saveWindowState: function(win) {
    var state = {
      mode: win.sizeMode || 'normal'
    };

    if (state.mode == 'normal') {
      state.x = win.x;
      state.y = win.y;
      state.width = win.width;
      state.height = win.height;
    }

    settings.windowState = state;
  },

  /**
   * Restore the window size and position.
   */
  restoreWindowState: function(win) {
    var state = settings.windowState;

    if (state.mode == 'maximized') {
      win.maximize();
    } else {
      win.resizeTo(state.width, state.height);
      win.moveTo(state.x, state.y);
    }

<<<<<<< HEAD
    if(!settings.startMinimized)
      win.show();
    else
      win.hide();
  },

  /**
   * Reload the app periodically until online.
   */
  reloadUntilOnline: function(win) {
    var reloadIntervalId = setInterval(function() {
      if (win.window.navigator.onLine) {
        clearInterval(reloadIntervalId);
      } else {
        win.reload();
      }
    }, 5 * 1000);
  }
};
<commit_msg>Fix problem in merge<commit_after>var gui = window.require('nw.gui');
var platform = require('./platform');
var settings = require('./settings');
var utils = require('./utils');

module.exports = {
  /**
   * Update the behaviour of the given window object.
   */
  set: function(win) {
    // Show the window when the dock icon is pressed
    gui.App.removeAllListeners('reopen');
    gui.App.on('reopen', function() {
      win.show();
    });

    gui.App.on('open', function() {
      win.show();
    });

    // Don't quit the app when the window is closed
      win.removeAllListeners('close');
      win.on('close', function(quit) {
        if (quit) {
          this.saveWindowState(win);
          win.close(true);
        } else {
          win.hide();
        }
      }.bind(this));
  },

  /**
   * Close the window using the ESC key.
   */
  closeWithEscKey: function(win, doc) {
    doc.onkeyup = function(e) {
      if (e.keyCode == 27 && settings.closeWithEscKey) {
        e.preventDefault();
        win.close();
        return false;
      }
    }
  },

  /**
   * Change the new window policy to open links in the browser or another window.
   */
  setNewWinPolicy: function(win) {
    win.removeAllListeners('new-win-policy');
    win.on('new-win-policy', function(frame, url, policy) {
      if (url == 'about:blank') {
        return policy.ignore(); // Ignore about:blank URLs
      }

      if (settings.openLinksInBrowser) {
        url = utils.skipFacebookRedirect(url);
        gui.Shell.openExternal(url);
        policy.ignore();
      } else {
        policy.forceNewWindow();
      }
    });
  },

  /**
   * Listen for window state events.
   */
  bindWindowStateEvents: function(win) {
    win.removeAllListeners('maximize');
    win.on('maximize', function() {
      win.sizeMode = 'maximized';
    });

    win.removeAllListeners('unmaximize');
    win.on('unmaximize', function() {
      win.sizeMode = 'normal';
    });

    win.removeAllListeners('minimize');
    win.on('minimize', function() {
      win.sizeMode = 'minimized';
    });

    win.removeAllListeners('restore');
    win.on('restore', function() {
      win.sizeMode = 'normal';
    });
  },

  /**
   * Bind the events of the node window to the content window.
   */
  bindEvents: function(win, contentWindow) {
    ['focus', 'blur'].forEach(function(name) {
      win.removeAllListeners(name);
      win.on(name, function() {
        if (contentWindow.dispatchEvent && contentWindow.Event) {
          contentWindow.dispatchEvent(new contentWindow.Event(name));
        }
      });
    });
  },

  /**
   * Set an interval to sync the badge and the title.
   */
  syncBadgeAndTitle: function(win, parentDoc, childDoc) {
    var notifCountRegex = /\((\d)\)/;
    var defaultTitle = childDoc.title;

    setInterval(function() {
      parentDoc.title = childDoc.title;
      defaultTitle = defaultTitle || childDoc.title;

      var label = '';

      if (childDoc.title != defaultTitle) {
        var countMatch = notifCountRegex.exec(childDoc.title);
        label = countMatch && countMatch[1] || '';

        if (!label) {
          // Probably it says that someone messaged the user
          // This prevents the badge from blinking at the same time with the title
          return;
        }
      }

      win.setBadgeLabel(label);

      // Update the tray icon too
      if (win.tray) {
        var type = platform.isOSX ? 'menubar' : 'tray';
        var alert = label ? '_alert' : '';
        var extension = platform.isOSX ? '.tiff' : '.png';
        win.tray.icon = 'images/icon_' + type + alert + extension;
      }
    }, 100);
  },

  /**
   * Store the window state.
   */
  saveWindowState: function(win) {
    var state = {
      mode: win.sizeMode || 'normal'
    };

    if (state.mode == 'normal') {
      state.x = win.x;
      state.y = win.y;
      state.width = win.width;
      state.height = win.height;
    }

    settings.windowState = state;
  },

  /**
   * Restore the window size and position.
   */
  restoreWindowState: function(win) {
    var state = settings.windowState;

    if (state.mode == 'maximized') {
      win.maximize();
    } else {
      win.resizeTo(state.width, state.height);
      win.moveTo(state.x, state.y);
    }

    if(!settings.startMinimized)
      win.show();
    else
      win.hide();
  },

  /**
   * Reload the app periodically until online.
   */
  reloadUntilOnline: function(win) {
    var reloadIntervalId = setInterval(function() {
      if (win.window.navigator.onLine) {
        clearInterval(reloadIntervalId);
      } else {
        win.reload();
      }
    }, 5 * 1000);
  }
};
<|endoftext|>


<commit_before>var localtunnel = require('localtunnel');

module.exports = function (slackapp, port) {
  if (process.env.ENABLE_LOCAL_TUNNEL) {
    localtunnel(port, (err, tunnel) => {
      if (err) {
        throw err;
      }

      console.log('Tunnel started at', tunnel.url);

      tunnel.on('close', () => {
        console.error('Your tunnel was closed.');
        process.exit();
      });
    });
  }

  // Custom endpoint for External Webhooks from Trustpilot
  slackapp.webserver.post('/incoming-webhooks/:teamId', (req, res) => {
    var events = req.body.events;

    events.filter((e) => {
      return e.eventName === 'service review created';
    }).forEach((e) => {
      e.eventData.consumer.displayName = e.eventData.consumer.name; // Massaging into expected format
      slackapp.postNewReview(e.eventData, req.params.teamId);
    });

    res.sendStatus(200);
  });
};<commit_msg>Fix event name for incoming webhook<commit_after>var localtunnel = require('localtunnel');

module.exports = function (slackapp, port) {
  if (process.env.ENABLE_LOCAL_TUNNEL) {
    localtunnel(port, (err, tunnel) => {
      if (err) {
        throw err;
      }

      console.log('Tunnel started at', tunnel.url);

      tunnel.on('close', () => {
        console.error('Your tunnel was closed.');
        process.exit();
      });
    });
  }

  // Custom endpoint for External Webhooks from Trustpilot
  slackapp.webserver.post('/incoming-webhooks/:teamId', (req, res) => {
    var events = req.body.events;

    events.filter((e) => {
      return e.eventName === 'service-review-created';
    }).forEach((e) => {
      e.eventData.consumer.displayName = e.eventData.consumer.name; // Massaging into expected format
      slackapp.postNewReview(e.eventData, req.params.teamId);
    });

    res.sendStatus(200);
  });
};<|endoftext|>


<commit_before>'use strict';

var React = require('react'),
	getExtension = require('./getExtension'),
	image = require('./plugins/image.js'),
	generic = require('./plugins/generic/generic.js'),
	pdfNative = require('./plugins/pdf/native.js');

var viewers = [
	image,
	pdfNative,
	generic
];

var FileViewer = React.createClass({
	propTypes: {
		src: React.PropTypes.string.isRequired
	},
	render: function() {
		var extension = getExtension(this.props.src);
		var viewer;
		for (var i = 0; i < viewers.length; i++) {
			if (viewers[i].test(extension)) {
				viewer = viewers[i].getComponent(this.props);
				break;
			}
		};
		return (
{viewer}
);
	}
});

module.exports = FileViewer;
<commit_msg>Removing unnecessary semi-colon (found using shared linting config).<commit_after>'use strict';

var React = require('react'),
	getExtension = require('./getExtension'),
	image = require('./plugins/image.js'),
	generic = require('./plugins/generic/generic.js'),
	pdfNative = require('./plugins/pdf/native.js');

var viewers = [
	image,
	pdfNative,
	generic
];

var FileViewer = React.createClass({
	propTypes: {
		src: React.PropTypes.string.isRequired
	},
	render: function() {
		var extension = getExtension(this.props.src);
		var viewer;
		for (var i = 0; i < viewers.length; i++) {
			if (viewers[i].test(extension)) {
				viewer = viewers[i].getComponent(this.props);
				break;
			}
		}
		return (
{viewer}
);
	}
});

module.exports = FileViewer;
<|endoftext|>


<commit_before>/*
 * flipbooker
 * https://github.com/corporadobob/flipbooker
 *
 * Copyright (c) 2014 JC
 * Licensed under the MIT license.
 */

(function($) {

  $.fn.flipbooker = function(elems, delay) {

    var currentIndex = 0,
    $container = this,
    $image,
    elemsLength = elems.length,
    currentImg;

    // set up the image element
    $container.append('<img>');
    $image = $container.children('img');

    setInterval(function(){

      currentImg = elems[currentIndex];
      $image.attr('src', currentImg.href);

      if (currentImg.pause) {
        $.sleep(currentImg.pause);
      }

      currentIndex++;

      // loop
      if (currentIndex >= elemsLength) {
        currentIndex = 0;
      }

    }, delay);

    return this;
  };

  $.sleep = function(milliseconds){
    var start = new Date().getTime();
    for (var i = 0; i < 1e7; i++) {
      if ((new Date().getTime() - start) > milliseconds){
        break;
      }
    }
  };

}(jQuery));
<commit_msg>add plugin options. abstract  from options instead of calling plugin on it<commit_after>/*
 * flipbooker
 * https://github.com/corporadobob/flipbooker
 *
 * Copyright (c) 2014 JC
 * Licensed under the MIT license.
 */

(function($) {

  $.flipbooker = function(imgArr, options) {

    var opts = $.extend(true, {}, $.flipbooker.defaults, options),
    currentIndex = 0,
    $container = $(opts.container),
    $image,
    imgArrSize = imgArr.length,
    currentImg;

    // set up the image element
    $container.append('<img>');
    $image = $container.children('img');

    setInterval(function(){

      // set the image source to the currentImg
      // object's href value
      currentImg = imgArr[currentIndex];
      $image.attr('src', currentImg.href);

      // sleep if the currentImg object requests a pause
      if (currentImg.pause) {
        $.sleep(currentImg.pause);
      }

      // move onto the next image object
      currentIndex++;

      // loop
      if (currentIndex >= imgArrSize) {
        currentIndex = 0;
      }

    }, opts.delay);

    return this;
  };

  $.flipbooker.defaults = {
    container: '#slideshow',
    loop: true,
    delay: 50
  };

  $.sleep = function(milliseconds){
    var start = new Date().getTime();
    for (var i = 0; i < 1e7; i++) {
      if ((new Date().getTime() - start) > milliseconds){
        break;
      }
    }
  };

}(jQuery));
<|endoftext|>


<commit_before>'use strict';

require('es6-promise').polyfill();

var debug = require('../helper/debug')('fn-ramp');
var columnName = require('../helper/column-name');
var TurboCartoError = require('../helper/turbo-carto-error');
var isResult = require('../model/is-result');
var linearBuckets = require('../helper/linear-buckets');
var ValuesResult = require('../model/values-result');
var FiltersResult = require('../model/filters-result');
var LazyFiltersResult = require('../model/lazy-filters-result');
var postcss = require('postcss');

function createSplitStrategy (selector) {
  return function splitStrategy (column, rampResult, decl) {
    var defaultValue = rampResult[1];
    var initialDecl = postcss.decl({ prop: decl.prop, value: defaultValue });
    decl.replaceWith(initialDecl);

    var previousNode = initialDecl;
    for (var i = 2, until = rampResult.length; i < until; i += 2) {
      var rule = postcss.rule({
        selector: selector(column, rampResult[i])
      });
      rule.append(postcss.decl({ prop: decl.prop, value: rampResult[i + 1] }));

      rule.moveAfter(previousNode);
      previousNode = rule;
    }

    return rampResult;
  };
}

var strategy = {
  max: function maxStrategy (column, rampResult, decl) {
    var defaultValue = rampResult[1];
    var initialDecl = postcss.decl({ prop: decl.prop, value: defaultValue });
    decl.replaceWith(initialDecl);

    var previousNode = initialDecl;
    for (var i = 0, until = rampResult.length - 2; i < until; i += 2) {
      var rule = postcss.rule({
        selector: '[ ' + column + ' > ' + rampResult[i] + ' ]'
      });
      rule.append(postcss.decl({ prop: decl.prop, value: rampResult[i + 3] }));

      rule.moveAfter(previousNode);
      previousNode = rule;
    }

    return rampResult;
  },

  split: createSplitStrategy(function gtSelector (column, value) {
    return '[ ' + column + ' > ' + value + ' ]';
  }),

  exact: createSplitStrategy(function exactSelector (column, value) {
    return Number.isFinite(value) ? '[ ' + column + ' = ' + value + ' ]' : '[ ' + column + ' = "' + value + '" ]';
  })
};

module.exports = function (datasource, decl) {
  return function fn$ramp (column, /* ... */args) {
    debug('fn$ramp(%j)', arguments);
    debug('Using "%s" datasource to calculate ramp', datasource.getName());

    args = Array.prototype.slice.call(arguments, 1);

    return ramp(datasource, column, args)
      .then(function (rampResult) {
        var strategyFn = strategy.hasOwnProperty(rampResult.strategy) ? strategy[rampResult.strategy] : strategy.max;
        return strategyFn(columnName(column), rampResult.ramp, decl);
      })
      .catch(function (err) {
        var context = {};
        if (decl.parent) {
          context.selector = decl.parent.selector;
        }
        if (decl.source) {
          context.source = {
            start: decl.source.start,
            end: decl.source.end
          };
        }
        throw new TurboCartoError('Failed to process "' + decl.prop + '" property:', err, context);
      });
  };
};

/**
 * @param datasource
 * @param {String} column
 * @param args
 *
 * ####################
 * ###  COLOR RAMP  ###
 * ####################
 *
 * [colorbrewer(Greens, 7), jenks]
 *  <Array>scheme         , <String>method
 *
 * [colorbrewer(Greens, 7)]
 *  <Array>scheme
 *
 * ####################
 * ### NUMERIC RAMP ###
 * ####################
 *
 * [10            , 20]
 *  <Number>minVal, <Number>maxValue
 *
 * [10            , 20,             , jenks]
 *  <Number>minVal, <Number>maxValue, <String>method
 *
 * [10            , 20,             , 4]
 *  <Number>minVal, <Number>maxValue, <Number>numBuckets
 *
 * [10            , 20,             , 4              , jenks]
 *  <Number>minVal, <Number>maxValue, <Number>numBuckets, <String>method
 */
function ramp (datasource, column, args) {
  if (args.length === 0) {
    return Promise.reject(
      new TurboCartoError('invalid number of arguments')
    );
  }

  /**
   * Overload scenarios to support
   * marker-width: ramp([price], 4, 100);
   * marker-width: ramp([price], 4, 100, method);
   * marker-width: ramp([price], 4, 100, 5, method);
   * marker-width: ramp([price], 4, 100, 3, (100, 200, 1000));
   * marker-width: ramp([price], 4, 100, (100, 150, 250, 200, 1000));
   */
  if (Number.isFinite(+args[0])) {
    return compatibilityNumericRamp(datasource, column, args);
  }

  /**
   * Overload methods to support
   * marker-fill: ramp([price], colorbrewer(Reds));
   * marker-fill: ramp([price], colorbrewer(Reds), jenks);
   */
  if (!isResult(args[1])) {
    return compatibilityValuesRamp(datasource, column, args);
  }

  /**
   * Overload methods to support from here
   * marker-fill: ramp([price], colorbrewer(Reds), (100, 200, 300, 400, 500));
   * marker-fill: ramp([price], colorbrewer(Reds), (100, 200, 300, 400, 500), =);
   */
  var values = args[0];
  var filters = args[1];
  var mapping = args[2];
  var strategy = strategyFromMapping(mapping);
  filters = filters.is(ValuesResult) ? new FiltersResult(filters.get(), strategy) : filters;
  if (filters.is(LazyFiltersResult)) {
    return filters.get(column).then(createRampFn(values));
  } else {
    return Promise.resolve(filters).then(createRampFn(values));
  }
}

function strategyFromMapping (mapping) {
  if (mapping === '=' || mapping === 'category') {
    return 'exact';
  }
  return 'split';
}

/**
 * Overload methods to support
 * marker-fill: ramp([price], colorbrewer(Reds));
 * marker-fill: ramp([price], colorbrewer(Reds), jenks);
 */
function compatibilityValuesRamp (datasource, column, args) {
  var values = args[0];
  var method = (args[1] || 'quantiles').toLowerCase();
  var numBuckets = values.getLength();
  return getRamp(datasource, column, numBuckets, method).then(compatibilityCreateRampFn(values));
}

/**
 * Overload scenarios to support
 * marker-width: ramp([price], 4, 100);
 * marker-width: ramp([price], 4, 100, method);
 * marker-width: ramp([price], 4, 100, 5, method); √
 * marker-width: ramp([price], 4, 100, 3, (100, 200, 1000)); √
 * marker-width: ramp([price], 4, 100, (100, 150, 250, 200, 1000)); √
 */
function compatibilityNumericRamp (datasource, column, args) {
  // jshint maxcomplexity:9
  if (args.length < 2) {
    return Promise.reject(
      new TurboCartoError('invalid number of arguments')
    );
  }

  var min = +args[0];
  var max = +args[1];

  var numBuckets;
  var filters = null;
  var method;

  if (Number.isFinite(+args[2])) {
    numBuckets = +args[2];

    if (isResult(args[3])) {
      filters = args[3];
      method = null;
      if (filters.getLength() !== numBuckets) {
        return Promise.reject(
          new TurboCartoError(
              'invalid ramp length, got ' + filters.getLength() + ' values, expected ' + numBuckets
          )
        );
      }
    } else {
      filters = null;
      method = args[3];
    }
  } else if (isResult(args[2])) {
    filters = args[2];
    numBuckets = filters.getLength();
    method = null;
  } else {
    filters = null;
    numBuckets = 5;
    method = args[2];
  }

  var values = new ValuesResult([min, max], numBuckets, linearBuckets, Number.POSITIVE_INFINITY);

  if (filters === null) {
    // normalize method
    method = (method || 'quantiles').toLowerCase();
    return getRamp(datasource, column, numBuckets, method).then(compatibilityCreateRampFn(values));
  }

  filters = filters.is(FiltersResult) ? filters : new FiltersResult(filters.get(), 'max');
  return Promise.resolve(filters).then(compatibilityCreateRampFn(values));
}

function getRamp (datasource, column, buckets, method) {
  return new Promise(function (resolve, reject) {
    datasource.getRamp(columnName(column), buckets, method, function (err, filters) {
      if (err) {
        return reject(
          new TurboCartoError('unable to compute ramp,', err)
        );
      }
      var strategy = 'max';
      if (!Array.isArray(filters)) {
        strategy = filters.strategy || 'max';
        filters = filters.ramp;
      }
      resolve(new FiltersResult(filters, strategy));
    });
  });
}

function compatibilityCreateRampFn (valuesResult) {
  return function prepareRamp (filtersResult) {
    var buckets = Math.min(valuesResult.getLength(), filtersResult.getLength());

    var i;
    var rampResult = [];

    var filters = filtersResult.get();
    var values = valuesResult.get();

    if (buckets > 0) {
      for (i = 0; i < buckets; i++) {
        rampResult.push(filters[i]);
        rampResult.push(values[i]);
      }
    } else {
      rampResult.push(null, values[0]);
    }

    return { ramp: rampResult, strategy: filtersResult.getStrategy() };
  };
}

function createRampFn (valuesResult) {
  return function prepareRamp (filtersResult) {
    var buckets = Math.min(valuesResult.getMaxSize(), filtersResult.getMaxSize());

    var i;
    var rampResult = [];

    var filters = filtersResult.get();
    var values = valuesResult.get(buckets);

    if (buckets > 0) {
      for (i = 0; i < buckets; i++) {
        rampResult.push(filters[i]);
        rampResult.push(values[i]);
      }
    } else {
      rampResult.push(null, values[0]);
    }

    return { ramp: rampResult, strategy: filtersResult.getStrategy() };
  };
}

module.exports.fnName = 'ramp';
<commit_msg>Add other supported signature<commit_after>'use strict';

require('es6-promise').polyfill();

var debug = require('../helper/debug')('fn-ramp');
var columnName = require('../helper/column-name');
var TurboCartoError = require('../helper/turbo-carto-error');
var isResult = require('../model/is-result');
var linearBuckets = require('../helper/linear-buckets');
var ValuesResult = require('../model/values-result');
var FiltersResult = require('../model/filters-result');
var LazyFiltersResult = require('../model/lazy-filters-result');
var postcss = require('postcss');

function createSplitStrategy (selector) {
  return function splitStrategy (column, rampResult, decl) {
    var defaultValue = rampResult[1];
    var initialDecl = postcss.decl({ prop: decl.prop, value: defaultValue });
    decl.replaceWith(initialDecl);

    var previousNode = initialDecl;
    for (var i = 2, until = rampResult.length; i < until; i += 2) {
      var rule = postcss.rule({
        selector: selector(column, rampResult[i])
      });
      rule.append(postcss.decl({ prop: decl.prop, value: rampResult[i + 1] }));

      rule.moveAfter(previousNode);
      previousNode = rule;
    }

    return rampResult;
  };
}

var strategy = {
  max: function maxStrategy (column, rampResult, decl) {
    var defaultValue = rampResult[1];
    var initialDecl = postcss.decl({ prop: decl.prop, value: defaultValue });
    decl.replaceWith(initialDecl);

    var previousNode = initialDecl;
    for (var i = 0, until = rampResult.length - 2; i < until; i += 2) {
      var rule = postcss.rule({
        selector: '[ ' + column + ' > ' + rampResult[i] + ' ]'
      });
      rule.append(postcss.decl({ prop: decl.prop, value: rampResult[i + 3] }));

      rule.moveAfter(previousNode);
      previousNode = rule;
    }

    return rampResult;
  },

  split: createSplitStrategy(function gtSelector (column, value) {
    return '[ ' + column + ' > ' + value + ' ]';
  }),

  exact: createSplitStrategy(function exactSelector (column, value) {
    return Number.isFinite(value) ? '[ ' + column + ' = ' + value + ' ]' : '[ ' + column + ' = "' + value + '" ]';
  })
};

module.exports = function (datasource, decl) {
  return function fn$ramp (column, /* ... */args) {
    debug('fn$ramp(%j)', arguments);
    debug('Using "%s" datasource to calculate ramp', datasource.getName());

    args = Array.prototype.slice.call(arguments, 1);

    return ramp(datasource, column, args)
      .then(function (rampResult) {
        var strategyFn = strategy.hasOwnProperty(rampResult.strategy) ? strategy[rampResult.strategy] : strategy.max;
        return strategyFn(columnName(column), rampResult.ramp, decl);
      })
      .catch(function (err) {
        var context = {};
        if (decl.parent) {
          context.selector = decl.parent.selector;
        }
        if (decl.source) {
          context.source = {
            start: decl.source.start,
            end: decl.source.end
          };
        }
        throw new TurboCartoError('Failed to process "' + decl.prop + '" property:', err, context);
      });
  };
};

/**
 * @param datasource
 * @param {String} column
 * @param args
 *
 * ####################
 * ###  COLOR RAMP  ###
 * ####################
 *
 * [colorbrewer(Greens, 7), jenks]
 *  <Array>scheme         , <String>method
 *
 * [colorbrewer(Greens, 7)]
 *  <Array>scheme
 *
 * ####################
 * ### NUMERIC RAMP ###
 * ####################
 *
 * [10            , 20]
 *  <Number>minVal, <Number>maxValue
 *
 * [10            , 20,             , jenks]
 *  <Number>minVal, <Number>maxValue, <String>method
 *
 * [10            , 20,             , 4]
 *  <Number>minVal, <Number>maxValue, <Number>numBuckets
 *
 * [10            , 20,             , 4              , jenks]
 *  <Number>minVal, <Number>maxValue, <Number>numBuckets, <String>method
 */
function ramp (datasource, column, args) {
  if (args.length === 0) {
    return Promise.reject(
      new TurboCartoError('invalid number of arguments')
    );
  }

  /**
   * Overload scenarios to support
   * marker-width: ramp([price], 4, 100);
   * marker-width: ramp([price], 4, 100, method);
   * marker-width: ramp([price], 4, 100, 5, method);
   * marker-width: ramp([price], 4, 100, 3, (100, 200, 1000));
   * marker-width: ramp([price], 4, 100, (100, 150, 250, 200, 1000));
   */
  if (Number.isFinite(+args[0])) {
    return compatibilityNumericRamp(datasource, column, args);
  }

  /**
   * Overload methods to support
   * marker-fill: ramp([price], colorbrewer(Reds));
   * marker-fill: ramp([price], colorbrewer(Reds), jenks);
   */
  if (!isResult(args[1])) {
    return compatibilityValuesRamp(datasource, column, args);
  }

  /**
   * Overload methods to support from here
   * marker-fill: ramp([price], colorbrewer(Reds), (100, 200, 300, 400, 500));
   * marker-fill: ramp([price], colorbrewer(Reds), (100, 200, 300, 400, 500), =);
   * marker-fill: ramp([price], (...values), (...filters), [mapping]);
   */
  var values = args[0];
  var filters = args[1];
  var mapping = args[2];
  var strategy = strategyFromMapping(mapping);
  filters = filters.is(ValuesResult) ? new FiltersResult(filters.get(), strategy) : filters;
  if (filters.is(LazyFiltersResult)) {
    return filters.get(column).then(createRampFn(values));
  } else {
    return Promise.resolve(filters).then(createRampFn(values));
  }
}

function strategyFromMapping (mapping) {
  if (mapping === '=' || mapping === 'category') {
    return 'exact';
  }
  return 'split';
}

/**
 * Overload methods to support
 * marker-fill: ramp([price], colorbrewer(Reds));
 * marker-fill: ramp([price], colorbrewer(Reds), jenks);
 */
function compatibilityValuesRamp (datasource, column, args) {
  var values = args[0];
  var method = (args[1] || 'quantiles').toLowerCase();
  var numBuckets = values.getLength();
  return getRamp(datasource, column, numBuckets, method).then(compatibilityCreateRampFn(values));
}

/**
 * Overload scenarios to support
 * marker-width: ramp([price], 4, 100);
 * marker-width: ramp([price], 4, 100, method);
 * marker-width: ramp([price], 4, 100, 5, method); √
 * marker-width: ramp([price], 4, 100, 3, (100, 200, 1000)); √
 * marker-width: ramp([price], 4, 100, (100, 150, 250, 200, 1000)); √
 */
function compatibilityNumericRamp (datasource, column, args) {
  // jshint maxcomplexity:9
  if (args.length < 2) {
    return Promise.reject(
      new TurboCartoError('invalid number of arguments')
    );
  }

  var min = +args[0];
  var max = +args[1];

  var numBuckets;
  var filters = null;
  var method;

  if (Number.isFinite(+args[2])) {
    numBuckets = +args[2];

    if (isResult(args[3])) {
      filters = args[3];
      method = null;
      if (filters.getLength() !== numBuckets) {
        return Promise.reject(
          new TurboCartoError(
              'invalid ramp length, got ' + filters.getLength() + ' values, expected ' + numBuckets
          )
        );
      }
    } else {
      filters = null;
      method = args[3];
    }
  } else if (isResult(args[2])) {
    filters = args[2];
    numBuckets = filters.getLength();
    method = null;
  } else {
    filters = null;
    numBuckets = 5;
    method = args[2];
  }

  var values = new ValuesResult([min, max], numBuckets, linearBuckets, Number.POSITIVE_INFINITY);

  if (filters === null) {
    // normalize method
    method = (method || 'quantiles').toLowerCase();
    return getRamp(datasource, column, numBuckets, method).then(compatibilityCreateRampFn(values));
  }

  filters = filters.is(FiltersResult) ? filters : new FiltersResult(filters.get(), 'max');
  return Promise.resolve(filters).then(compatibilityCreateRampFn(values));
}

function getRamp (datasource, column, buckets, method) {
  return new Promise(function (resolve, reject) {
    datasource.getRamp(columnName(column), buckets, method, function (err, filters) {
      if (err) {
        return reject(
          new TurboCartoError('unable to compute ramp,', err)
        );
      }
      var strategy = 'max';
      if (!Array.isArray(filters)) {
        strategy = filters.strategy || 'max';
        filters = filters.ramp;
      }
      resolve(new FiltersResult(filters, strategy));
    });
  });
}

function compatibilityCreateRampFn (valuesResult) {
  return function prepareRamp (filtersResult) {
    var buckets = Math.min(valuesResult.getLength(), filtersResult.getLength());

    var i;
    var rampResult = [];

    var filters = filtersResult.get();
    var values = valuesResult.get();

    if (buckets > 0) {
      for (i = 0; i < buckets; i++) {
        rampResult.push(filters[i]);
        rampResult.push(values[i]);
      }
    } else {
      rampResult.push(null, values[0]);
    }

    return { ramp: rampResult, strategy: filtersResult.getStrategy() };
  };
}

function createRampFn (valuesResult) {
  return function prepareRamp (filtersResult) {
    var buckets = Math.min(valuesResult.getMaxSize(), filtersResult.getMaxSize());

    var i;
    var rampResult = [];

    var filters = filtersResult.get();
    var values = valuesResult.get(buckets);

    if (buckets > 0) {
      for (i = 0; i < buckets; i++) {
        rampResult.push(filters[i]);
        rampResult.push(values[i]);
      }
    } else {
      rampResult.push(null, values[0]);
    }

    return { ramp: rampResult, strategy: filtersResult.getStrategy() };
  };
}

module.exports.fnName = 'ramp';
<|endoftext|>


<commit_before>/*global WebFont: false */
angular.module('schemaFormFontpicker').directive('fontPicker',  ['$q', '$interpolate', function($q, $interpolate) {
  'use strict';
  return {
    restrict: 'AE',
    scope: true,
    require: 'ngModel',
    templateUrl: 'directives/decorators/bootstrap/fontpicker/fontpickerdirective.html',
    link: function(scope, element, attrs, ngModel) {

      var fonturl = 'http://fonts.googleapis.com/css?family=';
      var modelValues = {};
      scope.Math = window.Math;
      scope.angular = angular;
      scope.modelValues = modelValues;
      scope.activePane = 'preset';
      scope.dropdown = false;
      scope.translations = {};
      scope.previewExpanded = true;
      scope.custom = {
        input: '',
        loading: false
      };

      // Take in the options once, then start evaluating them.
      var once = scope.$watch(attrs.fontPicker, function(form) {
        if (form !== undefined) {
          scope.fontlist = form.fontlist;
          scope.preview = form.preview;
          scope.strings = form.strings;
          scope.paneButtons = form.paneButtons;

          /* Setting defaults for form options */
          if (!scope.preview) { scope.preview = {}; }

          if (!scope.preview.sizes) { scope.preview.sizes = [12, 18, 24, 48]; }
          if (!scope.preview.defaultSize) { scope.preview.defaultSize = 18; }
          if (scope.preview.showTextAndSizeInput === undefined) {
            scope.preview.showTextAndSizeInput = true;
          }
          if (!scope.preview.text) {
            scope.preview.text = 'The quick brown fox jumps over the lazy dog';
          }
          if (!scope.preview.styles) {
            scope.preview.styles = [
              {},
              {'font-weight': 'bold'},
              {'font-style': 'italic'}
            ];
          }

          if (!scope.strings) { scope.strings = {}; }
          if (!scope.strings.loadButton) { scope.strings.loadButton = 'Load'; }
          if (!scope.strings.dropdown) { scope.strings.dropdown = 'Choose font'; }
          if (!scope.strings.loadError) { scope.strings.loadError = '{{fontName}} could not be loaded.'; }
          if (!scope.strings.customFontDesc) {
            scope.strings.customFontDesc = 'You can load any font from Google Fonts. Simply open any font by pressing the "Quick Use' +
            ' (right arrow)" button in the list. Make any alterations you wish in steps 1 and 2,' +
            ' then copy the "Standard" link from step 3. When you hit load the font is loaded and' +
            ' can be previewed. When you need to use the font, check step 4.';
          }

          if (!scope.paneButtons) { scope.paneButtons = {}; }
          if (!scope.paneButtons.preset) { scope.paneButtons.preset = {}; }
          if (!scope.paneButtons.custom) { scope.paneButtons.custom = {}; }
          if (!scope.paneButtons.preset.text) { scope.paneButtons.preset.text = 'Preset fonts'; }
          if (!scope.paneButtons.custom.text) { scope.paneButtons.custom.text = 'Load font'; }
          if (scope.paneButtons.preset.show === undefined) { scope.paneButtons.preset.show = true; }
          if (scope.paneButtons.custom.show === undefined) { scope.paneButtons.custom.show = true; }

          if (!scope.fontlist) {
            scope.fontlist = [
              {name: 'Old Standard TT',      url: fonturl + 'Old+Standard+TT:400,400italic,700'},
              {name: 'Josefin Slab',         url: fonturl + 'Josefin+Slab:300,400,700'},
              {name: 'Arvo',                 url: fonturl + 'Arvo:400,700'},
              {name: 'Vollkorn',             url: fonturl + 'Vollkorn:400,700'},
              {name: 'Abril Fatface',        url: fonturl + 'Abril+Fatface'},
              {name: 'Playfair Display SC',  url: fonturl + 'Playfair+Display+SC'},
              {name: 'Open Sans',            url: fonturl + 'Open+Sans:400,700,300,800'},
              {name: 'Lato',                 url: fonturl + 'Lato:100,300,400,700,900,100italic,300italic,400italic,700italic,900italic'},
              {name: 'Ubuntu',               url: fonturl + 'Ubuntu:300,400,500,700'},
              {name: 'PT Sans',              url: fonturl + 'PT+Sans:400,700'},
              {name: 'Droid Sans',           url: fonturl + 'Droid+Sans:400,700'},
              {name: 'Roboto',               url: fonturl + 'Roboto:400,300,700,900'},
              {name: 'Londrina Solid',       url: fonturl + 'Londrina+Solid'},
              {name: 'Indie Flower',         url: fonturl + 'Indie+Flower'},
              {name: 'Nothing You Could Do', url: fonturl + 'Nothing+You+Could+Do'},
              {name: 'Dancing Script',       url: fonturl + 'Dancing+Script'},
              {name: 'Satisfy',              url: fonturl + 'Satisfy'},
              {name: 'Calligraffitti',       url: fonturl + 'Calligraffitti'}
            ];
          }

          // Load initial fonts for preset list.
          loadFonts(scope.fontlist, 'preset');
          ngModel.$render();

          // Set up load error message
          scope.loadError = $interpolate(scope.strings.loadError);

          once();
        }

      });

      /* Genereal functions */
      // Take care of initial model value
      ngModel.$render = function() {
        var presetFont = false;
        if (scope.fontlist === undefined || ngModel.$viewValue  === undefined) { return false; }

        angular.forEach(scope.fontlist, function(font) {
          if (ngModel.$viewValue.url === font.url) { presetFont = true; }
        });

        if (presetFont) {
          scope.activePane = 'preset';
          scope.chooseFont(ngModel.$viewValue);
        }
        else {
          scope.activePane = 'custom';
          scope.loadCustomFont(ngModel.$viewValue);
          scope.custom.input = ngModel.$viewValue.url;
        }
      };

      // Choosing a font via select box.
      scope.chooseFont = function(fontObj) {
        ngModel.$setViewValue({name: fontObj.name, url: fontObj.url});
        modelValues.preset = fontObj;
        modelValues.current = modelValues.preset;
        scope.dropdown = false;
      };

      // Can be used in 2 ways, one is sending in a font object, for example
      // when loaded from model. The other way is without an argument, that
      // is when it's called from the template, this should read the value from the input.
      // also clear errors.
      scope.loadCustomFont = function(fontObj) {
        if (fontObj === undefined) { fontObj = parseCustomFont(scope.custom.input); }
        if (!(fontObj && fontObj.url)) { return false; }
        modelValues.current = modelValues.custom;
        scope.custom.loading = true;
        loadFonts([fontObj], 'custom').then(function() {
          ngModel.$setViewValue({name: fontObj.name, url: fontObj.url});
          modelValues.custom = fontObj;
          scope.custom.loading = false;
          modelValues.current = modelValues.custom;
        }).catch(function() {
          ngModel.$setViewValue();
          scope.modelValues.custom = {};
          scope.custom.loading = false;
        });
      };

      // For the button group.
      scope.switchPane = function(pane) {
        ngModel.$setViewValue();
        scope.activePane = pane;
        modelValues.current = modelValues[pane];
        if (pane === 'preset') {
          if (modelValues.preset !== undefined) { scope.chooseFont(modelValues.preset); }
        }
        else if (pane === 'custom') {
          if (modelValues.custom !== undefined) { scope.loadCustomFont(modelValues.custom); }
        }
      };

      scope.togglePreview = function() {
        scope.previewExpanded  = !scope.previewExpanded;
      };

      // Takes an import/link/url from google fonts and parses the name and url.
      function parseCustomFont(font) {
        if (/^(?:<link|@import|http)/.test(font)) {
          var fontObj = {};
          var match = /^(?:<link href='|@import url\()*([^')]+)+/.exec(font);
          fontObj.url = match[1];
          var nameRaw = /.*ily=([^:]+)+/.exec(fontObj.url);
          if (nameRaw === null) { scope.failedFont = font; return false; }
          fontObj.name = nameRaw[1].replace(/\+/g, ' ');
          return fontObj;
        }

        scope.failedFont = font;
        return false;
      }

      // Load font via web font loader
      function loadFonts(fonts, type) {
        var names = [];
        angular.forEach(fonts, function(font) {
          var nameRaw = font.url.match(/.*ily=(.+)$/);
          var name = nameRaw[1].replace(/\+/g, ' ');
          names.push(name);
        });

        return $q(function(resolve, reject) {
          scope.failedFont = '';
          WebFont.load({
            google: {
              families: names,
            },
            active: resolve,
            inactive: reject,
            fontinactive: function(familyName) {
              // I would prefer to handle this with a rejection but inactive is strangly called
              // before this method. Preset should always be correct, hence harsh error.
              if (type === 'preset') {
                throw 'Failed to load font: ' + familyName;
                //return false;
              }
              // And for custom font loading, that's on the user, so actually show the user.
              scope.failedFont = familyName;
              scope.$apply();
            },
            timeout: 3000
          });
        });
      }
    }
  };
}]);
<commit_msg>Prevent infinite digest.<commit_after>/*global WebFont: false */
angular.module('schemaFormFontpicker').directive('fontPicker',  ['$q', '$interpolate', function($q, $interpolate) {
  'use strict';
  return {
    restrict: 'AE',
    scope: true,
    require: 'ngModel',
    templateUrl: 'directives/decorators/bootstrap/fontpicker/fontpickerdirective.html',
    link: function(scope, element, attrs, ngModel) {

      var fonturl = 'http://fonts.googleapis.com/css?family=';
      var modelValues = {};
      scope.Math = window.Math;
      scope.angular = angular;
      scope.modelValues = modelValues;
      scope.activePane = 'preset';
      scope.dropdown = false;
      scope.translations = {};
      scope.previewExpanded = true;
      scope.custom = {
        input: '',
        loading: false
      };

      // Take in the options once, then start evaluating them.
      var once = scope.$watch(attrs.fontPicker, function(form) {
        if (form !== undefined) {
          scope.fontlist = form.fontlist;
          scope.preview = form.preview;
          scope.strings = form.strings;
          scope.paneButtons = form.paneButtons;

          /* Setting defaults for form options */
          if (!scope.preview) { scope.preview = {}; }

          if (!scope.preview.sizes) { scope.preview.sizes = [12, 18, 24, 48]; }
          if (!scope.preview.defaultSize) { scope.preview.defaultSize = 18; }
          if (scope.preview.showTextAndSizeInput === undefined) {
            scope.preview.showTextAndSizeInput = true;
          }
          if (!scope.preview.text) {
            scope.preview.text = 'The quick brown fox jumps over the lazy dog';
          }
          if (!scope.preview.styles) {
            scope.preview.styles = [
              {},
              {'font-weight': 'bold'},
              {'font-style': 'italic'}
            ];
          }

          if (!scope.strings) { scope.strings = {}; }
          if (!scope.strings.loadButton) { scope.strings.loadButton = 'Load'; }
          if (!scope.strings.dropdown) { scope.strings.dropdown = 'Choose font'; }
          if (!scope.strings.loadError) { scope.strings.loadError = '{{fontName}} could not be loaded.'; }
          if (!scope.strings.customFontDesc) {
            scope.strings.customFontDesc = 'You can load any font from Google Fonts. Simply open any font by pressing the "Quick Use' +
            ' (right arrow)" button in the list. Make any alterations you wish in steps 1 and 2,' +
            ' then copy the "Standard" link from step 3. When you hit load the font is loaded and' +
            ' can be previewed. When you need to use the font, check step 4.';
          }

          if (!scope.paneButtons) { scope.paneButtons = {}; }
          if (!scope.paneButtons.preset) { scope.paneButtons.preset = {}; }
          if (!scope.paneButtons.custom) { scope.paneButtons.custom = {}; }
          if (!scope.paneButtons.preset.text) { scope.paneButtons.preset.text = 'Preset fonts'; }
          if (!scope.paneButtons.custom.text) { scope.paneButtons.custom.text = 'Load font'; }
          if (scope.paneButtons.preset.show === undefined) { scope.paneButtons.preset.show = true; }
          if (scope.paneButtons.custom.show === undefined) { scope.paneButtons.custom.show = true; }

          if (!scope.fontlist) {
            scope.fontlist = [
              {name: 'Old Standard TT',      url: fonturl + 'Old+Standard+TT:400,400italic,700'},
              {name: 'Josefin Slab',         url: fonturl + 'Josefin+Slab:300,400,700'},
              {name: 'Arvo',                 url: fonturl + 'Arvo:400,700'},
              {name: 'Vollkorn',             url: fonturl + 'Vollkorn:400,700'},
              {name: 'Abril Fatface',        url: fonturl + 'Abril+Fatface'},
              {name: 'Playfair Display SC',  url: fonturl + 'Playfair+Display+SC'},
              {name: 'Open Sans',            url: fonturl + 'Open+Sans:400,700,300,800'},
              {name: 'Lato',                 url: fonturl + 'Lato:100,300,400,700,900,100italic,300italic,400italic,700italic,900italic'},
              {name: 'Ubuntu',               url: fonturl + 'Ubuntu:300,400,500,700'},
              {name: 'PT Sans',              url: fonturl + 'PT+Sans:400,700'},
              {name: 'Droid Sans',           url: fonturl + 'Droid+Sans:400,700'},
              {name: 'Roboto',               url: fonturl + 'Roboto:400,300,700,900'},
              {name: 'Londrina Solid',       url: fonturl + 'Londrina+Solid'},
              {name: 'Indie Flower',         url: fonturl + 'Indie+Flower'},
              {name: 'Nothing You Could Do', url: fonturl + 'Nothing+You+Could+Do'},
              {name: 'Dancing Script',       url: fonturl + 'Dancing+Script'},
              {name: 'Satisfy',              url: fonturl + 'Satisfy'},
              {name: 'Calligraffitti',       url: fonturl + 'Calligraffitti'}
            ];
          }

          // Load initial fonts for preset list.
          loadFonts(scope.fontlist, 'preset');
          ngModel.$render();

          // Set up load error message
          scope.loadError = $interpolate(scope.strings.loadError);

          once();
        }

      });

      /* Genereal functions */
      // Take care of initial model value
      ngModel.$render = function() {
        var presetFont = false;
        if (scope.fontlist === undefined || ngModel.$viewValue  === undefined) { return false; }

        angular.forEach(scope.fontlist, function(font) {
          if (ngModel.$viewValue.url === font.url) { presetFont = true; }
        });

        if (presetFont) {
          scope.activePane = 'preset';
          modelValues.preset = ngModel.$viewValue;
          modelValues.current = modelValues.preset;
          scope.dropdown = false;
        }
        else {
          scope.activePane = 'custom';
          scope.loadCustomFont(ngModel.$viewValue);
          scope.custom.input = ngModel.$viewValue.url;
        }
      };

      // Choosing a font via select box.
      scope.chooseFont = function(fontObj) {
        ngModel.$setViewValue({name: fontObj.name, url: fontObj.url});
        modelValues.preset = fontObj;
        modelValues.current = modelValues.preset;
        scope.dropdown = false;
      };

      // Can be used in 2 ways, one is sending in a font object, for example
      // when loaded from model. The other way is without an argument, that
      // is when it's called from the template, this should read the value from the input.
      // also clear errors.
      scope.loadCustomFont = function(fontObj) {
        if (fontObj === undefined) { fontObj = parseCustomFont(scope.custom.input); }
        if (!(fontObj && fontObj.url)) { return false; }
        modelValues.current = modelValues.custom;
        scope.custom.loading = true;
        loadFonts([fontObj], 'custom').then(function() {
          ngModel.$setViewValue({name: fontObj.name, url: fontObj.url});
          modelValues.custom = fontObj;
          scope.custom.loading = false;
          modelValues.current = modelValues.custom;
        }).catch(function() {
          ngModel.$setViewValue();
          scope.modelValues.custom = {};
          scope.custom.loading = false;
        });
      };

      // For the button group.
      scope.switchPane = function(pane) {
        ngModel.$setViewValue();
        scope.activePane = pane;
        modelValues.current = modelValues[pane];
        if (pane === 'preset') {
          if (modelValues.preset !== undefined) { scope.chooseFont(modelValues.preset); }
        }
        else if (pane === 'custom') {
          if (modelValues.custom !== undefined) { scope.loadCustomFont(modelValues.custom); }
        }
      };

      scope.togglePreview = function() {
        scope.previewExpanded  = !scope.previewExpanded;
      };

      // Takes an import/link/url from google fonts and parses the name and url.
      function parseCustomFont(font) {
        if (/^(?:<link|@import|http)/.test(font)) {
          var fontObj = {};
          var match = /^(?:<link href='|@import url\()*([^')]+)+/.exec(font);
          fontObj.url = match[1];
          var nameRaw = /.*ily=([^:]+)+/.exec(fontObj.url);
          if (nameRaw === null) { scope.failedFont = font; return false; }
          fontObj.name = nameRaw[1].replace(/\+/g, ' ');
          return fontObj;
        }

        scope.failedFont = font;
        return false;
      }

      // Load font via web font loader
      function loadFonts(fonts, type) {
        var names = [];
        angular.forEach(fonts, function(font) {
          var nameRaw = font.url.match(/.*ily=(.+)$/);
          var name = nameRaw[1].replace(/\+/g, ' ');
          names.push(name);
        });

        return $q(function(resolve, reject) {
          scope.failedFont = '';
          WebFont.load({
            google: {
              families: names,
            },
            active: resolve,
            inactive: reject,
            fontinactive: function(familyName) {
              // I would prefer to handle this with a rejection but inactive is strangly called
              // before this method. Preset should always be correct, hence harsh error.
              if (type === 'preset') {
                throw 'Failed to load font: ' + familyName;
                //return false;
              }
              // And for custom font loading, that's on the user, so actually show the user.
              scope.failedFont = familyName;
              scope.$apply();
            },
            timeout: 3000
          });
        });
      }
    }
  };
}]);
<|endoftext|>


<commit_before>'use strict';

const validation = require('../validation');

describe('aggregation validation', () => {
  it('should throw on wrong config object', async () => {
    const err = /^aggregation configuration must be an object$/;
    await expect(() => validation.aggregation()).toThrow(err);
    await expect(() => validation.aggregation(42)).toThrow(err);
    await expect(() => validation.aggregation(null)).toThrow(err);
  });

  it('should throw if collection is not a string', () => {
    return expect(() => validation.aggregation({ collection: 42 })).toThrow(
      /^config\.collection must be a string$/
    );
  });

  it('should throw if sources is not an object', async () => {
    const err = /^config\.sources must be an object$/;
    await expect(() =>
      validation.aggregation({ collection: 'dummy', sources: null })
    ).toThrow(err);
    await expect(() =>
      validation.aggregation({ collection: 'dummy', sources: 42 })
    ).toThrow(err);
  });

  it('should throw if chunkSize is not an integer', async () => {
    const err = /^config\.chunkSize must be a positive integer$/;
    await expect(() =>
      validation.aggregation({
        collection: 'dummy',
        sources: { a: 1 },
        chunkSize: 0.1
      })
    ).toThrow(err);
    await expect(() =>
      validation.aggregation({
        collection: 'dummy',
        sources: { a: 1 },
        chunkSize: -1
      })
    ).toThrow(err);
    await expect(() =>
      validation.aggregation({
        collection: 'dummy',
        sources: { a: 1 },
        chunkSize: 0
      })
    ).toThrow(err);
  });

  it('should throw if sources is an empty object', () => {
    return expect(() =>
      validation.aggregation({ collection: 'dummy', sources: {} })
    ).toThrow(/^config\.sources must have at least one source$/);
  });

  it('should throw if sources properties are not functions', () => {
    return expect(() =>
      validation.aggregation({
        collection: 'dummy',
        sources: {
          source1: 'abc'
        }
      })
    ).toThrow(
      'all sources in the aggregation config should be functions (source1)'
    );
  });
});
<commit_msg>add test: aggregation config validation should return new object<commit_after>'use strict';

const validation = require('../validation');

describe('aggregation validation', () => {
  it('should throw on wrong config object', async () => {
    const err = /^aggregation configuration must be an object$/;
    await expect(() => validation.aggregation()).toThrow(err);
    await expect(() => validation.aggregation(42)).toThrow(err);
    await expect(() => validation.aggregation(null)).toThrow(err);
  });

  it('should throw if collection is not a string', () => {
    return expect(() => validation.aggregation({ collection: 42 })).toThrow(
      /^config\.collection must be a string$/
    );
  });

  it('should throw if sources is not an object', async () => {
    const err = /^config\.sources must be an object$/;
    await expect(() =>
      validation.aggregation({ collection: 'dummy', sources: null })
    ).toThrow(err);
    await expect(() =>
      validation.aggregation({ collection: 'dummy', sources: 42 })
    ).toThrow(err);
  });

  it('should throw if chunkSize is not an integer', async () => {
    const err = /^config\.chunkSize must be a positive integer$/;
    await expect(() =>
      validation.aggregation({
        collection: 'dummy',
        sources: { a: 1 },
        chunkSize: 0.1
      })
    ).toThrow(err);
    await expect(() =>
      validation.aggregation({
        collection: 'dummy',
        sources: { a: 1 },
        chunkSize: -1
      })
    ).toThrow(err);
    await expect(() =>
      validation.aggregation({
        collection: 'dummy',
        sources: { a: 1 },
        chunkSize: 0
      })
    ).toThrow(err);
  });

  it('should throw if sources is an empty object', () => {
    return expect(() =>
      validation.aggregation({ collection: 'dummy', sources: {} })
    ).toThrow(/^config\.sources must have at least one source$/);
  });

  it('should throw if sources properties are not functions', () => {
    return expect(() =>
      validation.aggregation({
        collection: 'dummy',
        sources: {
          source1: 'abc'
        }
      })
    ).toThrow(
      'all sources in the aggregation config should be functions (source1)'
    );
  });

  it('should return a new object', () => {
    const aggConfig = {
      collection: 'dummy',
      sources: {
        source1: () => {
          // noop
        }
      }
    };
    const validatedConfig = validation.aggregation(aggConfig);
    expect(validatedConfig).toBeDefined();
    return expect(validatedConfig).not.toStrictEqual(aggConfig);
  });
});
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { api } from '../../data'
import { Provider, Text, Small } from 'rebass'
import Button from '../components/Button'
import theme from '../theme'
import LoadingAnimation from '../components/LoadingAnimation'
import { LoginButton, LogoutButton } from '../components/AuthButton'
import fetch from 'unfetch'
import { withRouter, Link } from 'react-static'

const AuthButtons = props => {
  if (props.needsToAuth) {
    return (

        <Text>There's something wrong with your session. Please try logging in again.</Text>
        <LoginButton />

    )
  } else {
    return (<LogoutButton />)
  }
}

const ListItem = props => (
  <Button is={Link} {...props} />
)

const timeSince = time => {
  const seconds = Math.floor((new Date() - new Date(time)) / 1000);
  const intervals = [
    [Math.floor(seconds / (60 * 60 * 24 * 7)), 'weeks'],
    [Math.floor(seconds / (60 * 60 * 24)), 'days'],
    [Math.floor(seconds / (60 * 60)), 'hours'],
    [Math.floor(seconds / 60), 'minutes']
  ]
  for (var i = 0; i < intervals.length; i++) {
    let interval = intervals[i]
    if (interval[0] > 1) {
      return interval.join(' ')
    }
  }
  return 'less than a minute'
}

const ApplicationCard = props => {
  return (
    <ListItem to={`/apply/club?id=${props.app.id}`}>
      <Text>{props.app.high_school_name || "Untitled Application"}</Text>
      Last edited {timeSince(props.app.updated_at)} ago
    </ListItem>
  )
}

const ApplicationListing = props => (

    {props.apps.map((app, index) => (
      <ApplicationCard app={app} key={index} />
    ))}

)

const NewApplicationButton = withRouter(props => {
  const { applicantId, authToken } = props
  const handleClick = (e) => {
    e.preventDefault()
    fetch(`${api}/v1/applicants/${applicantId}/club_applications`, {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${authToken}` }
    })
     .then(res => {
       if (res.ok) {
         return res.json()
       } else {
         throw res
       }
     })
     .then(json => {
       console.log(json)
       props.history.push({pathname: '/apply/club', search: `?id=${json.id}`})
     })
     .catch(e => {
       console.error(e)
     })
  }

  return <ListItem onClick={handleClick} to='/apply/club'>Start a new application</ListItem>
})

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

    this.state = {
      status: 'loading',
      authToken: null,
      applicantId: null
    }
  }

  componentDidMount() {
    const authToken = window.localStorage.getItem('authToken')
    const applicantId = window.localStorage.getItem('applicantId')

    // Ensure user is authenticated
    if (authToken === null || applicantId === null) {
      this.props.history.push('/apply/login')
    }

    this.setState({authToken, applicantId})
    console.log(this.state)

    // Populate the list of applications
    fetch(`${api}/v1/applicants/${applicantId}/club_applications`, {
      headers: { 'Authorization': `Bearer ${authToken}` }
    })
      .then(res => {
        if (res.ok) {
          return res.json()
        } else {
          throw res
        }
      })
      .then(json => {
        this.apps = json
        console.log(json)
        this.setState({
          status: 'finshed'
        })
      })
      .catch(e => {
        console.error(e)
        if (e.status === 401) {
          const status = 'needsToAuth'
          this.setState({status})
        }
      })
  }

  render() {
    const { status, authToken, applicantId } = this.state

    if (status === 'loading') {
      return (
        <Provider theme={theme}>
          <LoadingAnimation />
        </Provider>
      )
    } else {
      return (
        <Provider theme={theme}>
          <AuthButtons needsToAuth={status === 'needsToAuth'} />
          <NewApplicationButton authToken={authToken} applicantId={applicantId} />
          <ApplicationListing apps={this.apps} />
        </Provider>
      )
    }
  }
}

export default ApplicationIndex
<commit_msg>"Edit Leader Profile" buttons are listed alongside club applications<commit_after>import React, { Component } from 'react'
import { api } from '../../data'
import { Box, Provider, Text, Small } from 'rebass'
import Button from '../components/Button'
import theme from '../theme'
import LoadingAnimation from '../components/LoadingAnimation'
import { LoginButton, LogoutButton } from '../components/AuthButton'
import fetch from 'unfetch'
import { withRouter, Link } from 'react-static'

const AuthButtons = props => {
  if (props.needsToAuth) {
    return (

        <Text>There's something wrong with your session. Please try logging in again.</Text>
        <LoginButton />

    )
  } else {
    return (<LogoutButton />)
  }
}

const ListItem = props => (
  <Button {...props} is={Link} children={<Text>{props.children}</Text>} />
)

const timeSince = time => {
  const seconds = Math.floor((new Date() - new Date(time)) / 1000);
  const intervals = [
    [Math.floor(seconds / (60 * 60 * 24 * 7)), 'weeks'],
    [Math.floor(seconds / (60 * 60 * 24)), 'days'],
    [Math.floor(seconds / (60 * 60)), 'hours'],
    [Math.floor(seconds / 60), 'minutes']
  ]
  for (var i = 0; i < intervals.length; i++) {
    let interval = intervals[i]
    if (interval[0] > 1) {
      return interval.join(' ')
    }
  }
  return 'less than a minute'
}

const ApplicationCard = props => {
  const leaderProfile = props.app.applicant_profiles.find(profile => (
    profile.applicant.id == props.applicantId
  ))

  return (
    <Box>
      <Text>{props.app.high_school_name || "Untitled Application"}</Text>
      <ListItem to={`/apply/club?id=${props.app.id}`}>
        Edit Club Application
      </ListItem>

      <ListItem to={`/apply/leader?id=${leaderProfile.id}`}>
        Edit Leader Profile
      </ListItem>
      Last edited {timeSince(props.app.updated_at)} ago
    </Box>
  )
}

const ApplicationListing = props => (

    {props.apps.map((app, index) => (
      <ApplicationCard key={index} app={app} {...props} />
    ))}

)

const NewApplicationButton = withRouter(props => {
  const { applicantId, authToken } = props
  const handleClick = (e) => {
    e.preventDefault()
    fetch(`${api}/v1/applicants/${applicantId}/club_applications`, {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${authToken}` }
    })
     .then(res => {
       if (res.ok) {
         return res.json()
       } else {
         throw res
       }
     })
     .then(json => {
       console.log(json)
       props.history.push({pathname: '/apply/club', search: `?id=${json.id}`})
     })
     .catch(e => {
       console.error(e)
     })
  }

  return <ListItem onClick={handleClick} to='/apply/club'>Start a new application</ListItem>
})

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

    this.state = {
      status: 'loading',
      authToken: null,
      applicantId: null
    }
  }

  componentDidMount() {
    const authToken = window.localStorage.getItem('authToken')
    const applicantId = window.localStorage.getItem('applicantId')

    // Ensure user is authenticated
    if (authToken === null || applicantId === null) {
      this.props.history.push('/apply/login')
    }

    this.setState({authToken, applicantId})
    console.log(this.state)

    // Populate the list of applications
    fetch(`${api}/v1/applicants/${applicantId}/club_applications`, {
      headers: { 'Authorization': `Bearer ${authToken}` }
    })
      .then(res => {
        if (res.ok) {
          return res.json()
        } else {
          throw res
        }
      })
      .then(json => {
        this.apps = json
        console.log(json)
        this.setState({
          status: 'finshed'
        })
      })
      .catch(e => {
        console.error(e)
        if (e.status === 401) {
          const status = 'needsToAuth'
          this.setState({status})
        }
      })
  }

  render() {
    const { status, authToken, applicantId } = this.state

    if (status === 'loading') {
      return (
        <Provider theme={theme}>
          <LoadingAnimation />
        </Provider>
      )
    } else {
      return (
        <Provider theme={theme}>
          <AuthButtons needsToAuth={status === 'needsToAuth'} />
          <NewApplicationButton authToken={authToken} applicantId={applicantId} />
          <ApplicationListing apps={this.apps} applicantId={applicantId} />
        </Provider>
      )
    }
  }
}

export default ApplicationIndex
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import {
  ScrollView,
  StyleSheet,
  Text,
  View
} from 'react-native';
import { List, ListItem } from 'react-native-elements';
import moment from 'moment';
import _ from 'lodash';
import ReportPieChart from '../components/Charts/ReportPieChart';

import colors from '../themes/Colors';
import metrics from '../themes/Metrics';

const styles = StyleSheet.create({
  list: {
    marginTop: 0
  },
  headerContainer: {
    height: metrics.navBarHeight,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: colors.main,
    elevation: 3,
  },
  header: {
    fontSize: 18,
    color: 'white',
    fontWeight: 'bold'
  }
});

class ReportsContainer extends Component {
  render() {
    const {
      currentReport,
      pastReports
    } = this.props;

    return (
      <ScrollView >
        <ReportPieChart currentReport={currentReport} />
        <View style={styles.headerContainer}>
          <Text style={styles.header}>Past Reports</Text>
        </View>
        <List containerStyle={styles.list}>
          {
            pastReports.map((report, i) => {
              let leftIcon;
              if (report.result === 0) {
                leftIcon = {
                  name: 'trending-flat',
                  color: 'orange'
                };
              } else if (report.result < 0) {
                leftIcon = {
                  name: 'trending-down',
                  color: 'red'
                };
              } else {
                leftIcon = {
                  name: 'trending-up',
                  color: 'green'
                };
              }

              return (
                <ListItem
                  key={i}
                  leftIcon={leftIcon}
                  onPress={() => alert('clicked')}
                  subtitle={
                    <View>
                      <Text>Expenses: {report.expenseSum}</Text>
                      <Text>Incomes: {report.incomeSum}</Text>
                    </View>
                  }
                  title={`${report.date.format('YYYY MMMM')} (${report.result}$)`}
                  />
              );
            })
          }
        </List>
      </ScrollView>
    );
  }
}

ReportsContainer.propTypes = {
  pastReports: PropTypes.array,
  currentReport: PropTypes.object,
};

function getCurrentReport(ebudgie, from, to, salary) {
  const incomes = _.filter(ebudgie.incomes, (income) => {
    return moment(from) < moment(income.date) && moment(income.date) < moment(to);
  });

  const expenses = _.filter(ebudgie.expenses, (expense) => {
    return moment(from) < moment(expense.date) && moment(expense.date) < moment(to);
  });

  const incomeSum = _.sumBy(incomes, 'value');
  const expenseSum = _.sumBy(expenses, 'value');

  const report = {
    incomeSum,
    expenseSum,
    result: salary.value + incomeSum - expenseSum,
    date: from,
  };

  return report;
}

function getPastReports(ebudgie, salaries) {
  const now = moment();
  let firstIncome = now;

  for (let i = 0; i < ebudgie.incomes.length; i++) {
    firstIncome = moment.min(firstIncome, moment(ebudgie.incomes[i].date));
  }

  let firstExpense = moment();

  for (let i = 0; i < ebudgie.expenses.length; i++) {
    firstExpense = moment.min(firstExpense, moment(ebudgie.expenses[i].date));
  }

  let current = moment.min(firstExpense, firstIncome);

  const reports = [];

  while (current < now.startOf('month')) {
    const start = moment(current).startOf('month');
    const end = moment(current).endOf('month');
    let salary = _.findLast(salaries, (s) => {
      return start < moment(s.date) && moment(s.date) < end;
    });

    salary = _.first(salaries) || 0;

    reports.push(getCurrentReport(
      ebudgie,
      start,
      end,
      salary));

    current = current.add(1, 'months');
  }

  return reports;
}

function mapStateToProps(state) {
  const { salaries } = state.ebudgie;
  const currentSalary = salaries[salaries.length - 1] || {};

  return {
    currentReport: getCurrentReport(
      state.ebudgie,
      moment().startOf('month'),
      moment().endOf('month'),
      currentSalary),
    pastReports: getPastReports(state.ebudgie, salaries),
    currentSalary
  };
}

export default connect(
  mapStateToProps
)(ReportsContainer);
<commit_msg>Add message when user doesn't have past reports<commit_after>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import {
  ScrollView,
  StyleSheet,
  Text,
  View
} from 'react-native';
import { List, ListItem } from 'react-native-elements';
import moment from 'moment';
import _ from 'lodash';
import ReportPieChart from '../components/Charts/ReportPieChart';

import colors from '../themes/Colors';
import metrics from '../themes/Metrics';

const styles = StyleSheet.create({
  list: {
    marginTop: 0
  },
  headerContainer: {
    height: metrics.navBarHeight,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: colors.main,
    elevation: 3,
  },
  header: {
    fontSize: 18,
    color: 'white',
    fontWeight: 'bold'
  },
  noReports: {
    padding: 20,
    textAlign: 'center',
    fontSize: 18,
    fontWeight: 'bold'
  }
});

class ReportsContainer extends Component {
  render() {
    const {
      currentReport,
      pastReports
    } = this.props;

    const listItems = pastReports.map((report, i) => {
      let leftIcon;
      if (report.result === 0) {
        leftIcon = {
          name: 'trending-flat',
          color: 'orange'
        };
      } else if (report.result < 0) {
        leftIcon = {
          name: 'trending-down',
          color: 'red'
        };
      } else {
        leftIcon = {
          name: 'trending-up',
          color: 'green'
        };
      }

      return (
        <ListItem
          key={i}
          leftIcon={leftIcon}
          onPress={() => alert('clicked')}
          subtitle={
            <View>
              <Text>Expenses: {report.expenseSum}</Text>
              <Text>Incomes: {report.incomeSum}</Text>
            </View>
          }
          title={`${report.date.format('YYYY MMMM')} (${report.result}$)`}
          />
      );
    });

    return (
      <ScrollView >
        <ReportPieChart currentReport={currentReport} />
        <View style={styles.headerContainer}>
          <Text style={styles.header}>Past Reports</Text>
        </View>
        {
          listItems.length !== 0 ?
            <List containerStyle={styles.list}>
              {listItems}
            </List>
            : <Text style={styles.noReports}> No past reports</Text>
        }
      </ScrollView>
    );
  }
}

ReportsContainer.propTypes = {
  pastReports: PropTypes.array,
  currentReport: PropTypes.object,
};

function getCurrentReport(ebudgie, from, to, salary) {
  const incomes = _.filter(ebudgie.incomes, (income) => {
    return moment(from) < moment(income.date) && moment(income.date) < moment(to);
  });

  const expenses = _.filter(ebudgie.expenses, (expense) => {
    return moment(from) < moment(expense.date) && moment(expense.date) < moment(to);
  });

  const incomeSum = _.sumBy(incomes, 'value');
  const expenseSum = _.sumBy(expenses, 'value');

  const report = {
    incomeSum,
    expenseSum,
    result: salary.value + incomeSum - expenseSum,
    date: from,
  };

  return report;
}

function getPastReports(ebudgie, salaries) {
  const now = moment();
  let firstIncome = now;

  for (let i = 0; i < ebudgie.incomes.length; i++) {
    firstIncome = moment.min(firstIncome, moment(ebudgie.incomes[i].date));
  }

  let firstExpense = moment();

  for (let i = 0; i < ebudgie.expenses.length; i++) {
    firstExpense = moment.min(firstExpense, moment(ebudgie.expenses[i].date));
  }

  let current = moment.min(firstExpense, firstIncome);

  const reports = [];

  while (current < now.startOf('month')) {
    const start = moment(current).startOf('month');
    const end = moment(current).endOf('month');
    let salary = _.findLast(salaries, (s) => {
      return start < moment(s.date) && moment(s.date) < end;
    });

    salary = _.first(salaries) || 0;

    reports.push(getCurrentReport(
      ebudgie,
      start,
      end,
      salary));

    current = current.add(1, 'months');
  }

  return reports;
}

function mapStateToProps(state) {
  const { salaries } = state.ebudgie;
  const currentSalary = salaries[salaries.length - 1] || {};

  return {
    currentReport: getCurrentReport(
      state.ebudgie,
      moment().startOf('month'),
      moment().endOf('month'),
      currentSalary),
    pastReports: getPastReports(state.ebudgie, salaries),
    currentSalary
  };
}

export default connect(
  mapStateToProps
)(ReportsContainer);
<|endoftext|>