<commit_before>(function () {
	'use strict';

	mare.views.GallerySearchForm = Backbone.View.extend({
		// This view controls everything inside the element with class 'gallery-search-form'
		el: '.gallery-search-form',

		events: {

			'click .gallery-search-form__search-button' : 'updateGallery'

		},

		updateGallery: function updateGallery() {

			this.getFormFields();
			this.processFormFields();
			this.removeUneededFilters();
			this.updateChildren();
		},

		getFormFields: function getFormFields() {

			this.formFields = {
				genders							: $('.select-gender:checked'),
				minimumSiblings					: $('#minimum-number-of-siblings').val(),
				maximumSiblings					: $('#maximum-number-of-siblings').val(),
				youngestAge						: $('#youngest-age').val(),
				oldestAge						: $('#oldest-age').val(),
				races							: $('.select-race:checked'),
				primaryLanguage					: $('#primary-language').val(),
				contactWithBiologicalSiblings	: $('.select-contact-with-biological-siblings:checked').val(),
				contactWithBiologicalParents	: $('.select-contact-with-biological-parents:checked').val(),
				videoOnly						: $('.select-video-only:checked').length > 0,
				legallyFreeOnly					: $('.select-legally-free-only:checked').length > 0,
				updatedWithin					: $('#updated-within').val(),
				maximumPhysicalNeeds			: $('.select-maximum-physical-needs:checked').val(),
				maximumEmotionalNeeds			: $('.select-maximum-emotional-needs:checked').val(),
				maximumIntellectualNeeds		: $('.select-maximum-intellectual-needs:checked').val(),
				disabilities					: $('.select-disabilities:checked'),
				otherConsiderations				: $('.select-other-considerations:checked'),
				familyConstellation				: $('.select-family-constellation:checked').val(),
				numberOfChildrenInHome			: $('.select-number-of-children-in-home').val(),
				gendersOfChildrenInHome			: $('.select-genders-of-children-in-home:checked'),
				youngestChildAgeInHome			: $('#youngest-child-age-in-home').val(),
				oldestChildAgeInHome			: $('#oldest-child-age-in-home').val(),
				petsInHome						: $('.select-pets-in-home:checked').length > 0
			};
		},

		processFormFields: function processFormFields() {

			var gendersArray					= [],
				raceArray						= [],
				disabilityArray					= [],
				otherConsiderationsArray		= [],
				gendersOfChildrenInHomeArray	= [],
				formFields						= this.formFields;

			_.each(formFields.genders, function(gender) {
				gendersArray.push(gender.getAttribute('value'));
			});

			_.each(formFields.races, function(race) {
				raceArray.push(race.getAttribute('value'));
			});

			_.each(formFields.disabilities, function(disability) {
				disabilityArray.push(disability.getAttribute('value'));
			});

			_.each(formFields.otherConsiderations, function(consideration) {
				otherConsiderationsArray.push(consideration.getAttribute('value'));
			});

			_.each(formFields.gendersOfChildrenInHome, function(gender) {
				gendersOfChildrenInHomeArray.push(gender.getAttribute('value'));
			});

			formFields.genders					= gendersArray;
			formFields.races					= raceArray;
			formFields.disabilities				= disabilityArray;
			formFields.otherConsiderations		= otherConsiderationsArray;
			formFields.gendersOfChildrenInHome	= gendersOfChildrenInHomeArray;

			formFields.minimumSiblings			= parseInt(formFields.minimumSiblings, 10);
			formFields.maximumSiblings			= parseInt(formFields.maximumSiblings, 10);
			formFields.youngestAge				= parseInt(formFields.youngestAge, 10);
			formFields.oldestAge				= parseInt(formFields.oldestAge, 10);
			formFields.maximumPhysicalNeeds		= formFields.maximumPhysicalNeeds !== undefined ? parseInt(formFields.maximumPhysicalNeeds, 10) : 3;
			formFields.maximumEmotionalNeeds	= formFields.maximumEmotionalNeeds !== undefined ? parseInt(formFields.maximumEmotionalNeeds, 10) : 3;
			formFields.maximumIntellectualNeeds	= formFields.maximumIntellectualNeeds !== undefined ? parseInt(formFields.maximumIntellectualNeeds, 10) : 3;
			formFields.numberOfChildrenInHome	= parseInt(formFields.numberOfChildrenInHome, 10);
			formFields.youngestChildAgeInHome	= parseInt(formFields.youngestChildAgeInHome, 10);
			formFields.oldestChildAgeInHome		= parseInt(formFields.oldestChildAgeInHome, 10);

			formFields.contactWithBiologicalSiblings = formFields.contactWithBiologicalSiblings === 'no' ? false : true; // false : true order is needed, do not change
			formFields.contactWithBiologicalParents = formFields.contactWithBiologicalParents === 'no' ? false : true; // false : true order is needed, do not change
		},

		removeUneededFilters: function removeUneededFilters() {

			var formFields = this.formFields;

			if(formFields.genders.length === 0) { delete formFields.genders; }
			if(formFields.races.length === 0) { delete formFields.races; }
			if(formFields.primaryLanguage === '') { delete formFields.primaryLanguage; }
			if(formFields.contactWithBiologicalSiblings !== false) { delete formFields.contactWithBiologicalSiblings; }
			if(formFields.contactWithBiologicalParents !== false) { delete formFields.contactWithBiologicalParents; }
			if(!formFields.videoOnly) { delete formFields.videoOnly; }
			if(!formFields.legallyFreeOnly) { delete formFields.legallyFreeOnly; }
			if(formFields.updatedWithin === '') { delete formFields.updatedWithin; }
			if(formFields.maximumPhysicalNeeds === 3) { delete formFields.maximumPhysicalNeeds; }
			if(formFields.maximumEmotionalNeeds === 3) { delete formFields.maximumEmotionalNeeds; }
			if(formFields.maximumIntellectualNeeds === 3) { delete formFields.maximumIntellectualNeeds; }
			if(formFields.disabilities.length === 0) { delete formFields.disabilities; }
			if(formFields.otherConsiderations.length === 0) { delete formFields.otherConsiderations; }
			if(!formFields.familyConstellation) { delete formFields.familyConstellation; }
			if(formFields.gendersOfChildrenInHome.length === 0) { delete formFields.gendersOfChildrenInHome; }
			if(!formFields.petsInHome) { delete formFields.petsInHome; }

		},

		updateChildren: function updateChildren() {

			var formFields = this.formFields;

			// Clear out all contents of the current gallery collection
			var model;

			while (model = mare.collections.galleryChildren.first()) {
				model.destroy();
			}

			mare.collections.allChildren.each(function(child) {
				// console.log('child: ', child);
				// break out of the current loop if the child's gender wasn't selected (return is needed for this in _.each)
				if(formFields.genders && formFields.genders.indexOf(child.get('gender')) === -1 ) { return; }
				// break out of the current loop if the child has less than the min or more then the max specified (return is needed for this in _.each)
				if(formFields.minimumSiblings > child.get('siblingContactsCount') ||
				   formFields.maximumSiblings < child.get('siblingContactsCount')) { return; }
				// break out of the current loop if the child's age is less than the youngest or more than the oldest specified (return is needed for this in _.each)
				if(formFields.youngestAge > child.get('age') ||
				   formFields.oldestAge < child.get('age')) { return; }
				// break out of the current loop only if none of the child's races match a selected race (return is needed for this in _.each)
				// <3 Underscore.js for this one
				if(formFields.races && _.intersection(formFields.races, child.get('race')).length === 0) { return; }
				// break out of the current loop only if one of the child's language doesn't match the selected primary language (return is needed for this in _.each)
				if(formFields.primaryLanguage && child.get('language').indexOf(formFields.primaryLanguage) === -1) { return; }
				// break out of the current loop only if the child having contact with their biological siblings/parents doesn't match the user's selection (return is needed for this in _.each)
				if(formFields.contactWithBiologicalSiblings === false &&
				   child.get('hasContactWithBiologicalSiblings') !== false) { return; }

				if(formFields.contactWithBiologicalParents === false &&
				   child.get('hasContactWithBiologicalParents') !== false) { return; }
				// break out of the current loop if the child doesn't have a video and the user specifies that they should (return is needed for this in _.each)
				if(formFields.videoOnly && child.get('hasVideo') === false) { return; }
				// break out of the current loop if the child isn't legally free and the user specifies that they should be (return is needed for this in _.each)
				if(formFields.legallyFreeOnly && child.get('legalStatus') !== 'free') { return; }

				if(formFields.updatedWithin) {
					console.log('updated at:', child.get('updatedAt'));
					console.log(formFields.updatedWithin);
				}

				// break out of the loop if any of the child's needs exceed the maximum specified by the user (return is neede for this in _.each)
				if(formFields.maximumPhysicalNeeds !== undefined && child.get('physicalNeeds') > formFields.maximumPhysicalNeeds) { return; }
				if(formFields.maximumEmotionalNeeds !== undefined && child.get('emotionalNeeds') > formFields.maximumEmotionalNeeds) { return; }
				if(formFields.maximumIntellectualNeeds !== undefined && child.get('intellectualNeeds') > formFields.maximumIntellectualNeeds) { return; }
/************************/
				// break out of the current loop only if none of the child's disabilities match a selected race (return is needed for this in _.each)
				// <3 Underscore.js for this one
				if(formFields.disabilities) {
					console.log('disabilities: ', child.get('disabilities'));
					console.log(formFields.disabilities);
				}
				// if(formFields.disabilities && _.intersection(formFields.disabilities, child.get('disabilities')).length === 0) { return; }

				if(formFields.otherConsiderations) {
					console.log('other considerations: ', child.get('otherConsiderations'));
					console.log(formFields.otherConsiderations);
				}

				if(formFields.familyConstellation) {
					console.log('family constellation: ', child.get('recommendedFamilyConstellation'));
					console.log(formFields.familyConstellation);
				}

				// console.log('considerations: ', child.get('otherFamilyConstellationConsideration'));
				// console.log('form - children in home: ', formFields.numberOfChildrenInHome);
				// console.log('form - youngest: ', formFields.youngestChildAgeInHome);
				// console.log('form - oldest: ', formFields.oldestChildAgeInHome);

				if(formFields.petsInHome) {
					console.log('other considerations', child.get('otherFamilyConstellationConsideration'));
					console.log(formFields.petsInHome);
				}
				// If the child passes all checks, add them to the collection to display on the gallery
				mare.collections.galleryChildren.add(child);

			});

			console.log(mare.collections.galleryChildren);
			mare.collections.galleryChildren.trigger('updateComplete');

		}

	});
}());
<commit_msg>#1 | finish gallery search logic<commit_after>(function () {
	'use strict';

	mare.views.GallerySearchForm = Backbone.View.extend({
		// This view controls everything inside the element with class 'gallery-search-form'
		el: '.gallery-search-form',

		events: {

			'click .gallery-search-form__search-button' : 'updateGallery'

		},

		updateGallery: function updateGallery() {

			this.getFormFields();
			this.processFormFields();
			this.removeUneededFilters();
			this.updateChildren();
		},

		getFormFields: function getFormFields() {

			this.formFields = {
				genders							: $('.select-gender:checked'),
				minimumSiblings					: $('#minimum-number-of-siblings').val(),
				maximumSiblings					: $('#maximum-number-of-siblings').val(),
				youngestAge						: $('#youngest-age').val(),
				oldestAge						: $('#oldest-age').val(),
				races							: $('.select-race:checked'),
				primaryLanguage					: $('#primary-language').val(),
				contactWithBiologicalSiblings	: $('.select-contact-with-biological-siblings:checked').val(),
				contactWithBiologicalParents	: $('.select-contact-with-biological-parents:checked').val(),
				videoOnly						: $('.select-video-only:checked').length > 0,
				legallyFreeOnly					: $('.select-legally-free-only:checked').length > 0,
				updatedWithin					: $('#updated-within').val(),
				maximumPhysicalNeeds			: $('.select-maximum-physical-needs:checked').val(),
				maximumEmotionalNeeds			: $('.select-maximum-emotional-needs:checked').val(),
				maximumIntellectualNeeds		: $('.select-maximum-intellectual-needs:checked').val(),
				disabilities					: $('.select-disabilities:checked'),
				otherConsiderations				: $('.select-other-considerations:checked'),
				familyConstellation				: $('.select-family-constellation:checked').val(),
				numberOfChildrenInHome			: $('.select-number-of-children-in-home').val(),
				gendersOfChildrenInHome			: $('.select-genders-of-children-in-home:checked'),
				youngestChildAgeInHome			: $('#youngest-child-age-in-home').val(),
				oldestChildAgeInHome			: $('#oldest-child-age-in-home').val(),
				petsInHome						: $('.select-pets-in-home:checked').length > 0
			};
		},

		processFormFields: function processFormFields() {

			var gendersArray					= [],
				raceArray						= [],
				disabilityArray					= [],
				otherConsiderationsArray		= [],
				gendersOfChildrenInHomeArray	= [],
				formFields						= this.formFields;

			_.each(formFields.genders, function(gender) {
				gendersArray.push(gender.getAttribute('value'));
			});

			_.each(formFields.races, function(race) {
				raceArray.push(race.getAttribute('value'));
			});

			_.each(formFields.disabilities, function(disability) {
				disabilityArray.push(disability.getAttribute('value'));
			});

			_.each(formFields.otherConsiderations, function(consideration) {
				otherConsiderationsArray.push(consideration.getAttribute('value'));
			});

			_.each(formFields.gendersOfChildrenInHome, function(gender) {
				gendersOfChildrenInHomeArray.push(gender.getAttribute('value'));
			});

			formFields.genders					= gendersArray;
			formFields.races					= raceArray;
			formFields.disabilities				= disabilityArray;
			formFields.otherConsiderations		= otherConsiderationsArray;
			formFields.gendersOfChildrenInHome	= gendersOfChildrenInHomeArray;

			formFields.minimumSiblings			= parseInt(formFields.minimumSiblings, 10);
			formFields.maximumSiblings			= parseInt(formFields.maximumSiblings, 10);
			formFields.youngestAge				= parseInt(formFields.youngestAge, 10);
			formFields.oldestAge				= parseInt(formFields.oldestAge, 10);
			formFields.maximumPhysicalNeeds		= formFields.maximumPhysicalNeeds !== undefined ? parseInt(formFields.maximumPhysicalNeeds, 10) : 3;
			formFields.maximumEmotionalNeeds	= formFields.maximumEmotionalNeeds !== undefined ? parseInt(formFields.maximumEmotionalNeeds, 10) : 3;
			formFields.maximumIntellectualNeeds	= formFields.maximumIntellectualNeeds !== undefined ? parseInt(formFields.maximumIntellectualNeeds, 10) : 3;
			formFields.numberOfChildrenInHome	= parseInt(formFields.numberOfChildrenInHome, 10);
			formFields.youngestChildAgeInHome	= parseInt(formFields.youngestChildAgeInHome, 10);
			formFields.oldestChildAgeInHome		= parseInt(formFields.oldestChildAgeInHome, 10);

			formFields.contactWithBiologicalSiblings = formFields.contactWithBiologicalSiblings === 'no' ? false : true; // false : true order is needed, do not change
			formFields.contactWithBiologicalParents = formFields.contactWithBiologicalParents === 'no' ? false : true; // false : true order is needed, do not change
		},

		removeUneededFilters: function removeUneededFilters() {

			var formFields = this.formFields;

			if(formFields.genders.length === 0) { delete formFields.genders; }
			if(formFields.races.length === 0) { delete formFields.races; }
			if(formFields.primaryLanguage === '') { delete formFields.primaryLanguage; }
			if(formFields.contactWithBiologicalSiblings !== false) { delete formFields.contactWithBiologicalSiblings; }
			if(formFields.contactWithBiologicalParents !== false) { delete formFields.contactWithBiologicalParents; }
			if(!formFields.videoOnly) { delete formFields.videoOnly; }
			if(!formFields.legallyFreeOnly) { delete formFields.legallyFreeOnly; }
			if(formFields.updatedWithin === '') { delete formFields.updatedWithin; }
			if(formFields.maximumPhysicalNeeds === 3) { delete formFields.maximumPhysicalNeeds; }
			if(formFields.maximumEmotionalNeeds === 3) { delete formFields.maximumEmotionalNeeds; }
			if(formFields.maximumIntellectualNeeds === 3) { delete formFields.maximumIntellectualNeeds; }
			if(formFields.disabilities.length === 0) { delete formFields.disabilities; }
			if(formFields.otherConsiderations.length === 0) { delete formFields.otherConsiderations; }
			if(!formFields.familyConstellation) { delete formFields.familyConstellation; }
			if(formFields.gendersOfChildrenInHome.length === 0) { delete formFields.gendersOfChildrenInHome; }
			if(!formFields.petsInHome) { delete formFields.petsInHome; }

		},

		updateChildren: function updateChildren() {

			var formFields = this.formFields;

			// Clear out all contents of the current gallery collection
			mare.collections.galleryChildren.reset();

			mare.collections.allChildren.each(function(child) {
				// break out of the current loop if the child's gender wasn't selected (return is needed for this in _.each)
				if(formFields.genders && formFields.genders.indexOf(child.get('gender')) === -1 ) { return; }
				// break out of the current loop if the child has less than the min or more then the max specified (return is needed for this in _.each)
				if(formFields.minimumSiblings > child.get('siblingContactsCount') ||
				   formFields.maximumSiblings < child.get('siblingContactsCount')) { return; }
				// break out of the current loop if the child's age is less than the youngest or more than the oldest specified (return is needed for this in _.each)
				if(formFields.youngestAge > child.get('age') ||
				   formFields.oldestAge < child.get('age')) { return; }
				// break out of the current loop only if none of the child's races match a selected race (return is needed for this in _.each)
				// <3 Underscore.js for this one
				if(formFields.races && _.intersection(formFields.races, child.get('race')).length === 0) { return; }
				// break out of the current loop only if one of the child's language doesn't match the selected primary language (return is needed for this in _.each)
				if(formFields.primaryLanguage && child.get('language').indexOf(formFields.primaryLanguage) === -1) { return; }
				// break out of the current loop only if the child having contact with their biological siblings/parents doesn't match the user's selection (return is needed for this in _.each)
				if(formFields.contactWithBiologicalSiblings === false &&
				   child.get('hasContactWithBiologicalSiblings') !== false) { return; }

				if(formFields.contactWithBiologicalParents === false &&
				   child.get('hasContactWithBiologicalParents') !== false) { return; }
				// break out of the current loop if the child doesn't have a video and the user specifies that they should (return is needed for this in _.each)
				if(formFields.videoOnly && child.get('hasVideo') === false) { return; }
				// break out of the current loop if the child isn't legally free and the user specifies that they should be (return is needed for this in _.each)
				if(formFields.legallyFreeOnly && child.get('legalStatus') !== 'free') { return; }

				if(formFields.updatedWithin) {
					console.log('updated at:', child.get('updatedAt'));
					console.log(formFields.updatedWithin);
				}

				// break out of the loop if any of the child's needs exceed the maximum specified by the user (return is needed for this in _.each)
				if(formFields.maximumPhysicalNeeds !== undefined && child.get('physicalNeeds') > formFields.maximumPhysicalNeeds) { return; }
				if(formFields.maximumEmotionalNeeds !== undefined && child.get('emotionalNeeds') > formFields.maximumEmotionalNeeds) { return; }
				if(formFields.maximumIntellectualNeeds !== undefined && child.get('intellectualNeeds') > formFields.maximumIntellectualNeeds) { return; }
				// break out of the current loop only if none of the child's disabilities match a selected disability (return is needed for this in _.each)
				// <3 Underscore.js for this one
				if(formFields.disabilities &&
				   _.intersection(formFields.disabilities, child.get('disabilities')).length === 0) { return; }
				// break out of the current loop only if none of the child's other considerations match a selected consideration (return is needed for this in _.each)
				// <3 Underscore.js for this one
				if(formFields.otherConsiderations &&
				   _.intersection(formFields.otherConsiderations, child.get('otherConsiderations')).length === 0) { return; }
				// break out of the loop if the recommended family constellation for the child does not contain the one selected by the user (return is needed for this in _.each)
				if(formFields.familyConstellation !== undefined &&
				   child.get('recommendedFamilyConstellation').indexOf(formFields.familyConstellation) === -1) { return; }
				// break out of the loop if any of the other considerations selected don't match the child (return is needed for this in _.each)
				if(child.get('requiresSiblings') && formFields.numberOfChildrenInHome === 0) { return; }
				if(child.get('requiresNoSiblings') && formFields.numberOfChildrenInHome > 0) { return; }
				if(child.get('requiresOlderSibling') && formFields.oldestChildAgeInHome <= child.get('age')) { return; }
				if(child.get('requiresYoungerSibling') && formFields.youngestChildAgeInHome >= child.get('age')) { return; }
				if(child.get('noPets') && formFields.petsInHome) { return; }
				// If the child passes all checks, add them to the collection to display on the gallery
				mare.collections.galleryChildren.add(child);

			});
			// Emit an event to allow the gallery to update it's display now that we have all matching models
			mare.collections.galleryChildren.trigger('updateComplete');

		}

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


<commit_before>/**
 * Defines the entry point for a node.js-based image picker tool. This tool is
 * passed a single command line argument, a file. It...
 *
 *  - Ceases processing if $imagename.score is present
 *  - It reads the binary data image
 *  - Calculates the RMSD using each pixel in the original image and protanope image
 *  - Creates an output file, $imagename.score.tmp, and outputs the distance into it
 *  - Moves $imagename.score.tmp to $imagename.score
 *
 * This can be used to find images that change the most for a protanope, and
 * also other interesting things (like ones that change the least).
 *
 * This avoids async operations for simplicitly; I will just use xargs to
 * parallelize.
 */

var assert = require('assert');
var colors = require('../colors/colors.js');
var fs = require('fs');
// $FlowFixMe: lwip does not have externs.
var lwip = require('lwip');
var protanope = require('../colors/protanope.js');

/** Parses argv for the file. */
var getFileFromArgs = function(argv: Array<string>): ?string {
  if (argv.length != 3) {
    return null;
  }

  return argv[2];
};

var JPG_REGEX = /^[^.]+\.jpg$/g;
/**
 * Validates that the file arg passed to the program is real, and likely a
 * jpeg. Returns the arg if it is real, otherwise null.
 */
var validateFile = function(file: ?string): ?string {
  if (!file) {
    return null;
  }

  if (file.search(JPG_REGEX) != 0) {
    return null;
  }

  try {
    var stats = fs.statSync(file);
    return stats.isFile() ? file : null;
  } catch (e) {
    // Does not exist.
    return null;
  }
};

/** Returns the usage string for the program. */
var getUsage = function(): string {
  return 'Usage: ' +
    ' Run by itself: ' + process.argv[0] + ' ' + process.argv[1] + ' $imageName\n' +
    '\n' +
    'For the given image, calculates how different the image is as \n' +
    'a percentage, and outputs $imagename.score. Does not recalculate when \n' +
    '$imagname.score is already present';
};

/** Checks if the given image has already been processed */
var checkImageFinished = function(image: string): boolean {
  try {
    var stats = fs.statSync(image + '.score');
    return stats.isFile();
  } catch (e) {
    // Does not exist.
    return false;
  }
};

/**
 * Outputs the filename and difference score into a temp file, and then renames
 * it to the .score version. This avoids empty score files, since moves are
 * atomic.
 */
var outputDifferenceScore = function(
  sourceFilename: string, differenceScore: number) {

  assert(!isNaN(differenceScore));
  assert(differenceScore >= 0);
  fs.writeFileSync(
    sourceFilename + '.score.tmp', differenceScore + ',"' + sourceFilename + '"');
  fs.renameSync(sourceFilename + '.score.tmp', sourceFilename + '.score');
};

/** Calculates the direct distance between a and b. */
var calculateDistance = function(a: colors.Xyz, b: colors.Xyz): number {
  return Math.sqrt(
    Math.pow(a.X - b.X, 2) + Math.pow(a.Y - b.Y, 2) + Math.pow(a.Z - b.Z, 2));
};

/**
 * High-level program logic, roughly a pipeline, since LWIP is strictly async.
 * - main
 * - processImage
 */
var main = function(): void {
  var image = validateFile(getFileFromArgs(process.argv));
  if (!image) {
    console.error(process.argv, getUsage());
    return;
  }

  if (checkImageFinished(image)) {
    console.log('image %s already processed, exiting', image);
    return;
  }

  console.log('processing image %s', image);

  lwip.open(image, processImage);
};

/**
 * Calculates the root-mean-square deviation for the protanope image, after
 * converting into a color space with linear intensities (XYZ) and calculating
 * their absolute distances. Ultimately writes it into the score file.
 */
var processImage = function(err: Error, image: lwip.Image): void {
  var squaredError = 0;
  for (var row = 0; row < image.height(); row++) {
    for (var col = 0; col < image.width(); col++) {
      var pixel = image.getPixel(col, row);
      var originalColor = new colors.Rgb(pixel.r, pixel.g, pixel.b);
      var protanopeColor = protanope.convertToProtanope(originalColor);

      var originalXyz = colors.rgbToXyz(originalColor);
      var protanopeXyz = colors.rgbToXyz(protanopeColor);

      var distance = calculateDistance(originalXyz, protanopeXyz);
      squaredError += Math.pow(distance, 2);
    }
  }

  assert(squaredError >= 0);

  var meanSquaredDeviation = squaredError / (image.height() * image.width());
  var rmsd = Math.sqrt(meanSquaredDeviation);
  var file = getFileFromArgs(process.argv);
  if (!file) { // Needed for type checker.
    throw Error('This should never be hit: file already found in FS.');
  }
  outputDifferenceScore(file, rmsd);

  console.log('finished processing %s', file);
};

main();
<commit_msg>Modifies the image picker tool to output the processed images.<commit_after>/**
 * Defines the entry point for a node.js-based image picker tool. This tool is
 * passed a single command line argument, a file. It...
 *
 *  - Ceases processing if $imagename-proto.rmsd is present
 *  - It reads the binary data image from $imagename.ext
 *  - Calculates the RMSD using each pixel in the original image and protanope image
 *  - Creates an output file, $imagename-proto.rmsd.tmp, containing the RMSD
 *  - Moves $imagename-proto.rmsd.tmp to $imagename-proto.rmsd
 *  - Writes the proto image into $imagename-proto.ext
 *
 * This can be used to find images that change the most for a protanope, and
 * also other interesting things (like ones that change the least).
 *
 * This avoids batching for simplicity. Just use xargs to parallelize.
 */

var assert = require('assert');
var colors = require('../colors/colors.js');
var fs = require('fs');
// $FlowFixMe: lwip does not have externs.
var lwip = require('lwip');
var protanope = require('../colors/protanope.js');

/** Parses argv for the file. */
var getFileFromArgs = function(argv: Array<string>): ?string {
  if (argv.length != 3) {
    return null;
  }

  return argv[2];
};

var JPG_REGEX = /^([^.]+)\.jpg$/i;
/**
 * Validates that the file arg passed to the program is real, and likely a
 * jpeg. Returns the arg if it is real, otherwise null.
 */
var validateFile = function(file: ?string): ?string {
  if (!file) {
    return null;
  }

  if (file.search(JPG_REGEX) != 0) {
    return null;
  }

  try {
    var stats = fs.statSync(file);
    return stats.isFile() ? file : null;
  } catch (e) {
    // Does not exist.
    return null;
  }
};

/** Gets the filename without the .jpg extension. */
var getBasename = function(imageName: string): string {
  return imageName.match(JPG_REGEX)[1];
};

/** Returns the RMSD filename. */
var getRmsdFilename = function(imageName: string): string {
  return getBasename(imageName) + '-proto.rmsd';
};

/** Returns the processed filename. */
var getProtoFilename = function(imageName: string): string {
  return getBasename(imageName) + '-proto.jpg';
};

/** Returns the usage string for the program. */
var getUsage = function(): string {
  return 'Usage: ' + process.argv[0] + ' ' + process.argv[1] + ' $imageName\n' +
    '\n' +
    'For the given image, calculates how different the image is, and outputs\n' +
    '$imagename-proto.rmsd. Does not recalculate when the .rmsd file is\n' +
    'already present.\n' +
    '\n' +
    'TODO: only works with .jpg files.\n';
};

/** Checks if the given image has already been processed */
var checkImageFinished = function(imageName: string): boolean {
  try {
    var stats = fs.statSync(getRmsdFilename(imageName));
    return stats.isFile();
  } catch (e) {
    // Does not exist.
    return false;
  }
};

/**
 * Outputs the filename and difference score into a temp file, and then renames
 * it to the .rmsd version. This avoids empty score files, since moves are
 * atomic.
 */
var outputDifferenceScore = function(
  sourceFilename: string, differenceScore: number) {

  assert(!isNaN(differenceScore));
  assert(differenceScore >= 0);
  var rmsdFilename = getRmsdFilename(sourceFilename);
  fs.writeFileSync(
    rmsdFilename + '.tmp',
    differenceScore + ',"' + sourceFilename + '"');
  fs.renameSync(rmsdFilename + '.tmp', rmsdFilename);
};

/** Calculates the direct distance between a and b. */
var calculateDistance = function(a: colors.Xyz, b: colors.Xyz): number {
  return Math.sqrt(
    Math.pow(a.X - b.X, 2) + Math.pow(a.Y - b.Y, 2) + Math.pow(a.Z - b.Z, 2));
};

/**
 * High-level program logic, roughly a pipeline, since LWIP is strictly async.
 * - main
 * - processImage
 */
var main = function(): void {
  var image = validateFile(getFileFromArgs(process.argv));
  if (!image) {
    console.error(process.argv, getUsage());
    return;
  }

  if (checkImageFinished(image)) {
    console.log('image %s already processed, exiting', image);
    return;
  }

  console.log('processing image %s', image);

  lwip.open(image, processImage);
};

/**
 * Calculates the root-mean-square deviation for the protanope image, after
 * converting into a color space with linear intensities (XYZ) and calculating
 * their absolute distances. Ultimately writes it into the score file.
 */
var processImage = function(err: Error, image: lwip.Image): void {
  var batchImage = image.batch();
  var squaredError = 0;
  for (var row = 0; row < image.height(); row++) {
    for (var col = 0; col < image.width(); col++) {
      var pixel = image.getPixel(col, row);
      var originalColor = new colors.Rgb(pixel.r, pixel.g, pixel.b);
      var protanopeColor = protanope.convertToProtanope(originalColor);

      var originalXyz = colors.rgbToXyz(originalColor);
      var protanopeXyz = colors.rgbToXyz(protanopeColor);

      var distance = calculateDistance(originalXyz, protanopeXyz);
      squaredError += Math.pow(distance, 2);

      batchImage.setPixel(col, row, {
        r: protanopeColor.R,
        g: protanopeColor.G,
        b: protanopeColor.B,
        a: pixel.a
      });
    }
  }

  assert(squaredError >= 0);

  var meanSquaredDeviation = squaredError / (image.height() * image.width());
  var rmsd = Math.sqrt(meanSquaredDeviation);
  var file = getFileFromArgs(process.argv);
  if (!file) { // Needed for type checker.
    throw Error('This should never be hit: file already found in FS.');
  }
  outputDifferenceScore(file, rmsd);

  batchImage.writeFile(getProtoFilename(file), 'jpg', {quality:100}, function(err) {
    if (err) {
      console.error('Error writing protan file for ', file, err);
    } else {
      console.log('finished processing %s', file);
    }
  });
};

main();
<|endoftext|>


<commit_before>'use babel'

const NewLine = /\r?\n/

export class Message extends HTMLElement {
  initialize(message, includeLink = true) {
    this.message = message
    this.includeLink = includeLink
    this.status = false
    return this
  }
  updateVisibility(scope) {
    let status = true
    if (scope === 'Line')
      status = this.message.currentLine
    else if (scope === 'File')
      status = this.message.currentFile

    if (this.children.length && this.message.filePath) {
      const link = this.querySelector('.linter-message-link span')
      if (link) {
        if (scope === 'Project') {
          link.removeAttribute('hidden')
        } else {
          link.setAttribute('hidden', true)
        }
      }
    }

    this.status = status

    if (status) {
      this.removeAttribute('hidden')
    } else this.setAttribute('hidden', true)

    return this
  }
  attachedCallback() {
    if (atom.config.get('linter.showProviderName') && this.message.linter) {
      this.appendChild(Message.getName(this.message))
    }
    this.appendChild(Message.getRibbon(this.message))
    this.appendChild(Message.getMessage(this.message, this.includeLink))
  }
  static getLink(message) {
    const el = document.createElement('a')
    const pathEl = document.createElement('span')
    let displayFile = message.filePath

    el.className = 'linter-message-link'

    for (let path of atom.project.getPaths())
      if (displayFile.indexOf(path) === 0) {
        displayFile = displayFile.substr(path.length + 1) // Path + Path Separator
        break
      }

    if (message.range) {
      el.textContent = `at line ${message.range.start.row + 1} col ${message.range.start.column + 1}`
    }
    pathEl.textContent = ' in ' + displayFile
    el.appendChild(pathEl)
    el.addEventListener('click', function() {
      atom.workspace.open(message.filePath).then(function() {
        if (message.range) {
          atom.workspace.getActiveTextEditor().setCursorBufferPosition(message.range.start)
        }
      })
    })
    return el
  }
  static getMessage(message, includeLink) {
    if (message.multiline || NewLine.test(message.text)) {
      return Message.getMultiLineMessage(message, includeLink)
    }

    const el = document.createElement('span')
    const messageEl = document.createElement('linter-message-line')

    el.className = 'linter-message-item'

    el.appendChild(messageEl)

    if (includeLink && message.filePath) {
      el.appendChild(Message.getLink(message))
    }

    if (message.html && typeof message.html !== 'string') {
      messageEl.appendChild(message.html.cloneNode(true))
    } else if (message.html) {
      messageEl.innerHTML = message.html
    } else if (message.text) {
      messageEl.textContent = message.text
    }

    return el
  }
  static getMultiLineMessage(message, includeLink) {
    const container = document.createElement('span')
    const messageEl = document.createElement('linter-multiline-message')

    container.className = 'linter-message-item'
    messageEl.setAttribute('title', message.text)

    message.text.split(NewLine).forEach(function(line, index) {
      if (!line) return

      const el = document.createElement('linter-message-line')
      el.textContent = line
      messageEl.appendChild(el)

      // Render the link in the "title" line.
      if (index === 0 && includeLink && message.filePath) {
        messageEl.appendChild(Message.getLink(message))
      }
    })

    container.appendChild(messageEl)

    messageEl.addEventListener('click', function(e) {
      // Avoid opening the message contents when we click the link.
      var link = e.target.tagName === 'A' ? e.target : e.target.parentNode

      if (!link.classList.contains('linter-message-link')) {
        messageEl.classList.toggle('expanded')
      }
    })

    return container
  }
  static getName(message) {
    const el = document.createElement('span')
    el.className = 'linter-message-item badge badge-flexible linter-highlight'
    el.textContent = message.linter
    return el
  }
  static getRibbon(message) {
    const el = document.createElement('span')
    el.className = `linter-message-item badge badge-flexible linter-highlight ${message.class}`
    el.textContent = message.type
    return el
  }
  static fromMessage(message, includeLink) {
    return new MessageElement().initialize(message, includeLink)
  }
}

export const MessageElement = document.registerElement('linter-message', {
  prototype: Message.prototype
})
<commit_msg>:bug: Avoid adding duplicates when attached Fixes #940<commit_after>'use babel'

const NewLine = /\r?\n/

export class Message extends HTMLElement {
  initialize(message, includeLink = true) {
    this.message = message
    this.includeLink = includeLink
    this.status = false
    return this
  }
  updateVisibility(scope) {
    let status = true
    if (scope === 'Line')
      status = this.message.currentLine
    else if (scope === 'File')
      status = this.message.currentFile

    if (this.children.length && this.message.filePath) {
      const link = this.querySelector('.linter-message-link span')
      if (link) {
        if (scope === 'Project') {
          link.removeAttribute('hidden')
        } else {
          link.setAttribute('hidden', true)
        }
      }
    }

    this.status = status

    if (status) {
      this.removeAttribute('hidden')
    } else this.setAttribute('hidden', true)

    return this
  }
  attachedCallback() {
    if (!this.childNodes.length) {
      if (atom.config.get('linter.showProviderName') && this.message.linter) {
        this.appendChild(Message.getName(this.message))
      }
      this.appendChild(Message.getRibbon(this.message))
      this.appendChild(Message.getMessage(this.message, this.includeLink))
    }
  }
  static getLink(message) {
    const el = document.createElement('a')
    const pathEl = document.createElement('span')
    let displayFile = message.filePath

    el.className = 'linter-message-link'

    for (let path of atom.project.getPaths())
      if (displayFile.indexOf(path) === 0) {
        displayFile = displayFile.substr(path.length + 1) // Path + Path Separator
        break
      }

    if (message.range) {
      el.textContent = `at line ${message.range.start.row + 1} col ${message.range.start.column + 1}`
    }
    pathEl.textContent = ' in ' + displayFile
    el.appendChild(pathEl)
    el.addEventListener('click', function() {
      atom.workspace.open(message.filePath).then(function() {
        if (message.range) {
          atom.workspace.getActiveTextEditor().setCursorBufferPosition(message.range.start)
        }
      })
    })
    return el
  }
  static getMessage(message, includeLink) {
    if (message.multiline || NewLine.test(message.text)) {
      return Message.getMultiLineMessage(message, includeLink)
    }

    const el = document.createElement('span')
    const messageEl = document.createElement('linter-message-line')

    el.className = 'linter-message-item'

    el.appendChild(messageEl)

    if (includeLink && message.filePath) {
      el.appendChild(Message.getLink(message))
    }

    if (message.html && typeof message.html !== 'string') {
      messageEl.appendChild(message.html.cloneNode(true))
    } else if (message.html) {
      messageEl.innerHTML = message.html
    } else if (message.text) {
      messageEl.textContent = message.text
    }

    return el
  }
  static getMultiLineMessage(message, includeLink) {
    const container = document.createElement('span')
    const messageEl = document.createElement('linter-multiline-message')

    container.className = 'linter-message-item'
    messageEl.setAttribute('title', message.text)

    message.text.split(NewLine).forEach(function(line, index) {
      if (!line) return

      const el = document.createElement('linter-message-line')
      el.textContent = line
      messageEl.appendChild(el)

      // Render the link in the "title" line.
      if (index === 0 && includeLink && message.filePath) {
        messageEl.appendChild(Message.getLink(message))
      }
    })

    container.appendChild(messageEl)

    messageEl.addEventListener('click', function(e) {
      // Avoid opening the message contents when we click the link.
      var link = e.target.tagName === 'A' ? e.target : e.target.parentNode

      if (!link.classList.contains('linter-message-link')) {
        messageEl.classList.toggle('expanded')
      }
    })

    return container
  }
  static getName(message) {
    const el = document.createElement('span')
    el.className = 'linter-message-item badge badge-flexible linter-highlight'
    el.textContent = message.linter
    return el
  }
  static getRibbon(message) {
    const el = document.createElement('span')
    el.className = `linter-message-item badge badge-flexible linter-highlight ${message.class}`
    el.textContent = message.type
    return el
  }
  static fromMessage(message, includeLink) {
    return new MessageElement().initialize(message, includeLink)
  }
}

export const MessageElement = document.registerElement('linter-message', {
  prototype: Message.prototype
})
<|endoftext|>


<commit_before>module.exports = function (config) {
  config.set({
    customLaunchers: {
      ChromeCustom: {
        base: 'ChromeHeadless',
        // We must disable the Chrome sandbox when running Chrome inside Docker
        // (Chrome's sandbox needs more permissions than Docker allows by
        // default)
        flags: ['--no-sandbox']
      }
    },
    browsers: ['ChromeCustom'],
    files: [
      'test/unit_frontend/js/bootstrap.js',
      // Angular must be loaded before requirejs to set up angular global
      'public/js/libs/angular-1.6.4.min.js',
      'public/js/libs/angular-mocks.js',
      'public/js/libs/jquery-1.11.1.min.js',
      // Set up requirejs
      'test/unit_frontend/js/test-main.js',
      // Include source & test files, but don't "include" them as requirejs
      // handles this for us
      { pattern: 'public/js/**/*.js', included: false },
      { pattern: 'test/unit_frontend/js/**/*.js', included: false }
    ],
    frameworks: ['requirejs', 'mocha', 'chai-sinon'],
    plugins: [
      require('karma-requirejs'),
      require('karma-mocha'),
      require('karma-chai-sinon'),
      require('karma-chrome-launcher'),
      require('karma-tap-reporter')
    ],
    reporters: ['tap']
  });
}
<commit_msg>Switch to mocha reporter<commit_after>module.exports = function (config) {
  config.set({
    customLaunchers: {
      ChromeCustom: {
        base: 'ChromeHeadless',
        // We must disable the Chrome sandbox when running Chrome inside Docker
        // (Chrome's sandbox needs more permissions than Docker allows by
        // default)
        flags: ['--no-sandbox']
      }
    },
    browsers: ['ChromeCustom'],
    files: [
      'test/unit_frontend/js/bootstrap.js',
      // Angular must be loaded before requirejs to set up angular global
      'public/js/libs/angular-1.6.4.min.js',
      'public/js/libs/angular-mocks.js',
      'public/js/libs/jquery-1.11.1.min.js',
      // Set up requirejs
      'test/unit_frontend/js/test-main.js',
      // Include source & test files, but don't "include" them as requirejs
      // handles this for us
      { pattern: 'public/js/**/*.js', included: false },
      { pattern: 'test/unit_frontend/js/**/*.js', included: false }
    ],
    frameworks: ['requirejs', 'mocha', 'chai-sinon'],
    plugins: [
      require('karma-requirejs'),
      require('karma-mocha'),
      require('karma-chai-sinon'),
      require('karma-chrome-launcher'),
      require('karma-mocha-reporter')
    ],
    reporters: ['mocha']
  });
}
<|endoftext|>


<commit_before>Vps.Form.RadioGroup = Ext.extend(Ext.form.RadioGroup, {
    initComponent: function() {
        Vps.Form.RadioGroup.superclass.initComponent.call(this);
    },
    afterRender: function() {
        Vps.Form.RadioGroup.superclass.afterRender.call(this);
        if (this.value) {
            this.setValue(this.value);
        }
    },
    getValue: function() {
        if (!this.rendered) {
            return this.value;
        } else {
            var ret = null;
            this.items.each(function(c) {
                if (c.getValue()) {
                    ret = c.inputValue;
                    return false;
                }
            }, this);
            return ret;
        }
    },
    setValue: function(v) {
        if (!this.rendered) {
            this.value = v;
        } else {
            this.items.each(function(c) {
                if (c.inputValue == v) {
                    c.setValue(true);
                } else {
                    c.setValue(false);
                }
            }, this);
        }
    }
});
Ext.reg('radiogroup', Vps.Form.RadioGroup);
<commit_msg>change event für radiogroup<commit_after>Vps.Form.RadioGroup = Ext.extend(Ext.form.RadioGroup, {
    initComponent: function() {
        Vps.Form.RadioGroup.superclass.initComponent.call(this);
    },
    afterRender: function() {
        Vps.Form.RadioGroup.superclass.afterRender.call(this);
        if (this.value) {
            this.setValue(this.value);
        }
        this.items.each(function(c) {
            c.on('check', function(field, newValue, oldValue) {
                if (field.getValue()) {
                    this.fireEvent('change', this, field.inputValue, 'TODO');
                }
            }, this);
        }, this);
    },
    getValue: function() {
        if (!this.rendered) {
            return this.value;
        } else {
            var ret = null;
            this.items.each(function(c) {
                if (c.getValue()) {
                    ret = c.inputValue;
                    return false;
                }
            }, this);
            return ret;
        }
    },
    setValue: function(v) {
        if (!this.rendered) {
            this.value = v;
        } else {
            this.items.each(function(c) {
                if (c.inputValue == v) {
                    c.setValue(true);
                } else {
                    c.setValue(false);
                }
            }, this);
        }
    }
});
Ext.reg('radiogroup', Vps.Form.RadioGroup);
<|endoftext|>


<commit_before>enyo.kind({
	name: "moon.CalendarDate",
	kind: "enyo.Button",
	classes: "moon-calendar-date",
	spotlight: true,
	events: {
		ontap: ""
	},
	published: {
		value: null,
		color: 0,
	},
	colorChanged: function(inOld) {
		if (this.color) {
			this.addClass("moon-calendar-date-shadow");
		} else {
			this.removeClass("moon-calendar-date-shadow");
		}
	},
	valueChanged: function() {
		this.setContent(this.value.getDate());
	},
});

enyo.kind({
	name: "moon.CalendarWeek",
	published: {
		days: [],
		colors: [],
	},
	handlers: {
		ontap: "doTap"
	},
	components: [
		{name:"repeater", kind: "enyo.FlyweightRepeater", clientClasses: "moon-calendar-week", onSetupItem: "setupItem", count: 7, components: [
			{name: "item", kind: "moon.CalendarDate"}
		]},
	],
	/**
		Usually when we select same item, setupItem() is not called
		because setupItem() is called when changing selection is occured in repeater.
	*/
	doTap: function(inSender, inEvent) {
		if (this.$.repeater.isSelected(inEvent.index)) {
			this.setupItem(inSender, inEvent);
		}
	},
	setupItem: function(inSender, inEvent) {
		var index = inEvent.index;
		var value = this.days[index],
			color = this.colors[index];
		this.$.item.setValue(new Date(value.getFullYear(), value.getMonth(), value.getDate()));
		this.$.item.setColor(color);
	},
	fillDate: function(days, colors) {
		this.days = days;
		this.colors = colors;
		this.$.repeater.setCount(7);
	}
});

enyo.kind({
	name: "moon.Calendar",
	classes: "moon-calendar",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.
			_inEvent.value_ contains the current Date object (standard JS Date object).
		*/
		onChange: ""
	},
	handlers: {
		ontap: "doTap", //*onChange events coming from consituent controls (hour)
		onChange: "updateCalendar"
	},
	published: {
		//* Text to be displayed in the _currentValue_ control if no item is currently selected.
		noneText: "",
		/**
			Current locale used for formatting. Can be set after control
			creation, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en_us",
		/**
			The current Date object. When a Date object is passed to _setValue_,
			the control is updated to reflect the new value. _getValue_ returns
			a Date object.
		*/
		value: null,
		/**
			The maximum number of weeks to display in a screen.
			If this value over 9, it may show dates of 2 month later.
			(it is unexpected input, makes err)
		*/
		maxWeeks: 6,
		days: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],
		dateArray: [],
		colorArray: []
	},
	components: [
		{name: "datePicker", kind: "moon.DatePicker"},
		//{kind: 'enyo.Spotlight'},
		{name:"repeater", kind: "enyo.FlyweightRepeater", clientClasses: "moon-calendar-week", onSetupItem: "setupDays", count: 7, components: [
			{name: "day", classes: "moon-calendar-date"}
		]},
		{name: "dates"}
	],
	create: function() {
		this.inherited(arguments);
		if (enyo.g11n) {
			this.locale = enyo.g11n.currentLocale().getLocale();
		}
		this.initDefaults();
	//	this.noneTextChanged();
	},
	initDefaults: function() {
		//Attempt to use the g11n lib (ie assume it is loaded)
		if (enyo.g11n) {
			this._tf = new enyo.g11n.Fmts({locale:this.locale});
		}

		this.value = this.value || this.$.datePicker.getValue() || new Date();
		this.setupLayout();
//		this.setupCalendar(this._tf ? this._tf.getTimeFieldOrder() : 'hma');
		this.valueChanged();
	},
	/**
		Set days from the first day to the last day.
		Initially, SUN is the first day and SAT is the last day.
	*/
	setupDays: function(inSender, inEvent) {
		var index = inEvent.index;
		this.$.day.setContent(this.days[index]);
	},
	/**
		Set a layout of calendar.
		The content of it is not prepared at this time.
	*/
	setupLayout: function() {
		for (var i = 0; i < this.maxWeeks; i++) {
			var days = [];
			this.$.dates.createComponent(
				{kind: "moon.CalendarWeek", days: days}
			)
		}
	},
	/**
		Set the first week of this month.
		Before the first day of this month, some days from previous month will fill calendar
	*/
	setupPrevMonth: function() {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setDate(0);
		var thisYear = dt.getFullYear(),
			daysOfPrevMonth = dt.getDate(),
			dayOfLastDate = dt.getDay(),
			prevMonth = dt.getMonth();
		var firstDateOfWeek = daysOfPrevMonth - dayOfLastDate;
		if (dayOfLastDate != 0) {
			//var dateArray = [];
			for (var i = firstDateOfWeek; i <= daysOfPrevMonth; i++) {
				this.dateArray.push(new Date(thisYear, prevMonth, i));
				this.colorArray.push(1);
			}
		}
	},
	/**
		Set the last week of this month.
		After the last day of this month, some days from next month will fill calendar
	*/
	setupNextMonth: function(monthLength) {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setMonth(dt.getMonth() + 1);

		var thisYear = dt.getFullYear(),
			nextMonth = dt.getMonth(),
			thisDate = dt.getDate();
		var offset = this.maxWeeks * 7 - this.dateArray.length + 1;
		for (var i = 1; i < offset; i++) {
			this.dateArray.push(new Date(thisYear, nextMonth, i));
			this.colorArray.push(1);
		}
	},
	setupCalendar: function(ordering) {
		//* Make empty
		this.dateArray = [];
		this.colorArray = [];

		this.setupPrevMonth();

		var thisYear = this.value.getFullYear(),
			thisMonth = this.value.getMonth();
		var	monthLength = this.monthLength(thisYear, thisMonth);
		for (var i = 1; i <= monthLength; i++) {
			this.dateArray.push(new Date(thisYear, thisMonth, i));
			this.colorArray.push(0);
		}

		this.setupNextMonth(monthLength);
	},
	fillDate: function() {
		var calendarWeeks =this.$.dates.getControls();
		for (var i = 0; i < this.dateArray.length / 7; i++) {
			var days = [],
				colors = [];
			for (var j = 0; j < 7; j++) {
				days.push(this.dateArray[i * 7 + j]);
				colors.push(this.colorArray[i * 7 + j]);
			}
			calendarWeeks[i].fillDate(days, colors);
		}
	},
	parseDate: function(ordering) {
	},
	/**
		When value of DatePicker is changed, it will update Calendar
	*/
	updateCalendar: function(inSender, inEvent) {
		//* Avoid onChange events coming from itself
		if (inEvent && inEvent.originator == this || inEvent.originator.kind == "Selection") {
			return;
		}
		var value = this.$.datePicker.getValue();
		var year = value.getFullYear(),
			month = value.getMonth();
		//* Determine whether calender need to redraw or not
		if (year != this.value.getFullYear() || month != this.value.getMonth()) {
			this.setValue(new Date(value.getFullYear(), value.getMonth(), value.getDate()));
		} else {
			this.value.setYear(year);
			this.value.setMonth(month);
			this.value.setDate(value.getDate());
		}
		return true;
	},
	monthLength: function(inYear, inMonth) {
		// determine number of days in a particular month/year
		return 32 - new Date(inYear, inMonth, 32).getDate();
	},
	/**
		Change value of DatePicker with selected CalendarDate.
	*/
	doTap: function(inSender, inEvent) {
		if (inEvent.originator.kind == "moon.CalendarDate") {
			var value = inEvent.originator.value;
			this.$.datePicker.setValue(new Date(value.getFullYear(),
						value.getMonth(),
						value.getDate()));
		}
		return true;
	},
	valueChanged: function(inOld) {
		this.setupCalendar();
		this.fillDate();
		this.$.dates.render();
	},
	localeChanged: function() {
		this.refresh();
	},
	refresh: function(){
		this.destroyClientControls();
		this.initDefaults();
		this.render();
	}
});<commit_msg>Add header content for moon.DatePicker<commit_after>enyo.kind({
	name: "moon.CalendarDate",
	kind: "enyo.Button",
	classes: "moon-calendar-date",
	spotlight: true,
	events: {
		ontap: ""
	},
	published: {
		value: null,
		color: 0,
	},
	colorChanged: function(inOld) {
		if (this.color) {
			this.addClass("moon-calendar-date-shadow");
		} else {
			this.removeClass("moon-calendar-date-shadow");
		}
	},
	valueChanged: function() {
		this.setContent(this.value.getDate());
	},
});

enyo.kind({
	name: "moon.CalendarWeek",
	published: {
		days: [],
		colors: [],
	},
	handlers: {
		ontap: "doTap"
	},
	components: [
		{name:"repeater", kind: "enyo.FlyweightRepeater", clientClasses: "moon-calendar-week", onSetupItem: "setupItem", count: 7, components: [
			{name: "item", kind: "moon.CalendarDate"}
		]},
	],
	/**
		Usually when we select same item, setupItem() is not called
		because setupItem() is called when changing selection is occured in repeater.
	*/
	doTap: function(inSender, inEvent) {
		if (this.$.repeater.isSelected(inEvent.index)) {
			this.setupItem(inSender, inEvent);
		}
	},
	setupItem: function(inSender, inEvent) {
		var index = inEvent.index;
		var value = this.days[index],
			color = this.colors[index];
		this.$.item.setValue(new Date(value.getFullYear(), value.getMonth(), value.getDate()));
		this.$.item.setColor(color);
	},
	fillDate: function(days, colors) {
		this.days = days;
		this.colors = colors;
		this.$.repeater.setCount(7);
	}
});

enyo.kind({
	name: "moon.Calendar",
	classes: "moon-calendar",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.
			_inEvent.value_ contains the current Date object (standard JS Date object).
		*/
		onChange: ""
	},
	handlers: {
		ontap: "doTap", //*onChange events coming from consituent controls (hour)
		onChange: "updateCalendar"
	},
	published: {
		//* Text to be displayed in the _currentValue_ control if no item is currently selected.
		noneText: "",
		/**
			Current locale used for formatting. Can be set after control
			creation, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en_us",
		/**
			The current Date object. When a Date object is passed to _setValue_,
			the control is updated to reflect the new value. _getValue_ returns
			a Date object.
		*/
		value: null,
		/**
			The maximum number of weeks to display in a screen.
			If this value over 9, it may show dates of 2 month later.
			(it is unexpected input, makes err)
		*/
		maxWeeks: 6,
		days: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],
		dateArray: [],
		colorArray: []
	},
	components: [
		{name: "datePicker", kind: "moon.DatePicker", noneText: "Pick a Date", content: "Date", classes: "moon-date-picker-wrapper"},
		//{kind: 'enyo.Spotlight'},
		{name:"repeater", kind: "enyo.FlyweightRepeater", clientClasses: "moon-calendar-week", onSetupItem: "setupDays", count: 7, components: [
			{name: "day", classes: "moon-calendar-date"}
		]},
		{name: "dates"}
	],
	create: function() {
		this.inherited(arguments);
		if (enyo.g11n) {
			this.locale = enyo.g11n.currentLocale().getLocale();
		}
		this.initDefaults();
	//	this.noneTextChanged();
	},
	initDefaults: function() {
		//Attempt to use the g11n lib (ie assume it is loaded)
		if (enyo.g11n) {
			this._tf = new enyo.g11n.Fmts({locale:this.locale});
		}

		this.value = this.value || this.$.datePicker.getValue() || new Date();
		this.setupLayout();
//		this.setupCalendar(this._tf ? this._tf.getTimeFieldOrder() : 'hma');
		this.valueChanged();
	},
	/**
		Set days from the first day to the last day.
		Initially, SUN is the first day and SAT is the last day.
	*/
	setupDays: function(inSender, inEvent) {
		var index = inEvent.index;
		this.$.day.setContent(this.days[index]);
	},
	/**
		Set a layout of calendar.
		The content of it is not prepared at this time.
	*/
	setupLayout: function() {
		for (var i = 0; i < this.maxWeeks; i++) {
			var days = [];
			this.$.dates.createComponent(
				{kind: "moon.CalendarWeek", days: days}
			)
		}
	},
	/**
		Set the first week of this month.
		Before the first day of this month, some days from previous month will fill calendar
	*/
	setupPrevMonth: function() {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setDate(0);
		var thisYear = dt.getFullYear(),
			daysOfPrevMonth = dt.getDate(),
			dayOfLastDate = dt.getDay(),
			prevMonth = dt.getMonth();
		var firstDateOfWeek = daysOfPrevMonth - dayOfLastDate;
		if (dayOfLastDate != 0) {
			//var dateArray = [];
			for (var i = firstDateOfWeek; i <= daysOfPrevMonth; i++) {
				this.dateArray.push(new Date(thisYear, prevMonth, i));
				this.colorArray.push(1);
			}
		}
	},
	/**
		Set the last week of this month.
		After the last day of this month, some days from next month will fill calendar
	*/
	setupNextMonth: function(monthLength) {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setMonth(dt.getMonth() + 1);

		var thisYear = dt.getFullYear(),
			nextMonth = dt.getMonth(),
			thisDate = dt.getDate();
		var offset = this.maxWeeks * 7 - this.dateArray.length + 1;
		for (var i = 1; i < offset; i++) {
			this.dateArray.push(new Date(thisYear, nextMonth, i));
			this.colorArray.push(1);
		}
	},
	setupCalendar: function(ordering) {
		//* Make empty
		this.dateArray = [];
		this.colorArray = [];

		this.setupPrevMonth();

		var thisYear = this.value.getFullYear(),
			thisMonth = this.value.getMonth();
		var	monthLength = this.monthLength(thisYear, thisMonth);
		for (var i = 1; i <= monthLength; i++) {
			this.dateArray.push(new Date(thisYear, thisMonth, i));
			this.colorArray.push(0);
		}

		this.setupNextMonth(monthLength);
	},
	fillDate: function() {
		var calendarWeeks =this.$.dates.getControls();
		for (var i = 0; i < this.dateArray.length / 7; i++) {
			var days = [],
				colors = [];
			for (var j = 0; j < 7; j++) {
				days.push(this.dateArray[i * 7 + j]);
				colors.push(this.colorArray[i * 7 + j]);
			}
			calendarWeeks[i].fillDate(days, colors);
		}
	},
	parseDate: function(ordering) {
	},
	/**
		When value of DatePicker is changed, it will update Calendar
	*/
	updateCalendar: function(inSender, inEvent) {
		//* Avoid onChange events coming from itself
		if (inEvent && inEvent.originator == this || inEvent.originator.kind == "Selection") {
			return;
		}
		var value = this.$.datePicker.getValue();
		var year = value.getFullYear(),
			month = value.getMonth();
		//* Determine whether calender need to redraw or not
		if (year != this.value.getFullYear() || month != this.value.getMonth()) {
			this.setValue(new Date(value.getFullYear(), value.getMonth(), value.getDate()));
		} else {
			this.value.setYear(year);
			this.value.setMonth(month);
			this.value.setDate(value.getDate());
		}
		return true;
	},
	monthLength: function(inYear, inMonth) {
		// determine number of days in a particular month/year
		return 32 - new Date(inYear, inMonth, 32).getDate();
	},
	/**
		Change value of DatePicker with selected CalendarDate.
	*/
	doTap: function(inSender, inEvent) {
		if (inEvent.originator.kind == "moon.CalendarDate") {
			var value = inEvent.originator.value;
			this.$.datePicker.setValue(new Date(value.getFullYear(),
						value.getMonth(),
						value.getDate()));
		}
		return true;
	},
	valueChanged: function(inOld) {
		this.setupCalendar();
		this.fillDate();
		this.$.dates.render();
	},
	localeChanged: function() {
		this.refresh();
	},
	refresh: function(){
		this.destroyClientControls();
		this.initDefaults();
		this.render();
	}
});<|endoftext|>


<commit_before>cmpRipple = function(elements, options) {
  //TODO: add version/copyright notice + version tag
  //TODO: build encapsulated JS file
  //TODO: active state should listen on mousedown + stay triggered on click
  //TODO: destroy() function removing click event handlers
  //TODO: build animationEvent/rippleElement only once?
  //TODO: add init-class for elements to apply standard attributes
  //TODO: specify browser support
  //TODO: jQuery-object-checker in case someone forgets `[0]`

  'use strict';

  /* options */
  if (!options) {options = {};}

  if (!options.animationClass) { options.animationClass = 'cmp-ripple-animation'; }
  if (!options.bubble) { options.bubble = false; }

  //get browser prefixed animation event
  var animationEvent = stylePrefix('animationend');

  //check for element + animation event support
  if (elements && animationEvent) {

    //assume single element
    if (!(elements instanceof NodeList)) {
      elements = [elements];
    }

    for (var index = 0; index < elements.length; index++) {
      var rippleMe = elements[index];

      rippleMe.addEventListener("mousedown", function( clickEvent ) {

        startRipple(rippleMe, clickEvent);

      rippleMe.addEventListener("mouseup", function( clickEvent ) {
        rippleRemove(clickEvent.target.querySelector('.cmp-ripple-animation'));
      });

      var startRipple = function(element, clickEvent) {
        //prevent event bubbling
        if (!options.bubble) {
          clickEvent.stopPropagation();
        }

        //offset relation = (click offset - element offset) ÷ element dimension
        var offsetRel = {
          left : ( clickEvent.clientX - offset(clickEvent.target).left ) / clickEvent.target.offsetWidth,
          top  : ( clickEvent.clientY - offset(clickEvent.target).top ) / clickEvent.target.offsetHeight
        }

        //create element for ripple animation
        var ripple = document.createElement('span');
        ripple.className = options.animationClass;
        ripple.setAttribute('style','top: '+ offsetRel.top*100 +'%; left: '+ offsetRel.left*100 +'%;');

        //trigger animation
        clickEvent.target.insertBefore(ripple, clickEvent.target.firstChild);
      }

    }

    var rippleRemove = function(ripple) {
      ripple.className += ' cmp-ripple-remove';

      ripple.addEventListener(stylePrefix('transitionend'), function(event) {
        event.target.parentNode.removeChild(event.target);
      });
    }

  }
}

//helper function for element offset
//via https://youmightnotneedjquery.com
offset = function(element){
  var rect = element.getBoundingClientRect();
  /*
    note:

    we removed `document.body.scrollTop` and `document.body.scrollLeft` because
    these aren't respected – we need to checkback in IE

  */

  return offsetObject =
    {
      top: rect.top,
      left: rect.left
    }

}

/**
 * Testing on prefixes
 *
 * stolen from David Walsh here:
 * https://davidwalsh.name/css-animation-callback
 * originally stolen from Modernizr:
 *
 */
function stylePrefix(attributeName){
  var prefixStyleLookup = {
    'animationend': {
      'animation':'animationend',
      'WebkitAnimation':'webkitAnimationEnd'
    },
    'transitionend': {
      'transition':'transitionend',
      'WebkitTransition':'webkitTransitionEnd'
    }
  }

  var attribute;
  var el = document.createElement('fakeTestElement');
  for(attribute in prefixStyleLookup[attributeName]){
      if( el.style[attribute] !== undefined ){
          return prefixStyleLookup[attributeName][attribute];
      }
  }
}<commit_msg>JS: forgot-bracket-bug<commit_after>cmpRipple = function(elements, options) {
  //TODO: add version/copyright notice + version tag
  //TODO: build encapsulated JS file
  //TODO: active state should listen on mousedown + stay triggered on click
  //TODO: destroy() function removing click event handlers
  //TODO: build animationEvent/rippleElement only once?
  //TODO: add init-class for elements to apply standard attributes
  //TODO: specify browser support
  //TODO: jQuery-object-checker in case someone forgets `[0]`

  'use strict';

  /* options */
  if (!options) {options = {};}

  if (!options.animationClass) { options.animationClass = 'cmp-ripple-animation'; }
  if (!options.bubble) { options.bubble = false; }

  //get browser prefixed animation event
  var animationEvent = stylePrefix('animationend');

  //check for element + animation event support
  if (elements && animationEvent) {

    //assume single element
    if (!(elements instanceof NodeList)) {
      elements = [elements];
    }

    for (var index = 0; index < elements.length; index++) {
      var rippleMe = elements[index];

      rippleMe.addEventListener("mousedown", function( clickEvent ) {

        startRipple(rippleMe, clickEvent);
      });

      rippleMe.addEventListener("mouseup", function( clickEvent ) {
        rippleRemove(clickEvent.target.querySelector('.cmp-ripple-animation'));
      });

      var startRipple = function(element, clickEvent) {
        //prevent event bubbling
        if (!options.bubble) {
          clickEvent.stopPropagation();
        }

        //offset relation = (click offset - element offset) ÷ element dimension
        var offsetRel = {
          left : ( clickEvent.clientX - offset(clickEvent.target).left ) / clickEvent.target.offsetWidth,
          top  : ( clickEvent.clientY - offset(clickEvent.target).top ) / clickEvent.target.offsetHeight
        }

        //create element for ripple animation
        var ripple = document.createElement('span');
        ripple.className = options.animationClass;
        ripple.setAttribute('style','top: '+ offsetRel.top*100 +'%; left: '+ offsetRel.left*100 +'%;');

        //trigger animation
        clickEvent.target.insertBefore(ripple, clickEvent.target.firstChild);
      }

    }

    var rippleRemove = function(ripple) {
      ripple.className += ' cmp-ripple-remove';

      ripple.addEventListener(stylePrefix('transitionend'), function(event) {
        event.target.parentNode.removeChild(event.target);
      });
    }

  }
}

//helper function for element offset
//via https://youmightnotneedjquery.com
offset = function(element){
  var rect = element.getBoundingClientRect();
  /*
    note:

    we removed `document.body.scrollTop` and `document.body.scrollLeft` because
    these aren't respected – we need to checkback in IE

  */

  return offsetObject =
    {
      top: rect.top,
      left: rect.left
    }

}

/**
 * Testing on prefixes
 *
 * stolen from David Walsh here:
 * https://davidwalsh.name/css-animation-callback
 * originally stolen from Modernizr:
 *
 */
function stylePrefix(attributeName){
  var prefixStyleLookup = {
    'animationend': {
      'animation':'animationend',
      'WebkitAnimation':'webkitAnimationEnd'
    },
    'transitionend': {
      'transition':'transitionend',
      'WebkitTransition':'webkitTransitionEnd'
    }
  }

  var attribute;
  var el = document.createElement('fakeTestElement');
  for(attribute in prefixStyleLookup[attributeName]){
      if( el.style[attribute] !== undefined ){
          return prefixStyleLookup[attributeName][attribute];
      }
  }
}<|endoftext|>


<commit_before>import path from 'path'
import glob from 'glob'
import merge from 'deepmerge'

import detectSeleniumBackend from '../helpers/detectSeleniumBackend'

const HOOKS = ['before', 'beforeSuite', 'beforeHook', 'beforeTest', 'beforeCommand',
               'afterCommand', 'afterTest', 'afterHook', 'afterSuite', 'after']
const DEFAULT_TIMEOUT = 10000
const NOOP = function () {}
const DEFAULT_CONFIGS = {
    specs: [],
    exclude: [],
    logLevel: 'silent',
    coloredLogs: true,
    baseUrl: null,
    waitforTimeout: 1000,
    framework: 'mocha',
    reporter: [],
    maxInstances: 1,

    /**
     * framework defaults
     */
    mochaOpts: {
        timeout: DEFAULT_TIMEOUT
    },
    jasmineNodeOpts: {
        defaultTimeoutInterval: DEFAULT_TIMEOUT
    },

    /**
     * hooks
     */
    onPrepare: NOOP,
    before: [],
    beforeSuite: [],
    beforeHook: [],
    beforeTest: [],
    beforeCommand: [],
    afterCommand: [],
    afterTest: [],
    afterHook: [],
    afterSuite: [],
    after: [],
    onComplete: NOOP
}

class ConfigParser {
    constructor () {
        this._config = DEFAULT_CONFIGS
        this._capabilities = []
    }

    /**
     * merges config file with default values
     * @param {String} filename path of file relative to current directory
     */
    addConfigFile (filename) {
        if (typeof filename !== 'string') {
            throw new Error('addConfigFile requires filepath')
        }

        var filePath = path.resolve(process.cwd(), filename)

        try {
            var fileConfig = require(filePath).config

            if (typeof fileConfig !== 'object') {
                throw new Error('configuration file exports no config object')
            }

            /**
             * merge capabilities
             */
            this._capabilities = merge(this._capabilities, fileConfig.capabilities)
            delete fileConfig.capabilities

            /**
             * add service hooks and remove them from config
             */
            this.addService(fileConfig)
            for (let hookName of HOOKS) {
                delete fileConfig[hookName]
            }

            this._config = merge(this._config, fileConfig)

            /**
             * detect Selenium backend
             */
            this._config = merge(detectSeleniumBackend(this._config), this._config)
        } catch (e) {
            console.error('Failed loading configuration file: ', filePath)
            throw e
        }
    }

    /**
     * merge external object with config object
     * @param  {Object} object  desired object to merge into the config object
     */
    merge (object) {
        this._config = merge(this._config, object)

        /**
         * user and key could get added via cli arguments so we need to detect again
         * Note: cli arguments are on the right and overwrite config
         */
        this._config = merge(detectSeleniumBackend(this._config), this._config)
    }

    /**
     * add hooks from services to runner config
     * @param {Object} service  a service is basically an object that contains hook methods
     */
    addService (service) {
        for (let hookName of HOOKS) {
            if (!service[hookName]) {
                continue
            } else if (typeof service[hookName] === 'function') {
                this._config[hookName].push(service[hookName])
            }
        }
    }

    /**
     * get excluded files from config pattern
     */
    getSpecs (capSpecs, capExclude) {
        let specs = ConfigParser.getFilePaths(this._config.specs)
        let exclude = ConfigParser.getFilePaths(this._config.exclude)

        if (Array.isArray(capSpecs)) {
            specs = specs.concat(capSpecs)
        }
        if (Array.isArray(capExclude)) {
            exclude = exclude.concat(capExclude)
        }

        return specs.filter(spec => exclude.indexOf(spec) < 0)
    }

    /**
     * return configs
     */
    getConfig () {
        return this._config
    }

    /**
     * return capabilities
     */
    getCapabilities (i) {
        if (typeof i === 'number' && this._capabilities[i]) {
            return this._capabilities[i]
        }

        return this._capabilities
    }

    /**
     * returns a flatten list of globed files
     *
     * @param  {String[]} filenames  list of files to glob
     * @return {String[]} list of files
     */
    static getFilePaths (patterns, omitWarnings) {
        let files = []

        for (let pattern of patterns) {
            let filenames = glob.sync(pattern)

            filenames = filenames.filter(path =>
                path.slice(-3) === '.js' ||
                path.slice(-8) === '.feature' ||
                path.slice(-7) === '.coffee')

            filenames = filenames.map(path =>
                process.cwd() + '/' + path)

            if (filenames.length === 0 && !omitWarnings) {
                console.warn('pattern', pattern, 'did not match any file')
            }

            files = merge(files, filenames)
        }

        return files
    }
}

export default ConfigParser
<commit_msg>make sure service hook is executed with the right scope<commit_after>import path from 'path'
import glob from 'glob'
import merge from 'deepmerge'

import detectSeleniumBackend from '../helpers/detectSeleniumBackend'

const HOOKS = ['before', 'beforeSuite', 'beforeHook', 'beforeTest', 'beforeCommand',
               'afterCommand', 'afterTest', 'afterHook', 'afterSuite', 'after']
const DEFAULT_TIMEOUT = 10000
const NOOP = function () {}
const DEFAULT_CONFIGS = {
    specs: [],
    exclude: [],
    logLevel: 'silent',
    coloredLogs: true,
    baseUrl: null,
    waitforTimeout: 1000,
    framework: 'mocha',
    reporter: [],
    maxInstances: 1,

    /**
     * framework defaults
     */
    mochaOpts: {
        timeout: DEFAULT_TIMEOUT
    },
    jasmineNodeOpts: {
        defaultTimeoutInterval: DEFAULT_TIMEOUT
    },

    /**
     * hooks
     */
    onPrepare: NOOP,
    before: [],
    beforeSuite: [],
    beforeHook: [],
    beforeTest: [],
    beforeCommand: [],
    afterCommand: [],
    afterTest: [],
    afterHook: [],
    afterSuite: [],
    after: [],
    onComplete: NOOP
}

class ConfigParser {
    constructor () {
        this._config = DEFAULT_CONFIGS
        this._capabilities = []
    }

    /**
     * merges config file with default values
     * @param {String} filename path of file relative to current directory
     */
    addConfigFile (filename) {
        if (typeof filename !== 'string') {
            throw new Error('addConfigFile requires filepath')
        }

        var filePath = path.resolve(process.cwd(), filename)

        try {
            var fileConfig = require(filePath).config

            if (typeof fileConfig !== 'object') {
                throw new Error('configuration file exports no config object')
            }

            /**
             * merge capabilities
             */
            this._capabilities = merge(this._capabilities, fileConfig.capabilities)
            delete fileConfig.capabilities

            /**
             * add service hooks and remove them from config
             */
            this.addService(fileConfig)
            for (let hookName of HOOKS) {
                delete fileConfig[hookName]
            }

            this._config = merge(this._config, fileConfig)

            /**
             * detect Selenium backend
             */
            this._config = merge(detectSeleniumBackend(this._config), this._config)
        } catch (e) {
            console.error('Failed loading configuration file: ', filePath)
            throw e
        }
    }

    /**
     * merge external object with config object
     * @param  {Object} object  desired object to merge into the config object
     */
    merge (object) {
        this._config = merge(this._config, object)

        /**
         * user and key could get added via cli arguments so we need to detect again
         * Note: cli arguments are on the right and overwrite config
         */
        this._config = merge(detectSeleniumBackend(this._config), this._config)
    }

    /**
     * add hooks from services to runner config
     * @param {Object} service  a service is basically an object that contains hook methods
     */
    addService (service) {
        for (let hookName of HOOKS) {
            if (!service[hookName]) {
                continue
            } else if (typeof service[hookName] === 'function') {
                this._config[hookName].push(service[hookName].bind(service))
            }
        }
    }

    /**
     * get excluded files from config pattern
     */
    getSpecs (capSpecs, capExclude) {
        let specs = ConfigParser.getFilePaths(this._config.specs)
        let exclude = ConfigParser.getFilePaths(this._config.exclude)

        if (Array.isArray(capSpecs)) {
            specs = specs.concat(capSpecs)
        }
        if (Array.isArray(capExclude)) {
            exclude = exclude.concat(capExclude)
        }

        return specs.filter(spec => exclude.indexOf(spec) < 0)
    }

    /**
     * return configs
     */
    getConfig () {
        return this._config
    }

    /**
     * return capabilities
     */
    getCapabilities (i) {
        if (typeof i === 'number' && this._capabilities[i]) {
            return this._capabilities[i]
        }

        return this._capabilities
    }

    /**
     * returns a flatten list of globed files
     *
     * @param  {String[]} filenames  list of files to glob
     * @return {String[]} list of files
     */
    static getFilePaths (patterns, omitWarnings) {
        let files = []

        for (let pattern of patterns) {
            let filenames = glob.sync(pattern)

            filenames = filenames.filter(path =>
                path.slice(-3) === '.js' ||
                path.slice(-8) === '.feature' ||
                path.slice(-7) === '.coffee')

            filenames = filenames.map(path =>
                process.cwd() + '/' + path)

            if (filenames.length === 0 && !omitWarnings) {
                console.warn('pattern', pattern, 'did not match any file')
            }

            files = merge(files, filenames)
        }

        return files
    }
}

export default ConfigParser
<|endoftext|>


<commit_before>/**
 * @file Tool definition for the PC dual purpose selection tool. Provides event
 * handlers and special logic for the selection tool ONLY. Allows selection of
 * entire objects, rotation, scaling and node/segment adjustment.
 **/
"use strict";

module.exports = function(paper) {
  var tool = new paper.Tool();

  // Paper global extenders
  var Path = paper.Path;
  var Point = paper.Point;
  var Rectangle = paper.Rectangle;
  var project = paper.project;

  // Handy internal vars
  var selectionRectangle = null;
  var selectionRectangleScale = null;
  var selectionRectangleRotation = null;
  var segment, path;
  var hitOptions = {
    segments: true,
    stroke: true,
    fill: true,
    tolerance: 5
  };

  // Externalize deseletion
  paper.deselect = function() {
    if (selectionRectangle) selectionRectangle.remove();
  }

  // Tool identification (for building out tool palette)
  tool.name = 'tools.select';
  tool.key = 'select';

  tool.onMouseDown = function(event) {
    segment = path = null;

    var hitResult = project.hitTest(event.point, hitOptions);

    // If we didn't hit anything with hitTest...
    if (!hitResult) {
      // Find any items that are Paths (not layers) that contain the point
      var item = getBoundSelection(event.point);

      // If we actually found something, fake a fill hitResult
      if (item) {
        hitResult = {
          type: 'fill', // SURE it is ;)
          item: item
        };
      } else {
        // Deselect if nothing clicked (feels natural for deselection)
        if (selectionRectangle !== null) {
          selectionRectangle.remove();
        }

        return;
      }
    }

    if (event.modifiers.shift) {
      if (hitResult.type === 'segment') {
        hitResult.segment.remove();
      }
      return;
    }

    if (hitResult) {
      path = hitResult.item;

      if (hitResult.type === 'segment') {
        if (selectionRectangle !== null && path.name === "selection rectangle") {
          if (hitResult.segment.index >= 2 && hitResult.segment.index <= 4) {
            // Rotation hitbox
            selectionRectangleRotation = 0;
          } else {
            // Scale hitbox
            selectionRectangleScale = event.point.subtract(selectionRectangle.bounds.center).length / path.scaling.x;
          }
        } else {
          segment = hitResult.segment;
        }
      } else if (hitResult.type === 'stroke' && path !== selectionRectangle) {
        var location = hitResult.location;
        segment = path.insert(location.index + 1, event.point);
        //path.smooth();
      }

      if ((selectionRectangle === null || selectionRectangle.ppath !== path) && selectionRectangle !== path) {
        initSelectionRectangle(path);
      }
    } else { // Nothing hit
      if (selectionRectangle !== null) selectionRectangle.remove();
    }

    // If a fill hit, move the path
    if (hitResult.type === 'fill') {
      project.activeLayer.addChild(hitResult.item);
    }
  };

  tool.onMouseDrag = function(event) {
    if (selectionRectangleScale !== null) {
      // Path scale adjustment
      var ratio = event.point.subtract(selectionRectangle.bounds.center).length / selectionRectangleScale;
      var scaling = new Point(ratio, ratio);
      selectionRectangle.scaling = scaling;
      selectionRectangle.ppath.scaling = scaling;
      return;
    } else if (selectionRectangleRotation !== null) {
      // Path rotation adjustment
      var rotation = event.point.subtract(selectionRectangle.pivot).angle + 90;
      selectionRectangle.ppath.rotation = rotation;
      selectionRectangle.rotation = rotation;
      return;
    }

    if (segment) {
      // Individual path segment position adjustment
      segment.point.x += event.delta.x;
      segment.point.y += event.delta.y;

      // Smooth -only- non-polygonal paths
      if (!path.data.isPolygonal) path.smooth();

      initSelectionRectangle(path);
    } else if (path) {
      // Path translate position adjustment
      if (path !== selectionRectangle) {
        path.position.x += event.delta.x;
        path.position.y += event.delta.y;
        selectionRectangle.position.x += event.delta.x;
        selectionRectangle.position.y += event.delta.y;
      } else {
        selectionRectangle.position.x += event.delta.x;
        selectionRectangle.position.y += event.delta.y;
        selectionRectangle.ppath.position.x += event.delta.x;
        selectionRectangle.ppath.position.y += event.delta.y;
      }
    }
  };

  tool.onMouseMove = function(event) {
    project.activeLayer.selected = false;

    var boundItem = getBoundSelection(event.point);

    if (event.item) {
      event.item.selected = true;
      paper.setCursor('copy');
    } else if (boundItem) {
      boundItem.selected = true;
      paper.setCursor('move');
    } else {
      paper.setCursor();
    }

    if (selectionRectangle) {
      selectionRectangle.selected = true;
    }
  };

  tool.onMouseUp = function(event) {
    selectionRectangleScale = null;
    selectionRectangleRotation = null;
  };

  tool.onKeyDown = function (event) {
    if (event.key === 'delete' && selectionRectangle) {
      selectionRectangle.ppath.remove();
      selectionRectangle.remove();
      selectionRectangle = null;
    }
  };

  function initSelectionRectangle(path) {
    if (selectionRectangle !== null)
      selectionRectangle.remove();

    var reset = path.rotation === 0 && path.scaling.x === 1 && path.scaling.y === 1;
    var bounds;

    if (reset) {
      // Actually reset bounding box
      bounds = path.bounds;
      path.pInitialBounds = path.bounds;
    } else {
      // No bounding box reset
      bounds = path.pInitialBounds;
    }

    var b = bounds.clone().expand(10, 10);

    selectionRectangle = new Path.Rectangle(b);
    selectionRectangle.pivot = selectionRectangle.position;
    selectionRectangle.insert(2, new Point(b.center.x, b.top));
    selectionRectangle.insert(2, new Point(b.center.x, b.top - 25));
    selectionRectangle.insert(2, new Point(b.center.x, b.top));

    if (!reset) {
      selectionRectangle.position = path.bounds.center;
      selectionRectangle.rotation = path.rotation;
      selectionRectangle.scaling = path.scaling;
    }

    selectionRectangle.strokeWidth = 2;
    selectionRectangle.strokeColor = 'blue';
    selectionRectangle.name = "selection rectangle";
    selectionRectangle.selected = true;
    selectionRectangle.ppath = path;
    selectionRectangle.ppath.pivot = selectionRectangle.pivot;
  }

  function getBoundSelection(point) {
    // Check for items that are overlapping a rect around the event point
    var items = project.getItems({
      overlapping: new Rectangle(point.x - 2, point.y - 2,point.x + 2, point.y + 2)
    });

    var item = null;
    _.each(items, function (i) {
      if (i instanceof Path) {
        if (i.contains(point)) {
          item = i;
        }
      }
    });

    return item;
  }
};

<commit_msg>Fix can't see selection boundary bug<commit_after>/**
 * @file Tool definition for the PC dual purpose selection tool. Provides event
 * handlers and special logic for the selection tool ONLY. Allows selection of
 * entire objects, rotation, scaling and node/segment adjustment.
 **/
"use strict";

module.exports = function(paper) {
  var tool = new paper.Tool();

  // Paper global extenders
  var Path = paper.Path;
  var Point = paper.Point;
  var Rectangle = paper.Rectangle;
  var project = paper.project;

  // Handy internal vars
  var selectionRectangle = null;
  var selectionRectangleScale = null;
  var selectionRectangleRotation = null;
  var segment, path;
  var hitOptions = {
    segments: true,
    stroke: true,
    fill: true,
    tolerance: 5
  };

  // Externalize deseletion
  paper.deselect = function() {
    if (selectionRectangle) selectionRectangle.remove();
  }

  // Tool identification (for building out tool palette)
  tool.name = 'tools.select';
  tool.key = 'select';

  tool.onMouseDown = function(event) {
    segment = path = null;

    var hitResult = project.hitTest(event.point, hitOptions);

    // If we didn't hit anything with hitTest...
    if (!hitResult) {
      // Find any items that are Paths (not layers) that contain the point
      var item = getBoundSelection(event.point);

      // If we actually found something, fake a fill hitResult
      if (item) {
        hitResult = {
          type: 'fill', // SURE it is ;)
          item: item
        };
      } else {
        // Deselect if nothing clicked (feels natural for deselection)
        if (selectionRectangle !== null) {
          selectionRectangle.remove();
          selectionRectangle = null;
        }

        return;
      }
    }

    if (event.modifiers.shift) {
      if (hitResult.type === 'segment') {
        hitResult.segment.remove();
      }
      return;
    }

    if (hitResult) {
      path = hitResult.item;

      if (hitResult.type === 'segment') {
        if (selectionRectangle !== null && path.name === "selection rectangle") {
          if (hitResult.segment.index >= 2 && hitResult.segment.index <= 4) {
            // Rotation hitbox
            selectionRectangleRotation = 0;
          } else {
            // Scale hitbox
            selectionRectangleScale = event.point.subtract(selectionRectangle.bounds.center).length / path.scaling.x;
          }
        } else {
          segment = hitResult.segment;
        }
      } else if (hitResult.type === 'stroke' && path !== selectionRectangle) {
        var location = hitResult.location;
        segment = path.insert(location.index + 1, event.point);
        //path.smooth();
      }

      if ((selectionRectangle === null || selectionRectangle.ppath !== path) && selectionRectangle !== path) {
        initSelectionRectangle(path);
      }
    }

    // If a fill hit, move the path
    if (hitResult.type === 'fill') {
      project.activeLayer.addChild(hitResult.item);
    }
  };

  tool.onMouseDrag = function(event) {
    if (selectionRectangleScale !== null) {
      // Path scale adjustment
      var ratio = event.point.subtract(selectionRectangle.bounds.center).length / selectionRectangleScale;
      var scaling = new Point(ratio, ratio);
      selectionRectangle.scaling = scaling;
      selectionRectangle.ppath.scaling = scaling;
      return;
    } else if (selectionRectangleRotation !== null) {
      // Path rotation adjustment
      var rotation = event.point.subtract(selectionRectangle.pivot).angle + 90;
      selectionRectangle.ppath.rotation = rotation;
      selectionRectangle.rotation = rotation;
      return;
    }

    if (segment) {
      // Individual path segment position adjustment
      segment.point.x += event.delta.x;
      segment.point.y += event.delta.y;

      // Smooth -only- non-polygonal paths
      if (!path.data.isPolygonal) path.smooth();

      initSelectionRectangle(path);
    } else if (path) {
      // Path translate position adjustment
      if (path !== selectionRectangle) {
        path.position.x += event.delta.x;
        path.position.y += event.delta.y;
        selectionRectangle.position.x += event.delta.x;
        selectionRectangle.position.y += event.delta.y;
      } else {
        selectionRectangle.position.x += event.delta.x;
        selectionRectangle.position.y += event.delta.y;
        selectionRectangle.ppath.position.x += event.delta.x;
        selectionRectangle.ppath.position.y += event.delta.y;
      }
    }
  };

  tool.onMouseMove = function(event) {
    project.activeLayer.selected = false;

    var boundItem = getBoundSelection(event.point);

    if (event.item) {
      event.item.selected = true;
      paper.setCursor('copy');
    } else if (boundItem) {
      boundItem.selected = true;
      paper.setCursor('move');
    } else {
      paper.setCursor();
    }

    if (selectionRectangle) {
      selectionRectangle.selected = true;
    }
  };

  tool.onMouseUp = function(event) {
    selectionRectangleScale = null;
    selectionRectangleRotation = null;
  };

  tool.onKeyDown = function (event) {
    if (event.key === 'delete' && selectionRectangle) {
      selectionRectangle.ppath.remove();
      selectionRectangle.remove();
      selectionRectangle = null;
    }
  };

  function initSelectionRectangle(path) {
    if (selectionRectangle !== null) selectionRectangle.remove();

    var reset = path.rotation === 0 && path.scaling.x === 1 && path.scaling.y === 1;
    var bounds;

    if (reset) {
      // Actually reset bounding box
      bounds = path.bounds;
      path.pInitialBounds = path.bounds;
    } else {
      // No bounding box reset
      bounds = path.pInitialBounds;
    }

    var b = bounds.clone().expand(10, 10);

    selectionRectangle = new Path.Rectangle(b);
    selectionRectangle.pivot = selectionRectangle.position;
    selectionRectangle.insert(2, new Point(b.center.x, b.top));
    selectionRectangle.insert(2, new Point(b.center.x, b.top - 25));
    selectionRectangle.insert(2, new Point(b.center.x, b.top));

    if (!reset) {
      selectionRectangle.position = path.bounds.center;
      selectionRectangle.rotation = path.rotation;
      selectionRectangle.scaling = path.scaling;
    }

    selectionRectangle.strokeWidth = 2;
    selectionRectangle.strokeColor = 'blue';
    selectionRectangle.name = "selection rectangle";
    selectionRectangle.selected = true;
    selectionRectangle.ppath = path;
    selectionRectangle.ppath.pivot = selectionRectangle.pivot;
  }

  function getBoundSelection(point) {
    // Check for items that are overlapping a rect around the event point
    var items = project.getItems({
      overlapping: new Rectangle(point.x - 2, point.y - 2,point.x + 2, point.y + 2)
    });

    var item = null;
    _.each(items, function (i) {
      if (i instanceof Path) {
        if (i.contains(point)) {
          item = i;
        }
      }
    });

    return item;
  }
};

<|endoftext|>