<commit_before>/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2017 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

(function() {

    'use strict';

    /**
     * @ngdoc service
     * @name requisition.requisitionService
     *
     * @description
     * Responsible for retriving all information from server.
     */
    angular
        .module('requisition')
        .service('requisitionService', service);

    service.$inject = [
        '$q', '$resource', 'messageService', 'openlmisUrlFactory', 'requisitionUrlFactory',
        'Requisition', 'confirmService', 'notificationService', 'dateUtils',
        'localStorageFactory', 'offlineService', 'paginationFactory', 'PAGE_SIZE'
    ];

    function service($q, $resource, messageService, openlmisUrlFactory, requisitionUrlFactory,
                                Requisition, confirmService, notificationService, dateUtils,
                                localStorageFactory, offlineService, paginationFactory, PAGE_SIZE) {

        var offlineRequisitions = localStorageFactory('requisitions'),
            onlineOnlyRequisitions = localStorageFactory('onlineOnly'),
            offlineStockAdjustmentReasons = localStorageFactory('stockAdjustmentReasons'),
            offlineStatusMessages = localStorageFactory('statusMessages');

        var resource = $resource(requisitionUrlFactory('/api/requisitions/:id'), {}, {
            'get': {
                method: 'GET',
                transformResponse: transformGetResponse
            },
            'initiate': {
                url: requisitionUrlFactory('/api/requisitions/initiate'),
                method: 'POST'
            },
            'search': {
                url: requisitionUrlFactory('/api/requisitions/search'),
                method: 'GET',
                transformResponse: transformRequisitionSearchResponse
            },
            'forApproval': {
                url: requisitionUrlFactory('/api/requisitions/requisitionsForApproval'),
                method: 'GET',
                isArray: true,
                transformResponse: transformRequisitionListResponse
            },
            'forConvert': {
                url: requisitionUrlFactory('/api/requisitions/requisitionsForConvert'),
                method: 'GET',
                transformResponse: transformResponseForConvert
            },
            'convertToOrder': {
                url: requisitionUrlFactory('/api/requisitions/convertToOrder'),
                method: 'POST',
                transformRequest: transformRequest
            },
            'getStockAdjustmentReasonsByProgram': {
                url: openlmisUrlFactory('/api/stockAdjustmentReasons/search'),
                method: 'GET',
                isArray: true
            },
            'getStatusMessages': {
                url: requisitionUrlFactory('/api/requisitions/:id/statusMessages'),
                method: 'GET',
                isArray: true
            }
        });

        var service = {
            get: get,
            initiate: initiate,
            search: search,
            forApproval: forApproval,
            forConvert: forConvert,
            convertToOrder: convertToOrder
        };

        return service;

        /**
         * @ngdoc function
         * @name get
         * @methodOf requisition.requisitionService
         * @param {String} id Requisition UUID
         * @return {Promise} requisition promise
         *
         * @description
         * Retrieves requisition by id.
         *
         */
        function get(id) {
            var deferred = $q.defer();

            if (offlineService.isOffline()) {
                var requisition = offlineRequisitions.getBy('id', id),
                    reasons = offlineStockAdjustmentReasons.search({
                        program: {
                            id: requisition.program.id
                        }
                    }),
                    statusMessages = offlineStatusMessages.search({
                        requisitionId: requisition.id
                    });

                resolve(requisition, reasons, statusMessages);
            } else {
                var requisition = offlineRequisitions.search({
                    id: id,
                    $modified: true
                });
                if (!requisition || !requisition.length) {
                    var i = 0;
                    getRequisition(id).then(function(requisition) {
                        requisition.$availableOffline = !onlineOnlyRequisitions.contains(id);
                        $q.all([
                            getStockAdjustmentReasons(requisition),
                            getStatusMessages(requisition)
                        ]).then(function(responses) {
                            if (requisition.$availableOffline) {
                                storeResponses(requisition, responses[0], responses[1]);
                            }
                            resolve(requisition, responses[0], responses[1]);
                        }, function() {
                            if (requisition.$availableOffline) {
                                offlineRequisitions.put(requisition);
                            }
                            resolve(requisition);
                        });
                    }, error);
                } else {
                    var reasons = offlineStockAdjustmentReasons.search({
                            program: {
                                id: requisition[0].program.id
                            }
                        }),
                        statusMessages = offlineStatusMessages.search({
                            requisitionId: requisition[0].id
                        });

                    resolve(requisition[0], reasons, statusMessages);
                }
            }

            return deferred.promise;

            function resolve(requisition, reasons, statusMessages) {
                deferred.resolve(new Requisition(requisition, reasons, statusMessages));
            }

            function error() {
                deferred.reject();
            }
        }

        /**
         * @ngdoc function
         * @name initiate
         * @methodOf requisition.requisitionService
         * @param {String} facility Facility UUID
         * @param {String} program Program UUID
         * @param {String} suggestedPeriod Period UUID
         * @param {boolean} emergency Indicates if requisition is emergency or not
         * @return {Promise} requisition promise
         *
         * @description
         * Initates new requisition for program in facility with given period.
         *
         */
        function initiate(facility, program, suggestedPeriod, emergency) {
            return resource.initiate({
                facility: facility,
                program: program,
                suggestedPeriod: suggestedPeriod,
                emergency: emergency
            }, {}).$promise;
        }

        /**
         * @ngdoc function
         * @name search
         * @methodOf requisition.requisitionService
         * @param {boolean} offline Indicates if searching in offline requisitions
         * @param {Object} searchParams Contains parameters for searching requisitions, i.e.
         * {
         *      program: 'programID',
         *      facility: 'facilityID',
         *      initiatedDateFrom: 'startDate',
         *      initiatedDateTo: 'endDate',
         *      requisitionStatus: ['status1', 'status2'],
         *      emergency: false
         * }
         *
         * @return {Array} Array of requisitions for given criteria (optional)
         *
         * @description
         * Search requisitons by criteria from parameters.
         *
         */
        function search(offline, searchParams) {
            var deferred = $q.defer();

            if(offline === 'true' || offline === true) {
                var requisitions = offlineRequisitions.search(searchParams, 'requisitionSearch'),
                    page = searchParams.page ? parseInt(searchParams.page) : 0,
                    size = searchParams.size ? parseInt(searchParams.size) : PAGE_SIZE,
                    items = paginationFactory.getPage(requisitions, page, size);

                angular.forEach(items, transformRequisition);

                deferred.resolve({
                    content: items,
                    number: page,
                    totalElements: requisitions.length,
                    size: size
                });
            } else {
                return resource.search(searchParams).$promise;
            }

            return deferred.promise;
        }

        /**
         * @ngdoc function
         * @name forApproval
         * @methodOf requisition.requisitionService
         * @return {Array} Array of requisitions for approval
         *
         * @description
         * Retrieves all requisitions with authorized status for approve.
         *
         */
        function forApproval() {
            return resource.forApproval().$promise;
        }

        /**
         * @ngdoc function
         * @name forConvert
         * @methodOf requisition.requisitionService
         * @param {Object} params Request params, contains i.e.: filertBy, filterValue, sortBy, descending
         * @return {Array} Array of requisitions for convert
         *
         * @description
         * Search requisitons for convert to order by given criteria.
         *
         */
        function forConvert(params) {
            return resource.forConvert(params).$promise;
        }

        /**
         * @ngdoc function
         * @name convertToOrder
         * @methodOf requisition.requisitionService
         * @param {Array} requisitions Array of requisitions to convert
         *
         * @description
         * Converts given requisitions into orders.
         *
         */
        function convertToOrder(requisitions) {
            var deferred = $q.defer();

            confirmService.confirm('msg.question.confirmation.convertToOrder').then(function() {
                resource.convertToOrder(requisitions).$promise.then(function() {
                    deferred.resolve();
                    notificationService.success('msg.rnr.converted.to.order');
                }, function() {
                    deferred.reject();
                    notificationService.error('msg.error.occurred');
                });
            }, function() {
                deferred.reject();
            });

            return deferred.promise;
        }

        function getRequisition(id) {
            return resource.get({
                id: id
            }).$promise;
        }

        function getOfflineRequisition(id) {
            return offlineRequisitions.getBy('id', id);
        }

        function getStockAdjustmentReasons(requisition) {
            return resource.getStockAdjustmentReasonsByProgram({
                program: requisition.program.id
            }).$promise;
        }

        function getStatusMessages(requisition) {
            return resource.getStatusMessages({
                id: requisition.id
            }).$promise;
        }

        function storeResponses(requisition, reasons, statusMessages) {
            requisition.$modified = false;
            offlineRequisitions.put(requisition);

            reasons.forEach(function(reason) {
                offlineStockAdjustmentReasons.put(reason);
            });

            statusMessages.forEach(function(statusMessage) {
                offlineStatusMessages.put(statusMessage);
            });
        }

        function transformRequest(requisitionsWithDepots) {
            var body = [];
            angular.forEach(requisitionsWithDepots, function(requisitionWithDepots) {
                body.push({
                    requisitionId: requisitionWithDepots.requisition.id,
                    supplyingDepotId: requisitionWithDepots.requisition.supplyingFacility
                });
            });
            return angular.toJson(body);
        }

        function transformGetResponse(data, headers, status) {
            return transformResponse(data, status, function(response) {
                if (response.processingPeriod.startDate) {
                    response.processingPeriod.startDate = dateUtils.toDate(response.processingPeriod.startDate);
                }
                if (response.processingPeriod.endDate) {
                    response.processingPeriod.endDate = dateUtils.toDate(response.processingPeriod.endDate);
                }
                return response;
            });
        }

        function transformRequisitionSearchResponse(data, headers, status) {
            return transformResponse(data, status, function(response) {
                angular.forEach(response.content, transformRequisition);
                return response;
            });
        }

        function transformRequisitionListResponse(data, headers, status) {
            return transformResponse(data, status, function(requisitions) {
                angular.forEach(requisitions, transformRequisition);
                return requisitions;
            });
        }

        function transformResponseForConvert(data, headers, status) {
            return transformResponse(data, status, function(response) {
                angular.forEach(response.content, function(item) {
                    transformRequisition(item.requisition);
                });
                return response;
            });
        }

        function transformResponse(data, status, transformer) {
            if (status === 200) {
                return transformer(angular.fromJson(data));
            }
            return data;
        }

        function transformRequisition(requisition) {
            if (offlineRequisitions.getBy('id', requisition.id)) {
                requisition.$availableOffline = true;
            }
            requisition.createdDate = dateUtils.toDate(requisition.createdDate);
            requisition.processingPeriod.startDate = dateUtils.toDate(requisition.processingPeriod.startDate);
            requisition.processingPeriod.endDate = dateUtils.toDate(requisition.processingPeriod.endDate);
            requisition.processingPeriod.processingSchedule.modifiedDate = dateUtils.toDate(requisition.processingPeriod.processingSchedule.modifiedDate);
        }
    }
})();
<commit_msg>OLMIS-1968: requisitionService will no longer attemp requisition transformation after fetching it from the local storage<commit_after>/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2017 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

(function() {

    'use strict';

    /**
     * @ngdoc service
     * @name requisition.requisitionService
     *
     * @description
     * Responsible for retriving all information from server.
     */
    angular
        .module('requisition')
        .service('requisitionService', service);

    service.$inject = [
        '$q', '$resource', 'messageService', 'openlmisUrlFactory', 'requisitionUrlFactory',
        'Requisition', 'confirmService', 'notificationService', 'dateUtils',
        'localStorageFactory', 'offlineService', 'paginationFactory', 'PAGE_SIZE'
    ];

    function service($q, $resource, messageService, openlmisUrlFactory, requisitionUrlFactory,
                                Requisition, confirmService, notificationService, dateUtils,
                                localStorageFactory, offlineService, paginationFactory, PAGE_SIZE) {

        var offlineRequisitions = localStorageFactory('requisitions'),
            onlineOnlyRequisitions = localStorageFactory('onlineOnly'),
            offlineStockAdjustmentReasons = localStorageFactory('stockAdjustmentReasons'),
            offlineStatusMessages = localStorageFactory('statusMessages');

        var resource = $resource(requisitionUrlFactory('/api/requisitions/:id'), {}, {
            'get': {
                method: 'GET',
                transformResponse: transformGetResponse
            },
            'initiate': {
                url: requisitionUrlFactory('/api/requisitions/initiate'),
                method: 'POST'
            },
            'search': {
                url: requisitionUrlFactory('/api/requisitions/search'),
                method: 'GET',
                transformResponse: transformRequisitionSearchResponse
            },
            'forApproval': {
                url: requisitionUrlFactory('/api/requisitions/requisitionsForApproval'),
                method: 'GET',
                isArray: true,
                transformResponse: transformRequisitionListResponse
            },
            'forConvert': {
                url: requisitionUrlFactory('/api/requisitions/requisitionsForConvert'),
                method: 'GET',
                transformResponse: transformResponseForConvert
            },
            'convertToOrder': {
                url: requisitionUrlFactory('/api/requisitions/convertToOrder'),
                method: 'POST',
                transformRequest: transformRequest
            },
            'getStockAdjustmentReasonsByProgram': {
                url: openlmisUrlFactory('/api/stockAdjustmentReasons/search'),
                method: 'GET',
                isArray: true
            },
            'getStatusMessages': {
                url: requisitionUrlFactory('/api/requisitions/:id/statusMessages'),
                method: 'GET',
                isArray: true
            }
        });

        var service = {
            get: get,
            initiate: initiate,
            search: search,
            forApproval: forApproval,
            forConvert: forConvert,
            convertToOrder: convertToOrder
        };

        return service;

        /**
         * @ngdoc function
         * @name get
         * @methodOf requisition.requisitionService
         * @param {String} id Requisition UUID
         * @return {Promise} requisition promise
         *
         * @description
         * Retrieves requisition by id.
         *
         */
        function get(id) {
            var deferred = $q.defer();

            if (offlineService.isOffline()) {
                var requisition = offlineRequisitions.getBy('id', id),
                    reasons = offlineStockAdjustmentReasons.search({
                        program: {
                            id: requisition.program.id
                        }
                    }),
                    statusMessages = offlineStatusMessages.search({
                        requisitionId: requisition.id
                    });

                resolve(requisition, reasons, statusMessages);
            } else {
                var requisition = offlineRequisitions.search({
                    id: id,
                    $modified: true
                });
                if (!requisition || !requisition.length) {
                    var i = 0;
                    getRequisition(id).then(function(requisition) {
                        requisition.$availableOffline = !onlineOnlyRequisitions.contains(id);
                        $q.all([
                            getStockAdjustmentReasons(requisition),
                            getStatusMessages(requisition)
                        ]).then(function(responses) {
                            if (requisition.$availableOffline) {
                                storeResponses(requisition, responses[0], responses[1]);
                            }
                            resolve(requisition, responses[0], responses[1]);
                        }, function() {
                            if (requisition.$availableOffline) {
                                offlineRequisitions.put(requisition);
                            }
                            resolve(requisition);
                        });
                    }, error);
                } else {
                    var reasons = offlineStockAdjustmentReasons.search({
                            program: {
                                id: requisition[0].program.id
                            }
                        }),
                        statusMessages = offlineStatusMessages.search({
                            requisitionId: requisition[0].id
                        });

                    resolve(requisition[0], reasons, statusMessages);
                }
            }

            return deferred.promise;

            function resolve(requisition, reasons, statusMessages) {
                deferred.resolve(new Requisition(requisition, reasons, statusMessages));
            }

            function error() {
                deferred.reject();
            }
        }

        /**
         * @ngdoc function
         * @name initiate
         * @methodOf requisition.requisitionService
         * @param {String} facility Facility UUID
         * @param {String} program Program UUID
         * @param {String} suggestedPeriod Period UUID
         * @param {boolean} emergency Indicates if requisition is emergency or not
         * @return {Promise} requisition promise
         *
         * @description
         * Initates new requisition for program in facility with given period.
         *
         */
        function initiate(facility, program, suggestedPeriod, emergency) {
            return resource.initiate({
                facility: facility,
                program: program,
                suggestedPeriod: suggestedPeriod,
                emergency: emergency
            }, {}).$promise;
        }

        /**
         * @ngdoc function
         * @name search
         * @methodOf requisition.requisitionService
         * @param {boolean} offline Indicates if searching in offline requisitions
         * @param {Object} searchParams Contains parameters for searching requisitions, i.e.
         * {
         *      program: 'programID',
         *      facility: 'facilityID',
         *      initiatedDateFrom: 'startDate',
         *      initiatedDateTo: 'endDate',
         *      requisitionStatus: ['status1', 'status2'],
         *      emergency: false
         * }
         *
         * @return {Array} Array of requisitions for given criteria (optional)
         *
         * @description
         * Search requisitons by criteria from parameters.
         *
         */
        function search(offline, searchParams) {
            var deferred = $q.defer();

            if(offline === 'true' || offline === true) {
                var requisitions = offlineRequisitions.search(searchParams, 'requisitionSearch'),
                    page = searchParams.page ? parseInt(searchParams.page) : 0,
                    size = searchParams.size ? parseInt(searchParams.size) : PAGE_SIZE,
                    items = paginationFactory.getPage(requisitions, page, size);

                deferred.resolve({
                    content: items,
                    number: page,
                    totalElements: requisitions.length,
                    size: size
                });
            } else {
                return resource.search(searchParams).$promise;
            }

            return deferred.promise;
        }

        /**
         * @ngdoc function
         * @name forApproval
         * @methodOf requisition.requisitionService
         * @return {Array} Array of requisitions for approval
         *
         * @description
         * Retrieves all requisitions with authorized status for approve.
         *
         */
        function forApproval() {
            return resource.forApproval().$promise;
        }

        /**
         * @ngdoc function
         * @name forConvert
         * @methodOf requisition.requisitionService
         * @param {Object} params Request params, contains i.e.: filertBy, filterValue, sortBy, descending
         * @return {Array} Array of requisitions for convert
         *
         * @description
         * Search requisitons for convert to order by given criteria.
         *
         */
        function forConvert(params) {
            return resource.forConvert(params).$promise;
        }

        /**
         * @ngdoc function
         * @name convertToOrder
         * @methodOf requisition.requisitionService
         * @param {Array} requisitions Array of requisitions to convert
         *
         * @description
         * Converts given requisitions into orders.
         *
         */
        function convertToOrder(requisitions) {
            var deferred = $q.defer();

            confirmService.confirm('msg.question.confirmation.convertToOrder').then(function() {
                resource.convertToOrder(requisitions).$promise.then(function() {
                    deferred.resolve();
                    notificationService.success('msg.rnr.converted.to.order');
                }, function() {
                    deferred.reject();
                    notificationService.error('msg.error.occurred');
                });
            }, function() {
                deferred.reject();
            });

            return deferred.promise;
        }

        function getRequisition(id) {
            return resource.get({
                id: id
            }).$promise;
        }

        function getOfflineRequisition(id) {
            return offlineRequisitions.getBy('id', id);
        }

        function getStockAdjustmentReasons(requisition) {
            return resource.getStockAdjustmentReasonsByProgram({
                program: requisition.program.id
            }).$promise;
        }

        function getStatusMessages(requisition) {
            return resource.getStatusMessages({
                id: requisition.id
            }).$promise;
        }

        function storeResponses(requisition, reasons, statusMessages) {
            requisition.$modified = false;
            offlineRequisitions.put(requisition);

            reasons.forEach(function(reason) {
                offlineStockAdjustmentReasons.put(reason);
            });

            statusMessages.forEach(function(statusMessage) {
                offlineStatusMessages.put(statusMessage);
            });
        }

        function transformRequest(requisitionsWithDepots) {
            var body = [];
            angular.forEach(requisitionsWithDepots, function(requisitionWithDepots) {
                body.push({
                    requisitionId: requisitionWithDepots.requisition.id,
                    supplyingDepotId: requisitionWithDepots.requisition.supplyingFacility
                });
            });
            return angular.toJson(body);
        }

        function transformGetResponse(data, headers, status) {
            return transformResponse(data, status, function(response) {
                if (response.processingPeriod.startDate) {
                    response.processingPeriod.startDate = dateUtils.toDate(response.processingPeriod.startDate);
                }
                if (response.processingPeriod.endDate) {
                    response.processingPeriod.endDate = dateUtils.toDate(response.processingPeriod.endDate);
                }
                return response;
            });
        }

        function transformRequisitionSearchResponse(data, headers, status) {
            return transformResponse(data, status, function(response) {
                angular.forEach(response.content, transformRequisition);
                return response;
            });
        }

        function transformRequisitionListResponse(data, headers, status) {
            return transformResponse(data, status, function(requisitions) {
                angular.forEach(requisitions, transformRequisition);
                return requisitions;
            });
        }

        function transformResponseForConvert(data, headers, status) {
            return transformResponse(data, status, function(response) {
                angular.forEach(response.content, function(item) {
                    transformRequisition(item.requisition);
                });
                return response;
            });
        }

        function transformResponse(data, status, transformer) {
            if (status === 200) {
                return transformer(angular.fromJson(data));
            }
            return data;
        }

        function transformRequisition(requisition) {
            if (offlineRequisitions.getBy('id', requisition.id)) {
                requisition.$availableOffline = true;
            }
            requisition.createdDate = dateUtils.toDate(requisition.createdDate);
            requisition.processingPeriod.startDate = dateUtils.toDate(requisition.processingPeriod.startDate);
            requisition.processingPeriod.endDate = dateUtils.toDate(requisition.processingPeriod.endDate);
            requisition.processingPeriod.processingSchedule.modifiedDate = dateUtils.toDate(requisition.processingPeriod.processingSchedule.modifiedDate);
        }
    }
})();
<|endoftext|>


<commit_before>import React from 'react';
import {Text, View} from 'react-native';
import {SharedElementMotion} from 'navigation-react-native';

const getStyle = ({x, y, w, h, fontSize = 0, fontColor = 0}) => ({
  x,
  y,
  w,
  h,
  fontSize,
  fontColor,
});

export default ({stateNavigator}) => (
  <SharedElementMotion
    elementStyle={(name, data) => getStyle(data)}
    onAnimating={(name, oldRef, mountedRef, oldData, mountedData) => {
      mountedData.hide && mountedRef.setNativeProps({style:{opacity: 0}})
    }}
    onAnimated={(name, oldRef, mountedRef) => {mountedRef.setNativeProps({style:{opacity: 1}})}}
    stateNavigator={stateNavigator}>
    {({x, y, w, h, fontSize, fontColor}, name, {color}) => (
      !name.startsWith('text') ? <View
        style={{
          position: 'absolute',
          left: x,
          top: y,
          width: w,
          height: h,
          backgroundColor: color,
        }}>
      </View> : <Text
        style={{
          position: 'absolute',
          left: x,
          top: y,
          width: w,
          height: h,
          fontSize,
          textAlign: 'center',
          fontWeight: 'bold',
          color: `rgb(${fontColor},${fontColor},${fontColor})`,
          zIndex: 1,
        }}>
          {color}
        </Text>
    )}
  </SharedElementMotion>
);
<commit_msg>Without spring can return data directly<commit_after>import React from 'react';
import {Text, View} from 'react-native';
import {SharedElementMotion} from 'navigation-react-native';

export default ({stateNavigator}) => (
  <SharedElementMotion
    elementStyle={(name, data) => data}
    onAnimating={(name, oldRef, mountedRef, oldData, mountedData) => {
      mountedData.hide && mountedRef.setNativeProps({style:{opacity: 0}})
    }}
    onAnimated={(name, oldRef, mountedRef) => {mountedRef.setNativeProps({style:{opacity: 1}})}}
    stateNavigator={stateNavigator}>
    {({x, y, w, h, fontSize, fontColor}, name, {color}) => (
      !name.startsWith('text') ? <View
        style={{
          position: 'absolute',
          left: x,
          top: y,
          width: w,
          height: h,
          backgroundColor: color,
        }}>
      </View> : <Text
        style={{
          position: 'absolute',
          left: x,
          top: y,
          width: w,
          height: h,
          fontSize,
          textAlign: 'center',
          fontWeight: 'bold',
          color: `rgb(${fontColor},${fontColor},${fontColor})`,
          zIndex: 1,
        }}>
          {color}
        </Text>
    )}
  </SharedElementMotion>
);
<|endoftext|>


<commit_before>let path = require("path")
let { URL } = require("url")
let test = require("ava")
let webdriver = require("selenium-webdriver")
let { By, until, Key } = webdriver
let moment = require("moment")
let _includes = require("lodash/includes")
let _isRegExp = require("lodash/isRegExp")
let chalk = require("chalk")

let capabilities = {}
let testEnv =
  (process.env.TRAVIS_TAG && "remote") || process.env.TEST_ENV || "local"
if (testEnv === "local") {
  // This gives us access to send Chrome commands.
  require("chromedriver")
} else {
  // Set capabilities for BrowserStack
  require("./keep-alive.js")
  capabilities = require("../../config/browserstack.config.js")
}

// Webdriver's promise manager only made sense before Node had async/await support.
// Now it's a deprecated legacy feature, so we should use the simpler native Node support instead.
webdriver.promise.USE_PROMISE_MANAGER = false

console.log(
  chalk.bold.cyan(
    `These tests assume that you have recently run ${path.resolve(
      __dirname,
      "..",
      "..",
      "..",
      "insertBaseData-mssql.sql"
    )} on your database instance`
  )
)

function debugLog(...args) {
  if (process.env.DEBUG_LOG === "true") {
    console.log(chalk.grey("[DEBUG]"), ...args)
  }
}

// We use the before hook to put helpers on t.context and set up test scaffolding.
test.beforeEach(t => {
  let builder = new webdriver.Builder()
  if (testEnv === "local") {
    let chrome = require("selenium-webdriver/chrome")
    builder = builder
      .forBrowser("chrome")
      .setChromeOptions(new chrome.Options().headless())
  } else {
    capabilities.name = t.title.replace(/^beforeEach hook for /, "")
    builder = builder
      .usingServer("http://hub-cloud.browserstack.com/wd/hub")
      .withCapabilities(capabilities)
  }
  t.context.driver = builder.build()

  let shortWaitMs = moment.duration(1, "seconds").asMilliseconds()
  let mediumWaitMs = moment.duration(3, "seconds").asMilliseconds()
  let longWaitMs = moment.duration(9, "seconds").asMilliseconds()

  t.context.By = By
  t.context.until = until
  t.context.shortWaitMs = shortWaitMs
  t.context.mediumWaitMs = mediumWaitMs
  t.context.longWaitMs = longWaitMs
  t.context.Key = Key

  // This method is a helper so we don't have to keep repeating the hostname.
  // Passing the authentication through the querystring is a hack so we can
  // pass the information along via window.fetch.
  t.context.get = async(pathname, userPw) => {
    let credentials = userPw || "erin"
    let urlToGet = `${process.env.SERVER_URL}${pathname}?user=${credentials}&pass=${credentials}`
    debugLog("Getting URL", urlToGet)
    await t.context.driver.get(urlToGet)
    await t.context.waitForLoadingFinished()

    // If we have a page-wide error message, we would like to cleanly fail the test on that.
    try {
      await t.context.driver.wait(
        until.elementTextIs(
          await t.context.$(".not-found-text", shortWaitMs),
          "There was an error processing this request. Please contact an administrator."
        ),
        shortWaitMs
      )
      throw new Error(
        "The API returned a 500. Do you need to restart the server?"
      )
    } catch (e) {
      // NoSuchElementError: If we didn't find the "not found text" element, then we are not seeing a page-wide failure.
      // StaleElementReferenceError: If we found the "not found text" element but it's no longer on the page,
      //      then we are not seeing a page-wide failure.
      // TimeoutError: If we found the "not found text" element, but the text was never the backend error message,
      //      then we are not seeing a page-wide failure.
      if (
        !_includes(
          ["NoSuchElementError", "StaleElementReferenceError", "TimeoutError"],
          e.name
        )
      ) {
        throw e
      }
    }
  }

  t.context.waitForLoadingFinished = async() => {
    await t.context.assertElementNotPresent(
      t,
      "span.loading",
      "Loading indicator should disappear",
      mediumWaitMs
    )
  }

  // For debugging purposes.
  t.context.waitForever = async() => {
    console.log(chalk.red("Waiting forever so you can debug..."))
    await t.context.driver.wait(() => {})
  }

  t.context.$ = async(cssSelector, timeoutMs) => {
    debugLog(`Find element: $('${cssSelector}')`)
    let waitTimeoutMs = timeoutMs || longWaitMs
    let locator = By.css(cssSelector)
    await t.context.driver.wait(
      until.elementLocated(locator),
      waitTimeoutMs,
      `Could not find element by css selector ${cssSelector} within ${waitTimeoutMs} milliseconds`
    )
    return t.context.driver.findElement(locator)
  }
  t.context.$$ = async(cssSelector, timeoutMs) => {
    debugLog(`Find elements: $$('${cssSelector}')`)
    let waitTimeoutMs = timeoutMs || longWaitMs
    let locator = By.css(cssSelector)
    await t.context.driver.wait(
      until.elementsLocated(locator),
      waitTimeoutMs,
      `Could not find elements by css selector ${cssSelector} within ${waitTimeoutMs} milliseconds`
    )
    return t.context.driver.findElements(locator)
  }

  // A helper method to combine waiting for an element to have rendered and then asserting on its contents.
  t.context.assertElementText = async(t, $elem, expectedText, message) => {
    try {
      let untilCondition = _isRegExp(expectedText)
        ? until.elementTextMatches($elem, expectedText)
        : until.elementTextIs($elem, expectedText)

      await t.context.driver.wait(untilCondition, longWaitMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not have the text we expected, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    let actualText = (await $elem.getText()).trim()
    if (_isRegExp(expectedText)) {
      t.regex(actualText, expectedText, actualText, message)
    } else {
      t.is(actualText, expectedText, message)
    }
  }

  t.context.assertElementTextIsInt = (t, $elem, message) =>
    t.context.assertElementText(t, $elem, /^\d+$/)

  t.context.assertElementNotPresent = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    let waitTimeoutMs = timeoutMs || longWaitMs
    try {
      await t.context.driver.wait(
        async() => {
          const loopDelay = 250
          try {
            return !(await t.context.$(cssSelector, loopDelay))
          } catch (e) {
            // Hilariously, when Selenium can't find an element, sometimes it throws TimeoutError,
            // and sometimes it throws NoSuchElementError.
            if (e.name === "TimeoutError" || e.name === "NoSuchElementError") {
              return true
            }
            throw e
          }
        },
        waitTimeoutMs,
        `Element was still present after ${waitTimeoutMs} milliseconds`
      )
    } catch (e) {
      if (e.name === "TimeoutError") {
        t.fail(
          `Element with css selector '${cssSelector}' was still present after ${waitTimeoutMs} milliseconds: ${message}`
        )
      } else {
        throw e
      }
    }
    t.pass(message || "Element was not present")
  }

  // A helper method to combine waiting for an element to have rendered and then asserting on its enabled status
  t.context.assertElementEnabled = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    let waitTimeoutMs = timeoutMs || longWaitMs
    try {
      var elem = await t.context.$(cssSelector, waitTimeoutMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not load, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    t.is(await elem.isEnabled(), true, message)
  }

  // A helper method to combine waiting for an element to have rendered and then asserting it's disabled status
  t.context.assertElementDisabled = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    let waitTimeoutMs = timeoutMs || longWaitMs
    try {
      var elem = await t.context.$(cssSelector, waitTimeoutMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not load, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    t.is(await elem.isEnabled(), false, message)
  }

  t.context.getCurrentPathname = async() => {
    let currentUrl = await t.context.driver.getCurrentUrl()
    return new URL(currentUrl).pathname
  }

  t.context.pageHelpers = {
    async goHomeAndThenToReportsPage() {
      await t.context.get("/")

      let $createButton = await t.context.$("#createButton")
      await $createButton.click()
    },
    async clickTodayButton() {
      let $todayButton = await t.context.driver.findElement(
        By.xpath('//button/span[text()="Today"]')
      )
      await $todayButton.click()
    },
    async clickNextMonthDate() {
      let $nextMonthButton = await t.context.driver.findElement(
        By.xpath('//button/span[icon="chevron-right"]')
      )
      await $nextMonthButton.click()
      let $nextMonthDate = await t.context.driver.findElement(
        By.xpath('//div[class="DayPicker-Day--selected"]')
      )
      await $nextMonthDate.click()
    },
    async chooseAdvancedSelectOption(inputSelector, text) {
      const popoverSelector = `${inputSelector}-popover`
      let $advancedSelectInput = await t.context.$(inputSelector)
      await $advancedSelectInput.sendKeys(text)
      await t.context.driver.sleep(shortWaitMs) // give the advanced select some time to send the request (debounce!)
      let $advancedSelectSuggestion = await t.context.$(
        `${popoverSelector} tbody tr:first-child td input`
      )
      await $advancedSelectSuggestion.click()
      return $advancedSelectInput
    },
    async writeInForm(inputSelector, text) {
      let $meetingGoalInput = await t.context.$(inputSelector)
      await $meetingGoalInput.sendKeys(text)
    },
    async assertReportShowStatusText(t, text) {
      await t.context.assertElementText(
        t,
        await t.context.$(".report-show h4"),
        text
      )
    },
    async clickMyOrgLink() {
      let $myOrgLink = await t.context.$("#my-organization")
      await t.context.driver.wait(t.context.until.elementIsVisible($myOrgLink))
      await $myOrgLink.click()
      await t.context.waitForLoadingFinished()
    },
    async clickFormBottomSubmit() {
      let $formBottomSubmit = await t.context.$("#formBottomSubmit")
      await t.context.driver.wait(
        t.context.until.elementIsVisible($formBottomSubmit)
      )
      await $formBottomSubmit.click()
    },
    async clickPersonNameFromSupportedPositionsFieldset(personName) {
      let $supportedPositionsRows = await t.context.$$(
        "#supportedPositions table tbody tr"
      )
      for (let $row of $supportedPositionsRows) {
        let [$billetCell, $advisorCell] = await $row.findElements(By.css("td"))
        await t.context.driver.wait(until.elementIsVisible($billetCell))
        await $billetCell.getText()
        await t.context.driver.wait(until.elementIsVisible($advisorCell))
        let advisorText = await $advisorCell.getText()
        if (advisorText === personName) {
          let $advisorLink = await $advisorCell.findElement(By.css("a"))
          await t.context.driver.wait(until.elementIsVisible($advisorLink))
          await $advisorLink.click()
          await t.context.driver.wait(until.stalenessOf($advisorLink))
          return
        }
      }
      t.fail(
        `Could not find a row with person name = '${personName}'` +
          "in the supported positions table. " +
          "Please fix the database to be the way this test expects."
      )
    }
  }
})

// Shut down the browser when we are done.
test.afterEach.always(async t => {
  if (t.context.driver) {
    await t.context.driver.quit()
  }
})

/**
 * Technically speaking, everything should be wrapped in a wait() block to give
 * the the browser time to run JS to update the page. In practice, this does not
 * always seem to be necessary, since the JS can run very fast. If the tests are flaky,
 * this would be a good thing to investigate.
 *
 * Also, I suspect that we may see a bunch of stale element errors as React replaces
 * DOM nodes. To fix this, we should use a model of passing around CSS selectors instead
 * of element references, and always query for the element at the last possible moment.
 */

module.exports = test
module.exports.debugLog = debugLog
<commit_msg>Wait for loading to finish after advanced select<commit_after>let path = require("path")
let { URL } = require("url")
let test = require("ava")
let webdriver = require("selenium-webdriver")
let { By, until, Key } = webdriver
let moment = require("moment")
let _includes = require("lodash/includes")
let _isRegExp = require("lodash/isRegExp")
let chalk = require("chalk")

let capabilities = {}
let testEnv =
  (process.env.TRAVIS_TAG && "remote") || process.env.TEST_ENV || "local"
if (testEnv === "local") {
  // This gives us access to send Chrome commands.
  require("chromedriver")
} else {
  // Set capabilities for BrowserStack
  require("./keep-alive.js")
  capabilities = require("../../config/browserstack.config.js")
}

// Webdriver's promise manager only made sense before Node had async/await support.
// Now it's a deprecated legacy feature, so we should use the simpler native Node support instead.
webdriver.promise.USE_PROMISE_MANAGER = false

console.log(
  chalk.bold.cyan(
    `These tests assume that you have recently run ${path.resolve(
      __dirname,
      "..",
      "..",
      "..",
      "insertBaseData-mssql.sql"
    )} on your database instance`
  )
)

function debugLog(...args) {
  if (process.env.DEBUG_LOG === "true") {
    console.log(chalk.grey("[DEBUG]"), ...args)
  }
}

// We use the before hook to put helpers on t.context and set up test scaffolding.
test.beforeEach(t => {
  let builder = new webdriver.Builder()
  if (testEnv === "local") {
    let chrome = require("selenium-webdriver/chrome")
    builder = builder
      .forBrowser("chrome")
      .setChromeOptions(new chrome.Options().headless())
  } else {
    capabilities.name = t.title.replace(/^beforeEach hook for /, "")
    builder = builder
      .usingServer("http://hub-cloud.browserstack.com/wd/hub")
      .withCapabilities(capabilities)
  }
  t.context.driver = builder.build()

  let shortWaitMs = moment.duration(1, "seconds").asMilliseconds()
  let mediumWaitMs = moment.duration(3, "seconds").asMilliseconds()
  let longWaitMs = moment.duration(9, "seconds").asMilliseconds()

  t.context.By = By
  t.context.until = until
  t.context.shortWaitMs = shortWaitMs
  t.context.mediumWaitMs = mediumWaitMs
  t.context.longWaitMs = longWaitMs
  t.context.Key = Key

  // This method is a helper so we don't have to keep repeating the hostname.
  // Passing the authentication through the querystring is a hack so we can
  // pass the information along via window.fetch.
  t.context.get = async(pathname, userPw) => {
    let credentials = userPw || "erin"
    let urlToGet = `${process.env.SERVER_URL}${pathname}?user=${credentials}&pass=${credentials}`
    debugLog("Getting URL", urlToGet)
    await t.context.driver.get(urlToGet)
    await t.context.waitForLoadingFinished()

    // If we have a page-wide error message, we would like to cleanly fail the test on that.
    try {
      await t.context.driver.wait(
        until.elementTextIs(
          await t.context.$(".not-found-text", shortWaitMs),
          "There was an error processing this request. Please contact an administrator."
        ),
        shortWaitMs
      )
      throw new Error(
        "The API returned a 500. Do you need to restart the server?"
      )
    } catch (e) {
      // NoSuchElementError: If we didn't find the "not found text" element, then we are not seeing a page-wide failure.
      // StaleElementReferenceError: If we found the "not found text" element but it's no longer on the page,
      //      then we are not seeing a page-wide failure.
      // TimeoutError: If we found the "not found text" element, but the text was never the backend error message,
      //      then we are not seeing a page-wide failure.
      if (
        !_includes(
          ["NoSuchElementError", "StaleElementReferenceError", "TimeoutError"],
          e.name
        )
      ) {
        throw e
      }
    }
  }

  t.context.waitForLoadingFinished = async() => {
    await t.context.assertElementNotPresent(
      t,
      "span.loading",
      "Loading indicator should disappear",
      mediumWaitMs
    )
  }

  // For debugging purposes.
  t.context.waitForever = async() => {
    console.log(chalk.red("Waiting forever so you can debug..."))
    await t.context.driver.wait(() => {})
  }

  t.context.$ = async(cssSelector, timeoutMs) => {
    debugLog(`Find element: $('${cssSelector}')`)
    let waitTimeoutMs = timeoutMs || longWaitMs
    let locator = By.css(cssSelector)
    await t.context.driver.wait(
      until.elementLocated(locator),
      waitTimeoutMs,
      `Could not find element by css selector ${cssSelector} within ${waitTimeoutMs} milliseconds`
    )
    return t.context.driver.findElement(locator)
  }
  t.context.$$ = async(cssSelector, timeoutMs) => {
    debugLog(`Find elements: $$('${cssSelector}')`)
    let waitTimeoutMs = timeoutMs || longWaitMs
    let locator = By.css(cssSelector)
    await t.context.driver.wait(
      until.elementsLocated(locator),
      waitTimeoutMs,
      `Could not find elements by css selector ${cssSelector} within ${waitTimeoutMs} milliseconds`
    )
    return t.context.driver.findElements(locator)
  }

  // A helper method to combine waiting for an element to have rendered and then asserting on its contents.
  t.context.assertElementText = async(t, $elem, expectedText, message) => {
    try {
      let untilCondition = _isRegExp(expectedText)
        ? until.elementTextMatches($elem, expectedText)
        : until.elementTextIs($elem, expectedText)

      await t.context.driver.wait(untilCondition, longWaitMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not have the text we expected, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    let actualText = (await $elem.getText()).trim()
    if (_isRegExp(expectedText)) {
      t.regex(actualText, expectedText, actualText, message)
    } else {
      t.is(actualText, expectedText, message)
    }
  }

  t.context.assertElementTextIsInt = (t, $elem, message) =>
    t.context.assertElementText(t, $elem, /^\d+$/)

  t.context.assertElementNotPresent = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    let waitTimeoutMs = timeoutMs || longWaitMs
    try {
      await t.context.driver.wait(
        async() => {
          const loopDelay = 250
          try {
            return !(await t.context.$(cssSelector, loopDelay))
          } catch (e) {
            // Hilariously, when Selenium can't find an element, sometimes it throws TimeoutError,
            // and sometimes it throws NoSuchElementError.
            if (e.name === "TimeoutError" || e.name === "NoSuchElementError") {
              return true
            }
            throw e
          }
        },
        waitTimeoutMs,
        `Element was still present after ${waitTimeoutMs} milliseconds`
      )
    } catch (e) {
      if (e.name === "TimeoutError") {
        t.fail(
          `Element with css selector '${cssSelector}' was still present after ${waitTimeoutMs} milliseconds: ${message}`
        )
      } else {
        throw e
      }
    }
    t.pass(message || "Element was not present")
  }

  // A helper method to combine waiting for an element to have rendered and then asserting on its enabled status
  t.context.assertElementEnabled = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    let waitTimeoutMs = timeoutMs || longWaitMs
    try {
      var elem = await t.context.$(cssSelector, waitTimeoutMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not load, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    t.is(await elem.isEnabled(), true, message)
  }

  // A helper method to combine waiting for an element to have rendered and then asserting it's disabled status
  t.context.assertElementDisabled = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    let waitTimeoutMs = timeoutMs || longWaitMs
    try {
      var elem = await t.context.$(cssSelector, waitTimeoutMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not load, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    t.is(await elem.isEnabled(), false, message)
  }

  t.context.getCurrentPathname = async() => {
    let currentUrl = await t.context.driver.getCurrentUrl()
    return new URL(currentUrl).pathname
  }

  t.context.pageHelpers = {
    async goHomeAndThenToReportsPage() {
      await t.context.get("/")

      let $createButton = await t.context.$("#createButton")
      await $createButton.click()
    },
    async clickTodayButton() {
      let $todayButton = await t.context.driver.findElement(
        By.xpath('//button/span[text()="Today"]')
      )
      await $todayButton.click()
    },
    async clickNextMonthDate() {
      let $nextMonthButton = await t.context.driver.findElement(
        By.xpath('//button/span[icon="chevron-right"]')
      )
      await $nextMonthButton.click()
      let $nextMonthDate = await t.context.driver.findElement(
        By.xpath('//div[class="DayPicker-Day--selected"]')
      )
      await $nextMonthDate.click()
    },
    async chooseAdvancedSelectOption(inputSelector, text) {
      const popoverSelector = `${inputSelector}-popover`
      let $advancedSelectInput = await t.context.$(inputSelector)
      await $advancedSelectInput.sendKeys(text)
      await t.context.driver.sleep(shortWaitMs) // give the advanced select some time to send the request (debounce!)
      t.context.waitForLoadingFinished()
      let $advancedSelectSuggestion = await t.context.$(
        `${popoverSelector} tbody tr:first-child td input`
      )
      await $advancedSelectSuggestion.click()
      return $advancedSelectInput
    },
    async writeInForm(inputSelector, text) {
      let $meetingGoalInput = await t.context.$(inputSelector)
      await $meetingGoalInput.sendKeys(text)
    },
    async assertReportShowStatusText(t, text) {
      await t.context.assertElementText(
        t,
        await t.context.$(".report-show h4"),
        text
      )
    },
    async clickMyOrgLink() {
      let $myOrgLink = await t.context.$("#my-organization")
      await t.context.driver.wait(t.context.until.elementIsVisible($myOrgLink))
      await $myOrgLink.click()
      await t.context.waitForLoadingFinished()
    },
    async clickFormBottomSubmit() {
      let $formBottomSubmit = await t.context.$("#formBottomSubmit")
      await t.context.driver.wait(
        t.context.until.elementIsVisible($formBottomSubmit)
      )
      await $formBottomSubmit.click()
    },
    async clickPersonNameFromSupportedPositionsFieldset(personName) {
      let $supportedPositionsRows = await t.context.$$(
        "#supportedPositions table tbody tr"
      )
      for (let $row of $supportedPositionsRows) {
        let [$billetCell, $advisorCell] = await $row.findElements(By.css("td"))
        await t.context.driver.wait(until.elementIsVisible($billetCell))
        await $billetCell.getText()
        await t.context.driver.wait(until.elementIsVisible($advisorCell))
        let advisorText = await $advisorCell.getText()
        if (advisorText === personName) {
          let $advisorLink = await $advisorCell.findElement(By.css("a"))
          await t.context.driver.wait(until.elementIsVisible($advisorLink))
          await $advisorLink.click()
          await t.context.driver.wait(until.stalenessOf($advisorLink))
          return
        }
      }
      t.fail(
        `Could not find a row with person name = '${personName}'` +
          "in the supported positions table. " +
          "Please fix the database to be the way this test expects."
      )
    }
  }
})

// Shut down the browser when we are done.
test.afterEach.always(async t => {
  if (t.context.driver) {
    await t.context.driver.quit()
  }
})

/**
 * Technically speaking, everything should be wrapped in a wait() block to give
 * the the browser time to run JS to update the page. In practice, this does not
 * always seem to be necessary, since the JS can run very fast. If the tests are flaky,
 * this would be a good thing to investigate.
 *
 * Also, I suspect that we may see a bunch of stale element errors as React replaces
 * DOM nodes. To fix this, we should use a model of passing around CSS selectors instead
 * of element references, and always query for the element at the last possible moment.
 */

module.exports = test
module.exports.debugLog = debugLog
<|endoftext|>


<commit_before>// vim: set ts=2 sw=2:
"use strict";

let preloader = document.querySelector('.preloader');
let preloaderText = preloader.querySelector(':scope .preloader__text');
let the_physics = new Physics();
let the_longcat = new LongCat(document.querySelector('.longcat'), the_physics);
let sections = setupSections();

function setupSections() {
  const allSections = document.querySelectorAll('.section');
  let sections = [];

  sections[0] = new WallSection(allSections[0], the_physics);
  sections[1] = new EarthSection(allSections[1], the_physics);
  sections[2] = new Section(allSections[2], the_physics);  // street
  sections[3] = new BuildingsSection(allSections[3], the_physics);
  sections[4] = new Section(allSections[4], the_physics); // clouds
  sections[5] = new Section(allSections[5], the_physics); // aerspace one
  sections[6] = new InfiniteStartSection(allSections[6], the_physics); // infinite start

  return sections;
}

window.requestAnimationFrame = window.requestAnimationFrame
                            || window.mozRequestAnimationFrame
                            || window.webkitRequestAnimationFrame
                            || window.msRequestAnimationFrame
                            || function(cb) { window.setTimeout(cb, 1000/60); };

const score_el = document.querySelector('.score__number');
const highs_el = document.querySelector('.score__number--high');

// Rather use window.performance if it's available.
// It has sub-millisecond precision, while Date.now only has milliseconds.
const rtc = window.performance || Date;
let t0 = rtc.now();
let high_score = 0;

function mainloop() {
  let t = rtc.now();
  let dt = 0.001*(t - t0);  // from milliseconds to seconds

  // When the tab gets hidden, this animate loop is not called, which is good
  // as it pauses the game. But when the tab gets visible again, the dt is huge:
  // as large as the tab was hidden. We don't want this, as it undoes the pause.
  // so we only update on small dt.
  if (dt < 0.5) {
    dt = the_physics.update(dt);
    the_longcat.update(dt);

    sections.forEach(section => {
      section.update(dt);
    });

    // Update the titlebar according to the score.
    document.title = `Longcat is lo${'o'.repeat(the_physics.score)}ng!`

    // Update the score itself and the high-score.
    high_score = Math.max(high_score, the_physics.score);
    score_el.innerHTML = the_physics.score.toFixed(1);
    highs_el.innerHTML = high_score.toFixed(1);
  }

  // For the next timestep.
  t0 = t;
  window.requestAnimationFrame(mainloop);
}
window.requestAnimationFrame(mainloop);

function raise_longcat(ev) {
  // remove preloader
  if(preloader) {
    document.body.removeChild(preloader);
    preloader = false;
  }
  // Avoid stuff like drag/drop selection etc.
  ev.preventDefault();

  // Meow when we get starteeeed!
  if (the_physics.score == 0) {
      the_longcat.meow();
  }

  the_physics.speedup();
}

function init() {
  // preloader to instructions
  preloader.classList.add('preloader--loaded');
  preloaderText.classList.add('preloader__text--loaded');
  preloaderText.innerHTML = 'click or tap as fast as you can !!!!';

  // A click raises the longcat!
  window.addEventListener('mousedown', raise_longcat);
  window.addEventListener('touchstart', raise_longcat);

  // Tweet score.
  document.querySelector('.tweet').addEventListener('click', ev => {
    ev.preventDefault();
    window.open(
      `http://twitter.com/intent/tweet?text=Beat my highscore of ${high_score.toFixed(2)}km` +
      `&url=${encodeURIComponent("http://longcat.wtf")}` +
      `&hashtags=longcat,longcatwtf`,
      '_blank'
    );
  });
}

// Some cheats for testing
window.addEventListener('keyup', (ev) => {
  ev.preventDefault();
  if (ev.key == 'ArrowUp' || ev.key == 'Up') {
    the_physics.g = 0;
    the_physics.vy += 0.01;
  } else if (ev.key == 'ArrowDown' || ev.key == 'Down') {
    the_physics.g = 0;
    the_physics.vy -= 0.01;
  } else if (ev.key == '+' || ev.key == 'Add') {
    the_physics.vy = 1;
  } else if (ev.key == ' ') {
    the_physics.g = 0;
    the_physics.vy = 0;
    the_physics.a = 0;
  }
  console.log(ev.key)
})

window.addEventListener('keydown', (ev) => { ev.preventDefault(); });

window.onload = function() {
  init();
};
<commit_msg>Remove pseudo selector :scope in querySelector.<commit_after>// vim: set ts=2 sw=2:
"use strict";

let preloader = document.querySelector('.preloader');
let preloaderText = preloader.querySelector('.preloader__text');
let the_physics = new Physics();
let the_longcat = new LongCat(document.querySelector('.longcat'), the_physics);
let sections = setupSections();

function setupSections() {
  const allSections = document.querySelectorAll('.section');
  let sections = [];

  sections[0] = new WallSection(allSections[0], the_physics);
  sections[1] = new EarthSection(allSections[1], the_physics);
  sections[2] = new Section(allSections[2], the_physics);  // street
  sections[3] = new BuildingsSection(allSections[3], the_physics);
  sections[4] = new Section(allSections[4], the_physics); // clouds
  sections[5] = new Section(allSections[5], the_physics); // aerspace one
  sections[6] = new InfiniteStartSection(allSections[6], the_physics); // infinite start

  return sections;
}

window.requestAnimationFrame = window.requestAnimationFrame
                            || window.mozRequestAnimationFrame
                            || window.webkitRequestAnimationFrame
                            || window.msRequestAnimationFrame
                            || function(cb) { window.setTimeout(cb, 1000/60); };

const score_el = document.querySelector('.score__number');
const highs_el = document.querySelector('.score__number--high');

// Rather use window.performance if it's available.
// It has sub-millisecond precision, while Date.now only has milliseconds.
const rtc = window.performance || Date;
let t0 = rtc.now();
let high_score = 0;

function mainloop() {
  let t = rtc.now();
  let dt = 0.001*(t - t0);  // from milliseconds to seconds

  // When the tab gets hidden, this animate loop is not called, which is good
  // as it pauses the game. But when the tab gets visible again, the dt is huge:
  // as large as the tab was hidden. We don't want this, as it undoes the pause.
  // so we only update on small dt.
  if (dt < 0.5) {
    dt = the_physics.update(dt);
    the_longcat.update(dt);

    sections.forEach(section => {
      section.update(dt);
    });

    // Update the titlebar according to the score.
    document.title = `Longcat is lo${'o'.repeat(the_physics.score)}ng!`

    // Update the score itself and the high-score.
    high_score = Math.max(high_score, the_physics.score);
    score_el.innerHTML = the_physics.score.toFixed(1);
    highs_el.innerHTML = high_score.toFixed(1);
  }

  // For the next timestep.
  t0 = t;
  window.requestAnimationFrame(mainloop);
}
window.requestAnimationFrame(mainloop);

function raise_longcat(ev) {
  // remove preloader
  if(preloader) {
    document.body.removeChild(preloader);
    preloader = false;
  }
  // Avoid stuff like drag/drop selection etc.
  ev.preventDefault();

  // Meow when we get starteeeed!
  if (the_physics.score == 0) {
      the_longcat.meow();
  }

  the_physics.speedup();
}

function init() {
  // preloader to instructions
  preloader.classList.add('preloader--loaded');
  preloaderText.classList.add('preloader__text--loaded');
  preloaderText.innerHTML = 'click or tap as fast as you can !!!!';

  // A click raises the longcat!
  window.addEventListener('mousedown', raise_longcat);
  window.addEventListener('touchstart', raise_longcat);

  // Tweet score.
  document.querySelector('.tweet').addEventListener('click', ev => {
    ev.preventDefault();
    window.open(
      `http://twitter.com/intent/tweet?text=Beat my highscore of ${high_score.toFixed(2)}km` +
      `&url=${encodeURIComponent("http://longcat.wtf")}` +
      `&hashtags=longcat,longcatwtf`,
      '_blank'
    );
  });
}

// Some cheats for testing
window.addEventListener('keyup', (ev) => {
  ev.preventDefault();
  if (ev.key == 'ArrowUp' || ev.key == 'Up') {
    the_physics.g = 0;
    the_physics.vy += 0.01;
  } else if (ev.key == 'ArrowDown' || ev.key == 'Down') {
    the_physics.g = 0;
    the_physics.vy -= 0.01;
  } else if (ev.key == '+' || ev.key == 'Add') {
    the_physics.vy = 1;
  } else if (ev.key == ' ') {
    the_physics.g = 0;
    the_physics.vy = 0;
    the_physics.a = 0;
  }
  console.log(ev.key)
})

window.addEventListener('keydown', (ev) => { ev.preventDefault(); });

window.onload = function() {
  init();
};
<|endoftext|>


<commit_before>$(document).ready(function() {
    var sound_effect = new Audio("blip.ogg"); // buffers automatically when created

    console.log("document loaded");

    var score_limit = 70;
    //var score_limit = 20;
    var wordlist = ["foo", "bar", "pizza", "kalsonger", "ananas", "bananer", "kall", "varm"];
    var randomizer = 0;

    var team1_count = 0;
    var team2_count = 0;

    var current_team = 1;
    var countdown_counter = 60;
    //var countdown_counter = 5;

    var current_word = "foo";
    var correct_words = [];
    var dropped_words = [];
    var stolen_words = [];

    prepare_new_game();

    $(document).keypress(function(e){
        if ($("#activeroundpage").is(':visible')) {
            console.log("is visible");
            switch (e.keyCode) {
                case 40:
                    console.log('down');
                    $("#dropword").trigger("click");
                    break;
                case 38:
                    console.log('up');
                    break;
                case 37:
                    console.log('left');
                    if ($("#wordstolen").is(":visible")) {
                        $("#wordstolen").trigger("click");
                    }
                    break;
                case 39:
                    console.log('right');
                    $("#nextword").trigger("click");
                    break;
                default:
                    console.log('???');
                }
        } else {
            console.log("is NOT visible");
        }
    });

    function prepare_new_game() {
        $("#startpage").show();
        $("#winnerresultpage").hide();
        $("#roundpage").hide();
        $("#scorepage").hide();
        $("#scorelimit").val(score_limit);
    }

    $("#startnewgame-button").click(function() {
        score_limit = $("#scorelimit").val();
        console.log("score limit: "+score_limit);
        //var url = "ordlista.txt";
        //var url = "oneword.txt";
        var url = "swe.txt";
        $.ajax({
            url: url,
            success: function (data){
                console.log("done loading");
                wordlist = data.split('\n');
                // Skip last word which is empty because of the last newline
                wordlist.pop();
            }
        });
        start_new_game();
        return false;
    });

    function start_new_game() {
        $("#startpage").hide();
        $("#playpage").show();
        $("#scorepage").show();

        team1_count = 0;
        team2_count = 0;
        current_team = 1;

        prepare_new_round();
    }

    function prepare_new_round() {
        refresh_score();

        $("#startround").show();
        $("#roundpage").show();
        $("#activeroundpage").hide();
        $("#dropword").show();
        $("#wordstolen").hide();
        $("#roundresultpage").hide();

        $("#currentteam").html("Team "+current_team);
    }

    $("#startround").click(function() {
        start_round(current_team);
        return false;
    });

    function start_round(team_count) {
        console.log("Start round!");
        $("#startround").hide();
        $("#activeroundpage").show();

        countdown_counter = 5;
        correct_words = [];
        dropped_words = [];
        stolen_words = [];

        set_new_random_word();

        count_down();
        return false;
    }

    $("#nextword").click(function() {
        correct_words.push(current_word);
        if (countdown_counter > 0) {
            set_new_random_word();
        } else {
            $("#currentword").html("");
            show_results();
        }

        return false;
    });

    $("#dropword").click(function() {
        if (countdown_counter > 0) {
            dropped_words.push(current_word);
            set_new_random_word();
        } else {
            $("#currentword").html("");
            show_results();
        }

        return false;
    });

    $("#wordstolen").click(function() {
        stolen_words.push(current_word);
        $("#currentword").html("");
        show_results();

        return false;
    });

    function set_new_random_word() {
        current_word = get_random_word();
        $("#currentword").html(current_word);
    }

    function count_down() {
        countdown_counter = countdown_counter-1;
        console.log("Count down. Counter: "+countdown_counter);
        $("#timeleft").html(""+countdown_counter);
        if (countdown_counter > 0) {
            setTimeout(count_down, 1000);
        } else {
            finish_round();
        }
    }

    function finish_round() {
        sound_effect.play();
        $("#wordstolen").show();
    }

    function show_results() {
        $("#activeroundpage").hide();
        console.log("Correct words:");
        console.log(correct_words);

        console.log("Skipped words:");
        console.log(dropped_words);

        var correct_words_string = correct_words.join(", ");
        $("#correctwords").html(correct_words_string);
        $("#correctwordscount").val(correct_words.length);

        var dropped_words_string = dropped_words.join(", ");
        $("#droppedwords").html(dropped_words_string);
        $("#droppedwordscount").val(dropped_words.length);

        var stolen_words_string = stolen_words.join(", ");
        $("#stolenwords").html(stolen_words_string);
        $("#stolenwordscount").val(stolen_words.length);

        $("#roundresultpage").show();
    }

    function refresh_score() {
        $("#team1score").html(team1_count);
        $("#team2score").html(team2_count);
    }

    function next_team() {
        if (current_team == 1) {
            current_team = 2;
        } else {
            current_team = 1;
        }
    }

    $("#submitscore").click(function() {
        var correct = parseInt($("#correctwordscount").val());
        var dropped = parseInt($("#droppedwordscount").val());
        var total = correct-dropped;
        var other_team = parseInt($("#stolenwordscount").val());

        if (current_team == 1) {
            team1_count += total;
            team2_count += other_team;
        } else {
            team2_count += total;
            team1_count += other_team;
        }

        console.log("correct: "+correct);
        console.log("dropped: "+dropped);
        console.log("total: "+total);
        console.log("other_team: "+other_team);

        refresh_score();

        if (has_winner()) {
            console.log("Has winner");
            show_winner();
        } else {
            next_team();
            prepare_new_round();
        }

        return false;
    });

    function has_winner() {
        return (team1_count >= score_limit || team2_count >= score_limit);
    }

    function show_winner() {
        console.log("Show winner");
        var winner_team;
        if (team1_count >= score_limit) {
            winner_team = "Team 1";
        } else {
            winner_team = "Team 2";
        }

        $("#roundresultpage").hide();
        $("#winner").html(winner_team);
        $("#winnerresultpage").show();
    }

    $("#playagain-button").click(function() {
        prepare_new_game();
    });

    function get_random_word() {
        var word = wordlist[Math.floor(Math.random()*wordlist.length)];
        console.log("Random word: "+word);
        return word;
    }
});
<commit_msg>Reset counter to 60<commit_after>$(document).ready(function() {
    var sound_effect = new Audio("blip.ogg"); // buffers automatically when created

    console.log("document loaded");

    var score_limit = 70;
    //var score_limit = 20;
    var wordlist = ["foo", "bar", "pizza", "kalsonger", "ananas", "bananer", "kall", "varm"];
    var randomizer = 0;

    var team1_count = 0;
    var team2_count = 0;

    var current_team = 1;
    var countdown_counter = 60;
    //var countdown_counter = 5;

    var current_word = "foo";
    var correct_words = [];
    var dropped_words = [];
    var stolen_words = [];

    prepare_new_game();

    $(document).keypress(function(e){
        if ($("#activeroundpage").is(':visible')) {
            console.log("is visible");
            switch (e.keyCode) {
                case 40:
                    console.log('down');
                    $("#dropword").trigger("click");
                    break;
                case 38:
                    console.log('up');
                    break;
                case 37:
                    console.log('left');
                    if ($("#wordstolen").is(":visible")) {
                        $("#wordstolen").trigger("click");
                    }
                    break;
                case 39:
                    console.log('right');
                    $("#nextword").trigger("click");
                    break;
                default:
                    console.log('???');
                }
        } else {
            console.log("is NOT visible");
        }
    });

    function prepare_new_game() {
        $("#startpage").show();
        $("#winnerresultpage").hide();
        $("#roundpage").hide();
        $("#scorepage").hide();
        $("#scorelimit").val(score_limit);
    }

    $("#startnewgame-button").click(function() {
        score_limit = $("#scorelimit").val();
        console.log("score limit: "+score_limit);
        //var url = "ordlista.txt";
        //var url = "oneword.txt";
        var url = "swe.txt";
        $.ajax({
            url: url,
            success: function (data){
                console.log("done loading");
                wordlist = data.split('\n');
                // Skip last word which is empty because of the last newline
                wordlist.pop();
            }
        });
        start_new_game();
        return false;
    });

    function start_new_game() {
        $("#startpage").hide();
        $("#playpage").show();
        $("#scorepage").show();

        team1_count = 0;
        team2_count = 0;
        current_team = 1;

        prepare_new_round();
    }

    function prepare_new_round() {
        refresh_score();

        $("#startround").show();
        $("#roundpage").show();
        $("#activeroundpage").hide();
        $("#dropword").show();
        $("#wordstolen").hide();
        $("#roundresultpage").hide();

        $("#currentteam").html("Team "+current_team);
    }

    $("#startround").click(function() {
        start_round(current_team);
        return false;
    });

    function start_round(team_count) {
        console.log("Start round!");
        $("#startround").hide();
        $("#activeroundpage").show();

        countdown_counter = 60;
        correct_words = [];
        dropped_words = [];
        stolen_words = [];

        set_new_random_word();

        count_down();
        return false;
    }

    $("#nextword").click(function() {
        correct_words.push(current_word);
        if (countdown_counter > 0) {
            set_new_random_word();
        } else {
            $("#currentword").html("");
            show_results();
        }

        return false;
    });

    $("#dropword").click(function() {
        if (countdown_counter > 0) {
            dropped_words.push(current_word);
            set_new_random_word();
        } else {
            $("#currentword").html("");
            show_results();
        }

        return false;
    });

    $("#wordstolen").click(function() {
        stolen_words.push(current_word);
        $("#currentword").html("");
        show_results();

        return false;
    });

    function set_new_random_word() {
        current_word = get_random_word();
        $("#currentword").html(current_word);
    }

    function count_down() {
        countdown_counter = countdown_counter-1;
        console.log("Count down. Counter: "+countdown_counter);
        $("#timeleft").html(""+countdown_counter);
        if (countdown_counter > 0) {
            setTimeout(count_down, 1000);
        } else {
            finish_round();
        }
    }

    function finish_round() {
        sound_effect.play();
        $("#wordstolen").show();
    }

    function show_results() {
        $("#activeroundpage").hide();
        console.log("Correct words:");
        console.log(correct_words);

        console.log("Skipped words:");
        console.log(dropped_words);

        var correct_words_string = correct_words.join(", ");
        $("#correctwords").html(correct_words_string);
        $("#correctwordscount").val(correct_words.length);

        var dropped_words_string = dropped_words.join(", ");
        $("#droppedwords").html(dropped_words_string);
        $("#droppedwordscount").val(dropped_words.length);

        var stolen_words_string = stolen_words.join(", ");
        $("#stolenwords").html(stolen_words_string);
        $("#stolenwordscount").val(stolen_words.length);

        $("#roundresultpage").show();
    }

    function refresh_score() {
        $("#team1score").html(team1_count);
        $("#team2score").html(team2_count);
    }

    function next_team() {
        if (current_team == 1) {
            current_team = 2;
        } else {
            current_team = 1;
        }
    }

    $("#submitscore").click(function() {
        var correct = parseInt($("#correctwordscount").val());
        var dropped = parseInt($("#droppedwordscount").val());
        var total = correct-dropped;
        var other_team = parseInt($("#stolenwordscount").val());

        if (current_team == 1) {
            team1_count += total;
            team2_count += other_team;
        } else {
            team2_count += total;
            team1_count += other_team;
        }

        console.log("correct: "+correct);
        console.log("dropped: "+dropped);
        console.log("total: "+total);
        console.log("other_team: "+other_team);

        refresh_score();

        if (has_winner()) {
            console.log("Has winner");
            show_winner();
        } else {
            next_team();
            prepare_new_round();
        }

        return false;
    });

    function has_winner() {
        return (team1_count >= score_limit || team2_count >= score_limit);
    }

    function show_winner() {
        console.log("Show winner");
        var winner_team;
        if (team1_count >= score_limit) {
            winner_team = "Team 1";
        } else {
            winner_team = "Team 2";
        }

        $("#roundresultpage").hide();
        $("#winner").html(winner_team);
        $("#winnerresultpage").show();
    }

    $("#playagain-button").click(function() {
        prepare_new_game();
    });

    function get_random_word() {
        var word = wordlist[Math.floor(Math.random()*wordlist.length)];
        console.log("Random word: "+word);
        return word;
    }
});
<|endoftext|>


<commit_before>/*
 * This file is part of Search NEU and licensed under AGPL3.
 * See the license file in the root folder for details.
 */

import fs from 'fs-extra';
import path from 'path';
import moment from 'moment';
import macros from '../macros';

class SitemapGenerator {
  async go(termDump, mergedEmployees) {
    // Items to link to.
    // The part after the https://searchneu.com/
    const items = [];

    // Figure out what semester it currently happening and make the site map from that one.
    // If we are in between semesters, use the next one to occur.
    // If all the semesters occurred in the past, pick the one closest to the current date.

    const today = moment().diff(0, 'day');
    macros.log('It has been ', today, 'days since epoch.');

    let currentTerm;

    // Lets not spam the console if there are non-neu classes here.
    let foundNonNEUClass = false;

    for (const term of termDump.terms) {
      if (term.host !== 'neu.edu') {
        if (!term.host.startsWith('neu.edu')) {
          if (!foundNonNEUClass) {
            macros.error('Not adding non-NEU class to the index! Update this when we get another domain and redo the routing for the new domain.');
          }
          foundNonNEUClass = true;
          continue;
        }

        continue;
      }

      if (term.startDate < today && term.endDate > today) {
        currentTerm = term.termId;
        break;
      }
    }

    // Find the next term to occur.
    if (!currentTerm) {
      let minDaysSinceNextTerm;
      for (const term of termDump.terms) {
        if (term.host !== 'neu.edu') {
          continue;
        }

        if (term.startDate < today) {
          continue;
        }

        const daysUntilThisTermStarts = term.startDate - today;
        if (!minDaysSinceNextTerm || daysUntilThisTermStarts < minDaysSinceNextTerm) {
          minDaysSinceNextTerm = daysUntilThisTermStarts;
          currentTerm = term.termId;
        }
      }
    }

    // If all the terms have already ended, find the one that most recently ended.
    if (!currentTerm) {
      let maxEndDate;

      for (const term of termDump.terms) {
        if (term.host !== 'neu.edu') {
          continue;
        }

        if (!maxEndDate || term.endDate > maxEndDate) {
          maxEndDate = term.endDate;
          currentTerm = term.termId;
        }
      }
    }

    macros.log('The current term is:', currentTerm);

    // Add the subjects
    for (const subject of termDump.subjects) {
      if (subject.termId !== currentTerm) {
        continue;
      }

      items.push(subject.subject);
      items.push(subject.text);
    }

    // Add the classes
    for (const aClass of termDump.classes) {
      if (aClass.termId !== currentTerm) {
        continue;
      }

      if (aClass.host !== 'neu.edu') {
        continue;
      }

      items.push(`${aClass.subject} ${aClass.classId}`);
      items.push(aClass.name);
    }

    // Add the employees
    for (const employee of mergedEmployees) {
      items.push(employee.name);
    }

    // Convert the items to urls and put them inside xml
    const xml = ['<?xml version="1.0" encoding="UTF-8"?>', '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'];
    for (const item of items) {
      xml.push('  <url>');
      xml.push(`    <loc>https://searchneu.com/${encodeURIComponent(item)}</loc>`);
      xml.push('  </url>');
    }
    xml.push('</urlset>');

    const output = xml.join('\n');

    await fs.writeFile(path.join('public', 'sitemap.xml'), output);
  }
}

export default new SitemapGenerator();
<commit_msg>fix a bug with the sitemap.xml<commit_after>/*
 * This file is part of Search NEU and licensed under AGPL3.
 * See the license file in the root folder for details.
 */

import fs from 'fs-extra';
import path from 'path';
import moment from 'moment';
import macros from '../macros';

class SitemapGenerator {
  async go(termDump, mergedEmployees) {
    // Items to link to.
    // The part after the https://searchneu.com/
    let items = [];

    // Figure out what semester it currently happening and make the site map from that one.
    // If we are in between semesters, use the next one to occur.
    // If all the semesters occurred in the past, pick the one closest to the current date.

    const today = moment().diff(0, 'day');
    macros.log('It has been ', today, 'days since epoch.');

    let currentTerm;

    // Lets not spam the console if there are non-neu classes here.
    let foundNonNEUClass = false;

    for (const term of termDump.terms) {
      if (term.host !== 'neu.edu') {
        if (!term.host.startsWith('neu.edu')) {
          if (!foundNonNEUClass) {
            macros.error('Not adding non-NEU class to the index! Update this when we get another domain and redo the routing for the new domain.');
          }
          foundNonNEUClass = true;
          continue;
        }

        continue;
      }

      if (term.startDate < today && term.endDate > today) {
        currentTerm = term.termId;
        break;
      }
    }

    // Find the next term to occur.
    if (!currentTerm) {
      let minDaysSinceNextTerm;
      for (const term of termDump.terms) {
        if (term.host !== 'neu.edu') {
          continue;
        }

        if (term.startDate < today) {
          continue;
        }

        const daysUntilThisTermStarts = term.startDate - today;
        if (!minDaysSinceNextTerm || daysUntilThisTermStarts < minDaysSinceNextTerm) {
          minDaysSinceNextTerm = daysUntilThisTermStarts;
          currentTerm = term.termId;
        }
      }
    }

    // If all the terms have already ended, find the one that most recently ended.
    if (!currentTerm) {
      let maxEndDate;

      for (const term of termDump.terms) {
        if (term.host !== 'neu.edu') {
          continue;
        }

        if (!maxEndDate || term.endDate > maxEndDate) {
          maxEndDate = term.endDate;
          currentTerm = term.termId;
        }
      }
    }

    macros.log('The current term is:', currentTerm);

    // Add the subjects
    for (const subject of termDump.subjects) {
      if (subject.termId !== currentTerm) {
        continue;
      }

      items.push(subject.subject);
      items.push(subject.text);
    }

    // Add the classes
    for (const aClass of termDump.classes) {
      if (aClass.termId !== currentTerm) {
        continue;
      }

      if (aClass.host !== 'neu.edu') {
        continue;
      }

      items.push(`${aClass.subject} ${aClass.classId}`);
      items.push(aClass.name);
    }

    // Add the employees
    for (const employee of mergedEmployees) {
      items.push(employee.name);
    }

    // Remove duplicates
    items = Array.from(new Set(items));

    // Convert the items to urls and put them inside xml
    const xml = ['<?xml version="1.0" encoding="UTF-8"?>', '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'];
    for (const item of items) {
      xml.push('  <url>');
      xml.push(`    <loc>https://searchneu.com/${encodeURIComponent(item)}</loc>`);
      xml.push('  </url>');
    }
    xml.push('</urlset>');

    const output = xml.join('\n');

    await fs.writeFile(path.join('public', 'sitemap.xml'), output);
  }
}

export default new SitemapGenerator();
<|endoftext|>


<commit_before>// Requires
var buttons = require('sdk/ui/button/action');
var tabs = require('sdk/tabs');
var contextMenu = require('sdk/context-menu');
var data = require("sdk/self").data;
var _ = require('sdk/l10n').get;
var prefs = require('sdk/simple-prefs').prefs;
var Request = require("sdk/request").Request;
var URL = require("sdk/url").URL;
var privateBrowsing = require("sdk/private-browsing");

var workerTab = undefined;
var worker = undefined;

/**
 * @param string url URL which should be checked with Wayback Machine for past snapshots.
 */
var openInWaybackMachine = function (url) {
    tabs.open({
        url: data.url("worker-page.html"),
        isPrivate: privateBrowsing.isPrivate(tabs.activeTab),
        onLoad: function (tab) {
            workerTab = tab;
            worker = tab.attach({contentScriptFile: data.url('show-loading-page-info.js')});
            worker.port.emit('init', {icon_url: data.url('icon-64.png'), title: _("msg_requesting_snapshot", url)});
            Request({
                url: prefs.wayback_machine_url + encodeURIComponent(url),
                onComplete: processWaybackMachineResponse
            }).get();
        }
    });
};

var processWaybackMachineResponse = function (response) {
    if (response.status !== 200)
        return notifyAboutFailedRequest(response);

    if (!response.json
        || !response.json.hasOwnProperty('archived_snapshots'))
        return notifyAboutMalformedResponse(response);

    if (response.json.archived_snapshots.closest
        && !response.json.archived_snapshots.closest.url)
        return notifyAboutMalformedResponse(response);

    if (!response.json.archived_snapshots.closest)
        return notifyAboutNoSnapshotToReturnTo();

    return workerTab.url = response.json.archived_snapshots.closest.url;
};

var notifyAboutFailedRequest = function (response) {
    worker.port.emit('notify', {
        title: _("msg_error_connecting_title"),
        text: _("msg_error_connecting_text", response.status, response.text),
        class: 'error-connecting'
    });
};

var notifyAboutMalformedResponse = function (response) {
    worker.port.emit('notify', {
        title: _("msg_malformed_response_title"),
        text: _("msg_malformed_response_text", response.text),
        class: "malformed-data.png"
    });
};

var notifyAboutNoSnapshotToReturnTo = function () {
    worker.port.emit('notify', {
        title: _("msg_no_snapshot_title"),
        text: _("msg_no_snapshot_text"),
        class: "no-snapshot"
    });
};

// 1. Button - open current page in Wayback Machine
var button = buttons.ActionButton({
	id: "btn-open-in-wayback-machine",
	label: _("button_label"),
	icon: {
		'16': './icon-16.png',
		'32': './icon-32.png',
		'64': './icon-64.png'
	},
  onClick: function () {
      var current_url = (new URL(tabs.activeTab.url)).toString(); // poor man's sanitization
      openInWaybackMachine(current_url);
  }
});

// 2. Context menu - open link in Wayback Machine
var menuItem = contextMenu.Item({
	label: _("context_menu_item_label"),
	context: contextMenu.SelectorContext('a[href]'),
	contentScriptFile: data.url("context-menu-content-script.js"),
  onMessage: function (href_url) {
      var url = (new URL(href_url)).toString(); // poor man's sanitization.
      openInWaybackMachine(url);
  }
});

<commit_msg>Critical fix to not reload endlessly after getting latest snapshot.<commit_after>// Requires
var buttons = require('sdk/ui/button/action');
var tabs = require('sdk/tabs');
var contextMenu = require('sdk/context-menu');
var data = require("sdk/self").data;
var _ = require('sdk/l10n').get;
var prefs = require('sdk/simple-prefs').prefs;
var Request = require("sdk/request").Request;
var URL = require("sdk/url").URL;
var privateBrowsing = require("sdk/private-browsing");

var workerTab = undefined;
var worker = undefined;

/**
 * @param string url URL which should be checked with Wayback Machine for past snapshots.
 */
var openInWaybackMachine = function (url) {
    tabs.once("load", function (tab) {
        workerTab = tab;
        worker = tab.attach({contentScriptFile: data.url('show-loading-page-info.js')});
        worker.port.emit('init', {icon_url: data.url('icon-64.png'), title: _("msg_requesting_snapshot", url)});
        Request({
            url: prefs.wayback_machine_url + encodeURIComponent(url),
            onComplete: processWaybackMachineResponse
        }).get();
    });
    tabs.open({
        url: data.url("worker-page.html"),
        isPrivate: privateBrowsing.isPrivate(tabs.activeTab)
    });
};

var processWaybackMachineResponse = function (response) {
    if (response.status !== 200)
        notifyAboutFailedRequest(response);

    if (!response.json
        || !response.json.hasOwnProperty('archived_snapshots'))
        notifyAboutMalformedResponse(response);

    if (response.json.archived_snapshots.closest
        && !response.json.archived_snapshots.closest.url)
        notifyAboutMalformedResponse(response);

    if (!response.json.archived_snapshots.closest)
        notifyAboutNoSnapshotToReturnTo();

    workerTab.url = response.json.archived_snapshots.closest.url;
};

var notifyAboutFailedRequest = function (response) {
    worker.port.emit('notify', {
        title: _("msg_error_connecting_title"),
        text: _("msg_error_connecting_text", response.status, response.text),
        class: 'error-connecting'
    });
};

var notifyAboutMalformedResponse = function (response) {
    worker.port.emit('notify', {
        title: _("msg_malformed_response_title"),
        text: _("msg_malformed_response_text", response.text),
        class: "malformed-data.png"
    });
};

var notifyAboutNoSnapshotToReturnTo = function () {
    worker.port.emit('notify', {
        title: _("msg_no_snapshot_title"),
        text: _("msg_no_snapshot_text"),
        class: "no-snapshot"
    });
};

// 1. Button - open current page in Wayback Machine
var button = buttons.ActionButton({
	id: "btn-open-in-wayback-machine",
	label: _("button_label"),
	icon: {
		'16': './icon-16.png',
		'32': './icon-32.png',
		'64': './icon-64.png'
	},
  onClick: function () {
      var current_url = (new URL(tabs.activeTab.url)).toString(); // poor man's sanitization
      openInWaybackMachine(current_url);
  }
});

// 2. Context menu - open link in Wayback Machine
var menuItem = contextMenu.Item({
	label: _("context_menu_item_label"),
	context: contextMenu.SelectorContext('a[href]'),
	contentScriptFile: data.url("context-menu-content-script.js"),
  onMessage: function (href_url) {
      var url = (new URL(href_url)).toString(); // poor man's sanitization.
      openInWaybackMachine(url);
  }
});

<|endoftext|>