<commit_before>'use strict';

/* Controllers */

(function(){

    var errorMessages = {
        numberRequired: 'A whole number bigger than zero is required.',
        numberTooSmall: 'The number must be bigger than zero.',
        sampleCountTooLarge: 'The sample count must be smaller than the total count.',
        seedRequired: 'A random seed is required.'
    };

    var samplerControllers = angular.module('freeSamplerApp.controllers.form', [
        'freeSamplerApp.services'
    ]);

    // TODO: determine whether and where I need to use this function.
    //
    // Return whether an input element is empty.
    function isEmpty(inputValue) {
        // For input type "number", an empty input element can result
        // in an input value of null.
        if ((inputValue === null) || (inputValue === '')) {
            return true;
        }
        return false;
    }

    // Return an error object.
    function makeError(message, related) {
        return {
            message: message,
            related: related
        };
    }

    // Return an object with either an error message or the parsed integer.
    //
    // Note that with input type "number", the input value may already be
    // converted to a number and not be a string.
    //
    // Moreover, if the user supplied "-1" and the input element has
    // min="1", then input[key] will be undefined (and spellsInt()
    // will return NaN) for at least some browsers.  Thus, we include
    // the full helpful error message if the parsed integer is NaN.
    function validateNumber(spellsInt, inputValue) {
        var n = spellsInt(inputValue);
        if (isNaN(n)) {
            return {error: errorMessages.numberRequired};
        }
        if (n < 1) {
            return {error: errorMessages.numberTooSmall};
        }
        return {value: n};
    }

    // Return whether the form has an error up to this point.
    //
    // Params:
    //   parse: a function that accepts an input value and returns
    //     an object containing a parsed value or error.
    function handleInput(parseInput, form, parsed, inputLabel, hasError) {
        var result = parseInput(form.input[inputLabel]);

        // TODO: confirm this value for key not found.
        if (result.error) {
            var error = result.error;
            var related = error.related;

            if (form.errors[inputLabel] !== result.error) {
                form.errors[inputLabel] = result.error;
            }
            if (related) {
                for (var i = 0, len = related.length; i < len; i++) {
                    form.relatedErrors.push(related[i]);
                }
            }
            hasError = true;
        } else {
            parsed[inputLabel] = result.value;
            // It's okay to delete if the property does not exist.
            delete form.errors[inputLabel];
        }

        return hasError;
    }

    // Return an object containing the parsed input fields, or false if
    // form validation found an error.
    function validateForm($log, spellsInt, form) {
        var hasError = false,
            parsed = {},
            result,
            sampleCount,
            sampleCountError,
            seedError,
            totalCount,
            totalCountError;

        var input = form.input;
        var seed = input.seed;

        result = validateNumber(spellsInt, input.sampleCount);

        function parseSeed(inputValue) {
            var result = {};
            if (!inputValue) {
                result.error = errorMessages.seedRequired;
            } else {
                result.value = inputValue;
            }
            return result;
        }

        function parseNumber(inputValue) {
            return validateNumber(spellsInt, inputValue);
        }

        function parseSampleCount(inputValue) {
            var result = parseNumber(inputValue);
            if ((result.value !== undefined) &&
                (parsed.totalCount !== undefined) &&
                (result.value > parsed.totalCount)) {
                result.error = errorMessages.sampleCountTooLarge;
            }
            return result;
        }

        hasError = handleInput(parseSeed, form, parsed, 'seed', hasError);
        hasError = handleInput(parseNumber, form, parsed, 'totalCount', hasError);
        hasError = handleInput(parseSampleCount, form, parsed, 'sampleCount', hasError);

        return hasError ? false : parsed;
    }

    // TODO: move this to services.js?
    function showSamples(getSamplesUnique, output, seed, totalCount, sampleCount) {
        var result = getSamplesUnique(seed, totalCount, sampleCount);

        var uniqueItems = result[0];
        var sortedItems = uniqueItems.concat();  // make a copy.
        sortedItems.sort(function(a, b) {
            return a - b;
        });

        output.allItems = result[1];
        output.uniqueItems = uniqueItems;
        output.sortedItems = sortedItems;
    }

    /**
     * @ngdoc function
     * @name freeSamplerApp.controller:MainCtrl
     * @description
     * # MainCtrl
     * Controller of the freeSamplerApp
     *
     * We do not perform form validation on ng-keyup, ng-change, or even
     * ng-blur.  This is because we take the philosophy that the user
     * should be able to continue working on the form undisturbed until
     * pressing the submit button, which sends the message that the user
     * is finished.
     *     However, we do clear error messages on ng-change because that
     * signifies that the user has started to correct the error.  We also
     * clear errors on related input elements whose error message could
     * potentially change as a result (e.g. the error message saying that
     * the sample count is larger than the total number of items).
     *
     */
    samplerControllers.controller('MainCtrl', ['$log', '$scope', '$window',
      'getSamplesUnique', 'spellsInt',
      function ($log, $scope, $window, getSamplesUnique, spellsInt) {

        var errors = {};

        // Initialize the model.
        $scope.form = {};
        $scope.form.errors = errors;
        $scope.form.input = {};
        $scope.form.relatedErrors = {};
        $scope.output = {};

        $scope.seedChanged = function() {
            delete errors.seed;
        };

        $scope.sampleCountChanged = function() {
            delete errors.sampleCount;
        };

        $scope.totalCountChanged = function() {
            delete errors.totalCount;
        };

        $scope.submit = function() {
            var result = validateForm($log, spellsInt, $scope.form);
            if (!result) {
                return;
            }
            showSamples(getSamplesUnique, $scope.output, result.seed,
                        result.totalCount, result.sampleCount);
        };

    }]);

})();
<commit_msg>Continue work on form validation.<commit_after>'use strict';

/* Controllers */

(function(){

    var errorMessages = {
        numberRequired: 'A whole number bigger than zero is required.',
        numberTooSmall: 'The number must be bigger than zero.',
        sampleCountTooLarge: 'The sample count must be smaller than the total count.',
        seedRequired: 'A random seed is required.'
    };

    var samplerControllers = angular.module('freeSamplerApp.controllers.form', [
        'freeSamplerApp.services'
    ]);

    // TODO: determine whether and where I need to use this function.
    //
    // Return whether an input element is empty.
    function isEmpty(inputValue) {
        // For input type "number", an empty input element can result
        // in an input value of null.
        if ((inputValue === null) || (inputValue === '')) {
            return true;
        }
        return false;
    }

    // Return an error object.
    function makeError(message, related) {
        return {
            message: message,
            related: related
        };
    }

    function parseSeed(inputValue) {
        var result = {};
        if (!inputValue) {
            result.error = makeError(errorMessages.seedRequired);
        } else {
            result.value = inputValue;
        }
        return result;
    }

    // Return an object with either an error message or the parsed integer.
    //
    // Note that with input type "number", the input value may already be
    // converted to a number and not be a string.
    //
    // Moreover, if the user supplied "-1" and the input element has
    // min="1", then input[key] will be undefined (and spellsInt()
    // will return NaN) for at least some browsers.  Thus, we include
    // the full helpful error message if the parsed integer is NaN.
    function validateNumber(spellsInt, inputValue) {
        var result = {};
        var n = spellsInt(inputValue);
        if (isNaN(n)) {
            result.error = makeError(errorMessages.numberRequired);
        } else if (n < 1) {
            result.error = makeError(errorMessages.numberTooSmall);
        } else {
            result.value = n;
        }
        return result;
    }

    // Return whether the form has an error up to this point.
    //
    // Params:
    //   parse: a function that accepts an input value and returns
    //     an object containing a parsed value or error.
    function handleInput($log, parseInput, form, parsed, inputLabel, hasError) {
        var result = parseInput(form.input[inputLabel]);

        if (result.error) {
            var errorText = result.error.message;
            var related = result.error.related;

            if (form.errors[inputLabel] !== errorText) {
                form.errors[inputLabel] = errorText;
            }
            if (related !== undefined) {
                for (var i = 0, len = related.length; i < len; i++) {
                    form.relatedErrors.push(related[i]);
                }
            }
            hasError = true;
        } else {
            parsed[inputLabel] = result.value;
            // It's okay to delete if the property does not exist.
            delete form.errors[inputLabel];
        }

        return hasError;
    }

    // Return an object containing the parsed input fields, or false if
    // form validation found an error.
    function validateForm($log, spellsInt, form) {
        var hasError = false,
            parsed = {},
            result;

        var input = form.input;

        result = validateNumber(spellsInt, input.sampleCount);

        function parseNumber(inputValue) {
            return validateNumber(spellsInt, inputValue);
        }

        function parseSampleCount(inputValue) {
            var result = parseNumber(inputValue);
            if ((result.value !== undefined) &&
                (parsed.totalCount !== undefined) &&
                (result.value > parsed.totalCount)) {
                result.error = makeError(errorMessages.sampleCountTooLarge, ['totalCount']);
            }
            return result;
        }

        hasError = handleInput($log, parseSeed, form, parsed, 'seed', hasError);
        hasError = handleInput($log, parseNumber, form, parsed, 'totalCount', hasError);
        hasError = handleInput($log, parseSampleCount, form, parsed, 'sampleCount', hasError);

        return hasError ? false : parsed;
    }

    // TODO: move this to services.js?
    function showSamples(getSamplesUnique, output, seed, totalCount, sampleCount) {
        var result = getSamplesUnique(seed, totalCount, sampleCount);

        var uniqueItems = result[0];
        var sortedItems = uniqueItems.concat();  // make a copy.
        sortedItems.sort(function(a, b) {
            return a - b;
        });

        output.allItems = result[1];
        output.uniqueItems = uniqueItems;
        output.sortedItems = sortedItems;
    }

    /**
     * @ngdoc function
     * @name freeSamplerApp.controller:MainCtrl
     * @description
     * # MainCtrl
     * Controller of the freeSamplerApp
     *
     * We do not perform form validation on ng-keyup, ng-change, or even
     * ng-blur.  This is because we take the philosophy that the user
     * should be able to continue working on the form undisturbed until
     * pressing the submit button, which sends the message that the user
     * is finished.
     *     However, we do clear error messages on ng-change because that
     * signifies that the user has started to correct the error.  We also
     * clear errors on related input elements whose error message could
     * potentially change as a result (e.g. the error message saying that
     * the sample count is larger than the total number of items).
     *
     */
    samplerControllers.controller('MainCtrl', ['$log', '$scope', '$window',
      'getSamplesUnique', 'spellsInt',
      function ($log, $scope, $window, getSamplesUnique, spellsInt) {

        // Initialize the model.
        var form = {};
        $scope.form = form;
        $scope.form.errors = {};
        $scope.form.input = {};
        $scope.form.relatedErrors = {};
        $scope.output = {};

        $scope.onInputChange = function(inputLabel) {
            var errors = form.errors;
            var related = form.relatedErrors[inputLabel];
            if (related !== undefined) {
                for (var i = 0, len = related.length; i < len; i++) {
                    delete errors[related[i]];
                }
                // We only need to clear related errors once.
                delete form.relatedErrors[inputLabel];
            }
            delete errors[inputLabel];
        };

        $scope.submit = function() {
            var result = validateForm($log, spellsInt, form);
            if (!result) {
                return;
            }
            showSamples(getSamplesUnique, $scope.output, result.seed,
                        result.totalCount, result.sampleCount);
        };

    }]);

})();
<|endoftext|>


<commit_before>import moment from 'moment';

Meteor.methods({
  getAllResidentIds () {
    // TODO: determine how to secure this method to prevent client abuse

    // Get all residents
    const residents = Residents.find().fetch();

    // Create an array of resident IDs
    const residentIds = _.map(residents, function (resident) {
      return resident._id;
    });

    return residentIds;
  },
  getResidentActivitiesByType ({ residentId, activityTypeId, period }) {
    // Date period ago
    const activityPeriodStart = moment().subtract(period, "days").toDate();

    // Date today
    const now = new Date();

    // Get all activities of a specific type involving resident
    // make sure activities are in the past (i.e. not planned)
    //  sort in reverse order by activity date
    const activities = Activities.find({
      'residentIds': residentId,
      'activityTypeId': activityTypeId,
      activityDate: {$gte: activityPeriodStart, $lte: now}},
      {sort : {activityDate:  -1}
    }).fetch();

    if (activities) {
      return activities;
    }
  },
  getCountOfResidentActivitiesByType ({ residentId, activityTypeId, period }) {
    const activities = Meteor.call('getResidentActivitiesByType', { residentId, activityTypeId, period });

    // Placeholder for sum of activities
    let activityCount = 0;

    if (activities) {
      // Count the resident activity
      activityCount = activities.length;
    }

    return activityCount;
  },
  getAllResidentSelectOptions () {
    /*
    return an array of resident objects in the form of:
       label: firstName lastInitial
       value: residentId
   */

   // Get all residents
   const residents = Residents.find().fetch();

   // Create array of resident select options
   const residentSelectOptions = _.map(residents, (resident) => {
     return {
       label: `${resident.firstName} ${resident.lastInitial}`,
       value: resident._id,
     }
   });

   return residentSelectOptions;
  },
  getMinutesOfResidentActivitiesByType ({ residentId, activityTypeId, period }) {
    // Date period ago
    const activityPeriodStart = moment().subtract(period, "days").toDate();

    // Date today
    const now = new Date();

    const residentActivities = Activities.find(
      {
        residentIds: residentId,
        activityTypeId: activityTypeId,
        activityDate: {$gte: activityPeriodStart, $lte: now}
        },
      {
        sort : { activityDate:  -1 }
      }).fetch();

    // Sum of duration for particular resident & activity
    return residentActivities.reduce((prevResult, document) => prevResult + document.duration, 0)
  },
  residentNamesGroupedtByHomes(){

    // Get list of homes, sorted alphabetically
    const homes = Homes.find({}, { sort: { name: 1 } }).fetch();

    // Create an array residents grouped by home
    const residentsSelectOptions = _.map(homes, function (home) {
      // Get home ID
      const homeId = home._id;

      // do not show departed residents
      const notDeparted = {
        moveOut: {
          $exists: false
        }
      };

      // Sort by first name in alphabetical order
      const sort = { firstName: 1 };

      // Get a list of residents for current home
      const homeResidents = Residencies.find(
        { homeId, ...notDeparted },
        { sort }
      ).fetch().map(function (resident) {
        const residentId = resident.residentId
        const residentDet = Residents.findOne(residentId)

        const fullName = residentDet.fullName()
        return { ...resident, fullName }
      });

      // Create an object containing a home and its residents
      const homeGroup = {
        optgroup: home.name,
        options: _.map(homeResidents, function (resident) {

          // Create an object containing the resident name and ID
          const residentObject = {
            value: resident.residentId,
            label: resident.fullName
          };

          return residentObject;
        })
      };

      return homeGroup;
    });

    return residentsSelectOptions;
  }
});
<commit_msg>adding sort to filter feature<commit_after>import moment from 'moment';

Meteor.methods({
  getAllResidentIds () {
    // TODO: determine how to secure this method to prevent client abuse

    // Get all residents
    const residents = Residents.find().fetch();

    // Create an array of resident IDs
    const residentIds = _.map(residents, function (resident) {
      return resident._id;
    });

    return residentIds;
  },
  getResidentActivitiesByType ({ residentId, activityTypeId, period }) {
    // Date period ago
    const activityPeriodStart = moment().subtract(period, "days").toDate();

    // Date today
    const now = new Date();

    // Get all activities of a specific type involving resident
    // make sure activities are in the past (i.e. not planned)
    //  sort in reverse order by activity date
    const activities = Activities.find({
      'residentIds': residentId,
      'activityTypeId': activityTypeId,
      activityDate: {$gte: activityPeriodStart, $lte: now}},
      {sort : {activityDate:  -1}
    }).fetch();

    if (activities) {
      return activities;
    }
  },
  getCountOfResidentActivitiesByType ({ residentId, activityTypeId, period }) {
    const activities = Meteor.call('getResidentActivitiesByType', { residentId, activityTypeId, period });

    // Placeholder for sum of activities
    let activityCount = 0;

    if (activities) {
      // Count the resident activity
      activityCount = activities.length;
    }

    return activityCount;
  },
  getAllResidentSelectOptions () {
    /*
    return an array of resident objects in the form of:
       label: firstName lastInitial
       value: residentId
   */

   // Get all residents
   const residents = Residents.find().fetch();

   // Create array of resident select options
   const residentSelectOptions = _.map(residents, (resident) => {
     return {
       label: `${resident.firstName} ${resident.lastInitial}`,
       value: resident._id,
     }
   });

   return residentSelectOptions;
  },
  getMinutesOfResidentActivitiesByType ({ residentId, activityTypeId, period }) {
    // Date period ago
    const activityPeriodStart = moment().subtract(period, "days").toDate();

    // Date today
    const now = new Date();

    const residentActivities = Activities.find(
      {
        residentIds: residentId,
        activityTypeId: activityTypeId,
        activityDate: {$gte: activityPeriodStart, $lte: now}
        },
      {
        sort : { activityDate:  -1 }
      }).fetch();

    // Sum of duration for particular resident & activity
    return residentActivities.reduce((prevResult, document) => prevResult + document.duration, 0)
  },
  residentNamesGroupedtByHomes(){

    // Get list of homes, sorted alphabetically
    const homes = Homes.find({}, { sort: { name: 1 } }).fetch();

    // Create an array residents grouped by home
    const residentsSelectOptions = _.map(homes, function (home) {
      // Get home ID
      const homeId = home._id;

      // do not show departed residents
      const notDeparted = {
        moveOut: {
          $exists: false
        }
      };

      // Get a list of residents for current home
      const homeResidents = Residencies.find({ homeId, ...notDeparted })
        .fetch()
        .map(function (resident) {
          const residentId = resident.residentId;
          const residentDet = Residents.findOne(residentId);

          const fullName = residentDet.fullName();
          return { ...resident, fullName };
        });
      // Create an object containing a home and its residents
      const homeGroup = {
        optgroup: home.name,
        options: _.chain(homeResidents).map(function (resident) {

          // Create an object containing the resident name and ID
          const residentObject = {
            value: resident.residentId,
            label: resident.fullName,
          };

          return residentObject;
        }).sortBy("label")
          .value()
      };
      return homeGroup;
    });

    return residentsSelectOptions;
  }
});
<|endoftext|>


<commit_before>'use strict';

angular.module('belizeorchidsApp')
    .controller('MainCtrl', function ($http, $location, $scope) {
        $scope.orchids = [];
        $scope.detail_images = {};
        $scope.orchid_counts = {}

        var mongo_server = $location.host().indexOf("belizeorchids.com") >= 0 ?
            'http://mongodb.belizeorchids.com:27080' : 'http://localhost:27080',
            db = mongo_server + "/orchids",
            thumb_root = 'https://s3-us-west-2.amazonaws.com/www.belizeorchids.com/orchids/256x192/';

        $scope.show_index = -1;
        $scope.show_detail = function(index) {
            var name = $scope.orchids[index].name;
            $scope.show_index = index;
            $scope.detail_images[name] = [];
            $scope.orchids[index].thumbs.forEach(function(filename, thumb_index) {
                $scope.detail_images[name][thumb_index] = thumb_root + filename;
            });
        }

        // get the list of orchid names
        var config = {
            method: 'GET',
            url: db + "/orchids/_find",
            params: {
                limit: "500",
                batch_size: "500",
                sort: {name: 1}
            }
        }
        $http(config)
            .success(function(data, status, headers, config) {
                $scope.orchids = data.results;
            })
            .error(function(data, status, headers, config) {
                // do something
            })
  });
<commit_msg>move orchid images directory<commit_after>'use strict';

angular.module('belizeorchidsApp')
    .controller('MainCtrl', function ($http, $location, $scope) {
        $scope.orchids = [];
        $scope.detail_images = {};
        $scope.orchid_counts = {}

        var mongo_server = $location.host().indexOf("belizeorchids.com") >= 0 ?
            'http://mongodb.belizeorchids.com:27080' : 'http://localhost:27080',
            db = mongo_server + "/orchids",
            thumb_root = 'https://s3.amazonaws.com/belizeorchids.com/images/orchids/256x192/'

        $scope.show_index = -1;
        $scope.show_detail = function(index) {
            var name = $scope.orchids[index].name;
            $scope.show_index = index;
            $scope.detail_images[name] = [];
            $scope.orchids[index].thumbs.forEach(function(filename, thumb_index) {
                $scope.detail_images[name][thumb_index] = thumb_root + filename;
            });
        }

        // get the list of orchid names
        var config = {
            method: 'GET',
            url: db + "/orchids/_find",
            params: {
                limit: "500",
                batch_size: "500",
                sort: {name: 1}
            }
        }
        $http(config)
            .success(function(data, status, headers, config) {
                $scope.orchids = data.results;
            })
            .error(function(data, status, headers, config) {
                // do something
            })
  });
<|endoftext|>


<commit_before>'use strict';

angular.module('scrumPokerApp')
  .controller('RoomCtrl', function ($scope, $routeParams, socket) {

    $scope.room_name = $routeParams.room_id;
    $scope.playing = false;
    $scope.players = {};
    $scope.cards = [1, 2, 5, 8, 20, 40, 'C', '?'];
    $scope.guest_name = "guest"+Math.floor(Math.random()*10000);
    $scope.id = new Date().getTime();

    function getUsername() {
        return $scope.user_name ? $scope.user_name : $scope.guest_name;
    }

    function addVote(players, vote) {
        players[vote.playerid].vote = vote.value;
    }

    function onVote(vote) {
        addVote($scope.players, vote);
    }

    function onStart(story) {
        $scope.playing = true;
        for (var id in $scope.players) {
            $scope.players[id].vote = 0;
        }
        $scope.next_story_summary = undefined;
        if(typeof story.summary === "undefined") {
            story.summary = "No story defined";
        }
        $scope.current_story_summary = story.summary;
        console.log("blo"+$scope.current_story_summary);
    }

    socket.emit('hello', {id: $scope.id, username: getUsername(), room: $scope.room_name});

    socket.on('vote', function(data) { onVote(data); });

    socket.on('hello', function(game) {
        console.log(game);
        $scope.players = game.players;
        $scope.playing = game.state.playing;
    });

    socket.on('start', function(story) {
        onStart(story);
    });

    $scope.vote = function(value) {
        var vote = {
            playerid: $scope.id,
            value: value
        };
        console.log(vote);
        onVote(vote);
        socket.emit('vote', vote);
    };

    $scope.start = function() {
        var story = {
            summary: $scope.next_story_summary
        };
        onStart(story);
        socket.emit('start', story);
    }

  });
<commit_msg>Sync abort<commit_after>'use strict';

angular.module('scrumPokerApp')
  .controller('RoomCtrl', function ($scope, $routeParams, socket) {

    $scope.room_name = $routeParams.room_id;
    $scope.playing = false;
    $scope.players = {};
    $scope.cards = [1, 2, 5, 8, 20, 40, 'C', '?'];
    $scope.guest_name = "guest"+Math.floor(Math.random()*10000);
    $scope.id = new Date().getTime();

    function getUsername() {
        return $scope.user_name ? $scope.user_name : $scope.guest_name;
    }

    function addVote(players, vote) {
        players[vote.playerid].vote = vote.value;
    }

    function onVote(vote) {
        addVote($scope.players, vote);
    }

    function onStart(story) {
        $scope.playing = true;
        for (var id in $scope.players) {
            $scope.players[id].vote = 0;
        }
        $scope.next_story_summary = undefined;
        if(typeof story.summary === "undefined") {
            story.summary = "No story defined";
        }
        $scope.current_story_summary = story.summary;
        console.log("blo"+$scope.current_story_summary);
    }

    function onStop() {
         $scope.playing = false;
    }

    socket.emit('hello', {id: $scope.id, username: getUsername(), room: $scope.room_name});

    socket.on('vote', function(data) { onVote(data); });

    socket.on('hello', function(game) {
        console.log(game);
        $scope.players = game.players;
        $scope.playing = game.state.playing;
    });

    socket.on('start', function(story) {
        onStart(story);
    });

    socket.on('stop', function(story) {
        onStop();
    });

    $scope.vote = function(value) {
        var vote = {
            playerid: $scope.id,
            value: value
        };
        console.log(vote);
        onVote(vote);
        socket.emit('vote', vote);
    };

    $scope.start = function() {
        var story = {
            summary: $scope.next_story_summary
        };
        onStart(story);
        socket.emit('start', story);
    }

    $scope.stop = function() {
        onStop();
        socket.emit('stop');
    }

  });
<|endoftext|>


<commit_before>'use strict';

angular.module('anyfetchFrontApp.modalDirective', [])
.directive('modal', function(DocumentTypesService, ProvidersService, HighlightService, $location) {

  return {
    restrict: 'E',
    scope: {
      show: '=',
      documentfull: '=',
      query: '='
    },
    templateUrl: 'views/template modal.html',
    replace: true,
    link: function(scope) {
      scope.relDefaultLabel = 'undefined';
      scope.zoom = 100;
      scope.zoomClass = null;
      scope.showHighlighter = false;

      scope.resetScope = function() {
        scope.relatedShow = false;
        scope.relatedData = null;
        scope.fullText = null;
        scope.highlight_position = '';
      };

      scope.displayFull = function(id) {
        var actualSearch = $location.search();
        actualSearch.id = id;
        $location.search(actualSearch);
      };

      scope.zoomIn = function() {
        if (scope.zoom < 200) {
          scope.zoom += 10;

          if (scope.zoom === 100) {
            scope.zoomClass = null;
          } else {
            scope.zoomClass = 'zoom-' + scope.zoom;
          }
        }
      };

      scope.zoomOut = function() {
        if (scope.zoom > 50) {
          scope.zoom -= 10;

          if (scope.zoom === 100) {
            scope.zoomClass = null;
          } else {
            scope.zoomClass = 'zoom-' + scope.zoom;
          }
        }
      };

      scope.highlightNext = function(){
        HighlightService.next();
        scope.highlight_position = HighlightService.getTextPosition();
      };

      scope.highlightPrevious = function(){
        HighlightService.previous();
        scope.highlight_position = HighlightService.getTextPosition();
      };

      scope.getDocumentTypeIcon = function(document) {
        var docType = document.document_type;
        if (docType === '5252ce4ce4cfcd16f55cfa3c') {

          var path = document.data.path;

          // Prez icon
          if (/\.(<[^\>]+>)?(ppt|pptx|odp)(<[^\>]+>)?/.exec(path)) {
            return docType + '-prez';
          }

          // Pdf icon
          if (/\.(<[^\>]+>)?pdf(<[^\>]+>)?/.exec(path)) {
            return docType + '-pdf';
          }

          // Tab icon
          if (/\.(<[^\>]+>)?(xls|xlsx|ods)(<[^\>]+>)?/.exec(path)) {
            return docType + '-tab';
          }

        }

        return docType;
      };

      scope.unbindEchap = function() {
        $(document).keyup(null);
      };

      scope.bindEchap = function() {
        $(document).keyup(function(e){
          if (e.keyCode === 27) {
            scope.$apply(scope.hideModal);
          }
        });
      };

      scope.hideModal = function() {
        scope.unbindEchap();
        scope.show = false;
        scope.zoom = 0;
        $('body').removeClass('lock');
      };

      scope.show_similar = function() {
        if ($location.search().id) {
          var actualSearch = $location.search();
          actualSearch.similar_to = actualSearch.id;
          delete actualSearch.id;
          $location.search(actualSearch);
        }
        scope.hideModal();
      };

      scope.getTitleProjection = function(rel) {
        var htmlTemplate = DocumentTypesService.get().list[rel.document_type].templates.title;
        return Mustache.render(htmlTemplate, rel.data);
      };

      scope.relatedToggle = function() {
        scope.relatedShow = !scope.relatedShow;
      };

      scope.$watch('documentfull', function(newVal) {
        scope.resetScope();

        $(document).foundation();
        scope.query = scope.query || $location.search().q;
        if (newVal) {
          var htmlTemplate = DocumentTypesService.get().list[scope.documentfull.document_type].templates.full;
          scope.fullText = Mustache.render(htmlTemplate, scope.documentfull.data);
          scope.provider = ProvidersService.providers.list[scope.documentfull.token];

          var iframe = ($('#iframe')[0].contentWindow || $('#iframe')[0].contentDocument);
          iframe.document.body.innerHTML = '
'+scope.fullText+'
';
          iframe.document.body.innerHTML = '';

          scope.bindEchap();

          HighlightService.reset();
          if(HighlightService.getMaxIndex() > 0){
            scope.showHighlighter = true;
            scope.highlight_position = HighlightService.getTextOccurences();
          }

        }
      });
    }
  };
});
<commit_msg>body -> head<commit_after>'use strict';

angular.module('anyfetchFrontApp.modalDirective', [])
.directive('modal', function(DocumentTypesService, ProvidersService, HighlightService, $location) {

  return {
    restrict: 'E',
    scope: {
      show: '=',
      documentfull: '=',
      query: '='
    },
    templateUrl: 'views/template modal.html',
    replace: true,
    link: function(scope) {
      scope.relDefaultLabel = 'undefined';
      scope.zoom = 100;
      scope.zoomClass = null;
      scope.showHighlighter = false;

      scope.resetScope = function() {
        scope.relatedShow = false;
        scope.relatedData = null;
        scope.fullText = null;
        scope.highlight_position = '';
      };

      scope.displayFull = function(id) {
        var actualSearch = $location.search();
        actualSearch.id = id;
        $location.search(actualSearch);
      };

      scope.zoomIn = function() {
        if (scope.zoom < 200) {
          scope.zoom += 10;

          if (scope.zoom === 100) {
            scope.zoomClass = null;
          } else {
            scope.zoomClass = 'zoom-' + scope.zoom;
          }
        }
      };

      scope.zoomOut = function() {
        if (scope.zoom > 50) {
          scope.zoom -= 10;

          if (scope.zoom === 100) {
            scope.zoomClass = null;
          } else {
            scope.zoomClass = 'zoom-' + scope.zoom;
          }
        }
      };

      scope.highlightNext = function(){
        HighlightService.next();
        scope.highlight_position = HighlightService.getTextPosition();
      };

      scope.highlightPrevious = function(){
        HighlightService.previous();
        scope.highlight_position = HighlightService.getTextPosition();
      };

      scope.getDocumentTypeIcon = function(document) {
        var docType = document.document_type;
        if (docType === '5252ce4ce4cfcd16f55cfa3c') {

          var path = document.data.path;

          // Prez icon
          if (/\.(<[^\>]+>)?(ppt|pptx|odp)(<[^\>]+>)?/.exec(path)) {
            return docType + '-prez';
          }

          // Pdf icon
          if (/\.(<[^\>]+>)?pdf(<[^\>]+>)?/.exec(path)) {
            return docType + '-pdf';
          }

          // Tab icon
          if (/\.(<[^\>]+>)?(xls|xlsx|ods)(<[^\>]+>)?/.exec(path)) {
            return docType + '-tab';
          }

        }

        return docType;
      };

      scope.unbindEchap = function() {
        $(document).keyup(null);
      };

      scope.bindEchap = function() {
        $(document).keyup(function(e){
          if (e.keyCode === 27) {
            scope.$apply(scope.hideModal);
          }
        });
      };

      scope.hideModal = function() {
        scope.unbindEchap();
        scope.show = false;
        scope.zoom = 0;
        $('body').removeClass('lock');
      };

      scope.show_similar = function() {
        if ($location.search().id) {
          var actualSearch = $location.search();
          actualSearch.similar_to = actualSearch.id;
          delete actualSearch.id;
          $location.search(actualSearch);
        }
        scope.hideModal();
      };

      scope.getTitleProjection = function(rel) {
        var htmlTemplate = DocumentTypesService.get().list[rel.document_type].templates.title;
        return Mustache.render(htmlTemplate, rel.data);
      };

      scope.relatedToggle = function() {
        scope.relatedShow = !scope.relatedShow;
      };

      scope.$watch('documentfull', function(newVal) {
        scope.resetScope();

        $(document).foundation();
        scope.query = scope.query || $location.search().q;
        if (newVal) {
          var htmlTemplate = DocumentTypesService.get().list[scope.documentfull.document_type].templates.full;
          scope.fullText = Mustache.render(htmlTemplate, scope.documentfull.data);
          scope.provider = ProvidersService.providers.list[scope.documentfull.token];

          var iframe = ($('#iframe')[0].contentWindow || $('#iframe')[0].contentDocument);
          iframe.document.body.innerHTML = '
'+scope.fullText+'
';
          iframe.document.head.innerHTML = '';

          scope.bindEchap();

          HighlightService.reset();
          if(HighlightService.getMaxIndex() > 0){
            scope.showHighlighter = true;
            scope.highlight_position = HighlightService.getTextOccurences();
          }

        }
      });
    }
  };
});
<|endoftext|>


<commit_before>var serialPort = require('serialport'), EventEmitter = require('events').EventEmitter, StatusReader = require('./StatusReader'), CONFIG = require('./config'), logger = require('./logger').create(CONFIG);
function Hardware() {
  var hardware = new EventEmitter();
  var reader = new StatusReader();
  var serialConnected = false;
  var emitRawSerialData = false;
  hardware.serial = {};
  var self = this;
  reader.on('Arduino-settings-reported', function (settings) {
    hardware.emit('Arduino-settings-reported', settings);
  });
  hardware.connect = function () {

    hardware.serial = new serialPort.SerialPort(CONFIG.serial, {
      baudrate: CONFIG.serial_baud,
      parser: serialPort.parsers.readline('\r\n')
    });

    hardware.serial.on("open", function () {
      serialConnected = true;
      logger.log('Serial port open');
    });

    hardware.serial.on('close', function (data) {
      logger.log('!Serial port closed');
      serialConnected = false;
    });

    hardware.serial.on('data', function (data) {
      var status = reader.parseStatus(data);
      hardware.emit('status', status);
      if (emitRawSerialData)
        hardware.emit('serial-recieved', data + '\n');
    });
  };

  // This code intentionally spaces out the serial commands so that the buffer does not overflow
  var timesent = new Date();
  hardware.write = function (command) {
    logger.log(command);
    if (CONFIG.production && serialConnected) {
      var currenttime = new Date();
      var delay = 3-((currenttime.getTime() - timesent.getTime()));
      if (delay < 0) delay == 0;
      timesent = currenttime;
      timesent.setMilliseconds(timesent.getMilliseconds + delay);
      setTimeout(function(){
        hardware.serial.write(command);
        if (emitRawSerialData)  hardware.emit('serial-sent', command);
      }, delay);
    } else {
      logger.log('DID NOT SEND');
    }
  };
  hardware.toggleRawSerialData = function toggleRawSerialData() {
    emitRawSerialData = !emitRawSerialData;
  };
  hardware.close = function () {
    serialConnected = false;
    //This code is a work around for a race condition in the serial port code https://github.com/voodootikigod/node-serialport/issues/241#issuecomment-43058353
    var sp = hardware.serial;
    hardware.serial.flush(function(err) {
      setTimeout(function() {
        sp.close(function(err) {
        });
      }, 10);
  });

  };
  return hardware;
}
module.exports = Hardware;
<commit_msg>Fix a typo and make delay non-negative in serial comms throttling code.<commit_after>var serialPort = require('serialport'), EventEmitter = require('events').EventEmitter, StatusReader = require('./StatusReader'), CONFIG = require('./config'), logger = require('./logger').create(CONFIG);
function Hardware() {
  var hardware = new EventEmitter();
  var reader = new StatusReader();
  var serialConnected = false;
  var emitRawSerialData = false;
  hardware.serial = {};
  var self = this;
  reader.on('Arduino-settings-reported', function (settings) {
    hardware.emit('Arduino-settings-reported', settings);
  });
  hardware.connect = function () {

    hardware.serial = new serialPort.SerialPort(CONFIG.serial, {
      baudrate: CONFIG.serial_baud,
      parser: serialPort.parsers.readline('\r\n')
    });

    hardware.serial.on("open", function () {
      serialConnected = true;
      logger.log('Serial port open');
    });

    hardware.serial.on('close', function (data) {
      logger.log('!Serial port closed');
      serialConnected = false;
    });

    hardware.serial.on('data', function (data) {
      var status = reader.parseStatus(data);
      hardware.emit('status', status);
      if (emitRawSerialData)
        hardware.emit('serial-recieved', data + '\n');
    });
  };

  // This code intentionally spaces out the serial commands so that the buffer does not overflow
  var timesent = new Date();
  hardware.write = function (command) {
    logger.log(command);
    if (CONFIG.production && serialConnected) {
      var currenttime = new Date();
      var delay = 3-((currenttime.getTime() - timesent.getTime()));
      if (delay < 0) delay = 0;
      timesent = currenttime;
      timesent.setMilliseconds(timesent.getMilliseconds + delay);
      setTimeout(function(){
        hardware.serial.write(command);
        if (emitRawSerialData)  hardware.emit('serial-sent', command);
      }, delay);
    } else {
      logger.log('DID NOT SEND');
    }
  };
  hardware.toggleRawSerialData = function toggleRawSerialData() {
    emitRawSerialData = !emitRawSerialData;
  };
  hardware.close = function () {
    serialConnected = false;
    //This code is a work around for a race condition in the serial port code https://github.com/voodootikigod/node-serialport/issues/241#issuecomment-43058353
    var sp = hardware.serial;
    hardware.serial.flush(function(err) {
      setTimeout(function() {
        sp.close(function(err) {
        });
      }, 10);
  });

  };
  return hardware;
}
module.exports = Hardware;
<|endoftext|>


<commit_before>import { chromInfoBisector } from './';

const absToChr = (absPosition, chromInfo) => {
  let insertPoint = chromInfoBisector(chromInfo.cumPositions, absPosition);
  const lastChr = chromInfo.cumPositions[chromInfo.cumPositions.length - 1].chr;
  const lastLength = chromInfo.chromLengths[lastChr];

  insertPoint -= insertPoint > 0 && 1;

  let chrPosition = Math.floor(
    absPosition - chromInfo.cumPositions[insertPoint].pos,
  );
  let offset = 0;

  if (chrPosition < 0) {
    // before the start of the genome
    offset = chrPosition - 1;
    chrPosition = 1;
  }

  if (
    insertPoint == chromInfo.cumPositions.length - 1 &&
    chrPosition > lastLength
  ) {
    // beyond the last chromosome
    offset = chrPosition - lastLength;
    chrPosition = lastLength;
  }

  return [
    chromInfo.cumPositions[insertPoint].chr,
    chrPosition,
    offset,
    insertPoint,
  ];
};

export default absToChr;
<commit_msg>Robustify<commit_after>import { chromInfoBisector } from './';

const absToChr = (absPosition, chromInfo) => {
  let insertPoint = chromInfoBisector(chromInfo.cumPositions, absPosition);
  const lastChr = chromInfo.cumPositions[chromInfo.cumPositions.length - 1].chr;
  const lastLength = chromInfo.chromLengths[lastChr];

  insertPoint -= insertPoint > 0 && 1;

  let chrPosition = Math.floor(
    absPosition - chromInfo.cumPositions[insertPoint].pos,
  );
  let offset = 0;

  if (chrPosition < 0) {
    // before the start of the genome
    offset = chrPosition - 1;
    chrPosition = 1;
  }

  if (
    insertPoint === chromInfo.cumPositions.length - 1 &&
    chrPosition > lastLength
  ) {
    // beyond the last chromosome
    offset = chrPosition - lastLength;
    chrPosition = lastLength;
  }

  return [
    chromInfo.cumPositions[insertPoint].chr,
    chrPosition,
    offset,
    insertPoint,
  ];
};

export default absToChr;
<|endoftext|>


<commit_before>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import EXIF from 'exif-js';
import {
  extractFileExtension,
  extractContainingDirectoryPath,
  extractFileName,
  getMetaDirectoryPath
} from '../utils/paths';
import { base64ToArrayBuffer } from '../utils/misc';
import AppConfig from '../config';
import PlatformIO from '../services/platform-io';
import { Pro } from '../pro';

const maxSize = AppConfig.maxThumbSize;

const supportedImgs = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'bmp'];
const supportedContainers = [
  'zip',
  'epub',
  'docx',
  'pptx',
  'pptm',
  'potx',
  'potm',
  'ppxs',
  'ppsm',
  'sldx',
  'sldm',
  'dotx',
  'dotm',
  'xlsx',
  'xlsm',
  'xlst',
  'odp',
  'odg',
  'ods',
  'odt'
];
const supportedText = [
  'txt',
  'md',
  'coffee',
  'c',
  'cpp',
  'css',
  'groovy',
  'haxe',
  'xml',
  'java',
  'js',
  'json',
  'less',
  'markdown',
  'mdown',
  'php',
  'pl',
  'py',
  'rb',
  'ini',
  'sh',
  'sql',
  'mhtml'
];
const supportedVideos = ['mp4', 'webm', 'ogv', 'm4v'];
const maxFileSize = 30 * 1024 * 1024;

function saveThumbnailPromise(filePath, dataURL) {
  return new Promise((resolve, reject) => {
    if (!dataURL || dataURL.length < 7) { // data:,
      return reject('Invalid dataURL');
    }
    const baseString = dataURL.split(',').pop();
    const content = base64ToArrayBuffer(baseString);
    PlatformIO.saveBinaryFilePromise(filePath, content, true)
      .then(() => resolve(filePath))
      .catch((error) => {
        console.warn('Saving thumbnail for ' + filePath + ' failed with ' + JSON.stringify(error));
        return reject('Saving tmb failed for: ' + filePath);
      });
  });
}

function getThumbFileLocation(filePath) {
  const containingFolder = extractContainingDirectoryPath(filePath);
  const metaFolder = getMetaDirectoryPath(containingFolder);
  return metaFolder + AppConfig.dirSeparator + extractFileName(filePath) + AppConfig.thumbFileExt;
}

export function getThumbnailURLPromise(filePath) {
  return new Promise((resolve) => {
    PlatformIO.getPropertiesPromise(filePath).then((origStats) => {
      const thumbFilePath = getThumbFileLocation(filePath);
      PlatformIO.getPropertiesPromise(thumbFilePath).then((stats) => {
        if (stats) { // Thumbnail exists
          if (origStats.lmdt > stats.lmdt) { // Checking if is up to date
            createThumbnailPromise(filePath, origStats.size, thumbFilePath).then((tmbPath) => resolve({ filePath, tmbPath })).catch((err) => {
              console.warn('Thumb generation failed ' + err);
              resolve({ filePath, tmbPath: thumbFilePath });
            });
          } else { // Tmb up to date
            resolve({ filePath });
          }
        } else {
          // Thumbnail does not exists
          createThumbnailPromise(filePath, origStats.size, thumbFilePath).then((tmbPath) => resolve({ filePath, tmbPath })).catch(err => {
            console.warn('Thumb generation failed ' + err);
            resolve({ filePath });
          });
        }
      }).catch(err => {
        console.warn('Error getting tmb properties ' + err);
        resolve({ filePath });
      });
    }).catch(err => {
      console.warn('Error getting file properties ' + err);
      resolve({ filePath });
    });
  });
}

export function createThumbnailPromise(
  filePath: string,
  fileSize: number,
  thumbFilePath: string
) {
  return new Promise((resolve) => {
    generateThumbnailPromise(filePath, fileSize).then((dataURL) => {
      if (dataURL && dataURL.length) {
        saveThumbnailPromise(thumbFilePath, dataURL)
          .then(() => resolve(thumbFilePath))
          .catch((err) => {
            console.warn('Thumb saving failed ' + err);
            resolve();
          });
      } else {
        resolve();
      }
    }).catch((err) => {
      console.warn('Thumb generation failed ' + err);
      resolve();
    });
  });
}

export function generateThumbnailPromise(
  fileURL: string,
  fileSize: number
) {
  const ext = extractFileExtension(fileURL).toLowerCase();

  if (supportedImgs.indexOf(ext) >= 0) {
    if (fileSize && fileSize < maxFileSize) {
      return generateImageThumbnail(fileURL);
    }
  } else if (Pro && ext === 'pdf') {
    return Pro.ThumbsGenerator.generatePDFThumbnail(fileURL, maxSize);
  } else if (Pro && ext === 'html') {
    return Pro.ThumbsGenerator.generateHtmlThumbnail(fileURL, maxSize);
  } else if (Pro && ext === 'url') {
    return Pro.ThumbsGenerator.generateUrlThumbnail(fileURL, maxSize);
  } else if (Pro && ext === 'tiff') {
    return Pro.ThumbsGenerator.generateTiffThumbnail(fileURL, maxSize);
  } else if (Pro && ext === 'mp3') {
    if (fileSize && fileSize < maxFileSize) {
      // return Pro.ThumbsGenerator.generateMp3Thumbnail(fileURL, maxSize);
    }
  } else if (Pro && supportedText.indexOf(ext) >= 0) {
    return Pro.ThumbsGenerator.generateTextThumbnail(fileURL, maxSize);
  } else if (Pro && supportedContainers.indexOf(ext) >= 0) {
    if (fileSize && fileSize < maxFileSize) {
      return Pro.ThumbsGenerator.generateZipContainerImageThumbnail(fileURL, maxSize, supportedImgs);
    }
  } else if (supportedVideos.indexOf(ext) >= 0) {
    if (Pro) {
      return Pro.ThumbsGenerator.generateVideoThumbnail(fileURL, maxSize);
    }
    return generateVideoThumbnail(fileURL);
  }
  return generateDefaultThumbnail();
}

function generateDefaultThumbnail() {
  return Promise.resolve('');
}

function generateImageThumbnail(fileURL) {
  return new Promise((resolve) => {
    let canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    let img = new Image();

    const errorHandler = err => {
      console.warn(
        'Error while generating thumbnail for: ' +
        fileURL +
        ' - ' +
        JSON.stringify(err)
      );
      resolve('');
    };

    img.crossOrigin = 'anonymous';
    img.onload = () => {
      EXIF.getData(img, function () { // eslint-disable-line
        // TODO Use EXIF only for jpegs
        const orientation = EXIF.getTag(this, 'Orientation');
        /*
          1 - 0 degrees – the correct orientation, no adjustment is required.
          2 - 0 degrees, mirrored – image has been flipped back-to-front.
          3 - 180 degrees – image is upside down.
          4 - 180 degrees, mirrored – image is upside down and flipped back-to-front.
          5 - 90 degrees – image is on its side.
          6 - 90 degrees, mirrored – image is on its side and flipped back-to-front.
          7 - 270 degrees – image is on its far side.
          8 - 270 degrees, mirrored – image is on its far side and flipped back-to-front.
        */
        let angleInRadians;
        switch (orientation) {
        case 8:
          angleInRadians = 270 * (Math.PI / 180);
          break;
        case 3:
          angleInRadians = 180 * (Math.PI / 180);
          break;
        case 6:
          angleInRadians = 90 * (Math.PI / 180);
          break;
        case 1:
          // ctx.rotate(0);
          break;
        default:
          // ctx.rotate(0);
        }
        if (img.width >= img.height) {
          canvas.width = maxSize;
          canvas.height = (maxSize * img.height) / img.width;
        } else {
          canvas.height = maxSize;
          canvas.width = (maxSize * img.width) / img.height;
        }
        const width = canvas.width;
        const height = canvas.height;
        const x = canvas.width / 2;
        const y = canvas.height / 2;

        ctx.translate(x, y);
        ctx.rotate(angleInRadians);
        ctx.drawImage(img, -width / 2, -height / 2, width, height);
        ctx.rotate(-angleInRadians);
        ctx.translate(-x, -y);
        const dataurl = canvas.toDataURL(AppConfig.thumbType);
        resolve(dataurl);
        img = null;
        canvas = null;
      });
    };
    img.src = fileURL;
    img.onerror = errorHandler;
  });
}

function generateVideoThumbnail(fileURL) {
  return new Promise((resolve) => {
    let canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    let img = new Image();
    let video = document.createElement('video');
    const captureTime = 1.5; // time in seconds at which to capture the image from the video

    const errorHandler = err => {
      console.warn(
        'Error while generating thumbnail for: ' +
        fileURL +
        ' - ' +
        JSON.stringify(err)
      );
      resolve('');
    };

    video.onloadedmetadata = () => {
      video.currentTime = Math.min(Math.max(0, captureTime), video.duration);
    };

    video.onseeked = () => {
      canvas.height = video.videoHeight;
      canvas.width = video.videoWidth;
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
      const dataurl = canvas.toDataURL(AppConfig.thumbType);
      img.onerror = errorHandler;
      resolve(dataurl);
      img = null;
      canvas = null;
      video = null;
    };
    video.onerror = errorHandler;
    video.src = fileURL;
  });
}
<commit_msg>Include scale calculations to keep the video preview image dimensions below a max size.<commit_after>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import EXIF from 'exif-js';
import {
  extractFileExtension,
  extractContainingDirectoryPath,
  extractFileName,
  getMetaDirectoryPath
} from '../utils/paths';
import { base64ToArrayBuffer } from '../utils/misc';
import AppConfig from '../config';
import PlatformIO from '../services/platform-io';
import { Pro } from '../pro';

const maxSize = AppConfig.maxThumbSize;

const supportedImgs = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'bmp'];
const supportedContainers = [
  'zip',
  'epub',
  'docx',
  'pptx',
  'pptm',
  'potx',
  'potm',
  'ppxs',
  'ppsm',
  'sldx',
  'sldm',
  'dotx',
  'dotm',
  'xlsx',
  'xlsm',
  'xlst',
  'odp',
  'odg',
  'ods',
  'odt'
];
const supportedText = [
  'txt',
  'md',
  'coffee',
  'c',
  'cpp',
  'css',
  'groovy',
  'haxe',
  'xml',
  'java',
  'js',
  'json',
  'less',
  'markdown',
  'mdown',
  'php',
  'pl',
  'py',
  'rb',
  'ini',
  'sh',
  'sql',
  'mhtml'
];
const supportedVideos = ['mp4', 'webm', 'ogv', 'm4v'];
const maxFileSize = 30 * 1024 * 1024;

function saveThumbnailPromise(filePath, dataURL) {
  return new Promise((resolve, reject) => {
    if (!dataURL || dataURL.length < 7) { // data:,
      return reject('Invalid dataURL');
    }
    const baseString = dataURL.split(',').pop();
    const content = base64ToArrayBuffer(baseString);
    PlatformIO.saveBinaryFilePromise(filePath, content, true)
      .then(() => resolve(filePath))
      .catch((error) => {
        console.warn('Saving thumbnail for ' + filePath + ' failed with ' + JSON.stringify(error));
        return reject('Saving tmb failed for: ' + filePath);
      });
  });
}

function getThumbFileLocation(filePath) {
  const containingFolder = extractContainingDirectoryPath(filePath);
  const metaFolder = getMetaDirectoryPath(containingFolder);
  return metaFolder + AppConfig.dirSeparator + extractFileName(filePath) + AppConfig.thumbFileExt;
}

export function getThumbnailURLPromise(filePath) {
  return new Promise((resolve) => {
    PlatformIO.getPropertiesPromise(filePath).then((origStats) => {
      const thumbFilePath = getThumbFileLocation(filePath);
      PlatformIO.getPropertiesPromise(thumbFilePath).then((stats) => {
        if (stats) { // Thumbnail exists
          if (origStats.lmdt > stats.lmdt) { // Checking if is up to date
            createThumbnailPromise(filePath, origStats.size, thumbFilePath).then((tmbPath) => resolve({ filePath, tmbPath })).catch((err) => {
              console.warn('Thumb generation failed ' + err);
              resolve({ filePath, tmbPath: thumbFilePath });
            });
          } else { // Tmb up to date
            resolve({ filePath });
          }
        } else {
          // Thumbnail does not exists
          createThumbnailPromise(filePath, origStats.size, thumbFilePath).then((tmbPath) => resolve({ filePath, tmbPath })).catch(err => {
            console.warn('Thumb generation failed ' + err);
            resolve({ filePath });
          });
        }
      }).catch(err => {
        console.warn('Error getting tmb properties ' + err);
        resolve({ filePath });
      });
    }).catch(err => {
      console.warn('Error getting file properties ' + err);
      resolve({ filePath });
    });
  });
}

export function createThumbnailPromise(
  filePath: string,
  fileSize: number,
  thumbFilePath: string
) {
  return new Promise((resolve) => {
    generateThumbnailPromise(filePath, fileSize).then((dataURL) => {
      if (dataURL && dataURL.length) {
        saveThumbnailPromise(thumbFilePath, dataURL)
          .then(() => resolve(thumbFilePath))
          .catch((err) => {
            console.warn('Thumb saving failed ' + err);
            resolve();
          });
      } else {
        resolve();
      }
    }).catch((err) => {
      console.warn('Thumb generation failed ' + err);
      resolve();
    });
  });
}

export function generateThumbnailPromise(
  fileURL: string,
  fileSize: number
) {
  const ext = extractFileExtension(fileURL).toLowerCase();

  if (supportedImgs.indexOf(ext) >= 0) {
    if (fileSize && fileSize < maxFileSize) {
      return generateImageThumbnail(fileURL);
    }
  } else if (Pro && ext === 'pdf') {
    return Pro.ThumbsGenerator.generatePDFThumbnail(fileURL, maxSize);
  } else if (Pro && ext === 'html') {
    return Pro.ThumbsGenerator.generateHtmlThumbnail(fileURL, maxSize);
  } else if (Pro && ext === 'url') {
    return Pro.ThumbsGenerator.generateUrlThumbnail(fileURL, maxSize);
  } else if (Pro && ext === 'tiff') {
    return Pro.ThumbsGenerator.generateTiffThumbnail(fileURL, maxSize);
  } else if (Pro && ext === 'mp3') {
    if (fileSize && fileSize < maxFileSize) {
      // return Pro.ThumbsGenerator.generateMp3Thumbnail(fileURL, maxSize);
    }
  } else if (Pro && supportedText.indexOf(ext) >= 0) {
    return Pro.ThumbsGenerator.generateTextThumbnail(fileURL, maxSize);
  } else if (Pro && supportedContainers.indexOf(ext) >= 0) {
    if (fileSize && fileSize < maxFileSize) {
      return Pro.ThumbsGenerator.generateZipContainerImageThumbnail(fileURL, maxSize, supportedImgs);
    }
  } else if (supportedVideos.indexOf(ext) >= 0) {
    if (Pro) {
      return Pro.ThumbsGenerator.generateVideoThumbnail(fileURL, maxSize);
    }
    return generateVideoThumbnail(fileURL);
  }
  return generateDefaultThumbnail();
}

function generateDefaultThumbnail() {
  return Promise.resolve('');
}

function generateImageThumbnail(fileURL) {
  return new Promise((resolve) => {
    let canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    let img = new Image();

    const errorHandler = err => {
      console.warn(
        'Error while generating thumbnail for: ' +
        fileURL +
        ' - ' +
        JSON.stringify(err)
      );
      resolve('');
    };

    img.crossOrigin = 'anonymous';
    img.onload = () => {
      EXIF.getData(img, function () { // eslint-disable-line
        // TODO Use EXIF only for jpegs
        const orientation = EXIF.getTag(this, 'Orientation');
        /*
          1 - 0 degrees – the correct orientation, no adjustment is required.
          2 - 0 degrees, mirrored – image has been flipped back-to-front.
          3 - 180 degrees – image is upside down.
          4 - 180 degrees, mirrored – image is upside down and flipped back-to-front.
          5 - 90 degrees – image is on its side.
          6 - 90 degrees, mirrored – image is on its side and flipped back-to-front.
          7 - 270 degrees – image is on its far side.
          8 - 270 degrees, mirrored – image is on its far side and flipped back-to-front.
        */
        let angleInRadians;
        switch (orientation) {
        case 8:
          angleInRadians = 270 * (Math.PI / 180);
          break;
        case 3:
          angleInRadians = 180 * (Math.PI / 180);
          break;
        case 6:
          angleInRadians = 90 * (Math.PI / 180);
          break;
        case 1:
          // ctx.rotate(0);
          break;
        default:
          // ctx.rotate(0);
        }
        if (img.width >= img.height) {
          canvas.width = maxSize;
          canvas.height = (maxSize * img.height) / img.width;
        } else {
          canvas.height = maxSize;
          canvas.width = (maxSize * img.width) / img.height;
        }
        const width = canvas.width;
        const height = canvas.height;
        const x = canvas.width / 2;
        const y = canvas.height / 2;

        ctx.translate(x, y);
        ctx.rotate(angleInRadians);
        ctx.drawImage(img, -width / 2, -height / 2, width, height);
        ctx.rotate(-angleInRadians);
        ctx.translate(-x, -y);
        const dataurl = canvas.toDataURL(AppConfig.thumbType);
        resolve(dataurl);
        img = null;
        canvas = null;
      });
    };
    img.src = fileURL;
    img.onerror = errorHandler;
  });
}

function generateVideoThumbnail(fileURL) {
  return new Promise((resolve) => {
    let canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    let img = new Image();
    let video = document.createElement('video');
    const captureTime = 1.5; // time in seconds at which to capture the image from the video

    const errorHandler = err => {
      console.warn(
        'Error while generating thumbnail for: ' +
        fileURL +
        ' - ' +
        JSON.stringify(err)
      );
      resolve('');
    };

    video.onloadedmetadata = () => {
      video.currentTime = Math.min(Math.max(0, captureTime), video.duration);
      if (video.videoWidth >= video.videoHeight) {
        canvas.width = maxSize;
        canvas.height = (maxSize * video.videoHeight) / video.videoWidth;
      } else {
        canvas.height = maxSize;
        canvas.width = (maxSize * video.videoWidth) / video.videoHeight;
      }
    };

    video.onseeked = () => {
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
      const dataurl = canvas.toDataURL(AppConfig.thumbType);
      img.onerror = errorHandler;
      resolve(dataurl);
      img = null;
      canvas = null;
      video = null;
    };
    video.onerror = errorHandler;
    video.src = fileURL;
  });
}
<|endoftext|>


<commit_before>describe('Keyboard module', function() {

    describe('KeyboardManger', function() {
        // Inject the Keyboard module for this test
        beforeEach(module('Keyboard'));

        var keyboardManager;
        beforeEach(inject(function(KeyboardManager) {
            keyboardManager = KeyboardManager;
        }));

        it('should be injected', function() {
            expect(keyboardManager).toBeDefined();
        });

        describe('.on', function() {
            it('should add new callback function into keyEventHandlers', function() {
                var callback = function(key, e) {
                    console.log(key);
                };
                keyboardManager.on(callback);
                expect(keyboardManager.keyEventHandlers).toEqual([callback]);
            });
        });

        describe('._handleKeyEvent', function() {
            it('should call all callback functions for a key event', function() {
                var keyTriggered = 0;
                var callback = function(key, e) {
                    keyTriggered = key;
                };
                keyboardManager.keyEventHandlers = [callback];
                keyboardManager._handleKeyEvent('left');
                expect(keyTriggered).toEqual('left');
            });
        });

        describe('.init', function() {
            it('should initialize $document to be binded with \'keydown\' event', function() {
                keyboardManager.init();
                var keyTriggered = 0;
                var callback = function(key, e) {
                    keyTriggered = key;
                };
                keyboardManager.keyEventHandlers = [callback];
                var e = $.Event('keydown');
                // click left
                e.which = 37;
                angular.element(window.document).triggerHandler(e);
                expect(keyTriggered).toEqual('left');
            });
        });
    });
});
<commit_msg>Update unit test to match the change within KeyBoard Manager.<commit_after>describe('Keyboard module', function() {

    describe('KeyboardManger', function() {
        // Inject the Keyboard module for this test
        beforeEach(module('Keyboard'));

        var keyboardManager;
        beforeEach(inject(function(KeyboardManager) {
            keyboardManager = KeyboardManager;
        }));

        it('should be injected', function() {
            expect(keyboardManager).toBeDefined();
        });

        describe('.init', function() {
            it('should initialize $document to be binded with \'keydown\' event', function() {
                keyboardManager.init();
                var keyTriggered = 0;
                var callback = function(key, e) {
                    keyTriggered = key;
                };
                keyboardManager.keyEventHandlers = [callback];
                var e = $.Event('keydown');
                // click left
                e.which = 37;
                angular.element(window.document).triggerHandler(e);
                expect(keyTriggered).toEqual('left');
            });
        });

        beforeEach(function() {
            keyboardManager.init();
        });

        describe('.on', function() {
            it('should add new callback function into keyEventHandlers', function() {
                var callback = function(key, e) {
                    console.log(key);
                };
                keyboardManager.on(callback);
                expect(keyboardManager.keyEventHandlers).toEqual([callback]);
            });
        });

        describe('._handleKeyEvent', function() {
            it('should call all callback functions for a key event', function() {
                var keyTriggered = 0;
                var callback = function(key, e) {
                    keyTriggered = key;
                };
                keyboardManager.keyEventHandlers = [callback];
                keyboardManager._handleKeyEvent('left');
                expect(keyTriggered).toEqual('left');
            });
        });
    });
});
<|endoftext|>


<commit_before>// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

import expect from 'expect';
import fs from 'fs-promise';
import without from 'lodash/without';
import intersection from 'lodash/intersection';
import path from 'path';
import { REQUIRES_REGEX, IMPORTS_REGEX, globMany, regexFiles } from './shared';

const all = '/**/';
const valid = '*.@(js|jsx)';

// Node and electron native modules
const native = [
  'assert',
  'child_process',
  'electron',
  'fs',
  'os',
  'path',
  'stream',
  'timers',
];

describe('dependencies', () => {
  let appPackages;
  let mainPackages;
  let mainDevPackages;
  let prodPackages;

  beforeEach(async function() {
    const appManifest = await fs.readJson(path.join('app', 'package.json'));
    const mainManifest = await fs.readJson(path.join('package.json'));

    appPackages = Object.keys(appManifest.dependencies).sort();
    mainPackages = Object.keys(mainManifest.dependencies).sort();
    mainDevPackages = Object.keys(mainManifest.devDependencies).sort();
    prodPackages = [...appPackages, ...mainPackages].sort();
  });

  it('– shared code doesn\'t directly use native modules', async function() {
    const paths = [
      `app/shared${all}${valid}`,
    ];

    const ignored = [
      // Used for communicating with the main process.
      'electron',
      // Used for bunyan logging.
      'stream',
    ];

    const sources = await globMany(paths);
    const matches = await regexFiles(sources, REQUIRES_REGEX, IMPORTS_REGEX);

    const usedPackages = without(matches, ...ignored).sort();
    expect(intersection(usedPackages, prodPackages)).toEqual(usedPackages);
  });

  it('– frontend code doesn\'t directly use native modules', async function() {
    const paths = [
      `app/ui${all}${valid}`,
    ];

    const sources = await globMany(paths);
    const matches = await regexFiles(sources, REQUIRES_REGEX, IMPORTS_REGEX);

    const usedPackages = matches.sort();
    expect(intersection(usedPackages, prodPackages)).toEqual(usedPackages);
  });

  it('– processes only use app dependencies or main dependencies', async function() {
    const paths = [
      `app/main${all}${valid}`,
      `app/services${all}${valid}`,
      `app/shared${all}${valid}`,
      `app/ui${all}${valid}`,
    ];

    const unimported = [
      // Not imported, but referenced by bin/user-agent-service script.
      'datomish-user-agent-service',
    ];

    const sources = await globMany(paths);
    const matches = await regexFiles(sources, REQUIRES_REGEX, IMPORTS_REGEX);

    // Stripping out the native node modules and adding the custom used packages
    // should leave only the main `package.json` plus the `app/package.json`
    // production dependencies.
    const usedPackages = [...without(matches, ...native), ...unimported].sort();
    expect(usedPackages).toEqual(prodPackages);

    const installedUnimported = intersection(unimported, [...appPackages, ...mainPackages]);
    expect(installedUnimported).toEqual(unimported);
  });

  it('– build and test code only use main dependencies or devDependencies', async function() {
    const paths = [
      `build${all}${valid}`,
      `test${all}${valid}`,
    ];

    const unimported = [
      // Modules not imported anywhere, but used at the CLI.
      'cross-env',

      // Test modules not imported anywhere, but used at the CLI.
      'mocha',
      'electron-mocha',
      'coveralls',
      'nyc',

      // Webpack loaders.
      'babel-loader',
      'json-loader',

      // Needed by babel-loader.
      'babel-core',
      'babel-runtime',

      // Modules used only for configuration.
      'babel-eslint',
      'babel-plugin-transform-async-to-generator',
      'babel-plugin-transform-class-properties',
      'babel-plugin-transform-es2015-destructuring',
      'babel-plugin-transform-es2015-modules-commonjs',
      'babel-plugin-transform-es2015-parameters',
      'babel-plugin-transform-object-rest-spread',
      'babel-plugin-transform-runtime',
      'babel-preset-react-optimize',
      'babel-preset-react',
      'eslint-config-airbnb',
      'eslint-plugin-babel',
      'eslint-plugin-import',
      'eslint-plugin-jsx-a11y',
      'eslint-plugin-react',
      'react-addons-test-utils',
      'uglify-js',
    ];

    const sources = await globMany(paths);
    const matches = await regexFiles(sources, REQUIRES_REGEX, IMPORTS_REGEX);

    // Stripping out the production and native node modules, and adding the
    // custom used packages should leave only the main `package.json`
    // development dependencies.
    const usedPackages = [...without(matches, ...mainPackages, ...native), ...unimported].sort();
    expect(usedPackages).toEqual(mainDevPackages);

    const installedUnimported = intersection(unimported, [...mainPackages, ...mainDevPackages]);
    expect(installedUnimported).toEqual(unimported);
  });
});
<commit_msg>Add test making sure app and main packages aren't duplicated across json files<commit_after>// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

import expect from 'expect';
import fs from 'fs-promise';
import without from 'lodash/without';
import intersection from 'lodash/intersection';
import path from 'path';
import { REQUIRES_REGEX, IMPORTS_REGEX, globMany, regexFiles } from './shared';

const all = '/**/';
const valid = '*.@(js|jsx)';

// Node and electron native modules
const native = [
  'assert',
  'child_process',
  'electron',
  'fs',
  'os',
  'path',
  'stream',
  'timers',
];

describe('dependencies', () => {
  let appPackages;
  let appDevPackages;
  let mainPackages;
  let mainDevPackages;
  let prodPackages;

  beforeEach(async function() {
    const appManifest = await fs.readJson(path.join('app', 'package.json'));
    const mainManifest = await fs.readJson(path.join('package.json'));

    appPackages = Object.keys(appManifest.dependencies).sort();
    appDevPackages = appManifest.devDependencies;

    mainPackages = Object.keys(mainManifest.dependencies).sort();
    mainDevPackages = Object.keys(mainManifest.devDependencies).sort();

    prodPackages = [...appPackages, ...mainPackages].sort();
  });

  it('– app and main dependencies aren\'t duplicated', async function() {
    const ignored = [
      // See https://github.com/mozilla/tofino/issues/1377
      'yargs',
      'fs-promise',
    ];

    expect(intersection(appPackages, mainPackages)).toEqual([]);
    expect(intersection(without(appPackages, ...ignored), mainDevPackages)).toEqual([]);
    expect(appDevPackages).toEqual(undefined);

    // Make sure no garbage is accumulated in the `ignored` array.
    expect(intersection(ignored, appPackages)).toEqual(ignored);
  });

  it('– shared code doesn\'t directly use native modules', async function() {
    const paths = [
      `app/shared${all}${valid}`,
    ];

    const ignored = [
      // Used for communicating with the main process.
      'electron',
      // Used for bunyan logging.
      'stream',
    ];

    const sources = await globMany(paths);
    const matches = await regexFiles(sources, REQUIRES_REGEX, IMPORTS_REGEX);

    // Strip out the development packages, since we make sure those aren't
    // included in shared code in one of the following tests, and we don't
    // want them logged here as errors, since that would be confusing.
    const usedPackages = without(matches, ...ignored, ...mainDevPackages).sort();
    expect(intersection(usedPackages, prodPackages)).toEqual(usedPackages);
  });

  it('– frontend code doesn\'t directly use native modules', async function() {
    const paths = [
      `app/ui${all}${valid}`,
    ];

    const sources = await globMany(paths);
    const matches = await regexFiles(sources, REQUIRES_REGEX, IMPORTS_REGEX);

    // Strip out the development packages, since we make sure those aren't
    // included in frontend code in one of the following tests, and we don't
    // want them logged here as errors, since that would be confusing.
    const usedPackages = without(matches, ...mainDevPackages).sort();
    expect(intersection(usedPackages, prodPackages)).toEqual(usedPackages);
  });

  it('– processes only use app dependencies or main dependencies', async function() {
    const paths = [
      `app/main${all}${valid}`,
      `app/services${all}${valid}`,
      `app/shared${all}${valid}`,
      `app/ui${all}${valid}`,
    ];

    const unimported = [
      // Not imported, but referenced by bin/user-agent-service script.
      'datomish-user-agent-service',
    ];

    const sources = await globMany(paths);
    const matches = await regexFiles(sources, REQUIRES_REGEX, IMPORTS_REGEX);

    // Stripping out the native node modules and adding the custom used packages
    // should leave only the main `package.json` plus the `app/package.json`
    // production dependencies.
    const usedPackages = [...without(matches, ...native), ...unimported].sort();
    expect(usedPackages).toEqual(prodPackages);

    // Make sure no garbage is accumulated in the `unimported` array.
    const installedUnimported = intersection(unimported, [...appPackages, ...mainPackages]);
    expect(installedUnimported).toEqual(unimported);
  });

  it('– build and test code only use main dependencies or devDependencies', async function() {
    const paths = [
      `build${all}${valid}`,
      `test${all}${valid}`,
    ];

    const unimported = [
      // Modules not imported anywhere, but used at the CLI.
      'cross-env',

      // Test modules not imported anywhere, but used at the CLI.
      'mocha',
      'electron-mocha',
      'coveralls',
      'nyc',

      // Webpack loaders.
      'babel-loader',
      'json-loader',

      // Needed by babel-loader.
      'babel-core',
      'babel-runtime',

      // Modules used only for configuration.
      'babel-eslint',
      'babel-plugin-transform-async-to-generator',
      'babel-plugin-transform-class-properties',
      'babel-plugin-transform-es2015-destructuring',
      'babel-plugin-transform-es2015-modules-commonjs',
      'babel-plugin-transform-es2015-parameters',
      'babel-plugin-transform-object-rest-spread',
      'babel-plugin-transform-runtime',
      'babel-preset-react-optimize',
      'babel-preset-react',
      'eslint-config-airbnb',
      'eslint-plugin-babel',
      'eslint-plugin-import',
      'eslint-plugin-jsx-a11y',
      'eslint-plugin-react',
      'react-addons-test-utils',
      'uglify-js',
    ];

    const sources = await globMany(paths);
    const matches = await regexFiles(sources, REQUIRES_REGEX, IMPORTS_REGEX);

    // Stripping out the production and native node modules, and adding the
    // custom used packages should leave only the main `package.json`
    // development dependencies.
    const usedPackages = [...without(matches, ...mainPackages, ...native), ...unimported].sort();
    expect(usedPackages).toEqual(mainDevPackages);

    // Make sure no garbage is accumulated in the `unimported` array.
    const installedUnimported = intersection(unimported, [...mainPackages, ...mainDevPackages]);
    expect(installedUnimported).toEqual(unimported);
  });
});
<|endoftext|>


<commit_before>/*
 * grunt-set-app-mode
 * https://github.com/cfogelberg/grunt-set-app-mode
 *
 * Copyright (c) 2014 Christo Fogelberg
 * Licensed under the MIT license.
 */

"use strict";

describe("ModeGroupGonfig", function() {
  var should = require("should");
  var path = require("path");
  var ModeGroupConfig = require("../../lib/mode_group_config");

  var TARGET_MODE = "dev";
  var EXPECTED_MODES = [ TARGET_MODE, "staging", "prod" ];
  var SRC_MODE_GLOB = "test/src/config.{{MODE}}.js";
  var SRC_NON_EXISTENT_GLOB = "src/config.js";
  var SRC_FILES_DO_NOT_EXIST_GLOB = "src/foo.{{MODE}}.js";
  var DEST_DIR = "test/tmp";
  var DEST_FILE = "test/src/config.dev.js"

  var VALID_MODE_GROUP_CONFIG = {
    src: SRC_MODE_GLOB,
    dest: DEST_DIR
  };

  var MISSING_DEST_MODE_GROUP_CONFIG = {
    src: SRC_MODE_GLOB
  };

  var MISSING_SRC_MODE_GROUP_CONFIG = {
    dest: DEST_DIR
  };

  var SOURCE_FILE_DOES_NOT_EXIST_CONFIG = {
    src: SRC_FILES_DO_NOT_EXIST_GLOB,
    dest: DEST_DIR
  };

  var DESTINATION_IS_A_FILE_CONFIG = {
    src: SRC_MODE_GLOB,
    dest: DEST_FILE
  };

  var SOURCE_NON_EXISTENT_GLOB = {
    src: SRC_NON_EXISTENT_GLOB,
    dest: DEST_DIR
  };

  it("does not throw an error with valid files and expected_modes", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
  });

  it("returns the correct source glob", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    mgc.get_src_glob().should.equal(SRC_MODE_GLOB.replace("{{MODE}}", "*"));
  });

  it("returns the correct src filepath", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    mgc.get_src_filepath(TARGET_MODE).should.equal(SRC_MODE_GLOB.replace("{{MODE}}", TARGET_MODE));
  });

  it("returns the correct src filename", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    var src_filename = path.basename(SRC_MODE_GLOB.replace("{{MODE}}", TARGET_MODE));
    mgc.get_src_filename(TARGET_MODE).should.equal(src_filename);
  });

  it("returns the correct dest", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    mgc.get_dest_dir().should.equal(DEST_DIR);
  });

  it("returns the correct destination filename", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    var dest_filepath = path.join(DEST_DIR, path.basename(SRC_MODE_GLOB.replace(".{{MODE}}", "")));
    mgc.get_dest_filepath(TARGET_MODE).should.equal(dest_filepath);
  });

  it("returns the correct destination glob", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    var dest_filepath = path.join(DEST_DIR, path.basename(SRC_MODE_GLOB.replace("{{MODE}}", "*")));
    mgc.get_dest_glob(TARGET_MODE).should.equal(dest_filepath);
  });

  it("throws error when configuration dest is missing", function() {
    (function() {
      var mgc = new ModeGroupConfig(MISSING_DEST_MODE_GROUP_CONFIG, EXPECTED_MODES);
    }).should.throw();
  });

  it("throws error when configuration src is missing", function() {
    (function() {
      var mgc = new ModeGroupConfig(MISSING_SRC_MODE_GROUP_CONFIG, EXPECTED_MODES);
    }).should.throw();
  });

  it("throws error src does not contain '{{MODE}}'", function() {
    (function() {
      var mgc = new ModeGroupConfig(SRC_NON_EXISTENT_GLOB, EXPECTED_MODES);
    }).should.throw();
  });

  it("throws error when destination is a file not a directory", function() {
    (function() {
      var mgc = new ModeGroupConfig(DESTINATION_IS_A_FILE_CONFIG, EXPECTED_MODES);
    }).should.throw();
  });
});
<commit_msg>MGC: Tests for missing expected mode and also for {{MODE}} flag (file not existing prob catches)<commit_after>/*
 * grunt-set-app-mode
 * https://github.com/cfogelberg/grunt-set-app-mode
 *
 * Copyright (c) 2014 Christo Fogelberg
 * Licensed under the MIT license.
 */

"use strict";

describe("ModeGroupGonfig", function() {
  var should = require("should");
  var path = require("path");
  var ModeGroupConfig = require("../../lib/mode_group_config");

  var TARGET_MODE = "dev";
  var EXPECTED_MODES = [ TARGET_MODE, "staging", "prod" ];
  var EXPECTED_MODES_ONE_WRONG = [ "bronze", "silver", "GOLD" ];
  var SRC_MODE_GLOB = "test/src/config.{{MODE}}.js";
  var SRC_NON_EXISTENT_GLOB = "src/config.js";
  var SRC_MODE_GLOB_NO_FLAG = "src/config.MODE.js";
  var SRC_FILES_DO_NOT_EXIST_GLOB = "src/foo.{{MODE}}.js";
  var DEST_DIR = "test/tmp";
  var DEST_FILE = "test/src/config.dev.js"

  var VALID_MODE_GROUP_CONFIG = {
    src: SRC_MODE_GLOB,
    dest: DEST_DIR
  };

  var MISSING_DEST_MODE_GROUP_CONFIG = {
    src: SRC_MODE_GLOB
  };

  var MISSING_SRC_MODE_GROUP_CONFIG = {
    dest: DEST_DIR
  };

  var SOURCE_FILE_DOES_NOT_EXIST_CONFIG = {
    src: SRC_FILES_DO_NOT_EXIST_GLOB,
    dest: DEST_DIR
  };

  var DESTINATION_IS_A_FILE_CONFIG = {
    src: SRC_MODE_GLOB,
    dest: DEST_FILE
  };

  var SOURCE_NON_EXISTENT_GLOB = {
    src: SRC_NON_EXISTENT_GLOB,
    dest: DEST_DIR
  };

  var NO_MODE_FLAG_MODE_GROUP_CONFIG = {
    src: SRC_MODE_GLOB_NO_FLAG,
    dest: DEST_DIR
  }

  it("does not throw an error with valid files and expected_modes", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
  });

  it("returns the correct source glob", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    mgc.get_src_glob().should.equal(SRC_MODE_GLOB.replace("{{MODE}}", "*"));
  });

  it("returns the correct src filepath", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    mgc.get_src_filepath(TARGET_MODE).should.equal(SRC_MODE_GLOB.replace("{{MODE}}", TARGET_MODE));
  });

  it("returns the correct src filename", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    var src_filename = path.basename(SRC_MODE_GLOB.replace("{{MODE}}", TARGET_MODE));
    mgc.get_src_filename(TARGET_MODE).should.equal(src_filename);
  });

  it("returns the correct dest", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    mgc.get_dest_dir().should.equal(DEST_DIR);
  });

  it("returns the correct destination filename", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    var dest_filepath = path.join(DEST_DIR, path.basename(SRC_MODE_GLOB.replace(".{{MODE}}", "")));
    mgc.get_dest_filepath(TARGET_MODE).should.equal(dest_filepath);
  });

  it("returns the correct destination glob", function() {
    var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES);
    var dest_filepath = path.join(DEST_DIR, path.basename(SRC_MODE_GLOB.replace("{{MODE}}", "*")));
    mgc.get_dest_glob(TARGET_MODE).should.equal(dest_filepath);
  });

  it("throws error when configuration dest is missing", function() {
    (function() {
      var mgc = new ModeGroupConfig(MISSING_DEST_MODE_GROUP_CONFIG, EXPECTED_MODES);
    }).should.throw();
  });

  it("throws error when configuration src is missing", function() {
    (function() {
      var mgc = new ModeGroupConfig(MISSING_SRC_MODE_GROUP_CONFIG, EXPECTED_MODES);
    }).should.throw();
  });

  it("throws error src does not contain '{{MODE}}'", function() {
    (function() {
      var mgc = new ModeGroupConfig(SRC_NON_EXISTENT_GLOB, EXPECTED_MODES);
    }).should.throw();
  });

  it("throws error when destination is a file not a directory", function() {
    (function() {
      var mgc = new ModeGroupConfig(DESTINATION_IS_A_FILE_CONFIG, EXPECTED_MODES);
    }).should.throw();
  });

  it("throws error when src mode glob does not contain {{MODE}}", function() {
    (function() {
      var mgc = new ModeGroupConfig(VALID_MODE_GROUP_CONFIG, EXPECTED_MODES_ONE_WRONG);
    }).should.throw();
  });

  it("throws error when source file not present for one or more expected modes", function() {
    (function() {
      var mgc = new ModeGroupConfig(NO_MODE_FLAG_MODE_GROUP_CONFIG, EXPECTED_MODES);
    }).should.throw();
  })
});
<|endoftext|>