<commit_before>/*
 * Copyright 2017 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-ul
 *
 * iotagent-ul is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-ul is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-ul.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[iot_support@tid.es]
 *
 * Modified by: Fernando Méndez, Daniel Calvo - ATOS Research & Innovation
 */

/* eslint-disable no-unused-vars */

const iotagentUl = require('../../../');
const config = require('./config-test.js');
const nock = require('nock');
const async = require('async');
const utils = require('../../utils');
const request = utils.request;
const iotAgentLib = require('iotagent-node-lib');
const amqp = require('amqplib/callback_api');
const apply = async.apply;
let contextBrokerMock;
let contextBrokerUnprovMock;
let amqpConn;
let channel;

function startConnection(exchange, callback) {
    amqp.connect('amqp://localhost', function (err, conn) {
        amqpConn = conn;

        conn.createChannel(function (err, ch) {
            ch.assertExchange(exchange, 'topic', {});

            channel = ch;
            callback(err);
        });
    });
}

describe('AMQP Transport binding: measures', function () {
    beforeEach(function (done) {
        const provisionOptions = {
            url: 'http://localhost:' + config.iota.server.port + '/iot/devices',
            method: 'POST',
            json: utils.readExampleFile('./test/unit/ngsiv2/deviceProvisioning/provisionDeviceAMQP1.json'),
            headers: {
                'fiware-service': 'smartgondor',
                'fiware-servicepath': '/gardens'
            }
        };

        nock.cleanAll();

        // This mock does not check the payload since the aim of the test is not to verify
        // device provisioning functionality. Appropriate verification is done in tests under
        // provisioning folder of iotagent-node-lib
        contextBrokerMock = nock('http://192.168.1.1:1026')
            .matchHeader('fiware-service', 'smartgondor')
            .matchHeader('fiware-servicepath', '/gardens')
            .post('/v2/entities?options=upsert')
            .reply(204);

        async.series(
            [
                apply(iotagentUl.start, config),
                apply(utils.request, provisionOptions),
                apply(startConnection, config.amqp.exchange)
            ],
            done
        );
    });

    afterEach(function (done) {
        nock.cleanAll();

        amqpConn.close();

        async.series([iotAgentLib.clearAll, iotagentUl.stop], done);
    });

    describe('When a new single measure arrives to a Device routing key', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/singleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send a new update context request to the Context Broker with just that attribute', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs.temperature', Buffer.from('23'));

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });

    describe('When a new measure arrives for an unprovisioned Device', function () {
        const groupCreation = {
            url: 'http://localhost:4061/iot/services',
            method: 'POST',
            json: utils.readExampleFile('./test/unit/ngsiv2/groupProvisioning/provisionFullGroupAMQP.json'),
            headers: {
                'fiware-service': 'TestService',
                'fiware-servicepath': '/testingPath'
            }
        };

        beforeEach(function (done) {
            // This mock does not check the payload since the aim of the test is not to verify
            // device provisioning functionality. Appropriate verification is done in tests under
            // provisioning folder of iotagent-node-lib
            contextBrokerUnprovMock = nock('http://unexistentHost:1026')
                .matchHeader('fiware-service', 'TestService')
                .matchHeader('fiware-servicepath', '/testingPath')
                .post('/v2/entities?options=upsert')
                .reply(204);

            contextBrokerUnprovMock
                .matchHeader('fiware-service', 'TestService')
                .matchHeader('fiware-servicepath', '/testingPath')
                .patch(
                    '/v2/entities/SensorMachine:UL_UNPROVISIONED/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/unprovisionedMeasure.json')
                )
                .query({ type: 'SensorMachine' })
                .reply(204);

            request(groupCreation, function (error, response, body) {
                done();
            });
        });

        it('should send a new update context request to the Context Broker with just that attribute', function (done) {
            channel.publish(
                config.amqp.exchange,
                '.80K09H324HV8732.UL_UNPROVISIONED.attrs.temperature',
                Buffer.from('23')
            );

            setTimeout(function () {
                contextBrokerUnprovMock.done();
                done();
            }, 100);
        });
    });

    describe('When a new multiple measure arrives to a Device routing key with one measure', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/singleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send a single update context request with all the attributes', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs', Buffer.from('temperature|23'));

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });

    describe('When a new multiple measure arrives to a Device routing key with a faulty payload', function () {
        it('should silently ignore the error (without crashing)', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs', Buffer.from('notAULPayload '));

            setTimeout(function () {
                // FIXME: nothing to check in this case except expectations in log file.
                // However, currently we doen't have a mechanism to check expentations in log files,
                // so this marked for future improvement.
                done();
            }, 100);
        });
    });

    describe('When single message with multiple measures arrive to a Device routing key', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send one update context per measure group to the Contet Broker', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs', Buffer.from('temperature|23|humidity|98'));

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });

    describe('When a message with multiple measure groups arrives to a Device routing key', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/singleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);

            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/secondSingleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send a two update context requests to the Context Broker one with each attribute', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs', Buffer.from('temperature|23#humidity|98'));

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });
    describe('When multiple groups of measures arrive, with multiple attributes, to a Device routing key', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);

            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/secondMultipleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send a two update context requests to the Context Broker one with each attribute', function (done) {
            channel.publish(
                config.amqp.exchange,
                '.1234.MQTT_2.attrs',
                Buffer.from('temperature|23|humidity|98#temperature|16|humidity|34')
            );

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });

    describe('When a measure with a timestamp arrives with an alias to TimeInstant', function () {
        const provisionProduction = {
            url: 'http://localhost:' + config.iota.server.port + '/iot/devices',
            method: 'POST',
            json: utils.readExampleFile('./test/unit/ngsiv2/deviceProvisioning/provisionTimeInstant.json'),
            headers: {
                'fiware-service': 'smartgondor',
                'fiware-servicepath': '/gardens'
            }
        };

        beforeEach(function (done) {
            // This mock does not check the payload since the aim of the test is not to verify
            // device provisioning functionality. Appropriate verification is done in tests under
            // provisioning folder of iotagent-node-lib
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .post('/v2/entities?options=upsert')
                .reply(204)
                .patch(
                    '/v2/entities/TimeInstant%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/timeInstantDuplicated.json')
                )
                .query({ type: 'clock' })
                .reply(204);

            config.iota.timestamp = true;

            nock('http://localhost:8082').post('/protocols').reply(200, {});

            iotagentUl.stop(function () {
                iotagentUl.start(config, function (error) {
                    request(provisionProduction, function (error, response, body) {
                        done();
                    });
                });
            });
        });

        afterEach(function () {
            config.iota.timestamp = false;
        });

        it('should use the provided TimeInstant as the general timestamp for the measures', function (done) {
            channel.publish(
                config.amqp.exchange,
                '.1234.timestampedDevice.attrs',
                Buffer.from('tmp|24.4|tt|2016-09-26T12:19:26.476659Z')
            );

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });
});
<commit_msg>Update test/unit/ngsiv2/amqpBinding-test.js<commit_after>/*
 * Copyright 2017 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-ul
 *
 * iotagent-ul is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-ul is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-ul.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[iot_support@tid.es]
 *
 * Modified by: Fernando Méndez, Daniel Calvo - ATOS Research & Innovation
 */

/* eslint-disable no-unused-vars */

const iotagentUl = require('../../../');
const config = require('./config-test.js');
const nock = require('nock');
const async = require('async');
const utils = require('../../utils');
const request = utils.request;
const iotAgentLib = require('iotagent-node-lib');
const amqp = require('amqplib/callback_api');
const apply = async.apply;
let contextBrokerMock;
let contextBrokerUnprovMock;
let amqpConn;
let channel;

function startConnection(exchange, callback) {
    amqp.connect('amqp://localhost', function (err, conn) {
        amqpConn = conn;

        conn.createChannel(function (err, ch) {
            ch.assertExchange(exchange, 'topic', {});

            channel = ch;
            callback(err);
        });
    });
}

describe('AMQP Transport binding: measures', function () {
    beforeEach(function (done) {
        const provisionOptions = {
            url: 'http://localhost:' + config.iota.server.port + '/iot/devices',
            method: 'POST',
            json: utils.readExampleFile('./test/unit/ngsiv2/deviceProvisioning/provisionDeviceAMQP1.json'),
            headers: {
                'fiware-service': 'smartgondor',
                'fiware-servicepath': '/gardens'
            }
        };

        nock.cleanAll();

        // This mock does not check the payload since the aim of the test is not to verify
        // device provisioning functionality. Appropriate verification is done in tests under
        // provisioning folder of iotagent-node-lib
        contextBrokerMock = nock('http://192.168.1.1:1026')
            .matchHeader('fiware-service', 'smartgondor')
            .matchHeader('fiware-servicepath', '/gardens')
            .post('/v2/entities?options=upsert')
            .reply(204);

        async.series(
            [
                apply(iotagentUl.start, config),
                apply(request, provisionOptions),
                apply(startConnection, config.amqp.exchange)
            ],
            done
        );
    });

    afterEach(function (done) {
        nock.cleanAll();

        amqpConn.close();

        async.series([iotAgentLib.clearAll, iotagentUl.stop], done);
    });

    describe('When a new single measure arrives to a Device routing key', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/singleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send a new update context request to the Context Broker with just that attribute', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs.temperature', Buffer.from('23'));

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });

    describe('When a new measure arrives for an unprovisioned Device', function () {
        const groupCreation = {
            url: 'http://localhost:4061/iot/services',
            method: 'POST',
            json: utils.readExampleFile('./test/unit/ngsiv2/groupProvisioning/provisionFullGroupAMQP.json'),
            headers: {
                'fiware-service': 'TestService',
                'fiware-servicepath': '/testingPath'
            }
        };

        beforeEach(function (done) {
            // This mock does not check the payload since the aim of the test is not to verify
            // device provisioning functionality. Appropriate verification is done in tests under
            // provisioning folder of iotagent-node-lib
            contextBrokerUnprovMock = nock('http://unexistentHost:1026')
                .matchHeader('fiware-service', 'TestService')
                .matchHeader('fiware-servicepath', '/testingPath')
                .post('/v2/entities?options=upsert')
                .reply(204);

            contextBrokerUnprovMock
                .matchHeader('fiware-service', 'TestService')
                .matchHeader('fiware-servicepath', '/testingPath')
                .patch(
                    '/v2/entities/SensorMachine:UL_UNPROVISIONED/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/unprovisionedMeasure.json')
                )
                .query({ type: 'SensorMachine' })
                .reply(204);

            request(groupCreation, function (error, response, body) {
                done();
            });
        });

        it('should send a new update context request to the Context Broker with just that attribute', function (done) {
            channel.publish(
                config.amqp.exchange,
                '.80K09H324HV8732.UL_UNPROVISIONED.attrs.temperature',
                Buffer.from('23')
            );

            setTimeout(function () {
                contextBrokerUnprovMock.done();
                done();
            }, 100);
        });
    });

    describe('When a new multiple measure arrives to a Device routing key with one measure', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/singleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send a single update context request with all the attributes', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs', Buffer.from('temperature|23'));

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });

    describe('When a new multiple measure arrives to a Device routing key with a faulty payload', function () {
        it('should silently ignore the error (without crashing)', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs', Buffer.from('notAULPayload '));

            setTimeout(function () {
                // FIXME: nothing to check in this case except expectations in log file.
                // However, currently we doen't have a mechanism to check expentations in log files,
                // so this marked for future improvement.
                done();
            }, 100);
        });
    });

    describe('When single message with multiple measures arrive to a Device routing key', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send one update context per measure group to the Contet Broker', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs', Buffer.from('temperature|23|humidity|98'));

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });

    describe('When a message with multiple measure groups arrives to a Device routing key', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/singleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);

            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/secondSingleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send a two update context requests to the Context Broker one with each attribute', function (done) {
            channel.publish(config.amqp.exchange, '.1234.MQTT_2.attrs', Buffer.from('temperature|23#humidity|98'));

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });
    describe('When multiple groups of measures arrive, with multiple attributes, to a Device routing key', function () {
        beforeEach(function () {
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);

            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .patch(
                    '/v2/entities/Second%20UL%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/secondMultipleMeasure.json')
                )
                .query({ type: 'AnMQTTDevice' })
                .reply(204);
        });

        it('should send a two update context requests to the Context Broker one with each attribute', function (done) {
            channel.publish(
                config.amqp.exchange,
                '.1234.MQTT_2.attrs',
                Buffer.from('temperature|23|humidity|98#temperature|16|humidity|34')
            );

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });

    describe('When a measure with a timestamp arrives with an alias to TimeInstant', function () {
        const provisionProduction = {
            url: 'http://localhost:' + config.iota.server.port + '/iot/devices',
            method: 'POST',
            json: utils.readExampleFile('./test/unit/ngsiv2/deviceProvisioning/provisionTimeInstant.json'),
            headers: {
                'fiware-service': 'smartgondor',
                'fiware-servicepath': '/gardens'
            }
        };

        beforeEach(function (done) {
            // This mock does not check the payload since the aim of the test is not to verify
            // device provisioning functionality. Appropriate verification is done in tests under
            // provisioning folder of iotagent-node-lib
            contextBrokerMock
                .matchHeader('fiware-service', 'smartgondor')
                .matchHeader('fiware-servicepath', '/gardens')
                .post('/v2/entities?options=upsert')
                .reply(204)
                .patch(
                    '/v2/entities/TimeInstant%20Device/attrs',
                    utils.readExampleFile('./test/unit/ngsiv2/contextRequests/timeInstantDuplicated.json')
                )
                .query({ type: 'clock' })
                .reply(204);

            config.iota.timestamp = true;

            nock('http://localhost:8082').post('/protocols').reply(200, {});

            iotagentUl.stop(function () {
                iotagentUl.start(config, function (error) {
                    request(provisionProduction, function (error, response, body) {
                        done();
                    });
                });
            });
        });

        afterEach(function () {
            config.iota.timestamp = false;
        });

        it('should use the provided TimeInstant as the general timestamp for the measures', function (done) {
            channel.publish(
                config.amqp.exchange,
                '.1234.timestampedDevice.attrs',
                Buffer.from('tmp|24.4|tt|2016-09-26T12:19:26.476659Z')
            );

            setTimeout(function () {
                contextBrokerMock.done();
                done();
            }, 100);
        });
    });
});
<|endoftext|>


<commit_before>/**
 * Graph.js
 *
 * @author Andrew Bowler, Alberto Gomez-Estrada
 */

'use strict';

var React = require('react');
var ReactDOM = require('react-dom');
var Vis = require('vis');

var Graph = React.createClass({
  componentDidMount: function() {
    var element = ReactDOM.findDOMNode(this);
    this.setState({
      network: new Vis.Network(element, this.props.graphData, this.props.options)
    });
  },

  shouldComponentUpdate: function(nextProps, nextState) {
    return (
      this.props.totalChunks != nextProps.currentChunk
      || this.props.graphData != nextProps.graphData
    );
  },

  componentDidUpdate: function() {
    this.state.network.setData({
      nodes: this.props.graphData.nodes,
      edges: this.props.graphData.edges
    });

    this.state.network.on('selectNode', function(event) {
      var nodeID = event.nodes[0];
      var node = this.state.network.body.data.nodes.get(nodeID);
      this.props.updateSelectedNode(node);
    }.bind(this));

    // Called when Vis is finished drawing the graph
    this.state.network.on('afterDrawing', function(event) {
      this.props.logger('Graph finished drawing');
    }.bind(this));
  },

  getDefaultProps: function() {
    return {
      options: {
        nodes: {
          shape: 'dot',
          fixed: true
        },
        edges: {
          arrows: {
            to: {
              scaleFactor: 0.5
            }
          },
          smooth: {
            type: 'continuous'
          }
        },
        physics: false,
        interaction: {
          dragNodes: false,
        }
      }
    }
  },

  getInitialState: function() {
    return {
      network: {}
    };
  },

  render: function() {
    return (

);
  }
});

module.exports = Graph;
<commit_msg>Re-enable physics, otherwise chunking causes the data to overlap each other.<commit_after>/**
 * Graph.js
 *
 * @author Andrew Bowler, Alberto Gomez-Estrada
 */

'use strict';

var React = require('react');
var ReactDOM = require('react-dom');
var Vis = require('vis');

var Graph = React.createClass({
  componentDidMount: function() {
    var element = ReactDOM.findDOMNode(this);
    this.setState({
      network: new Vis.Network(element, this.props.graphData, this.props.options)
    });
  },

  shouldComponentUpdate: function(nextProps, nextState) {
    return (
      this.props.totalChunks != nextProps.currentChunk
      || this.props.graphData != nextProps.graphData
    );
  },

  componentDidUpdate: function() {
    this.state.network.setData({
      nodes: this.props.graphData.nodes,
      edges: this.props.graphData.edges
    });

    this.state.network.on('selectNode', function(event) {
      var nodeID = event.nodes[0];
      var node = this.state.network.body.data.nodes.get(nodeID);
      this.props.updateSelectedNode(node);
    }.bind(this));

    // Called when Vis is finished drawing the graph
    this.state.network.on('afterDrawing', function(event) {
      this.props.logger('Graph finished drawing');
    }.bind(this));
  },

  getDefaultProps: function() {
    return {
      options: {
        nodes: {
          shape: 'dot',
          size: 10
        },
        edges: {
          arrows: {
            to: {
              scaleFactor: 0.5
            }
          },
          smooth: {
            type: 'continuous'
          }
        },
        physics: {
          repulsion: {
            nodeDistance: 200,
            springLength: 10
          }
        },
        interaction: {
          dragNodes: false,
        }
      }
    }
  },

  getInitialState: function() {
    return {
      network: {}
    };
  },

  render: function() {
    return (

);
  }
});

module.exports = Graph;
<|endoftext|>


<commit_before>import bcrypt from 'bcrypt';
import config from './config';
import log from './logger';
import passport from 'passport';
import url from 'url';
import Users from './data/users.table';

import { Strategy as LocalStrategy } from 'passport-local';
import { Strategy as GoogleStrategy } from 'passport-google-oauth';
import { Strategy as GithubStrategy } from 'passport-github';

export default function(app) {
	passport.use(
		new LocalStrategy(
			(username, password, done) => {
				Users
					.query(username)
					.usingIndex('UserNameIndex')
					.limit(1)
					.execAsync()
					.then(response => {
						if (response.Items.length === 0) {
							log.info('Could not log in user "', username, '". User does not exist');
							return done(null, null);
						}

						const result = response.Items[0];

						log.debug('User:', result.attrs);
						if (!bcrypt.compareSync(password, result.get('passwordHash'))) {
							log.info('Could not log in user "', username, '". Password was invalid.');
							return done(null, null);
						}

						done(null, result.attrs);
					})
					.catch(done);
			}));

	passport.use(
		new GoogleStrategy(
			{
				consumerKey: config.auth.google.consumerId,
				consumerSecret: config.auth.google.consumerSecret,
				callbackURL: url.resolve(config.baseUrl, '/api/1.0/auth/google/callback')
			},
			(token, tokenSecret, profile, done) => {
				done();
			}));

	passport.use(
		new GithubStrategy(
			{
				clientID: config.auth.github.clientId,
				clientSecret: config.auth.github.clientSecret,
				callbackURL: url.resolve(config.baseUrl, '/api/1.0/auth/github/callback')
			},
			(accessToken, refreshToken, profile, done) => {
				return done();
			}));

	passport.serializeUser((user, done) => {
		log.trace('Serializing user:', user);
		done(null, user.userId);
	});

	passport.deserializeUser((userId, done) => {
		log.trace('Deserializing session user:', userId);
		Users.getAsync(userId)
			.then(result => {
				if (!result) {
					return done(null, null);
				}

				const user = {
					userId: result.get('userId'),
					userName: result.get('userName'),
					email: result.get('email'),
					displayName: result.get('displayName'),
					role: result.get('role'),
					createdAt: result.get('createdAt')
				};

				done(null, user);
			})
			.catch(done);
	});

	app.use(passport.initialize());
	app.use(passport.session());
}
<commit_msg>Deleting logging line that should not be there.<commit_after>import bcrypt from 'bcrypt';
import config from './config';
import log from './logger';
import passport from 'passport';
import url from 'url';
import Users from './data/users.table';

import { Strategy as LocalStrategy } from 'passport-local';
import { Strategy as GoogleStrategy } from 'passport-google-oauth';
import { Strategy as GithubStrategy } from 'passport-github';

export default function(app) {
	passport.use(
		new LocalStrategy(
			(username, password, done) => {
				Users
					.query(username)
					.usingIndex('UserNameIndex')
					.limit(1)
					.execAsync()
					.then(response => {
						if (response.Items.length === 0) {
							log.info('Could not log in user "', username, '". User does not exist');
							return done(null, null);
						}

						const result = response.Items[0];

						if (!bcrypt.compareSync(password, result.get('passwordHash'))) {
							log.info('Could not log in user "', username, '". Password was invalid.');
							return done(null, null);
						}

						done(null, result.attrs);
					})
					.catch(done);
			}));

	passport.use(
		new GoogleStrategy(
			{
				consumerKey: config.auth.google.consumerId,
				consumerSecret: config.auth.google.consumerSecret,
				callbackURL: url.resolve(config.baseUrl, '/api/1.0/auth/google/callback')
			},
			(token, tokenSecret, profile, done) => {
				done();
			}));

	passport.use(
		new GithubStrategy(
			{
				clientID: config.auth.github.clientId,
				clientSecret: config.auth.github.clientSecret,
				callbackURL: url.resolve(config.baseUrl, '/api/1.0/auth/github/callback')
			},
			(accessToken, refreshToken, profile, done) => {
				return done();
			}));

	passport.serializeUser((user, done) => {
		log.trace('Serializing user:', user);
		done(null, user.userId);
	});

	passport.deserializeUser((userId, done) => {
		log.trace('Deserializing session user:', userId);
		Users.getAsync(userId)
			.then(result => {
				if (!result) {
					return done(null, null);
				}

				const user = {
					userId: result.get('userId'),
					userName: result.get('userName'),
					email: result.get('email'),
					displayName: result.get('displayName'),
					role: result.get('role'),
					createdAt: result.get('createdAt')
				};

				done(null, user);
			})
			.catch(done);
	});

	app.use(passport.initialize());
	app.use(passport.session());
}
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var request = require('request-promise');
var Handlebars = require('handlebars');
var moment = require('moment');
var sources = require('../lib/sources');
var marked = require('marked');
var zlib = require('zlib');
var PolyfillSet = require('./PolyfillSet');
var polyfillservice = require('../lib');
var compatdata = require('../docs/assets/compat.json');
var extend = require('lodash').extend;

var cache = {};
var cachettl = 1800;

// Pre-cache page templates and partials
var templates = ['index', 'usage', 'compat', 'api', 'examples', 'contributing'].reduce(function(map, temName) {
	map[temName] = Handlebars.compile(
		fs.readFileSync(path.join(__dirname, '/../docs/' + temName + '.html'), {encoding: 'UTF-8'})
	);
	return map;
}, {});
['header', 'footer', 'nav'].forEach(function(partialName) {
	Handlebars.registerPartial(partialName, Handlebars.compile(
		fs.readFileSync(path.join(__dirname, '/../docs/'+partialName+'.html'), {encoding: 'UTF-8'})
	));
});

// Register template helpers
Handlebars.registerHelper("prettifyDate", function(timestamp) {
     return moment(timestamp*1000).format("D MMM YYYY HH:mm");
});
Handlebars.registerHelper("prettifyDuration", function(seconds) {
     return seconds ? moment.duration(seconds*1000).humanize() : 'None';
});
Handlebars.registerHelper('sectionHighlight', function(section, options) {
	return (section === options.hash.name) ? new Handlebars.SafeString(' aria-selected="true"') : '';
});
Handlebars.registerHelper('dispBytes', function(bytes) {
	var i = 0;
	var byteUnits = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
	do {
		bytes /= 1024;
		i++;
	} while (bytes > 1024);

	return new Handlebars.SafeString(Math.max(bytes, 0.1).toFixed(1) + byteUnits[i]);
});
Handlebars.registerHelper('lower', function(str) {
	return str.toLowerCase();
});

function getData(type) {
	var handlers = {
		fastly: function() {
			if (!process.env.FASTLY_SERVICE_ID) return Promise.reject("Fastly environment vars not set");
			return request({
				url: 'https://api.fastly.com/stats/service/' + process.env.FASTLY_SERVICE_ID + '?from=7 days ago&to=2 hours ago&by=hour',
				headers: { 'fastly-key': process.env.FASTLY_API_KEY },
			}).then(function (response) {
				var rollup = {requests:0, hits:0, miss:0, bandwidth:0};
				var data = (response && JSON.parse(response)) || {data:[]};
				var byhour = data.data.map(function(result) {
					rollup.requests += result.requests;
					rollup.hits += result.hits;
					rollup.miss += result.miss;
					rollup.bandwidth += result.bandwidth;
					return {date: result.start_time, requests:result.requests, hits:result.hits, miss:result.miss};
				});
				return {byhour:byhour, rollup:rollup};
			});
		},
		respTimes: function() {
			if (!process.env.PINGDOM_CHECK_ID) return Promise.reject("Pingdom environment vars not set");
			var to = ((new Date()).getTime()/1000) - 3600;
			var from = to - (60*60*24*7);
			return request({
				url: 'https://api.pingdom.com/api/2.0/summary.performance/' + process.env.PINGDOM_CHECK_ID + '?from='+from+'&to='+to+'&resolution=hour',
				headers: {
					'app-key': process.env.PINGDOM_API_KEY,
					'Account-Email': process.env.PINGDOM_ACCOUNT
				},
				auth: {
					user: process.env.PINGDOM_USERNAME,
					pass: process.env.PINGDOM_PASSWORD
				}
			}).then(function (response) {
				var data = (response && JSON.parse(response)) || [];
				if (!data.summary) {
					data = {summary:{hours:[]}};
				}
				return data.summary.hours.map(function(result) {
					return {date: result.starttime, respTime: result.avgresponse};
				});
			});
		},
		outages: function() {
			if (!process.env.PINGDOM_CHECK_ID) return Promise.reject("Pingdom environment vars not set");
			var data = {};
			var periods = {
				"30d": 60*60*24*30,
				"3m": (60*60*24*365) / 4,
				"12m": (60*60*24*365)
			};
			var end = ((new Date()).getTime()/1000) - 3600;  // Ignore the last hour (Pingdom data processing delay)
			return Promise.all(Object.keys(periods).map(function(period) {
				var start = end - periods[period];
				return request({
					url: 'https://api.pingdom.com/api/2.0/summary.average/' + process.env.PINGDOM_CHECK_ID + '?from='+start+'&to='+end+'&includeuptime=true',
					headers: {
						'app-key': process.env.PINGDOM_API_KEY,
						'Account-Email': process.env.PINGDOM_ACCOUNT
					},
					auth: {
						user: process.env.PINGDOM_USERNAME,
						pass: process.env.PINGDOM_PASSWORD
					}
				}).then(function (response) {
					response = JSON.parse(response);
					data[period] = response.summary.status.totaldown;
				});
			})).then(function() {
				return data;
			}).catch(function(e) {
				console.log(e.stack || e);
			});
		},
		sizes: function() {

			// Define a list of all the browsers in which we tested one of the polyfills
			var firstfeature = compatdata[Object.keys(compatdata)[0]];
			var UAs = Object.keys(firstfeature).reduce(function(UAs, family) {
				UAs = UAs.concat(Object.keys(firstfeature[family]).map(function(uaVersion) {
					return {family: family, ver: uaVersion};
				}));
				return UAs;
			}, []);

			// For each of these browsers, get a bundle and report its size
			return Promise.all(UAs.map(function(browser) {
				var opts = {
					features: PolyfillSet.fromQueryParam('default').get(),
					uaString: browser.family+'/'+browser.ver,
				};
				return Promise.all(
					polyfillservice.getPolyfillString(extend({minify: true}, opts)),
					polyfillservice.getPolyfillString(extend({minify: false}, opts))
				).then(function (srcs) {
					var item = {
						family: browser.family,
						ver: browser.ver,
						minsrc: srcs[0],
						rawbytes: srcs[1].length,
						minbytes: srcs[0].length
					};
					return new Promise(function(resolve, reject) {
						zlib.gzip(item.minsrc, function(err, gzipsrc) {
							if (!err) {
								item.gzipbytes = gzipsrc.length;
							}
							resolve(item);
						});
					});
				});
			}));
		}
	};

	if (cache.hasOwnProperty(type) && cache[type].creationTime > ((new Date()).getTime() - (cachettl*1000)) ) {
		return cache[type].promise;
	} else {
		cache[type] = {creationTime: (new Date()).getTime()};
		try {
			return cache[type].promise = handlers[type]();
		} catch(err) {
			return cache[type].promise = Promise.reject(err.toString());
		}
	}
}

function getCompat() {
	var sourceslib = sources.latest;
	var browsers = ['ie', 'firefox', 'chrome', 'safari', 'opera', 'ios_saf'];
	var msgs = {
		'native': 'Supported natively',
		'polyfilled': 'Supported with polyfill service',
		'missing': 'Not supported'
	};
	return Promise.all(Object.keys(compatdata)
		.filter(function(feature) {
			return sourceslib.polyfillExistsSync(feature) && feature.indexOf('_') !== 0;
		})
		.sort()
		.map(function(feat) {
			return sourceslib.getPolyfill(feat).then(function(polyfill) {
				var fdata = {
					feature: feat,
					slug: feat.replace(/\./g, '_'),
					size: Object.keys(polyfill.variants).reduce(function(size, variantName) {
						return Math.max(size, polyfill.variants[variantName].minSource.length);
					}, 0),
					isDefault: (polyfill.aliases && polyfill.aliases.indexOf('default') !== -1),
					hasTests: polyfill.hasTests,
					docs: polyfill.docs,
					baseDir: polyfill.baseDir.replace(path.resolve(path.join(__dirname, '..')), ''),
					spec: polyfill.spec,
					notes: polyfill.notes ? polyfill.notes.map(function (n) { return marked(n); }) : [],
					license: polyfill.variants.default.license,
					licenseIsUrl: polyfill.variants.default.license && polyfill.variants.default.license.length > 5
				};
				browsers.forEach(function(browser) {
					if (compatdata[feat][browser]) {
						fdata[browser] = [];
						Object.keys(compatdata[feat][browser]).sort(function(a, b) {
							if (isNaN(a)) {
								return 1;
							} else if (isNaN(b)) {
								return -1;
							} else {
								return (parseFloat(a) < parseFloat(b)) ? -1 : 1;
							}
						}).forEach(function(version) {
							fdata[browser].push({
								status: compatdata[feat][browser][version],
								statusMsg: msgs[compatdata[feat][browser][version]],
								version: version
							});
						});
					}
				});
				return fdata;
			});
		})
	);
}

// Quick helper for Promise.all to spread results over separate arguments rather than an array
function spread(fn) {
	return function(results) {
		fn.apply(fn, results);
	};
}

function route(req, res, next) {
	if (req.path.length < "/v1/docs/".length) {
		return res.redirect('/v1/docs/');
	}

	if (!req.params || !req.params[0]) {
		res.send(templates.index({section: 'index'}));

	} else if (req.params[0] === 'usage') {
		// Set the ttl to one hour for the usage page so the graphs are
		// updated more frequently, overriding the default cache-control
		// behaviour set in index.js
		var one_hour = 60 * 60;
		var one_week = one_hour * 24 * 7;
		res.set('Cache-Control', 'public, max-age=' + one_hour +', stale-while-revalidate=' + one_week + ', stale-if-error=' + one_week);
		Promise.all([getData('fastly'), getData('outages'), getData('respTimes')]).then(spread(function(fastly, outages, respTimes) {
			res.send(templates.usage({
				section: 'usage',
				requestsData: fastly.byhour,
				downtime: outages,
				respTimes: respTimes,
				hitCount: fastly.rollup.hits,
				missCount: fastly.rollup.miss
			}));
		})).catch(function(rejectReason) {
			res.send(templates.usage({
				section: 'usage',
				msg: rejectReason.toString()
			}));
		});

	} else if (req.params[0] === 'features') {
		Promise.all([getData('sizes'), getCompat()]).then(function(results) {
			var sizes = results[0];
			var compat = results[1];
			res.send(templates.compat({
				section: 'features',
				compat: compat,
				sizes: sizes
			}));
		}).catch(function (err) {
			console.log (err.stack || err);
		});

	} else if (req.params[0] === 'api') {
		res.send(templates.api({
			hasVersions: sources.listVersions().length > 1,
			versions: sources.listVersions(),
			section: 'api'
		}));

	} else if (req.params[0] === 'examples') {

		res.send(templates.examples({section: 'examples'}));

	} else if (req.params[0] === 'contributing') {
		res.send(templates.contributing({section: 'contributing', baselines: require('../lib/UA').getBaselines()}));

	} else {
		next();
	}
}

module.exports = {
	route: route
};
<commit_msg>Use spread for improved semantics (could upgrade to ES6 with use of preprocessor...)<commit_after>var fs = require('fs');
var path = require('path');
var request = require('request-promise');
var Handlebars = require('handlebars');
var moment = require('moment');
var sources = require('../lib/sources');
var marked = require('marked');
var zlib = require('zlib');
var PolyfillSet = require('./PolyfillSet');
var polyfillservice = require('../lib');
var compatdata = require('../docs/assets/compat.json');
var extend = require('lodash').extend;

var cache = {};
var cachettl = 1800;

// Pre-cache page templates and partials
var templates = ['index', 'usage', 'compat', 'api', 'examples', 'contributing'].reduce(function(map, temName) {
	map[temName] = Handlebars.compile(
		fs.readFileSync(path.join(__dirname, '/../docs/' + temName + '.html'), {encoding: 'UTF-8'})
	);
	return map;
}, {});
['header', 'footer', 'nav'].forEach(function(partialName) {
	Handlebars.registerPartial(partialName, Handlebars.compile(
		fs.readFileSync(path.join(__dirname, '/../docs/'+partialName+'.html'), {encoding: 'UTF-8'})
	));
});

// Register template helpers
Handlebars.registerHelper("prettifyDate", function(timestamp) {
     return moment(timestamp*1000).format("D MMM YYYY HH:mm");
});
Handlebars.registerHelper("prettifyDuration", function(seconds) {
     return seconds ? moment.duration(seconds*1000).humanize() : 'None';
});
Handlebars.registerHelper('sectionHighlight', function(section, options) {
	return (section === options.hash.name) ? new Handlebars.SafeString(' aria-selected="true"') : '';
});
Handlebars.registerHelper('dispBytes', function(bytes) {
	var i = 0;
	var byteUnits = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
	do {
		bytes /= 1024;
		i++;
	} while (bytes > 1024);

	return new Handlebars.SafeString(Math.max(bytes, 0.1).toFixed(1) + byteUnits[i]);
});
Handlebars.registerHelper('lower', function(str) {
	return str.toLowerCase();
});

function getData(type) {
	var handlers = {
		fastly: function() {
			if (!process.env.FASTLY_SERVICE_ID) return Promise.reject("Fastly environment vars not set");
			return request({
				url: 'https://api.fastly.com/stats/service/' + process.env.FASTLY_SERVICE_ID + '?from=7 days ago&to=2 hours ago&by=hour',
				headers: { 'fastly-key': process.env.FASTLY_API_KEY },
			}).then(function (response) {
				var rollup = {requests:0, hits:0, miss:0, bandwidth:0};
				var data = (response && JSON.parse(response)) || {data:[]};
				var byhour = data.data.map(function(result) {
					rollup.requests += result.requests;
					rollup.hits += result.hits;
					rollup.miss += result.miss;
					rollup.bandwidth += result.bandwidth;
					return {date: result.start_time, requests:result.requests, hits:result.hits, miss:result.miss};
				});
				return {byhour:byhour, rollup:rollup};
			});
		},
		respTimes: function() {
			if (!process.env.PINGDOM_CHECK_ID) return Promise.reject("Pingdom environment vars not set");
			var to = ((new Date()).getTime()/1000) - 3600;
			var from = to - (60*60*24*7);
			return request({
				url: 'https://api.pingdom.com/api/2.0/summary.performance/' + process.env.PINGDOM_CHECK_ID + '?from='+from+'&to='+to+'&resolution=hour',
				headers: {
					'app-key': process.env.PINGDOM_API_KEY,
					'Account-Email': process.env.PINGDOM_ACCOUNT
				},
				auth: {
					user: process.env.PINGDOM_USERNAME,
					pass: process.env.PINGDOM_PASSWORD
				}
			}).then(function (response) {
				var data = (response && JSON.parse(response)) || [];
				if (!data.summary) {
					data = {summary:{hours:[]}};
				}
				return data.summary.hours.map(function(result) {
					return {date: result.starttime, respTime: result.avgresponse};
				});
			});
		},
		outages: function() {
			if (!process.env.PINGDOM_CHECK_ID) return Promise.reject("Pingdom environment vars not set");
			var data = {};
			var periods = {
				"30d": 60*60*24*30,
				"3m": (60*60*24*365) / 4,
				"12m": (60*60*24*365)
			};
			var end = ((new Date()).getTime()/1000) - 3600;  // Ignore the last hour (Pingdom data processing delay)
			return Promise.all(Object.keys(periods).map(function(period) {
				var start = end - periods[period];
				return request({
					url: 'https://api.pingdom.com/api/2.0/summary.average/' + process.env.PINGDOM_CHECK_ID + '?from='+start+'&to='+end+'&includeuptime=true',
					headers: {
						'app-key': process.env.PINGDOM_API_KEY,
						'Account-Email': process.env.PINGDOM_ACCOUNT
					},
					auth: {
						user: process.env.PINGDOM_USERNAME,
						pass: process.env.PINGDOM_PASSWORD
					}
				}).then(function (response) {
					response = JSON.parse(response);
					data[period] = response.summary.status.totaldown;
				});
			})).then(function() {
				return data;
			}).catch(function(e) {
				console.log(e.stack || e);
			});
		},
		sizes: function() {

			// Define a list of all the browsers in which we tested one of the polyfills
			var firstfeature = compatdata[Object.keys(compatdata)[0]];
			var UAs = Object.keys(firstfeature).reduce(function(UAs, family) {
				UAs = UAs.concat(Object.keys(firstfeature[family]).map(function(uaVersion) {
					return {family: family, ver: uaVersion};
				}));
				return UAs;
			}, []);

			// For each of these browsers, get a bundle and report its size
			return Promise.all(UAs.map(function(browser) {
				var opts = {
					features: PolyfillSet.fromQueryParam('default').get(),
					uaString: browser.family+'/'+browser.ver,
				};
				return Promise.all(
					polyfillservice.getPolyfillString(extend({minify: true}, opts)),
					polyfillservice.getPolyfillString(extend({minify: false}, opts))
				).then(function (srcs) {
					var item = {
						family: browser.family,
						ver: browser.ver,
						minsrc: srcs[0],
						rawbytes: srcs[1].length,
						minbytes: srcs[0].length
					};
					return new Promise(function(resolve, reject) {
						zlib.gzip(item.minsrc, function(err, gzipsrc) {
							if (!err) {
								item.gzipbytes = gzipsrc.length;
							}
							resolve(item);
						});
					});
				});
			}));
		}
	};

	if (cache.hasOwnProperty(type) && cache[type].creationTime > ((new Date()).getTime() - (cachettl*1000)) ) {
		return cache[type].promise;
	} else {
		cache[type] = {creationTime: (new Date()).getTime()};
		try {
			return cache[type].promise = handlers[type]();
		} catch(err) {
			return cache[type].promise = Promise.reject(err.toString());
		}
	}
}

function getCompat() {
	var sourceslib = sources.latest;
	var browsers = ['ie', 'firefox', 'chrome', 'safari', 'opera', 'ios_saf'];
	var msgs = {
		'native': 'Supported natively',
		'polyfilled': 'Supported with polyfill service',
		'missing': 'Not supported'
	};
	return Promise.all(Object.keys(compatdata)
		.filter(function(feature) {
			return sourceslib.polyfillExistsSync(feature) && feature.indexOf('_') !== 0;
		})
		.sort()
		.map(function(feat) {
			return sourceslib.getPolyfill(feat).then(function(polyfill) {
				var fdata = {
					feature: feat,
					slug: feat.replace(/\./g, '_'),
					size: Object.keys(polyfill.variants).reduce(function(size, variantName) {
						return Math.max(size, polyfill.variants[variantName].minSource.length);
					}, 0),
					isDefault: (polyfill.aliases && polyfill.aliases.indexOf('default') !== -1),
					hasTests: polyfill.hasTests,
					docs: polyfill.docs,
					baseDir: polyfill.baseDir.replace(path.resolve(path.join(__dirname, '..')), ''),
					spec: polyfill.spec,
					notes: polyfill.notes ? polyfill.notes.map(function (n) { return marked(n); }) : [],
					license: polyfill.variants.default.license,
					licenseIsUrl: polyfill.variants.default.license && polyfill.variants.default.license.length > 5
				};
				browsers.forEach(function(browser) {
					if (compatdata[feat][browser]) {
						fdata[browser] = [];
						Object.keys(compatdata[feat][browser]).sort(function(a, b) {
							if (isNaN(a)) {
								return 1;
							} else if (isNaN(b)) {
								return -1;
							} else {
								return (parseFloat(a) < parseFloat(b)) ? -1 : 1;
							}
						}).forEach(function(version) {
							fdata[browser].push({
								status: compatdata[feat][browser][version],
								statusMsg: msgs[compatdata[feat][browser][version]],
								version: version
							});
						});
					}
				});
				return fdata;
			});
		})
	);
}

// Quick helper for Promise.all to spread results over separate arguments rather than an array
function spread(fn) {
	return function(results) {
		return fn.apply(fn, results);
	};
}

function route(req, res, next) {
	if (req.path.length < "/v1/docs/".length) {
		return res.redirect('/v1/docs/');
	}

	if (!req.params || !req.params[0]) {
		res.send(templates.index({section: 'index'}));

	} else if (req.params[0] === 'usage') {
		// Set the ttl to one hour for the usage page so the graphs are
		// updated more frequently, overriding the default cache-control
		// behaviour set in index.js
		var one_hour = 60 * 60;
		var one_week = one_hour * 24 * 7;
		res.set('Cache-Control', 'public, max-age=' + one_hour +', stale-while-revalidate=' + one_week + ', stale-if-error=' + one_week);
		Promise.all([getData('fastly'), getData('outages'), getData('respTimes')]).then(spread(function(fastly, outages, respTimes) {
			res.send(templates.usage({
				section: 'usage',
				requestsData: fastly.byhour,
				downtime: outages,
				respTimes: respTimes,
				hitCount: fastly.rollup.hits,
				missCount: fastly.rollup.miss
			}));
		})).catch(function(rejectReason) {
			res.send(templates.usage({
				section: 'usage',
				msg: rejectReason.toString()
			}));
		});

	} else if (req.params[0] === 'features') {
		Promise.all([getData('sizes'), getCompat()]).then(spread(function(sizes, compat) {
			res.send(templates.compat({
				section: 'features',
				compat: compat,
				sizes: sizes
			}));
		})).catch(function (err) {
			console.log (err.stack || err);
		});

	} else if (req.params[0] === 'api') {
		res.send(templates.api({
			hasVersions: sources.listVersions().length > 1,
			versions: sources.listVersions(),
			section: 'api'
		}));

	} else if (req.params[0] === 'examples') {

		res.send(templates.examples({section: 'examples'}));

	} else if (req.params[0] === 'contributing') {
		res.send(templates.contributing({section: 'contributing', baselines: require('../lib/UA').getBaselines()}));

	} else {
		next();
	}
}

module.exports = {
	route: route
};
<|endoftext|>


<commit_before>'use strict';

describe('Gregory first End To End Test', function() {

    beforeEach(function() {
	browser.driver.ignoreSynchronization = true;
	});

    it('Sign into google account', function () {
	browser.driver.manage().deleteAllCookies();
	browser.driver.get('https://accounts.google.com/ServiceLogin?sacu=1&hl=en');

	var emailInput = browser.driver.findElement(by.id('Email'));
	emailInput.sendKeys('teambrown394');

	var signInButton = browser.driver.findElement(by.id('next'));
	signInButton.click();

	browser.driver.sleep(2000);

	var passwordInput = browser.driver.findElement(by.id('Passwd'));
	passwordInput.sendKeys('goteambrown394');

	var signInButton = browser.driver.findElement(by.id('signIn'));
	signInButton.click();

	browser.driver.sleep(5000);
    });

    it('should add an event to the calendar at 5:00 AM', function() {
	browser.driver.get('http://localhost:8000/app/index.html#/view1');
	browser.driver.sleep(2000);
	element(by.buttonText("Click here to get your Calendar!")).click();
	browser.driver.sleep(5000);

	element(by.buttonText("Add")).click();

	element(by.model("updateData.summary")).sendKeys("Greg Event");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T05:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T06:00:00-05:00";

	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Add Event")).click();

	browser.driver.sleep(2000);

	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(true);
    });

    it('should update the event that was added at 5:00 AM to start at 4:00 AM and change the title to New Test', function() {
	element(by.buttonText("Edit Greg Event")).click();
	browser.driver.sleep(1000);

	element(by.model("updateData.summary")).clear();
	element(by.model("updateData.summary")).sendKeys("New Test");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T04:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T05:00:00-05:00";
	element(by.model("updateData.start.dateTime")).clear();
	element(by.model("updateData.end.dateTime")).clear();
	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Update")).click();
	browser.driver.sleep(2000);

	expect(element(by.buttonText('Edit New Test')).isPresent()).toBe(true);
    });

    it('should remove the event that was updated to start at 4:00 AM', function() {
	element(by.buttonText("Edit New Test")).click();
	browser.driver.sleep(1000);
	element(by.buttonText("Delete")).click();
	browser.driver.sleep(2000);

	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(false);
    });
});

describe('Gregory second End To End Test', function() {
    it('should add two events to the calendar at 5:00 AM', function() {
	browser.driver.get('http://localhost:8000/app/index.html#/view1');
	element(by.buttonText("Click here to get your Calendar!")).click();

	browser.driver.sleep(2000);

	element(by.buttonText("Add")).click();

	browser.driver.sleep(1000);

	element(by.model("updateData.summary")).sendKeys("Greg Event");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T05:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T06:00:00-05:00";

	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Add Event")).click();

	browser.driver.sleep(2000);

	element(by.buttonText("Add")).click();

	browser.driver.sleep(1000)

	element(by.model("updateData.summary")).sendKeys("New Test");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T05:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T06:00:00-05:00";

	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Add Event")).click();

	browser.driver.sleep(2000);

	expect(element(by.buttonText('Time Conflict!')).isPresent()).toBe(true);
    });

    it('should remove both events that were added to start at 5:00 AM', function() {
	element(by.buttonText("Edit Greg Event")).click();
	browser.driver.sleep(1000);
	element(by.buttonText("Delete")).click();
	browser.driver.sleep(1000);

	element(by.buttonText("Edit New Test")).click();
	browser.driver.sleep(1000);
	element(by.buttonText("Delete")).click();
	browser.driver.sleep(1000);

	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(false);
    });
});

describe('Gregory third End To End Test', function() {
    it('should add an event to the calendar at 5:00 AM of the next day', function() {
	browser.driver.get('http://localhost:8000/app/index.html#/view1');
	element(by.buttonText("Click here to get your Calendar!")).click();

	browser.driver.sleep(2000);

	element(by.buttonText("Add")).click();

	element(by.model("updateData.summary")).sendKeys("Greg Event");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T05:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T06:00:00-05:00";

	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Add Event")).click();

	browser.driver.sleep(2000);

	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(true);
    });

    it('should update the event that was added at 5:00 AM to be a reminder', function() {
	element(by.buttonText("Edit Greg Event")).click();
	browser.driver.sleep(1000);

	element(by.model("updateData.summary")).clear();
	element(by.model("updateData.summary")).sendKeys("New Test");

	var currDate = new Date(Date.now());
	var tomorrow = currDate.getDate() + 1;
	var day = ("0" + tomorrow).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T06:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T07:00:00-05:00";

	element(by.model("updateData.start.dateTime")).clear();
	element(by.model("updateData.end.dateTime")).clear();
	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Set Reminder")).click();
	browser.driver.sleep(2000);

	element(by.buttonText("Update")).click();
	browser.driver.sleep(2000);
	expect(element(by.buttonText('New Test in 1 day')).isPresent()).toBe(true);
    });

    it('should delete the reminder, then go to the next day in the calendar, and remove the event that was updated to be a reminder', function() {
	element(by.buttonText("Delete New Test")).click();
	browser.driver.sleep(1500);
	expect(element(by.buttonText('New Test due in 1 day')).isPresent()).toBe(false);

	element(by.buttonText("Next Day")).click();
	browser.driver.sleep(2500);

	element(by.buttonText("Edit [reminder]New Test")).click();
	browser.driver.sleep(1000);
	element(by.buttonText("Delete")).click();
	browser.driver.sleep(2000);
	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(false);
    });

});
<commit_msg>It works<commit_after>'use strict';

describe('Gregory first End To End Test', function() {

    beforeEach(function() {
	browser.driver.ignoreSynchronization = true;
	});

    it('Sign into google account', function () {
	browser.driver.manage().deleteAllCookies();
	browser.driver.get('https://accounts.google.com/ServiceLogin?sacu=1&hl=en');

	var emailInput = browser.driver.findElement(by.id('Email'));
	emailInput.sendKeys('teambrown394');

	var signInButton = browser.driver.findElement(by.id('next'));
	signInButton.click();

	browser.driver.sleep(2000);

	var passwordInput = browser.driver.findElement(by.id('Passwd'));
	passwordInput.sendKeys('goteambrown394');

	var signInButton = browser.driver.findElement(by.id('signIn'));
	signInButton.click();

	browser.driver.sleep(5000);
    });

    it('should add an event to the calendar at 5:00 AM', function() {
	browser.driver.get('http://localhost:8000/app/index.html#/view1');
	browser.driver.sleep(2000);
	element(by.buttonText("Click here to get your Calendar!")).click();
	browser.driver.sleep(5000);

	element(by.buttonText("Add")).click();

	element(by.model("updateData.summary")).sendKeys("Greg Event");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T05:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T06:00:00-05:00";

	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Add Event")).click();

	browser.driver.sleep(2000);

	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(true);
    });

    it('should update the event that was added at 5:00 AM to start at 4:00 AM and change the title to New Test', function() {
	element(by.buttonText("Edit Greg Event")).click();
	browser.driver.sleep(1000);

	element(by.model("updateData.summary")).clear();
	element(by.model("updateData.summary")).sendKeys("New Test");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T04:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T05:00:00-05:00";
	element(by.model("updateData.start.dateTime")).clear();
	element(by.model("updateData.end.dateTime")).clear();
	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Update")).click();
	browser.driver.sleep(2000);

	expect(element(by.buttonText('Edit New Test')).isPresent()).toBe(true);
    });

    it('should remove the event that was updated to start at 4:00 AM', function() {
	element(by.buttonText("Edit New Test")).click();
	browser.driver.sleep(1000);
	element(by.buttonText("Delete")).click();
	browser.driver.sleep(2000);

	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(false);
    });
});

describe('Gregory second End To End Test', function() {
    it('should add two events to the calendar at 5:00 AM', function() {
	browser.driver.get('http://localhost:8000/app/index.html#/view1');
	element(by.buttonText("Click here to get your Calendar!")).click();

	browser.driver.sleep(2000);

	element(by.buttonText("Add")).click();

	browser.driver.sleep(1000);

	element(by.model("updateData.summary")).sendKeys("Greg Event");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T05:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T06:00:00-05:00";

	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Add Event")).click();

	browser.driver.sleep(2000);

	element(by.buttonText("Add")).click();

	browser.driver.sleep(1000)

	element(by.model("updateData.summary")).sendKeys("New Test");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T05:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T06:00:00-05:00";

	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Add Event")).click();

	browser.driver.sleep(2000);

	expect(element(by.buttonText('Time Conflict!')).isPresent()).toBe(true);
    });

    it('should remove both events that were added to start at 5:00 AM', function() {
	element(by.buttonText("Edit Greg Event")).click();
	browser.driver.sleep(1000);
	element(by.buttonText("Delete")).click();
	browser.driver.sleep(2000);

	element(by.buttonText("Edit New Test")).click();
	browser.driver.sleep(1000);
	element(by.buttonText("Delete")).click();
	browser.driver.sleep(1000);

	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(false);
    });
});

describe('Gregory third End To End Test', function() {
    it('should add an event to the calendar at 5:00 AM of the next day', function() {
	browser.driver.get('http://localhost:8000/app/index.html#/view1');
	element(by.buttonText("Click here to get your Calendar!")).click();

	browser.driver.sleep(2000);

	element(by.buttonText("Add")).click();

	element(by.model("updateData.summary")).sendKeys("Greg Event");

	var currDate = new Date(Date.now());
	var day = ("0" + currDate.getDate()).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T05:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T06:00:00-05:00";

	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Add Event")).click();

	browser.driver.sleep(2000);

	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(true);
    });

    it('should update the event that was added at 5:00 AM to be a reminder', function() {
	element(by.buttonText("Edit Greg Event")).click();
	browser.driver.sleep(1000);

	element(by.model("updateData.summary")).clear();
	element(by.model("updateData.summary")).sendKeys("New Test");

	var currDate = new Date(Date.now());
	var tomorrow = currDate.getDate() + 1;
	var day = ("0" + tomorrow).slice(-2);
	var month = ("0" + (currDate.getMonth() + 1)).slice(-2);

	var start = "2015-" + month + "-" + day + "T06:00:00-05:00";
	var end = "2015-" + month + "-" + day + "T07:00:00-05:00";

	element(by.model("updateData.start.dateTime")).clear();
	element(by.model("updateData.end.dateTime")).clear();
	element(by.model("updateData.start.dateTime")).sendKeys(start);
	element(by.model("updateData.end.dateTime")).sendKeys(end);

	element(by.buttonText("Set Reminder")).click();
	browser.driver.sleep(2000);

	element(by.buttonText("Update")).click();
	browser.driver.sleep(2000);
	expect(element(by.buttonText('New Test in 1 day')).isPresent()).toBe(true);
    });

    it('should delete the reminder, then go to the next day in the calendar, and remove the event that was updated to be a reminder', function() {
	element(by.buttonText("Delete New Test")).click();
	browser.driver.sleep(1500);
	expect(element(by.buttonText('New Test due in 1 day')).isPresent()).toBe(false);

	element(by.buttonText("Next Day")).click();
	browser.driver.sleep(2500);

	element(by.buttonText("Edit [reminder]New Test")).click();
	browser.driver.sleep(1000);
	element(by.buttonText("Delete")).click();
	browser.driver.sleep(2000);
	expect(element(by.buttonText('Edit Greg Event')).isPresent()).toBe(false);
    });

});
<|endoftext|>


<commit_before>(function(global){

//
// Check for native Promise and it has correct interface
//

var NativePromise = global['Promise'];
var nativePromiseSupported =
  NativePromise &&
  // Some of these methods are missing from
  // Firefox/Chrome experimental implementations
  'resolve' in NativePromise &&
  'reject' in NativePromise &&
  'all' in NativePromise &&
  'race' in NativePromise &&
  // Older version of the spec had a resolver object
  // as the arg rather than a function
  (function(){
    var resolve;
    new NativePromise(function(r){ resolve = r; });
    return typeof resolve === 'function';
  })();

//
// export if necessary
//

if (typeof exports !== 'undefined' && exports)
{
  // node.js
  exports.Promise = nativePromiseSupported ? NativePromise : Promise;
}
else
{
  // AMD
  if ( typeof define == 'function' && define.amd ) {
    define(function () {
        return nativePromiseSupported ? NativePromise : Promise;
    })
  }
  else
  // in browser add to global
  if (!nativePromiseSupported)
    global['Promise'] = Promise;
}

//
// Polyfill
//

var PENDING = 'pending';
var SEALED = 'sealed';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';
var NOOP = function(){};

// async calls
var asyncSetTimer = typeof setImmediate !== 'undefined' ? setImmediate : setTimeout;
var asyncQueue = [];
var asyncTimer;

function asyncFlush(){
  // run promise callbacks
  for (var i = 0; i < asyncQueue.length; i++)
    asyncQueue[i][0](asyncQueue[i][1]);

  // reset async asyncQueue
  asyncQueue = [];
  asyncTimer = false;
}

function asyncCall(callback, arg){
  asyncQueue.push([callback, arg]);

  if (!asyncTimer)
  {
    asyncTimer = true;
    asyncSetTimer(asyncFlush, 0);
  }
}

function invokeResolver(resolver, promise) {
  function resolvePromise(value) {
    resolve(promise, value);
  }

  function rejectPromise(reason) {
    reject(promise, reason);
  }

  try {
    resolver(resolvePromise, rejectPromise);
  } catch(e) {
    rejectPromise(e);
  }
}

function invokeCallback(subscriber){
  var owner = subscriber.owner;
  var settled = owner.state_;
  var value = owner.data_;
  var callback = subscriber[settled];
  var promise = subscriber.then;

  if (typeof callback === 'function')
  {
    settled = FULFILLED;
    try {
      value = callback(value);
    } catch(e) {
      reject(promise, e);
    }
  }

  if (!handleThenable(promise, value))
  {
    if (settled === FULFILLED)
      resolve(promise, value);

    if (settled === REJECTED)
      reject(promise, value);
  }
}

function handleThenable(promise, value) {
  var resolved;

  try {
    if (promise === value)
      throw new TypeError('A promises callback cannot return that same promise.');

    if (value && (typeof value === 'function' || typeof value === 'object'))
    {
      var then = value.then;  // then should be retrived only once

      if (typeof then === 'function')
      {
        then.call(value, function(val){
          if (!resolved)
          {
            resolved = true;

            if (value !== val)
              resolve(promise, val);
            else
              fulfill(promise, val);
          }
        }, function(reason){
          if (!resolved)
          {
            resolved = true;

            reject(promise, reason);
          }
        });

        return true;
      }
    }
  } catch (e) {
    if (!resolved)
      reject(promise, e);

    return true;
  }

  return false;
}

function resolve(promise, value){
  if (promise === value || !handleThenable(promise, value))
    fulfill(promise, value);
}

function fulfill(promise, value){
  if (promise.state_ === PENDING)
  {
    promise.state_ = SEALED;
    promise.data_ = value;

    asyncCall(publishFulfillment, promise);
  }
}

function reject(promise, reason){
  if (promise.state_ === PENDING)
  {
    promise.state_ = SEALED;
    promise.data_ = reason;

    asyncCall(publishRejection, promise);
  }
}

function publish(promise) {
  promise.then_ = promise.then_.forEach(invokeCallback);
}

function publishFulfillment(promise){
  promise.state_ = FULFILLED;
  publish(promise);
}

function publishRejection(promise){
  promise.state_ = REJECTED;
  publish(promise);
}

/**
* @class
*/
function Promise(resolver){
  if (typeof resolver !== 'function')
    throw new TypeError('Promise constructor takes a function argument');

  if (this instanceof Promise === false)
    throw new TypeError('Failed to construct \'Promise\': Please use the \'new\' operator, this object constructor cannot be called as a function.');

  this.then_ = [];

  invokeResolver(resolver, this);
}

Promise.prototype = {
  constructor: Promise,

  state_: PENDING,
  then_: null,
  data_: undefined,

  then: function(onFulfillment, onRejection){
    var subscriber = {
      owner: this,
      then: new this.constructor(NOOP),
      fulfilled: onFulfillment,
      rejected: onRejection
    };

    if (this.state_ === FULFILLED || this.state_ === REJECTED)
    {
      // already resolved, call callback async
      asyncCall(invokeCallback, subscriber);
    }
    else
    {
      // subscribe
      this.then_.push(subscriber);
    }

    return subscriber.then;
  },

  'catch': function(onRejection) {
    return this.then(null, onRejection);
  }
};

Promise.all = function(promises){
  var Class = this;

  if (!Array.isArray(promises))
    throw new TypeError('You must pass an array to Promise.all().');

  return new Class(function(resolve, reject){
    var results = [];
    var remaining = 0;

    function resolver(index){
      remaining++;
      return function(value){
        results[index] = value;
        if (!--remaining)
          resolve(results);
      };
    }

    for (var i = 0, promise; i < promises.length; i++)
    {
      promise = promises[i];

      if (promise && typeof promise.then === 'function')
        promise.then(resolver(i), reject);
      else
        results[i] = promise;
    }

    if (!remaining)
      resolve(results);
  });
};

Promise.race = function(promises){
  var Class = this;

  if (!Array.isArray(promises))
    throw new TypeError('You must pass an array to Promise.race().');

  return new Class(function(resolve, reject) {
    for (var i = 0, promise; i < promises.length; i++)
    {
      promise = promises[i];

      if (promise && typeof promise.then === 'function')
        promise.then(resolve, reject);
      else
        resolve(promise);
    }
  });
};

Promise.resolve = function(value){
  var Class = this;

  if (value && typeof value === 'object' && value.constructor === Class)
    return value;

  return new Class(function(resolve){
    resolve(value);
  });
};

Promise.reject = function(reason){
  var Class = this;

  return new Class(function(resolve, reject){
    reject(reason);
  });
};

})(typeof window != 'undefined' ? window : typeof global != 'undefined' ? global : typeof self != 'undefined' ? self : this);
<commit_msg>code style<commit_after>(function(global){

//
// Check for native Promise and it has correct interface
//

var NativePromise = global['Promise'];
var nativePromiseSupported =
  NativePromise &&
  // Some of these methods are missing from
  // Firefox/Chrome experimental implementations
  'resolve' in NativePromise &&
  'reject' in NativePromise &&
  'all' in NativePromise &&
  'race' in NativePromise &&
  // Older version of the spec had a resolver object
  // as the arg rather than a function
  (function(){
    var resolve;
    new NativePromise(function(r){ resolve = r; });
    return typeof resolve === 'function';
  })();

//
// export if necessary
//

if (typeof exports !== 'undefined' && exports)
{
  // node.js
  exports.Promise = nativePromiseSupported ? NativePromise : Promise;
}
else
{
  // AMD
  if (typeof define == 'function' && define.amd)
  {
    define(function(){
      return nativePromiseSupported ? NativePromise : Promise;
    });
  }
  else
  {
    // in browser add to global
    if (!nativePromiseSupported)
      global['Promise'] = Promise;
  }
}

//
// Polyfill
//

var PENDING = 'pending';
var SEALED = 'sealed';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';
var NOOP = function(){};

// async calls
var asyncSetTimer = typeof setImmediate !== 'undefined' ? setImmediate : setTimeout;
var asyncQueue = [];
var asyncTimer;

function asyncFlush(){
  // run promise callbacks
  for (var i = 0; i < asyncQueue.length; i++)
    asyncQueue[i][0](asyncQueue[i][1]);

  // reset async asyncQueue
  asyncQueue = [];
  asyncTimer = false;
}

function asyncCall(callback, arg){
  asyncQueue.push([callback, arg]);

  if (!asyncTimer)
  {
    asyncTimer = true;
    asyncSetTimer(asyncFlush, 0);
  }
}

function invokeResolver(resolver, promise) {
  function resolvePromise(value) {
    resolve(promise, value);
  }

  function rejectPromise(reason) {
    reject(promise, reason);
  }

  try {
    resolver(resolvePromise, rejectPromise);
  } catch(e) {
    rejectPromise(e);
  }
}

function invokeCallback(subscriber){
  var owner = subscriber.owner;
  var settled = owner.state_;
  var value = owner.data_;
  var callback = subscriber[settled];
  var promise = subscriber.then;

  if (typeof callback === 'function')
  {
    settled = FULFILLED;
    try {
      value = callback(value);
    } catch(e) {
      reject(promise, e);
    }
  }

  if (!handleThenable(promise, value))
  {
    if (settled === FULFILLED)
      resolve(promise, value);

    if (settled === REJECTED)
      reject(promise, value);
  }
}

function handleThenable(promise, value) {
  var resolved;

  try {
    if (promise === value)
      throw new TypeError('A promises callback cannot return that same promise.');

    if (value && (typeof value === 'function' || typeof value === 'object'))
    {
      var then = value.then;  // then should be retrived only once

      if (typeof then === 'function')
      {
        then.call(value, function(val){
          if (!resolved)
          {
            resolved = true;

            if (value !== val)
              resolve(promise, val);
            else
              fulfill(promise, val);
          }
        }, function(reason){
          if (!resolved)
          {
            resolved = true;

            reject(promise, reason);
          }
        });

        return true;
      }
    }
  } catch (e) {
    if (!resolved)
      reject(promise, e);

    return true;
  }

  return false;
}

function resolve(promise, value){
  if (promise === value || !handleThenable(promise, value))
    fulfill(promise, value);
}

function fulfill(promise, value){
  if (promise.state_ === PENDING)
  {
    promise.state_ = SEALED;
    promise.data_ = value;

    asyncCall(publishFulfillment, promise);
  }
}

function reject(promise, reason){
  if (promise.state_ === PENDING)
  {
    promise.state_ = SEALED;
    promise.data_ = reason;

    asyncCall(publishRejection, promise);
  }
}

function publish(promise) {
  promise.then_ = promise.then_.forEach(invokeCallback);
}

function publishFulfillment(promise){
  promise.state_ = FULFILLED;
  publish(promise);
}

function publishRejection(promise){
  promise.state_ = REJECTED;
  publish(promise);
}

/**
* @class
*/
function Promise(resolver){
  if (typeof resolver !== 'function')
    throw new TypeError('Promise constructor takes a function argument');

  if (this instanceof Promise === false)
    throw new TypeError('Failed to construct \'Promise\': Please use the \'new\' operator, this object constructor cannot be called as a function.');

  this.then_ = [];

  invokeResolver(resolver, this);
}

Promise.prototype = {
  constructor: Promise,

  state_: PENDING,
  then_: null,
  data_: undefined,

  then: function(onFulfillment, onRejection){
    var subscriber = {
      owner: this,
      then: new this.constructor(NOOP),
      fulfilled: onFulfillment,
      rejected: onRejection
    };

    if (this.state_ === FULFILLED || this.state_ === REJECTED)
    {
      // already resolved, call callback async
      asyncCall(invokeCallback, subscriber);
    }
    else
    {
      // subscribe
      this.then_.push(subscriber);
    }

    return subscriber.then;
  },

  'catch': function(onRejection) {
    return this.then(null, onRejection);
  }
};

Promise.all = function(promises){
  var Class = this;

  if (!Array.isArray(promises))
    throw new TypeError('You must pass an array to Promise.all().');

  return new Class(function(resolve, reject){
    var results = [];
    var remaining = 0;

    function resolver(index){
      remaining++;
      return function(value){
        results[index] = value;
        if (!--remaining)
          resolve(results);
      };
    }

    for (var i = 0, promise; i < promises.length; i++)
    {
      promise = promises[i];

      if (promise && typeof promise.then === 'function')
        promise.then(resolver(i), reject);
      else
        results[i] = promise;
    }

    if (!remaining)
      resolve(results);
  });
};

Promise.race = function(promises){
  var Class = this;

  if (!Array.isArray(promises))
    throw new TypeError('You must pass an array to Promise.race().');

  return new Class(function(resolve, reject) {
    for (var i = 0, promise; i < promises.length; i++)
    {
      promise = promises[i];

      if (promise && typeof promise.then === 'function')
        promise.then(resolve, reject);
      else
        resolve(promise);
    }
  });
};

Promise.resolve = function(value){
  var Class = this;

  if (value && typeof value === 'object' && value.constructor === Class)
    return value;

  return new Class(function(resolve){
    resolve(value);
  });
};

Promise.reject = function(reason){
  var Class = this;

  return new Class(function(resolve, reject){
    reject(reason);
  });
};

})(typeof window != 'undefined' ? window : typeof global != 'undefined' ? global : typeof self != 'undefined' ? self : this);
<|endoftext|>