<commit_before>define([
  'intern!object',
  'intern/chai!expect',
  'require',
  // https://theintern.github.io/leadfoot/keys.html
  'intern/dojo/node!leadfoot/keys',
  // https://theintern.github.io/leadfoot/pollUntil.html
  'intern/dojo/node!leadfoot/helpers/pollUntil',
], function(registerSuite, expect, require, keys, pollUntil) {

  registerSuite(function() {
    var timeout = 120000;

    return {
      name: 'maintain/tab-focus',

      before: function() {
        return this.remote
          .get(require.toUrl('test/pages/observe.interaction-type.test.html'))
          .setPageLoadTimeout(timeout)
          .setFindTimeout(timeout)
          .setExecuteAsyncTimeout(timeout)
          // wait until we're really initialized
          .then(pollUntil('return window.platform'), timeout);
      },

      pointer: function() {
        this.timeout = timeout;
        return this.remote
          .execute('return window.platform.name')
          .then(function(platformName) {
            if (platformName === 'Firefox') {
              this.skip('WebDriver pressMouseButton not supported in Firefox');
            }
          }.bind(this))

          .pressMouseButton(0, 0)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(true, 'pointer state down');
          })

          .releaseMouseButton(0)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(false, 'pointer state up');
          });
      },
      touch: function() {
        var test = this;
        this.timeout = timeout;
        return this.remote
          .then(function() {
            if (!this.session.capabilities.touchEnabled) {
              test.skip('WebDriver touch not supported');
            }
          })

          .pressFinger(0, 0)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(true, 'pointer state down');
          })

          .releaseFinger(0, 0)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(false, 'pointer state up');
          });
      },
      'multi touch': function() {
        var test = this;
        this.timeout = timeout;
        return this.remote
          .then(function() {
            if (!this.session.capabilities.touchEnabled) {
              test.skip('WebDriver touch not supported');
            }
          })

          .pressFinger(0, 0)
          .pressFinger(50, 50)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(true, 'pointer state down');
          })

          .releaseFinger(0, 0)
          .releaseFinger(50, 50)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(false, 'pointer state up');
          });
      },
    };
  });
});
<commit_msg>test(observe/interaction-type): fixing test name<commit_after>define([
  'intern!object',
  'intern/chai!expect',
  'require',
  // https://theintern.github.io/leadfoot/keys.html
  'intern/dojo/node!leadfoot/keys',
  // https://theintern.github.io/leadfoot/pollUntil.html
  'intern/dojo/node!leadfoot/helpers/pollUntil',
], function(registerSuite, expect, require, keys, pollUntil) {

  registerSuite(function() {
    var timeout = 120000;

    return {
      name: 'observe/interaction-type',

      before: function() {
        return this.remote
          .get(require.toUrl('test/pages/observe.interaction-type.test.html'))
          .setPageLoadTimeout(timeout)
          .setFindTimeout(timeout)
          .setExecuteAsyncTimeout(timeout)
          // wait until we're really initialized
          .then(pollUntil('return window.platform'), timeout);
      },

      pointer: function() {
        this.timeout = timeout;
        return this.remote
          .execute('return window.platform.name')
          .then(function(platformName) {
            if (platformName === 'Firefox') {
              this.skip('WebDriver pressMouseButton not supported in Firefox');
            }
          }.bind(this))

          .pressMouseButton(0, 0)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(true, 'pointer state down');
          })

          .releaseMouseButton(0)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(false, 'pointer state up');
          });
      },
      touch: function() {
        var test = this;
        this.timeout = timeout;
        return this.remote
          .then(function() {
            if (!this.session.capabilities.touchEnabled) {
              test.skip('WebDriver touch not supported');
            }
          })

          .pressFinger(0, 0)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(true, 'pointer state down');
          })

          .releaseFinger(0, 0)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(false, 'pointer state up');
          });
      },
      'multi touch': function() {
        var test = this;
        this.timeout = timeout;
        return this.remote
          .then(function() {
            if (!this.session.capabilities.touchEnabled) {
              test.skip('WebDriver touch not supported');
            }
          })

          .pressFinger(0, 0)
          .pressFinger(50, 50)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(true, 'pointer state down');
          })

          .releaseFinger(0, 0)
          .releaseFinger(50, 50)
          .execute('return window.handle.get()')
          .then(function(state) {
            expect(state.pointer).to.equal(false, 'pointer state up');
          });
      },
    };
  });
});
<|endoftext|>


<commit_before>angular.module('popup', [
    'popup.menutree',
    'popup.redirect',
    'popup.fl-editor',
    'fnx.kango-q'
])
.config(function($httpProvider, $compileProvider) {
    // Use x-www-form-urlencoded Content-Type
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
    $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|local|data|chrome|chrome-extension):/);

    /**
     * The workhorse; converts an object to x-www-form-urlencoded serialization.
     * @param {Object} obj
     * @return {String}
     */
    var param = function(obj) {
        var query = '',
            name, value, fullSubName, subName, subValue, innerObj, i;

        for (name in obj) {
            value = obj[name];

            if (value instanceof Array) {
                for (i = 0; i < value.length; ++i) {
                    subValue = value[i];
                    fullSubName = name + '[' + i + ']';
                    innerObj = {};
                    innerObj[fullSubName] = subValue;
                    query += param(innerObj) + '&';
                }
            } else if (value instanceof Object) {
                for (subName in value) {
                    subValue = value[subName];
                    fullSubName = name + '[' + subName + ']';
                    innerObj = {};
                    innerObj[fullSubName] = subValue;
                    query += param(innerObj) + '&';
                }
            } else if (value !== undefined && value !== null)
                query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
        }

        return query.length ? query.substr(0, query.length - 1) : query;
    };

    // Override $http service's default transformRequest
    $httpProvider.defaults.transformRequest = [function(data) {
        return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
    }];
})

.directive('ngPath', function (Redirect, RedirectMode, $timeout) {
    return {
        restrict: 'A',
        scope: {
            ngPath: '='
        },
        link: function (scope, element, attrs) {
            var url = scope.ngPath || attrs.ngPathStr;
            var mode = RedirectMode.UNDEFINED;
            var timer = null;
            element.on('click auxclick', function(event) {
                event.stopPropagation();
                if (event.button==1) {
                    mode = RedirectMode.UNDEFINED;
                    Redirect(url, RedirectMode.BACKGROUND);
                    return;
                }
                mode++;
                if (!timer) {
                    timer = $timeout(function () {
                        Redirect(url, mode);
                        mode = RedirectMode.UNDEFINED;
                        timer = null;
                    }, 300);
                }
            });
        }
    }
})

.directive('keyboardControlledList', function () {
    function sibling (dir, element) {
        var res = element[dir + 'Sibling'];
        while(res && res.nodeType != 1) {
            res = res[dir + 'Sibling'];
        }
        return res || element;
    }
    return {
        restrict: 'A',
        link: function (scope, element) {
            var active = null;
            element.on('focus', function (event) { active = this; });
            element.on('keydown', function (event) {
                // Up arrow:
                if (event.keyCode === 38) {
                    return sibling('previous', active).focus();
                }
                // Down arrow:
                if (event.keyCode === 40) {
                    return sibling('next', active).focus();
                }
                // Enter or Space:
                if (event.keyCode === 13 || event.keyCode === 32) {
                    return active.click();
                }
            });
        }
    }
})

.directive('mainMenu', function() {
    return {
        restrict: 'A',
        templateUrl: 'mainMenuTemplate',
        replace: false,
        controller: function($scope, MenuTree) {
            $scope.menu = MenuTree;

            $scope.toggleEditor = function() {
                $scope.$broadcast('toggle-editor');
            };

            $scope.$on('save-links', loadFL);

            function loadFL() {
                $scope.links = kango.storage.getItem('fast-links') || [];
            }
            loadFL();
        }
    }
})

.filter('prettyDateTime', function () {
    return function (seconds) {
        var date = new Date(seconds);
        var diff = (Date.now() - seconds) / 1000;
        var day_diff = Math.floor(diff / 86400);

        var time = date.toLocaleString('ru-RU', { hour: '2-digit', minute: '2-digit' });
        var dayOfWeek = date.toLocaleString('ru-RU', { weekday: 'short' });
        var dayMonth = date.toLocaleString('ru-RU', { day: 'numeric', month: 'short' });
        var year = date.getFullYear();

        if (day_diff === 0) {
            return time;
        }
        if (day_diff === 1) {
            return 'Вчера, ' + time;
        }
        if (day_diff < 7) {
            return dayOfWeek + ', ' + time;
        }
        return dayMonth + (year === (new Date()).getFullYear() ? '' : year) + ', ' + time;
    };
})

.filter('avatar', function () {
    return function (id, hasAvatar, size) {
        var url;
        if (hasAvatar) {
            url = 'http://i.klavogonki.ru/avatars/' + id;
            url += size === 'small' ? '.gif' : '_big.png';
        } else {
            url = 'http://klavogonki.ru/img/';
            url += size === 'small' ? 'avatar_dummy_16.png' : 'avatar_dummy.gif';
        }
        return url;
    };
})

.controller('popup:SearchUser', function ($scope, $http, $q) {
    var ctrl = this;

    ctrl.login = '';
    ctrl.id = 0;
    ctrl.loading = false;

    ctrl.search = function() {
        ctrl.loading = true;
        ctrl.id = 0;
        ctrl.actualLogin = '';
        ctrl.totalRacesCount = '';
        ctrl.friendsCount = '';
        ctrl.recordDefault = '';
        ctrl.hasAvatar = undefined;
        ctrl.blocked = false;

        function getSummary (userId) {
            return $http.get('http://klavogonki.ru/api/profile/get-summary', {
                params: { id: userId },
            });
        }

        function getIndexData (userId) {
            return $http.get('http://klavogonki.ru/api/profile/get-index-data', {
                params: { userId: userId },
            });
        }

        $http.post('http://klavogonki.ru/.fetchuser?KTS_REQUEST', {login: ctrl.login})
        .then(function (res) {
            ctrl.id = res.data.id;
            return $q.all([getSummary(ctrl.id), getIndexData(ctrl.id)]);
        })
        .then(function (res) {
            // TODO: write decorator $q.spread?
            var summary = res[0];
            var index = res[1];
            ctrl.actualLogin = summary.data.user.login;
            ctrl.hasAvatar = !!summary.data.user.avatar;
            ctrl.rank = summary.data.level;
            ctrl.blocked = summary.data.blocked;
            ctrl.achievementsCount = index.data.stats.achieves_cnt;
            ctrl.totalRacesCount = index.data.stats.total_num_races;
            ctrl.friendsCount = index.data.stats.friends_cnt;
            ctrl.recordDefault = index.data.stats.best_speed;
            ctrl.loading = false;
        })
        .catch(function (err) {
            console.error(err);
            ctrl.loading = false;
        });
    };
})

.controller('popup:Mail', function ($scope, $http) {
    var ctrl = this;
    ctrl.data = {};
    var hide = kango.storage.getItem('popup_hideReadMessages');
    hide = typeof hide === 'boolean' ? hide : true;
    $scope.hideReadMessages = hide;
    $scope.saveSettings = function (name) {
        kango.storage.setItem('popup_' + name, $scope[name]);
    };

    $http.get('http://klavogonki.ru/api/profile/get-messages-contacts?KTS_REQUEST')
    .then(function (res) {
        if (res.status == 200) {
            var messages = res.data.messages.map(function (message) {
                message.respondentLogin = res.data.users[message.respondent_id].login;
                message.hasAvatar = !!res.data.users[message.respondent_id].avatar;
                return message;
            });
            ctrl.messages = messages;
        }
    });
});

KangoAPI.onReady(function() {
    function resize () {
        KangoAPI.resizeWindow(document.documentElement.scrollWidth, document.documentElement.scrollHeight)
    }
    // Fix for the FireFox:
    resize();
    window.addEventListener('overflow', resize, false);
    angular.bootstrap(document.body, ['popup']);
});
<commit_msg>(f) Another fix for an issue with new avatars<commit_after>angular.module('popup', [
    'popup.menutree',
    'popup.redirect',
    'popup.fl-editor',
    'fnx.kango-q'
])
.config(function($httpProvider, $compileProvider) {
    // Use x-www-form-urlencoded Content-Type
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
    $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|local|data|chrome|chrome-extension):/);

    /**
     * The workhorse; converts an object to x-www-form-urlencoded serialization.
     * @param {Object} obj
     * @return {String}
     */
    var param = function(obj) {
        var query = '',
            name, value, fullSubName, subName, subValue, innerObj, i;

        for (name in obj) {
            value = obj[name];

            if (value instanceof Array) {
                for (i = 0; i < value.length; ++i) {
                    subValue = value[i];
                    fullSubName = name + '[' + i + ']';
                    innerObj = {};
                    innerObj[fullSubName] = subValue;
                    query += param(innerObj) + '&';
                }
            } else if (value instanceof Object) {
                for (subName in value) {
                    subValue = value[subName];
                    fullSubName = name + '[' + subName + ']';
                    innerObj = {};
                    innerObj[fullSubName] = subValue;
                    query += param(innerObj) + '&';
                }
            } else if (value !== undefined && value !== null)
                query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
        }

        return query.length ? query.substr(0, query.length - 1) : query;
    };

    // Override $http service's default transformRequest
    $httpProvider.defaults.transformRequest = [function(data) {
        return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
    }];
})

.directive('ngPath', function (Redirect, RedirectMode, $timeout) {
    return {
        restrict: 'A',
        scope: {
            ngPath: '='
        },
        link: function (scope, element, attrs) {
            var url = scope.ngPath || attrs.ngPathStr;
            var mode = RedirectMode.UNDEFINED;
            var timer = null;
            element.on('click auxclick', function(event) {
                event.stopPropagation();
                if (event.button==1) {
                    mode = RedirectMode.UNDEFINED;
                    Redirect(url, RedirectMode.BACKGROUND);
                    return;
                }
                mode++;
                if (!timer) {
                    timer = $timeout(function () {
                        Redirect(url, mode);
                        mode = RedirectMode.UNDEFINED;
                        timer = null;
                    }, 300);
                }
            });
        }
    }
})

.directive('keyboardControlledList', function () {
    function sibling (dir, element) {
        var res = element[dir + 'Sibling'];
        while(res && res.nodeType != 1) {
            res = res[dir + 'Sibling'];
        }
        return res || element;
    }
    return {
        restrict: 'A',
        link: function (scope, element) {
            var active = null;
            element.on('focus', function (event) { active = this; });
            element.on('keydown', function (event) {
                // Up arrow:
                if (event.keyCode === 38) {
                    return sibling('previous', active).focus();
                }
                // Down arrow:
                if (event.keyCode === 40) {
                    return sibling('next', active).focus();
                }
                // Enter or Space:
                if (event.keyCode === 13 || event.keyCode === 32) {
                    return active.click();
                }
            });
        }
    }
})

.directive('mainMenu', function() {
    return {
        restrict: 'A',
        templateUrl: 'mainMenuTemplate',
        replace: false,
        controller: function($scope, MenuTree) {
            $scope.menu = MenuTree;

            $scope.toggleEditor = function() {
                $scope.$broadcast('toggle-editor');
            };

            $scope.$on('save-links', loadFL);

            function loadFL() {
                $scope.links = kango.storage.getItem('fast-links') || [];
            }
            loadFL();
        }
    }
})

.filter('prettyDateTime', function () {
    return function (seconds) {
        var date = new Date(seconds);
        var diff = (Date.now() - seconds) / 1000;
        var day_diff = Math.floor(diff / 86400);

        var time = date.toLocaleString('ru-RU', { hour: '2-digit', minute: '2-digit' });
        var dayOfWeek = date.toLocaleString('ru-RU', { weekday: 'short' });
        var dayMonth = date.toLocaleString('ru-RU', { day: 'numeric', month: 'short' });
        var year = date.getFullYear();

        if (day_diff === 0) {
            return time;
        }
        if (day_diff === 1) {
            return 'Вчера, ' + time;
        }
        if (day_diff < 7) {
            return dayOfWeek + ', ' + time;
        }
        return dayMonth + (year === (new Date()).getFullYear() ? '' : year) + ', ' + time;
    };
})

.filter('avatar', function () {
    return function (id, hasAvatar, size) {
        var url;
        if (hasAvatar) {
            url = 'http://i.klavogonki.ru/avatars/' + id;
            url += size === 'small' ? '.png' : '_big.png';
        } else {
            url = 'http://klavogonki.ru/img/';
            url += size === 'small' ? 'avatar_dummy_16.png' : 'avatar_dummy.gif';
        }
        return url;
    };
})

.controller('popup:SearchUser', function ($scope, $http, $q) {
    var ctrl = this;

    ctrl.login = '';
    ctrl.id = 0;
    ctrl.loading = false;

    ctrl.search = function() {
        ctrl.loading = true;
        ctrl.id = 0;
        ctrl.actualLogin = '';
        ctrl.totalRacesCount = '';
        ctrl.friendsCount = '';
        ctrl.recordDefault = '';
        ctrl.hasAvatar = undefined;
        ctrl.blocked = false;

        function getSummary (userId) {
            return $http.get('http://klavogonki.ru/api/profile/get-summary', {
                params: { id: userId },
            });
        }

        function getIndexData (userId) {
            return $http.get('http://klavogonki.ru/api/profile/get-index-data', {
                params: { userId: userId },
            });
        }

        $http.post('http://klavogonki.ru/.fetchuser?KTS_REQUEST', {login: ctrl.login})
        .then(function (res) {
            ctrl.id = res.data.id;
            return $q.all([getSummary(ctrl.id), getIndexData(ctrl.id)]);
        })
        .then(function (res) {
            // TODO: write decorator $q.spread?
            var summary = res[0];
            var index = res[1];
            ctrl.actualLogin = summary.data.user.login;
            ctrl.hasAvatar = !!summary.data.user.avatar;
            ctrl.rank = summary.data.level;
            ctrl.blocked = summary.data.blocked;
            ctrl.achievementsCount = index.data.stats.achieves_cnt;
            ctrl.totalRacesCount = index.data.stats.total_num_races;
            ctrl.friendsCount = index.data.stats.friends_cnt;
            ctrl.recordDefault = index.data.stats.best_speed;
            ctrl.loading = false;
        })
        .catch(function (err) {
            console.error(err);
            ctrl.loading = false;
        });
    };
})

.controller('popup:Mail', function ($scope, $http) {
    var ctrl = this;
    ctrl.data = {};
    var hide = kango.storage.getItem('popup_hideReadMessages');
    hide = typeof hide === 'boolean' ? hide : true;
    $scope.hideReadMessages = hide;
    $scope.saveSettings = function (name) {
        kango.storage.setItem('popup_' + name, $scope[name]);
    };

    $http.get('http://klavogonki.ru/api/profile/get-messages-contacts?KTS_REQUEST')
    .then(function (res) {
        if (res.status == 200) {
            var messages = res.data.messages.map(function (message) {
                message.respondentLogin = res.data.users[message.respondent_id].login;
                message.hasAvatar = !!res.data.users[message.respondent_id].avatar;
                return message;
            });
            ctrl.messages = messages;
        }
    });
});

KangoAPI.onReady(function() {
    function resize () {
        KangoAPI.resizeWindow(document.documentElement.scrollWidth, document.documentElement.scrollHeight)
    }
    // Fix for the FireFox:
    resize();
    window.addEventListener('overflow', resize, false);
    angular.bootstrap(document.body, ['popup']);
});
<|endoftext|>


<commit_before>const { expect } = require('chai');
const app = require('../../../../src/app');
const testObjects = require('../../helpers/testObjects')(app);
const { generateRequestParamsFromUser } = require('../../helpers/services/login')(app);

const courseScopeListService = app.service('/users/:scopeId/courses');

describe('courses scopelist service', () => {
	it('is properly registered', () => {
		expect(courseScopeListService).to.not.equal(undefined);
	});

	it('fetches only active courses by default', async () => {
		const user = await testObjects.createTestUser();
		const activeCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const archivedCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({ route: { scopeId: user._id }, query: {} });
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(activeCourse._id.toString())).to.equal(true);
		expect(courseIds.includes(archivedCourse._id.toString())).to.equal(false);
	});

	it('may explicitly fetch only active courses', async () => {
		const user = await testObjects.createTestUser();
		const activeCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const archivedCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { filter: 'active' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(activeCourse._id.toString())).to.equal(true);
		expect(courseIds.includes(archivedCourse._id.toString())).to.equal(false);
	});

	it('may fetch only archived courses', async () => {
		const user = await testObjects.createTestUser();
		const activeCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const archivedCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { filter: 'archived' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(activeCourse._id.toString())).to.equal(false);
		expect(courseIds.includes(archivedCourse._id.toString())).to.equal(true);
	});

	it('may fetch all courses', async () => {
		const user = await testObjects.createTestUser();
		const activeCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const archivedCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { filter: 'all' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(2);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(activeCourse._id.toString())).to.equal(true);
		expect(courseIds.includes(archivedCourse._id.toString())).to.equal(true);
	});

	it('fetches only courses as student/teacher by default', async () => {
		const user = await testObjects.createTestUser();
		const courseAsStudent = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsTeacher = await testObjects.createTestCourse({
			teacherIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsSubstitutionTeacher = await testObjects.createTestCourse({
			substitutionIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: {},
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(2);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(courseAsStudent._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsTeacher._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsSubstitutionTeacher._id.toString())).to.equal(false);
	});

	it('may fetch only courses as student/teacher', async () => {
		const user = await testObjects.createTestUser();
		const courseAsStudent = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsTeacher = await testObjects.createTestCourse({
			teacherIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsSubstitutionTeacher = await testObjects.createTestCourse({
			substitutionIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { substitution: 'false' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(2);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(courseAsStudent._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsTeacher._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsSubstitutionTeacher._id.toString())).to.equal(false);
	});

	it('may fetch only courses as substitution teacher', async () => {
		const user = await testObjects.createTestUser();
		const courseAsStudent = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsTeacher = await testObjects.createTestCourse({
			teacherIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsSubstitutionTeacher = await testObjects.createTestCourse({
			substitutionIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { substitution: 'true' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(courseAsStudent._id.toString())).to.equal(false);
		expect(courseIds.includes(courseAsTeacher._id.toString())).to.equal(false);
		expect(courseIds.includes(courseAsSubstitutionTeacher._id.toString())).to.equal(true);
	});

	it('may fetch as both substitution teacher and student/teacher', async () => {
		const user = await testObjects.createTestUser();
		const courseAsStudent = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsTeacher = await testObjects.createTestCourse({
			teacherIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsSubstitutionTeacher = await testObjects.createTestCourse({
			substitutionIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { substitution: 'all' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(3);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(courseAsStudent._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsTeacher._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsSubstitutionTeacher._id.toString())).to.equal(true);
	});

	it('passes on pagination', async () => {
		const user = await testObjects.createTestUser();
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { $limit: 23, $skip: 18 },
		});
		expect(response).to.not.equal(undefined);
		expect(response.limit).to.equal(23);
		expect(response.skip).to.equal(18);
	});

	it('allows disabling pagination', async () => {
		const user = await testObjects.createTestUser();
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: {},
			paginate: false,
		});
		expect(response).to.not.equal(undefined);
		expect(Array.isArray(response)).to.equal(true);
	});

	it('may return count of active courses', async () => {
		const user = await testObjects.createTestUser();
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { count: 'true' },
			paginate: false,
		});
		expect(response).to.equal(1);
	});

	it('may return count of active courses', async () => {
		const user = await testObjects.createTestUser();
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { filter: 'archived', count: 'true' },
			paginate: false,
		});
		expect(response).to.equal(2);
	});

	after(async () => {
		await testObjects.cleanup();
	});
});

describe('courses scopelist service integration', () => {
	let server;

	before((done) => {
		server = app.listen(0, done);
	});

	after((done) => {
		server.close(done);
	});

	it('fails without authorization', async () => {
		try {
			const user = await testObjects.createTestUser();
			await courseScopeListService.find({
				route: { scopeId: user._id },
				query: {},
				provider: 'rest',
			});
			throw new Error('should have failed');
		} catch (err) {
			expect(err).to.not.equal(undefined);
			expect(err.name).to.equal('NotAuthenticated');
			expect(err.message).to.equal('No auth token');
		}
	});

	it('fails for different users', async () => {
		try {
			const user = await testObjects.createTestUser();
			const targetUser = await testObjects.createTestUser();
			const params = await generateRequestParamsFromUser(user);
			params.route = { scopeId: targetUser._id };
			params.query = {};
			await courseScopeListService.find(params);
			throw new Error('should have failed');
		} catch (err) {
			expect(err).to.not.equal(undefined);
			expect(err.name).to.equal('Forbidden');
			expect(err.message).to.equal('Requested and requesting userIds do not match.');
		}
	});

	it('succeeds for authenticated user', async () => {
		const user = await testObjects.createTestUser();
		const course = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const params = await generateRequestParamsFromUser(user);
		params.route = { scopeId: user._id };
		params.query = {};
		const response = await courseScopeListService.find(params);
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		expect(response.data[0]._id.toString()).to.equal(course._id.toString());
	});
});
<commit_msg>changed expect of test  <commit_after>const { expect } = require('chai');
const app = require('../../../../src/app');
const testObjects = require('../../helpers/testObjects')(app);
const { generateRequestParamsFromUser } = require('../../helpers/services/login')(app);

const courseScopeListService = app.service('/users/:scopeId/courses');

describe('courses scopelist service', () => {
	it('is properly registered', () => {
		expect(courseScopeListService).to.not.equal(undefined);
	});

	it('fetches only active courses by default', async () => {
		const user = await testObjects.createTestUser();
		const activeCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const archivedCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({ route: { scopeId: user._id }, query: {} });
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(activeCourse._id.toString())).to.equal(true);
		expect(courseIds.includes(archivedCourse._id.toString())).to.equal(false);
	});

	it('may explicitly fetch only active courses', async () => {
		const user = await testObjects.createTestUser();
		const activeCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const archivedCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { filter: 'active' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(activeCourse._id.toString())).to.equal(true);
		expect(courseIds.includes(archivedCourse._id.toString())).to.equal(false);
	});

	it('may fetch only archived courses', async () => {
		const user = await testObjects.createTestUser();
		const activeCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const archivedCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { filter: 'archived' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(activeCourse._id.toString())).to.equal(false);
		expect(courseIds.includes(archivedCourse._id.toString())).to.equal(true);
	});

	it('may fetch all courses', async () => {
		const user = await testObjects.createTestUser();
		const activeCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const archivedCourse = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { filter: 'all' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(2);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(activeCourse._id.toString())).to.equal(true);
		expect(courseIds.includes(archivedCourse._id.toString())).to.equal(true);
	});

	it('fetches only courses as student/teacher by default', async () => {
		const user = await testObjects.createTestUser();
		const courseAsStudent = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsTeacher = await testObjects.createTestCourse({
			teacherIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsSubstitutionTeacher = await testObjects.createTestCourse({
			substitutionIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: {},
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(2);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(courseAsStudent._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsTeacher._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsSubstitutionTeacher._id.toString())).to.equal(false);
	});

	it('may fetch only courses as student/teacher', async () => {
		const user = await testObjects.createTestUser();
		const courseAsStudent = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsTeacher = await testObjects.createTestCourse({
			teacherIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsSubstitutionTeacher = await testObjects.createTestCourse({
			substitutionIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { substitution: 'false' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(2);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(courseAsStudent._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsTeacher._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsSubstitutionTeacher._id.toString())).to.equal(false);
	});

	it('may fetch only courses as substitution teacher', async () => {
		const user = await testObjects.createTestUser();
		const courseAsStudent = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsTeacher = await testObjects.createTestCourse({
			teacherIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsSubstitutionTeacher = await testObjects.createTestCourse({
			substitutionIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { substitution: 'true' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(courseAsStudent._id.toString())).to.equal(false);
		expect(courseIds.includes(courseAsTeacher._id.toString())).to.equal(false);
		expect(courseIds.includes(courseAsSubstitutionTeacher._id.toString())).to.equal(true);
	});

	it('may fetch as both substitution teacher and student/teacher', async () => {
		const user = await testObjects.createTestUser();
		const courseAsStudent = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsTeacher = await testObjects.createTestCourse({
			teacherIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const courseAsSubstitutionTeacher = await testObjects.createTestCourse({
			substitutionIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { substitution: 'all' },
		});
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(3);
		expect(response.data).to.not.equal(undefined);
		const courseIds = response.data.map(course => course._id.toString());
		expect(courseIds.includes(courseAsStudent._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsTeacher._id.toString())).to.equal(true);
		expect(courseIds.includes(courseAsSubstitutionTeacher._id.toString())).to.equal(true);
	});

	it('passes on pagination', async () => {
		const user = await testObjects.createTestUser();
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { $limit: 23, $skip: 18 },
		});
		expect(response).to.not.equal(undefined);
		expect(response.limit).to.equal(23);
		expect(response.skip).to.equal(18);
	});

	it('allows disabling pagination', async () => {
		const user = await testObjects.createTestUser();
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: {},
			paginate: false,
		});
		expect(response).to.not.equal(undefined);
		expect(Array.isArray(response)).to.equal(true);
	});

	it('may return count of active courses', async () => {
		const user = await testObjects.createTestUser();
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { count: 'true' },
			paginate: false,
		});
		expect(response).to.equal({
			total: 1,
		});
	});

	it('may return count of active courses', async () => {
		const user = await testObjects.createTestUser();
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() - 600000,
		});
		const response = await courseScopeListService.find({
			route: { scopeId: user._id },
			query: { filter: 'archived', count: 'true' },
			paginate: false,
		});
		expect(response).to.equal({
			total: 2,
		});
	});

	after(async () => {
		await testObjects.cleanup();
	});
});

describe('courses scopelist service integration', () => {
	let server;

	before((done) => {
		server = app.listen(0, done);
	});

	after((done) => {
		server.close(done);
	});

	it('fails without authorization', async () => {
		try {
			const user = await testObjects.createTestUser();
			await courseScopeListService.find({
				route: { scopeId: user._id },
				query: {},
				provider: 'rest',
			});
			throw new Error('should have failed');
		} catch (err) {
			expect(err).to.not.equal(undefined);
			expect(err.name).to.equal('NotAuthenticated');
			expect(err.message).to.equal('No auth token');
		}
	});

	it('fails for different users', async () => {
		try {
			const user = await testObjects.createTestUser();
			const targetUser = await testObjects.createTestUser();
			const params = await generateRequestParamsFromUser(user);
			params.route = { scopeId: targetUser._id };
			params.query = {};
			await courseScopeListService.find(params);
			throw new Error('should have failed');
		} catch (err) {
			expect(err).to.not.equal(undefined);
			expect(err.name).to.equal('Forbidden');
			expect(err.message).to.equal('Requested and requesting userIds do not match.');
		}
	});

	it('succeeds for authenticated user', async () => {
		const user = await testObjects.createTestUser();
		const course = await testObjects.createTestCourse({
			userIds: [user._id],
			untilDate: Date.now() + 600000,
		});
		const params = await generateRequestParamsFromUser(user);
		params.route = { scopeId: user._id };
		params.query = {};
		const response = await courseScopeListService.find(params);
		expect(response).to.not.equal(undefined);
		expect(response.total).to.equal(1);
		expect(response.data).to.not.equal(undefined);
		expect(response.data[0]._id.toString()).to.equal(course._id.toString());
	});
});
<|endoftext|>


<commit_before>'use strict';

var angular = require('angular'),
  moment = require('moment');

describe('mwlCalendarHourList directive', function() {
  var MwlCalendarCtrl,
    element,
    scope,
    $rootScope,
    directiveScope,
    calendarConfig,
    showModal,
    clock,
    template =
      '<mwl-calendar-hour-list ' +
        'day-view-start="dayViewStart" ' +
        'day-view-end="dayViewEnd" ' +
        'day-view-split="dayViewSplit || 30" ' +
        'on-event-times-changed="eventDropped(calendarEvent, calendarDate, calendarNewEventStart, calendarNewEventEnd)" ' +
        'cell-modifier="cellModifier"' +
        'day-width="dayWidth"' +
        'view="{{ view }}"' +
      '></mwl-calendar-hour-list>';

  function prepareScope(vm) {
    //These variables MUST be set as a minimum for the calendar to work
    vm.dayViewStart = '06:00';
    vm.dayViewEnd = '22:59';
    vm.dayViewsplit = 30;
    vm.cellModifier = sinon.stub();
    vm.view = 'day';

    showModal = sinon.spy();

    vm.onEventClick = function(event) {
      showModal('Clicked', event);
    };

    vm.onEventTimesChanged = function(event) {
      showModal('Dropped or resized', event);
    };
  }

  beforeEach(angular.mock.module('mwl.calendar'));

  beforeEach(angular.mock.inject(function($compile, _$rootScope_, _calendarConfig_) {
    calendarConfig = _calendarConfig_;
    clock = sinon.useFakeTimers(new Date('October 20, 2015 11:10:00').getTime());
    $rootScope = _$rootScope_;
    scope = $rootScope.$new();
    prepareScope(scope);

    element = $compile(template)(scope);
    scope.$apply();
    directiveScope = element.isolateScope();
    MwlCalendarCtrl = directiveScope.vm;
  }));

  afterEach(function() {
    clock.restore();
  });

  it('should define a list of hours', function() {
    expect(MwlCalendarCtrl.hourGrid.length).to.equal(17);
  });

  it('should update the list of hours when the calendar refreshes if the locale changes', function() {
    sinon.stub(moment, 'locale').returns('another locale');
    scope.dayViewStart = '00:00';
    scope.$apply();
    scope.$broadcast('calendar.refreshView');
    expect(MwlCalendarCtrl.hourGrid.length).to.equal(23);
    moment.locale.restore();
  });

  it('should call the event times changed callback when an event is dropped', function() {
    var event = {
      startsAt: moment().toDate(),
      endsAt: moment().add(1, 'day').toDate()
    };
    var date = moment().add(2, 'days').toDate();
    MwlCalendarCtrl.onEventTimesChanged = sinon.spy();
    MwlCalendarCtrl.eventDropped(event, date);
    var calledWith = MwlCalendarCtrl.onEventTimesChanged.getCall(0).args[0];
    expect(calledWith.calendarEvent).to.equal(event);
    expect(calledWith.calendarDate.getTime()).to.equal(date.getTime());
    expect(calledWith.calendarNewEventStart.getTime()).to.equal(date.getTime());
    expect(calledWith.calendarNewEventEnd.getTime()).to.equal(moment().add(3, 'days').toDate().getTime());
  });

  it('should initialise the date range select', function() {

    var date = new Date();
    MwlCalendarCtrl.onDragSelectStart(date, 1);
    expect(MwlCalendarCtrl.dateRangeSelect).to.deep.equal({
      active: true,
      startDate: date,
      endDate: date,
      dayIndex: 1
    });

  });

  it('should update the date ranges end date', function() {
    var date = new Date();
    MwlCalendarCtrl.dateRangeSelect = {
      startDate: 'date1',
      endDate: 'date2'
    };
    MwlCalendarCtrl.onDragSelectMove(date);
    expect(MwlCalendarCtrl.dateRangeSelect).to.deep.equal({
      startDate: 'date1',
      endDate: date
    });
  });

  it('should not throw if there is no date range being selected', function() {
    var date = new Date();
    MwlCalendarCtrl.dateRangeSelect = null;
    expect(function() {
      MwlCalendarCtrl.onDragSelectMove(date);
    }).not.to.throw();
  });

  it('should call the onDateRangeSelect callback if there is a valid date range', function() {
    MwlCalendarCtrl.onDateRangeSelect = sinon.spy();
    var date1 = moment();
    var date2 = moment(Date.now() + 100000);
    MwlCalendarCtrl.dateRangeSelect = {
      startDate: date1,
      endDate: moment(Date.now() + 10)
    };
    MwlCalendarCtrl.onDragSelectEnd(date2);
    expect(MwlCalendarCtrl.onDateRangeSelect).to.have.been.calledWith({
      calendarRangeStartDate: date1.toDate(),
      calendarRangeEndDate: date2.toDate()
    });
    expect(MwlCalendarCtrl.dateRangeSelect).to.be.undefined;
  });

  it('should not call the onDateRangeSelect callback if there is an invalid date range', function() {
    MwlCalendarCtrl.onDateRangeSelect = sinon.spy();
    var date1 = moment();
    var date2 = moment(Date.now() - 100000);
    MwlCalendarCtrl.dateRangeSelect = {
      startDate: date1,
      endDate: moment(Date.now() - 10)
    };
    MwlCalendarCtrl.onDragSelectEnd(date2);
    expect(MwlCalendarCtrl.onDateRangeSelect).not.to.have.been.called;
    expect(MwlCalendarCtrl.dateRangeSelect).to.be.undefined;
  });

  it('should allow the hour segments to have their CSS class overridden', function() {
    sinon.stub(moment, 'locale').returns('another locale');
    scope.cellModifier = function(args) {
      args.calendarCell.cssClass = 'foo';
    };
    scope.$apply();
    scope.$broadcast('calendar.refreshView');
    scope.$apply();
    moment.locale.restore();
    expect(element[0].querySelector('.cal-day-hour-part.foo')).to.be.ok;
  });

  it('should allow the week view with times day segments CSS classes to be customised', function() {
    scope.view = 'week';
    scope.dayWidth = 50;
    sinon.stub(moment, 'locale').returns('another locale');
    scope.cellModifier = function(args) {
      args.calendarCell.cssClass = 'foo';
    };
    scope.$apply();
    scope.$broadcast('calendar.refreshView');
    scope.$apply();
    moment.locale.restore();
    expect(element[0].querySelector('.cal-day-hour-part-spacer.foo')).to.be.ok;
  });

  it('should not throw if there is no current range being selected', function() {
    expect(function() {
      MwlCalendarCtrl.onDragSelectEnd(new Date());
    }).not.to.throw();
  });

});
<commit_msg>test: fix eslint error<commit_after>'use strict';

var angular = require('angular'),
  moment = require('moment');

describe('mwlCalendarHourList directive', function() {
  var MwlCalendarCtrl,
    element,
    scope,
    $rootScope,
    directiveScope,
    showModal,
    clock,
    template =
      '<mwl-calendar-hour-list ' +
        'day-view-start="dayViewStart" ' +
        'day-view-end="dayViewEnd" ' +
        'day-view-split="dayViewSplit || 30" ' +
        'on-event-times-changed="eventDropped(calendarEvent, calendarDate, calendarNewEventStart, calendarNewEventEnd)" ' +
        'cell-modifier="cellModifier"' +
        'day-width="dayWidth"' +
        'view="{{ view }}"' +
      '></mwl-calendar-hour-list>';

  function prepareScope(vm) {
    //These variables MUST be set as a minimum for the calendar to work
    vm.dayViewStart = '06:00';
    vm.dayViewEnd = '22:59';
    vm.dayViewsplit = 30;
    vm.cellModifier = sinon.stub();
    vm.view = 'day';

    showModal = sinon.spy();

    vm.onEventClick = function(event) {
      showModal('Clicked', event);
    };

    vm.onEventTimesChanged = function(event) {
      showModal('Dropped or resized', event);
    };
  }

  beforeEach(angular.mock.module('mwl.calendar'));

  beforeEach(angular.mock.inject(function($compile, _$rootScope_) {
    clock = sinon.useFakeTimers(new Date('October 20, 2015 11:10:00').getTime());
    $rootScope = _$rootScope_;
    scope = $rootScope.$new();
    prepareScope(scope);

    element = $compile(template)(scope);
    scope.$apply();
    directiveScope = element.isolateScope();
    MwlCalendarCtrl = directiveScope.vm;
  }));

  afterEach(function() {
    clock.restore();
  });

  it('should define a list of hours', function() {
    expect(MwlCalendarCtrl.hourGrid.length).to.equal(17);
  });

  it('should update the list of hours when the calendar refreshes if the locale changes', function() {
    sinon.stub(moment, 'locale').returns('another locale');
    scope.dayViewStart = '00:00';
    scope.$apply();
    scope.$broadcast('calendar.refreshView');
    expect(MwlCalendarCtrl.hourGrid.length).to.equal(23);
    moment.locale.restore();
  });

  it('should call the event times changed callback when an event is dropped', function() {
    var event = {
      startsAt: moment().toDate(),
      endsAt: moment().add(1, 'day').toDate()
    };
    var date = moment().add(2, 'days').toDate();
    MwlCalendarCtrl.onEventTimesChanged = sinon.spy();
    MwlCalendarCtrl.eventDropped(event, date);
    var calledWith = MwlCalendarCtrl.onEventTimesChanged.getCall(0).args[0];
    expect(calledWith.calendarEvent).to.equal(event);
    expect(calledWith.calendarDate.getTime()).to.equal(date.getTime());
    expect(calledWith.calendarNewEventStart.getTime()).to.equal(date.getTime());
    expect(calledWith.calendarNewEventEnd.getTime()).to.equal(moment().add(3, 'days').toDate().getTime());
  });

  it('should initialise the date range select', function() {

    var date = new Date();
    MwlCalendarCtrl.onDragSelectStart(date, 1);
    expect(MwlCalendarCtrl.dateRangeSelect).to.deep.equal({
      active: true,
      startDate: date,
      endDate: date,
      dayIndex: 1
    });

  });

  it('should update the date ranges end date', function() {
    var date = new Date();
    MwlCalendarCtrl.dateRangeSelect = {
      startDate: 'date1',
      endDate: 'date2'
    };
    MwlCalendarCtrl.onDragSelectMove(date);
    expect(MwlCalendarCtrl.dateRangeSelect).to.deep.equal({
      startDate: 'date1',
      endDate: date
    });
  });

  it('should not throw if there is no date range being selected', function() {
    var date = new Date();
    MwlCalendarCtrl.dateRangeSelect = null;
    expect(function() {
      MwlCalendarCtrl.onDragSelectMove(date);
    }).not.to.throw();
  });

  it('should call the onDateRangeSelect callback if there is a valid date range', function() {
    MwlCalendarCtrl.onDateRangeSelect = sinon.spy();
    var date1 = moment();
    var date2 = moment(Date.now() + 100000);
    MwlCalendarCtrl.dateRangeSelect = {
      startDate: date1,
      endDate: moment(Date.now() + 10)
    };
    MwlCalendarCtrl.onDragSelectEnd(date2);
    expect(MwlCalendarCtrl.onDateRangeSelect).to.have.been.calledWith({
      calendarRangeStartDate: date1.toDate(),
      calendarRangeEndDate: date2.toDate()
    });
    expect(MwlCalendarCtrl.dateRangeSelect).to.be.undefined;
  });

  it('should not call the onDateRangeSelect callback if there is an invalid date range', function() {
    MwlCalendarCtrl.onDateRangeSelect = sinon.spy();
    var date1 = moment();
    var date2 = moment(Date.now() - 100000);
    MwlCalendarCtrl.dateRangeSelect = {
      startDate: date1,
      endDate: moment(Date.now() - 10)
    };
    MwlCalendarCtrl.onDragSelectEnd(date2);
    expect(MwlCalendarCtrl.onDateRangeSelect).not.to.have.been.called;
    expect(MwlCalendarCtrl.dateRangeSelect).to.be.undefined;
  });

  it('should allow the hour segments to have their CSS class overridden', function() {
    sinon.stub(moment, 'locale').returns('another locale');
    scope.cellModifier = function(args) {
      args.calendarCell.cssClass = 'foo';
    };
    scope.$apply();
    scope.$broadcast('calendar.refreshView');
    scope.$apply();
    moment.locale.restore();
    expect(element[0].querySelector('.cal-day-hour-part.foo')).to.be.ok;
  });

  it('should allow the week view with times day segments CSS classes to be customised', function() {
    scope.view = 'week';
    scope.dayWidth = 50;
    sinon.stub(moment, 'locale').returns('another locale');
    scope.cellModifier = function(args) {
      args.calendarCell.cssClass = 'foo';
    };
    scope.$apply();
    scope.$broadcast('calendar.refreshView');
    scope.$apply();
    moment.locale.restore();
    expect(element[0].querySelector('.cal-day-hour-part-spacer.foo')).to.be.ok;
  });

  it('should not throw if there is no current range being selected', function() {
    expect(function() {
      MwlCalendarCtrl.onDragSelectEnd(new Date());
    }).not.to.throw();
  });

});
<|endoftext|>


<commit_before>const router = require('koa-router')();
const api = require('./api');
const auth = require('./auth');
const responseFormatter = require('../middleware/response_formatter');

// 添加格式化处理响应结果的中间件，在路由之前调用
// 仅对api开头的url进行格式化
router.use(responseFormatter('^/api'));

// 用户认证相关路由
router.use('/auth', auth.routes(), auth.allowedMethods());

// Rest api
router.use('/api', api.routes(), api.allowedMethods());

router.get('*', async (ctx, next) => {
    ctx.state = {
        title: 'koa2 title'
    };

    await ctx.render('index', {});

    next();
});
module.exports = router;
<commit_msg>Node端调整auth接口对返回结果的封装<commit_after>const router = require('koa-router')();
const api = require('./api');
const auth = require('./auth');
const responseFormatter = require('../middleware/response_formatter');

// 添加格式化处理响应结果的中间件，在路由之前调用
// 仅对api开头的url进行格式化
router.use(responseFormatter('^/api|^/auth'));

// 用户认证相关路由
router.use('/auth', auth.routes(), auth.allowedMethods());

// Rest api
router.use('/api', api.routes(), api.allowedMethods());

router.get('*', async (ctx, next) => {
    ctx.state = {
        title: 'koa2 title'
    };

    await ctx.render('index', {});

    next();
});
module.exports = router;
<|endoftext|>


<commit_before>function loadChartBuilder(pageData, onSave, chart) {
  var chart = chart;
  var pageUrl = localStorage.getItem('pageurl');
  var html = templates.chartBuilder(chart);
  var table = false;
  $('body').append(html);
  $('.chart-builder').css("display", "block");

  if (chart) {
    $('#chart-data').val(toTsv(chart));
    refreshBarLineSection();
  }

  renderChart();

  function refreshBarLineSection() {
    var data = editBarline(chart);
    var html = templates.chartEditBarlines(data);
    $('#barline').html(html);
  }

  function editBarline(chart) {
    var data = [];
    var series = chart.series;

    if (chart.type === 'barline') { // if we have a bar line we want to populate the entries for each series
      if (chart.types) { // if we have existing types use them
        var type = _.values(chart.types);
        for (var i = 0; i < chart.series.length; i += 1) {
          data.push({
            series: series[i], type: type[i],
            isChecked: (function () {
              var checked = _.indexOf(chart.groups[0], series[i]);
              if (checked < 0) {
                return checked = false;
              } else {
                return checked = true;
              }
            })()
          });
        }
      } else { // if we have no existing types, default them
        for (var i = 0; i < chart.series.length; i += 1) {
          data.push({series: series[i], type: '', isChecked: false});
        }
      }
    }
    return data;
  }

  $('#edit-chart').on('input change', ':input', function () {
    chart = buildChartObject();
    refreshBarLineSection();
    renderChart();
  });

  $('.btn-chart-builder-cancel').on('click', function () {
    $('.chart-builder').stop().fadeOut(200).remove();
  });

  $('.btn-chart-builder-create').on('click', function () {

    var path = pageUrl + "/" + chart.filename;
    var jsonPath = path + ".json";
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + jsonPath,
      type: "POST",
      data: JSON.stringify(buildChartObject()),
      processData: false,
      contentType: false,
      success: function (res) {
        if (!table) {
          generatePng();
        }

        if (!pageData.charts) {
          pageData.charts = []
        }

        existingChart = _.find(pageData.charts, function (existingChart) {
          return existingChart.filename === chart.filename
        });

        if (existingChart) {
          existingChart.title = chart.title;
        } else {
          pageData.charts.push({title: chart.title, filename: chart.filename, path: path});
        }

        if (onSave) {
          onSave(chart.filename, '<ons-chart path="' + getPathName() + '/' + chart.filename + '" />');
        }
        $('.chart-builder').stop().fadeOut(200).remove();
      }
    });
  });

  // Builds, parses, and renders our chart in the chart editor
  function renderChart() {
    chart = buildChartObject();
    if (table) {
      $('#preview-chart').empty();
      $('#preview-chart').html('

');
      drawTable(chart);
    }

    var preview = $('#preview-chart');
    preview.html('

');

    var chartHeight = preview.width() * chart.aspectRatio;
    var chartWidth = preview.width();

    if (chartHeight > preview.height()) {
      chartHeight = preview.height();
      chartWidth = preview.height() / chart.aspectRatio;
    }

    renderChartObject('#chart', chart, chartHeight, chartWidth);
  }

  function buildChartObject() {
    var json = $('#chart-data').val();
    if (!chart) {
      chart = {};
    }

    chart.title = $('#chart-title').val();
    chart.filename = chart.filename ? chart.filename : StringUtils.randomId(); //  chart.title.replace(/[^A-Z0-9]+/ig, "").toLowerCase();
    chart.subtitle = $('#chart-subtitle').val();
    chart.unit = $('#chart-unit').val();
    chart.source = $('#chart-source').val();
    chart.legend = $('#chart-legend').val();
    chart.hideLegend = (chart.legend === 'false') ? true : false;

    if (chart.title === '') {
      chart.title = '[Title]'
    }

    chart.data = tsvJSON(json);
    chart.headers = tsvJSONHeaders(json);
    chart.series = tsvJSONColNames(json);
    chart.categories = tsvJSONRowNames(json);

    chart.aspectRatio = $('#aspect-ratio').val();

    if (chart.type === 'barline') {
      var types = {};
      var groups = [];
      var group = [];
      var seriesData = chart.series;
      for (var i = 0; i < seriesData.length; i++) {
        types[seriesData[i]] = $('#types_' + i).val() || 'bar';
      }
      (function () {
        $('#barline input:checkbox:checked').each(function () {
          group.push($(this).val());
        });
        groups.push(group);
        return groups;
      })();
      chart.types = types;
      chart.groups = groups;
    }

    chart.type = $('#chart-type').val();
    if (chart.type === 'table') {
      table = true;
    } else {
      table = false;
    }

    //console.log(chart);
    parseChartObject(chart);

    return chart;
  }

  function parseChartObject(chart) {

    // Determine if we have a time series
    var timeSeries = axisAsTimeSeries(chart.categories);
    if (timeSeries && timeSeries.length > 0) {
      chart.isTimeSeries = true;

      // We create data specific to time
      timeData = [];
      _.each(timeSeries, function (time) {
        var item = chart.data[time['row']];
        item.date = time['date'];
        item.label = time['label'];
        timeData.push(item);
      })

      chart.timeSeries = timeData;
    }
  }

// Data load from text box functions
  function tsvJSON(input) {
    var lines = input.split("\n");
    var result = [];
    var headers = lines[0].split("\t");

    for (var i = 1; i < lines.length; i++) {
      var obj = {};
      if (!table) {
        var currentline = lines[i].split(",").join("").split("\t");
      } else {
        var currentline = lines[i].split("\t");
      }
      for (var j = 0; j < headers.length; j++) {
        obj[headers[j]] = currentline[j];
      }
      result.push(obj);
    }
    return result; //JSON
  }

  function toTsv(data) {
    var output = "";

    for (var i = 0; i < data.headers.length; i++) {
      if (i === data.headers.length - 1) {
        output += data.headers[i];
      } else {
        output += data.headers[i] + "\t";
      }
    }

    for (var i = 0; i < data.categories.length; i++) {
      output += "\n" + toTsvLine(data.data[i], data.headers);
    }

    return output;
  }

  function toTsvLine(data, headers) {

    var output = "";

    for (var i = 0; i < headers.length; i++) {
      if (i === headers.length - 1) {
        output += data[headers[i]];
      } else {
        output += data[headers[i]] + "\t";
      }
    }
    return output;
  }

  function tsvJSONRowNames(input) {
    var lines = input.split("\n");
    var result = [];

    for (var i = 1; i < lines.length; i++) {
      var currentline = lines[i].split("\t");
      result.push(currentline[0]);
    }
    return result
  }

  function tsvJSONColNames(input) {
    var lines = input.split("\n");
    var headers = lines[0].split("\t");
    headers.shift();
    return headers;
  }

  function tsvJSONHeaders(input) {
    var lines = input.split("\n");
    var headers = lines[0].split("\t");
    return headers;
  }

  function exportToSVG() {
    var svgContainer = $('#chart');
    var svg = svgContainer.find('svg');

    var styleContent = "\n";
    for (var i = 0; i < document.styleSheets.length; i++) {
      str = document.styleSheets[i].href.split("/");
      if (str[str.length - 1] == "c3.css") {
        var rules = document.styleSheets[i].rules;
        for (var j = 0; j < rules.length; j++) {
          styleContent += (rules[j].cssText + "\n");
        }
        break;
      }
    }

    //var style = document.createElementNS("http://www.w3.org/2000/svg", "style");
    //$(style).textContent += "\n<![CDATA[\n" + styleContent + "\n]]>\n";
    //
    //svg.prepend(style);
    //svg[0].getElementsByTagName("defs")[0].appendChild(style);

    svg.prepend("\n");
    svg.find("style").textContent += "\n<![CDATA[" + styleContent + "]]>\n";

    //if ($('#chart-type').val() === 'line') {
    //  $('.c3 line').css("fill", "none");
    //  console.log($('.c3 line'))
    //}

    var source = (new XMLSerializer).serializeToString(svg[0]);
    //console.log(source);

    //add name spaces.
    if (!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)) {
      source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
    }
    if (!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)) {
      source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
    }
    //add padding
    source = source.replace(/style="overflow: hidden;"/, 'style="overflow: hidden; padding: 50px;"');

    //add xml declaration
    source = '<?xml version="1.0" standalone="no"?>\r\n' + source;

    return source;
  }

// Steps through time series points
  function axisAsTimeSeries(axis) {
    var result = [];
    var rowNumber = 0;

    _.each(axis, function (tryTimeString) {
      var time = convertTimeString(tryTimeString);
      if (time) {
        time.row = rowNumber;
        rowNumber = rowNumber + 1;

        result.push(time);
      } else {
        return null;
      }
    });
    return result;
  }

  function convertTimeString(timeString) {
    // First time around parse the time string according to rules from regular timeseries
    var result = {};
    result.label = timeString;

    // Format time string
    // Check for strings that will turn themselves into a strange format
    twoDigitYearEnd = timeString.match(/\W\d\d$/);
    if (twoDigitYearEnd !== null) {
      year = parseInt(twoDigitYearEnd = timeString.match(/\W\d\d$/));
      prefix = timeString.substr(0, timeString.length - 3);

      if (year >= 40) {
        timeString = prefix + " 19" + year;
      } else {
        timeString = prefix + " 20" + year;
      }
    }

    // We are going with all times in a common format
    var date = new Date(timeString);
    if (!isNaN(date.getTime())) {
      result.date = date;
      result.period = 'other';
      return result;
    }

    return (null);
  }

  function drawTable(data) {
    var title = data.headers;
    var rows = data.data;
    drawTitles(title);
    for (var i = 0; i < rows.length; i++) {
      drawRow(rows[i]);
    }

    function drawTitles(title) {
      var row = $("
");
      $("#dataTable").append(row);
      for (var j = 0; j < title.length; j++) {
        row.append($("" + title[j] + "	"));
      }
    }

    function drawRow(rowData) {
      var row = $("
")
      $("#dataTable").append(row);
      for (var j = 0; j < title.length; j++) {
        row.append($("" + rowData[title[j]] + "	"));
      }
    }
  }

  //generatePng();
  function generatePng() {

    var preview = $('#chart');
    var chartHeight = preview.width() * chart.aspectRatio;
    var chartWidth = preview.width();

    if (chartHeight > preview.height()) {
      chartHeight = preview.height();
      chartWidth = preview.height() / chart.aspectRatio;
    }

    var content = exportToSVG().trim();

    var $canvas = $('#hiddenCanvas');
    $canvas.width(chartWidth);
    $canvas.height(chartHeight);

    var canvas = $canvas.get(0);

    // Draw svg on canvas
    canvg(canvas, content);

    // get data url from canvas.
    var dataUrl = canvas.toDataURL('image/png');
    var pngData = dataUrl.replace(/^data:image\/(png|jpg);base64,/, "");
    //console.log(dataUrl);

    // render png
    //var $png = $('#hiddenPng');
    //var png = $png[0];
    //$png.attr('src', dataUrl);

    var raw = window.atob(pngData);
    var rawLength = raw.length;
    var array = new Uint8Array(new ArrayBuffer(rawLength));

    for (var i = 0; i < rawLength; i++) {
      array[i] = raw.charCodeAt(i);
    }

    var pngUri = pageUrl + "/" + chart.filename + ".png";
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + pngUri,
      type: "POST",
      data: new Blob([array], {type: 'image/png'}),
      contentType: "image/png",
      processData: false,
      success: function (res) {
        console.log('png uploaded!');
      }
    });
  }
}

<commit_msg>Date parser works with quarters<commit_after>function loadChartBuilder(pageData, onSave, chart) {
  var chart = chart;
  var pageUrl = localStorage.getItem('pageurl');
  var html = templates.chartBuilder(chart);
  var table = false;
  $('body').append(html);
  $('.chart-builder').css("display", "block");

  if (chart) {
    $('#chart-data').val(toTsv(chart));
    refreshBarLineSection();
  }

  renderChart();

  function refreshBarLineSection() {
    var data = editBarline(chart);
    var html = templates.chartEditBarlines(data);
    $('#barline').html(html);
  }

  function editBarline(chart) {
    var data = [];
    var series = chart.series;

    if (chart.type === 'barline') { // if we have a bar line we want to populate the entries for each series
      if (chart.types) { // if we have existing types use them
        var type = _.values(chart.types);
        for (var i = 0; i < chart.series.length; i += 1) {
          data.push({
            series: series[i], type: type[i],
            isChecked: (function () {
              var checked = _.indexOf(chart.groups[0], series[i]);
              if (checked < 0) {
                return checked = false;
              } else {
                return checked = true;
              }
            })()
          });
        }
      } else { // if we have no existing types, default them
        for (var i = 0; i < chart.series.length; i += 1) {
          data.push({series: series[i], type: '', isChecked: false});
        }
      }
    }
    return data;
  }

  $('#edit-chart').on('input change', ':input', function () {
    chart = buildChartObject();
    refreshBarLineSection();
    renderChart();
  });

  $('.btn-chart-builder-cancel').on('click', function () {
    $('.chart-builder').stop().fadeOut(200).remove();
  });

  $('.btn-chart-builder-create').on('click', function () {

    var path = pageUrl + "/" + chart.filename;
    var jsonPath = path + ".json";
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + jsonPath,
      type: "POST",
      data: JSON.stringify(buildChartObject()),
      processData: false,
      contentType: false,
      success: function (res) {
        if (!table) {
          generatePng();
        }

        if (!pageData.charts) {
          pageData.charts = []
        }

        existingChart = _.find(pageData.charts, function (existingChart) {
          return existingChart.filename === chart.filename
        });

        if (existingChart) {
          existingChart.title = chart.title;
        } else {
          pageData.charts.push({title: chart.title, filename: chart.filename, path: path});
        }

        if (onSave) {
          onSave(chart.filename, '<ons-chart path="' + getPathName() + '/' + chart.filename + '" />');
        }
        $('.chart-builder').stop().fadeOut(200).remove();
      }
    });
  });

  // Builds, parses, and renders our chart in the chart editor
  function renderChart() {
    chart = buildChartObject();
    if (table) {
      $('#preview-chart').empty();
      $('#preview-chart').html('

');
      drawTable(chart);
    }

    var preview = $('#preview-chart');
    preview.html('

');

    var chartHeight = preview.width() * chart.aspectRatio;
    var chartWidth = preview.width();

    if (chartHeight > preview.height()) {
      chartHeight = preview.height();
      chartWidth = preview.height() / chart.aspectRatio;
    }

    renderChartObject('#chart', chart, chartHeight, chartWidth);
  }

  function buildChartObject() {
    var json = $('#chart-data').val();
    if (!chart) {
      chart = {};
    }

    chart.title = $('#chart-title').val();
    chart.filename = chart.filename ? chart.filename : StringUtils.randomId(); //  chart.title.replace(/[^A-Z0-9]+/ig, "").toLowerCase();
    chart.subtitle = $('#chart-subtitle').val();
    chart.unit = $('#chart-unit').val();
    chart.source = $('#chart-source').val();
    chart.legend = $('#chart-legend').val();
    chart.hideLegend = (chart.legend === 'false') ? true : false;

    if (chart.title === '') {
      chart.title = '[Title]'
    }

    chart.data = tsvJSON(json);
    chart.headers = tsvJSONHeaders(json);
    chart.series = tsvJSONColNames(json);
    chart.categories = tsvJSONRowNames(json);

    chart.aspectRatio = $('#aspect-ratio').val();

    if (chart.type === 'barline') {
      var types = {};
      var groups = [];
      var group = [];
      var seriesData = chart.series;
      for (var i = 0; i < seriesData.length; i++) {
        types[seriesData[i]] = $('#types_' + i).val() || 'bar';
      }
      (function () {
        $('#barline input:checkbox:checked').each(function () {
          group.push($(this).val());
        });
        groups.push(group);
        return groups;
      })();
      chart.types = types;
      chart.groups = groups;
    }

    chart.type = $('#chart-type').val();
    if (chart.type === 'table') {
      table = true;
    } else {
      table = false;
    }

    //console.log(chart);
    parseChartObject(chart);

    return chart;
  }

  function parseChartObject(chart) {

    // Determine if we have a time series
    var timeSeries = axisAsTimeSeries(chart.categories);
    if (timeSeries && timeSeries.length > 0) {
      chart.isTimeSeries = true;

      // We create data specific to time
      timeData = [];
      _.each(timeSeries, function (time) {
        var item = chart.data[time['row']];
        item.date = time['date'];
        item.label = time['label'];
        timeData.push(item);
      })

      chart.timeSeries = timeData;
    }
  }

// Data load from text box functions
  function tsvJSON(input) {
    var lines = input.split("\n");
    var result = [];
    var headers = lines[0].split("\t");

    for (var i = 1; i < lines.length; i++) {
      var obj = {};
      if (!table) {
        var currentline = lines[i].split(",").join("").split("\t");
      } else {
        var currentline = lines[i].split("\t");
      }
      for (var j = 0; j < headers.length; j++) {
        obj[headers[j]] = currentline[j];
      }
      result.push(obj);
    }
    return result; //JSON
  }

  function toTsv(data) {
    var output = "";

    for (var i = 0; i < data.headers.length; i++) {
      if (i === data.headers.length - 1) {
        output += data.headers[i];
      } else {
        output += data.headers[i] + "\t";
      }
    }

    for (var i = 0; i < data.categories.length; i++) {
      output += "\n" + toTsvLine(data.data[i], data.headers);
    }

    return output;
  }

  function toTsvLine(data, headers) {

    var output = "";

    for (var i = 0; i < headers.length; i++) {
      if (i === headers.length - 1) {
        output += data[headers[i]];
      } else {
        output += data[headers[i]] + "\t";
      }
    }
    return output;
  }

  function tsvJSONRowNames(input) {
    var lines = input.split("\n");
    var result = [];

    for (var i = 1; i < lines.length; i++) {
      var currentline = lines[i].split("\t");
      result.push(currentline[0]);
    }
    return result
  }

  function tsvJSONColNames(input) {
    var lines = input.split("\n");
    var headers = lines[0].split("\t");
    headers.shift();
    return headers;
  }

  function tsvJSONHeaders(input) {
    var lines = input.split("\n");
    var headers = lines[0].split("\t");
    return headers;
  }

  function exportToSVG() {
    var svgContainer = $('#chart');
    var svg = svgContainer.find('svg');

    var styleContent = "\n";
    for (var i = 0; i < document.styleSheets.length; i++) {
      str = document.styleSheets[i].href.split("/");
      if (str[str.length - 1] == "c3.css") {
        var rules = document.styleSheets[i].rules;
        for (var j = 0; j < rules.length; j++) {
          styleContent += (rules[j].cssText + "\n");
        }
        break;
      }
    }

    //var style = document.createElementNS("http://www.w3.org/2000/svg", "style");
    //$(style).textContent += "\n<![CDATA[\n" + styleContent + "\n]]>\n";
    //
    //svg.prepend(style);
    //svg[0].getElementsByTagName("defs")[0].appendChild(style);

    svg.prepend("\n");
    svg.find("style").textContent += "\n<![CDATA[" + styleContent + "]]>\n";

    //if ($('#chart-type').val() === 'line') {
    //  $('.c3 line').css("fill", "none");
    //  console.log($('.c3 line'))
    //}

    var source = (new XMLSerializer).serializeToString(svg[0]);
    //console.log(source);

    //add name spaces.
    if (!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)) {
      source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
    }
    if (!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)) {
      source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
    }
    //add padding
    source = source.replace(/style="overflow: hidden;"/, 'style="overflow: hidden; padding: 50px;"');

    //add xml declaration
    source = '<?xml version="1.0" standalone="no"?>\r\n' + source;

    return source;
  }

// Steps through time series points
  function axisAsTimeSeries(axis) {
    var result = [];
    var rowNumber = 0;

    _.each(axis, function (tryTimeString) {
      var time = convertTimeString(tryTimeString);
      if (time) {
        time.row = rowNumber;
        rowNumber = rowNumber + 1;

        result.push(time);
      } else {
        return null;
      }
    });
    return result;
  }

  function convertTimeString(timeString) {
    // First time around parse the time string according to rules from regular timeseries
    var result = {};
    result.label = timeString;

    // Format time string
    // Check for strings that will turn themselves into a strange format
    twoDigitYearEnd = timeString.match(/\W\d\d$/);
    if (twoDigitYearEnd !== null) {
      year = parseInt(timeString.substr(timeString.length - 2, timeString.length));
      prefix = timeString.substr(0, timeString.length - 2).trim();

      if (year >= 40) {
        timeString = prefix + " 19" + year;
      } else {
        timeString = prefix + " 20" + year;
      }
    }

    // Check for quarters
    quarter = timeString.match(/Q\d/);
    year = timeString.match(/\d\d\d\d/);
    if((quarter !== null) && (year !== null)) {
      months = ["February ", "May ", "August ", "November "];
      quarterMid = parseInt(quarter[0][1]);
      timeString = months[quarterMid - 1] + year[0];
    }

    // We are going with all times in a common format
    var date = new Date(timeString);
    if (!isNaN(date.getTime())) {
      result.date = date;
      result.period = 'other';
      return result;
    }

    return (null);
  }

  function drawTable(data) {
    var title = data.headers;
    var rows = data.data;
    drawTitles(title);
    for (var i = 0; i < rows.length; i++) {
      drawRow(rows[i]);
    }

    function drawTitles(title) {
      var row = $("
");
      $("#dataTable").append(row);
      for (var j = 0; j < title.length; j++) {
        row.append($("" + title[j] + "	"));
      }
    }

    function drawRow(rowData) {
      var row = $("
")
      $("#dataTable").append(row);
      for (var j = 0; j < title.length; j++) {
        row.append($("" + rowData[title[j]] + "	"));
      }
    }
  }

  //generatePng();
  function generatePng() {

    var preview = $('#chart');
    var chartHeight = preview.width() * chart.aspectRatio;
    var chartWidth = preview.width();

    if (chartHeight > preview.height()) {
      chartHeight = preview.height();
      chartWidth = preview.height() / chart.aspectRatio;
    }

    var content = exportToSVG().trim();

    var $canvas = $('#hiddenCanvas');
    $canvas.width(chartWidth);
    $canvas.height(chartHeight);

    var canvas = $canvas.get(0);

    // Draw svg on canvas
    canvg(canvas, content);

    // get data url from canvas.
    var dataUrl = canvas.toDataURL('image/png');
    var pngData = dataUrl.replace(/^data:image\/(png|jpg);base64,/, "");
    //console.log(dataUrl);

    // render png
    //var $png = $('#hiddenPng');
    //var png = $png[0];
    //$png.attr('src', dataUrl);

    var raw = window.atob(pngData);
    var rawLength = raw.length;
    var array = new Uint8Array(new ArrayBuffer(rawLength));

    for (var i = 0; i < rawLength; i++) {
      array[i] = raw.charCodeAt(i);
    }

    var pngUri = pageUrl + "/" + chart.filename + ".png";
    $.ajax({
      url: "/zebedee/content/" + Florence.collection.id + "?uri=" + pngUri,
      type: "POST",
      data: new Blob([array], {type: 'image/png'}),
      contentType: "image/png",
      processData: false,
      success: function (res) {
        console.log('png uploaded!');
      }
    });
  }
}

<|endoftext|>