<commit_before>'use strict';
(function () {
    //Projects Controller Spec
    describe('MEAN controllers', function () {
        describe('ProjectsController', function () {
            beforeEach(function () {
                jasmine.addMatchers({
                    toEqualData: function () {
                        return {
                            compare: function (actual, expected) {
                                return {
                                    pass: angular.equals(actual, expected)
                                };
                            }
                        };
                    }
                });
            });

            beforeEach(function () {
                module('mean');
                module('mean.system');
                module('mean.projects');

                module('authMock');
            });

            var ProjectsController,
                    scope,
                    $httpBackend,
                    $stateParams,
                    $location,
                    MeanUser;

            beforeEach(inject(function ($controller, $rootScope, _$location_, _$stateParams_, _$httpBackend_, _MeanUser_) {
                MeanUser = _MeanUser_;
                scope = $rootScope.$new();
                ProjectsController = $controller('ProjectsController', {
                    $scope: scope,
                    MeanUser: MeanUser
                });

                $stateParams = _$stateParams_;
                $httpBackend = _$httpBackend_;
                $location = _$location_;
            }));

            it('$scope.find() should create an array with at least one project object ' +
                    'fetched from XHR', function () {

                        MeanUser.login();
                        $httpBackend.expectGET('api\/projects?ascending=true&ordering=project_ref&page=1').respond([{
                                title: 'Human rights'
                            }]);
                        // run controller
                        scope.find();

                        $httpBackend.flush();
                        expect(scope.projects).toEqualData([{
                                "title": "Human rights"
                            }]);
                    });

            it('$scope.findOne() should create an array with one project object fetched ' +
                    'from XHR using a projectId URL parameter', function () {
                        // fixture URL parament
                        $stateParams.projectId = '525a8422f6d0f87f0e407a33';
                        // fixture response object
                        var testProjectData = function () {
                            return {
                                title: 'Human rights',
                                intermediary_reports: []
                            };
                        };
                        // test expected GET request with response object
                        $httpBackend.expectGET(/api\/projects\/([0-9a-fA-F]{24})$/).respond(testProjectData());
                        // run controller
                        scope.findOne();
                        $httpBackend.flush();
                        // test scope value
                        expect(scope.project).toEqualData(testProjectData());
                    });

            it('$scope.create() with valid form data should send POST request ', function () {

                var now = new Date();

                scope.project = {
                    title: 'Human rights',
                    methods: [],
                    reg_date: new Date(now.getFullYear(), now.getMonth(),
                            now.getDay() + 15).toISOString()
                            // I have no idea on earth why the day is off by 15...
                };

                var postProjectData = function () {
                    return scope.project;
                };
//                // fixture response data
                var responseProjectData = function () {
                    return {
                        _id: '525cf20451979dea2c000001',
                        title: 'Human rights',
                        methods: []
                    };
                };

                scope.title = 'Human rights';
                // test post request is sent
                $httpBackend.expectPOST('api\/projects', postProjectData()).respond(responseProjectData());
                // Run controller
                scope.create(true);

                $httpBackend.flush();
                // test form input is reset
                expect(scope.project).toEqual({});
                // test URL location to new object'

                expect($location.path()).toEqual('/projects/' + responseProjectData()._id);
            });

            it('$scope.remove() should send a DELETE request with a valid projectId ' +
                    'and remove the project from the scope', inject(function (Projects) {

                        // fixture rideshare
                        var project = new Projects({
                            _id: '525a8422f6d0f87f0e407a33'
                        });

                        // mock rideshares in scope
                        scope.projects = [];
                        scope.projects.push(project);

                        // test expected rideshare DELETE request
                        $httpBackend.expectDELETE(/api\/projects\/([0-9a-fA-F]{24})$/).respond(204);

                        // run controller
                        scope.remove(project);
                        $httpBackend.flush();

                        // test after successful delete URL location projects list
                        expect($location.path()).toBe('/projects');
                        expect(scope.projects.length).toBe(0);

                    }));

                    // TODO: update-function is using MeanUser.user.name to get
                    // user's name for updated-array and test fails there.
//            it('$scope.update(true) should a valid project', inject(function (Projects) {
//
//                MeanUser.login();
//
//                // fixture rideshare
//                var putProjectData = function () {
//                    return {
//                        _id: '525a8422f6d0117f0e407a33',
//                        intermediary_reports: [],
//                        end_report: {
//                            date: undefined,
//                            processed: false
//                        },
//                        state: 'rekisteröity',
//                        title: 'Human Rights',
//                        to: 'Children rights'
//                    };
//                };
//
//                var project = new Projects(putProjectData);
//                scope.project = project;
//                scope.project.intermediary_reports = [];
//                scope.project.end_report = {};
////                MeanUser.name = "test user";
//                var user = "test user";
//
//                spyOn(MeanUser, 'user').andReturn(user);
//
//                // test PUT happens correctly
//                $httpBackend.expectPUT(/api\/projects\/([0-9a-fA-F]{24})$\/edit/).respond();
//
//                // run controller
//                scope.update(true);
//                $httpBackend.flush();
//
//                // test URL location to new object
//                expect($location.path()).toBe('/projects/' + putProjectData()._id);
//            }));

            it('$scope.addReviewState(true) should update a valid project', inject(function (Projects) {

                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        in_review: {
                            comments: "This is a comment"
                        },
                        state: 'rekisteröity',
                        to: 'käsittelyssä'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/rev\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addReviewState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

            it('$scope.addApprovedState(true) should update a valid project', inject(function (Projects) {

                scope.day = 12;
                scope.month = 11;
                scope.year = 2015;
                scope.date = scope.convertDate(scope.day, scope.month, scope.year);
                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        approved: {
                          approved_date: scope.date,
                          approved_by: "Toiminnanjohtaja",
                          board_notified: scope.date,
                          themes: ["Oikeusvaltio ja demokratia"],
                          granted_sum_eur: 60000
                        },
                        state: 'käsittelyssä',
                        to: 'hyväksytty'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/appr\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addApprovedState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

            it('$scope.addRejectedState(true) should update a valid project', inject(function (Projects) {

                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        rejected: {
                            rejection_categories: ["1 Hanke ei ole ihmisoikeushanke"]
                        },
                        state: 'käsittelyssä',
                        to: 'hylätty'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;
                scope.addedRejections = ["1 Hanke ei ole ihmisoikeushanke"];

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/rej\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addRejectedState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

            it('$scope.addSignedState(true) should update a valid project', inject(function (Projects) {

                // fixture rideshare

                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        signed: {
                            signed_by: 'Jaana Jantunen',
                            signed_date: scope.convertDate(11, 11, 2015),
                            planned_payments: [{"date": scope.convertDate(11, 11, 2015), "sum_eur": 50000, "sum_local": 80000}],
                            intreport_deadlines: [{"report": "1. väliraportti", "date": scope.convertDate(11, 11, 2015)}]
                        },
                        state: 'hyväksytty',
                        to: 'allekirjoitettu'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;
                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/sign\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addSignedState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

            it('$scope.addIntReportState(true) should update a valid project', inject(function (Projects) {
//                scope.day = 12;
//                scope.month = 11;
//                scope.year = 2015;
//                scope.date = scope.convertDate(scope.day, scope.month, scope.year);
//
                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        intermediary_reports: [],
                        intermediary_report: {
                            methods: ["Onnistui", "Onnistui kohtalaisesti"],
                            objectives: ["Lorem ipsum"],
                            overall_rating_kios: "Comments from kios",
                            approved_by: "Halko",
                            date_approved: scope.convertDate(11, 11, 2015),
                            comments: "General comments"
                        },
                        state: 'allekirjoitettu',
                        to: 'väliraportti'

                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/intReport\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addIntReportState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);
            }));

            it('$scope.addEndReportState(true) should update a valid project', inject(function (Projects) {
                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        end_report: {
                            audit: {"date": scope.convertDate(11, 12, 2015), "review": "arvio"},
                            approved_by: "toimitusjohtaja",
                            approved_date: scope.convertDate(12, 12, 2015),
                            general_review: "kommentti",
                            methods: [{"name": "metodi", "level": "paikallinen"}],
                            objectives: "tavoite",
                            comments: "kommenttia"
                        },
                        state: 'väliraportti',
                        to: 'loppuraportti'

                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/endReport\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addEndReportState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);
            }));

            it('$scope.addEndedState(true) should update a valid project', inject(function (Projects) {

                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        ended: {
                            end_date: new Date(2015, 12, 12),
                            board_notified: new Date(2015, 12, 12),
                            approved_by: "toimitusjohtaja",
                            other_comments: "kommentti"
                        },
                        state: 'loppuraportti',
                        to: 'päättynyt'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/end\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addEndedState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

        });
    });
}());

// Mocked service to check if user is logged in
angular.module('authMock', [])
        .provider('MeanUser', function () {
            this.checkLoggedin = false;
            this.$get = function () {
                return  {
                    login: function () {
                        this.checkLoggedin = true;
                    },
                    logout: function () {
                        this.checkLoggedin = false;
                    },
                    isLoggedIn: function () {
                        return this.checkLoggedin;
                    }
                };
            };
        });
<commit_msg>Fixed that issue with dates in project controller test suite. I was transforming date to string using day of the week instead day of the month for the day value...<commit_after>'use strict';
(function () {
    //Projects Controller Spec
    describe('MEAN controllers', function () {
        describe('ProjectsController', function () {
            beforeEach(function () {
                jasmine.addMatchers({
                    toEqualData: function () {
                        return {
                            compare: function (actual, expected) {
                                return {
                                    pass: angular.equals(actual, expected)
                                };
                            }
                        };
                    }
                });
            });

            beforeEach(function () {
                module('mean');
                module('mean.system');
                module('mean.projects');

                module('authMock');
            });

            var ProjectsController,
                    scope,
                    $httpBackend,
                    $stateParams,
                    $location,
                    MeanUser;

            beforeEach(inject(function ($controller, $rootScope, _$location_, _$stateParams_, _$httpBackend_, _MeanUser_) {
                MeanUser = _MeanUser_;
                scope = $rootScope.$new();
                ProjectsController = $controller('ProjectsController', {
                    $scope: scope,
                    MeanUser: MeanUser
                });

                $stateParams = _$stateParams_;
                $httpBackend = _$httpBackend_;
                $location = _$location_;
            }));

            it('$scope.find() should create an array with at least one project object ' +
                    'fetched from XHR', function () {

                        MeanUser.login();
                        $httpBackend.expectGET('api\/projects?ascending=true&ordering=project_ref&page=1').respond([{
                                title: 'Human rights'
                            }]);
                        // run controller
                        scope.find();

                        $httpBackend.flush();
                        expect(scope.projects).toEqualData([{
                                "title": "Human rights"
                            }]);
                    });

            it('$scope.findOne() should create an array with one project object fetched ' +
                    'from XHR using a projectId URL parameter', function () {
                        // fixture URL parament
                        $stateParams.projectId = '525a8422f6d0f87f0e407a33';
                        // fixture response object
                        var testProjectData = function () {
                            return {
                                title: 'Human rights',
                                intermediary_reports: []
                            };
                        };
                        // test expected GET request with response object
                        $httpBackend.expectGET(/api\/projects\/([0-9a-fA-F]{24})$/).respond(testProjectData());
                        // run controller
                        scope.findOne();
                        $httpBackend.flush();
                        // test scope value
                        expect(scope.project).toEqualData(testProjectData());
                    });

            it('$scope.create() with valid form data should send POST request ', function () {

                var now = new Date();

                scope.project = {
                    title: 'Human rights',
                    methods: [],
                    reg_date: new Date(now.getFullYear(), now.getMonth(),
                            now.getDate() + 1).toISOString()
                            // I have no idea on earth why the day is off by 15...
                };

                var postProjectData = function () {
                    return scope.project;
                };
//                // fixture response data
                var responseProjectData = function () {
                    return {
                        _id: '525cf20451979dea2c000001',
                        title: 'Human rights',
                        methods: []
                    };
                };

                scope.title = 'Human rights';
                // test post request is sent
                $httpBackend.expectPOST('api\/projects', postProjectData()).respond(responseProjectData());
                // Run controller
                scope.create(true);

                $httpBackend.flush();
                // test form input is reset
                expect(scope.project).toEqual({});
                // test URL location to new object'

                expect($location.path()).toEqual('/projects/' + responseProjectData()._id);
            });

            it('$scope.remove() should send a DELETE request with a valid projectId ' +
                    'and remove the project from the scope', inject(function (Projects) {

                        // fixture rideshare
                        var project = new Projects({
                            _id: '525a8422f6d0f87f0e407a33'
                        });

                        // mock rideshares in scope
                        scope.projects = [];
                        scope.projects.push(project);

                        // test expected rideshare DELETE request
                        $httpBackend.expectDELETE(/api\/projects\/([0-9a-fA-F]{24})$/).respond(204);

                        // run controller
                        scope.remove(project);
                        $httpBackend.flush();

                        // test after successful delete URL location projects list
                        expect($location.path()).toBe('/projects');
                        expect(scope.projects.length).toBe(0);

                    }));

                    // TODO: update-function is using MeanUser.user.name to get
                    // user's name for updated-array and test fails there.
//            it('$scope.update(true) should a valid project', inject(function (Projects) {
//
//                MeanUser.login();
//
//                // fixture rideshare
//                var putProjectData = function () {
//                    return {
//                        _id: '525a8422f6d0117f0e407a33',
//                        intermediary_reports: [],
//                        end_report: {
//                            date: undefined,
//                            processed: false
//                        },
//                        state: 'rekisteröity',
//                        title: 'Human Rights',
//                        to: 'Children rights'
//                    };
//                };
//
//                var project = new Projects(putProjectData);
//                scope.project = project;
//                scope.project.intermediary_reports = [];
//                scope.project.end_report = {};
////                MeanUser.name = "test user";
//                var user = "test user";
//
//                spyOn(MeanUser, 'user').andReturn(user);
//
//                // test PUT happens correctly
//                $httpBackend.expectPUT(/api\/projects\/([0-9a-fA-F]{24})$\/edit/).respond();
//
//                // run controller
//                scope.update(true);
//                $httpBackend.flush();
//
//                // test URL location to new object
//                expect($location.path()).toBe('/projects/' + putProjectData()._id);
//            }));

            it('$scope.addReviewState(true) should update a valid project', inject(function (Projects) {

                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        in_review: {
                            comments: "This is a comment"
                        },
                        state: 'rekisteröity',
                        to: 'käsittelyssä'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/rev\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addReviewState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

            it('$scope.addApprovedState(true) should update a valid project', inject(function (Projects) {

                scope.day = 12;
                scope.month = 11;
                scope.year = 2015;
                scope.date = scope.convertDate(scope.day, scope.month, scope.year);
                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        approved: {
                          approved_date: scope.date,
                          approved_by: "Toiminnanjohtaja",
                          board_notified: scope.date,
                          themes: ["Oikeusvaltio ja demokratia"],
                          granted_sum_eur: 60000
                        },
                        state: 'käsittelyssä',
                        to: 'hyväksytty'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/appr\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addApprovedState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

            it('$scope.addRejectedState(true) should update a valid project', inject(function (Projects) {

                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        rejected: {
                            rejection_categories: ["1 Hanke ei ole ihmisoikeushanke"]
                        },
                        state: 'käsittelyssä',
                        to: 'hylätty'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;
                scope.addedRejections = ["1 Hanke ei ole ihmisoikeushanke"];

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/rej\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addRejectedState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

            it('$scope.addSignedState(true) should update a valid project', inject(function (Projects) {

                // fixture rideshare

                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        signed: {
                            signed_by: 'Jaana Jantunen',
                            signed_date: scope.convertDate(11, 11, 2015),
                            planned_payments: [{"date": scope.convertDate(11, 11, 2015), "sum_eur": 50000, "sum_local": 80000}],
                            intreport_deadlines: [{"report": "1. väliraportti", "date": scope.convertDate(11, 11, 2015)}]
                        },
                        state: 'hyväksytty',
                        to: 'allekirjoitettu'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;
                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/sign\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addSignedState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

            it('$scope.addIntReportState(true) should update a valid project', inject(function (Projects) {
//                scope.day = 12;
//                scope.month = 11;
//                scope.year = 2015;
//                scope.date = scope.convertDate(scope.day, scope.month, scope.year);
//
                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        intermediary_reports: [],
                        intermediary_report: {
                            methods: ["Onnistui", "Onnistui kohtalaisesti"],
                            objectives: ["Lorem ipsum"],
                            overall_rating_kios: "Comments from kios",
                            approved_by: "Halko",
                            date_approved: scope.convertDate(11, 11, 2015),
                            comments: "General comments"
                        },
                        state: 'allekirjoitettu',
                        to: 'väliraportti'

                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/intReport\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addIntReportState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);
            }));

            it('$scope.addEndReportState(true) should update a valid project', inject(function (Projects) {
                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        end_report: {
                            audit: {"date": scope.convertDate(11, 12, 2015), "review": "arvio"},
                            approved_by: "toimitusjohtaja",
                            approved_date: scope.convertDate(12, 12, 2015),
                            general_review: "kommentti",
                            methods: [{"name": "metodi", "level": "paikallinen"}],
                            objectives: "tavoite",
                            comments: "kommenttia"
                        },
                        state: 'väliraportti',
                        to: 'loppuraportti'

                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/endReport\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addEndReportState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);
            }));

            it('$scope.addEndedState(true) should update a valid project', inject(function (Projects) {

                // fixture rideshare
                var putProjectData = function () {
                    return {
                        _id: '525a8422f6d0f87f0e407a33',
                        ended: {
                            end_date: new Date(2015, 12, 12),
                            board_notified: new Date(2015, 12, 12),
                            approved_by: "toimitusjohtaja",
                            other_comments: "kommentti"
                        },
                        state: 'loppuraportti',
                        to: 'päättynyt'
                    };
                };

                // mock project object from form
                var project = new Projects(putProjectData());

                // mock project in scope
                scope.project = project;

                // test PUT happens correctly
                $httpBackend.expectPUT(/api\/projects\/end\/([0-9a-fA-F]{24})$/).respond();

                // run controller
                scope.addEndedState(true);
                $httpBackend.flush();

                // test URL location to new object
                expect($location.path()).toBe('/projects/' + putProjectData()._id);

            }));

        });
    });
}());

// Mocked service to check if user is logged in
angular.module('authMock', [])
        .provider('MeanUser', function () {
            this.checkLoggedin = false;
            this.$get = function () {
                return  {
                    login: function () {
                        this.checkLoggedin = true;
                    },
                    logout: function () {
                        this.checkLoggedin = false;
                    },
                    isLoggedIn: function () {
                        return this.checkLoggedin;
                    }
                };
            };
        });
<|endoftext|>


<commit_before>/**
 * Creates the timestamp popup
 * @param target is a dom element to show the popup on
 * @param timestamp is the timestamp to use for the popup
 */
function createTimestampPopup(target, timestamp) {
    var m = moment(timestamp);
    $(target).popup({
        title: m.fromNow(),
        content: m.format("dddd, MMMM Do YYYY"),
        //popup: '.timestampPopup',
        position: "top right"
        //hoverable: true,
        //movePopup: true
    }).popup('show');
}

/**
 * Parses out #room mentions in message
 * @param message
 * @returns message with html room replacements
 */
function parseRoomLinks(message) {
    var rooms = Rooms.find({}, {'_id': 1, 'name': 1}).fetch();
    rooms = _.sortBy(rooms, function (room) {
        return -room.name.length;
    }); // TODO: Not this every message
    var loc = -1;
    while ((loc = message.indexOf("#", loc + 1)) >= 0) {
        for (var i = 0; i < rooms.length; i++) {
            var roomName = rooms[i].name;
            if (message.indexOf(rooms[i].name, loc) === loc + 1) {
                var leftHalf = message.substring(0, loc);
                var middle = '#' + roomName + '';
                var rightHalf = message.substring(loc + roomName.length + 1, message.length + middle.length);
                message = leftHalf + middle + rightHalf;
                loc = loc + middle.length - 1;
                break;
            }
        }
    }
    return message;
}

/**
 * Parses @mentions in messages
 * @param message
 * @returns message with html name replacements
 */
function parseNameMentions(message) {
    var users = Meteor.users.find({}, {fields: {'_id': 1, 'username': 1, 'profile.color': 1}}).fetch();
    users = _(users).sortBy(function (user) {
        return -user.username.length;
    }); // TODO: Not this every message
    var loc = -1;
    while ((loc = message.indexOf("@", loc + 1)) >= 0) {
        for (var i = 0; i < users.length; i++) {
            var userName = users[i].username;
            if (message.indexOf(userName, loc) === loc + 1) {
                var leftHalf = message.substring(0, loc);
                var userColor = (users[i].profile && users[i].profile.color) || "black";
                var styleString = 'style="border-bottom: 2px solid ' + userColor + ';' + 'background: ' + tinycolor(userColor).setAlpha(0.075).toRgbString() + ';"';
                var middle = '@' + userName + '';
                var rightHalf = message.substring(loc + userName.length + 1, message.length + middle.length);
                message = leftHalf + middle + rightHalf;
                loc = loc + middle.length - 1;
                break;
            }
        }
    }
    return message;
}

/**
 * Determines if message contains current users username
 * @param message
 * @returns true if message contains current username, false otherwise
 */
function hasUserMentions(message) {
    if (!message || typeof  message !== "string") {return false;}
    var regex = new RegExp("[@\\s]+(" + Meteor.user({}, {fields: {'username': 1}}).username + ")($|[\\s!.?]+)");
    var regexMatch = message.match(regex);

    return regexMatch && regexMatch.length > 0;
}

Template.message.created = function () {
    //createTimestampPopup(this.data.timestamp);
};

Template.message.rendered = function () {
    //this.$('.likeMessageLink').popup();
};

Template.message.helpers({
    myMessage: function () {
        return this.authorId === Meteor.userId() ? "my-message" : "";
    },
    color: function () {
        var user = Meteor.users.findOne({_id: this.authorId}, {fields: {'profile.color': 1}});
        if (user && user.profile && user.profile.color) {
            return "border-left: 3px solid" + user.profile.color;
        }
        else {
            return "border-left: 3px solid transparent";
        }
    },
    hasEdits: function () {
        return this.lastedited;
    },
    lastEditTime: function () {
        if (!this.lastedited) {
            return;
        }
        return moment(this.lastedited).format("h:mm:ss a");
    },
    showTimestamp: function () {
        var m = moment(new Date(this.timestamp));
        var user = Meteor.users.findOne({_id: Meteor.userId()}, {fields: {"profile.use24HrTime": 1}});
        return user && user.profile && user.profile.use24HrTime ? m.format("HH:mm:ss") : m.format("hh:mm:ss a");
    },
    isPlain: function () {
        return this.type === "plain";
    },
    isRich: function () {
        return this.type === "rich";
    },
    layoutName: function () {
        return this.layout + "Message";
    },
    isFeedback: function () {
        return this.type === "feedback";
    },
    isUnderEdit: function () {
        return Session.get('editingId') === this._id;
    },
    canEdit: function () {
        return this.authorId === Meteor.userId();
    },
    hasMention: function () {
        return this.authorId !== Meteor.userId() && hasUserMentions(this.message) ? "has-mention" : "";
    },
    finalMessageBody: function () {
        if (this.message && typeof(this.message) === "string") {
            var emojiString = emojify.replace(parseRoomLinks(parseNameMentions(_s.escapeHTML(this.message))));
            return Autolinker.link(emojiString, {twitter: false, className: "message-link"});
        }
    },
    starIcon: function () {
        return _(this.likedBy).contains(Meteor.userId()) ? "fa-star" : "fa-star-o";
    },
    starSizingStyle: function () {
        var parentContext = Template.instance().parentTemplate();
        if (parentContext && parentContext.supressStarSizing) {
            return "";
        }
        if (this.likedBy.length === 0) {
            return "";
        }
        var room = Rooms.findOne({_id: this.roomId}, {fields: {users: 1}});
        if (!room || !room.users || room.users.length < 1) {
            return "";
        }
        var scale = this.likedBy.length / (room.users.length / 1.5);
        var bonus = 400;
        var total = 100 + _.min([bonus * scale, 400]);
        return "font-size: " + total + "%;";
    }
});

Template.message.events({
    "click .likeMessageLink": function (event, template) {
        event.preventDefault();

        if (!_(this.likedBy).contains(Meteor.userId())) {
            Meteor.call('likeMessage', template.data._id);
        }
        else {
            Meteor.call('unlikeMessage', template.data._id);
        }

    },
    "click .editMessageButton": function (event, template) {
        if (template.data.authorId === Meteor.userId()) {
            Session.set('editingId', template.data._id);
        }
    },
    "click .messageEditSubmit": function (event, template) {
        event.preventDefault();
        var newMessage = template.find('input[name=newMessageText]').value;

        Meteor.call('editMessage', {_id: template.data._id, message: newMessage});
        Session.set('editingId', "");
    },
    "click .canceleEditSubmit": function () {
        Session.set('editingId', "");
    },
    'mouseenter .message-timestamp': function (event, template) {
        createTimestampPopup(event.target, template.data.timestamp);
    },
    'mouseenter .message-user-mention': function (event, template) {
        var userId = $(event.target).data("userid");
        showPopup(event.target, "userProfileCard", userId);
    },
    'mouseenter .likeMessageLink' : function(event, template){
        var userId = $(event.target).data("userid");
        showPopup(event.target, "starredByListPopup", template.data._id);
    },
    'click .removeMessageButton': function (event, template) {
        event.preventDefault();
        Meteor.call('removeMessage', template.data._id);
    },
});<commit_msg>dead code<commit_after>/**
 * Creates the timestamp popup
 * @param target is a dom element to show the popup on
 * @param timestamp is the timestamp to use for the popup
 */
function createTimestampPopup(target, timestamp) {
    var m = moment(timestamp);
    $(target).popup({
        title: m.fromNow(),
        content: m.format("dddd, MMMM Do YYYY"),
        //popup: '.timestampPopup',
        position: "top right"
        //hoverable: true,
        //movePopup: true
    }).popup('show');
}

/**
 * Parses out #room mentions in message
 * @param message
 * @returns message with html room replacements
 */
function parseRoomLinks(message) {
    var rooms = Rooms.find({}, {'_id': 1, 'name': 1}).fetch();
    rooms = _.sortBy(rooms, function (room) {
        return -room.name.length;
    }); // TODO: Not this every message
    var loc = -1;
    while ((loc = message.indexOf("#", loc + 1)) >= 0) {
        for (var i = 0; i < rooms.length; i++) {
            var roomName = rooms[i].name;
            if (message.indexOf(rooms[i].name, loc) === loc + 1) {
                var leftHalf = message.substring(0, loc);
                var middle = '#' + roomName + '';
                var rightHalf = message.substring(loc + roomName.length + 1, message.length + middle.length);
                message = leftHalf + middle + rightHalf;
                loc = loc + middle.length - 1;
                break;
            }
        }
    }
    return message;
}

/**
 * Parses @mentions in messages
 * @param message
 * @returns message with html name replacements
 */
function parseNameMentions(message) {
    var users = Meteor.users.find({}, {fields: {'_id': 1, 'username': 1, 'profile.color': 1}}).fetch();
    users = _(users).sortBy(function (user) {
        return -user.username.length;
    }); // TODO: Not this every message
    var loc = -1;
    while ((loc = message.indexOf("@", loc + 1)) >= 0) {
        for (var i = 0; i < users.length; i++) {
            var userName = users[i].username;
            if (message.indexOf(userName, loc) === loc + 1) {
                var leftHalf = message.substring(0, loc);
                var userColor = (users[i].profile && users[i].profile.color) || "black";
                var styleString = 'style="border-bottom: 2px solid ' + userColor + ';' + 'background: ' + tinycolor(userColor).setAlpha(0.075).toRgbString() + ';"';
                var middle = '@' + userName + '';
                var rightHalf = message.substring(loc + userName.length + 1, message.length + middle.length);
                message = leftHalf + middle + rightHalf;
                loc = loc + middle.length - 1;
                break;
            }
        }
    }
    return message;
}

/**
 * Determines if message contains current users username
 * @param message
 * @returns true if message contains current username, false otherwise
 */
function hasUserMentions(message) {
    if (!message || typeof  message !== "string") {return false;}
    var regex = new RegExp("[@\\s]+(" + Meteor.user({}, {fields: {'username': 1}}).username + ")($|[\\s!.?]+)");
    var regexMatch = message.match(regex);

    return regexMatch && regexMatch.length > 0;
}

Template.message.helpers({
    myMessage: function () {
        return this.authorId === Meteor.userId() ? "my-message" : "";
    },
    color: function () {
        var user = Meteor.users.findOne({_id: this.authorId}, {fields: {'profile.color': 1}});
        if (user && user.profile && user.profile.color) {
            return "border-left: 3px solid" + user.profile.color;
        }
        else {
            return "border-left: 3px solid transparent";
        }
    },
    hasEdits: function () {
        return this.lastedited;
    },
    lastEditTime: function () {
        if (!this.lastedited) {
            return;
        }
        return moment(this.lastedited).format("h:mm:ss a");
    },
    showTimestamp: function () {
        var m = moment(new Date(this.timestamp));
        var user = Meteor.users.findOne({_id: Meteor.userId()}, {fields: {"profile.use24HrTime": 1}});
        return user && user.profile && user.profile.use24HrTime ? m.format("HH:mm:ss") : m.format("hh:mm:ss a");
    },
    isPlain: function () {
        return this.type === "plain";
    },
    isRich: function () {
        return this.type === "rich";
    },
    layoutName: function () {
        return this.layout + "Message";
    },
    isFeedback: function () {
        return this.type === "feedback";
    },
    isUnderEdit: function () {
        return Session.get('editingId') === this._id;
    },
    canEdit: function () {
        return this.authorId === Meteor.userId();
    },
    hasMention: function () {
        return this.authorId !== Meteor.userId() && hasUserMentions(this.message) ? "has-mention" : "";
    },
    finalMessageBody: function () {
        if (this.message && typeof(this.message) === "string") {
            var emojiString = emojify.replace(parseRoomLinks(parseNameMentions(_s.escapeHTML(this.message))));
            return Autolinker.link(emojiString, {twitter: false, className: "message-link"});
        }
    },
    starIcon: function () {
        return _(this.likedBy).contains(Meteor.userId()) ? "fa-star" : "fa-star-o";
    },
    starSizingStyle: function () {
        var parentContext = Template.instance().parentTemplate();
        if (parentContext && parentContext.supressStarSizing) {
            return "";
        }
        if (this.likedBy.length === 0) {
            return "";
        }
        var room = Rooms.findOne({_id: this.roomId}, {fields: {users: 1}});
        if (!room || !room.users || room.users.length < 1) {
            return "";
        }
        var scale = this.likedBy.length / (room.users.length / 1.5);
        var bonus = 400;
        var total = 100 + _.min([bonus * scale, 400]);
        return "font-size: " + total + "%;";
    }
});

Template.message.events({
    "click .likeMessageLink": function (event, template) {
        event.preventDefault();

        if (!_(this.likedBy).contains(Meteor.userId())) {
            Meteor.call('likeMessage', template.data._id);
        }
        else {
            Meteor.call('unlikeMessage', template.data._id);
        }

    },
    "click .editMessageButton": function (event, template) {
        if (template.data.authorId === Meteor.userId()) {
            Session.set('editingId', template.data._id);
        }
    },
    "click .messageEditSubmit": function (event, template) {
        event.preventDefault();
        var newMessage = template.find('input[name=newMessageText]').value;

        Meteor.call('editMessage', {_id: template.data._id, message: newMessage});
        Session.set('editingId', "");
    },
    "click .canceleEditSubmit": function () {
        Session.set('editingId', "");
    },
    'mouseenter .message-timestamp': function (event, template) {
        createTimestampPopup(event.target, template.data.timestamp);
    },
    'mouseenter .message-user-mention': function (event, template) {
        var userId = $(event.target).data("userid");
        showPopup(event.target, "userProfileCard", userId);
    },
    'mouseenter .likeMessageLink' : function(event, template){
        var userId = $(event.target).data("userid");
        showPopup(event.target, "starredByListPopup", template.data._id);
    },
    'click .removeMessageButton': function (event, template) {
        event.preventDefault();
        Meteor.call('removeMessage', template.data._id);
    },
});<|endoftext|>


<commit_before>/**
  @module ember-data
*/

import {
  RecordArray,
  FilteredRecordArray,
  AdapterPopulatedRecordArray,
  ManyArray
} from "ember-data/system/record_arrays";
import {
  MapWithDefault,
  OrderedSet
} from "ember-data/system/map";
var get = Ember.get;
var forEach = Ember.EnumerableUtils.forEach;

/**
  @class RecordArrayManager
  @namespace DS
  @private
  @extends Ember.Object
*/
export default Ember.Object.extend({
  init: function() {
    this.filteredRecordArrays = MapWithDefault.create({
      defaultValue: function() { return []; }
    });

    this.changedRecords = [];
    this._adapterPopulatedRecordArrays = [];
  },

  recordDidChange: function(record) {
    if (this.changedRecords.push(record) !== 1) { return; }

    Ember.run.schedule('actions', this, this.updateRecordArrays);
  },

  recordArraysForRecord: function(record) {
    record._recordArrays = record._recordArrays || OrderedSet.create();
    return record._recordArrays;
  },

  /**
    This method is invoked whenever data is loaded into the store by the
    adapter or updated by the adapter, or when a record has changed.

    It updates all record arrays that a record belongs to.

    To avoid thrashing, it only runs at most once per run loop.

    @method updateRecordArrays
    @param {Class} type
    @param {Number|String} clientId
  */
  updateRecordArrays: function() {
    forEach(this.changedRecords, function(record) {
      if (get(record, 'isDeleted')) {
        this._recordWasDeleted(record);
      } else {
        this._recordWasChanged(record);
      }
    }, this);

    this.changedRecords.length = 0;
  },

  _recordWasDeleted: function (record) {
    var recordArrays = record._recordArrays;

    if (!recordArrays) { return; }

    recordArrays.forEach(function(array){
      array.removeRecord(record);
    });
  },

  _recordWasChanged: function (record) {
    var type = record.constructor;
    var recordArrays = this.filteredRecordArrays.get(type);
    var filter;

    forEach(recordArrays, function(array) {
      filter = get(array, 'filterFunction');
      this.updateRecordArray(array, filter, type, record);
    }, this);

    // loop through all manyArrays containing an unloaded copy of this
    // clientId and notify them that the record was loaded.
    var manyArrays = record._loadingRecordArrays;

    if (manyArrays) {
      for (var i=0, l=manyArrays.length; i<l; i++) {
        manyArrays[i].loadedRecord();
      }

      record._loadingRecordArrays = [];
    }
  },

  /**
    Update an individual filter.

    @method updateRecordArray
    @param {DS.FilteredRecordArray} array
    @param {Function} filter
    @param {Class} type
    @param {Number|String} clientId
  */
  updateRecordArray: function(array, filter, type, record) {
    var shouldBeInArray;

    if (!filter) {
      shouldBeInArray = true;
    } else {
      shouldBeInArray = filter(record);
    }

    var recordArrays = this.recordArraysForRecord(record);

    if (shouldBeInArray) {
      if (!recordArrays.has(array)) {
        array.pushRecord(record);
        recordArrays.add(array);
      }
    } else if (!shouldBeInArray) {
      recordArrays.delete(array);
      array.removeRecord(record);
    }
  },

  /**
    This method is invoked if the `filterFunction` property is
    changed on a `DS.FilteredRecordArray`.

    It essentially re-runs the filter from scratch. This same
    method is invoked when the filter is created in th first place.

    @method updateFilter
    @param array
    @param type
    @param filter
  */
  updateFilter: function(array, type, filter) {
    var typeMap = this.store.typeMapFor(type);
    var records = typeMap.records, record;

    for (var i=0, l=records.length; i<l; i++) {
      record = records[i];

      if (!get(record, 'isDeleted') && !get(record, 'isEmpty')) {
        this.updateRecordArray(array, filter, type, record);
      }
    }
  },

  /**
    Create a `DS.ManyArray` for a type and list of record references, and index
    the `ManyArray` under each reference. This allows us to efficiently remove
    records from `ManyArray`s when they are deleted.

    @method createManyArray
    @param {Class} type
    @param {Array} references
    @return {DS.ManyArray}
  */
  createManyArray: function(type, records) {
    var manyArray = ManyArray.create({
      type: type,
      content: records,
      store: this.store
    });

    forEach(records, function(record) {
      var arrays = this.recordArraysForRecord(record);
      arrays.add(manyArray);
    }, this);

    return manyArray;
  },

  /**
    Create a `DS.RecordArray` for a type and register it for updates.

    @method createRecordArray
    @param {Class} type
    @return {DS.RecordArray}
  */
  createRecordArray: function(type) {
    var array = RecordArray.create({
      type: type,
      content: Ember.A(),
      store: this.store,
      isLoaded: true
    });

    this.registerFilteredRecordArray(array, type);

    return array;
  },

  /**
    Create a `DS.FilteredRecordArray` for a type and register it for updates.

    @method createFilteredRecordArray
    @param {Class} type
    @param {Function} filter
    @param {Object} query (optional
    @return {DS.FilteredRecordArray}
  */
  createFilteredRecordArray: function(type, filter, query) {
    var array = FilteredRecordArray.create({
      query: query,
      type: type,
      content: Ember.A(),
      store: this.store,
      manager: this,
      filterFunction: filter
    });

    this.registerFilteredRecordArray(array, type, filter);

    return array;
  },

  /**
    Create a `DS.AdapterPopulatedRecordArray` for a type with given query.

    @method createAdapterPopulatedRecordArray
    @param {Class} type
    @param {Object} query
    @return {DS.AdapterPopulatedRecordArray}
  */
  createAdapterPopulatedRecordArray: function(type, query) {
    var array = AdapterPopulatedRecordArray.create({
      type: type,
      query: query,
      content: Ember.A(),
      store: this.store,
      manager: this
    });

    this._adapterPopulatedRecordArrays.push(array);

    return array;
  },

  /**
    Register a RecordArray for a given type to be backed by
    a filter function. This will cause the array to update
    automatically when records of that type change attribute
    values or states.

    @method registerFilteredRecordArray
    @param {DS.RecordArray} array
    @param {Class} type
    @param {Function} filter
  */
  registerFilteredRecordArray: function(array, type, filter) {
    var recordArrays = this.filteredRecordArrays.get(type);
    recordArrays.push(array);

    this.updateFilter(array, type, filter);
  },

  /**
    Unregister a FilteredRecordArray.
    So manager will not update this array.

    @method unregisterFilteredRecordArray
    @param {DS.RecordArray} array
  */
  unregisterFilteredRecordArray: function(array) {
    var recordArrays = this.filteredRecordArrays.get(array.type);
    var index = recordArrays.indexOf(array);
    recordArrays.splice(index, 1);
  },

  // Internally, we maintain a map of all unloaded IDs requested by
  // a ManyArray. As the adapter loads data into the store, the
  // store notifies any interested ManyArrays. When the ManyArray's
  // total number of loading records drops to zero, it becomes
  // `isLoaded` and fires a `didLoad` event.
  registerWaitingRecordArray: function(record, array) {
    var loadingRecordArrays = record._loadingRecordArrays || [];
    loadingRecordArrays.push(array);
    record._loadingRecordArrays = loadingRecordArrays;
  },

  willDestroy: function(){
    this._super();

    forEach(flatten(values(this.filteredRecordArrays.values)), destroy);
    forEach(this._adapterPopulatedRecordArrays, destroy);
  }
});

function values(obj) {
  var result = [];
  var keys = Ember.keys(obj);

  for (var i = 0; i < keys.length; i++) {
    result.push(obj[keys[i]]);
  }

  return result;
}

function destroy(entry) {
  entry.destroy();
}

function flatten(list) {
  var length = list.length;
  var result = Ember.A();

  for (var i = 0; i < length; i++) {
    result = result.concat(list[i]);
  }

  return result;
}
<commit_msg>use indexOf polyfill<commit_after>/**
  @module ember-data
*/

import {
  RecordArray,
  FilteredRecordArray,
  AdapterPopulatedRecordArray,
  ManyArray
} from "ember-data/system/record_arrays";
import {
  MapWithDefault,
  OrderedSet
} from "ember-data/system/map";
var get = Ember.get;
var forEach = Ember.EnumerableUtils.forEach;
var indexOf = Ember.EnumerableUtils.indexOf;

/**
  @class RecordArrayManager
  @namespace DS
  @private
  @extends Ember.Object
*/
export default Ember.Object.extend({
  init: function() {
    this.filteredRecordArrays = MapWithDefault.create({
      defaultValue: function() { return []; }
    });

    this.changedRecords = [];
    this._adapterPopulatedRecordArrays = [];
  },

  recordDidChange: function(record) {
    if (this.changedRecords.push(record) !== 1) { return; }

    Ember.run.schedule('actions', this, this.updateRecordArrays);
  },

  recordArraysForRecord: function(record) {
    record._recordArrays = record._recordArrays || OrderedSet.create();
    return record._recordArrays;
  },

  /**
    This method is invoked whenever data is loaded into the store by the
    adapter or updated by the adapter, or when a record has changed.

    It updates all record arrays that a record belongs to.

    To avoid thrashing, it only runs at most once per run loop.

    @method updateRecordArrays
    @param {Class} type
    @param {Number|String} clientId
  */
  updateRecordArrays: function() {
    forEach(this.changedRecords, function(record) {
      if (get(record, 'isDeleted')) {
        this._recordWasDeleted(record);
      } else {
        this._recordWasChanged(record);
      }
    }, this);

    this.changedRecords.length = 0;
  },

  _recordWasDeleted: function (record) {
    var recordArrays = record._recordArrays;

    if (!recordArrays) { return; }

    recordArrays.forEach(function(array){
      array.removeRecord(record);
    });
  },

  _recordWasChanged: function (record) {
    var type = record.constructor;
    var recordArrays = this.filteredRecordArrays.get(type);
    var filter;

    forEach(recordArrays, function(array) {
      filter = get(array, 'filterFunction');
      this.updateRecordArray(array, filter, type, record);
    }, this);

    // loop through all manyArrays containing an unloaded copy of this
    // clientId and notify them that the record was loaded.
    var manyArrays = record._loadingRecordArrays;

    if (manyArrays) {
      for (var i=0, l=manyArrays.length; i<l; i++) {
        manyArrays[i].loadedRecord();
      }

      record._loadingRecordArrays = [];
    }
  },

  /**
    Update an individual filter.

    @method updateRecordArray
    @param {DS.FilteredRecordArray} array
    @param {Function} filter
    @param {Class} type
    @param {Number|String} clientId
  */
  updateRecordArray: function(array, filter, type, record) {
    var shouldBeInArray;

    if (!filter) {
      shouldBeInArray = true;
    } else {
      shouldBeInArray = filter(record);
    }

    var recordArrays = this.recordArraysForRecord(record);

    if (shouldBeInArray) {
      if (!recordArrays.has(array)) {
        array.pushRecord(record);
        recordArrays.add(array);
      }
    } else if (!shouldBeInArray) {
      recordArrays.delete(array);
      array.removeRecord(record);
    }
  },

  /**
    This method is invoked if the `filterFunction` property is
    changed on a `DS.FilteredRecordArray`.

    It essentially re-runs the filter from scratch. This same
    method is invoked when the filter is created in th first place.

    @method updateFilter
    @param array
    @param type
    @param filter
  */
  updateFilter: function(array, type, filter) {
    var typeMap = this.store.typeMapFor(type);
    var records = typeMap.records, record;

    for (var i=0, l=records.length; i<l; i++) {
      record = records[i];

      if (!get(record, 'isDeleted') && !get(record, 'isEmpty')) {
        this.updateRecordArray(array, filter, type, record);
      }
    }
  },

  /**
    Create a `DS.ManyArray` for a type and list of record references, and index
    the `ManyArray` under each reference. This allows us to efficiently remove
    records from `ManyArray`s when they are deleted.

    @method createManyArray
    @param {Class} type
    @param {Array} references
    @return {DS.ManyArray}
  */
  createManyArray: function(type, records) {
    var manyArray = ManyArray.create({
      type: type,
      content: records,
      store: this.store
    });

    forEach(records, function(record) {
      var arrays = this.recordArraysForRecord(record);
      arrays.add(manyArray);
    }, this);

    return manyArray;
  },

  /**
    Create a `DS.RecordArray` for a type and register it for updates.

    @method createRecordArray
    @param {Class} type
    @return {DS.RecordArray}
  */
  createRecordArray: function(type) {
    var array = RecordArray.create({
      type: type,
      content: Ember.A(),
      store: this.store,
      isLoaded: true
    });

    this.registerFilteredRecordArray(array, type);

    return array;
  },

  /**
    Create a `DS.FilteredRecordArray` for a type and register it for updates.

    @method createFilteredRecordArray
    @param {Class} type
    @param {Function} filter
    @param {Object} query (optional
    @return {DS.FilteredRecordArray}
  */
  createFilteredRecordArray: function(type, filter, query) {
    var array = FilteredRecordArray.create({
      query: query,
      type: type,
      content: Ember.A(),
      store: this.store,
      manager: this,
      filterFunction: filter
    });

    this.registerFilteredRecordArray(array, type, filter);

    return array;
  },

  /**
    Create a `DS.AdapterPopulatedRecordArray` for a type with given query.

    @method createAdapterPopulatedRecordArray
    @param {Class} type
    @param {Object} query
    @return {DS.AdapterPopulatedRecordArray}
  */
  createAdapterPopulatedRecordArray: function(type, query) {
    var array = AdapterPopulatedRecordArray.create({
      type: type,
      query: query,
      content: Ember.A(),
      store: this.store,
      manager: this
    });

    this._adapterPopulatedRecordArrays.push(array);

    return array;
  },

  /**
    Register a RecordArray for a given type to be backed by
    a filter function. This will cause the array to update
    automatically when records of that type change attribute
    values or states.

    @method registerFilteredRecordArray
    @param {DS.RecordArray} array
    @param {Class} type
    @param {Function} filter
  */
  registerFilteredRecordArray: function(array, type, filter) {
    var recordArrays = this.filteredRecordArrays.get(type);
    recordArrays.push(array);

    this.updateFilter(array, type, filter);
  },

  /**
    Unregister a FilteredRecordArray.
    So manager will not update this array.

    @method unregisterFilteredRecordArray
    @param {DS.RecordArray} array
  */
  unregisterFilteredRecordArray: function(array) {
    var recordArrays = this.filteredRecordArrays.get(array.type);
    var index = indexOf(recordArrays, array);
    recordArrays.splice(index, 1);
  },

  // Internally, we maintain a map of all unloaded IDs requested by
  // a ManyArray. As the adapter loads data into the store, the
  // store notifies any interested ManyArrays. When the ManyArray's
  // total number of loading records drops to zero, it becomes
  // `isLoaded` and fires a `didLoad` event.
  registerWaitingRecordArray: function(record, array) {
    var loadingRecordArrays = record._loadingRecordArrays || [];
    loadingRecordArrays.push(array);
    record._loadingRecordArrays = loadingRecordArrays;
  },

  willDestroy: function(){
    this._super();

    forEach(flatten(values(this.filteredRecordArrays.values)), destroy);
    forEach(this._adapterPopulatedRecordArrays, destroy);
  }
});

function values(obj) {
  var result = [];
  var keys = Ember.keys(obj);

  for (var i = 0; i < keys.length; i++) {
    result.push(obj[keys[i]]);
  }

  return result;
}

function destroy(entry) {
  entry.destroy();
}

function flatten(list) {
  var length = list.length;
  var result = Ember.A();

  for (var i = 0; i < length; i++) {
    result = result.concat(list[i]);
  }

  return result;
}
<|endoftext|>


<commit_before>import React from 'react'
import {FormattedValue} from 'tocco-ui'

const customTypeMap = {
  'document': 'document-compact',
  'binary': 'document-compact'
}

const getOptions = (type, intl) => {
  switch (type) {
    case 'document-compact':
      return {
        downloadTitle: intl.formatMessage({id: 'client.component.upload.downloadTitle'})
      }
  }

  return null
}

const formattedValueCustomTypeMapper = type => customTypeMap[type] ? customTypeMap[type] : type

export const MultiSeparator = () => ', '

export default (fieldDefinition, entity, intl) => {
  const {id, path} = fieldDefinition
  const value = entity[path]
  const contents = Array.isArray(value) ? value : [value]

  return contents.length > 0
    ?
      {
        contents
          .map((content, idx) => {
            const {type, value} = content
            const mappedType = formattedValueCustomTypeMapper(type)
            const options = getOptions(mappedType, intl)
            return <FormattedValue key={idx} type={mappedType} value={value} {...(options ? {options} : {})}/>
          })
          .reduce((prev, curr, idx) => [prev, <MultiSeparator key={`ms-${idx}`}/>, curr])
      }

    : null
}
<commit_msg>fix(entity-list): use path with id fallback in formattedValueFactory<commit_after>import React from 'react'
import {FormattedValue} from 'tocco-ui'

const customTypeMap = {
  'document': 'document-compact',
  'binary': 'document-compact'
}

const getOptions = (type, intl) => {
  switch (type) {
    case 'document-compact':
      return {
        downloadTitle: intl.formatMessage({id: 'client.component.upload.downloadTitle'})
      }
  }

  return null
}

const formattedValueCustomTypeMapper = type => customTypeMap[type] ? customTypeMap[type] : type

export const MultiSeparator = () => ', '

export default (fieldDefinition, entity, intl) => {
  const {id, path} = fieldDefinition
  const value = entity[path || id]
  const contents = Array.isArray(value) ? value : [value]

  return contents.length > 0
    ?
      {
        contents
          .map((content, idx) => {
            const {type, value} = content
            const mappedType = formattedValueCustomTypeMapper(type)
            const options = getOptions(mappedType, intl)
            return <FormattedValue key={idx} type={mappedType} value={value} {...(options ? {options} : {})}/>
          })
          .reduce((prev, curr, idx) => [prev, <MultiSeparator key={`ms-${idx}`}/>, curr])
      }

    : null
}
<|endoftext|>


<commit_before>const test = require('ava')
const Runtime = require('./runtime')

test('can pass arguments', async t => {
  const r = new Runtime()
  r.load(`${__dirname}/../fixtures/good-plugins/args`)
  const context = await r.run('args hello steve kellock', { caps: false })

  t.is('steve kellock', context.parameters.string)
  t.is('steve', context.parameters.first)
  t.is('kellock', context.parameters.second)
  t.is('hello', context.parameters.command)
  t.is('args', context.parameters.plugin)
  t.deepEqual(['steve', 'kellock'], context.parameters.array)
  t.is('steve kellock', context.parameters.string)
  t.deepEqual({ caps: false }, context.parameters.options)
})

test('can pass arguments, even with nested alias', async t => {
  const r = new Runtime()
  r.load(`${__dirname}/../fixtures/good-plugins/nested`)
  const context = await r.run('nested t f jamon holmgren', { chocolate: true })

  t.is('jamon holmgren', context.parameters.string)
  t.is('jamon', context.parameters.first)
  t.is('holmgren', context.parameters.second)
  t.is('foo', context.parameters.command)
  t.is('nested', context.parameters.plugin)
  t.deepEqual(['thing', 'foo'], context.command.commandPath)
  t.deepEqual(['jamon', 'holmgren'], context.parameters.array)
  t.is('jamon holmgren', context.parameters.string)
  t.deepEqual({ chocolate: true }, context.parameters.options)
})
<commit_msg>Flips assert arg order<commit_after>const test = require('ava')
const Runtime = require('./runtime')

test('can pass arguments', async t => {
  const r = new Runtime()
  r.load(`${__dirname}/../fixtures/good-plugins/args`)
  const { command, parameters } = await r.run('args hello steve kellock', { caps: false })

  t.is(parameters.string, 'steve kellock')
  t.is(parameters.first, 'steve')
  t.is(parameters.second, 'kellock')
  t.is(parameters.command, 'hello')
  t.is(parameters.plugin, 'args')
  t.is(parameters.string, 'steve kellock')
  t.deepEqual(parameters.array, ['steve', 'kellock'])
  t.deepEqual(parameters.options, { caps: false })
  t.deepEqual(command.commandPath, ['hello'])
})

test('can pass arguments, even with nested alias', async t => {
  const r = new Runtime()
  r.load(`${__dirname}/../fixtures/good-plugins/nested`)
  const { command, parameters } = await r.run('nested t f jamon holmgren', { chocolate: true })

  t.is(parameters.string, 'jamon holmgren')
  t.is(parameters.first, 'jamon')
  t.is(parameters.second, 'holmgren')
  t.is(parameters.command, 'foo')
  t.is(parameters.plugin, 'nested')
  t.is(parameters.string, 'jamon holmgren')
  t.deepEqual(parameters.array, ['jamon', 'holmgren'])
  t.deepEqual(parameters.options, { chocolate: true })
  t.deepEqual(command.commandPath, ['thing', 'foo'])
})
<|endoftext|>


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

/**
 * @fileoverview Tests whether the page is using Date.now().
 * @author Eric Bidelman
 */

/* global window, __returnResults */

'use strict';

const Gatherer = require('../gatherer');

function patchDateNow() {
  window.__stackTraceErrors = [];
  // Override Date.now() so we know when if it's called.
  const orig = Date.now;
  Date.now = function() {
    try {
      throw Error('__called Date.now()__');
    } catch (e) {
      if (e.stack) {
        const split = e.stack.split('\n');
        const m = split[split.length - 1].match(/(https?:\/\/.*):(\d+):(\d+)/);
        if (m) {
          window.__stackTraceErrors.push({url: m[1], line: m[2], col: m[3]});
        }
      }
    }

    return orig();
  };
}

function collectDateNowUsage() {
  __returnResults(window.__stackTraceErrors);
}

class DateNowUse extends Gatherer {

  beforePass(options) {
    return options.driver.evaluateScriptOnLoad(`(${patchDateNow.toString()}())`);
  }

  afterPass(options) {
    return options.driver.evaluateAsync(`(${collectDateNowUsage.toString()}())`)
        .then(errors => {
          this.artifact.errors = errors;
        }, _ => {
          this.artifact = -1;
          return;
        });
  }
}

module.exports = DateNowUse;
<commit_msg>Remove @author<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Tests whether the page is using Date.now().
 */

/* global window, __returnResults */

'use strict';

const Gatherer = require('../gatherer');

function patchDateNow() {
  window.__stackTraceErrors = [];
  // Override Date.now() so we know when if it's called.
  const orig = Date.now;
  Date.now = function() {
    try {
      throw Error('__called Date.now()__');
    } catch (e) {
      if (e.stack) {
        const split = e.stack.split('\n');
        const m = split[split.length - 1].match(/(https?:\/\/.*):(\d+):(\d+)/);
        if (m) {
          window.__stackTraceErrors.push({url: m[1], line: m[2], col: m[3]});
        }
      }
    }

    return orig();
  };
}

function collectDateNowUsage() {
  __returnResults(window.__stackTraceErrors);
}

class DateNowUse extends Gatherer {

  beforePass(options) {
    return options.driver.evaluateScriptOnLoad(`(${patchDateNow.toString()}())`);
  }

  afterPass(options) {
    return options.driver.evaluateAsync(`(${collectDateNowUsage.toString()}())`)
        .then(errors => {
          this.artifact.errors = errors;
        }, _ => {
          this.artifact = -1;
          return;
        });
  }
}

module.exports = DateNowUse;
<|endoftext|>


<commit_before>var gm      = require ('gm').subClass({ imageMagick: true }),
    express = require('express'),
    Asset   = require('./model/asset');

/**
 * Returns express middleware which retruns assets with stored mime type
 *
 */
module.exports = function (bauhausConfig) {
    'use strict';

    var app = express();

    app.param('id', function (req, res, next, id) {

        if (typeof id === 'string' && id.length === 24) {
            return next();
        } else {
            return next(new Error('Invalid Asset id'));
        }
    });

    /*
     *	Route to view the assets
     *
     */
    app.get('/:id', function (req, res, next) {
        var routeParams = req.route.params,
            id          = routeParams.id,
            query       = req.query; //contains the urls query parameters

        Asset.findOne ({_id : id},'name metadata data',function (err,assetDocument) {
            var options;

            if (err) { return next(err); }  // Mongo error
            if (!assetDocument) { return next(); } // No asset found
            if (assetDocument && !assetDocument.data) { return next(); } // Asset has no content
            if (assetDocument && assetDocument.metadata && !assetDocument.metadata['content-type']) { return next(); } // Asset has no metadata

            options = {};

            if (typeof query.transform !==  'undefined') {
                options.transform = {
                    width : query.width,
                    height: query.height,
                    cropCoords: query.cropCoords,	//cropping coords left - top :  right - bottom  e.g: [[20,20],[30,30]] cuts 20px from the left and top, and 30 px from right and bottom
                    scale_mode: query.scale_mode?query.scale_mode:'fit',	//gm options for resizing, "!" forces a resize, ignoring the original aspect ratio
                    transform: query.transform	//optional transformation order ['resize','crop']
                };
            }
            sendAsset (res, assetDocument, options);
        });

    });

    /*
     *  Sends the buffer of the asset to the client.
     *  Accepts options to perform transformations on the asset.
     */
    function sendAsset (res,assetDocument,options) {
        var data,
            id;

        if (assetDocument.metadata && assetDocument.metadata['content-type']) {
            res.setHeader('Content-Type', assetDocument.metadata['content-type']);
        } else {
            res.setHeader('Content-Type', 'text/plain');
        }

        data = assetDocument.data;
          id = assetDocument.id;

        /*
         *	Performs transformations on an image
         *	TODO: Think about a more generic way to perform other transformations.
         *	TODO: Implement filters
         */
        if (options && options.transform)  {
            var aspectRatio       = assetDocument.metadata.aspectRatio.value,
                width             = parseInt (options.transform.width,10) || assetDocument.metadata.width,  //Use the original values, since the transformation uses the lower value, aspect ratio gets corrected
                height            = parseInt (options.transform.height,10) || assetDocument.metadata.height,
                scale_mode        = options.transform.scale_mode==='fit'?'':'!', //gm accepts an third parameter "options" which forces a resize when set to '!'
                transformData     = {},  // contains the data, applied to their respectice transform function from gm
                transformOrderDef = ["resize","crop"], //The default transformorder
                transformOrder    = options.transform.transform, //Weakly match length to filter all falsy values, if no transform order was specified, use the default one.
                cropCoords        = options.transform.cropCoords,
                cleanedCoords  = [], // [width,height,x,y]
                cropSize,
                parsedCoords, //JSON parsed cropCoords
                gmInstance,
                tmpCond, //used to temporarily assign test conditions
                query,
                max;

            //Parse the transformorder
            if (transformOrder) {
                try {
                    transformOrder = JSON.parse (transformOrder); //Should be assigned to its own variable
                } catch (e) {
                    transformOrder = transformOrderDef;
                }
            } else {
                transformOrder = transformOrderDef;
            }

            /*
             *  preserve the right values for width and height, to match the aspect ratio if scale mode is fit
             *	We need to normalize the data to e.g. get the same cached version for w: 50 h: 7889 and w:50 h: 123
             */

            if (scale_mode !== '!') { //'' == false.
                max = Math.max (width,height);
                if (width === max) {
                    width  = Math.round (height * aspectRatio);	//Round it if the aspect ratio * height is not an int
                } else if (height === max) {
                    height = Math.round (width / aspectRatio);
                }
            }

            //If the aspect ratio matches the original, scale_mode:fill should be corrected, to make sure the cached assets are the same
            if ((width / height).toPrecision (5) === aspectRatio.toPrecision (5)) {
                scale_mode = '';
            }

            // reformat the cropcoords vlaue to be able to directly apply it on the crop function of gm
            if (typeof cropCoords !== 'undefined') {
                try {
                    parsedCoords = JSON.parse (cropCoords);

                    //The sum of the cropcoords, only a value > 0 makes sense...
                    tmpCond = parsedCoords.reduce (function sum (a,b) {
                        return a + (Array.isArray (b)?b.reduce (sum,0):b);
                    },0);

                    if (transformOrder.indexOf ("crop") > transformOrder.indexOf ("resize")) {
                        cropSize = [width,height];   //If the cropping should happen after the resizing, we should use the resized images width and height for reference
                    } else {
                        cropSize = [assetDocument.metadata.width,assetDocument.metadata.height]; //otherwise we use the original image size as reference for the cropping
                    }

                    //TODO: Handle negative new width and rheight values
                    if (tmpCond > 0) {
                        cleanedCoords [0] = cropSize [0] - parsedCoords [1][0];	//new width
                        cleanedCoords [1] = cropSize [1] - parsedCoords [1][1];	//new height
                        cleanedCoords [2] = parsedCoords [0][0];	//x
                        cleanedCoords [3] = parsedCoords [0][1];	//y
                    }

                    tmpCond = null;
                } catch (e) {
                    //don't crop the image if false values have been passed
                }
            }

            //If the recaalculated width and height either match the original size or are larger, use the original image.
            if (width >= assetDocument.metadata.width && height >= assetDocument.metadata.height && cleanedCoords.length === 0) {
                res.writeHead(200);
                return  res.end(data);
            }

            query = {
                parentId: id,
                'transforms.width':width,
                'transforms.height':height,
                 'transforms.scale_mode':scale_mode,
                 'transforms.cropCoords': cleanedCoords,
                 'transforms.transformOrder': transformOrder
             };	//The query to find the cached version, if existent

            Asset.findOne (query,'parentId transforms data', function (err, cachedAssetDocument) {
                if (!cachedAssetDocument) {	//If no transformed asset exists with the given parameters
                    cachedAssetDocument = new Asset ();	//we create a new one, and save it in the mongodb

                    //Define the data that should be passed to the different transformation methods
                    transformData.resize = [width, height, scale_mode];
                    transformData.crop = cleanedCoords;

                    gmInstance = gm (data);

                    /*	Iterate over the transformations that shopuld be performed, in their (optionally) defined order.
                     *	check if we have a defined dataset that can be applied to the transformation
                     *	and if the data exists
                     */
                    for (var i = 0,transformation,currentData; i < transformOrder.length; i++) {
                        transformation = transformOrder [i];
                        currentData = transformData [transformation];
                        if (currentData && currentData.length > 0) {
                            gmInstance [transformation].apply (gmInstance, currentData);
                        }
                    }

                    //When the transformations were applied, save the resulting image and return its buffer
                    gmInstance.toBuffer (function (err,buffer) {
                        if (err) { return next(err); }

                        cachedAssetDocument.data       = buffer;
                        cachedAssetDocument.parentId   = id;
                        cachedAssetDocument.transforms = {
                            width: width,
                            height: height,
                            cropCoords: cleanedCoords,
                            transformOrder: transformOrder,
                            scale_mode: scale_mode
                        };

                        cachedAssetDocument.save ( function (err) {
                            if (err) { return next(err); }
                            return res.send (buffer);
                        });
                    });
                } else {  //Otherwise send back the cached version
                    return res.send (cachedAssetDocument.data);
                }
            });
        } else {
            res.writeHead(200);
            return res.end(data);
        }
    }

    return app;
};
<commit_msg>Replace throw error  by next(error)<commit_after>var gm      = require ('gm').subClass({ imageMagick: true }),
    express = require('express'),
    Asset   = require('./model/asset');

/**
 * Returns express middleware which retruns assets with stored mime type
 *
 */
module.exports = function (bauhausConfig) {
    'use strict';

    var app = express();

    app.param('id', function (req, res, next, id) {
        console.log('id', id);
        if (typeof id === 'string' && id.length === 24) {
            next();
        } else {
            next(new Error('Invalid Asset id'));
        }
    });

    /*
     *	Route to view the assets
     *
     */
    app.get('/:id', function (req, res) {
        var routeParams = req.route.params,
            id          = routeParams.id,
            query       = req.query; //contains the urls query parameters

        Asset.findOne ({_id : id},'name metadata data',function (err,assetDocument) {
            var options;

            if (err) { return next(err); }  // Mongo error
            if (!assetDocument) { return next(); } // No asset found
            if (assetDocument && !assetDocument.data) { return next(); } // Asset has no content
            if (assetDocument && assetDocument.metadata && !assetDocument.metadata['content-type']) { return next(); } // Asset has no metadata

            options = {};

            if (typeof query.transform !==  'undefined') {
                options.transform = {
                    width : query.width,
                    height: query.height,
                    cropCoords: query.cropCoords,	//cropping coords left - top :  right - bottom  e.g: [[20,20],[30,30]] cuts 20px from the left and top, and 30 px from right and bottom
                    scale_mode: query.scale_mode?query.scale_mode:'fit',	//gm options for resizing, "!" forces a resize, ignoring the original aspect ratio
                    transform: query.transform	//optional transformation order ['resize','crop']
                };
            }
            sendAsset (res, assetDocument, options);
        });

    });

    /*
     *  Sends the buffer of the asset to the client.
     *  Accepts options to perform transformations on the asset.
     */
    function sendAsset (res,assetDocument,options) {
        var data,
            id;

        if (assetDocument.metadata && assetDocument.metadata['content-type']) {
            res.setHeader('Content-Type', assetDocument.metadata['content-type']);
        } else {
            res.setHeader('Content-Type', 'text/plain');
        }

        data = assetDocument.data;
          id = assetDocument.id;

        /*
         *	Performs transformations on an image
         *	TODO: Think about a more generic way to perform other transformations.
         *	TODO: Implement filters
         */
        if (options && options.transform)  {
            var aspectRatio       = assetDocument.metadata.aspectRatio.value,
                width             = parseInt (options.transform.width,10) || assetDocument.metadata.width,  //Use the original values, since the transformation uses the lower value, aspect ratio gets corrected
                height            = parseInt (options.transform.height,10) || assetDocument.metadata.height,
                scale_mode        = options.transform.scale_mode==='fit'?'':'!', //gm accepts an third parameter "options" which forces a resize when set to '!'
                transformData     = {},  // contains the data, applied to their respectice transform function from gm
                transformOrderDef = ["resize","crop"], //The default transformorder
                transformOrder    = options.transform.transform, //Weakly match length to filter all falsy values, if no transform order was specified, use the default one.
                cropCoords        = options.transform.cropCoords,
                cleanedCoords  = [], // [width,height,x,y]
                cropSize,
                parsedCoords, //JSON parsed cropCoords
                gmInstance,
                tmpCond, //used to temporarily assign test conditions
                query,
                max;

            //Parse the transformorder
            if (transformOrder) {
                try {
                    transformOrder = JSON.parse (transformOrder); //Should be assigned to its own variable
                } catch (e) {
                    transformOrder = transformOrderDef;
                }
            } else {
                transformOrder = transformOrderDef;
            }

            /*
             *  preserve the right values for width and height, to match the aspect ratio if scale mode is fit
             *	We need to normalize the data to e.g. get the same cached version for w: 50 h: 7889 and w:50 h: 123
             */

            if (scale_mode !== '!') { //'' == false.
                max = Math.max (width,height);
                if (width === max) {
                    width  = Math.round (height * aspectRatio);	//Round it if the aspect ratio * height is not an int
                } else if (height === max) {
                    height = Math.round (width / aspectRatio);
                }
            }

            //If the aspect ratio matches the original, scale_mode:fill should be corrected, to make sure the cached assets are the same
            if ((width / height).toPrecision (5) === aspectRatio.toPrecision (5)) {
                scale_mode = '';
            }

            // reformat the cropcoords vlaue to be able to directly apply it on the crop function of gm
            if (typeof cropCoords !== 'undefined') {
                try {
                    parsedCoords = JSON.parse (cropCoords);

                    //The sum of the cropcoords, only a value > 0 makes sense...
                    tmpCond = parsedCoords.reduce (function sum (a,b) {
                        return a + (Array.isArray (b)?b.reduce (sum,0):b);
                    },0);

                    if (transformOrder.indexOf ("crop") > transformOrder.indexOf ("resize")) {
                        cropSize = [width,height];   //If the cropping should happen after the resizing, we should use the resized images width and height for reference
                    } else {
                        cropSize = [assetDocument.metadata.width,assetDocument.metadata.height]; //otherwise we use the original image size as reference for the cropping
                    }

                    //TODO: Handle negative new width and rheight values
                    if (tmpCond > 0) {
                        cleanedCoords [0] = cropSize [0] - parsedCoords [1][0];	//new width
                        cleanedCoords [1] = cropSize [1] - parsedCoords [1][1];	//new height
                        cleanedCoords [2] = parsedCoords [0][0];	//x
                        cleanedCoords [3] = parsedCoords [0][1];	//y
                    }

                    tmpCond = null;
                } catch (e) {
                    //don't crop the image if false values have been passed
                }
            }

            //If the recaalculated width and height either match the original size or are larger, use the original image.
            if (width >= assetDocument.metadata.width && height >= assetDocument.metadata.height && cleanedCoords.length === 0) {
                res.writeHead(200);
                return  res.end(data);
            }

            query = {
                parentId: id,
                'transforms.width':width,
                'transforms.height':height,
                 'transforms.scale_mode':scale_mode,
                 'transforms.cropCoords': cleanedCoords,
                 'transforms.transformOrder': transformOrder
             };	//The query to find the cached version, if existent

            Asset.findOne (query,'parentId transforms data', function (err, cachedAssetDocument) {
                if (!cachedAssetDocument) {	//If no transformed asset exists with the given parameters
                    cachedAssetDocument = new Asset ();	//we create a new one, and save it in the mongodb

                    //Define the data that should be passed to the different transformation methods
                    transformData.resize = [width, height, scale_mode];
                    transformData.crop = cleanedCoords;

                    gmInstance = gm (data);

                    /*	Iterate over the transformations that shopuld be performed, in their (optionally) defined order.
                     *	check if we have a defined dataset that can be applied to the transformation
                     *	and if the data exists
                     */
                    for (var i = 0,transformation,currentData; i < transformOrder.length; i++) {
                        transformation = transformOrder [i];
                        currentData = transformData [transformation];
                        if (currentData && currentData.length > 0) {
                            gmInstance [transformation].apply (gmInstance, currentData);
                        }
                    }

                    //When the transformations were applied, save the resulting image and return its buffer
                    gmInstance.toBuffer (function (err,buffer) {
                        if (err) { next(err); }

                        cachedAssetDocument.data       = buffer;
                        cachedAssetDocument.parentId   = id;
                        cachedAssetDocument.transforms = {
                            width: width,
                            height: height,
                            cropCoords: cleanedCoords,
                            transformOrder: transformOrder,
                            scale_mode: scale_mode
                        };

                        cachedAssetDocument.save ( function (err) {
                            if (err) { next(err); }
                            res.send (buffer);
                        });
                    });
                } else {  //Otherwise send back the cached version
                    res.send (cachedAssetDocument.data);
                }
            });
        } else {
            res.writeHead(200);
            return  res.end(data);
        }
    }

    return app;
};
<|endoftext|>