<commit_before>// jshint quotmark: false

var helmet = require('../');

var _ = require('underscore');
var connect = require('connect');
var request = require('supertest');
var assert = require('assert');

function helloWorld(req, res) {
    res.end('Hello world!');
}

describe('csp middleware', function () {

    var POLICY = {
        'default-src': ["'self'", 'default.com'],
        'script-src': ['scripts.com'],
        'style-src': ['style.com'],
        'img-src': ['img.com'],
        'connect-src': ['connect.com'],
        'font-src': ['font.com'],
        'object-src': ['object.com'],
        'media-src': ['media.com'],
        'frame-src': ['frame.com'],
        'sandbox': ['allow-forms', 'allow-scripts'],
        'report-uri': ['/report-violation']
    };

    var AGENTS = {
        'Firefox 22': {
            string: 'Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/22.0',
            special: true
        },
        'Firefox 23': {
            string: 'Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/23.0',
            header: 'Content-Security-Policy'
        },
        'Chrome 24': {
            string: 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17',
            header: 'X-WebKit-CSP'
        },
        'Chrome 27': {
            string: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36',
            header: 'Content-Security-Policy'
        },
        'Opera 15': {
            string: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36 OPR/15.0.1387.64',
            header: 'Content-Security-Policy'
        },
        'Safari 5.1': {
            string: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10',
            special: true
        },
        'Safari 6': {
            string: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1',
            header: 'X-WebKit-CSP'
        },
        'Safari 7': {
            string: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.74.9 (KHTML, like Gecko) Version/7.0.2 Safari/537.74.9',
            header: 'Content-Security-Policy'
        }
    };

    function use (policy) {
        var result = connect();
        result.use(helmet.csp(policy));
        result.use(helloWorld);
        return result;
    }

    it('sets headers by string', function (done) {
        var app = use({ 'default-src': 'a.com b.biz' });
        request(app).get('/')
        .expect('Content-Security-Policy', /default-src a.com b.biz/, done);
    });

    it('sets all the headers if you tell it to', function (done) {
        var app = use({
            setAllHeaders: true,
            'default-src': ["'self'", 'domain.com']
        });
        request(app).get('/').set('User-Agent', AGENTS['Firefox 23'].string)
        .expect('X-Content-Security-Policy', /default-src 'self' domain.com/)
        .expect('Content-Security-Policy', /default-src 'self' domain.com/)
        .expect('X-WebKit-CSP', /default-src 'self' domain.com/)
        .end(done);
    });

    it('sets all the headers if you provide an unknown user-agent', function (done) {
        var app = use({ 'default-src': ["'self'", 'domain.com'] });
        request(app).get('/').set('User-Agent', 'Burrito Browser')
        .expect('X-Content-Security-Policy', /default-src 'self' domain.com/)
        .expect('Content-Security-Policy', /default-src 'self' domain.com/)
        .expect('X-WebKit-CSP', /default-src 'self' domain.com/)
        .end(done);
    });

    it('sets the report-only headers', function (done) {
        var app = use({
            reportOnly: true,
            setAllHeaders: true,
            'default-src': ["'self'"],
            'report-uri': '/reporter'
        });
        request(app).get('/').set('User-Agent', AGENTS['Firefox 23'].string)
        .expect('X-Content-Security-Policy-Report-Only', /default-src 'self'/)
        .expect('Content-Security-Policy-Report-Only', /default-src 'self'/)
        .expect('X-WebKit-CSP-Report-Only', /default-src 'self'/)
        .end(done);
    });

    it('throws an error when directives need quotes', function () {
        assert.throws(function() {
            helmet.csp({ 'default-src': ['none'] });
        }, Error);
        assert.throws(function() {
            helmet.csp({ 'default-src': ['self'] });
        }, Error);
        assert.throws(function() {
            helmet.csp({ 'script-src': ['unsafe-inline'] });
        }, Error);
        assert.throws(function() {
            helmet.csp({ 'script-src': ['unsafe-eval'] });
        }, Error);
        assert.throws(function() {
            helmet.csp({ 'default-src': 'self' });
        }, Error);
    });

    it('throws an error reportOnly is true and there is no report-uri', function () {
        assert.throws(function() {
            helmet.csp({ reportOnly: true });
        }, Error);
    });

    _.each(AGENTS, function(agent) {

        if (agent.special) {
            return;
        }

        it('sets the header properly for ' + agent.name, function (done) {
            var app = use(POLICY);
            var header = agent.header;
            request(app).get('/').set('User-Agent', agent.string)
            .expect(header, /default-src 'self' default.com/)
            .expect(header, /script-src scripts.com/)
            .expect(header, /img-src img.com/)
            .expect(header, /connect-src connect.com/)
            .expect(header, /font-src font.com/)
            .expect(header, /object-src object.com/)
            .expect(header, /media-src media.com/)
            .expect(header, /frame-src frame.com/)
            .expect(header, /sandbox allow-forms allow-scripts/)
            .expect(header, /report-uri \/report-violation/)
            .end(done);
        });

    });

    it('sets the header properly for Firefox 22', function (done) {
        var policy = _.extend({
            safari5: true
        }, POLICY);
        var app = use(policy);
        var header = 'X-Content-Security-Policy';
        request(app).get('/').set('User-Agent', AGENTS['Firefox 22'].string)
        .expect(header, /default-src 'self' default.com/)
        .expect(header, /script-src scripts.com/)
        .expect(header, /img-src img.com/)
        .expect(header, /xhr-src connect.com/)
        .expect(header, /font-src font.com/)
        .expect(header, /object-src object.com/)
        .expect(header, /media-src media.com/)
        .expect(header, /frame-src frame.com/)
        .expect(header, /sandbox allow-forms allow-scripts/)
        .expect(header, /report-uri \/report-violation/)
        .end(done);
    });

    it("doesn't set the property for Safari 5.1 by default", function (done) {
        var app = use(POLICY);
        request(app).get('/').set('User-Agent', AGENTS['Safari 5.1'].string)
        .end(function(err, res) {
            if (err) {
                return done(err);
            }
            assert(res.header['X-WebKit-CSP'] === undefined);
            assert(res.header['Content-Security-Policy'] === undefined);
            assert(res.header['X-Content-Security-Policy'] === undefined);
            done();
        });
    });

    it('sets the header for Safari 5.1 if you force it', function (done) {
        var app = use({
            safari5: true,
            'default-src': 'a.com'
        });
        request(app).get('/').set('User-Agent', AGENTS['Safari 5.1'].string)
        .expect('X-WebKit-CSP', 'default-src a.com', done);
    });

    it("doesn't splice the original array", function (done) {
        var app = use({
            'style-src': [
                "'self'",
                "'unsafe-inline'"
            ]
        });
        var chrome = AGENTS['Chrome 27'];
        var ff = AGENTS['Firefox 22'];
        request(app).get('/').set('User-Agent', chrome.string)
        .expect(chrome.header, /style-src 'self' 'unsafe-inline'/)
        .end(function(err,res) {
            request(app).get('/').set('User-Agent', ff.string)
            .expect(ff.header, /style-src 'self'/)
            .end(function(err, res) {
                request(app).get('/').set('User-Agent', chrome.string)
                .expect(chrome.header, /style-src 'self' 'unsafe-inline'/)
                .end(done);
            });
        });
    });
});
<commit_msg>Remove unused arguments from a CSP test<commit_after>// jshint quotmark: false

var helmet = require('../');

var _ = require('underscore');
var connect = require('connect');
var request = require('supertest');
var assert = require('assert');

function helloWorld(req, res) {
    res.end('Hello world!');
}

describe('csp middleware', function () {

    var POLICY = {
        'default-src': ["'self'", 'default.com'],
        'script-src': ['scripts.com'],
        'style-src': ['style.com'],
        'img-src': ['img.com'],
        'connect-src': ['connect.com'],
        'font-src': ['font.com'],
        'object-src': ['object.com'],
        'media-src': ['media.com'],
        'frame-src': ['frame.com'],
        'sandbox': ['allow-forms', 'allow-scripts'],
        'report-uri': ['/report-violation']
    };

    var AGENTS = {
        'Firefox 22': {
            string: 'Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/22.0',
            special: true
        },
        'Firefox 23': {
            string: 'Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/23.0',
            header: 'Content-Security-Policy'
        },
        'Chrome 24': {
            string: 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17',
            header: 'X-WebKit-CSP'
        },
        'Chrome 27': {
            string: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36',
            header: 'Content-Security-Policy'
        },
        'Opera 15': {
            string: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36 OPR/15.0.1387.64',
            header: 'Content-Security-Policy'
        },
        'Safari 5.1': {
            string: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10',
            special: true
        },
        'Safari 6': {
            string: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1',
            header: 'X-WebKit-CSP'
        },
        'Safari 7': {
            string: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.74.9 (KHTML, like Gecko) Version/7.0.2 Safari/537.74.9',
            header: 'Content-Security-Policy'
        }
    };

    function use (policy) {
        var result = connect();
        result.use(helmet.csp(policy));
        result.use(helloWorld);
        return result;
    }

    it('sets headers by string', function (done) {
        var app = use({ 'default-src': 'a.com b.biz' });
        request(app).get('/')
        .expect('Content-Security-Policy', /default-src a.com b.biz/, done);
    });

    it('sets all the headers if you tell it to', function (done) {
        var app = use({
            setAllHeaders: true,
            'default-src': ["'self'", 'domain.com']
        });
        request(app).get('/').set('User-Agent', AGENTS['Firefox 23'].string)
        .expect('X-Content-Security-Policy', /default-src 'self' domain.com/)
        .expect('Content-Security-Policy', /default-src 'self' domain.com/)
        .expect('X-WebKit-CSP', /default-src 'self' domain.com/)
        .end(done);
    });

    it('sets all the headers if you provide an unknown user-agent', function (done) {
        var app = use({ 'default-src': ["'self'", 'domain.com'] });
        request(app).get('/').set('User-Agent', 'Burrito Browser')
        .expect('X-Content-Security-Policy', /default-src 'self' domain.com/)
        .expect('Content-Security-Policy', /default-src 'self' domain.com/)
        .expect('X-WebKit-CSP', /default-src 'self' domain.com/)
        .end(done);
    });

    it('sets the report-only headers', function (done) {
        var app = use({
            reportOnly: true,
            setAllHeaders: true,
            'default-src': ["'self'"],
            'report-uri': '/reporter'
        });
        request(app).get('/').set('User-Agent', AGENTS['Firefox 23'].string)
        .expect('X-Content-Security-Policy-Report-Only', /default-src 'self'/)
        .expect('Content-Security-Policy-Report-Only', /default-src 'self'/)
        .expect('X-WebKit-CSP-Report-Only', /default-src 'self'/)
        .end(done);
    });

    it('throws an error when directives need quotes', function () {
        assert.throws(function() {
            helmet.csp({ 'default-src': ['none'] });
        }, Error);
        assert.throws(function() {
            helmet.csp({ 'default-src': ['self'] });
        }, Error);
        assert.throws(function() {
            helmet.csp({ 'script-src': ['unsafe-inline'] });
        }, Error);
        assert.throws(function() {
            helmet.csp({ 'script-src': ['unsafe-eval'] });
        }, Error);
        assert.throws(function() {
            helmet.csp({ 'default-src': 'self' });
        }, Error);
    });

    it('throws an error reportOnly is true and there is no report-uri', function () {
        assert.throws(function() {
            helmet.csp({ reportOnly: true });
        }, Error);
    });

    _.each(AGENTS, function(agent) {

        if (agent.special) {
            return;
        }

        it('sets the header properly for ' + agent.name, function (done) {
            var app = use(POLICY);
            var header = agent.header;
            request(app).get('/').set('User-Agent', agent.string)
            .expect(header, /default-src 'self' default.com/)
            .expect(header, /script-src scripts.com/)
            .expect(header, /img-src img.com/)
            .expect(header, /connect-src connect.com/)
            .expect(header, /font-src font.com/)
            .expect(header, /object-src object.com/)
            .expect(header, /media-src media.com/)
            .expect(header, /frame-src frame.com/)
            .expect(header, /sandbox allow-forms allow-scripts/)
            .expect(header, /report-uri \/report-violation/)
            .end(done);
        });

    });

    it('sets the header properly for Firefox 22', function (done) {
        var policy = _.extend({
            safari5: true
        }, POLICY);
        var app = use(policy);
        var header = 'X-Content-Security-Policy';
        request(app).get('/').set('User-Agent', AGENTS['Firefox 22'].string)
        .expect(header, /default-src 'self' default.com/)
        .expect(header, /script-src scripts.com/)
        .expect(header, /img-src img.com/)
        .expect(header, /xhr-src connect.com/)
        .expect(header, /font-src font.com/)
        .expect(header, /object-src object.com/)
        .expect(header, /media-src media.com/)
        .expect(header, /frame-src frame.com/)
        .expect(header, /sandbox allow-forms allow-scripts/)
        .expect(header, /report-uri \/report-violation/)
        .end(done);
    });

    it("doesn't set the property for Safari 5.1 by default", function (done) {
        var app = use(POLICY);
        request(app).get('/').set('User-Agent', AGENTS['Safari 5.1'].string)
        .end(function(err, res) {
            if (err) {
                return done(err);
            }
            assert(res.header['X-WebKit-CSP'] === undefined);
            assert(res.header['Content-Security-Policy'] === undefined);
            assert(res.header['X-Content-Security-Policy'] === undefined);
            done();
        });
    });

    it('sets the header for Safari 5.1 if you force it', function (done) {
        var app = use({
            safari5: true,
            'default-src': 'a.com'
        });
        request(app).get('/').set('User-Agent', AGENTS['Safari 5.1'].string)
        .expect('X-WebKit-CSP', 'default-src a.com', done);
    });

    it("doesn't splice the original array", function (done) {
        var app = use({
            'style-src': [
                "'self'",
                "'unsafe-inline'"
            ]
        });
        var chrome = AGENTS['Chrome 27'];
        var ff = AGENTS['Firefox 22'];
        request(app).get('/').set('User-Agent', chrome.string)
        .expect(chrome.header, /style-src 'self' 'unsafe-inline'/)
        .end(function() {
            request(app).get('/').set('User-Agent', ff.string)
            .expect(ff.header, /style-src 'self'/)
            .end(function() {
                request(app).get('/').set('User-Agent', chrome.string)
                .expect(chrome.header, /style-src 'self' 'unsafe-inline'/)
                .end(done);
            });
        });
    });
});
<|endoftext|>


<commit_before>var Plots = require('@src/plots/plots');
var Lib = require('@src/lib');

var supplyDefaults = require('@src/traces/histogram/defaults');
var calc = require('@src/traces/histogram/calc');

describe('Test histogram', function() {
    'use strict';

    describe('supplyDefaults', function() {
        var traceIn,
            traceOut;

        beforeEach(function() {
            traceOut = {};
        });

        it('should set visible to false when x or y is empty', function() {
            traceIn = {
                x: []
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);

            traceIn = {
                y: []
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);
        });

        it('should set visible to false when type is histogram2d and x or y are empty', function() {
            traceIn = {
                type: 'histogram2d',
                x: [],
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);

            traceIn = {
                type: 'histogram2d',
                x: [1, 2, 2],
                y: []
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);

            traceIn = {
                type: 'histogram2d',
                x: [],
                y: []
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);

            traceIn = {
                type: 'histogram2dcontour',
                x: [],
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);
        });

        it('should set orientation to v by default', function() {
            traceIn = {
                x: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.orientation).toBe('v');

            traceIn = {
                x: [1, 2, 2],
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.orientation).toBe('v');
        });

        it('should set orientation to h when only y is supplied', function() {
            traceIn = {
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.orientation).toBe('h');

        });

        // coercing bin attributes got moved to calc because it needs
        // axis type - so here we just test that it's NOT happening

        it('should not coerce autobinx regardless of xbins', function() {
            traceIn = {
                x: [1, 2, 2],
                xbins: {
                    start: 1,
                    end: 3,
                    size: 1
                }
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.autobinx).toBeUndefined();

            traceIn = {
                x: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.autobinx).toBeUndefined();
        });

        it('should not coerce autobiny regardless of ybins', function() {
            traceIn = {
                y: [1, 2, 2],
                ybins: {
                    start: 1,
                    end: 3,
                    size: 1
                }
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.autobiny).toBeUndefined();

            traceIn = {
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.autobiny).toBeUndefined();
        });

    });

    describe('calc', function() {
        function _calc(opts) {
            var base = { type: 'histogram' },
                trace = Lib.extendFlat({}, base, opts),
                gd = { data: [trace] };

            Plots.supplyDefaults(gd);
            var fullTrace = gd._fullData[0];

            var out = calc(gd, fullTrace);
            delete out[0].trace;
            return out;
        }

        var oneDay = 24 * 3600000;

        it('should handle auto dates with nonuniform (month) bins', function() {
            // All data on exact years: shift so bin center is an
            // exact year, except on leap years
            var out = _calc({
                x: ['1970-01-01', '1970-01-01', '1971-01-01', '1973-01-01'],
                nbinsx: 4
            });

            // TODO: this gives half-day gaps between all but the first two
            // bars. Now that we have explicit per-bar positioning, perhaps
            // we should fill the space, rather than insisting on equal-width
            // bars?
            expect(out).toEqual([
                // full calcdata has x and y too (and t in the first one),
                // but those come later from setPositions.
                {b: 0, p: Date.UTC(1970, 0, 1), s: 2},
                {b: 0, p: Date.UTC(1971, 0, 1), s: 1},
                {b: 0, p: Date.UTC(1972, 0, 1, 12), s: 0},
                {b: 0, p: Date.UTC(1973, 0, 1), s: 1}
            ]);

            // All data on exact months: shift so bin center is on (31-day months)
            // or in (shorter months) that month
            out = _calc({
                x: ['1970-01-01', '1970-01-01', '1970-02-01', '1970-04-01'],
                nbinsx: 4
            });

            expect(out).toEqual([
                {b: 0, p: Date.UTC(1970, 0, 1), s: 2},
                {b: 0, p: Date.UTC(1970, 1, 1), s: 1},
                {b: 0, p: Date.UTC(1970, 2, 2.5), s: 0},
                {b: 0, p: Date.UTC(1970, 3, 1), s: 1}
            ]);

            // data on exact days: shift so each bin goes from noon to noon
            // even though this gives kind of odd bin centers since the bins
            // are months... but the important thing is it's unambiguous which
            // bin any given day is in.
            out = _calc({
                x: ['1970-01-02', '1970-01-31', '1970-02-13', '1970-04-19'],
                nbinsx: 4
            });

            expect(out).toEqual([
                // dec 31 12:00 -> jan 31 12:00, middle is jan 16
                {b: 0, p: Date.UTC(1970, 0, 16), s: 2},
                // jan 31 12:00 -> feb 28 12:00, middle is feb 14 12:00
                {b: 0, p: Date.UTC(1970, 1, 14, 12), s: 1},
                {b: 0, p: Date.UTC(1970, 2, 16), s: 0},
                {b: 0, p: Date.UTC(1970, 3, 15, 12), s: 1}
            ]);
        });

        it('should handle auto dates with uniform (day) bins', function() {
            var out = _calc({
                x: ['1970-01-01', '1970-01-01', '1970-01-02', '1970-01-04'],
                nbinsx: 4
            });

            var x0 = 0,
                x1 = x0 + oneDay,
                x2 = x1 + oneDay,
                x3 = x2 + oneDay;

            expect(out).toEqual([
                {b: 0, p: x0, s: 2},
                {b: 0, p: x1, s: 1},
                {b: 0, p: x2, s: 0},
                {b: 0, p: x3, s: 1}
            ]);
        });

    });
});
<commit_msg>robustify histogram_test some environments accept non-integer date parts, some don't.<commit_after>var Plots = require('@src/plots/plots');
var Lib = require('@src/lib');

var supplyDefaults = require('@src/traces/histogram/defaults');
var calc = require('@src/traces/histogram/calc');

describe('Test histogram', function() {
    'use strict';

    describe('supplyDefaults', function() {
        var traceIn,
            traceOut;

        beforeEach(function() {
            traceOut = {};
        });

        it('should set visible to false when x or y is empty', function() {
            traceIn = {
                x: []
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);

            traceIn = {
                y: []
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);
        });

        it('should set visible to false when type is histogram2d and x or y are empty', function() {
            traceIn = {
                type: 'histogram2d',
                x: [],
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);

            traceIn = {
                type: 'histogram2d',
                x: [1, 2, 2],
                y: []
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);

            traceIn = {
                type: 'histogram2d',
                x: [],
                y: []
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);

            traceIn = {
                type: 'histogram2dcontour',
                x: [],
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.visible).toBe(false);
        });

        it('should set orientation to v by default', function() {
            traceIn = {
                x: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.orientation).toBe('v');

            traceIn = {
                x: [1, 2, 2],
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.orientation).toBe('v');
        });

        it('should set orientation to h when only y is supplied', function() {
            traceIn = {
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.orientation).toBe('h');

        });

        // coercing bin attributes got moved to calc because it needs
        // axis type - so here we just test that it's NOT happening

        it('should not coerce autobinx regardless of xbins', function() {
            traceIn = {
                x: [1, 2, 2],
                xbins: {
                    start: 1,
                    end: 3,
                    size: 1
                }
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.autobinx).toBeUndefined();

            traceIn = {
                x: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.autobinx).toBeUndefined();
        });

        it('should not coerce autobiny regardless of ybins', function() {
            traceIn = {
                y: [1, 2, 2],
                ybins: {
                    start: 1,
                    end: 3,
                    size: 1
                }
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.autobiny).toBeUndefined();

            traceIn = {
                y: [1, 2, 2]
            };
            supplyDefaults(traceIn, traceOut);
            expect(traceOut.autobiny).toBeUndefined();
        });

    });

    describe('calc', function() {
        function _calc(opts) {
            var base = { type: 'histogram' },
                trace = Lib.extendFlat({}, base, opts),
                gd = { data: [trace] };

            Plots.supplyDefaults(gd);
            var fullTrace = gd._fullData[0];

            var out = calc(gd, fullTrace);
            delete out[0].trace;
            return out;
        }

        var oneDay = 24 * 3600000;

        it('should handle auto dates with nonuniform (month) bins', function() {
            // All data on exact years: shift so bin center is an
            // exact year, except on leap years
            var out = _calc({
                x: ['1970-01-01', '1970-01-01', '1971-01-01', '1973-01-01'],
                nbinsx: 4
            });

            // TODO: this gives half-day gaps between all but the first two
            // bars. Now that we have explicit per-bar positioning, perhaps
            // we should fill the space, rather than insisting on equal-width
            // bars?
            expect(out).toEqual([
                // full calcdata has x and y too (and t in the first one),
                // but those come later from setPositions.
                {b: 0, p: Date.UTC(1970, 0, 1), s: 2},
                {b: 0, p: Date.UTC(1971, 0, 1), s: 1},
                {b: 0, p: Date.UTC(1972, 0, 1, 12), s: 0},
                {b: 0, p: Date.UTC(1973, 0, 1), s: 1}
            ]);

            // All data on exact months: shift so bin center is on (31-day months)
            // or in (shorter months) that month
            out = _calc({
                x: ['1970-01-01', '1970-01-01', '1970-02-01', '1970-04-01'],
                nbinsx: 4
            });

            expect(out).toEqual([
                {b: 0, p: Date.UTC(1970, 0, 1), s: 2},
                {b: 0, p: Date.UTC(1970, 1, 1), s: 1},
                {b: 0, p: Date.UTC(1970, 2, 2, 12), s: 0},
                {b: 0, p: Date.UTC(1970, 3, 1), s: 1}
            ]);

            // data on exact days: shift so each bin goes from noon to noon
            // even though this gives kind of odd bin centers since the bins
            // are months... but the important thing is it's unambiguous which
            // bin any given day is in.
            out = _calc({
                x: ['1970-01-02', '1970-01-31', '1970-02-13', '1970-04-19'],
                nbinsx: 4
            });

            expect(out).toEqual([
                // dec 31 12:00 -> jan 31 12:00, middle is jan 16
                {b: 0, p: Date.UTC(1970, 0, 16), s: 2},
                // jan 31 12:00 -> feb 28 12:00, middle is feb 14 12:00
                {b: 0, p: Date.UTC(1970, 1, 14, 12), s: 1},
                {b: 0, p: Date.UTC(1970, 2, 16), s: 0},
                {b: 0, p: Date.UTC(1970, 3, 15, 12), s: 1}
            ]);
        });

        it('should handle auto dates with uniform (day) bins', function() {
            var out = _calc({
                x: ['1970-01-01', '1970-01-01', '1970-01-02', '1970-01-04'],
                nbinsx: 4
            });

            var x0 = 0,
                x1 = x0 + oneDay,
                x2 = x1 + oneDay,
                x3 = x2 + oneDay;

            expect(out).toEqual([
                {b: 0, p: x0, s: 2},
                {b: 0, p: x1, s: 1},
                {b: 0, p: x2, s: 0},
                {b: 0, p: x3, s: 1}
            ]);
        });

    });
});
<|endoftext|>


<commit_before>'use strict';

var ev3 = require('../index');

console.log('ev3 started...');

ev3.button.up.on('press', function () {
    console.log('up button pressed!');
});

ev3.button.enter.on('press', function () {
    console.log('enter button pressed!');

    console.log('battery voltage: ' + ev3.battery.getVoltage());
});

ev3.button.down.on('press', function () {
    console.log('down button pressed!');
});

ev3.button.right.on('press', function () {
    console.log('right button pressed!');
});

ev3.button.left.on('press', function () {
    console.log('left button pressed!');
});

ev3.button.escape.on('press', function () {
    console.log('escape button pressed!');

    process.exit();
});
<commit_msg>Update ev3 test file<commit_after>'use strict';

var ev3 = require('../index'),
    motor = new ev3.Motor('ABCD');

console.log('ev3 started...');

ev3.button.up.on('press', function () {
    console.log('motor a speed: ' + ev3.Motor.getSpeed('A'));
    console.log('motor b speed: ' + ev3.Motor.getSpeed('B'));
    console.log('motor c speed: ' + ev3.Motor.getSpeed('C'));
    console.log('motor d speed: ' + ev3.Motor.getSpeed('D'));
});

ev3.button.enter.on('press', function () {
    console.log('battery voltage: ' + ev3.battery.getVoltage());
});

ev3.button.down.on('press', function () {
    console.log('motor a tachoCount: ' + ev3.Motor.getTachoCount('A'));
    console.log('motor b tachoCount: ' + ev3.Motor.getTachoCount('B'));
    console.log('motor c tachoCount: ' + ev3.Motor.getTachoCount('C'));
    console.log('motor d tachoCount: ' + ev3.Motor.getTachoCount('D'));
});

ev3.button.right.on('press', function () {
    motor.resetTachoCount();
    motor.setSpeed(50);
    motor.start();
});

ev3.button.left.on('press', function () {
    motor.stopAndBrake();
});

ev3.button.escape.on('press', function () {
    process.exit();
});
<|endoftext|>


<commit_before>/* eslint-disable ghost/ember/alias-model-in-controller */
import $ from 'jquery';
import ModalComponent from 'ghost-admin/components/modal-base';
import RSVP from 'rsvp';
import {
    IMAGE_EXTENSIONS,
    IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {computed} from '@ember/object';
import {htmlSafe} from '@ember/string';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';
import {timeout} from 'ember-concurrency';

const ICON_EXTENSIONS = ['ico', 'png'];

export default ModalComponent.extend({
    config: service(),
    ghostPaths: service(),
    notifications: service(),
    session: service(),
    settings: service(),
    ajax: service(),

    imageExtensions: IMAGE_EXTENSIONS,
    imageMimeTypes: IMAGE_MIME_TYPES,
    iconExtensions: null,
    iconMimeTypes: 'image/png,image/x-icon',

    previewGuid: (new Date()).valueOf(),

    frontendUrl: computed('config.blogUrl', function () {
        return `${this.get('config.blogUrl')}`;
    }),

    themePreviewUrl: computed(function () {
        let origin = window.location.origin;
        let subdir = this.ghostPaths.subdir;
        let url = this.ghostPaths.url.join(origin, subdir);

        return url.replace(/\/$/, '/ghost/preview/');
    }),

    accentColorPickerValue: computed('settings.accentColor', function () {
        return this.get('settings.accentColor') || '#ffffff';
    }),

    accentColor: computed('settings.accentColor', function () {
        let color = this.get('settings.accentColor');
        if (color && color[0] === '#') {
            return color.slice(1);
        }
        return color;
    }),

    accentColorBgStyle: computed(function () {
        return htmlSafe(`background-color: ${this.accentColorPickerValue}`);
    }),

    getPreviewData: computed('settings.{accentColor,icon,logo,coverImage}', function () {
        let string = `c=${encodeURIComponent(this.get('settings.accentColor'))}&icon=${encodeURIComponent(this.get('settings.icon'))}&logo=${encodeURIComponent(this.get('settings.logo'))}&cover=${encodeURIComponent(this.get('settings.coverImage'))}`;

        return string;
    }),

    init() {
        this._super(...arguments);
        this.iconExtensions = ICON_EXTENSIONS;
    },

    didInsertElement() {
        this._previewListener = run.bind(this, this.handlePreviewIframeMessage);
        window.addEventListener('message', this._previewListener);
    },

    willDestroyElement() {
        window.removeEventListener('message', this._previewListener);
    },

    actions: {
        save() {
            this.save.perform();
        },

        toggleLeaveSettingsModal(transition) {
            let leaveTransition = this.leaveSettingsTransition;

            if (!transition && this.showLeaveSettingsModal) {
                this.set('leaveSettingsTransition', null);
                this.set('showLeaveSettingsModal', false);
                return;
            }

            if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
                this.set('leaveSettingsTransition', transition);

                // if a save is running, wait for it to finish then transition
                if (this.save.isRunning) {
                    return this.save.last.then(() => {
                        transition.retry();
                    });
                }

                // we genuinely have unsaved data, show the modal
                this.set('showLeaveSettingsModal', true);
            }
        },

        leaveSettings() {
            let transition = this.leaveSettingsTransition;
            let settings = this.settings;

            if (!transition) {
                this.notifications.showAlert('Sorry, there was an error in the application. Please let the Ghost team know what happened.', {type: 'error'});
                return;
            }

            // roll back changes on settings props
            settings.rollbackAttributes();

            return transition.retry();
        },

        reset() {},

        async removeImage(image) {
            // setting `null` here will error as the server treats it as "null"
            this.settings.set(image, '');
            this.refreshPreview();
        },

        /**
         * Opens a file selection dialog - Triggered by "Upload Image" buttons,
         * searches for the hidden file input within the .gh-setting element
         * containing the clicked button then simulates a click
         * @param  {MouseEvent} event - MouseEvent fired by the button click
         */
        triggerFileDialog(event) {
            // simulate click to open file dialog
            // using jQuery because IE11 doesn't support MouseEvent
            $(event.target)
                .closest('.gh-uploadbutton-container')
                .find('input[type="file"]')
                .click();
        },

        /**
         * Fired after an image upload completes
         * @param  {string} property - Property name to be set on `this.settings`
         * @param  {UploadResult[]} results - Array of UploadResult objects
         * @return {string} The URL that was set on `this.settings.property`
         */
        async imageUploaded(property, results) {
            if (results[0]) {
                let result = this.settings.set(property, results[0].url);
                this.refreshPreview();
                return result;
            }
        },

        updateAccentColor(event) {
            this._updateAccentColor(event);
        }
    },

    replacePreview() {
        const ghostFrontendUrl = this.frontendUrl;

        const options = {
            contentType: 'text/html;charset=utf-8',
            // ember-ajax will try and parse the response as JSON if not explicitly set
            dataType: 'text',
            headers: {
                'x-ghost-preview': this.getPreviewData
            }
        };

        this.ajax
            .post(ghostFrontendUrl, options)
            .then((response) => {
                const iframe = this.getPreviewIframe();
                if (iframe) {
                    iframe.contentWindow.postMessage(response, '*');
                }
            });
    },

    refreshPreview() {
        // this.set('previewGuid',(new Date()).valueOf());
        // REset the src and trigger a reload
        this.getPreviewIframe().src = this.themePreviewUrl;
    },

    getPreviewIframe() {
        return document.getElementById('site-frame');
    },

    handlePreviewIframeMessage(event) {
        if (event && event.data && event.data === 'loaded') {
            this.replacePreview();
        }
    },

    debounceUpdateAccentColor: task(function* (event) {
        yield timeout(500);
        this._updateAccentColor(event);
    }).restartable(),

    saveTask: task(function* () {
        let notifications = this.notifications;
        let validationPromises = [];

        try {
            yield RSVP.all(validationPromises);
            return yield this.settings.save();
        } catch (error) {
            if (error) {
                notifications.showAPIError(error);
                throw error;
            }
        }
    }),

    async _updateAccentColor(event) {
        let newColor = event.target.value;
        let oldColor = this.get('settings.accentColor');

        // reset errors and validation
        this.get('settings.errors').remove('accentColor');
        this.get('settings.hasValidated').removeObject('accentColor');

        if (newColor === '') {
            if (newColor === oldColor) {
                return;
            }

            // clear out the accent color
            this.settings.set('accentColor', '');
            this.refreshPreview();
            return;
        }

        // accentColor will be null unless the user has input something
        if (!newColor) {
            newColor = oldColor;
        }

        if (newColor[0] !== '#') {
            newColor = `#${newColor}`;
        }

        if (newColor.match(/#[0-9A-Fa-f]{6}$/)) {
            if (newColor === oldColor) {
                return;
            }

            this.set('settings.accentColor', newColor);
            this.refreshPreview();
        } else {
            this.get('settings.errors').add('accentColor', 'The colour should be in valid hex format');
            this.get('settings.hasValidated').pushObject('accentColor');
            return;
        }
    }
});
<commit_msg>Fixed brand settings not resetting when cancelling<commit_after>/* eslint-disable ghost/ember/alias-model-in-controller */
import $ from 'jquery';
import ModalComponent from 'ghost-admin/components/modal-base';
import RSVP from 'rsvp';
import {
    IMAGE_EXTENSIONS,
    IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {computed} from '@ember/object';
import {htmlSafe} from '@ember/string';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';
import {timeout} from 'ember-concurrency';

const ICON_EXTENSIONS = ['ico', 'png'];

export default ModalComponent.extend({
    config: service(),
    ghostPaths: service(),
    notifications: service(),
    session: service(),
    settings: service(),
    ajax: service(),

    imageExtensions: IMAGE_EXTENSIONS,
    imageMimeTypes: IMAGE_MIME_TYPES,
    iconExtensions: null,
    iconMimeTypes: 'image/png,image/x-icon',

    previewGuid: (new Date()).valueOf(),

    frontendUrl: computed('config.blogUrl', function () {
        return `${this.get('config.blogUrl')}`;
    }),

    themePreviewUrl: computed(function () {
        let origin = window.location.origin;
        let subdir = this.ghostPaths.subdir;
        let url = this.ghostPaths.url.join(origin, subdir);

        return url.replace(/\/$/, '/ghost/preview/');
    }),

    accentColorPickerValue: computed('settings.accentColor', function () {
        return this.get('settings.accentColor') || '#ffffff';
    }),

    accentColor: computed('settings.accentColor', function () {
        let color = this.get('settings.accentColor');
        if (color && color[0] === '#') {
            return color.slice(1);
        }
        return color;
    }),

    accentColorBgStyle: computed(function () {
        return htmlSafe(`background-color: ${this.accentColorPickerValue}`);
    }),

    getPreviewData: computed('settings.{accentColor,icon,logo,coverImage}', function () {
        let string = `c=${encodeURIComponent(this.get('settings.accentColor'))}&icon=${encodeURIComponent(this.get('settings.icon'))}&logo=${encodeURIComponent(this.get('settings.logo'))}&cover=${encodeURIComponent(this.get('settings.coverImage'))}`;

        return string;
    }),

    init() {
        this._super(...arguments);
        this.iconExtensions = ICON_EXTENSIONS;
    },

    didInsertElement() {
        this._previewListener = run.bind(this, this.handlePreviewIframeMessage);
        window.addEventListener('message', this._previewListener);
    },

    willDestroyElement() {
        window.removeEventListener('message', this._previewListener);
        // reset any unsaved changes when closing
        this.settings.rollbackAttributes();
    },

    actions: {
        save() {
            this.save.perform();
        },

        async removeImage(image) {
            // setting `null` here will error as the server treats it as "null"
            this.settings.set(image, '');
            this.refreshPreview();
        },

        /**
         * Opens a file selection dialog - Triggered by "Upload Image" buttons,
         * searches for the hidden file input within the .gh-setting element
         * containing the clicked button then simulates a click
         * @param  {MouseEvent} event - MouseEvent fired by the button click
         */
        triggerFileDialog(event) {
            // simulate click to open file dialog
            // using jQuery because IE11 doesn't support MouseEvent
            $(event.target)
                .closest('.gh-uploadbutton-container')
                .find('input[type="file"]')
                .click();
        },

        /**
         * Fired after an image upload completes
         * @param  {string} property - Property name to be set on `this.settings`
         * @param  {UploadResult[]} results - Array of UploadResult objects
         * @return {string} The URL that was set on `this.settings.property`
         */
        async imageUploaded(property, results) {
            if (results[0]) {
                let result = this.settings.set(property, results[0].url);
                this.refreshPreview();
                return result;
            }
        },

        updateAccentColor(event) {
            this._updateAccentColor(event);
        }
    },

    replacePreview() {
        const ghostFrontendUrl = this.frontendUrl;

        const options = {
            contentType: 'text/html;charset=utf-8',
            // ember-ajax will try and parse the response as JSON if not explicitly set
            dataType: 'text',
            headers: {
                'x-ghost-preview': this.getPreviewData
            }
        };

        this.ajax
            .post(ghostFrontendUrl, options)
            .then((response) => {
                const iframe = this.getPreviewIframe();
                if (iframe) {
                    iframe.contentWindow.postMessage(response, '*');
                }
            });
    },

    refreshPreview() {
        // this.set('previewGuid',(new Date()).valueOf());
        // REset the src and trigger a reload
        this.getPreviewIframe().src = this.themePreviewUrl;
    },

    getPreviewIframe() {
        return document.getElementById('site-frame');
    },

    handlePreviewIframeMessage(event) {
        if (event && event.data && event.data === 'loaded') {
            this.replacePreview();
        }
    },

    debounceUpdateAccentColor: task(function* (event) {
        yield timeout(500);
        this._updateAccentColor(event);
    }).restartable(),

    saveTask: task(function* () {
        let notifications = this.notifications;
        let validationPromises = [];

        try {
            yield RSVP.all(validationPromises);
            yield this.settings.save();
            this.closeModal();
            return true;
        } catch (error) {
            if (error) {
                notifications.showAPIError(error);
                throw error;
            }
        }
    }),

    async _updateAccentColor(event) {
        let newColor = event.target.value;
        let oldColor = this.get('settings.accentColor');

        // reset errors and validation
        this.get('settings.errors').remove('accentColor');
        this.get('settings.hasValidated').removeObject('accentColor');

        if (newColor === '') {
            if (newColor === oldColor) {
                return;
            }

            // clear out the accent color
            this.settings.set('accentColor', '');
            this.refreshPreview();
            return;
        }

        // accentColor will be null unless the user has input something
        if (!newColor) {
            newColor = oldColor;
        }

        if (newColor[0] !== '#') {
            newColor = `#${newColor}`;
        }

        if (newColor.match(/#[0-9A-Fa-f]{6}$/)) {
            if (newColor === oldColor) {
                return;
            }

            this.set('settings.accentColor', newColor);
            this.refreshPreview();
        } else {
            this.get('settings.errors').add('accentColor', 'The colour should be in valid hex format');
            this.get('settings.hasValidated').pushObject('accentColor');
            return;
        }
    }
});
<|endoftext|>


<commit_before>define([
    "./dom",
    "./crud",
    "./schema"
], function (
    dom,
    crud,
    schema
) {

    "use strict";

    //this maps CRUD operations to link methods
    var actions = {
        "GET": function (link, links, data, container) {
            renderer.render(link.href, container);
        },
        "POST": function (link, links, data, container) {

            console.log("rendering create form for " + link.rel);
            var s = schema.load(link.schema.$ref),
                selfLink = schema.find(links, "schema/rel/self"),
                backLink;

            link.schema = s;

            if (selfLink) {
                backLink = {
                    rel: "schema/rel/up",
                    "method": selfLink.method,
                    "href": selfLink.href
                };
            }

            createForm(link, backLink, data, container);

        },
        "PUT": function (link, links, data, container) {

            console.log("rendering edit form for " + link.rel);
            var s = schema.load(link.schema.$ref),
                selfLink = schema.find(links, "schema/rel/self"),
                backLink;

            link.schema = s;

            if (selfLink) {
                backLink = {
                    rel: "schema/rel/up",
                    "method": selfLink.method,
                    "href": selfLink.href
                };
            }

            createForm(link, backLink, data, container);

        },
        "DELETE": function (link, links, data, container) {
            crud.exec(link, null, function (response) {
                var parentLink = schema.find(links, "schema/rel/collection");
                renderer.render(parentLink.href, container);
            });
        }
    },
    //these are links we don't show directly
    hideLinks = {
        "schema/rel/monitor": true, //monitor can be used to setup polling
        "schema/rel/item": true //TODO: this should be used to load items in a collection, rather than clicking the href like we currently do
    },
    //properties on the response objects that may be "private" so we don't really want to show them
    hideProps = {
        "_id": true,
        "uri": true
    },
    friendlyMap = {
        "schema/rel/self": "Refresh",
        "schema/rel/collection": "Parent List"
    };

    //make a friendly label
    function friendly(rel) {
        var text = friendlyMap[rel],
            word;
        if (!text) {
            //if there isn't any friendly text pre-defined for a link, we'll make it look a little prettier
            word = rel.substring(rel.lastIndexOf("/") + 1, rel.length);
            text = word.substring(0, 1).toUpperCase() + word.substring(1, word.length).replace("-", " ");
        }
        return text;
    }

    function createForm(link, backLink, item, container) {

        console.log("creating form for " + link.rel);

        dom.empty(container);

        var header = dom.create("header", {innerHTML: friendly(link.rel).toUpperCase()}, container),
            schema = link.schema,
            properties = schema.properties,
            keys = Object.keys(properties),
            elements = {};

        keys.sort();

        keys.forEach(function (key) {

            var value = properties[key],
                label, element;

            if (!hideProps[key]) {
                label = dom.create("label", {innerHTML: key}, container);
                element = dom.create("input", {
                    type: "text",
                    className: "item-" + key
                });

                if (item && item[key]) {
                    element.value = item[key];
                }

                if (value.readonly) {
                    label.style.display = "none";
                    element.style.display = "none";
                }

                elements[key] = element;

                dom.append(container, element);
                dom.create("br", null, container);

            } else {
                console.log(key + " is hidden or readonly");
            }

        });

        //create the submit button
        dom.create("button", {
            innerHTML: "Submit",
            onclick: function (e) {
                e.target.disabled = true;
                var data = {};
                Object.keys(elements).forEach(function (key) {
                    var element = elements[key];
                    data[key] = element.value;
                });
                crud.exec(link, data, function (response) {
                    console.log("received successful edit");
                    //go back to the previous view now that the form submission is complete
                    renderer.render(backLink.href, container);
                });
            }
        }, container);

        renderLinks({
            links: [backLink]
        }, container);

        breadcrumb(link.href, container);

    }

    function addItem(item, table, container, includeLinkCell) {

        console.log("adding item " + item.uri);

        var keys = Object.keys(item),
            tr = dom.create("tr"),
            uri, linkcell, a;

        keys.sort();

        keys.forEach(function (key) {

            var value = item[key], td;

            if (key === "uri") {
                uri = value;
            }

            if (!hideProps[key]) {

                td = dom.create("td", {innerHTML: value, className: "item-" + key}, tr);

            }

        });

        //special cell to hold the object link
        if (includeLinkCell) {
            linkcell = dom.create("td", {className: "link-cell"}, tr);
            a = dom.create("a", {
                href: "#",
                innerHTML: "view",
                title: uri,
                className: "item-uri",
                onclick: function () {
                    renderer.render(uri, container);
                }
            }, linkcell);
        }

        dom.append(table, tr);
    }

    function tableStart(item, container, includeLinkCell) {
        //TODO: use the response schema to determine the table template, instead of grabbing an item from the list
        var keys = Object.keys(item),
            table = dom.create("table"),
            tr = dom.create("tr", null, table);

        keys.sort();

        keys.forEach(function (key) {
            if (!hideProps[key]) {
                dom.create("th", {innerHTML: key}, tr);
            }
        });

        if (includeLinkCell) {
            dom.create("th", {innerHTML: "details", className: "link-cell"}, tr);
        }

        dom.append(container, table);

        return table;
    }

    function renderItem(response, container) {
        var header = dom.create("header", {innerHTML: "ITEM"}, container),
            data = response.data,
            table;

        if (data) {
            table = tableStart(data, container, false);
            addItem(data, table, container);
        } else {
            dom.create("p", {innerHTML: "(No item details)"}, container);
        }

    }

    function renderItems(response, container) {

        var header = dom.create("header", {innerHTML: "ITEMS"}, container),
            data = response.data,
            items = data.items,
            table;

        if (items.length > 0) {
            table = tableStart(items[0], container, true);
            items.forEach(function (item) {
                addItem(item, table, container, true);
            });
        } else {
            dom.create("p", {innerHTML: "(No items in list)"}, container);
        }

    }

    function renderLinks(response, container) {

        var header = dom.create("header", {innerHTML: "LINKS"}, container),
            links = response.links;

        links.forEach(function (link) {
            var rel = link.rel,
                method = link.method || "GET",
                button, text = friendly(rel);

            if (!hideLinks[rel]) {

                button = dom.create("button", {
                    innerHTML: text,
                    onclick: function () {
                        console.log("link clicked! " + rel + " | " + method);
                        var action = actions[link.method];
                        action(link, links, response.data, container);

                    }
                }, container);

            }

        });

    }

    function breadcrumb(uri, container) {

        var splits = uri.split("/"),
            split,
            full = "",
            footnote = dom.create("footnote", {innerHTML: "You are here: "}),
            i, l, a, text;

        for (i = 0, l = splits.length; i < l; i += 1) {
            split = splits[i];
            full += split + "/";
            a = dom.create("a", {
                innerHTML: split,
                href: "#",
                tag: full,
                onclick: function (e) {
                    renderer.render(e.target.tag, container);
                }
            }, footnote);

            if (l > 1 && i < l - 1) {
                text = dom.create("text", {innerHTML: " / "}, footnote);
            }

        }

        dom.append(container, footnote);

    }

    var renderer = {

        render: function (uri, container) {

            dom.empty(container);

            crud.exec({href: uri}, function (response) {

                console.log("got response to render", response);

                if (response.data) {
                    if (response.data.items) {
                        renderItems(response, container);
                    } else {
                        renderItem(response, container);
                    }
                }

                renderLinks(response, container);

                breadcrumb(uri, container);

            });

        }

    };

    return renderer;
});<commit_msg>breadcrumb fix<commit_after>define([
    "./dom",
    "./crud",
    "./schema"
], function (
    dom,
    crud,
    schema
) {

    "use strict";

    //this maps CRUD operations to link methods
    var actions = {
        "GET": function (link, links, data, container) {
            renderer.render(link.href, container);
        },
        "POST": function (link, links, data, container) {

            console.log("rendering create form for " + link.rel);
            var s = schema.load(link.schema.$ref),
                selfLink = schema.find(links, "schema/rel/self"),
                backLink;

            link.schema = s;

            if (selfLink) {
                backLink = {
                    rel: "schema/rel/up",
                    "method": selfLink.method,
                    "href": selfLink.href
                };
            }

            createForm(link, backLink, data, container);

        },
        "PUT": function (link, links, data, container) {

            console.log("rendering edit form for " + link.rel);
            var s = schema.load(link.schema.$ref),
                selfLink = schema.find(links, "schema/rel/self"),
                backLink;

            link.schema = s;

            if (selfLink) {
                backLink = {
                    rel: "schema/rel/up",
                    "method": selfLink.method,
                    "href": selfLink.href
                };
            }

            createForm(link, backLink, data, container);

        },
        "DELETE": function (link, links, data, container) {
            crud.exec(link, null, function (response) {
                var parentLink = schema.find(links, "schema/rel/collection");
                renderer.render(parentLink.href, container);
            });
        }
    },
    //these are links we don't show directly
    hideLinks = {
        "schema/rel/monitor": true, //monitor can be used to setup polling
        "schema/rel/item": true //TODO: this should be used to load items in a collection, rather than clicking the href like we currently do
    },
    //properties on the response objects that may be "private" so we don't really want to show them
    hideProps = {
        "_id": true,
        "uri": true
    },
    friendlyMap = {
        "schema/rel/self": "Refresh",
        "schema/rel/collection": "Parent List"
    };

    //make a friendly label
    function friendly(rel) {
        var text = friendlyMap[rel],
            word;
        if (!text) {
            //if there isn't any friendly text pre-defined for a link, we'll make it look a little prettier
            word = rel.substring(rel.lastIndexOf("/") + 1, rel.length);
            text = word.substring(0, 1).toUpperCase() + word.substring(1, word.length).replace("-", " ");
        }
        return text;
    }

    function createForm(link, backLink, item, container) {

        console.log("creating form for " + link.rel);

        dom.empty(container);

        var header = dom.create("header", {innerHTML: friendly(link.rel).toUpperCase()}, container),
            schema = link.schema,
            properties = schema.properties,
            keys = Object.keys(properties),
            elements = {};

        keys.sort();

        keys.forEach(function (key) {

            var value = properties[key],
                label, element;

            if (!hideProps[key]) {
                label = dom.create("label", {innerHTML: key}, container);
                element = dom.create("input", {
                    type: "text",
                    className: "item-" + key
                });

                if (item && item[key]) {
                    element.value = item[key];
                }

                if (value.readonly) {
                    label.style.display = "none";
                    element.style.display = "none";
                }

                elements[key] = element;

                dom.append(container, element);
                dom.create("br", null, container);

            } else {
                console.log(key + " is hidden or readonly");
            }

        });

        //create the submit button
        dom.create("button", {
            innerHTML: "Submit",
            onclick: function (e) {
                e.target.disabled = true;
                var data = {};
                Object.keys(elements).forEach(function (key) {
                    var element = elements[key];
                    data[key] = element.value;
                });
                crud.exec(link, data, function (response) {
                    console.log("received successful edit");
                    //go back to the previous view now that the form submission is complete
                    renderer.render(backLink.href, container);
                });
            }
        }, container);

        renderLinks({
            links: [backLink]
        }, container);

        breadcrumb(link.href, container);

    }

    function addItem(item, table, container, includeLinkCell) {

        console.log("adding item " + item.uri);

        var keys = Object.keys(item),
            tr = dom.create("tr"),
            uri, linkcell, a;

        keys.sort();

        keys.forEach(function (key) {

            var value = item[key], td;

            if (key === "uri") {
                uri = value;
            }

            if (!hideProps[key]) {

                td = dom.create("td", {innerHTML: value, className: "item-" + key}, tr);

            }

        });

        //special cell to hold the object link
        if (includeLinkCell) {
            linkcell = dom.create("td", {className: "link-cell"}, tr);
            a = dom.create("a", {
                href: "#",
                innerHTML: "view",
                title: uri,
                className: "item-uri",
                onclick: function () {
                    renderer.render(uri, container);
                }
            }, linkcell);
        }

        dom.append(table, tr);
    }

    function tableStart(item, container, includeLinkCell) {
        //TODO: use the response schema to determine the table template, instead of grabbing an item from the list
        var keys = Object.keys(item),
            table = dom.create("table"),
            tr = dom.create("tr", null, table);

        keys.sort();

        keys.forEach(function (key) {
            if (!hideProps[key]) {
                dom.create("th", {innerHTML: key}, tr);
            }
        });

        if (includeLinkCell) {
            dom.create("th", {innerHTML: "details", className: "link-cell"}, tr);
        }

        dom.append(container, table);

        return table;
    }

    function renderItem(response, container) {
        var header = dom.create("header", {innerHTML: "ITEM"}, container),
            data = response.data,
            table;

        if (data) {
            table = tableStart(data, container, false);
            addItem(data, table, container);
        } else {
            dom.create("p", {innerHTML: "(No item details)"}, container);
        }

    }

    function renderItems(response, container) {

        var header = dom.create("header", {innerHTML: "ITEMS"}, container),
            data = response.data,
            items = data.items,
            table;

        if (items.length > 0) {
            table = tableStart(items[0], container, true);
            items.forEach(function (item) {
                addItem(item, table, container, true);
            });
        } else {
            dom.create("p", {innerHTML: "(No items in list)"}, container);
        }

    }

    function renderLinks(response, container) {

        var header = dom.create("header", {innerHTML: "LINKS"}, container),
            links = response.links;

        links.forEach(function (link) {
            var rel = link.rel,
                method = link.method || "GET",
                button, text = friendly(rel);

            if (!hideLinks[rel]) {

                button = dom.create("button", {
                    innerHTML: text,
                    onclick: function () {
                        console.log("link clicked! " + rel + " | " + method);
                        var action = actions[link.method];
                        action(link, links, response.data, container);

                    }
                }, container);

            }

        });

    }

    function breadcrumb(uri, container) {

        var splits = uri.split("/"),
            split,
            full = "",
            footnote = dom.create("footnote", {innerHTML: "You are here: "}),
            i, l, a, text;

        for (i = 0, l = splits.length; i < l; i += 1) {
            split = splits[i];
            full += split;
            if (i < l - 1) {
                full += "/";
            }
            a = dom.create("a", {
                innerHTML: split,
                href: "#",
                tag: full,
                onclick: function (e) {
                    renderer.render(e.target.tag, container);
                }
            }, footnote);

            if (l > 1 && i < l - 1) {
                text = dom.create("text", {innerHTML: " / "}, footnote);
            }

        }

        dom.append(container, footnote);

    }

    var renderer = {

        render: function (uri, container) {

            dom.empty(container);

            crud.exec({href: uri}, function (response) {

                console.log("got response to render", response);

                if (response.data) {
                    if (response.data.items) {
                        renderItems(response, container);
                    } else {
                        renderItem(response, container);
                    }
                }

                renderLinks(response, container);

                breadcrumb(uri, container);

            });

        }

    };

    return renderer;
});<|endoftext|>


<commit_before>import Ember from 'ember';
import THREE from "npm:three";
import config from '../config/environment';
import THREEPerformance from '../mixins/threejs-performance';

/**
* This component contains the core mechanics of the different (three.js-based)
* renderer. All functions below are called in a determined order, hence you only
* need to override them in your custom renderer.
*
* See {{#crossLink "Landscape-Rendering"}}{{/crossLink}} or
* {{#crossLink "Application-Rendering"}}{{/crossLink}} for example usage.
*
* Call order:
*
* 1.
*
* @class Rendering-Core
* @extends Ember.Component
*/
export default Ember.Component.extend(Ember.Evented, THREEPerformance, {

  state: null,

  // Declare url-builder service
  urlBuilder: Ember.inject.service("url-builder"),

  // Declare view-importer service
  viewImporter: Ember.inject.service("view-importer"),

  reloadHandler: Ember.inject.service("reload-handler"),
  landscapeRepo: Ember.inject.service("repos/landscape-repository"),
  renderingService: Ember.inject.service(),

  classNames: ['viz'],

  scene : null,
  webglrenderer: null,
  camera: null,

  canvas: null,

  font: null,
  animationFrameId: null,

  initDone: false,

  // @Override
  didRender(){
    this._super(...arguments);
    this.initRendering();
    this.initListener();
  },

  // @Override
  willDestroyElement() {
    this._super(...arguments);
    this.cleanup();
  },

  /**
   * This function is called once on the didRender event. Inherit this function
   * to call other important function, e.g. initInteraction as shown in
   * {@landscape-rendering}.
   *
   * @method initRendering
   */
  initRendering() {

    this.debug('init rendering-core');

    const self = this;

    // get size if outer ember div
    const height = this.$()[0].clientHeight;
    const width = this.$()[0].clientWidth;

    const canvas = this.$('#threeCanvas')[0];

    this.set('canvas', canvas);

    this.set('scene', new THREE.Scene());
    this.set('scene.background', new THREE.Color(0xffffff));

    this.set('camera', new THREE.PerspectiveCamera(75, width / height, 0.1, 1000));

    this.set('webglrenderer', new THREE.WebGLRenderer({
      antialias: true,
      canvas: canvas
    }));
    this.get('webglrenderer').setPixelRatio(window.devicePixelRatio);
    this.get('webglrenderer').setSize(width, height);

    // Rendering loop //
    function render() {
      const animationId = requestAnimationFrame(render);
      self.set('animationFrameId', animationId);

      if(config.environment === "development" || config.environment === "akr") {
        self.get('threexStats').update(self.get('webglrenderer'));
        self.get('stats').begin();
      }

      self.get('webglrenderer').render(self.get('scene'), self.get('camera'));

      if(config.environment === "development" || config.environment === "akr") {
        self.get('stats').end();
      }

    }

    render();

    ////////////////////

    // load font for labels and synchronously proceed with populating the scene
    new THREE.FontLoader()
      .load('three.js/fonts/roboto_mono_bold_typeface.json', function(font) {

      self.set('font', font);
      self.set('initDone', true);
      self.populateScene();
      // import new view
      self.importView();
    });

  },

  initListener() {

    const self = this;

    this.$(window).on('resize.visualization', function(){
      const outerDiv = this.$('.viz')[0];

      if(outerDiv) {

        const height = Math.round(this.$('.viz').height());
        const width = Math.round(this.$('.viz').width());

        self.set('camera.aspect', width / height);
        self.get('camera').updateProjectionMatrix();

        self.get('webglrenderer').setSize(width, height);

        self.onResized();
      }
    });

    this.get('viewImporter').on('transmitView', function(newState) {
        self.set('newState', newState);
    });

    this.get('renderingService').on('reSetupScene', function() {
      self.onReSetupScene();
    });

    this.get('urlBuilder').on('requestURL', function() {
      const state = {};

      // get timestamp
      state.timestamp = self.get('landscapeRepo.latestLandscape')
        .get('timestamp');

      // get latestApp, may be null
      const latestMaybeApp = self.get('landscapeRepo.latestApplication');
      state.appID = latestMaybeApp ? latestMaybeApp.get('id') : null;

      state.camX = self.get('camera').position.x;
      state.camY = self.get('camera').position.y;
      state.camZ = self.get('camera').position.z;

      // Passes the state from component via service to controller
      self.get('urlBuilder').transmitState(state);
    });

    this.get('landscapeRepo').on("updated", function() {
      self.onUpdated();
    });

  },

  /**
    This method is used to update the camera with query parameters
  */
  importView(){

    this.get('viewImporter').requestView();

    const camX = this.get('newState').camX;
    const camY = this.get('newState').camY;
    const camZ = this.get('newState').camZ;

    if(!isNaN(camX)){
      this.get('camera').position.x = camX;
    }
    if(!isNaN(camY)){
      this.get('camera').position.y = camY;
    }
    if(!isNaN(camZ)){
      console.log(camZ);
      this.get('camera').position.z = camZ;
    }
    this.get('camera').updateProjectionMatrix();

    // load actual landscape

    const timestamp = this.get('newState').timestamp;
    const appID = this.get('newState').appID;

    if(timestamp) {
      this.get('reloadHandler').stopExchange();
      this.get('reloadHandler').loadLandscapeById(timestamp, appID);
    }
  },

  /**
   * This function is called once on initRendering. Inherit this function to
   * insert objects in the Three.js scene. Have a look
   * at {{#crossLink "Landscape-Rendering"}}{{/crossLink}} for an example.
   *
   * @method populateScene
   */
  populateScene() {},

  /**
   * This function is called when the willDestroyElement event is fired. Inherit this
   * function to cleanup custom properties or unbind listener
   * as shown in {{#crossLink "Landscape-Rendering"}}{{/crossLink}}.
   *
   * @method cleanup
   */
  cleanup() {
    cancelAnimationFrame(this.get('animationFrameId'));

    this.set('scene', null);
    this.set('webglrenderer', null);
    this.set('camera', null);
    this.get('urlBuilder').off('requestURL');

    this.removePerformanceMeasurement();

    this.$(window).off('resize.visualization');
    this.get('viewImporter').off('transmitView');
    this.get('renderingService').off('reSetupScene');
    this.get('landscapeRepo').off('updated');
  },

  /**
   * Inherit this function to update the scene with a new renderingModel. It
   * automatically removes every mesh from the scene. Add your custom code
   * as shown in landscape-rendering.
   *
   * @method cleanAndUpdateScene
   */
  cleanAndUpdateScene() {
    const scene = this.get('scene');

    removeAllChildren(scene);

    function removeAllChildren(entity) {
      for (let i = entity.children.length - 1; i >= 0 ; i--) {
        let child = entity.children[i];

        removeAllChildren(child);

        if (child.type !== 'AmbientLight' && child.type !== 'SpotLight' && child.type !== 'DirectionalLight') {
          if(child.type !== 'Object3D') {
            child.geometry.dispose();
            child.material.dispose();
          }
          entity.remove(child);
        }
      }
    }
  },

  /**
   * This function is called automatically when a new landscape was fetched. It
   * is executed before
   * {{#crossLink "Rendering-Core/cleanAndUpdateScene:method"}}{{/crossLink}}.
   * Inherit this function to preprocess the
   * {{#crossLink "Landscape"}}{{/crossLink}} for rendering, e.g. filter some
   * value.
   *
   * See {{#crossLink "Application-Rendering"}}{{/crossLink}} for example usage.
   *
   * @method preProcessEntity
   */
  preProcessEntity() {},

  // Listener-Callbacks. Override in extending components

  onReSetupScene() {},

  onUpdated() {},

  onResized() {}

});
<commit_msg>clean up WebGL rendering context<commit_after>import Ember from 'ember';
import THREE from "npm:three";
import config from '../config/environment';
import THREEPerformance from '../mixins/threejs-performance';

/**
* This component contains the core mechanics of the different (three.js-based)
* renderer. All functions below are called in a determined order, hence you only
* need to override them in your custom renderer.
*
* See {{#crossLink "Landscape-Rendering"}}{{/crossLink}} or
* {{#crossLink "Application-Rendering"}}{{/crossLink}} for example usage.
*
* Call order:
*
* 1.
*
* @class Rendering-Core
* @extends Ember.Component
*/
export default Ember.Component.extend(Ember.Evented, THREEPerformance, {

  state: null,

  // Declare url-builder service
  urlBuilder: Ember.inject.service("url-builder"),

  // Declare view-importer service
  viewImporter: Ember.inject.service("view-importer"),

  reloadHandler: Ember.inject.service("reload-handler"),
  landscapeRepo: Ember.inject.service("repos/landscape-repository"),
  renderingService: Ember.inject.service(),

  classNames: ['viz'],

  scene : null,
  webglrenderer: null,
  camera: null,

  canvas: null,

  font: null,
  animationFrameId: null,

  initDone: false,

  // @Override
  didRender(){
    this._super(...arguments);
    this.initRendering();
    this.initListener();
  },

  // @Override
  willDestroyElement() {
    this._super(...arguments);
    this.cleanup();
  },

  /**
   * This function is called once on the didRender event. Inherit this function
   * to call other important function, e.g. initInteraction as shown in
   * {@landscape-rendering}.
   *
   * @method initRendering
   */
  initRendering() {

    this.debug('init rendering-core');

    const self = this;

    // get size if outer ember div
    const height = this.$()[0].clientHeight;
    const width = this.$()[0].clientWidth;

    const canvas = this.$('#threeCanvas')[0];

    this.set('canvas', canvas);

    this.set('scene', new THREE.Scene());
    this.set('scene.background', new THREE.Color(0xffffff));

    this.set('camera', new THREE.PerspectiveCamera(75, width / height, 0.1, 1000));

    this.set('webglrenderer', new THREE.WebGLRenderer({
      antialias: true,
      canvas: canvas
    }));
    this.get('webglrenderer').setPixelRatio(window.devicePixelRatio);
    this.get('webglrenderer').setSize(width, height);

    // Rendering loop //
    function render() {
      const animationId = requestAnimationFrame(render);
      self.set('animationFrameId', animationId);

      if(config.environment === "development" || config.environment === "akr") {
        self.get('threexStats').update(self.get('webglrenderer'));
        self.get('stats').begin();
      }

      self.get('webglrenderer').render(self.get('scene'), self.get('camera'));

      if(config.environment === "development" || config.environment === "akr") {
        self.get('stats').end();
      }

    }

    render();

    ////////////////////

    // load font for labels and synchronously proceed with populating the scene
    new THREE.FontLoader()
      .load('three.js/fonts/roboto_mono_bold_typeface.json', function(font) {

      self.set('font', font);
      self.set('initDone', true);
      self.populateScene();
      // import new view
      self.importView();
    });

  },

  initListener() {

    const self = this;

    this.$(window).on('resize.visualization', function(){
      const outerDiv = this.$('.viz')[0];

      if(outerDiv) {

        const height = Math.round(this.$('.viz').height());
        const width = Math.round(this.$('.viz').width());

        self.set('camera.aspect', width / height);
        self.get('camera').updateProjectionMatrix();

        self.get('webglrenderer').setSize(width, height);

        self.onResized();
      }
    });

    this.get('viewImporter').on('transmitView', function(newState) {
        self.set('newState', newState);
    });

    this.get('renderingService').on('reSetupScene', function() {
      self.onReSetupScene();
    });

    this.get('urlBuilder').on('requestURL', function() {
      const state = {};

      // get timestamp
      state.timestamp = self.get('landscapeRepo.latestLandscape')
        .get('timestamp');

      // get latestApp, may be null
      const latestMaybeApp = self.get('landscapeRepo.latestApplication');
      state.appID = latestMaybeApp ? latestMaybeApp.get('id') : null;

      state.camX = self.get('camera').position.x;
      state.camY = self.get('camera').position.y;
      state.camZ = self.get('camera').position.z;

      // Passes the state from component via service to controller
      self.get('urlBuilder').transmitState(state);
    });

    this.get('landscapeRepo').on("updated", function() {
      self.onUpdated();
    });

  },

  /**
    This method is used to update the camera with query parameters
  */
  importView(){

    this.get('viewImporter').requestView();

    const camX = this.get('newState').camX;
    const camY = this.get('newState').camY;
    const camZ = this.get('newState').camZ;

    if(!isNaN(camX)){
      this.get('camera').position.x = camX;
    }
    if(!isNaN(camY)){
      this.get('camera').position.y = camY;
    }
    if(!isNaN(camZ)){
      console.log(camZ);
      this.get('camera').position.z = camZ;
    }
    this.get('camera').updateProjectionMatrix();

    // load actual landscape

    const timestamp = this.get('newState').timestamp;
    const appID = this.get('newState').appID;

    if(timestamp) {
      this.get('reloadHandler').stopExchange();
      this.get('reloadHandler').loadLandscapeById(timestamp, appID);
    }
  },

  /**
   * This function is called once on initRendering. Inherit this function to
   * insert objects in the Three.js scene. Have a look
   * at {{#crossLink "Landscape-Rendering"}}{{/crossLink}} for an example.
   *
   * @method populateScene
   */
  populateScene() {},

  /**
   * This function is called when the willDestroyElement event is fired. Inherit this
   * function to cleanup custom properties or unbind listener
   * as shown in {{#crossLink "Landscape-Rendering"}}{{/crossLink}}.
   *
   * @method cleanup
   */
  cleanup() {
    cancelAnimationFrame(this.get('animationFrameId'));

    this.set('scene', null);
    this.set('webglrenderer', null);

    // clean up WebGL rendering context by forcing context loss
    var gl = this.get('canvas').getContext('webgl');
    gl.getExtension('WEBGL_lose_context').loseContext();

    this.set('camera', null);
    this.get('urlBuilder').off('requestURL');

    this.removePerformanceMeasurement();

    this.$(window).off('resize.visualization');
    this.get('viewImporter').off('transmitView');
    this.get('renderingService').off('reSetupScene');
    this.get('landscapeRepo').off('updated');
  },

  /**
   * Inherit this function to update the scene with a new renderingModel. It
   * automatically removes every mesh from the scene. Add your custom code
   * as shown in landscape-rendering.
   *
   * @method cleanAndUpdateScene
   */
  cleanAndUpdateScene() {
    const scene = this.get('scene');

    removeAllChildren(scene);

    function removeAllChildren(entity) {
      for (let i = entity.children.length - 1; i >= 0 ; i--) {
        let child = entity.children[i];

        removeAllChildren(child);

        if (child.type !== 'AmbientLight' && child.type !== 'SpotLight' && child.type !== 'DirectionalLight') {
          if(child.type !== 'Object3D') {
            child.geometry.dispose();
            child.material.dispose();
          }
          entity.remove(child);
        }
      }
    }
  },

  /**
   * This function is called automatically when a new landscape was fetched. It
   * is executed before
   * {{#crossLink "Rendering-Core/cleanAndUpdateScene:method"}}{{/crossLink}}.
   * Inherit this function to preprocess the
   * {{#crossLink "Landscape"}}{{/crossLink}} for rendering, e.g. filter some
   * value.
   *
   * See {{#crossLink "Application-Rendering"}}{{/crossLink}} for example usage.
   *
   * @method preProcessEntity
   */
  preProcessEntity() {},

  // Listener-Callbacks. Override in extending components

  onReSetupScene() {},

  onUpdated() {},

  onResized() {}

});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import List from 'components/common/list';
import AreaList from 'containers/settings/area-list';
import {
  View,
  Text,
  TouchableHighlight,
  ScrollView,
  Image
} from 'react-native';

import DatePicker from 'react-native-datepicker';
import LeftBtn from 'components/common/header/left-btn';
import Theme from 'config/theme';
import I18n from 'locales';
import headerStyles from 'components/common/header/styles';
import tracker from 'helpers/googleAnalytics';
import moment from 'moment';

import styles from './styles';

const plusIcon = require('assets/plus.png');

const dateFormat = 'YYYYMMDD';
const dateFormatDisplay = 'MMM Do YYYY';
class Settings extends Component {
  constructor() {
    super();
    this.state = {};
  }

  componentDidMount() {
    // cache disabled for now
    // if (!this.props.areas || !this.props.areas.length > 0) {
    tracker.trackScreenView('Set Up');
    this.props.getAreas();
    // }
  }

  onAreaPress = (areaId) => {
    this.props.navigate('AreaDetail', { id: areaId });
  }

  onLogoutPress = () => {
    this.props.logout();
    this.props.navigate('Home');
  }

  onDateChange = date => this.props.updateDate(date)

  handlePartnersLink = () => {
    this.props.navigate('Partners');
  }

  render() {
    const aboutSections = [
      {
        text: I18n.t('settings.aboutPartners'),
        image: null,
        functionOnPress: this.handlePartnersLink
      },
      {
        text: I18n.t('settings.aboutTerms'),
        image: null,
        functionOnPress: null
      }
    ];
    const { areas, navigate } = this.props;

    return (
      <View style={styles.container}>
        <ScrollView
          style={styles.list}
          contentContainerStyle={styles.listContent}
          showsVerticalScrollIndicator={false}
          showsHorizontalScrollIndicator={false}
        >
          <Text style={styles.label}>
            {I18n.t('settings.loggedIn')}
          </Text>

          <View style={styles.user}>
            <View style={styles.info}>
              <Text style={styles.name}>
                {this.props.user.fullName}
              </Text>
              <Text style={styles.email} numberOfLines={1} ellipsizeMode="tail" >
                {this.props.user.email}
              </Text>
            </View>
            <TouchableHighlight
              activeOpacity={0.5}
              underlayColor="transparent"
              onPress={this.onLogoutPress}
            >
              <Text style={styles.logout}>{I18n.t('settings.logOut')}</Text>
            </TouchableHighlight>
          </View>

          {areas && areas.length
            ? <View style={styles.areas}>
              <Text style={styles.label}>
                {I18n.t('settings.yourAreas')}
              </Text>
              <AreaList onAreaPress={(areaId) => this.onAreaPress(areaId)} />
            </View>
          : null
          }
          <TouchableHighlight
            activeOpacity={0.5}
            underlayColor="transparent"
            onPress={() => navigate('Setup')}
          >
            <View style={styles.addButton}>
              <Image style={Theme.icon} source={plusIcon} />
              <Text style={styles.addButtonText}>
                {I18n.t('settings.addArea').toUpperCase()}
              </Text>
            </View>
          </TouchableHighlight>

          <View style={styles.datesSection}>
            <Text style={styles.dateContainerLabel}>
              Time Frame
            </Text>
            <View style={styles.dateContainer}>
              <Text style={styles.dateLabel}>
                From
              </Text>
              <DatePicker
                showIcon={false}
                date={moment(this.props.fromDate, dateFormat).format(dateFormatDisplay)}
                mode="date"
                format={dateFormatDisplay}
                minDate="2010-01-01"
                // if set to null DatePicker will try to parse it as a date and crash, undefined prevents this
                maxDate={this.state.fromDate || undefined}
                placeholder={I18n.t('report.datePlaceholder')}
                cancelBtnText={I18n.t('commonText.cancel')}
                confirmBtnText={I18n.t('commonText.confirm')}
                onDateChange={date => this.onDateChange({ fromDate: moment(date, dateFormatDisplay).format(dateFormat) })}
                customStyles={{
                  dateInput: styles.dateInput,
                  dateText: styles.dateText
                }}
              />
            </View>
            <View style={styles.dateContainer}>
              <Text style={styles.dateLabel}>
                To
              </Text>
              <DatePicker
                style={styles.datePicker}
                showIcon={false}
                date={moment(this.props.toDate, dateFormat).format(dateFormatDisplay)}
                mode="date"
                format={dateFormatDisplay}
                minDate={this.state.fromDate || '2010-01-01'}
                placeholder={I18n.t('report.datePlaceholder')}
                cancelBtnText={I18n.t('commonText.cancel')}
                confirmBtnText={I18n.t('commonText.confirm')}
                onDateChange={date => this.onDateChange({ toDate: moment(date, dateFormatDisplay).format(dateFormat) })}
                customStyles={{
                  dateInput: styles.dateInput,
                  dateText: styles.dateText
                }}
              />
            </View>
          </View>

          <View style={styles.aboutSection}>
            <Text style={styles.label}>
              {I18n.t('settings.aboutApp')}
            </Text>

            <List content={aboutSections} bigSeparation={false}>{}</List>

            <Text style={[styles.label, styles.footerText]}>
              {I18n.t('commonText.appName')}
            </Text>

          </View>
        </ScrollView>
      </View>
    );
  }
}

Settings.propTypes = {
  user: React.PropTypes.any,
  areas: React.PropTypes.any,
  fromDate: React.PropTypes.string,
  toDate: React.PropTypes.string,
  navigate: React.PropTypes.func.isRequired,
  getAreas: React.PropTypes.func.isRequired,
  logout: React.PropTypes.func.isRequired,
  updateDate: React.PropTypes.func.isRequired
};

Settings.navigationOptions = {
  header: ({ goBack }) => ({
    left: <LeftBtn goBack={goBack} />,
    tintColor: Theme.colors.color1,
    style: headerStyles.style,
    titleStyle: headerStyles.titleStyle,
    title: I18n.t('settings.title')
  })
};

export default Settings;
<commit_msg>Fix minDates in datePicker<commit_after>import React, { Component } from 'react';
import List from 'components/common/list';
import AreaList from 'containers/settings/area-list';
import {
  View,
  Text,
  TouchableHighlight,
  ScrollView,
  Image
} from 'react-native';

import DatePicker from 'react-native-datepicker';
import LeftBtn from 'components/common/header/left-btn';
import Theme from 'config/theme';
import I18n from 'locales';
import headerStyles from 'components/common/header/styles';
import tracker from 'helpers/googleAnalytics';
import moment from 'moment';

import styles from './styles';

const plusIcon = require('assets/plus.png');

const dateFormat = 'YYYYMMDD';
const dateFormatDisplay = 'MMM Do YYYY';
const START_DATE = 'Jan 1st 2015';
class Settings extends Component {
  constructor() {
    super();
    this.state = {};
  }

  componentDidMount() {
    // cache disabled for now
    // if (!this.props.areas || !this.props.areas.length > 0) {
    tracker.trackScreenView('Set Up');
    this.props.getAreas();
    // }
  }

  onAreaPress = (areaId) => {
    this.props.navigate('AreaDetail', { id: areaId });
  }

  onLogoutPress = () => {
    this.props.logout();
    this.props.navigate('Home');
  }

  onDateChange = date => this.props.updateDate(date)

  handlePartnersLink = () => {
    this.props.navigate('Partners');
  }

  render() {
    const aboutSections = [
      {
        text: I18n.t('settings.aboutPartners'),
        image: null,
        functionOnPress: this.handlePartnersLink
      },
      {
        text: I18n.t('settings.aboutTerms'),
        image: null,
        functionOnPress: null
      }
    ];
    const { areas, navigate } = this.props;

    return (
      <View style={styles.container}>
        <ScrollView
          style={styles.list}
          contentContainerStyle={styles.listContent}
          showsVerticalScrollIndicator={false}
          showsHorizontalScrollIndicator={false}
        >
          <Text style={styles.label}>
            {I18n.t('settings.loggedIn')}
          </Text>

          <View style={styles.user}>
            <View style={styles.info}>
              <Text style={styles.name}>
                {this.props.user.fullName}
              </Text>
              <Text style={styles.email} numberOfLines={1} ellipsizeMode="tail" >
                {this.props.user.email}
              </Text>
            </View>
            <TouchableHighlight
              activeOpacity={0.5}
              underlayColor="transparent"
              onPress={this.onLogoutPress}
            >
              <Text style={styles.logout}>{I18n.t('settings.logOut')}</Text>
            </TouchableHighlight>
          </View>

          {areas && areas.length
            ? <View style={styles.areas}>
              <Text style={styles.label}>
                {I18n.t('settings.yourAreas')}
              </Text>
              <AreaList onAreaPress={(areaId) => this.onAreaPress(areaId)} />
            </View>
          : null
          }
          <TouchableHighlight
            activeOpacity={0.5}
            underlayColor="transparent"
            onPress={() => navigate('Setup')}
          >
            <View style={styles.addButton}>
              <Image style={Theme.icon} source={plusIcon} />
              <Text style={styles.addButtonText}>
                {I18n.t('settings.addArea').toUpperCase()}
              </Text>
            </View>
          </TouchableHighlight>

          <View style={styles.datesSection}>
            <Text style={styles.dateContainerLabel}>
              Time Frame
            </Text>
            <View style={styles.dateContainer}>
              <Text style={styles.dateLabel}>
                From
              </Text>
              <DatePicker
                showIcon={false}
                date={moment(this.props.fromDate, dateFormat).format(dateFormatDisplay)}
                mode="date"
                format={dateFormatDisplay}
                minDate={START_DATE}
                // if set to null DatePicker will try to parse it as a date and crash, undefined prevents this
                maxDate={moment(this.props.fromDate, dateFormat).format(dateFormatDisplay) || undefined}
                placeholder={I18n.t('report.datePlaceholder')}
                cancelBtnText={I18n.t('commonText.cancel')}
                confirmBtnText={I18n.t('commonText.confirm')}
                onDateChange={date => this.onDateChange({ fromDate: moment(date, dateFormatDisplay).format(dateFormat) })}
                customStyles={{
                  dateInput: styles.dateInput,
                  dateText: styles.dateText
                }}
              />
            </View>
            <View style={styles.dateContainer}>
              <Text style={styles.dateLabel}>
                To
              </Text>
              <DatePicker
                style={styles.datePicker}
                showIcon={false}
                date={moment(this.props.toDate, dateFormat).format(dateFormatDisplay)}
                mode="date"
                format={dateFormatDisplay}
                minDate={moment(this.props.fromDate, dateFormat).format(dateFormatDisplay) || START_DATE}
                placeholder={I18n.t('report.datePlaceholder')}
                cancelBtnText={I18n.t('commonText.cancel')}
                confirmBtnText={I18n.t('commonText.confirm')}
                onDateChange={date => this.onDateChange({ toDate: moment(date, dateFormatDisplay).format(dateFormat) })}
                customStyles={{
                  dateInput: styles.dateInput,
                  dateText: styles.dateText
                }}
              />
            </View>
          </View>

          <View style={styles.aboutSection}>
            <Text style={styles.label}>
              {I18n.t('settings.aboutApp')}
            </Text>

            <List content={aboutSections} bigSeparation={false}>{}</List>

            <Text style={[styles.label, styles.footerText]}>
              {I18n.t('commonText.appName')}
            </Text>

          </View>
        </ScrollView>
      </View>
    );
  }
}

Settings.propTypes = {
  user: React.PropTypes.any,
  areas: React.PropTypes.any,
  fromDate: React.PropTypes.string,
  toDate: React.PropTypes.string,
  navigate: React.PropTypes.func.isRequired,
  getAreas: React.PropTypes.func.isRequired,
  logout: React.PropTypes.func.isRequired,
  updateDate: React.PropTypes.func.isRequired
};

Settings.navigationOptions = {
  header: ({ goBack }) => ({
    left: <LeftBtn goBack={goBack} />,
    tintColor: Theme.colors.color1,
    style: headerStyles.style,
    titleStyle: headerStyles.titleStyle,
    title: I18n.t('settings.title')
  })
};

export default Settings;
<|endoftext|>