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

var chai = require("chai");
var expect = chai.expect;
var fs = require('fs');
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
var Client = require("../../.").Client;
var Controller = require('./../RC');
var Config = require('../..').Config;
var HzErrors = require('../..').HazelcastErrors;
var Promise = require('bluebird');
var markEnterprise = require('../Util').markEnterprise;
var Path = require('path');

describe('SSL Client Authentication Test', function () {
    var cluster;
    var member;

    var maRequiredXML = __dirname + '/hazelcast-ma-required.xml';
    var maOptionalXML = __dirname + '/hazelcast-ma-optional.xml';

    before(function () {
        markEnterprise(this);
    });

    function createMemberWithXML(serverXML) {
        return Controller.createCluster(null, fs.readFileSync(serverXML, 'utf8')).then(function (cl) {
            cluster = cl;
            return Controller.startMember(cluster.id);
        }).then(function (m) {
            member = m;
        });
    }

    function createClientConfigWithSSLOptsUsingProgrammaticConfiguration(key, cert, ca) {
        var sslOpts = {
            servername: 'foo.bar.com',
            rejectUnauthorized: true,
            ca: fs.readFileSync(Path.join(__dirname, ca)),
            key: fs.readFileSync(Path.join(__dirname, key)),
            cert: fs.readFileSync(Path.join(__dirname, cert))
        };
        var cfg = new Config.ClientConfig();
        cfg.networkConfig.sslOptions = sslOpts;
        cfg.networkConfig.connectionAttemptLimit = 1;
        cfg.networkConfig.connectionTimeout = 1000;
        return cfg;
    }

    function createClientConfigWithSSLOptsUsingBasicSSLOptionsFactory(key, cert, ca) {
        var cfg = new Config.ClientConfig();
        cfg.networkConfig.sslOptionsFactoryConfig = {
            exportedName: 'BasicSSLOptionsFactory'
        };
        cfg.networkConfig.sslOptionsFactoryProperties = {
            caPath: Path.resolve(__dirname, ca),
            keyPath: Path.resolve(__dirname, key),
            certPath: Path.resolve(__dirname, cert),
            rejectUnauthorized: true,
            servername: 'foo.bar.com'
        };
        cfg.networkConfig.connectionAttemptLimit = 1;
        return cfg;
    }

    [false, true].forEach(function (value) {
        if (value) {
            var createClientConfigWithSSLOpts = createClientConfigWithSSLOptsUsingBasicSSLOptionsFactory;
            var title = 'via BasicSSLOptionsFactory';
        } else {
            var createClientConfigWithSSLOpts = createClientConfigWithSSLOptsUsingProgrammaticConfiguration;
            var title = 'via programmatic configuration';
        }

        describe(title, function () {

            afterEach(function () {
                return Controller.terminateCluster(cluster.id);
            });

            it('ma:required, they both know each other should connect', function () {
                return createMemberWithXML(maRequiredXML).then(function () {
                    return Client.newHazelcastClient(createClientConfigWithSSLOpts('./client1-key.pem', './client1-cert.pem', './server1-cert.pem'));
                }).then(function (client) {
                    client.shutdown();
                });
            });

            it('ma:required, server knows client, client does not know server should fail', function () {
                return createMemberWithXML(maRequiredXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client1-key.pem', './client1-cert.pem', './server2-cert.pem')))
                        .to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:required, server does not know client, client knows server should fail', function () {
                return createMemberWithXML(maRequiredXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client2-key.pem', './client2-cert.pem',
                        './server1-cert.pem'))).to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:required, neither one knows the other should fail', function () {
                return createMemberWithXML(maRequiredXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client2-key.pem', './client2-cert.pem',
                        './server2-cert.pem'))).to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:optional, they both know each other should connect', function () {
                return createMemberWithXML(maOptionalXML).then(function () {
                    return Client.newHazelcastClient(createClientConfigWithSSLOpts('./client1-key.pem', './client1-cert.pem', './server1-cert.pem'));
                }).then(function (client) {
                    client.shutdown();
                });
            });

            it('ma:optional, server knows client, client does not know server should fail', function () {
                return createMemberWithXML(maOptionalXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client1-key.pem', './client1-cert.pem', './server2-cert.pem')))
                        .to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:optional, server does not know client, client knows server should fail', function () {
                return createMemberWithXML(maOptionalXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client2-key.pem', './client2-cert.pem', './server1-cert.pem')))
                        .to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:optional, neither knows the other should fail', function () {
                return createMemberWithXML(maOptionalXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client2-key.pem', './client2-cert.pem', './server2-cert.pem')))
                        .to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });
        });
    });

});
<commit_msg>don't run ssl test under server v3.8.1 (#321)<commit_after>/*
 * Copyright (c) 2008-2018, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var chai = require("chai");
var expect = chai.expect;
var fs = require('fs');
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
var Client = require("../../.").Client;
var Controller = require('./../RC');
var Config = require('../..').Config;
var HzErrors = require('../..').HazelcastErrors;
var markEnterprise = require('../Util').markEnterprise;
var Path = require('path');
var Util = require('../Util');

describe('SSL Client Authentication Test', function () {
    var cluster;
    var member;

    var maRequiredXML = __dirname + '/hazelcast-ma-required.xml';
    var maOptionalXML = __dirname + '/hazelcast-ma-optional.xml';

    before(function () {
        markEnterprise(this);
    });

    function createMemberWithXML(serverXML) {
        return Controller.createCluster(null, fs.readFileSync(serverXML, 'utf8')).then(function (cl) {
            cluster = cl;
            return Controller.startMember(cluster.id);
        }).then(function (m) {
            member = m;
        });
    }

    function createClientConfigWithSSLOptsUsingProgrammaticConfiguration(key, cert, ca) {
        var sslOpts = {
            servername: 'foo.bar.com',
            rejectUnauthorized: true,
            ca: fs.readFileSync(Path.join(__dirname, ca)),
            key: fs.readFileSync(Path.join(__dirname, key)),
            cert: fs.readFileSync(Path.join(__dirname, cert))
        };
        var cfg = new Config.ClientConfig();
        cfg.networkConfig.sslOptions = sslOpts;
        cfg.networkConfig.connectionAttemptLimit = 1;
        cfg.networkConfig.connectionTimeout = 1000;
        return cfg;
    }

    function createClientConfigWithSSLOptsUsingBasicSSLOptionsFactory(key, cert, ca) {
        var cfg = new Config.ClientConfig();
        cfg.networkConfig.sslOptionsFactoryConfig = {
            exportedName: 'BasicSSLOptionsFactory'
        };
        cfg.networkConfig.sslOptionsFactoryProperties = {
            caPath: Path.resolve(__dirname, ca),
            keyPath: Path.resolve(__dirname, key),
            certPath: Path.resolve(__dirname, cert),
            rejectUnauthorized: true,
            servername: 'foo.bar.com'
        };
        cfg.networkConfig.connectionAttemptLimit = 1;
        return cfg;
    }

    [false, true].forEach(function (value) {
        if (value) {
            var createClientConfigWithSSLOpts = createClientConfigWithSSLOptsUsingBasicSSLOptionsFactory;
            var title = 'via BasicSSLOptionsFactory';
        } else {
            var createClientConfigWithSSLOpts = createClientConfigWithSSLOptsUsingProgrammaticConfiguration;
            var title = 'via programmatic configuration';
        }

        describe(title, function () {

            before(function () {
                Util.markServerVersionAtLeast(this, null, '3.8.1');
            });

            afterEach(function () {
                return Controller.terminateCluster(cluster.id);
            });

            it('ma:required, they both know each other should connect', function () {
                return createMemberWithXML(maRequiredXML).then(function () {
                    return Client.newHazelcastClient(createClientConfigWithSSLOpts('./client1-key.pem', './client1-cert.pem', './server1-cert.pem'));
                }).then(function (client) {
                    client.shutdown();
                });
            });

            it('ma:required, server knows client, client does not know server should fail', function () {
                return createMemberWithXML(maRequiredXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client1-key.pem', './client1-cert.pem', './server2-cert.pem')))
                        .to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:required, server does not know client, client knows server should fail', function () {
                return createMemberWithXML(maRequiredXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client2-key.pem', './client2-cert.pem',
                        './server1-cert.pem'))).to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:required, neither one knows the other should fail', function () {
                return createMemberWithXML(maRequiredXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client2-key.pem', './client2-cert.pem',
                        './server2-cert.pem'))).to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:optional, they both know each other should connect', function () {
                return createMemberWithXML(maOptionalXML).then(function () {
                    return Client.newHazelcastClient(createClientConfigWithSSLOpts('./client1-key.pem', './client1-cert.pem', './server1-cert.pem'));
                }).then(function (client) {
                    client.shutdown();
                });
            });

            it('ma:optional, server knows client, client does not know server should fail', function () {
                return createMemberWithXML(maOptionalXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client1-key.pem', './client1-cert.pem', './server2-cert.pem')))
                        .to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:optional, server does not know client, client knows server should fail', function () {
                return createMemberWithXML(maOptionalXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client2-key.pem', './client2-cert.pem', './server1-cert.pem')))
                        .to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });

            it('ma:optional, neither knows the other should fail', function () {
                return createMemberWithXML(maOptionalXML).then(function () {
                    return expect(Client.newHazelcastClient(createClientConfigWithSSLOpts('./client2-key.pem', './client2-cert.pem', './server2-cert.pem')))
                        .to.be.rejectedWith(HzErrors.IllegalStateError);
                });
            });
        });
    });

});
<|endoftext|>


<commit_before>import {
  DropDownMenu,
  Menu,
  MenuItem,
  Popover,
} from 'material-ui';
import {
  Icon,
  Loader,
  StoryCard,
} from '../../components';

import Clipboard from 'clipboard';
import React from 'react';
import {hashHistory} from 'react-router';
import ls from 'local-storage';
import moment from 'moment';
import radium from 'radium';
import request from 'request';
import styles from './styles';

function sortStoriesByCreatedTime(a, b) {
  return new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime();
}

@radium
export default class ProjectStoriesView extends React.Component {

  state = {
    project_stories_fetched: false,
    project_memberships_fetched: false,
    searchFilter: '',
    storyTypeFilter: 'all',
    ownerFilter: 'all',
    stagesFilter: 'all',
    labelFilters: [],
    showLabelsPopover: false,
    labelsEl: null,
  };

  componentDidMount() {

    const CB = new Clipboard('.storyId');

    CB.on('success', (e) => {
        this.props.setNotification(true, `${e.text} copied to clipboard`);
    });

    const headers = new Headers();
    const projectId = this.props.params.projectId;
    headers.append('X-TrackerToken', ls('pp-api'));
    this.props.setViewTitle(ls(`pp-project-${projectId}-details`).name);
    this.props.setShowBack({
      link: '/projects',
      text: 'Projects',
      clearOnClick: true,
    });
    // TODO paginate requests
    fetch(`https://www.pivotaltracker.com/services/v5/projects/${projectId}/stories?limit=1000`, {
      mode: 'cors',
      headers,
      method: 'GET',
    }).then(res => res.json()).then((res) => {
      ls.set(`pp-project-${projectId}-stories`, res);
      this.setState({project_stories_fetched: true});
    });

    fetch(`https://www.pivotaltracker.com/services/v5/projects/${projectId}/memberships`, {
      mode: 'cors',
      headers,
      method: 'GET',
    }).then(res => res.json()).then((res) => {
      ls.set(`pp-project-${projectId}-memberships`, res);
      this.setState({project_memberships_fetched: true});
    });

    fetch(`https://www.pivotaltracker.com/services/v5/projects/${projectId}/labels`, {
      mode: 'cors',
      headers,
      method: 'GET',
    }).then(res => res.json()).then((res) => {
      ls.set(`pp-project-${projectId}-labels`, res);
      this.setState({project_labels_fetched: true});
    });
  }

  sortActiveLabels = (a, b) => {
    const sortA = this.state.labelFilters.includes(a.id) ? -1 : 1;
    const sortB = this.state.labelFilters.includes(b.id) ? -1 : 1;
    return sortA - sortB;
  }

  filterBySearch = story => {
    const reg = new RegExp(this.state.searchFilter, 'i', 'g');
    return story.name.match(reg);
  }

  filterByType = story => {
    if (this.state.storyTypeFilter === 'all') return true;
    return story.story_type === this.state.storyTypeFilter;
  }

  filterByOwner = story => {
    if (this.state.ownerFilter === 'all') return true;
    return story.owned_by_id === this.state.ownerFilter;
  }

  filterByStage = story => {
    if (this.state.stagesFilter === 'all') return true;
    return story.current_state === this.state.stagesFilter;
  }

  filterByLabels = story => {
    let res = false;

    if (!this.state.labelFilters.length) return true;

    if (!story.labels.length) return false;

    for (let i = 0; i < story.labels.length; i++) {
      if (this.state.labelFilters.includes(story.labels[i].id)) res = true;
    }

    return res;
  }

  toggleLabelsPopover = (el) => {
    if (el) {
      this.setState({
        showLabelsPopover: true,
        labelsEl: el,
      });
    } else {
      this.setState({
        showLabelsPopover: false,
        labelsEl: null,
      });
    }
  }

  handleStoryTypeChange = (e, t, val) => this.setState({ storyTypeFilter: val });

  handleOwnerChange = (e, t, val) => this.setState({ ownerFilter: val });

  handleStagesChange = (e, t, val) => this.setState({ stagesFilter: val });

  handleLabelChange = (val) => {
    let labels = this.state.labelFilters;

    if (!val) {
      labels = [];
    } else {
      if (labels.includes(val)) {
        labels.splice(labels.indexOf(val), 1);
      } else {
        labels.push(val);
      }
    }
    console.log(labels);

    this.setState({ labelFilters: labels });
    this.toggleLabelsPopover();
  }

  renderFilteredStories = (search, type, owner, stage, label) => {
    return ls(`pp-project-${this.props.params.projectId}-stories`)
      .filter(search)
      .filter(owner)
      .filter(type)
      .filter(stage)
      .filter(label)
      .sort(sortStoriesByCreatedTime)
  }

  render() {
    console.log(this.props.params.projectId);
    return (

            <Icon icon="search" style={styles.searchIcon} />
            <input
              placeholder="search stories"
              style={styles.searchInput}
              onKeyUp={(e) => {
                this.setState({ searchFilter: e.currentTarget.value });
              }}
            />

          <Icon icon={typeIcons[this.state.storyTypeFilter]} style={{ fontSize: '1.25em', color: '#fff', margin: 'auto 0 auto 20px', flex: '0 0 auto'}} />
          <DropDownMenu
            underlineStyle={{
              margin: 0,
              borderTop: '2px solid rgba(0,0,0,0.15)',
              display: 'none',
            }}
            labelStyle={{
              paddingLeft: 10,
              fontSize: '1em',
              color: '#fff',
              fontWeight: 700,
            }}
            style={{ margin: 'auto 0', height: 'auto', flex: '0 0 auto' }}
            value={this.state.storyTypeFilter}
            onChange={this.handleStoryTypeChange}
          >
            <MenuItem leftIcon={<Icon icon="group_work" style={{ fontSize: '1.2em', color: '#aaa' }} />} value='all' primaryText="All Types" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '0px solid #eee' }}  />
            <MenuItem leftIcon={<Icon icon="bug_report" style={{ fontSize: '1.2em', color: '#aaa' }} />} value='bug' primaryText="Bugs" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }}  />
            <MenuItem leftIcon={<Icon icon="build" style={{ fontSize: '1.2em', color: '#aaa' }} />} value='chore' primaryText="Chores" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }}  />
            <MenuItem leftIcon={<Icon icon="extension" />} value='feature' primaryText="Features" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }}  />
            <MenuItem leftIcon={<Icon icon="backup" style={{ fontSize: '1.2em', color: '#aaa' }} />} value='release' primaryText="Releases" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }}  />
          </DropDownMenu>

 this.toggleLabelsPopover(e.currentTarget)}>
            <Icon icon={this.state.labelFilters.length ? 'label_outline' : 'label'} style={{ margin: 'auto 10px auto 0'}} />
            {this.state.labelFilters.length ? (
              {this.state.labelFilters.length} label{(this.state.labelFilters.length === 1) ? false : 's'}
            ) : (
              All Labels
            )}
            <Icon icon="arrow_drop_down" style={{ fontSize: '1.5em', margin: 'auto 0 auto 10px'}} />

          <Popover
            open={this.state.showLabelsPopover}
            anchorEl={this.state.labelsEl}
            anchorOrigin={{horizontal: 'left', vertical: 'bottom'}}
            targetOrigin={{horizontal: 'left', vertical: 'top'}}
            onRequestClose={() => this.toggleLabelsPopover()}
          >
            <Menu maxHeight={350}>
              <MenuItem leftIcon={<Icon icon="label" />} onClick={() => this.handleLabelChange()} primaryText="All Labels" />
              {this.state.project_labels_fetched ? ls(`pp-project-${this.props.params.projectId}-labels`).sort(this.sortActiveLabels).map((label, labelIndex) => (
                <MenuItem key={`label-${labelIndex}`}onClick={() => this.handleLabelChange(label.id)} leftIcon={<Icon icon="label_outline" style={{color: '#aaa'}} />} value={label.id} primaryText={label.name} style={{ borderTop: '1px solid #eee', backgroundColor: (this.state.labelFilters.includes(label.id) ? '#AED6F1' : '#fff') }} />
              )) : false}
            </Menu>
          </Popover>

          <Icon icon={(this.state.ownerFilter === 'all') ? 'group' : 'person'} style={{flex: '0 0 auto', fontSize: '1.25em', color: '#fff', margin: 'auto 0 auto 20px'}} />
          <DropDownMenu
            underlineStyle={{
              margin: 0,
              borderTop: '2px solid rgba(0,0,0,0.15)',
              display: 'none',
            }}
            labelStyle={{
              paddingLeft: 10,
              fontSize: '1em',
              color: '#fff',
              fontWeight: 700,
            }}
            style={{ margin: 'auto 0', height: 'auto', flex: '0 0 auto', }}
            value={this.state.ownerFilter}
            onChange={this.handleOwnerChange}
            maxHeight={300}
          >
            <MenuItem leftIcon={<Icon icon="group" />} value='all' primaryText="All Owners" />
            <MenuItem leftIcon={<Icon icon="person" />} value={ls('pp-me').id} primaryText="Me" />
            {this.state.project_memberships_fetched ? ls(`pp-project-${this.props.params.projectId}-memberships`).filter((val) => val.person.id !== ls('pp-me').id).map((member, memberIndex) => (
              <MenuItem key={`member-${memberIndex}`} leftIcon={<Icon icon="person" style={styles.ownerIcon}/>} value={member.person.id} primaryText={member.person.name} style={{ textTransform: 'capitalize', borderTop: '1px solid #eee' }} />
            )) : false}
          </DropDownMenu>

          <DropDownMenu
            underlineStyle={{
              margin: 0,
              borderTop: '2px solid rgba(0,0,0,0.15)',
              display: 'none',
            }}
            labelStyle={{
              paddingLeft: 10,
              fontSize: '1em',
              color: '#fff',
              fontWeight: 700,
              textTransform: 'capitalize',
            }}
            style={{ margin: 'auto 0', height: 'auto', flex: '0 0 auto' }}
            value={this.state.stagesFilter}
            onChange={this.handleStagesChange}
            maxHeight={350}
          >
            <MenuItem value='all' primaryText="All Stages" />
            {statuses.map((status, statusIndex) => (
              <MenuItem key={`status-${statusIndex}`} value={status} primaryText={`${statusIndex} - ${status}`} style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }} />
            ))}
          </DropDownMenu>

        {this.state.project_stories_fetched ? (

            {this.renderFilteredStories(this.filterBySearch, this.filterByType, this.filterByOwner, this.filterByStage, this.filterByLabels) ? this.renderFilteredStories(this.filterBySearch, this.filterByType, this.filterByOwner, this.filterByStage, this.filterByLabels).map((story, storyIndex) => (
              <StoryCard
                key={storyIndex}
                story={story}
                storyIndex={storyIndex}
                setNotification={this.props.setNotification}
                handleLabelChange={this.handleLabelChange}
                labelFilters={this.state.labelFilters}
              />
            )) : (

No stories

            )}

        ) : (
          <Loader />
        )}

    );
  }
}

const typeIcons = {
  all: 'group_work',
  feature: 'extension',
  bug: 'bug_report',
  chore: 'build',
  release: 'backup',
};

const statuses = [
  'unscheduled',
  'unstarted',
  'started',
  'finished',
  'delivered',
  'rejected',
  'accepted',
];

const statusColors = {
  unscheduled: {
    text: '#888',
    bg: '#eee',
  },
  unstarted: {
    text: '#eee',
    bg: '#888',
  },
  started: {
    text: '#fff',
    bg: '#444',
  },
  finished: {
    text: '#fff',
    bg: '#3E7293',
  },
  delivered: {
    text: '#fff',
    bg: '#09a3ed',
  },
  accepted: {
    text: '#fff',
    bg: '#8bc34a',
  },
  rejected: {
    text: '#fff',
    bg: 'red',
  },
};
<commit_msg>labels<commit_after>import {
  DropDownMenu,
  Menu,
  MenuItem,
  Popover,
} from 'material-ui';
import {
  Icon,
  Loader,
  StoryCard,
} from '../../components';

import Clipboard from 'clipboard';
import React from 'react';
import {hashHistory} from 'react-router';
import ls from 'local-storage';
import moment from 'moment';
import radium from 'radium';
import request from 'request';
import styles from './styles';

function sortStoriesByCreatedTime(a, b) {
  return new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime();
}

@radium
export default class ProjectStoriesView extends React.Component {

  state = {
    project_stories_fetched: false,
    project_memberships_fetched: false,
    searchFilter: '',
    storyTypeFilter: 'all',
    ownerFilter: 'all',
    stagesFilter: 'all',
    labelFilters: [],
    showLabelsPopover: false,
    labelsEl: null,
  };

  componentDidMount() {

    const CB = new Clipboard('.storyId');

    CB.on('success', (e) => {
        this.props.setNotification(true, `${e.text} copied to clipboard`);
    });

    const headers = new Headers();
    const projectId = this.props.params.projectId;
    headers.append('X-TrackerToken', ls('pp-api'));
    this.props.setViewTitle(ls(`pp-project-${projectId}-details`).name);
    this.props.setShowBack({
      link: '/projects',
      text: 'Projects',
      clearOnClick: true,
    });
    // TODO paginate requests
    fetch(`https://www.pivotaltracker.com/services/v5/projects/${projectId}/stories?limit=1000`, {
      mode: 'cors',
      headers,
      method: 'GET',
    }).then(res => res.json()).then((res) => {
      ls.set(`pp-project-${projectId}-stories`, res);
      this.setState({project_stories_fetched: true});
    });

    fetch(`https://www.pivotaltracker.com/services/v5/projects/${projectId}/memberships`, {
      mode: 'cors',
      headers,
      method: 'GET',
    }).then(res => res.json()).then((res) => {
      ls.set(`pp-project-${projectId}-memberships`, res);
      this.setState({project_memberships_fetched: true});
    });

    fetch(`https://www.pivotaltracker.com/services/v5/projects/${projectId}/labels`, {
      mode: 'cors',
      headers,
      method: 'GET',
    }).then(res => res.json()).then((res) => {
      ls.set(`pp-project-${projectId}-labels`, res);
      this.setState({project_labels_fetched: true});
    });
  }

  sortActiveLabels = (a, b) => {
    const sortA = this.state.labelFilters.includes(a.id) ? -1 : 1;
    const sortB = this.state.labelFilters.includes(b.id) ? -1 : 1;
    return sortA - sortB;
  }

  filterBySearch = story => {
    const reg = new RegExp(this.state.searchFilter, 'i', 'g');
    return story.name.match(reg);
  }

  filterByType = story => {
    if (this.state.storyTypeFilter === 'all') return true;
    return story.story_type === this.state.storyTypeFilter;
  }

  filterByOwner = story => {
    if (this.state.ownerFilter === 'all') return true;
    return story.owned_by_id === this.state.ownerFilter;
  }

  filterByStage = story => {
    if (this.state.stagesFilter === 'all') return true;
    return story.current_state === this.state.stagesFilter;
  }

  filterByLabels = story => {
    let res = false;

    if (!this.state.labelFilters.length) return true;

    if (!story.labels.length) return false;

    for (let i = 0; i < story.labels.length; i++) {
      if (this.state.labelFilters.includes(story.labels[i].id)) res = true;
    }

    return res;
  }

  toggleLabelsPopover = (el) => {
    if (el) {
      this.setState({
        showLabelsPopover: true,
        labelsEl: el,
      });
    } else {
      this.setState({
        showLabelsPopover: false,
        labelsEl: null,
      });
    }
  }

  handleStoryTypeChange = (e, t, val) => this.setState({ storyTypeFilter: val });

  handleOwnerChange = (e, t, val) => this.setState({ ownerFilter: val });

  handleStagesChange = (e, t, val) => this.setState({ stagesFilter: val });

  handleLabelChange = (val) => {
    let labels = this.state.labelFilters;

    if (!val) {
      labels = [];
    } else {
      if (labels.includes(val)) {
        labels.splice(labels.indexOf(val), 1);
      } else {
        labels.push(val);
      }
    }
    console.log(labels);

    this.setState({ labelFilters: labels });
    this.toggleLabelsPopover();
  }

  renderFilteredStories = (search, type, owner, stage, label) => {
    return ls(`pp-project-${this.props.params.projectId}-stories`)
      .filter(search)
      .filter(owner)
      .filter(type)
      .filter(stage)
      .filter(label)
      .sort(sortStoriesByCreatedTime)
  }

  render() {
    console.log(this.props.params.projectId);
    return (

            <Icon icon="search" style={styles.searchIcon} />
            <input
              placeholder="search stories"
              style={styles.searchInput}
              onKeyUp={(e) => {
                this.setState({ searchFilter: e.currentTarget.value });
              }}
            />

          <Icon icon={typeIcons[this.state.storyTypeFilter]} style={{ fontSize: '1.25em', color: '#fff', margin: 'auto 0 auto 20px', flex: '0 0 auto'}} />
          <DropDownMenu
            underlineStyle={{
              margin: 0,
              borderTop: '2px solid rgba(0,0,0,0.15)',
              display: 'none',
            }}
            labelStyle={{
              paddingLeft: 10,
              fontSize: '1em',
              color: '#fff',
              fontWeight: 700,
            }}
            style={{ margin: 'auto 0', height: 'auto', flex: '0 0 auto' }}
            value={this.state.storyTypeFilter}
            onChange={this.handleStoryTypeChange}
          >
            <MenuItem leftIcon={<Icon icon="group_work" style={{ fontSize: '1.2em', color: '#aaa' }} />} value='all' primaryText="All Types" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '0px solid #eee' }}  />
            <MenuItem leftIcon={<Icon icon="bug_report" style={{ fontSize: '1.2em', color: '#aaa' }} />} value='bug' primaryText="Bugs" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }}  />
            <MenuItem leftIcon={<Icon icon="build" style={{ fontSize: '1.2em', color: '#aaa' }} />} value='chore' primaryText="Chores" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }}  />
            <MenuItem leftIcon={<Icon icon="extension" />} value='feature' primaryText="Features" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }}  />
            <MenuItem leftIcon={<Icon icon="backup" style={{ fontSize: '1.2em', color: '#aaa' }} />} value='release' primaryText="Releases" style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }}  />
          </DropDownMenu>

 this.toggleLabelsPopover(e.currentTarget)}>
            <Icon icon={this.state.labelFilters.length ? 'label_outline' : 'label'} style={{ margin: 'auto 10px auto 0'}} />
            {this.state.labelFilters.length ? (
              {this.state.labelFilters.length} label{(this.state.labelFilters.length === 1) ? false : 's'}
            ) : (
              All Labels
            )}
            <Icon icon="arrow_drop_down" style={{ fontSize: '1.5em', margin: 'auto 0 auto 10px'}} />

          <Popover
            open={this.state.showLabelsPopover}
            anchorEl={this.state.labelsEl}
            anchorOrigin={{horizontal: 'left', vertical: 'bottom'}}
            targetOrigin={{horizontal: 'left', vertical: 'top'}}
            onRequestClose={() => this.toggleLabelsPopover()}
          >
            <Menu maxHeight={350} style={{ width: 200, overflowX: 'hidden', textOverflow: 'ellpsis', whiteSpace: 'nowrap'}}>
              <MenuItem leftIcon={<Icon icon="label" />} onClick={() => this.handleLabelChange()} primaryText="All Labels" />
              {this.state.project_labels_fetched ? ls(`pp-project-${this.props.params.projectId}-labels`).sort(this.sortActiveLabels).map((label, labelIndex) => (
                <MenuItem key={`label-${labelIndex}`}onClick={() => this.handleLabelChange(label.id)} leftIcon={<Icon icon="label_outline" style={{color: '#aaa' }} />} value={label.id} primaryText={label.name} style={{ borderTop: '1px solid #eee', flex: 1, fontSize: '0.9em', padding: 0, backgroundColor: (this.state.labelFilters.includes(label.id) ? '#AED6F1' : '#fff') }} />
              )) : false}
            </Menu>
          </Popover>

          <Icon icon={(this.state.ownerFilter === 'all') ? 'group' : 'person'} style={{flex: '0 0 auto', fontSize: '1.25em', color: '#fff', margin: 'auto 0 auto 20px'}} />
          <DropDownMenu
            underlineStyle={{
              margin: 0,
              borderTop: '2px solid rgba(0,0,0,0.15)',
              display: 'none',
            }}
            labelStyle={{
              paddingLeft: 10,
              fontSize: '1em',
              color: '#fff',
              fontWeight: 700,
            }}
            style={{ margin: 'auto 0', height: 'auto', flex: '0 0 auto', }}
            value={this.state.ownerFilter}
            onChange={this.handleOwnerChange}
            maxHeight={300}
          >
            <MenuItem leftIcon={<Icon icon="group" />} value='all' primaryText="All Owners" />
            <MenuItem leftIcon={<Icon icon="person" />} value={ls('pp-me').id} primaryText="Me" />
            {this.state.project_memberships_fetched ? ls(`pp-project-${this.props.params.projectId}-memberships`).filter((val) => val.person.id !== ls('pp-me').id).map((member, memberIndex) => (
              <MenuItem key={`member-${memberIndex}`} leftIcon={<Icon icon="person" style={styles.ownerIcon}/>} value={member.person.id} primaryText={member.person.name} style={{ textTransform: 'capitalize', borderTop: '1px solid #eee' }} />
            )) : false}
          </DropDownMenu>

          <DropDownMenu
            underlineStyle={{
              margin: 0,
              borderTop: '2px solid rgba(0,0,0,0.15)',
              display: 'none',
            }}
            labelStyle={{
              paddingLeft: 10,
              fontSize: '1em',
              color: '#fff',
              fontWeight: 700,
              textTransform: 'capitalize',
            }}
            style={{ margin: 'auto 0', height: 'auto', flex: '0 0 auto' }}
            value={this.state.stagesFilter}
            onChange={this.handleStagesChange}
            maxHeight={350}
          >
            <MenuItem value='all' primaryText="All Stages" />
            {statuses.map((status, statusIndex) => (
              <MenuItem key={`status-${statusIndex}`} value={status} primaryText={`${statusIndex} - ${status}`} style={{ textTransform: 'capitalize', alignItems: 'center', borderTop: '1px solid #eee' }} />
            ))}
          </DropDownMenu>

        {this.state.project_stories_fetched ? (

            {this.renderFilteredStories(this.filterBySearch, this.filterByType, this.filterByOwner, this.filterByStage, this.filterByLabels) ? this.renderFilteredStories(this.filterBySearch, this.filterByType, this.filterByOwner, this.filterByStage, this.filterByLabels).map((story, storyIndex) => (
              <StoryCard
                key={storyIndex}
                story={story}
                storyIndex={storyIndex}
                setNotification={this.props.setNotification}
                handleLabelChange={this.handleLabelChange}
                labelFilters={this.state.labelFilters}
              />
            )) : (

No stories

            )}

        ) : (
          <Loader />
        )}

    );
  }
}

const typeIcons = {
  all: 'group_work',
  feature: 'extension',
  bug: 'bug_report',
  chore: 'build',
  release: 'backup',
};

const statuses = [
  'unscheduled',
  'unstarted',
  'started',
  'finished',
  'delivered',
  'rejected',
  'accepted',
];

const statusColors = {
  unscheduled: {
    text: '#888',
    bg: '#eee',
  },
  unstarted: {
    text: '#eee',
    bg: '#888',
  },
  started: {
    text: '#fff',
    bg: '#444',
  },
  finished: {
    text: '#fff',
    bg: '#3E7293',
  },
  delivered: {
    text: '#fff',
    bg: '#09a3ed',
  },
  accepted: {
    text: '#fff',
    bg: '#8bc34a',
  },
  rejected: {
    text: '#fff',
    bg: 'red',
  },
};
<|endoftext|>


<commit_before>/**
 * d3.tip
 * Copyright (c) 2013 Justin Palmer
 *
 * Tooltips for d3.js SVG visualizations
 */
// eslint-disable-next-line no-extra-semi
;(function(root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module with d3 as a dependency.
    define([
      'd3-collection',
      'd3-selection'
    ], factory)
  } else if (typeof module === 'object' && module.exports) {
    /* eslint-disable global-require */
    // CommonJS
    var d3Collection = require('d3-collection'),
        d3Selection = require('d3-selection')
    module.exports = factory(d3Collection, d3Selection)
    /* eslint-enable global-require */
  } else {
    // Browser global.
    var d3 = root.d3
    // eslint-disable-next-line no-param-reassign
    root.d3.tip = factory(d3, d3)
  }
}(this, function(d3Collection, d3Selection) {
  // Public - contructs a new tooltip
  //
  // Returns a tip
  return function() {
    var direction   = d3TipDirection,
        offset      = d3TipOffset,
        html        = d3TipHTML,
        rootElement = document.body,
        node        = initNode(),
        svg         = null,
        point       = null,
        target      = null

    function tip(vis) {
      svg = getSVGNode(vis)
      if (!svg) return
      point = svg.createSVGPoint()
      rootElement.appendChild(node)
    }

    // Public - show the tooltip on the screen
    //
    // Returns a tip
    tip.show = function() {
      var args = Array.prototype.slice.call(arguments)
      if (args[args.length - 1] instanceof SVGElement) target = args.pop()

      var content = html.apply(this, args),
          poffset = offset.apply(this, args),
          dir     = direction.apply(this, args),
          nodel   = getNodeEl(),
          i       = directions.length,
          coords,
          scrollTop  = document.documentElement.scrollTop ||
            rootElement.scrollTop,
          scrollLeft = document.documentElement.scrollLeft ||
            rootElement.scrollLeft

      nodel.html(content)
        .style('opacity', 1).style('pointer-events', 'all')

      while (i--) nodel.classed(directions[i], false)
      coords = directionCallbacks.get(dir).apply(this)
      nodel.classed(dir, true)
        .style('top', (coords.top + poffset[0]) + scrollTop + 'px')
        .style('left', (coords.left + poffset[1]) + scrollLeft + 'px')

      return tip
    }

    // Public - hide the tooltip
    //
    // Returns a tip
    tip.hide = function() {
      var nodel = getNodeEl()
      nodel.style('opacity', 0).style('pointer-events', 'none')
      return tip
    }

    // Public: Proxy attr calls to the d3 tip container.
    // Sets or gets attribute value.
    //
    // n - name of the attribute
    // v - value of the attribute
    //
    // Returns tip or attribute value
    // eslint-disable-next-line no-unused-vars
    tip.attr = function(n, v) {
      if (arguments.length < 2 && typeof n === 'string') {
        return getNodeEl().attr(n)
      }

      var args =  Array.prototype.slice.call(arguments)
      d3Selection.selection.prototype.attr.apply(getNodeEl(), args)
      return tip
    }

    // Public: Proxy style calls to the d3 tip container.
    // Sets or gets a style value.
    //
    // n - name of the property
    // v - value of the property
    //
    // Returns tip or style property value
    // eslint-disable-next-line no-unused-vars
    tip.style = function(n, v) {
      if (arguments.length < 2 && typeof n === 'string') {
        return getNodeEl().style(n)
      }

      var args = Array.prototype.slice.call(arguments)
      d3Selection.selection.prototype.style.apply(getNodeEl(), args)
      return tip
    }

    // Public: Set or get the direction of the tooltip
    //
    // v - One of n(north), s(south), e(east), or w(west), nw(northwest),
    //     sw(southwest), ne(northeast) or se(southeast)
    //
    // Returns tip or direction
    tip.direction = function(v) {
      if (!arguments.length) return direction
      direction = v == null ? v : functor(v)

      return tip
    }

    // Public: Sets or gets the offset of the tip
    //
    // v - Array of [x, y] offset
    //
    // Returns offset or
    tip.offset = function(v) {
      if (!arguments.length) return offset
      offset = v == null ? v : functor(v)

      return tip
    }

    // Public: sets or gets the html value of the tooltip
    //
    // v - String value of the tip
    //
    // Returns html value or tip
    tip.html = function(v) {
      if (!arguments.length) return html
      html = v == null ? v : functor(v)

      return tip
    }

    // Public: sets or gets the root element anchor of the tooltip
    //
    // v - root element of the tooltip
    //
    // Returns root node of tip
    tip.rootElement = function(v) {
      if (!arguments.length) return rootElement
      rootElement = v == null ? v : functor(v)

      return tip
    }

    // Public: destroys the tooltip and removes it from the DOM
    //
    // Returns a tip
    tip.destroy = function() {
      if (node) {
        getNodeEl().remove()
        node = null
      }
      return tip
    }

    function d3TipDirection() { return 'n' }
    function d3TipOffset() { return [0, 0] }
    function d3TipHTML() { return ' ' }

    var directionCallbacks = d3Collection.map({
          n:  directionNorth,
          s:  directionSouth,
          e:  directionEast,
          w:  directionWest,
          nw: directionNorthWest,
          ne: directionNorthEast,
          sw: directionSouthWest,
          se: directionSouthEast
        }),
        directions = directionCallbacks.keys()

    function directionNorth() {
      var bbox = getScreenBBox()
      return {
        top:  bbox.n.y - node.offsetHeight,
        left: bbox.n.x - node.offsetWidth / 2
      }
    }

    function directionSouth() {
      var bbox = getScreenBBox()
      return {
        top:  bbox.s.y,
        left: bbox.s.x - node.offsetWidth / 2
      }
    }

    function directionEast() {
      var bbox = getScreenBBox()
      return {
        top:  bbox.e.y - node.offsetHeight / 2,
        left: bbox.e.x
      }
    }

    function directionWest() {
      var bbox = getScreenBBox()
      return {
        top:  bbox.w.y - node.offsetHeight / 2,
        left: bbox.w.x - node.offsetWidth
      }
    }

    function directionNorthWest() {
      var bbox = getScreenBBox()
      return {
        top:  bbox.nw.y - node.offsetHeight,
        left: bbox.nw.x - node.offsetWidth
      }
    }

    function directionNorthEast() {
      var bbox = getScreenBBox()
      return {
        top:  bbox.ne.y - node.offsetHeight,
        left: bbox.ne.x
      }
    }

    function directionSouthWest() {
      var bbox = getScreenBBox()
      return {
        top:  bbox.sw.y,
        left: bbox.sw.x - node.offsetWidth
      }
    }

    function directionSouthEast() {
      var bbox = getScreenBBox()
      return {
        top:  bbox.se.y,
        left: bbox.se.x
      }
    }

    function initNode() {
      var div = d3Selection.select(document.createElement('div'))
      div
        .style('position', 'absolute')
        .style('top', 0)
        .style('opacity', 0)
        .style('pointer-events', 'none')
        .style('box-sizing', 'border-box')

      return div.node()
    }

    function getSVGNode(element) {
      var svgNode = element.node()
      if (!svgNode) return null
      if (svgNode.tagName.toLowerCase() === 'svg') return svgNode
      return svgNode.ownerSVGElement
    }

    function getNodeEl() {
      if (node == null) {
        node = initNode()
        // re-add node to DOM
        rootElement.appendChild(node)
      }
      return d3Selection.select(node)
    }

    // Private - gets the screen coordinates of a shape
    //
    // Given a shape on the screen, will return an SVGPoint for the directions
    // n(north), s(south), e(east), w(west), ne(northeast), se(southeast),
    // nw(northwest), sw(southwest).
    //
    //    +-+-+
    //    |   |
    //    +   +
    //    |   |
    //    +-+-+
    //
    // Returns an Object {n, s, e, w, nw, sw, ne, se}
    function getScreenBBox() {
      var targetel   = target || d3Selection.event.target

      while (targetel.getScreenCTM == null && targetel.parentNode == null) {
        targetel = targetel.parentNode
      }

      var bbox       = {},
          matrix     = targetel.getScreenCTM(),
          tbbox      = targetel.getBBox(),
          width      = tbbox.width,
          height     = tbbox.height,
          x          = tbbox.x,
          y          = tbbox.y

      point.x = x
      point.y = y
      bbox.nw = point.matrixTransform(matrix)
      point.x += width
      bbox.ne = point.matrixTransform(matrix)
      point.y += height
      bbox.se = point.matrixTransform(matrix)
      point.x -= width
      bbox.sw = point.matrixTransform(matrix)
      point.y -= height / 2
      bbox.w = point.matrixTransform(matrix)
      point.x += width
      bbox.e = point.matrixTransform(matrix)
      point.x -= width / 2
      point.y -= height / 2
      bbox.n = point.matrixTransform(matrix)
      point.y += height
      bbox.s = point.matrixTransform(matrix)

      return bbox
    }

    // Private - replace D3JS 3.X d3.functor() function
    function functor(v) {
      return typeof v === 'function' ? v : function() {
        return v
      }
    }

    return tip
  }
// eslint-disable-next-line semi
}));<commit_msg>Delete d3-tip.js<commit_after><|endoftext|>


<commit_before>(function($, UI) {

    "use strict";

    var scrollpos = {x: window.scrollX, y: window.scrollY},
        $win      = $(window),
        $doc      = $(document),
        Offcanvas = {

        show: function(element) {

            element = $(element);

            if (!element.length) return;

            var $html     = $('body'),
                winwidth  = $win.width(),
                bar       = element.find(".uk-offcanvas-bar:first"),
                rtl       = ($.UIkit.langdirection == "right"),
                flip      = bar.hasClass("uk-offcanvas-bar-flip") ? -1:1,
                dir       = flip * (rtl ? -1 : 1);

            scrollpos = {x: window.scrollX, y: window.scrollY};

            element.addClass("uk-active");

            $html.css({"width": window.innerWidth, "height": window.innerHeight}).addClass("uk-offcanvas-page");
            $html.css((rtl ? "margin-right" : "margin-left"), (rtl ? -1 : 1) * (bar.outerWidth() * dir)).width(); // .width() - force redraw

            bar.addClass("uk-offcanvas-bar-show");

            element.off(".ukoffcanvas").on("click.ukoffcanvas swipeRight.ukoffcanvas swipeLeft.ukoffcanvas", function(e) {

                var target = $(e.target);

                if (!e.type.match(/swipe/)) {

                    if (!target.hasClass("uk-offcanvas-close")) {
                        if (target.hasClass("uk-offcanvas-bar")) return;
                        if (target.parents(".uk-offcanvas-bar:first").length) return;
                    }
                }

                e.stopImmediatePropagation();
                Offcanvas.hide();
            });

            $doc.on('keydown.ukoffcanvas', function(e) {
                if (e.keyCode === 27) { // ESC
                    Offcanvas.hide();
                }
            });
        },

        hide: function(force) {

            var $html = $('body'),
                panel = $(".uk-offcanvas.uk-active"),
                rtl   = ($.UIkit.langdirection == "right"),
                bar   = panel.find(".uk-offcanvas-bar:first");

            if (!panel.length) return;

            if ($.UIkit.support.transition && !force) {

                $html.one($.UIkit.support.transition.end, function() {
                    $html.removeClass("uk-offcanvas-page").css({"width": "", "height": ""});
                    panel.removeClass("uk-active");
                    window.scrollTo(scrollpos.x, scrollpos.y);
                }).css((rtl ? "margin-right" : "margin-left"), "");

                setTimeout(function(){
                    bar.removeClass("uk-offcanvas-bar-show");
                }, 0);

            } else {
                $html.removeClass("uk-offcanvas-page").css({"width": "", "height": ""});
                panel.removeClass("uk-active");
                bar.removeClass("uk-offcanvas-bar-show");
                window.scrollTo(scrollpos.x, scrollpos.y);
            }

            panel.off(".ukoffcanvas");
            $doc.off(".ukoffcanvas");
        }
    };

    UI.component('offcanvasTrigger', {

        init: function() {

            var $this = this;

            this.options = $.extend({
                "target": $this.element.is("a") ? $this.element.attr("href") : false
            }, this.options);

            this.on("click", function(e) {
                e.preventDefault();
                Offcanvas.show($this.options.target);
            });
        }
    });

    UI.offcanvas = Offcanvas;

    // init code
    $doc.on("click.offcanvas.uikit", "[data-uk-offcanvas]", function(e) {

        e.preventDefault();

        var ele = $(this);

        if (!ele.data("offcanvasTrigger")) {
            var obj = UI.offcanvasTrigger(ele, UI.Utils.options(ele.attr("data-uk-offcanvas")));
            ele.trigger("click");
        }
    });

})(jQuery, jQuery.UIkit);<commit_msg>Fixes #367<commit_after>(function($, UI) {

    "use strict";

    var scrollpos = {x: window.scrollX, y: window.scrollY},
        $win      = $(window),
        $doc      = $(document),
        $html     = $('html'),
        Offcanvas = {

        show: function(element) {

            element = $(element);

            if (!element.length) return;

            var $body     = $('body'),
                winwidth  = $win.width(),
                bar       = element.find(".uk-offcanvas-bar:first"),
                rtl       = ($.UIkit.langdirection == "right"),
                flip      = bar.hasClass("uk-offcanvas-bar-flip") ? -1:1,
                dir       = flip * (rtl ? -1 : 1);

            scrollpos = {x: window.scrollX, y: window.scrollY};

            element.addClass("uk-active");

            $body.css({"width": window.innerWidth, "height": window.innerHeight}).addClass("uk-offcanvas-page");
            $body.css((rtl ? "margin-right" : "margin-left"), (rtl ? -1 : 1) * (bar.outerWidth() * dir)).width(); // .width() - force redraw

            $html.css('margin-top', scrollpos.y * -1);

            bar.addClass("uk-offcanvas-bar-show");

            element.off(".ukoffcanvas").on("click.ukoffcanvas swipeRight.ukoffcanvas swipeLeft.ukoffcanvas", function(e) {

                var target = $(e.target);

                if (!e.type.match(/swipe/)) {

                    if (!target.hasClass("uk-offcanvas-close")) {
                        if (target.hasClass("uk-offcanvas-bar")) return;
                        if (target.parents(".uk-offcanvas-bar:first").length) return;
                    }
                }

                e.stopImmediatePropagation();
                Offcanvas.hide();
            });

            $doc.on('keydown.ukoffcanvas', function(e) {
                if (e.keyCode === 27) { // ESC
                    Offcanvas.hide();
                }
            });
        },

        hide: function(force) {

            var $body = $('body'),
                panel = $(".uk-offcanvas.uk-active"),
                rtl   = ($.UIkit.langdirection == "right"),
                bar   = panel.find(".uk-offcanvas-bar:first");

            if (!panel.length) return;

            if ($.UIkit.support.transition && !force) {

                $body.one($.UIkit.support.transition.end, function() {
                    $body.removeClass("uk-offcanvas-page").css({"width": "", "height": ""});
                    panel.removeClass("uk-active");
                    $html.css('margin-top', '');
                    window.scrollTo(scrollpos.x, scrollpos.y);
                }).css((rtl ? "margin-right" : "margin-left"), "");

                setTimeout(function(){
                    bar.removeClass("uk-offcanvas-bar-show");
                }, 0);

            } else {
                $body.removeClass("uk-offcanvas-page").css({"width": "", "height": ""});
                panel.removeClass("uk-active");
                bar.removeClass("uk-offcanvas-bar-show");
                $html.css('margin-top', '');
                window.scrollTo(scrollpos.x, scrollpos.y);
            }

            panel.off(".ukoffcanvas");
            $doc.off(".ukoffcanvas");
        }
    };

    UI.component('offcanvasTrigger', {

        init: function() {

            var $this = this;

            this.options = $.extend({
                "target": $this.element.is("a") ? $this.element.attr("href") : false
            }, this.options);

            this.on("click", function(e) {
                e.preventDefault();
                Offcanvas.show($this.options.target);
            });
        }
    });

    UI.offcanvas = Offcanvas;

    // init code
    $doc.on("click.offcanvas.uikit", "[data-uk-offcanvas]", function(e) {

        e.preventDefault();

        var ele = $(this);

        if (!ele.data("offcanvasTrigger")) {
            var obj = UI.offcanvasTrigger(ele, UI.Utils.options(ele.attr("data-uk-offcanvas")));
            ele.trigger("click");
        }
    });

})(jQuery, jQuery.UIkit);<|endoftext|>


<commit_before>import errorHanlder from 'errorhandler';
import accepts from 'accepts';

export default function prodErrorHandler() {
  if (process.env.NODE_ENV === 'development') {
    return errorHanlder({ log: true });
  }
  // error handling in production.
  // disabling eslint due to express parity rules for error handlers
  return function(err, req, res, next) { // eslint-disable-line
    // respect err.status
    if (err.status) {
      res.statusCode = err.status;
    }

    // default status code to 500
    if (res.statusCode < 400) {
      res.statusCode = 500;
    }

    // parse res type
    const accept = accepts(req);
    const type = accept.type('html', 'json', 'text');

    const message = 'Oops! Something went wrong. Please try again later';
    if (type === 'html') {
      if (typeof req.flash === 'function') {
        req.flash(err.messageType || 'errors', {
          msg: err.userMessage || message
        });
      }
      return res.redirect(err.redirectTo || '/map');
      // json
    } else if (type === 'json') {
      res.setHeader('Content-Type', 'application/json');
      return res.send({
        message: message
      });
      // plain text
    } else {
      res.setHeader('Content-Type', 'text/plain');
      return res.send(message);
    }
  };
}
<commit_msg>Fix typo. errorHanlder -> errorHandler<commit_after>import errorHandler from 'errorhandler';
import accepts from 'accepts';

export default function prodErrorHandler() {
  if (process.env.NODE_ENV === 'development') {
    return errorHandler({ log: true });
  }
  // error handling in production.
  // disabling eslint due to express parity rules for error handlers
  return function(err, req, res, next) { // eslint-disable-line
    // respect err.status
    if (err.status) {
      res.statusCode = err.status;
    }

    // default status code to 500
    if (res.statusCode < 400) {
      res.statusCode = 500;
    }

    // parse res type
    const accept = accepts(req);
    const type = accept.type('html', 'json', 'text');

    const message = 'Oops! Something went wrong. Please try again later';
    if (type === 'html') {
      if (typeof req.flash === 'function') {
        req.flash(err.messageType || 'errors', {
          msg: err.userMessage || message
        });
      }
      return res.redirect(err.redirectTo || '/map');
      // json
    } else if (type === 'json') {
      res.setHeader('Content-Type', 'application/json');
      return res.send({
        message: message
      });
      // plain text
    } else {
      res.setHeader('Content-Type', 'text/plain');
      return res.send(message);
    }
  };
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
const check = require('./index.js');
const cowsay = require('cowsay');

try{
  check(process.argv.slice(2));
}catch(e){
  console.error(cowsay.say({
    text : e.message,
  }));
  process.exit(1);
}

// OK
process.exit(0);<commit_msg>ES5 compatibility changes<commit_after>#!/usr/bin/env node
var cowsay = require('cowsay');
var check = require('./');

try {
  check(process.argv.slice(2));
} catch(err) {
  console.error(cowsay.say({
    text: err.message
  }));

  process.exit(1);
}
<|endoftext|>


<commit_before>const Task             = require('../tasks/-task');
const logger           = require('../utils/logger');
const Promise          = require('rsvp').Promise;
const chalk            = require('chalk');
const find             = require('lodash').find;

module.exports = Task.extend({
  framework: undefined,
  configPath: undefined,

  relativeConfigPath(framework) {
    if (framework === 'vue') {
      return 'vue.config.js';
    } else {
      return 'config/webpack.config.dev.js';
    }
  },

  getConfig() {
    return require(this.configPath);
  },

  warnMsg() {
    let relativeConfigPath = this.relativeConfigPath(this.framework);

    return logger.warn(chalk.yellow(`
      Could not find corber-webpack-plugin in ${relativeConfigPath}.
      This means the cordova.js & plugins will not be available in livereload.

      Ensure corber-webpack-plugin has been installed with:
        yarn add corber-webpack-plugin --dev
      and the ${relativeConfigPath} configureWebpack.plugins array contains
        new CorberWebpackPlugin()

      Read More: http://corber.io/pages/frameworks/${this.framework}`
    ));
  },

  includesWebpack(plugins) {
    let webpackPlugin = find(plugins, function(plugin) {
      if (plugin.constructor.name === 'CorberWebpackPlugin') {
        return true;
      }
    });

    return webpackPlugin !== undefined;
  },

  run() {
    process.env.NODE_ENV = 'development';

    let config = this.getConfig();

    if (this.framework === 'vue') {
      if (config.configureWebpack === undefined ||
        !(config.configureWebpack.plugins instanceof Array) ||
        !this.includesWebpack(config.configureWebpack.plugins))  {

        this.warnMsg();
      }
    } else {
      if (!(config.plugins instanceof Array) ||
        !this.includesWebpack(config.plugins))  {

        this.warnMsg();
      }
    }

    return Promise.resolve();
  }
});
<commit_msg>feat(webpack-plugin): Update validator to list NPM install command<commit_after>const Task             = require('../tasks/-task');
const logger           = require('../utils/logger');
const Promise          = require('rsvp').Promise;
const chalk            = require('chalk');
const find             = require('lodash').find;

module.exports = Task.extend({
  framework: undefined,
  configPath: undefined,

  relativeConfigPath(framework) {
    if (framework === 'vue') {
      return 'vue.config.js';
    } else {
      return 'config/webpack.config.dev.js';
    }
  },

  getConfig() {
    return require(this.configPath);
  },

  warnMsg() {
    let relativeConfigPath = this.relativeConfigPath(this.framework);

    return logger.warn(chalk.yellow(`
      Could not find corber-webpack-plugin in ${relativeConfigPath}.
      This means the cordova.js & plugins will not be available in livereload.

      Ensure corber-webpack-plugin has been installed with:
        yarn add corber-webpack-plugin --dev
      or
        npm install corber-webpack-plugin --save-dev
      and the ${relativeConfigPath} configureWebpack.plugins array contains
        new CorberWebpackPlugin()

      Read More: http://corber.io/pages/frameworks/${this.framework}`
    ));
  },

  includesWebpack(plugins) {
    let webpackPlugin = find(plugins, function(plugin) {
      if (plugin.constructor.name === 'CorberWebpackPlugin') {
        return true;
      }
    });

    return webpackPlugin !== undefined;
  },

  run() {
    process.env.NODE_ENV = 'development';

    let config = this.getConfig();

    if (this.framework === 'vue') {
      if (config.configureWebpack === undefined ||
        !(config.configureWebpack.plugins instanceof Array) ||
        !this.includesWebpack(config.configureWebpack.plugins))  {

        this.warnMsg();
      }
    } else {
      if (!(config.plugins instanceof Array) ||
        !this.includesWebpack(config.plugins))  {

        this.warnMsg();
      }
    }

    return Promise.resolve();
  }
});
<|endoftext|>


<commit_before>// Recomputes all computed indexes and reduced records

'use strict';

var flatten      = require('es5-ext/array/#/flatten')
  , ensureString = require('es5-ext/object/validate-stringifiable-value')
  , startsWith   = require('es5-ext/string/#/starts-with')
  , deferred     = require('deferred')
  , debug        = require('debug-ext')('setup', 4)
  , ensureDriver = require('dbjs-persistence/ensure-driver')
  , recompute    = require('dbjs-persistence/recompute');

module.exports = function (driver, slavePath/*, options*/) {
	var userStorage = ensureDriver(driver).getStorage('user')
	  , anyIdToStorage = require('../../utils/any-id-to-storage')
	  , options = Object(arguments[2]);

	var getBusinessProcessData = function (bpId) {
		return anyIdToStorage(bpId)(function (storage) {
			if (!storage) return [];
			return storage.getObject(bpId)(function (bpData) {
				var id = bpId + '/derivedBusinessProcess';
				return deferred.map(bpData, function (data) {
					if (data.id !== id) return data;
					if (data.data.value[0] !== '7') return data;
					return getBusinessProcessData(data.data.value.slice(1))(function (result) {
						result.push(data);
						return result;
					});
				});
			});
		});
	};

	debug.open("db-recompute");
	return recompute(driver, {
		slaveScriptPath: ensureString(slavePath),
		ids: (function () {
			var ids = [];
			return userStorage.search(null, function (id, data) {
				if (data.value === '7User#') ids.push(id);
			})(ids);
		}()),
		getData: function (userId) {
			return userStorage.getObject(userId)(function (userData) {
				var prefix = userId + '/initialBusinessProcesses*7';
				return deferred.map(userData, function (data) {
					if (!startsWith.call(data.id, prefix)) return data;
					if (data.data.value !== '11') return data;
					return getBusinessProcessData(data.id.slice(prefix.length))(function (result) {
						result.push(data);
						return result;
					});
				});
			}).invoke(flatten);
		},
		initialData: options.initialData
	}).on('progress', function (event) {
		if (event.type === 'nextObject') debug.progress();
		if (event.type === 'nextPool') debug.progress('↻');
	})(function () {
		debug.progress();
		return driver.recalculateAllSizes();
	})(debug.close);
};
<commit_msg>Simplify users id resolution<commit_after>// Recomputes all computed indexes and reduced records

'use strict';

var flatten      = require('es5-ext/array/#/flatten')
  , ensureString = require('es5-ext/object/validate-stringifiable-value')
  , startsWith   = require('es5-ext/string/#/starts-with')
  , deferred     = require('deferred')
  , debug        = require('debug-ext')('setup', 4)
  , ensureDriver = require('dbjs-persistence/ensure-driver')
  , recompute    = require('dbjs-persistence/recompute');

module.exports = function (driver, slavePath/*, options*/) {
	var userStorage = ensureDriver(driver).getStorage('user')
	  , anyIdToStorage = require('../../utils/any-id-to-storage')
	  , options = Object(arguments[2]);

	var getBusinessProcessData = function (bpId) {
		return anyIdToStorage(bpId)(function (storage) {
			if (!storage) return [];
			return storage.getObject(bpId)(function (bpData) {
				var id = bpId + '/derivedBusinessProcess';
				return deferred.map(bpData, function (data) {
					if (data.id !== id) return data;
					if (data.data.value[0] !== '7') return data;
					return getBusinessProcessData(data.data.value.slice(1))(function (result) {
						result.push(data);
						return result;
					});
				});
			});
		});
	};

	debug.open("db-recompute");
	return recompute(driver, {
		slaveScriptPath: ensureString(slavePath),
		ids: userStorage.getAllObjectIds(),
		getData: function (userId) {
			return userStorage.getObject(userId)(function (userData) {
				var prefix = userId + '/initialBusinessProcesses*7';
				return deferred.map(userData, function (data) {
					if (!startsWith.call(data.id, prefix)) return data;
					if (data.data.value !== '11') return data;
					return getBusinessProcessData(data.id.slice(prefix.length))(function (result) {
						result.push(data);
						return result;
					});
				});
			}).invoke(flatten);
		},
		initialData: options.initialData
	}).on('progress', function (event) {
		if (event.type === 'nextObject') debug.progress();
		if (event.type === 'nextPool') debug.progress('↻');
	})(function () {
		debug.progress();
		return driver.recalculateAllSizes();
	})(debug.close);
};
<|endoftext|>


<commit_before>const uuid = require('uuid/v4');
const browserstack = require('browserstack-local');

// Stop node from complaining about fake memory leaks at higher concurrency
require('events').defaultMaxListeners = 20;
require('dotenv').config();

const ONLY_BROWSERS = process.env.ONLY_BROWSERS;
const localIdentifier = uuid();
const screenResolution = '1920x1080';

let projectName = 'Restricted Input';

if (!process.env.TRAVIS_BRANCH) {
  projectName += ' - Local';
} else if (process.env.TRAVIS_BRANCH !== 'master') {
  projectName += ' - PR';
}

const defaultCapabilities = {
  project: projectName,
  'browserstack.debug': true,
  'browserstack.local': true,
  'browserstack.networkLogs': true,
  'browserstack.localIdentifier': localIdentifier
};

const desktopCapabilities = {
  ...defaultCapabilities,
  os: 'windows',
  os_version: '10',
  resolution: screenResolution
};

const mobileCapabilities = {
  ...defaultCapabilities,
  real_mobile: true,
  'browserstack.appium_version': '1.14.0'
};

let capabilities = [
  {
    ...desktopCapabilities,
    browserName: 'Google Chrome',
    browser: 'chrome',
    'browserstack.console': 'info'
  },
  // TODO mobile browsers are having trouble with browser.keys
  //{
  //  ...mobileCapabilities,
  //  browserName: 'iPhone XS Safari',
  //  device: 'iPhone XS',
  //  os_version: '13'
  //},
  //{
  //  ...mobileCapabilities,
  //  browserName: 'Google Pixel 3 Chrome',
  //  device: 'Google Pixel 3',
  //  os_version: '9.0',
  //  chromeOptions: {
  //    prefs: {
  //      // disable chrome's annoying password manager, may be unnec
  //      'profile.password_manager_enabled': false,
  //      credentials_enable_service: false,
  //      password_manager_enabled: false
  //    }
  //  }
  //},
  {
    ...desktopCapabilities,
    browser: 'IE',
    browserName: 'IE 11',
    browser_version: '11.0',
    'browserstack.selenium_version' : '3.5.2'
  },
  {
    ...desktopCapabilities,
    browserName: 'Microsoft Edge',
    browser: 'edge',
    browser_version: '18.0'
  },
  {
    ...desktopCapabilities,
    browserName: 'Firefox',
    browser: 'firefox',
    'browserstack.console': 'info'
  },
  {
    ...desktopCapabilities,
    browserName: 'Desktop Safari',
    browser: 'safari',
    os: 'OS X',
    os_version: 'Mojave'
  }
];

if (ONLY_BROWSERS) {
  capabilities = ONLY_BROWSERS
    .split(',')
    .map(browser => capabilities
      .find(config => config.browser.toLowerCase() === browser.toLowerCase())
    );

  if (capabilities.length === 0) {
    throw new Error(`Could not find browsers ${ONLY_BROWSERS} in config`);
  }
}

exports.config = {
  runner: 'local',
  user: process.env.BROWSERSTACK_USERNAME,
  key: process.env.BROWSERSTACK_ACCESS_KEY,
  browserstackLocal: true,
  specs: require('fs')
    .readdirSync('./test/integration')
    .map(f => `./test/integration/${f}`),
  maxInstances: 6,
  capabilities,
  sync: true,
  logLevel: 'error',
  deprecationWarnings: true,
  bail: 0,
  baseUrl: process.env.HOST,
  waitforTimeout: 20000,
  connectionRetryTimeout: 90000,
  connectionRetryCount: 1,
  services: ['browserstack'],
  framework: 'mocha',
  mochaOpts: {
    timeout: 60000
  },
  reporters: ['spec'],
  reportOptions: {
    outputDir: './'
  },
  onPrepare() {
    /* eslint-disable no-console */
    console.log('Connecting local');
    return new Promise((resolve, reject) => {
      exports.bs_local = new browserstack.Local();
      exports.bs_local.start(
        {
          key: process.env.BROWSERSTACK_ACCESS_KEY,
          localIdentifier
        },
        error => {
          if (error) return reject(error);
          console.log(`Connected with localIdentifier=${localIdentifier}`);
          console.log(
            'Testing in the following browsers:',
            capabilities
              .map(
                browser =>
                  browser.real_mobile
                    ? `${browser.device}@${browser.os_version}`
                    : `${browser.browser}@${browser.browser_version}`
              )
              .join(', ')
          );

          return resolve();
        }
      );
    });
    /* eslint-enable no-console */
  },
  before(capabilities) {
    // Mobile devices/selenium don't support the following APIs yet
    if (!capabilities.real_mobile) {
      browser.maximizeWindow();
      browser.setTimeout({
        pageLoad: 10000,
        script: 5 * 60 * 1000
      });
    }
  },
  onComplete() {
    exports.bs_local.stop(() => {});
  }
};
<commit_msg>Set maxInstances to 4<commit_after>const uuid = require('uuid/v4');
const browserstack = require('browserstack-local');

// Stop node from complaining about fake memory leaks at higher concurrency
require('events').defaultMaxListeners = 20;
require('dotenv').config();

const ONLY_BROWSERS = process.env.ONLY_BROWSERS;
const localIdentifier = uuid();
const screenResolution = '1920x1080';

let projectName = 'Restricted Input';

if (!process.env.TRAVIS_BRANCH) {
  projectName += ' - Local';
} else if (process.env.TRAVIS_BRANCH !== 'master') {
  projectName += ' - PR';
}

const defaultCapabilities = {
  project: projectName,
  'browserstack.debug': true,
  'browserstack.local': true,
  'browserstack.networkLogs': true,
  'browserstack.localIdentifier': localIdentifier
};

const desktopCapabilities = {
  ...defaultCapabilities,
  os: 'windows',
  os_version: '10',
  resolution: screenResolution
};

const mobileCapabilities = {
  ...defaultCapabilities,
  real_mobile: true,
  'browserstack.appium_version': '1.14.0'
};

let capabilities = [
  {
    ...desktopCapabilities,
    browserName: 'Google Chrome',
    browser: 'chrome',
    'browserstack.console': 'info'
  },
  // TODO mobile browsers are having trouble with browser.keys
  //{
  //  ...mobileCapabilities,
  //  browserName: 'iPhone XS Safari',
  //  device: 'iPhone XS',
  //  os_version: '13'
  //},
  //{
  //  ...mobileCapabilities,
  //  browserName: 'Google Pixel 3 Chrome',
  //  device: 'Google Pixel 3',
  //  os_version: '9.0',
  //  chromeOptions: {
  //    prefs: {
  //      // disable chrome's annoying password manager, may be unnec
  //      'profile.password_manager_enabled': false,
  //      credentials_enable_service: false,
  //      password_manager_enabled: false
  //    }
  //  }
  //},
  {
    ...desktopCapabilities,
    browser: 'IE',
    browserName: 'IE 11',
    browser_version: '11.0',
    'browserstack.selenium_version' : '3.5.2'
  },
  {
    ...desktopCapabilities,
    browserName: 'Microsoft Edge',
    browser: 'edge',
    browser_version: '18.0'
  },
  {
    ...desktopCapabilities,
    browserName: 'Firefox',
    browser: 'firefox',
    'browserstack.console': 'info'
  },
  {
    ...desktopCapabilities,
    browserName: 'Desktop Safari',
    browser: 'safari',
    os: 'OS X',
    os_version: 'Mojave'
  }
];

if (ONLY_BROWSERS) {
  capabilities = ONLY_BROWSERS
    .split(',')
    .map(browser => capabilities
      .find(config => config.browser.toLowerCase() === browser.toLowerCase())
    );

  if (capabilities.length === 0) {
    throw new Error(`Could not find browsers ${ONLY_BROWSERS} in config`);
  }
}

exports.config = {
  runner: 'local',
  user: process.env.BROWSERSTACK_USERNAME,
  key: process.env.BROWSERSTACK_ACCESS_KEY,
  browserstackLocal: true,
  specs: require('fs')
    .readdirSync('./test/integration')
    .map(f => `./test/integration/${f}`),
  maxInstances: 4,
  capabilities,
  sync: true,
  logLevel: 'error',
  deprecationWarnings: true,
  bail: 0,
  baseUrl: process.env.HOST,
  waitforTimeout: 20000,
  connectionRetryTimeout: 90000,
  connectionRetryCount: 1,
  services: ['browserstack'],
  framework: 'mocha',
  mochaOpts: {
    timeout: 60000
  },
  reporters: ['spec'],
  reportOptions: {
    outputDir: './'
  },
  onPrepare() {
    /* eslint-disable no-console */
    console.log('Connecting local');
    return new Promise((resolve, reject) => {
      exports.bs_local = new browserstack.Local();
      exports.bs_local.start(
        {
          key: process.env.BROWSERSTACK_ACCESS_KEY,
          localIdentifier
        },
        error => {
          if (error) return reject(error);
          console.log(`Connected with localIdentifier=${localIdentifier}`);
          console.log(
            'Testing in the following browsers:',
            capabilities
              .map(
                browser =>
                  browser.real_mobile
                    ? `${browser.device}@${browser.os_version}`
                    : `${browser.browser}@${browser.browser_version}`
              )
              .join(', ')
          );

          return resolve();
        }
      );
    });
    /* eslint-enable no-console */
  },
  before(capabilities) {
    // Mobile devices/selenium don't support the following APIs yet
    if (!capabilities.real_mobile) {
      browser.maximizeWindow();
      browser.setTimeout({
        pageLoad: 10000,
        script: 5 * 60 * 1000
      });
    }
  },
  onComplete() {
    exports.bs_local.stop(() => {});
  }
};
<|endoftext|>


<commit_before>'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const FriendSchema = new Schema({
  userId: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true,
  },
  friendId: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true,
  },
  createdAt: {
    type: Date,
    required: true,
  },
  updatedAt: {
    type: Date,
    required: true,
  },
  statsAgainst: {
    wins: {
      type: Number,
      default: 0,
      required: true,
    },
    losses: {
      type: Number,
      default: 0,
      required: true,
    },
  },
});

FriendSchema.index({ userId: -1, friendId: -1 }, { unique: true });

module.exports = mongoose.model('Friend', FriendSchema);
<commit_msg>Add ties to statsAgainst in Friend schema.<commit_after>'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const FriendSchema = new Schema({
  userId: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true,
  },
  friendId: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true,
  },
  createdAt: {
    type: Date,
    required: true,
  },
  updatedAt: {
    type: Date,
    required: true,
  },
  statsAgainst: {
    wins: {
      type: Number,
      required: true,
      default: 0,
    },
    losses: {
      type: Number,
      required: true,
      default: 0,
    },
    ties: {
      type: Number,
      required: true,
      default: 0,
    }
  },
});

FriendSchema.index({ userId: -1, friendId: -1 }, { unique: true });

module.exports = mongoose.model('Friend', FriendSchema);
<|endoftext|>


<commit_before>(function() {
	function convertToPixels(_str, _context) {
		if (/px$/.test(_str)) {
			return parseFloat(_str);
		}

		if (!_context) {
			context = document.body;
		}

		var tmp = document.createElement('div');
		tmp.style.visbility = 'hidden';
		tmp.style.position = 'absolute';
		tmp.style.lineHeight = '0';

		if (/%$/.test(_str)) {
			context = _context.parentNode || _context;
			tmp.style.height = _str;
		} else {
			tmp.style.borderStyle = 'solid';
			tmp.style.borderBottomWidth = '0';
			tmp.style.borderTopWidth = _str;
		}

		context.appendChild(tmp);
		var px = tmp.offsetHeight;
		_context.removeChild(tmp);

		return px + 'px';
	}

	// TODO fix for IE
	var original_top_margin = window.getComputedStyle(document.body).getPropertyValue("margin-top");
	var message = 'You are using an out-of-date version of <%= browser_name %> (<%= version.string %>). It is encouraged that you update your browser. <a href="<%= read_param(:link) || default_info_link %>?for=<%= browser %>&version=<%= version.string %>" style="color:#8B0000">Click here for more details.';

	var alert_box = document.createElement('div');
	alert_box.id = 'bad-browser-warning';
	alert_box.innerHTML	= '
'+ message +'x
'

	document.body.appendChild(alert_box);
	document.body.style.marginTop = (parseFloat(convertToPixels(original_top_margin)) + 25) + 'px';

	document.getElementById('bad-browser-warning-remove').onclick = function() {
		var alert_box = document.getElementById('bad-browser-warning');

		document.body.removeChild(alert_box);
		document.body.style.margin = original_top_margin;
	}
;}());
<commit_msg>fix ie<commit_after>(function() {
	function getTopMargin(){
		if (window.getComputedStyle)
			return window.getComputedStyle(document.body).getPropertyValue("margin-top");

		var margin = document.body.currentStyle['margin'];
		var match = margin.match(/^\d+/)
		return match ? margin[0] : '0px';
	}

	function convertToPixels(_str, _context) {
		if (/px$/.test(_str)) {
			return parseFloat(_str);
		}

		if (!_context) {
			context = document.body;
		}

		var tmp = document.createElement('div');
		tmp.style.visbility = 'hidden';
		tmp.style.position = 'absolute';
		tmp.style.lineHeight = '0';

		if (/%$/.test(_str)) {
			context = _context.parentNode || _context;
			tmp.style.height = _str;
		} else {
			tmp.style.borderStyle = 'solid';
			tmp.style.borderBottomWidth = '0';
			tmp.style.borderTopWidth = _str;
		}

		context.appendChild(tmp);
		var px = tmp.offsetHeight;
		_context.removeChild(tmp);

		return px + 'px';
	}

	// TODO fix for IE
	var original_top_margin = getTopMargin();
	var message = 'You are using an out-of-date version of <%= browser_name %> (<%= version.string %>). It is encouraged that you update your browser. <a href="<%= read_param(:link) || default_info_link %>?for=<%= browser %>&version=<%= version.string %>" style="color:#8B0000">Click here for more details.';

	var alert_box = document.createElement('div');
	alert_box.id = 'bad-browser-warning';
	alert_box.innerHTML	= '
'+ message +'x
'

	document.body.appendChild(alert_box);
	document.body.style.marginTop = (parseFloat(convertToPixels(original_top_margin)) + 25) + 'px';

	document.getElementById('bad-browser-warning-remove').onclick = function() {
		var alert_box = document.getElementById('bad-browser-warning');

		document.body.removeChild(alert_box);
		document.body.style.margin = original_top_margin;
	}
;}());
<|endoftext|>