<commit_before>var init = require('../utils/hooks').init
  , beforeEachFn = require('../utils/hooks').beforeEach
  , expect = require('expect.js')
  , errors = require('../../../src/getstream').errors
  , utils = require('../utils')
  , config = require('../utils/config');

describe('[INTEGRATION] Stream client (Common)', function() {

    init.call(this);
    beforeEach(beforeEachFn);

    it.skip('handlers', function() {
        var called = {};
        var self = this;
        called.request = 0;
        called.response = 0;

        function callback() {
            called.request += 1;
        }

        function responseCallback() {
            called.response += 1;
        }

        this.client.on('request', callback);
        this.client.on('response', responseCallback);

        return this.user1.get({
                'limit': 1
            })
            .then(function() {
                self.client.off();
                return self.user1.get({ 'limit': 1 });
            })
            .then(function() {
                expect(called.request).to.eql(1);
                expect(called.response).to.eql(1);
            });
    });

    it.skip('err not null', function(done) {
        this.user1.addActivity({
            actor: 'actorname',
            actorName: 'abc',
            verb: 'follow'
        }, function(error, response, body) {
            expect(error).to.be.an(Object);
            done();
        });
    });

    it('signing', function(done) {
        expect(this.user1.token).to.be.an('string');
        done();
    });

    it('get wrong feed', function(done) {
        var self = this;

        var getFeed = function() {
            self.client.feed('flat1');
        };
        expect(getFeed).to.throwException(function(e) {
            expect(e).to.be.a(errors.FeedError);
        });
        done();
    });

    it('get wrong format', function(done) {
        var self = this;

        var getFeed = function() {
            self.client.feed('flat:1', '2');
        };
        expect(getFeed).to.throwException(function(e) {
            expect(e).to.be.a(errors.FeedError);
        });
        done();
    });

    it('get invalid format', function() {
        var self = this;

        var invalidFormats = [];
        invalidFormats.push(function() {
            self.client.feed('flat 1', '2');
        });
        invalidFormats.push(function() {
            self.client.feed('flat1', '2:3');
        });
        invalidFormats.push(function() {
            self.user1.follow('flat 1', '3');
        });
        invalidFormats.push(function() {
            self.user1.follow('flat', '3 3');
        });
        // verify all of the above throw an error
        for (var i = 0; i < invalidFormats.length; i++) {
            var callable = invalidFormats[i];
            expect(callable).to.throwException(function(e) {
                expect(e).to.be.a(errors.FeedError);
            });
        }
        // a dash should be allowed
        this.client.feed('flat1', '2-3', 'token');
    });

    it('add activity', function() {
        var self = this;
        var activityId = null;
        var activity = {
            'actor': 'test-various:characters',
            'verb': 'add',
            'object': 1,
            'tweet': 'hello world'
        };

        return this.user1.addActivity(activity)
            .then(function(body) {
                activityId = body['id'];
                return self.user1.get({'limit': 1});
            })
            .then(function(body) {
                expect(body['results'][0]['id']).to.eql(activityId);
            });
    });

    it('add complex activity', function() {
        var self = this;
        var activityId = null;
        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };
        activity['participants'] = ['Thierry', 'Tommaso'];
        activity['route'] = {
            'name': 'Vondelpark',
            'distance': '20'
        };
        var currentDate = new Date();
        activity['date'] = currentDate;
        var isoDate = currentDate.toISOString();

        return this.user1.addActivity(activity)
            .then(function(body) {
                activityId = body['id'];
                return self.user1.get({ 'limit': 1 });
            })
            .then(function(body) {
                expect(body['results'][0]['id']).to.eql(activityId);
                expect(body['results'][0]['participants']).to.eql(['Thierry', 'Tommaso']);
                expect(body['results'][0]['route']).to.eql({
                    'name': 'Vondelpark',
                    'distance': '20'
                });
                expect(body['results'][0]['date']).to.eql(isoDate);
            });
    });

    it('add activity no callback', function() {
        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return this.user1.addActivity(activity);
    });

    it('remove activity', function() {
        var self = this;
        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return this.user1.addActivity(activity)
            .then(function(body) {
                var activityId = body['id'];
                return self.user1.removeActivity(activityId);
            });
    });

    it('remove activity foreign id', function() {
        var self = this;
        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1,
            'foreign_id': 'add:2'
        };
        var now = new Date();
        activity.time = now.toISOString();

        return self.user4.addActivity(activity)
            .then(function(body) {
                return self.user4.removeActivity({
                    foreignId: 'add:2'
                });
            })
            .then(function() {
                return self.user4.get({ limit: 10 });
            })
            .then(function(body) {
                expect(body['results'].length).to.be(0);
            });
    });

    it('add activities', function() {
        var self = this;
        var activityIdFirst, activityIdLast;
        var activities = [{
            'actor': 1,
            'verb': 'tweet',
            'object': 1
        }, {
            'actor': 2,
            'verb': 'tweet',
            'object': 3
        }, ];

        return this.user1.addActivities(activities)
            .then(function(body) {
                activityIdFirst = body['activities'][0]['id'];
                activityIdLast = body['activities'][1]['id'];
                return self.user1.get({ 'limit': 2 });
            })
            .then(function(body) {
                expect(body['results'][0]['id']).to.eql(activityIdLast);
                expect(body['results'][1]['id']).to.eql(activityIdFirst);
            });
    });

    it('follow', function() {
        var self = this;
        var activityId = null;

        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return self.user1.addActivity(activity)
            .then(function (body) {
                activityId = body['id'];
                return self.aggregated2.follow('user', self.user1.userId);
            })
            .then(function() {
                return utils.delay(config.READ_TIMEOUT);
            })
            .then(function check() {
                return self.aggregated2.get({ 'limit': 1 });
            })
            .then(function(body) {
                expect(body['results'][0]['activities'][0]['id']).to.eql(activityId);
            });
    });

    it('follow without callback', function() {
        return this.aggregated2.follow('user', '111');
    });

    it('follow with copy limit', function() {
        return this.aggregated2.follow('user', '999', {
            limit: 500
        });
    });

    it('unfollow', function() {
        var self = this;
        var activityId = null;

        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return self.user1.addActivity(activity)
            .then(function follow(body) {
                activityId = body['id'];
                return self.aggregated2.follow('user', self.user1.userId);
            }).then(function () {
                return self.aggregated2.unfollow('user', self.user1.userId);
            }).then(function () {
                return utils.delay(config.READ_TIMEOUT);
            }).then(function() {
                return self.aggregated2.get({ 'limit': 1 });
            }).then(function(body) {
                var firstResult = body['results'][0];
                var activityFound = (firstResult) ? firstResult['activities'][0]['id'] : null;
                expect(activityFound).to.not.eql(activityId);
            });
    });

    it('unfollow keep_history', function() {
        var self = this;
        this.timeout(6000);

        var activityId = null;

        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };
        return self.user1.addActivity(activity)
            .then(function (body) {
                activityId = body['id'];
                self.flat3.follow('user', self.user1.userId);
            })
            .then(function () {
                return self.flat3.unfollow('user', self.user1.userId, {
                    keepHistory: true
                });
            })
            .then(function() {
                return utils.delay(config.READ_TIMEOUT);
            })
            .then(function() {
                return self.flat3.get({ 'limit': 1 });
            })
            .then(function(body) {
                var firstResult = body['results'][0];
                var activityFound = (firstResult) ? firstResult['id'] : null;
                expect(activityFound).to.eql(activityId);
            });
    });

    it('list followers', function() {
        return this.user1.followers({
            limit: '10',
            offset: '10'
        });
    });

    it('list following', function() {
        return this.user1.following({
            limit: '10',
            offset: '10'
        });
    });

    it('do i follow', function() {
        var self = this;

        return this.user1.follow('flat', '33')
            .then(function doifollow() {
                return self.user1.following({
                    'filter': ['flat:33', 'flat:44']
                });
            })
            .then(function callback(body) {
                var results = body.results;
                expect(results.length).to.eql(1);
                expect(results[0].target_id).to.eql('flat:33');
            });
    });

    it('get read-only feed', function() {
        return this.user1ReadOnly.get({
            'limit': 2
        });
    });

    it('get filtering', function() {
        // first add three activities
        //TODO find a library to make async testing easier on the eye
        var self = this;
        var activityIdOne = null;
        var activityIdTwo = null;
        var activityIdThree = null;

        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return self.user1.addActivity(activity)
            .then(function add2(body) {
                activityIdOne = body['id'];
                var activity = {
                    'actor': 2,
                    'verb': 'watch',
                    'object': 2
                };
                return self.user1.addActivity(activity);
            })
            .then(function add3(body) {
                activityIdTwo = body['id'];
                var activity = {
                    'actor': 3,
                    'verb': 'run',
                    'object': 2
                };
                return self.user1.addActivity(activity);
            })
            .then(function(body) {
                return utils.delay(200, body);
            })
            .then(function get(body) {
                activityIdThree = body['id'];
                return self.user1.get({
                    'limit': 2
                });
            })
            .then(function check(body) {
                expect(body['results'].length).to.eql(2);
                expect(body['results'][0]['id']).to.eql(activityIdThree);
                expect(body['results'][1]['id']).to.eql(activityIdTwo);

                return self.user1.get({
                    limit: 2,
                    offset: 1
                });
            })
            .then(function check2(body) {
                expect(body['results'].length).to.eql(2);
                expect(body['results'][0]['id']).to.eql(activityIdTwo);
                expect(body['results'][1]['id']).to.eql(activityIdOne);

                return self.user1.get({
                    limit: 1,
                    id_lt: activityIdTwo
                });
            })
            .then(function check3(body) {
                expect(body['results'].length).to.eql(1);
                expect(body['results'][0]['id']).to.eql(activityIdOne);
            });

    });

    it('mark read and seen', function() {
        // add 2 activities to ensure we have new data
        var self = this;
        var params = {
            limit: 2
        };
        var activities = [{
            'actor': 1,
            'verb': 'add',
            'object': 1
        }, {
            'actor': 2,
            'verb': 'test',
            'object': 2
        }];

        return this.notification3.addActivities(activities)
            .then(function() {
                return self.notification3.get(params);
            })
            .then(function(body) {
                var notificationId = body['results'][0]['id'];
                var params = {
                    limit: 2,
                    mark_seen: true,
                    mark_read: notificationId
                };

                return self.notification3.get(params);
            })
            .then(function() {
                return self.notification3.get(params);
            })
            .then(function(body) {
                expect(body['results'][0]['is_seen']).to.eql(true);
                expect(body['results'][1]['is_seen']).to.eql(true);
                expect(body['results'][0]['is_read']).to.eql(true);
                expect(body['results'][1]['is_read']).to.eql(false);
                // expect(body['unread']).to.be.greaterThan(1);
                expect(body['unseen']).to.eql(0);
            });

    });

});<commit_msg>Remove timeout<commit_after>var init = require('../utils/hooks').init
  , beforeEachFn = require('../utils/hooks').beforeEach
  , expect = require('expect.js')
  , errors = require('../../../src/getstream').errors
  , utils = require('../utils')
  , config = require('../utils/config');

describe('[INTEGRATION] Stream client (Common)', function() {

    init.call(this);
    beforeEach(beforeEachFn);

    it.skip('handlers', function() {
        var called = {};
        var self = this;
        called.request = 0;
        called.response = 0;

        function callback() {
            called.request += 1;
        }

        function responseCallback() {
            called.response += 1;
        }

        this.client.on('request', callback);
        this.client.on('response', responseCallback);

        return this.user1.get({
                'limit': 1
            })
            .then(function() {
                self.client.off();
                return self.user1.get({ 'limit': 1 });
            })
            .then(function() {
                expect(called.request).to.eql(1);
                expect(called.response).to.eql(1);
            });
    });

    it.skip('err not null', function(done) {
        this.user1.addActivity({
            actor: 'actorname',
            actorName: 'abc',
            verb: 'follow'
        }, function(error, response, body) {
            expect(error).to.be.an(Object);
            done();
        });
    });

    it('signing', function(done) {
        expect(this.user1.token).to.be.an('string');
        done();
    });

    it('get wrong feed', function(done) {
        var self = this;

        var getFeed = function() {
            self.client.feed('flat1');
        };
        expect(getFeed).to.throwException(function(e) {
            expect(e).to.be.a(errors.FeedError);
        });
        done();
    });

    it('get wrong format', function(done) {
        var self = this;

        var getFeed = function() {
            self.client.feed('flat:1', '2');
        };
        expect(getFeed).to.throwException(function(e) {
            expect(e).to.be.a(errors.FeedError);
        });
        done();
    });

    it('get invalid format', function() {
        var self = this;

        var invalidFormats = [];
        invalidFormats.push(function() {
            self.client.feed('flat 1', '2');
        });
        invalidFormats.push(function() {
            self.client.feed('flat1', '2:3');
        });
        invalidFormats.push(function() {
            self.user1.follow('flat 1', '3');
        });
        invalidFormats.push(function() {
            self.user1.follow('flat', '3 3');
        });
        // verify all of the above throw an error
        for (var i = 0; i < invalidFormats.length; i++) {
            var callable = invalidFormats[i];
            expect(callable).to.throwException(function(e) {
                expect(e).to.be.a(errors.FeedError);
            });
        }
        // a dash should be allowed
        this.client.feed('flat1', '2-3', 'token');
    });

    it('add activity', function() {
        var self = this;
        var activityId = null;
        var activity = {
            'actor': 'test-various:characters',
            'verb': 'add',
            'object': 1,
            'tweet': 'hello world'
        };

        return this.user1.addActivity(activity)
            .then(function(body) {
                activityId = body['id'];
                return self.user1.get({'limit': 1});
            })
            .then(function(body) {
                expect(body['results'][0]['id']).to.eql(activityId);
            });
    });

    it('add complex activity', function() {
        var self = this;
        var activityId = null;
        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };
        activity['participants'] = ['Thierry', 'Tommaso'];
        activity['route'] = {
            'name': 'Vondelpark',
            'distance': '20'
        };
        var currentDate = new Date();
        activity['date'] = currentDate;
        var isoDate = currentDate.toISOString();

        return this.user1.addActivity(activity)
            .then(function(body) {
                activityId = body['id'];
                return self.user1.get({ 'limit': 1 });
            })
            .then(function(body) {
                expect(body['results'][0]['id']).to.eql(activityId);
                expect(body['results'][0]['participants']).to.eql(['Thierry', 'Tommaso']);
                expect(body['results'][0]['route']).to.eql({
                    'name': 'Vondelpark',
                    'distance': '20'
                });
                expect(body['results'][0]['date']).to.eql(isoDate);
            });
    });

    it('add activity no callback', function() {
        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return this.user1.addActivity(activity);
    });

    it('remove activity', function() {
        var self = this;
        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return this.user1.addActivity(activity)
            .then(function(body) {
                var activityId = body['id'];
                return self.user1.removeActivity(activityId);
            });
    });

    it('remove activity foreign id', function() {
        var self = this;
        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1,
            'foreign_id': 'add:2'
        };
        var now = new Date();
        activity.time = now.toISOString();

        return self.user4.addActivity(activity)
            .then(function(body) {
                return self.user4.removeActivity({
                    foreignId: 'add:2'
                });
            })
            .then(function() {
                return self.user4.get({ limit: 10 });
            })
            .then(function(body) {
                expect(body['results'].length).to.be(0);
            });
    });

    it('add activities', function() {
        var self = this;
        var activityIdFirst, activityIdLast;
        var activities = [{
            'actor': 1,
            'verb': 'tweet',
            'object': 1
        }, {
            'actor': 2,
            'verb': 'tweet',
            'object': 3
        }, ];

        return this.user1.addActivities(activities)
            .then(function(body) {
                activityIdFirst = body['activities'][0]['id'];
                activityIdLast = body['activities'][1]['id'];
                return self.user1.get({ 'limit': 2 });
            })
            .then(function(body) {
                expect(body['results'][0]['id']).to.eql(activityIdLast);
                expect(body['results'][1]['id']).to.eql(activityIdFirst);
            });
    });

    it('follow', function() {
        var self = this;
        var activityId = null;

        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return self.user1.addActivity(activity)
            .then(function (body) {
                activityId = body['id'];
                return self.aggregated2.follow('user', self.user1.userId);
            })
            .then(function() {
                return utils.delay(config.READ_TIMEOUT);
            })
            .then(function check() {
                return self.aggregated2.get({ 'limit': 1 });
            })
            .then(function(body) {
                expect(body['results'][0]['activities'][0]['id']).to.eql(activityId);
            });
    });

    it('follow without callback', function() {
        return this.aggregated2.follow('user', '111');
    });

    it('follow with copy limit', function() {
        return this.aggregated2.follow('user', '999', {
            limit: 500
        });
    });

    it('unfollow', function() {
        var self = this;
        var activityId = null;

        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return self.user1.addActivity(activity)
            .then(function follow(body) {
                activityId = body['id'];
                return self.aggregated2.follow('user', self.user1.userId);
            }).then(function () {
                return self.aggregated2.unfollow('user', self.user1.userId);
            }).then(function () {
                return utils.delay(config.READ_TIMEOUT);
            }).then(function() {
                return self.aggregated2.get({ 'limit': 1 });
            }).then(function(body) {
                var firstResult = body['results'][0];
                var activityFound = (firstResult) ? firstResult['activities'][0]['id'] : null;
                expect(activityFound).to.not.eql(activityId);
            });
    });

    it('unfollow keep_history', function() {
        var self = this;

        var activityId = null;

        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };
        return self.user1.addActivity(activity)
            .then(function (body) {
                activityId = body['id'];
                self.flat3.follow('user', self.user1.userId);
            })
            .then(function () {
                return self.flat3.unfollow('user', self.user1.userId, {
                    keepHistory: true
                });
            })
            .then(function() {
                return utils.delay(config.READ_TIMEOUT);
            })
            .then(function() {
                return self.flat3.get({ 'limit': 1 });
            })
            .then(function(body) {
                var firstResult = body['results'][0];
                var activityFound = (firstResult) ? firstResult['id'] : null;
                expect(activityFound).to.eql(activityId);
            });
    });

    it('list followers', function() {
        return this.user1.followers({
            limit: '10',
            offset: '10'
        });
    });

    it('list following', function() {
        return this.user1.following({
            limit: '10',
            offset: '10'
        });
    });

    it('do i follow', function() {
        var self = this;

        return this.user1.follow('flat', '33')
            .then(function doifollow() {
                return self.user1.following({
                    'filter': ['flat:33', 'flat:44']
                });
            })
            .then(function callback(body) {
                var results = body.results;
                expect(results.length).to.eql(1);
                expect(results[0].target_id).to.eql('flat:33');
            });
    });

    it('get read-only feed', function() {
        return this.user1ReadOnly.get({
            'limit': 2
        });
    });

    it('get filtering', function() {
        // first add three activities
        //TODO find a library to make async testing easier on the eye
        var self = this;
        var activityIdOne = null;
        var activityIdTwo = null;
        var activityIdThree = null;

        var activity = {
            'actor': 1,
            'verb': 'add',
            'object': 1
        };

        return self.user1.addActivity(activity)
            .then(function add2(body) {
                activityIdOne = body['id'];
                var activity = {
                    'actor': 2,
                    'verb': 'watch',
                    'object': 2
                };
                return self.user1.addActivity(activity);
            })
            .then(function add3(body) {
                activityIdTwo = body['id'];
                var activity = {
                    'actor': 3,
                    'verb': 'run',
                    'object': 2
                };
                return self.user1.addActivity(activity);
            })
            .then(function(body) {
                return utils.delay(200, body);
            })
            .then(function get(body) {
                activityIdThree = body['id'];
                return self.user1.get({
                    'limit': 2
                });
            })
            .then(function check(body) {
                expect(body['results'].length).to.eql(2);
                expect(body['results'][0]['id']).to.eql(activityIdThree);
                expect(body['results'][1]['id']).to.eql(activityIdTwo);

                return self.user1.get({
                    limit: 2,
                    offset: 1
                });
            })
            .then(function check2(body) {
                expect(body['results'].length).to.eql(2);
                expect(body['results'][0]['id']).to.eql(activityIdTwo);
                expect(body['results'][1]['id']).to.eql(activityIdOne);

                return self.user1.get({
                    limit: 1,
                    id_lt: activityIdTwo
                });
            })
            .then(function check3(body) {
                expect(body['results'].length).to.eql(1);
                expect(body['results'][0]['id']).to.eql(activityIdOne);
            });

    });

    it('mark read and seen', function() {
        // add 2 activities to ensure we have new data
        var self = this;
        var params = {
            limit: 2
        };
        var activities = [{
            'actor': 1,
            'verb': 'add',
            'object': 1
        }, {
            'actor': 2,
            'verb': 'test',
            'object': 2
        }];

        return this.notification3.addActivities(activities)
            .then(function() {
                return self.notification3.get(params);
            })
            .then(function(body) {
                var notificationId = body['results'][0]['id'];
                var params = {
                    limit: 2,
                    mark_seen: true,
                    mark_read: notificationId
                };

                return self.notification3.get(params);
            })
            .then(function() {
                return self.notification3.get(params);
            })
            .then(function(body) {
                expect(body['results'][0]['is_seen']).to.eql(true);
                expect(body['results'][1]['is_seen']).to.eql(true);
                expect(body['results'][0]['is_read']).to.eql(true);
                expect(body['results'][1]['is_read']).to.eql(false);
                // expect(body['unread']).to.be.greaterThan(1);
                expect(body['unseen']).to.eql(0);
            });

    });

});<|endoftext|>


<commit_before>/* Modernizr 2.8.3 (Custom Build) | MIT & BSD
 * Build: http://modernizr.com/download/#-svg-cssclasses-teststyles-testprop-testallprops-prefixes-domprefixes-load
 */
;

window.Modernizr = (function( window, document, undefined ) {

    var version = '2.8.3',

    Modernizr = {},

    enableClasses = true,

    docElement = document.documentElement,

    mod = 'modernizr',
    modElem = document.createElement(mod),
    mStyle = modElem.style,

    inputElem  ,

    toString = {}.toString,

    prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),

    omPrefixes = 'Webkit Moz O ms',

    cssomPrefixes = omPrefixes.split(' '),

    domPrefixes = omPrefixes.toLowerCase().split(' '),

    ns = {'svg': 'http://www.w3.org/2000/svg'},

    tests = {},
    inputs = {},
    attrs = {},

    classes = [],

    slice = classes.slice,

    featureName,

    injectElementWithStyles = function( rule, callback, nodes, testnames ) {

      var style, ret, node, docOverflow,
          div = document.createElement('div'),
                body = document.body,
                fakeBody = body || document.createElement('body');

      if ( parseInt(nodes, 10) ) {
                      while ( nodes-- ) {
              node = document.createElement('div');
              node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
              div.appendChild(node);
          }
      }

                style = ['','<style id="s', mod, '">', rule, '</style>'].join('');
      div.id = mod;
          (body ? div : fakeBody).innerHTML += style;
      fakeBody.appendChild(div);
      if ( !body ) {
                fakeBody.style.background = '';
                fakeBody.style.overflow = 'hidden';
          docOverflow = docElement.style.overflow;
          docElement.style.overflow = 'hidden';
          docElement.appendChild(fakeBody);
      }

      ret = callback(div, rule);
        if ( !body ) {
          fakeBody.parentNode.removeChild(fakeBody);
          docElement.style.overflow = docOverflow;
      } else {
          div.parentNode.removeChild(div);
      }

      return !!ret;

    },
    _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;

    if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
      hasOwnProp = function (object, property) {
        return _hasOwnProperty.call(object, property);
      };
    }
    else {
      hasOwnProp = function (object, property) {
        return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
      };
    }

    if (!Function.prototype.bind) {
      Function.prototype.bind = function bind(that) {

        var target = this;

        if (typeof target != "function") {
            throw new TypeError();
        }

        var args = slice.call(arguments, 1),
            bound = function () {

            if (this instanceof bound) {

              var F = function(){};
              F.prototype = target.prototype;
              var self = new F();

              var result = target.apply(
                  self,
                  args.concat(slice.call(arguments))
              );
              if (Object(result) === result) {
                  return result;
              }
              return self;

            } else {

              return target.apply(
                  that,
                  args.concat(slice.call(arguments))
              );

            }

        };

        return bound;
      };
    }

    function setCss( str ) {
        mStyle.cssText = str;
    }

    function setCssAll( str1, str2 ) {
        return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
    }

    function is( obj, type ) {
        return typeof obj === type;
    }

    function contains( str, substr ) {
        return !!~('' + str).indexOf(substr);
    }

    function testProps( props, prefixed ) {
        for ( var i in props ) {
            var prop = props[i];
            if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
                return prefixed == 'pfx' ? prop : true;
            }
        }
        return false;
    }

    function testDOMProps( props, obj, elem ) {
        for ( var i in props ) {
            var item = obj[props[i]];
            if ( item !== undefined) {

                            if (elem === false) return props[i];

                            if (is(item, 'function')){
                                return item.bind(elem || obj);
                }

                            return item;
            }
        }
        return false;
    }

    function testPropsAll( prop, prefixed, elem ) {

        var ucProp  = prop.charAt(0).toUpperCase() + prop.slice(1),
            props   = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');

            if(is(prefixed, "string") || is(prefixed, "undefined")) {
          return testProps(props, prefixed);

            } else {
          props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
          return testDOMProps(props, prefixed, elem);
        }
    }    tests['svg'] = function() {
        return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
    };
    for ( var feature in tests ) {
        if ( hasOwnProp(tests, feature) ) {
                                    featureName  = feature.toLowerCase();
            Modernizr[featureName] = tests[feature]();

            classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
        }
    }

     Modernizr.addTest = function ( feature, test ) {
       if ( typeof feature == 'object' ) {
         for ( var key in feature ) {
           if ( hasOwnProp( feature, key ) ) {
             Modernizr.addTest( key, feature[ key ] );
           }
         }
       } else {

         feature = feature.toLowerCase();

         if ( Modernizr[feature] !== undefined ) {
                                              return Modernizr;
         }

         test = typeof test == 'function' ? test() : test;

         if (typeof enableClasses !== "undefined" && enableClasses) {
           docElement.className += ' ' + (test ? '' : 'no-') + feature;
         }
         Modernizr[feature] = test;

       }

       return Modernizr;
     };

    setCss('');
    modElem = inputElem = null;

    Modernizr._version      = version;

    Modernizr._prefixes     = prefixes;
    Modernizr._domPrefixes  = domPrefixes;
    Modernizr._cssomPrefixes  = cssomPrefixes;

    Modernizr.testProp      = function(prop){
        return testProps([prop]);
    };

    Modernizr.testAllProps  = testPropsAll;

    Modernizr.testStyles    = injectElementWithStyles;    docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +

                                                    (enableClasses ? ' js ' + classes.join(' ') : '');

    return Modernizr;

})(this, this.document);
/*yepnope1.5.4|WTFPL*/
(function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,h){var i=b(a),j=i.autoCallback;i.url.split(".").pop().split("?").shift(),i.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]),i.instead?i.instead(a,e,f,g,h):(y[i.url]?i.noexec=!0:y[i.url]=1,f.load(i.url,i.forceCSS||!i.forceJS&&"css"==i.url.split(".").pop().split("?").shift()?"c":c,i.noexec,i.attrs,i.timeout),(d(e)||d(j))&&f.load(function(){k(),e&&e(i.origUrl,h,g),j&&j(i.origUrl,h,g),y[i.url]=2})))}function h(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var i,j,l=this.yepnope.loader;if(e(a))g(a,0,l,0);else if(w(a))for(i=0;i<a.length;i++)j=a[i],e(j)?g(j,0,l,0):w(j)?B(j):Object(j)===j&&h(j,l);else Object(a)===a&&h(a,l)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,null==b.readyState&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}})(this,document);
Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0));};
;
<commit_msg>Delete modernizr.svg.js<commit_after><|endoftext|>


<commit_before>/*
 * @copyright Adam Benda, 2016
 */

/**
 * @class
 * @classdesc Ball
 */

/**
 *
 * @param {number} x - x coordinate
 * @param {number} y - y coordinate
 * @param {number} r - radius
 * @returns {Ball}
 */
var Ball = function (world, x, y, r) {
    this.world = world;
    this.initX = x;
    this.initY = y;
    this.initR = r;
    this.createBody(x, y, r);
    Matter.Composite.add(this.world, this.body);

};

Ball.prototype.toggleHook = function () {
    var world = this.world;

    if (!this.hooked) {
        //find a nearest
        //todo: to a separate function
        var target = null;
        var shortestDistance = 99999999;
        for (var i = 0; i < world.bodies.length; i++) {
            var body = world.bodies[i];
            if (body.isStatic) {
                var thisDistance = Matter.Vector.magnitudeSquared(
                        Matter.Vector.sub(body.position, this.body.position)
                        );
                if (shortestDistance > thisDistance) {
                    shortestDistance = thisDistance;
                    target = body;
                }
            }
        }

        if (target === "null") {
            //no target available
            return;
        }

        this.rope = Matter.Constraint.create({bodyA: this.body, bodyB: target});
        this.body.onRopeGravity = 1;
        Matter.World.add(world, this.rope);
        this.body.aVel = Matter.Vector.create(this.body.velocity.x, this.body.velocity.y);
        this.hooked = true;
    } else {
        Matter.World.remove(world, this.rope);
        this.body.onRopeGravity = 0;
        this.hooked = false;
    }

};

Ball.prototype.checkBoundaries = function (engine) {
    if (this.hooked) {
        //if we are hooked up its ok
        return true;
    }

    if (
            engine.render.bounds.max.y < this.body.position.y - this.body.r ||
            engine.render.bounds.min.x > this.body.position.x + this.body.r ||
            engine.render.bounds.max.x < this.body.position.x - this.body.r
            ) {
        this.killed();
    }
};

Ball.prototype.killed = function () {
    //unhook
    if (this.hooked) {
        this.toogleHook(this.world);
    }

    Matter.Composite.remove(this.world, this.body);
    this.createBody(this.initX, this.initY, this.initR);
    Matter.Composite.add(this.world, this.body);
};

Ball.prototype.createBody = function (x, y, r) {
    this.body = Matter.Bodies.circle(x, y, r);
    this.body.force.x = 0;
    this.body.force.y = 0.1;
    this.body.frictionAir = 0;
    this.body.friction = 0;
    this.body.render.sprite.texture = "gfx/ball_" + r + ".png";
    //passing this for texture changing
    this.body.r = r;

    /**
     * Disable effect of ordinary gravity and apply my special rope gravity
     * @param {number} gx - gravity x part (world.gravity.x * gravity.scale)
     * @param {number} gy - gravity y part
     * @returns {undefined}
     */
    this.body.applyRopeGravity = function (gx, gy) {
        //this is Ball.body

        if (this.onRopeGravity > 0) {
            //negating effect of _bodiesApplyGravity
            this.force.x -= gx * this.mass;
            this.force.y -= gy * this.mass;
        }
        this.velocity.x = this.aVel.x;
        this.velocity.y = this.aVel.y;
    };
};<commit_msg>Rotational speed increasing (magic constant)<commit_after>/*
 * @copyright Adam Benda, 2016
 */

/**
 * @class
 * @classdesc Ball
 */

/**
 *
 * @param {number} x - x coordinate
 * @param {number} y - y coordinate
 * @param {number} r - radius
 * @returns {Ball}
 */
var Ball = function (world, x, y, r) {
    this.world = world;
    this.initX = x;
    this.initY = y;
    this.initR = r;
    this.createBody(x, y, r);
    Matter.Composite.add(this.world, this.body);

};

Ball.prototype.toggleHook = function () {
    var world = this.world;

    if (!this.hooked) {
        //find a nearest
        //todo: to a separate function
        var target = null;
        var shortestDistance = 99999999;
        for (var i = 0; i < world.bodies.length; i++) {
            var body = world.bodies[i];
            if (body.isStatic) {
                var thisDistance = Matter.Vector.magnitudeSquared(
                        Matter.Vector.sub(body.position, this.body.position)
                        );
                if (shortestDistance > thisDistance) {
                    shortestDistance = thisDistance;
                    target = body;
                }
            }
        }

        if (target === "null") {
            //no target available
            return;
        }

        this.rope = Matter.Constraint.create({bodyA: this.body, bodyB: target});
        this.body.onRopeGravity = 1;
        Matter.World.add(world, this.rope);
        this.hooked = true;
    } else {
        Matter.World.remove(world, this.rope);
        this.body.onRopeGravity = 0;
        this.hooked = false;
    }

};

Ball.prototype.checkBoundaries = function (engine) {
    if (this.hooked) {
        //if we are hooked up its ok
        return true;
    }

    if (
            engine.render.bounds.max.y < this.body.position.y - this.body.r ||
            engine.render.bounds.min.x > this.body.position.x + this.body.r ||
            engine.render.bounds.max.x < this.body.position.x - this.body.r
            ) {
        this.killed();
    }
};

Ball.prototype.killed = function () {
    //unhook
    if (this.hooked) {
        this.toogleHook(this.world);
    }

    Matter.Composite.remove(this.world, this.body);
    this.createBody(this.initX, this.initY, this.initR);
    Matter.Composite.add(this.world, this.body);
};

Ball.prototype.createBody = function (x, y, r) {
    this.body = Matter.Bodies.circle(x, y, r);
    this.body.force.x = 0;
    this.body.force.y = 0.1;
    this.body.frictionAir = 0;
    this.body.friction = 0;
    this.body.render.sprite.texture = "gfx/ball_" + r + ".png";
    //passing this for texture changing
    this.body.r = r;

    /**
     * Disable effect of ordinary gravity and apply my special rope gravity
     * @param {number} gx - gravity x part (world.gravity.x * gravity.scale)
     * @param {number} gy - gravity y part
     * @returns {undefined}
     */
    this.body.applyRopeGravity = function (gx, gy) {
        //this is Ball.body

        if (this.onRopeGravity > 0) {
            //negating effect of _bodiesApplyGravity
            this.force.x -= gx * this.mass;
            this.force.y -= gy * this.mass;
        }
        this.force.x+=this.velocity.x/20000;
        this.force.y+=this.velocity.y/20000;
    };
};<|endoftext|>


<commit_before>import { find, isNullOrUndefined, isCallable, values, parseSelector } from './utils';

// @flow

export default class ErrorBag {
  items: FieldError[];

  constructor (errorBag = null, id = null) {
    this.vmId = id || null;
    // make this bag a mirror of the provided one, sharing the same items reference.
    if (errorBag && errorBag instanceof ErrorBag) {
      this.items = errorBag.items;
    } else {
      this.items = [];
    }
  }

  [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator'] () {
    let index = 0;
    return {
      next: () => {
        return { value: this.items[index++], done: index > this.items.length };
      }
    };
  }

  /**
   * Adds an error to the internal array.
   */
  add (error: FieldError | FieldError[]) {
    this.items.push(
      ...this._normalizeError(error)
    );
  }

  /**
   * Normalizes passed errors to an error array.
   */
  _normalizeError (error: FieldError | FieldError[]): FieldError[] {
    if (Array.isArray(error)) {
      return error.map(e => {
        e.scope = !isNullOrUndefined(e.scope) ? e.scope : null;
        e.vmId = !isNullOrUndefined(e.vmId) ? e.vmId : (this.vmId || null);

        return e;
      });
    }

    error.scope = !isNullOrUndefined(error.scope) ? error.scope : null;
    error.vmId = !isNullOrUndefined(error.vmId) ? error.vmId : (this.vmId || null);

    return [error];
  }

  /**
   * Regenrates error messages if they have a generator function.
   */
  regenerate (): void {
    this.items.forEach(i => {
      i.msg = isCallable(i.regenerate) ? i.regenerate() : i.msg;
    });
  }

  /**
   * Updates a field error with the new field scope.
   */
  update (id: string, error: FieldError) {
    const item = find(this.items, i => i.id === id);
    if (!item) {
      return;
    }

    const idx = this.items.indexOf(item);
    this.items.splice(idx, 1);
    item.scope = error.scope;
    this.items.push(item);
  }

  /**
   * Gets all error messages from the internal array.
   */
  all (scope: string): Array<string> {
    const filterFn = (item) => {
      let matchesScope = true;
      let matchesVM = true;
      if (!isNullOrUndefined(scope)) {
        matchesScope = item.scope === scope;
      }

      if (!isNullOrUndefined(this.vmId)) {
        matchesVM = item.vmId === this.vmId;
      }

      return matchesVM && matchesScope;
    };

    return this.items.filter(filterFn).map(e => e.msg);
  }

  /**
   * Checks if there are any errors in the internal array.
   */
  any (scope: ?string): boolean {
    const filterFn = (item) => {
      let matchesScope = true;
      let matchesVM = true;
      if (!isNullOrUndefined(scope)) {
        matchesScope = item.scope === scope;
      }

      if (!isNullOrUndefined(this.vmId)) {
        matchesScope = item.vmId === this.vmId;
      }

      return matchesVM && matchesScope;
    };

    return !!this.items.filter(filterFn).length;
  }

  /**
   * Removes all items from the internal array.
   */
  clear (scope?: ?string) {
    let matchesVM = isNullOrUndefined(this.id) ? () => true : (i) => i.vmId === this.vmId;
    if (isNullOrUndefined(scope)) {
      scope = null;
    }

    for (let i = 0; i < this.items.length; ++i) {
      if (matchesVM(this.items[i]) && this.items[i].scope === scope) {
        this.items.splice(i, 1);
        --i;
      }
    }
  }

  /**
   * Collects errors into groups or for a specific field.
   */
  collect (field?: string, scope?: string | null, map?: boolean = true) {
    const isSingleField = !isNullOrUndefined(field) && !field.includes('*');
    const groupErrors = items => {
      const errors = items.reduce((collection, error) => {
        if (!isNullOrUndefined(this.vmId) && error.vmId !== this.vmId) {
          return collection;
        }

        if (!collection[error.field]) {
          collection[error.field] = [];
        }

        collection[error.field].push(map ? error.msg : error);

        return collection;
      }, {});

      // reduce the collection to be a single array.
      if (isSingleField) {
        return values(errors)[0] || [];
      }

      return errors;
    };

    if (isNullOrUndefined(field)) {
      return groupErrors(this.items);
    }

    const selector = isNullOrUndefined(scope) ? String(field) : `${scope}.${field}`;
    const { isPrimary, isAlt } = this._makeCandidateFilters(selector);

    let collected = this.items.reduce((prev, curr) => {
      if (isPrimary(curr)) {
        prev.primary.push(curr);
      }

      if (isAlt(curr)) {
        prev.alt.push(curr);
      }

      return prev;
    }, { primary: [], alt: [] });

    collected = collected.primary.length ? collected.primary : collected.alt;

    return groupErrors(collected);
  }

  /**
   * Gets the internal array length.
   */
  count (): number {
    if (this.vmId) {
      return this.items.filter(e => e.vmId === this.vmId).length;
    }

    return this.items.length;
  }

  /**
   * Finds and fetches the first error message for the specified field id.
   */
  firstById (id: string): string | null {
    const error = find(this.items, i => i.id === id);

    return error ? error.msg : undefined;
  }

  /**
   * Gets the first error message for a specific field.
   */
  first (field: string, scope?: ?string = null) {
    const selector = isNullOrUndefined(scope) ? field : `${scope}.${field}`;
    const match = this._match(selector);

    return match && match.msg;
  }

  /**
   * Returns the first error rule for the specified field
   */
  firstRule (field: string, scope?: string): string | null {
    const errors = this.collect(field, scope, false);

    return (errors.length && errors[0].rule) || undefined;
  }

  /**
   * Checks if the internal array has at least one error for the specified field.
   */
  has (field: string, scope?: ?string = null): boolean {
    return !!this.first(field, scope);
  }

  /**
   * Gets the first error message for a specific field and a rule.
   */
  firstByRule (name: string, rule: string, scope?: string | null = null) {
    const error = this.collect(name, scope, false).filter(e => e.rule === rule)[0];

    return (error && error.msg) || undefined;
  }

  /**
   * Gets the first error message for a specific field that not match the rule.
   */
  firstNot (name: string, rule?: string = 'required', scope?: string | null = null) {
    const error = this.collect(name, scope, false).filter(e => e.rule !== rule)[0];

    return (error && error.msg) || undefined;
  }

  /**
   * Removes errors by matching against the id or ids.
   */
  removeById (id: string | string[]) {
    let condition = (item) => item.id === id;
    if (Array.isArray(id)) {
      condition = (item) => id.indexOf(item.id) !== -1;
    }

    for (let i = 0; i < this.items.length; ++i) {
      if (condition(this.items[i])) {
        this.items.splice(i, 1);
        --i;
      }
    }
  }

  /**
   * Removes all error messages associated with a specific field.
   */
  remove (field: string, scope: ?string) {
    if (isNullOrUndefined(field)) {
      return;
    }

    const selector = isNullOrUndefined(scope) ? String(field) : `${scope}.${field}`;
    const { isPrimary } = this._makeCandidateFilters(selector);

    for (let i = 0; i < this.items.length; ++i) {
      if (isPrimary(this.items[i])) {
        this.items.splice(i, 1);
        --i;
      }
    }
  }

  _makeCandidateFilters (selector) {
    let matchesRule = () => true;
    let matchesScope = () => true;
    let matchesName = () => true;
    let matchesVM = () => true;

    const { id, rule, scope, name } = parseSelector(selector);

    if (rule) {
      matchesRule = (item) => item.rule === rule;
    }

    // match by id, can be combined with rule selection.
    if (id) {
      return {
        isPrimary: item => matchesRule(item) && (item => id === item.id),
        isAlt: () => false
      };
    }

    if (isNullOrUndefined(scope)) {
      // if no scope specified, make sure the found error has no scope.
      matchesScope = item => isNullOrUndefined(item.scope);
    } else {
      matchesScope = item => item.scope === scope;
    }

    if (!isNullOrUndefined(name) && name !== '*') {
      matchesName = item => item.field === name;
    }

    if (!isNullOrUndefined(this.vmId)) {
      matchesVM = (item) => item.vmId === this.vmId;
    }

    // matches the first candidate.
    const isPrimary = (item) => {
      return matchesVM(item) && matchesName(item) && matchesRule(item) && matchesScope(item);
    };

    // matches a second candidate, which is a field with a name containing the '.' character.
    const isAlt = (item) => {
      return matchesVM(item) && matchesRule(item) && item.field === `${scope}.${name}`;
    };

    return {
      isPrimary,
      isAlt
    };
  }

  _match (selector: string) {
    if (isNullOrUndefined(selector)) {
      return undefined;
    }

    const { isPrimary, isAlt } = this._makeCandidateFilters(selector);

    return this.items.reduce((prev, item, idx, arr) => {
      const isLast = idx === arr.length - 1;
      if (prev.primary) {
        return isLast ? prev.primary : prev;
      }

      if (isPrimary(item)) {
        prev.primary = item;
      }

      if (isAlt(item)) {
        prev.alt = item;
      }

      // keep going.
      if (!isLast) {
        return prev;
      }

      return prev.primary || prev.alt;
    }, {});
  };
}
<commit_msg>fix: errors.clear() not respecting the vmId scope closes #1514<commit_after>import { find, isNullOrUndefined, isCallable, values, parseSelector } from './utils';

// @flow

export default class ErrorBag {
  items: FieldError[];

  constructor (errorBag = null, id = null) {
    this.vmId = id || null;
    // make this bag a mirror of the provided one, sharing the same items reference.
    if (errorBag && errorBag instanceof ErrorBag) {
      this.items = errorBag.items;
    } else {
      this.items = [];
    }
  }

  [typeof Symbol === 'function' ? Symbol.iterator : '@@iterator'] () {
    let index = 0;
    return {
      next: () => {
        return { value: this.items[index++], done: index > this.items.length };
      }
    };
  }

  /**
   * Adds an error to the internal array.
   */
  add (error: FieldError | FieldError[]) {
    this.items.push(
      ...this._normalizeError(error)
    );
  }

  /**
   * Normalizes passed errors to an error array.
   */
  _normalizeError (error: FieldError | FieldError[]): FieldError[] {
    if (Array.isArray(error)) {
      return error.map(e => {
        e.scope = !isNullOrUndefined(e.scope) ? e.scope : null;
        e.vmId = !isNullOrUndefined(e.vmId) ? e.vmId : (this.vmId || null);

        return e;
      });
    }

    error.scope = !isNullOrUndefined(error.scope) ? error.scope : null;
    error.vmId = !isNullOrUndefined(error.vmId) ? error.vmId : (this.vmId || null);

    return [error];
  }

  /**
   * Regenrates error messages if they have a generator function.
   */
  regenerate (): void {
    this.items.forEach(i => {
      i.msg = isCallable(i.regenerate) ? i.regenerate() : i.msg;
    });
  }

  /**
   * Updates a field error with the new field scope.
   */
  update (id: string, error: FieldError) {
    const item = find(this.items, i => i.id === id);
    if (!item) {
      return;
    }

    const idx = this.items.indexOf(item);
    this.items.splice(idx, 1);
    item.scope = error.scope;
    this.items.push(item);
  }

  /**
   * Gets all error messages from the internal array.
   */
  all (scope: string): Array<string> {
    const filterFn = (item) => {
      let matchesScope = true;
      let matchesVM = true;
      if (!isNullOrUndefined(scope)) {
        matchesScope = item.scope === scope;
      }

      if (!isNullOrUndefined(this.vmId)) {
        matchesVM = item.vmId === this.vmId;
      }

      return matchesVM && matchesScope;
    };

    return this.items.filter(filterFn).map(e => e.msg);
  }

  /**
   * Checks if there are any errors in the internal array.
   */
  any (scope: ?string): boolean {
    const filterFn = (item) => {
      let matchesScope = true;
      let matchesVM = true;
      if (!isNullOrUndefined(scope)) {
        matchesScope = item.scope === scope;
      }

      if (!isNullOrUndefined(this.vmId)) {
        matchesScope = item.vmId === this.vmId;
      }

      return matchesVM && matchesScope;
    };

    return !!this.items.filter(filterFn).length;
  }

  /**
   * Removes all items from the internal array.
   */
  clear (scope?: ?string) {
    let matchesVM = isNullOrUndefined(this.vmId) ? () => true : (i) => i.vmId === this.vmId;
    if (isNullOrUndefined(scope)) {
      scope = null;
    }

    for (let i = 0; i < this.items.length; ++i) {
      if (matchesVM(this.items[i]) && this.items[i].scope === scope) {
        this.items.splice(i, 1);
        --i;
      }
    }
  }

  /**
   * Collects errors into groups or for a specific field.
   */
  collect (field?: string, scope?: string | null, map?: boolean = true) {
    const isSingleField = !isNullOrUndefined(field) && !field.includes('*');
    const groupErrors = items => {
      const errors = items.reduce((collection, error) => {
        if (!isNullOrUndefined(this.vmId) && error.vmId !== this.vmId) {
          return collection;
        }

        if (!collection[error.field]) {
          collection[error.field] = [];
        }

        collection[error.field].push(map ? error.msg : error);

        return collection;
      }, {});

      // reduce the collection to be a single array.
      if (isSingleField) {
        return values(errors)[0] || [];
      }

      return errors;
    };

    if (isNullOrUndefined(field)) {
      return groupErrors(this.items);
    }

    const selector = isNullOrUndefined(scope) ? String(field) : `${scope}.${field}`;
    const { isPrimary, isAlt } = this._makeCandidateFilters(selector);

    let collected = this.items.reduce((prev, curr) => {
      if (isPrimary(curr)) {
        prev.primary.push(curr);
      }

      if (isAlt(curr)) {
        prev.alt.push(curr);
      }

      return prev;
    }, { primary: [], alt: [] });

    collected = collected.primary.length ? collected.primary : collected.alt;

    return groupErrors(collected);
  }

  /**
   * Gets the internal array length.
   */
  count (): number {
    if (this.vmId) {
      return this.items.filter(e => e.vmId === this.vmId).length;
    }

    return this.items.length;
  }

  /**
   * Finds and fetches the first error message for the specified field id.
   */
  firstById (id: string): string | null {
    const error = find(this.items, i => i.id === id);

    return error ? error.msg : undefined;
  }

  /**
   * Gets the first error message for a specific field.
   */
  first (field: string, scope?: ?string = null) {
    const selector = isNullOrUndefined(scope) ? field : `${scope}.${field}`;
    const match = this._match(selector);

    return match && match.msg;
  }

  /**
   * Returns the first error rule for the specified field
   */
  firstRule (field: string, scope?: string): string | null {
    const errors = this.collect(field, scope, false);

    return (errors.length && errors[0].rule) || undefined;
  }

  /**
   * Checks if the internal array has at least one error for the specified field.
   */
  has (field: string, scope?: ?string = null): boolean {
    return !!this.first(field, scope);
  }

  /**
   * Gets the first error message for a specific field and a rule.
   */
  firstByRule (name: string, rule: string, scope?: string | null = null) {
    const error = this.collect(name, scope, false).filter(e => e.rule === rule)[0];

    return (error && error.msg) || undefined;
  }

  /**
   * Gets the first error message for a specific field that not match the rule.
   */
  firstNot (name: string, rule?: string = 'required', scope?: string | null = null) {
    const error = this.collect(name, scope, false).filter(e => e.rule !== rule)[0];

    return (error && error.msg) || undefined;
  }

  /**
   * Removes errors by matching against the id or ids.
   */
  removeById (id: string | string[]) {
    let condition = (item) => item.id === id;
    if (Array.isArray(id)) {
      condition = (item) => id.indexOf(item.id) !== -1;
    }

    for (let i = 0; i < this.items.length; ++i) {
      if (condition(this.items[i])) {
        this.items.splice(i, 1);
        --i;
      }
    }
  }

  /**
   * Removes all error messages associated with a specific field.
   */
  remove (field: string, scope: ?string) {
    if (isNullOrUndefined(field)) {
      return;
    }

    const selector = isNullOrUndefined(scope) ? String(field) : `${scope}.${field}`;
    const { isPrimary } = this._makeCandidateFilters(selector);

    for (let i = 0; i < this.items.length; ++i) {
      if (isPrimary(this.items[i])) {
        this.items.splice(i, 1);
        --i;
      }
    }
  }

  _makeCandidateFilters (selector) {
    let matchesRule = () => true;
    let matchesScope = () => true;
    let matchesName = () => true;
    let matchesVM = () => true;

    const { id, rule, scope, name } = parseSelector(selector);

    if (rule) {
      matchesRule = (item) => item.rule === rule;
    }

    // match by id, can be combined with rule selection.
    if (id) {
      return {
        isPrimary: item => matchesRule(item) && (item => id === item.id),
        isAlt: () => false
      };
    }

    if (isNullOrUndefined(scope)) {
      // if no scope specified, make sure the found error has no scope.
      matchesScope = item => isNullOrUndefined(item.scope);
    } else {
      matchesScope = item => item.scope === scope;
    }

    if (!isNullOrUndefined(name) && name !== '*') {
      matchesName = item => item.field === name;
    }

    if (!isNullOrUndefined(this.vmId)) {
      matchesVM = (item) => item.vmId === this.vmId;
    }

    // matches the first candidate.
    const isPrimary = (item) => {
      return matchesVM(item) && matchesName(item) && matchesRule(item) && matchesScope(item);
    };

    // matches a second candidate, which is a field with a name containing the '.' character.
    const isAlt = (item) => {
      return matchesVM(item) && matchesRule(item) && item.field === `${scope}.${name}`;
    };

    return {
      isPrimary,
      isAlt
    };
  }

  _match (selector: string) {
    if (isNullOrUndefined(selector)) {
      return undefined;
    }

    const { isPrimary, isAlt } = this._makeCandidateFilters(selector);

    return this.items.reduce((prev, item, idx, arr) => {
      const isLast = idx === arr.length - 1;
      if (prev.primary) {
        return isLast ? prev.primary : prev;
      }

      if (isPrimary(item)) {
        prev.primary = item;
      }

      if (isAlt(item)) {
        prev.alt = item;
      }

      // keep going.
      if (!isLast) {
        return prev;
      }

      return prev.primary || prev.alt;
    }, {});
  };
}
<|endoftext|>


<commit_before>module.exports = function(config) {
    config.set({
        //basePath: "./",

        frameworks: [
            "jasmine",
            "karma-typescript"
        ],

        plugins: [
            "karma-typescript",
            "karma-jasmine",
            "karma-mocha-reporter",
            "karma-jasmine-html-reporter",
            "karma-spec-reporter",
            "karma-chrome-launcher"
        ],

        files: [
            // Decorators (and tests if they are in this folder)
            { pattern: "src/**/*.ts" },

            // Tests in its own folder. If the tests would be placed in the src folder (next to source files)
            // this line should be commented (it's enough with the previous one pattern)
            { pattern: "test/**/*.ts" }
        ],

        preprocessors: {
            "**/*.ts": ["karma-typescript"]
        },

        reporters: ["mocha", "karma-typescript"],  // progress, dots, coverage, html, mocha, kjhtml

        browsers: [
            //"Chrome",
            "ChromeHeadless"
        ],

        port: 9876,
        logLevel: config.LOG_INFO,
        singleRun: true,

        karmaTypescriptConfig: {
            tsconfig: "./tsconfig.test.json",
            include: [
                // This line is only needed if the tests are located in its own folder
                "test/**/*.spec.ts"
            ],
            coverageOptions: {
                // Exclude all .d.ts, .spec.ts and index.ts for coverage report
                exclude: [/\.(d|spec)\.ts$/i, /index\.ts$/i]
            },
            threshold: {
                // Computed over whole source code base, it means, over the total/global
                global: {
                    statements: 75,  // global statements coverage
                    branches: 70,    // global branches coverage
                    functions: -50,  // global uncovered functions coverage
                    lines: 70        // global line coverage
                },
                // Thresholds for each file
                file: {
                    statements: 75,
                    branches: 70,
                    functions: -10,
                    lines: 70,
                    overrides: {
                    // Overrides for specific files
                    'src/services/offline-manager.service.ts': {
                        statements: 60,
                        branches: 60,
                        functions: -40,
                        lines: 60
                    }
                    }
                }
            },
            reports: {
                // Define a reporter to see through console, after the test execution, a coverage summary
                'html': 'coverage',
                'text-summary': null
            }
        },

        remapIstanbulReporter: {
            reports: {
                html: 'coverage',
                lcovonly: './coverage/coverage.lcov'
            }
        }
    });
};<commit_msg>Sort config entries alphabetically<commit_after>module.exports = function(config) {
    config.set({
        basePath: "./",

        browsers: [
            //"Chrome",
            "ChromeHeadless"
        ],

        files: [
            // Decorators (and tests if they are in this folder)
            { pattern: "src/**/*.ts" },

            // Tests in its own folder. If the tests would be placed in the src folder (next to source files)
            // this line should be commented (it's enough with the previous one pattern)
            { pattern: "test/**/*.ts" }
        ],

        frameworks: [
            "jasmine",
            "karma-typescript"
        ],

        karmaTypescriptConfig: {
            tsconfig: "./tsconfig.test.json",
            include: [
                // This line is only needed if the tests are located in its own folder
                "test/**/*.spec.ts"
            ],
            coverageOptions: {
                // Exclude all .d.ts, .spec.ts and index.ts for coverage report
                exclude: [/\.(d|spec)\.ts$/i, /index\.ts$/i]
            },
            threshold: {
                // Computed over whole source code base, it means, over the total/global
                global: {
                    statements: 75,  // global statements coverage
                    branches: 70,    // global branches coverage
                    functions: -50,  // global uncovered functions coverage
                    lines: 70        // global line coverage
                },
                // Thresholds for each file
                file: {
                    statements: 75,
                    branches: 70,
                    functions: -10,
                    lines: 70,
                    overrides: {
                    // Overrides for specific files
                    'src/services/offline-manager.service.ts': {
                        statements: 60,
                        branches: 60,
                        functions: -40,
                        lines: 60
                    }
                    }
                }
            },
            reports: {
                // Define a reporter to see through console, after the test execution, a coverage summary
                'html': 'coverage',
                'text-summary': null
            }
        },

        logLevel: config.LOG_INFO,

        plugins: [
            "karma-typescript",
            "karma-jasmine",
            "karma-mocha-reporter",
            "karma-jasmine-html-reporter",
            "karma-spec-reporter",
            "karma-chrome-launcher"
        ],

        port: 9876,

        preprocessors: {
            "**/*.ts": ["karma-typescript"]
        },

        remapIstanbulReporter: {
            reports: {
                html: 'coverage',
                lcovonly: './coverage/coverage.lcov'
            }
        },

        reporters: ["mocha", "karma-typescript"],  // progress, dots, coverage, html, mocha, kjhtml

        singleRun: true
    });
};<|endoftext|>


<commit_before>import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import rootReducer from '../reducers';

const logger = createLogger(
  {
    collapsed: true,
    predicate: (getState, action) => process.env.NODE_ENV === `development`, // eslint-disable-line no-unused-vars
  }
);

const createStoreWithMiddleware = applyMiddleware(
  thunkMiddleware,
  logger
)(createStore);

export default function configureStore(initialState) {
  const store = createStoreWithMiddleware(rootReducer, initialState);

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept('../reducers', () => {
      const nextRootReducer = require('../reducers/index').default;
      store.replaceReducer(nextRootReducer);
    });
  }

  return store;
}
<commit_msg>Store confiigure<commit_after>import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import rootReducer from '../reducers';

const logger = createLogger({
  collapsed: true,
  predicate: () =>
    process.env.NODE_ENV === `development`, // eslint-disable-line no-unused-vars
});

const createStoreWithMiddleware = applyMiddleware(
  thunkMiddleware,
  logger
)(createStore);

export default function configureStore(initialState) {
  const store = createStoreWithMiddleware(rootReducer, initialState);

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept('../reducers', () => {
      const nextRootReducer = require('../reducers/index').default;
      store.replaceReducer(nextRootReducer);
    });
  }

  return store;
}
<|endoftext|>


<commit_before>(function() {

    var thelawfactory = window.thelawfactory || (window.thelawfactory = {});

    // Sizes in pixels
    var amendmentSize = 20;
    var subjectHMargin = 50;
    var scrollbarWidth;

    // Available size in number of amendments
    var availableWidth;

    // Max number of amendments amongst all subjects
    var maxAmendments = 0;

    // Political groups
    var groupes;

    // Amendment status helpers
    var sort_ordre = {
        "adopté": 0,
        "rejeté": 1,
        "non-voté": 2,
        "en attente": 3
    }, sort_name = {
        "adopté": "Adopté",
        "rejeté": "Rejeté",
        "non-voté": "Non voté",
        "en attente": "En attente"
    }, sort_image = {
        "adopté": "img/ok.png",
        "rejeté": "img/ko.png",
        "non-voté": "img/nd.png",
        "en attente": "img/at.png"
    }, tri = {
        "sort": tri_amdts_sort,
        "groupe": tri_amdts_groupe
    };

    // Helper functions
    function cssColor(col)              { return thelawfactory.utils.adjustColor(col).toString(); }
    function compare_sujets(a, b)       { return a.order - b.order; }
    function compare_groupes(a, b)      { return groupes[a].order - groupes[b].order; }
    function compare_amdts_sort(a, b)   { return sort_ordre[a.sort] - sort_ordre[b.sort]; }
    function compare_amdts_groupe(a, b) { return groupes[a.groupe].order - groupes[b.groupe].order; }
    function compare_amdts_numero(a, b) { return a.numero.replace(/^\D+/, '') - b.numero.replace(/^\D+/, ''); }
    function tri_amdts_sort(a, b)       { return compare_amdts_sort(a, b) || compare_amdts_groupe(a, b) || compare_amdts_numero(a, b); }
    function tri_amdts_groupe(a, b)     { return compare_amdts_groupe(a, b) || compare_amdts_sort(a, b) || compare_amdts_numero(a, b); }

    // Computes the number of amendments in a row
    function getRowSize(isSnake, nbAmdts, rows, row) {
        var rowSize;

        if (isSnake) {
            var fullCols = Math.floor(nbAmdts / rows);
            var lastCount = nbAmdts - fullCols * rows;

            rowSize = fullCols;

            if (fullCols % 2) {
                rowSize += row < rows - lastCount ? 0 : 1;
            } else {
                rowSize += row < lastCount ? 1 : 0;
            }
        } else {
            rowSize = Math.min(nbAmdts - availableWidth * row, availableWidth);
        }

        return rowSize;
    }

    // Column allocation inside a subject
    function allocateAmendments(sujet, sortOrder) {
        var amendements = sujet.amendements;
        var snake = sujet.amendements_snake = [];
        var nb = sujet.amendements.length;

        var nlignes = Math.ceil(nb / availableWidth);
        sujet.height = nlignes * amendmentSize;

        if (sortOrder === 'numero' || nb < availableWidth) {
            // Single line or sort by number
            sujet.amendements_snake = sujet.amendements;
        } else {
            // Multiline, allocate amendments in "snake" order (In columns, left to
            // right, each column in the reverse order as the previous one)
            var ncolonnes = Math.ceil(nb / nlignes);

            // Add placeholders with a unique id_api for holes in the last column
            for (var i = 0; i < nlignes; i++) {
                snake[ncolonnes * nlignes - (i + 1)] = { empty: true, id_api: 'empty-' + i + '-' + sujet.article };
            }

            amendements.forEach(function(a, index) {
                var col = Math.floor(index / nlignes);
                var row = index - col * nlignes;
                if (col % 2 === 1) row = nlignes - row - 1;
                snake[nlignes * col + row] = a;
            });
        }
    }

    thelawfactory.amendements = {
        // Computes available width in amendments
        measureViewportSize: function() {
            if (typeof scrollbarWidth === 'undefined') {
                scrollbarWidth = $('#probe').width() - $('#probe #inside').width();
            }

            var viewportWidth = $('#viz').width() - scrollbarWidth;
            availableWidth = Math.floor((viewportWidth - subjectHMargin) / amendmentSize);
        },

        // Finds amendment id after move from current amendment
        getOffsetAmendmentId: function(scope, amdt, sujet, direction) {
            var sujets = scope.data.sujets;
            var sindex = sujets.indexOf(sujet);
            var schanged = false;

            var nb = sujet.amendements.length;
            var isSnake = sujet.amendements_snake !== sujet.amendements;
            var rows = sujet.height / amendmentSize;
            var cols = isSnake ? Math.ceil(nb / rows) : Math.min(nb, availableWidth);

            var row, col;
            var index = sujet.amendements_snake.indexOf(amdt);

            // Compute row, col position of current amendment
            if (isSnake) {
                col = Math.floor(index / rows);
                row = index - col * rows;
            } else {
                row = Math.floor(index / cols);
                col = index - row * cols;
            }

            // Update position
            switch (direction) {
            case 'up':      row--; break;
            case 'down':    row++; break;
            case 'left':    col--; break;
            case 'right':   col++; break;
            }

            // Horizontal wrap => change rows
            if (col < 0) {
                col = availableWidth - 1;
                row--;
            } else if (col >= getRowSize(isSnake, nb, rows, row)) {
                col = 0;
                row++;
            }

            // Vertical wrap => change subjects
            if (row < 0) {
                if (sindex <= 0) return; // No subject before

                sujet = sujets[sindex - 1];
                schanged = true;
                row = sujet.height / amendmentSize - 1;
            } else if (row >= rows) {
                if (sindex >= sujets.length - 1) return; // No subject after

                sujet = sujets[sindex + 1];
                schanged = true;
                row = 0;
            }

            if (schanged) {
                // Recompute subject variables
                nb = sujet.amendements.length;
                isSnake = sujet.amendements_snake !== sujet.amendements;
                rows = sujet.height / amendmentSize;
                cols = isSnake ? Math.ceil(nb / rows) : Math.min(nb, availableWidth);
            }

            // Restrain col to the actual number of amdts on target row
            var rowSize = getRowSize(isSnake, nb, rows, row);
            col = Math.min(col, rowSize - 1);

            // Find amendment at new row, col position
            if (isSnake) {
                return sujet.amendements_snake[col * rows + row].id_api;
            } else {
                return sujet.amendements_snake[row * cols + col].id_api;
            }
        },

        // Transforms data from api format to something usable by the angular template
        // If onlyReallocate is truthy, only column allocation is performed
        transformData: function(scope, onlyReallocate) {
            var apiData = scope.apiData;
            var tri_amdts = tri[scope.sortOrder] || compare_amdts_numero;
            var en_attente = false;
            var data;

            if (!onlyReallocate) {
                groupes = apiData.groupes;

                data = {
                    groupes: groupes,
                    legende: {
                        groupes: {},
                        sorts: {}
                    },
                    sujets: [],
                    colonnes: ''
                };

                if (scope.groupAll) {
                    // Create single subject when in group mode
                    data.sujets.push({
                        titre: "Tout le texte",
                        amendements: []
                    });
                }

                // Loop over subjects
                Object.keys(apiData.sujets)
                .forEach(function(key) {
                    var sujet = apiData.sujets[key];

                    // Extract article number
                    sujet.article = sujet.details.replace(/^article /, '');

                    // Add status image, group color and group name to amendments
                    sujet.amendements.forEach(function(amdt) {
                        amdt.sort_image = sort_image[amdt.sort];
                        amdt.color = cssColor(groupes[amdt.groupe].color);
                        amdt.nom_groupe = groupes[amdt.groupe].nom;

                        if (amdt.sort === 'en attente') {
                            en_attente = true;
                        }
                    });

                    if (scope.groupAll) {
                        // Group all amendments
                        data.sujets[0].amendements = data.sujets[0].amendements.concat(sujet.amendements);
                    } else {
                        // Sort amendments in the subject
                        sujet.amendements.sort(tri_amdts);

                        // Append subject to subject list
                        data.sujets.push(sujet);

                        // Update max amendment count
                        maxAmendments = Math.max(maxAmendments, sujet.amendements.length);
                    }
                });

                if (scope.groupAll) {
                    // Sort amendments in global subject
                    data.sujets[0].amendements.sort(tri_amdts);
                } else {
                    // Sort subjects
                    data.sujets.sort(compare_sujets);
                }

                // Build legend contents
                Object.keys(groupes).sort(compare_groupes).forEach(function(key) {
                    groupes[key].cssColor = cssColor(groupes[key].color);

                    if (key !== 'Gouvernement') {
                        data.legende.groupes[key] = groupes[key];
                    }
                });

                Object.keys(sort_image).forEach(function(sort) {
                    if (en_attente || sort !== 'en attente') {
                        data.legende.sorts[sort] = {
                            name: sort_name[sort],
                            img: sort_image[sort]
                        };
                    }
                });
            } else {
                data = scope.data;
            }

            var twoColumns = (availableWidth > maxAmendments * 2 + 1);
            data.sujets.forEach(function(s) {
                allocateAmendments(s, scope.sortOrder);
            });

            scope.twoColumnMode = twoColumns;
            scope.subjectWidth = amendmentSize * (twoColumns ? Math.floor((availableWidth / 2) - 1) : availableWidth);
            scope.data = data;
        }
    };

}());<commit_msg>Handle keypresses correctly in two-column mode<commit_after>(function() {

    var thelawfactory = window.thelawfactory || (window.thelawfactory = {});

    // Sizes in pixels
    var amendmentSize = 20;
    var subjectHMargin = 50;
    var scrollbarWidth;

    // Available size in number of amendments
    var availableWidth;

    // Max number of amendments amongst all subjects
    var maxAmendments = 0;

    // Political groups
    var groupes;

    // Amendment status helpers
    var sort_ordre = {
        "adopté": 0,
        "rejeté": 1,
        "non-voté": 2,
        "en attente": 3
    }, sort_name = {
        "adopté": "Adopté",
        "rejeté": "Rejeté",
        "non-voté": "Non voté",
        "en attente": "En attente"
    }, sort_image = {
        "adopté": "img/ok.png",
        "rejeté": "img/ko.png",
        "non-voté": "img/nd.png",
        "en attente": "img/at.png"
    }, tri = {
        "sort": tri_amdts_sort,
        "groupe": tri_amdts_groupe
    };

    // Helper functions
    function cssColor(col)              { return thelawfactory.utils.adjustColor(col).toString(); }
    function compare_sujets(a, b)       { return a.order - b.order; }
    function compare_groupes(a, b)      { return groupes[a].order - groupes[b].order; }
    function compare_amdts_sort(a, b)   { return sort_ordre[a.sort] - sort_ordre[b.sort]; }
    function compare_amdts_groupe(a, b) { return groupes[a.groupe].order - groupes[b.groupe].order; }
    function compare_amdts_numero(a, b) { return a.numero.replace(/^\D+/, '') - b.numero.replace(/^\D+/, ''); }
    function tri_amdts_sort(a, b)       { return compare_amdts_sort(a, b) || compare_amdts_groupe(a, b) || compare_amdts_numero(a, b); }
    function tri_amdts_groupe(a, b)     { return compare_amdts_groupe(a, b) || compare_amdts_sort(a, b) || compare_amdts_numero(a, b); }

    // Computes the number of amendments in a row
    function getRowSize(isSnake, nbAmdts, twoColumns, rows, row) {
        var rowSize;
        var maxWidth = twoColumns ? (availableWidth / 2) - 1 : availableWidth;

        if (isSnake) {
            var fullCols = Math.floor(nbAmdts / rows);
            var lastCount = nbAmdts - fullCols * rows;

            rowSize = fullCols;

            if (fullCols % 2) {
                rowSize += row < rows - lastCount ? 0 : 1;
            } else {
                rowSize += row < lastCount ? 1 : 0;
            }
        } else {
            rowSize = Math.min(nbAmdts - maxWidth * row, maxWidth);
        }

        return rowSize;
    }

    // Column allocation inside a subject
    function allocateAmendments(sujet, sortOrder) {
        var amendements = sujet.amendements;
        var snake = sujet.amendements_snake = [];
        var nb = sujet.amendements.length;

        var nlignes = Math.ceil(nb / availableWidth);
        sujet.height = nlignes * amendmentSize;

        if (sortOrder === 'numero' || nb < availableWidth) {
            // Single line or sort by number
            sujet.amendements_snake = sujet.amendements;
        } else {
            // Multiline, allocate amendments in "snake" order (In columns, left to
            // right, each column in the reverse order as the previous one)
            var ncolonnes = Math.ceil(nb / nlignes);

            // Add placeholders with a unique id_api for holes in the last column
            for (var i = 0; i < nlignes; i++) {
                snake[ncolonnes * nlignes - (i + 1)] = { empty: true, id_api: 'empty-' + i + '-' + sujet.article };
            }

            amendements.forEach(function(a, index) {
                var col = Math.floor(index / nlignes);
                var row = index - col * nlignes;
                if (col % 2 === 1) row = nlignes - row - 1;
                snake[nlignes * col + row] = a;
            });
        }
    }

    thelawfactory.amendements = {
        // Computes available width in amendments
        measureViewportSize: function() {
            if (typeof scrollbarWidth === 'undefined') {
                scrollbarWidth = $('#probe').width() - $('#probe #inside').width();
            }

            var viewportWidth = $('#viz').width() - scrollbarWidth;
            availableWidth = Math.floor((viewportWidth - subjectHMargin) / amendmentSize);
        },

        // Finds amendment id after move from current amendment
        getOffsetAmendmentId: function(scope, amdt, sujet, direction) {
            var twoColumns = scope.twoColumnMode;
            var maxWidth = twoColumns ? (availableWidth / 2) - 1 : availableWidth;
            var sujets = scope.data.sujets;
            var sindex = sujets.indexOf(sujet);
            var schanged = false;

            var nb = sujet.amendements.length;
            var isSnake = sujet.amendements_snake !== sujet.amendements;
            var rows = sujet.height / amendmentSize;
            var cols = isSnake ? Math.ceil(nb / rows) : Math.min(nb, maxWidth);

            var row, col;
            var index = sujet.amendements_snake.indexOf(amdt);

            // Compute row, col position of current amendment
            if (isSnake) {
                col = Math.floor(index / rows);
                row = index - col * rows;
            } else {
                row = Math.floor(index / cols);
                col = index - row * cols;
            }

            // Update position
            switch (direction) {
            case 'up':      row--; break;
            case 'down':    row++; break;
            case 'left':    col--; break;
            case 'right':   col++; break;
            }

            // Horizontal wrap => change rows
            if (col < 0) {
                col = maxWidth - 1;
                row--;
            } else if (col >= getRowSize(isSnake, nb, twoColumns, rows, row)) {
                col = 0;
                row++;
            }

            // Vertical wrap => change subjects
            var inc = (twoColumns && (direction === 'up' || direction === 'down')) ? 2 : 1;
            if (row < 0) {
                if (sindex < inc) return; // No subject before

                sujet = sujets[sindex - inc];
                schanged = true;
                row = sujet.height / amendmentSize - 1;
            } else if (row >= rows) {
                if (sindex >= sujets.length - inc) return; // No subject after

                sujet = sujets[sindex + inc];
                schanged = true;
                row = 0;
            }

            if (schanged) {
                // Recompute subject variables
                nb = sujet.amendements.length;
                isSnake = sujet.amendements_snake !== sujet.amendements;
                rows = sujet.height / amendmentSize;
                cols = isSnake ? Math.ceil(nb / rows) : Math.min(nb, maxWidth);
            }

            // Restrain col to the actual number of amdts on target row
            var rowSize = getRowSize(isSnake, nb, twoColumns, rows, row);
            col = Math.min(col, rowSize - 1);

            // Find amendment at new row, col position
            if (isSnake) {
                return sujet.amendements_snake[col * rows + row].id_api;
            } else {
                return sujet.amendements_snake[row * cols + col].id_api;
            }
        },

        // Transforms data from api format to something usable by the angular template
        // If onlyReallocate is truthy, only column allocation is performed
        transformData: function(scope, onlyReallocate) {
            var apiData = scope.apiData;
            var tri_amdts = tri[scope.sortOrder] || compare_amdts_numero;
            var en_attente = false;
            var data;

            if (!onlyReallocate) {
                groupes = apiData.groupes;

                data = {
                    groupes: groupes,
                    legende: {
                        groupes: {},
                        sorts: {}
                    },
                    sujets: [],
                    colonnes: ''
                };

                if (scope.groupAll) {
                    // Create single subject when in group mode
                    data.sujets.push({
                        titre: "Tout le texte",
                        amendements: []
                    });
                }

                // Loop over subjects
                Object.keys(apiData.sujets)
                .forEach(function(key) {
                    var sujet = apiData.sujets[key];

                    // Extract article number
                    sujet.article = sujet.details.replace(/^article /, '');

                    // Add status image, group color and group name to amendments
                    sujet.amendements.forEach(function(amdt) {
                        amdt.sort_image = sort_image[amdt.sort];
                        amdt.color = cssColor(groupes[amdt.groupe].color);
                        amdt.nom_groupe = groupes[amdt.groupe].nom;

                        if (amdt.sort === 'en attente') {
                            en_attente = true;
                        }
                    });

                    if (scope.groupAll) {
                        // Group all amendments
                        data.sujets[0].amendements = data.sujets[0].amendements.concat(sujet.amendements);
                    } else {
                        // Sort amendments in the subject
                        sujet.amendements.sort(tri_amdts);

                        // Append subject to subject list
                        data.sujets.push(sujet);

                        // Update max amendment count
                        maxAmendments = Math.max(maxAmendments, sujet.amendements.length);
                    }
                });

                if (scope.groupAll) {
                    // Sort amendments in global subject
                    data.sujets[0].amendements.sort(tri_amdts);
                } else {
                    // Sort subjects
                    data.sujets.sort(compare_sujets);
                }

                // Build legend contents
                Object.keys(groupes).sort(compare_groupes).forEach(function(key) {
                    groupes[key].cssColor = cssColor(groupes[key].color);

                    if (key !== 'Gouvernement') {
                        data.legende.groupes[key] = groupes[key];
                    }
                });

                Object.keys(sort_image).forEach(function(sort) {
                    if (en_attente || sort !== 'en attente') {
                        data.legende.sorts[sort] = {
                            name: sort_name[sort],
                            img: sort_image[sort]
                        };
                    }
                });
            } else {
                data = scope.data;
            }

            var twoColumns = (availableWidth > maxAmendments * 2 + 1);
            data.sujets.forEach(function(s) {
                allocateAmendments(s, scope.sortOrder);
            });

            scope.twoColumnMode = twoColumns;
            scope.subjectWidth = amendmentSize * (twoColumns ? Math.floor((availableWidth / 2) - 1) : availableWidth);
            scope.data = data;
        }
    };

}());<|endoftext|>