<commit_before>/* eslint-env browser */
(function() {
  /**
   * Creates an Array of DOM nodes that match the selector
   * @param selector {string} CSS selector - selector to match against
   * @return {array} Array of DOM nodes
   */
  function getDomNodeArray(selector) {
    var nodes = Array.prototype.slice.apply(document.querySelectorAll(selector));
    if (!nodes) {
      nodes = [];
    }
    return nodes;
  }

  /*
  Adding guests
   */
  var newGuest = document.querySelector('input.new-guest');
  var people = document.querySelector('.attendees');
  var guestsDisplay = document.querySelector('.card-detail-actual.guests');
  newGuest.onkeydown = function(evt) {
    if (evt.keyIdentifier === 'Enter') {
      let enteredPerson = document.createElement('div');
      let enteredPerson2 = document.createElement('div');
      let deletePerson = document.createElement('button');
      deletePerson.innerHTML = "-";
      deletePerson.parent = enteredPerson;

      deletePerson.onclick = function() {
        let dataAttr = this.parent.getAttribute('data-guest');
        let guestToRemove = document.querySelector(`[data-guest="${dataAttr}"]`);
        people.removeChild(this.parent);
        guestsDisplay.removeChild(guestToRemove); // remove from card-detail preview
      };
      enteredPerson.innerHTML = newGuest.value;
      enteredPerson.setAttribute('data-guest', newGuest.value); // for easy access later
      enteredPerson.appendChild(deletePerson);
      // add to card-detail preview
      enteredPerson2.innerHTML = newGuest.value;
      enteredPerson2.setAttribute('data-guest', newGuest.value); // for easy access later
      guestsDisplay.appendChild(enteredPerson2);

      people.appendChild(enteredPerson);
      newGuest.value = "";
    }
  };
  /*
  Adding functionality to hide/show guest in card-detail preview
   */
  var attendeesCheckbox = document.getElementById('attendees-list');
  var guestsDisplayContainer = document.querySelector('.card-detail.guests');
  attendeesCheckbox.onclick = function() {
    if (this.checked === true) {
      guestsDisplayContainer.classList.remove('hidden')
    } else {
      guestsDisplayContainer.classList.add('hidden');
    }
  }

  /*
  class="model model-*" Refer to inputs by the name after class model-*
   */
  function Model() {
    var self = this;

    function Binding(elem, value) {
      elem = elem || null;
      value = value || null;

      this.elem = elem;
      this.value = value;
      this.hasChanged = false;
      this.oninput = function() {};
    }

    // pulls values from elems of class="model model-*" to create Model's raw info and set up 1-way binding
    getDomNodeArray('.model').forEach(function(elem) {
      elem.classList.forEach(function(className) {
          var possiblyMatch = className.match(/model\-/g);
          if (possiblyMatch) {
              // create the Model value
              var name = className.slice(6);
              var value = elem.value;

              // set default values
              if (name.indexOf('Month') > -1) {
                  value = value - 1;
              } else if (name.indexOf('Time' > -1)) {
                  value = value || '00:00';
              }

              self[name] = self[name] || new Binding(elem, value);
              elem.binding = elem.binding || self[name];

              // bind data oninput
              elem.oninput = function() {
                  self[name].hasChanged = true;
                  self[name] = self[name] || new Binding(elem, value);
                  self[name].value = elem.value;

                  // for callbacks
                  self[name].oninput();
              };
            }
        });
    });
  };

/*
To create a placeholder effect. Assumes that display element starts with 'placeholder' class
 */
function displayWithPlaceholder(inputBinding, displayElem, placeholder) {
    if (displayElem.classList.contains('placeholder')) {
        displayElem.classList.remove('placeholder');
    };
    if (inputBinding.value === '') {
        inputBinding.value = placeholder;
        // let the model know that input has gone back to default
        inputBinding.hasChanged = false;

        displayElem.classList.add('placeholder');
    }
    displayElem.innerHTML = inputBinding.value;
  };
  var model = new Model();

  // Update the view oninput
  model.title.oninput = function() {
      var titleDisplay = document.querySelector('.title-display');
      displayWithPlaceholder(model.title, titleDisplay, 'Untitled Event');
  };

  model.type.oninput = function() {
      var typeDisplay = document.querySelector('.card-detail-actual.what');
      displayWithPlaceholder(model.type, typeDisplay, 'Description');
  };

  model.host.oninput = function() {
      var hostDisplay = document.querySelector('.card-detail-actual.who');
      displayWithPlaceholder(model.host, hostDisplay, 'Host');
  };

  model.starttime.oninput = function() {
      var startTimeDisplay = document.querySelector('.card-detail-actual.starttime');
      displayWithPlaceholder(model.starttime, startTimeDisplay, 'Start Time');
  };

  model.endtime.oninput = function() {
      var endTimeDisplay = document.querySelector('.card-detail-actual.endtime');
      displayWithPlaceholder(model.endtime, endTimeDisplay, 'End Time');
  };

  function validate() {
      var self = this;
      var allGood = false;
      var errorMessage = 'Please correct the following errors:
';
      var people = getDomNodeArray('.people>div');

      var validations = [{
          errorMessage: 'Please include a title.',
          validationMethod: function() {
              return model.title.hasChanged;
          }
      }, {
          errorMessage: 'Please include a description.',
          validationMethod: function() {
              return model.description.hasChanged;
          }
      }, {
          errorMessage: 'Please include guests.',
          validationMethod: function() {
              if (people.length > 0) {
                  return true;
              } else {
                  return false;
              }
          }
      }, {
          errorMessage: 'Please include valid email addresses.',
          validationMethod: function() {
              var areReal = false;
              var emailRegex = new RegExp("^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$");
              people.forEach(function(guest, index) {
                  var result = emailRegex.exec(guest.value);
                  if (result && result['index'] === 0) {
                      if (index === 0) {
                          areReal = true;
                      } else {
                          areReal = areReal && true;
                      }
                  } else {
                      areReal = areReal && false;
                  }
              });
              return areReal;
          }
      }];

      validations.forEach(function(val, index) {
          if (val.validationMethod(self)) {
              if (index === 0) {
                  allGood = true;
              } else {
                  allGood = allGood && true;
              }
          } else {
              allGood = allGood && false;
              errorMessage = errorMessage + val.errorMessage + '
';
          }
      });
      errorMessage = errorMessage.trim();

      return {
          isValid: allGood,
          errorMessage: errorMessage
      }
  };

  var createButton = document.querySelector('button#create');
  createButton.onclick = function() {
      var validState = validate();

      if (!validState.isValid) {
          var errorMessage = document.querySelector('.error-message');
          errorMessage.innerHTML = validState.errorMessage;
      } else {
          alert('Valid form. Thanks for submitting!');
      }
  };

  /*
  Set up Google Maps autocomplete location input
   */
  var input = document.getElementById('location');
  var whereDisplay = document.querySelector('.card-detail-actual.where');
  var autocomplete = new google.maps.places.Autocomplete(input);

  autocomplete.addListener('place_changed', () => {
    let place = autocomplete.getPlace();
    displayWithPlaceholder({
        value: place.formatted_address
    }, whereDisplay, 'Location');
  });
})();
<commit_msg>Refactor to address eslint problems<commit_after>/* eslint-env browser */
(function() {
  /**
   * Creates an Array of DOM nodes that match the selector
   * @param selector {string} CSS selector - selector to match against
   * @return {array} Array of DOM nodes
   */
  function getDomNodeArray(selector) {
    var nodes = Array.prototype.slice.apply(document.querySelectorAll(selector));
    if (!nodes) {
      nodes = [];
    }
    return nodes;
  }

  /*
  Adding guests
   */
  var newGuest = document.querySelector('input.new-guest');
  var people = document.querySelector('.attendees');
  var guestsDisplay = document.querySelector('.card-detail-actual.guests');
  newGuest.onkeydown = function(evt) {
    if (evt.keyIdentifier === 'Enter') {
      let enteredPerson = document.createElement('div');
      let enteredPerson2 = document.createElement('div');
      let deletePerson = document.createElement('button');
      deletePerson.innerHTML = '-';
      deletePerson.parent = enteredPerson;

      deletePerson.onclick = function() {
        let dataAttr = this.parent.getAttribute('data-guest');
        let guestToRemove = document.querySelector(`[data-guest="${dataAttr}"]`);
        people.removeChild(this.parent);
        guestsDisplay.removeChild(guestToRemove); // remove from card-detail preview
      };
      enteredPerson.innerHTML = newGuest.value;
      enteredPerson.setAttribute('data-guest', newGuest.value); // for easy access later
      enteredPerson.appendChild(deletePerson);
      // add to card-detail preview
      enteredPerson2.innerHTML = newGuest.value;
      enteredPerson2.setAttribute('data-guest', newGuest.value); // for easy access later
      guestsDisplay.appendChild(enteredPerson2);

      people.appendChild(enteredPerson);
      newGuest.value = '';
    }
  };
  /*
  Adding functionality to hide/show guest in card-detail preview
   */
  var attendeesCheckbox = document.getElementById('attendees-list');
  var guestsDisplayContainer = document.querySelector('.card-detail.guests');
  attendeesCheckbox.onclick = function() {
    if (this.checked === true) {
      guestsDisplayContainer.classList.remove('hidden');
    } else {
      guestsDisplayContainer.classList.add('hidden');
    }
  };

  /*
  class="model model-*" Refer to inputs by the name after class model-*
   */
  function Model() {
    var self = this;

    function Binding(elem, value) {
      elem = elem || null;
      value = value || null;

      this.elem = elem;
      this.value = value;
      this.hasChanged = false;
      this.oninput = function() {};
    }

    // pulls values from elems of class="model model-*" to create Model's raw info and set up 1-way binding
    getDomNodeArray('.model').forEach(function(elem) {
      elem.classList.forEach(function(className) {
        var possiblyMatch = className.match(/model\-/g);
        if (possiblyMatch) {
          // create the Model value
          var name = className.slice(6);
          var value = elem.value;

          // set default values
          if (name.indexOf('Month') > -1) {
            value--;
          } else if (name.indexOf('Time' > -1)) {
            value = value || '00:00';
          }

          self[name] = self[name] || new Binding(elem, value);
          elem.binding = elem.binding || self[name];

          // bind data oninput
          elem.oninput = function() {
            self[name].hasChanged = true;
            self[name] = self[name] || new Binding(elem, value);
            self[name].value = elem.value;

            // for callbacks
            self[name].oninput();
          };
        }
      });
    });
  }

  /*
  To create a placeholder effect. Assumes that display element starts with 'placeholder' class
   */
  function displayWithPlaceholder(inputBinding, displayElem, placeholder) {
    if (displayElem.classList.contains('placeholder')) {
      displayElem.classList.remove('placeholder');
    }
    if (inputBinding.value === '') {
      inputBinding.value = placeholder;
      // let the model know that input has gone back to default
      inputBinding.hasChanged = false;

      displayElem.classList.add('placeholder');
    }
    displayElem.innerHTML = inputBinding.value;
  }
  var model = new Model();

  // Update the view oninput
  model.title.oninput = function() {
    var titleDisplay = document.querySelector('.title-display');
    displayWithPlaceholder(model.title, titleDisplay, 'Untitled Event');
  };

  model.type.oninput = function() {
    var typeDisplay = document.querySelector('.card-detail-actual.what');
    displayWithPlaceholder(model.type, typeDisplay, 'Description');
  };

  model.host.oninput = function() {
    var hostDisplay = document.querySelector('.card-detail-actual.who');
    displayWithPlaceholder(model.host, hostDisplay, 'Host');
  };

  model.starttime.oninput = function() {
    var startTimeDisplay = document.querySelector('.card-detail-actual.starttime');
    displayWithPlaceholder(model.starttime, startTimeDisplay, 'Start Time');
  };

  model.endtime.oninput = function() {
    var endTimeDisplay = document.querySelector('.card-detail-actual.endtime');
    displayWithPlaceholder(model.endtime, endTimeDisplay, 'End Time');
  };

  function validate() {
    var self = this;
    var allGood = false;
    var errorMessage = 'Please correct the following errors:
';
    var people = getDomNodeArray('.people>div');

    var validations = [{
      errorMessage: 'Please include a title.',
      validationMethod: function() {
        return model.title.hasChanged;
      }
    }, {
      errorMessage: 'Please include a description.',
      validationMethod: function() {
        return model.description.hasChanged;
      }
    }, {
      errorMessage: 'Please include guests.',
      validationMethod: function() {
        if (people.length > 0) {
          return true;
        }
        return false;
      }
    }, {
      errorMessage: 'Please include valid email addresses.',
      validationMethod: function() {
        var areReal = false;
        var emailRegex = new RegExp('^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$');
        people.forEach(function(guest, index) {
          var result = emailRegex.exec(guest.value);
          if (result && result['index'] === 0) {
            if (index === 0) {
              areReal = true;
            } else {
              areReal = areReal && true;
            }
          } else {
            areReal = areReal && false;
          }
        });
        return areReal;
      }
    }];

    validations.forEach(function(val, index) {
      if (val.validationMethod(self)) {
        if (index === 0) {
          allGood = true;
        } else {
          allGood = allGood && true;
        }
      } else {
        allGood = allGood && false;
        errorMessage = errorMessage + val.errorMessage + '
';
      }
    });
    errorMessage = errorMessage.trim();

    return {
      isValid: allGood,
      errorMessage: errorMessage
    }
  }

  var createButton = document.querySelector('button#create');
  createButton.onclick = function() {
    var validState = validate();

    if (!validState.isValid) {
      var errorMessage = document.querySelector('.error-message');
      errorMessage.innerHTML = validState.errorMessage;
    } else {
      alert('Valid form. Thanks for submitting!');
    }
  };

  /*
  Set up Google Maps autocomplete location input
   */
  var input = document.getElementById('location');
  var whereDisplay = document.querySelector('.card-detail-actual.where');
  var autocomplete = new google.maps.places.Autocomplete(input);

  autocomplete.addListener('place_changed', () => {
    let place = autocomplete.getPlace();
    displayWithPlaceholder({
      value: place.formatted_address
    }, whereDisplay, 'Location');
  });
})();
<|endoftext|>


<commit_before>'use strict';

// Submission ID, S7755084
// Common Name, Black-bellied Whistling-Duck
// Scientific Name, Dendrocygna autumnalis
// Taxonomic Order, 215
// Count, X
// State/Province, US-TX
// County, Cameron
// Location, Brownsville
// Latitude, 25.911388
// Longitude, -97.4904876
// Date, 04-17-2004
// Time,
// Protocol, eBird - Casual Observation
// Duration (Min),
// All Obs Reported,
// Distance Traveled (km),
// Area Covered (ha),
// Number of Observers,
// Breeding Code,
// Species Comments,
// Checklist Comments

var SightingList = function (inRows, inPhotos) {
	this.rows = [];
	this._uniqueValuesCache = {};
	this.rowsByYear = {};
	this.rowsByMonth = { '01': [], '02': [], '03': [], '04': [], '05': [], '06': [], '07': [], '08': [], '09': [], '10': [], '11': [], '12': [] };
	this._speciesByDate = {};
	this._earliestRowByCommonName = null;
	this.earliestDateObject = null;
	this.latestDateObject = null;
	this.dates = [];
	this.dateObjects = [];
	this.dayNames = [];
	this.photos = inPhotos;

	if (inRows) {
		if (inRows instanceof Array) {
			this.addRows(inRows);
		} else {
			throw new Error('not an array');
		}
	}
};

SightingList.omittedCommonNames = [];
SightingList.customDayNames = [];

SightingList.setCustomDayNames = function(inNames) {
	SightingList.customDayNames = inNames;
};

SightingList.setOmittedCommonNames = function(inNames) {
	SightingList.omittedCommonNames = inNames;
};

SightingList.families = [];
SightingList.families.push(['ducks', 0.0, 600.0]);
SightingList.families.push(['game birds', 600.0, 1554.0]);
SightingList.families.push(['loons and grebes', 1555.0, 1700.0]);
SightingList.families.push(['seabirds', 1700.0, 2100.0]);
SightingList.families.push(['wading birds', 2100.0, 2324.0]);
SightingList.families.push(['vultures and kites', 2325.0, 2558.0]);
SightingList.families.push(['hawks and eagles', 2559.0, 3100.0]);
SightingList.families.push(['rails', 3200.0, 3700.0]);
SightingList.families.push(['shorebirds', 3700.0, 4138.0]);
SightingList.families.push(['gulls and alcids', 4139.0, 4400.0]);
SightingList.families.push(['terns', 4400.0, 4600.0]);
SightingList.families.push(['doves', 4600.0, 5100.0]);
SightingList.families.push(['cuckoos', 6500.0, 6951.0]);
SightingList.families.push(['owls', 6952.0, 7700.0]);
SightingList.families.push(['nightjars', 7700.0, 7900.0]);
SightingList.families.push(['hummingbirds and switfts', 8000.0, 9240.0]);
SightingList.families.push(['trogons and allies', 9250.0, 9450.0]);
SightingList.families.push(['woodpeckers', 10000.0, 11500.0]);
SightingList.families.push(['falcons', 11500.0, 11583.0]);
SightingList.families.push(['parrots', 11587.0, 11600.0]);
SightingList.families.push(['tyrants', 13000.0, 14000.0]);
SightingList.families.push(['flycatchers', 14000.0, 15000.0]);
SightingList.families.push(['kingbirds', 15000.0, 16000.0]);
SightingList.families.push(['vireos', 17000.0, 18000.0]);
SightingList.families.push(['jays and crows', 18000.0, 19134.0]);
SightingList.families.push(['swallows', 19135.0, 20000.0]);
SightingList.families.push(['chickadees and titmice', 20000.0, 20883.0]);
SightingList.families.push(['wrens', 20884.0, 22000.0]);
SightingList.families.push(['thrushes', 24000.0, 25000.0]);
SightingList.families.push(['mimics', 26000.0, 27000.0]);
SightingList.families.push(['warblers', 27000.0, 28000.0]);
SightingList.families.push(['tanagers', 28000.0, 29000.0]);
SightingList.families.push(['sparrows', 29000.0, 30338.0]);
SightingList.families.push(['ictarids', 30339.0, 30693.0]);
SightingList.families.push(['finches', 30694.0, 32000.0]);

SightingList.prototype.initialize = function(inData) {
	this.rows = inData.rows;
	this._uniqueValuesCache = inData._uniqueValuesCache;
	this.rowsByYear = inData.rowsByYear;
	this.rowsByMonth = inData.rowsByMonth;
	this._speciesByDate = inData.speciesByDate;
	this._earliestRowByCommonName = inData._earliestRowByCommonName;
	this.earliestDateObject = new Date(inData.earliestDateObject);
	this.latestDateObject = new Date(inData.latestDateObject);
	this.dates = inData.dates;
	this.dateObjects = inData.dateObjects;
	this.dayNames = inData.dayNames;
	this.photos = inData.photos;

	// fix the dates
	for (var index = 0; index < this.dateObjects.length; index++) {
		this.dateObjects[index] = new Date(this.dateObjects[index]);
	}

	for (index = 0; index < this.rows.length; index++) {
		this.rows[index].DateObject = new Date(this.rows[index].DateObject);
	}
};

SightingList.prototype.setGlobalIDs = function() {
	for (var index = 0; index < this.rows.length; index++) {
		var sighting = this.rows[index];
		sighting.id = index;
	}
};

SightingList.prototype.addRows = function(inRows) {
	for (var index = 0; index < inRows.length; index++) {
		var sighting = inRows[index];

		if (sighting['Date']) {
			// Parse the date
			var pieces = sighting['Date'].split('-');

			// order the pieces in a sensible way
			var fixedDateString = [pieces[0], '/', pieces[1], '/', pieces[2]].join('');

			// create and save the new dat
			var newDate = new Date(fixedDateString);
			sighting['DateObject'] = newDate;

			if (this.dates.indexOf(sighting['Date']) < 0) {
				this.dates.push(sighting['Date']);
				this.dateObjects.push(newDate);
				this.dayNames.push(SightingList.customDayNames[sighting['Date']]);
			}

			if (this.earliestDateObject == null || newDate < this.earliestDateObject) {
				this.earliestDateObject = newDate;
			}

			if (this.latestDateObject == null || newDate > this.latestDateObject) {
				this.latestDateObject = newDate;
			}

			if (! this.rowsByYear[pieces[2]]) {
				this.rowsByYear[pieces[2]] = [];
			}
			this.rowsByYear[pieces[2]].push(sighting);

			if (! this.rowsByMonth[pieces[0]]) {
				this.rowsByMonth[pieces[0]] = [];
			}
			this.rowsByMonth[pieces[0]].push(sighting);

		} else {
			console.log('ERROR SIGHTING HAS NO DATE', index, JSON.stringify(sighting));
		}
	}

	this.rows = this.rows.concat(inRows);

	this.dateObjects.sort(function(a, b) { return b - a; });
};

SightingList.prototype.sortByDate = function() {
	// TODO: this is probably unnecessary sort!
	this.rows.sort(function(a, b) { return a['DateObject'] - b['DateObject']; });
}

SightingList.prototype.earliestDateObject = function() {
	return this.earliestDateObject;
};

SightingList.prototype.latestDateObject = function() {
	return this.latestDateObject;
};

SightingList.prototype.filter = function(filterFunc) {
	return this.rows.filter(filterFunc);
};

SightingList.prototype.length = function() {
	return this.rows.length;
};

SightingList.prototype.byYear = function() {
	return this.rowsByYear;
};

SightingList.prototype.byMonth = function() {
	return [
		this.rowsByMonth['01'],
		this.rowsByMonth['02'],
		this.rowsByMonth['03'],
		this.rowsByMonth['04'],
		this.rowsByMonth['05'],
		this.rowsByMonth['06'],
		this.rowsByMonth['07'],
		this.rowsByMonth['08'],
		this.rowsByMonth['09'],
		this.rowsByMonth['10'],
		this.rowsByMonth['11'],
		this.rowsByMonth['12'],
	];
};

SightingList.prototype.getUniqueValues = function(fieldName) {
	if (this._uniqueValuesCache[fieldName]) {
		console.log('returning cached unique values for', fieldName);
	} else {
		console.log('computing unique values for', fieldName);
		var tmpValues = [];
		for (var index = 0; index < this.rows.length; index++) {
			var aValue = this.rows[index][fieldName];
			if (tmpValues.indexOf(aValue) < 0) {
				tmpValues.push(aValue);
			}
		}
		this._uniqueValuesCache[fieldName] = tmpValues;
	}

	return this._uniqueValuesCache[fieldName];
};

SightingList.prototype.getLocationHierarchy = function() {
	var provinces = {};

	for (var index = 0; index < this.rows.length; index++) {
		var aSighting = this.rows[index];
		var province = aSighting['State/Province'];
		var county = aSighting['County'];
		var location = aSighting['Location'];

		if (! provinces[province]) {
			provinces[province] = {};
		}

		if (! provinces[province][county]) {
			provinces[province][county] = [];
		}

		if (provinces[province][county].indexOf(location) < 0) {
			provinces[province][county].push(location);
		}
	}

	return provinces;
};

SightingList.getFamily = function(inTaxonomicOrderID) {
	for (var index = 0; index < SightingList.families.length; index++) {
		var tmp = SightingList.families[index];
		if ((tmp[1] < inTaxonomicOrderID) && (inTaxonomicOrderID < tmp[2])) {
			return tmp[0];
		}
	}

	return null;
}

SightingList.prototype.getTaxonomyHierarchy = function() {
	var byFamily = {};

	console.log(byFamily);

	for (var index = 0; index < this.rows.length; index++) {
		var aSighting = this.rows[index];
		var commonName = aSighting['Common Name'];
		if (aSighting['Taxonomic Order']) {
			var taxoID = parseFloat(aSighting['Taxonomic Order']);
			var aFamily = SightingList.getFamily(taxoID);

			if (aFamily == null) {
				console.log(taxoID, commonName);
				continue;
			}

			if (! byFamily[aFamily]) {
				byFamily[aFamily] = [];
			}

			if (byFamily[aFamily].indexOf(commonName) < 0) {
				byFamily[aFamily].push(commonName);
				// byFamily[aFamily].push(commonName + ' ' + aSighting['Taxonomic Order']);
			}
		} else {
			console.log('no scientific name', aSighting);
		}
	}

	return byFamily;
};

SightingList.prototype.getSpeciesByDate = function() {
	console.log('computing speciesByDate');

	for (var index = 0; index < this.rows.length; index++) {
		var sighting = this.rows[index];

		if (! this._speciesByDate[sighting['Date']]) {
			this._speciesByDate[sighting['Date']] = {
				commonNames: [],
				dateObject: sighting['DateObject'],
			};
		}
		if (this._speciesByDate[sighting['Date']].commonNames.indexOf(sighting['Common Name']) < 0) {
			this._speciesByDate[sighting['Date']].commonNames.push(sighting['Common Name']);
		}
	};

	return this._speciesByDate;
};

SightingList.prototype.getEarliestByCommonName = function() {
	console.log('computing earliestByCommonName');

	this._earliestRowByCommonName = {};

	for (var index = 0; index < this.rows.length; index++) {
		var sighting = this.rows[index];

		if (SightingList.omittedCommonNames.indexOf(sighting['Common Name']) < 0) {
			if (! this._earliestRowByCommonName[sighting['Common Name']]) {
				this._earliestRowByCommonName[sighting['Common Name']] = sighting;
			} else if (sighting.DateObject < this._earliestRowByCommonName[sighting['Common Name']].DateObject) {
				this._earliestRowByCommonName[sighting['Common Name']] = sighting;
			}
		} else {
			console.log('omit', sighting['Common Name']);
		}
	};

	return this._earliestRowByCommonName;
};

if (typeof module != 'undefined') {
	module.exports = SightingList;
}
<commit_msg>capitalize group names<commit_after>'use strict';

// Submission ID, S7755084
// Common Name, Black-bellied Whistling-Duck
// Scientific Name, Dendrocygna autumnalis
// Taxonomic Order, 215
// Count, X
// State/Province, US-TX
// County, Cameron
// Location, Brownsville
// Latitude, 25.911388
// Longitude, -97.4904876
// Date, 04-17-2004
// Time,
// Protocol, eBird - Casual Observation
// Duration (Min),
// All Obs Reported,
// Distance Traveled (km),
// Area Covered (ha),
// Number of Observers,
// Breeding Code,
// Species Comments,
// Checklist Comments

var SightingList = function (inRows, inPhotos) {
	this.rows = [];
	this._uniqueValuesCache = {};
	this.rowsByYear = {};
	this.rowsByMonth = { '01': [], '02': [], '03': [], '04': [], '05': [], '06': [], '07': [], '08': [], '09': [], '10': [], '11': [], '12': [] };
	this._speciesByDate = {};
	this._earliestRowByCommonName = null;
	this.earliestDateObject = null;
	this.latestDateObject = null;
	this.dates = [];
	this.dateObjects = [];
	this.dayNames = [];
	this.photos = inPhotos;

	if (inRows) {
		if (inRows instanceof Array) {
			this.addRows(inRows);
		} else {
			throw new Error('not an array');
		}
	}
};

SightingList.omittedCommonNames = [];
SightingList.customDayNames = [];

SightingList.setCustomDayNames = function(inNames) {
	SightingList.customDayNames = inNames;
};

SightingList.setOmittedCommonNames = function(inNames) {
	SightingList.omittedCommonNames = inNames;
};

SightingList.families = [];
SightingList.families.push(['Ducks', 0.0, 600.0]);
SightingList.families.push(['Game birds', 600.0, 1554.0]);
SightingList.families.push(['Loons and Grebes', 1555.0, 1700.0]);
SightingList.families.push(['Seabirds', 1700.0, 2100.0]);
SightingList.families.push(['Wading birds', 2100.0, 2324.0]);
SightingList.families.push(['Vultures and kites', 2325.0, 2558.0]);
SightingList.families.push(['Hawks and Eagles', 2559.0, 3100.0]);
SightingList.families.push(['Rails', 3200.0, 3700.0]);
SightingList.families.push(['Shorebirds', 3700.0, 4138.0]);
SightingList.families.push(['Gulls and Alcids', 4139.0, 4400.0]);
SightingList.families.push(['Terns', 4400.0, 4600.0]);
SightingList.families.push(['Doves', 4600.0, 5100.0]);
SightingList.families.push(['Cuckoos', 6500.0, 6951.0]);
SightingList.families.push(['Owls', 6952.0, 7700.0]);
SightingList.families.push(['Nightjars', 7700.0, 7900.0]);
SightingList.families.push(['Hummingbirds and Switfts', 8000.0, 9240.0]);
SightingList.families.push(['Trogons and allies', 9250.0, 9450.0]);
SightingList.families.push(['Wwoodpeckers', 10000.0, 11500.0]);
SightingList.families.push(['Falcons', 11500.0, 11583.0]);
SightingList.families.push(['Parrots', 11587.0, 11600.0]);
SightingList.families.push(['Tyrants', 13000.0, 14000.0]);
SightingList.families.push(['Flycatchers', 14000.0, 15000.0]);
SightingList.families.push(['Kingbirds', 15000.0, 16000.0]);
SightingList.families.push(['Vireos', 17000.0, 18000.0]);
SightingList.families.push(['Jays and Crows', 18000.0, 19134.0]);
SightingList.families.push(['Swallows', 19135.0, 20000.0]);
SightingList.families.push(['Chickadees and Titmice', 20000.0, 20883.0]);
SightingList.families.push(['Wrens', 20884.0, 22000.0]);
SightingList.families.push(['Thrushes', 24000.0, 25000.0]);
SightingList.families.push(['Mimics', 26000.0, 27000.0]);
SightingList.families.push(['Warblers', 27000.0, 28000.0]);
SightingList.families.push(['Tanagers', 28000.0, 29000.0]);
SightingList.families.push(['Sparrows', 29000.0, 30338.0]);
SightingList.families.push(['Ictarids', 30339.0, 30693.0]);
SightingList.families.push(['Finches', 30694.0, 32000.0]);

SightingList.prototype.initialize = function(inData) {
	this.rows = inData.rows;
	this._uniqueValuesCache = inData._uniqueValuesCache;
	this.rowsByYear = inData.rowsByYear;
	this.rowsByMonth = inData.rowsByMonth;
	this._speciesByDate = inData.speciesByDate;
	this._earliestRowByCommonName = inData._earliestRowByCommonName;
	this.earliestDateObject = new Date(inData.earliestDateObject);
	this.latestDateObject = new Date(inData.latestDateObject);
	this.dates = inData.dates;
	this.dateObjects = inData.dateObjects;
	this.dayNames = inData.dayNames;
	this.photos = inData.photos;

	// fix the dates
	for (var index = 0; index < this.dateObjects.length; index++) {
		this.dateObjects[index] = new Date(this.dateObjects[index]);
	}

	for (index = 0; index < this.rows.length; index++) {
		this.rows[index].DateObject = new Date(this.rows[index].DateObject);
	}
};

SightingList.prototype.setGlobalIDs = function() {
	for (var index = 0; index < this.rows.length; index++) {
		var sighting = this.rows[index];
		sighting.id = index;
	}
};

SightingList.prototype.addRows = function(inRows) {
	for (var index = 0; index < inRows.length; index++) {
		var sighting = inRows[index];

		if (sighting['Date']) {
			// Parse the date
			var pieces = sighting['Date'].split('-');

			// order the pieces in a sensible way
			var fixedDateString = [pieces[0], '/', pieces[1], '/', pieces[2]].join('');

			// create and save the new dat
			var newDate = new Date(fixedDateString);
			sighting['DateObject'] = newDate;

			if (this.dates.indexOf(sighting['Date']) < 0) {
				this.dates.push(sighting['Date']);
				this.dateObjects.push(newDate);
				this.dayNames.push(SightingList.customDayNames[sighting['Date']]);
			}

			if (this.earliestDateObject == null || newDate < this.earliestDateObject) {
				this.earliestDateObject = newDate;
			}

			if (this.latestDateObject == null || newDate > this.latestDateObject) {
				this.latestDateObject = newDate;
			}

			if (! this.rowsByYear[pieces[2]]) {
				this.rowsByYear[pieces[2]] = [];
			}
			this.rowsByYear[pieces[2]].push(sighting);

			if (! this.rowsByMonth[pieces[0]]) {
				this.rowsByMonth[pieces[0]] = [];
			}
			this.rowsByMonth[pieces[0]].push(sighting);

		} else {
			console.log('ERROR SIGHTING HAS NO DATE', index, JSON.stringify(sighting));
		}
	}

	this.rows = this.rows.concat(inRows);

	this.dateObjects.sort(function(a, b) { return b - a; });
};

SightingList.prototype.sortByDate = function() {
	// TODO: this is probably unnecessary sort!
	this.rows.sort(function(a, b) { return a['DateObject'] - b['DateObject']; });
}

SightingList.prototype.earliestDateObject = function() {
	return this.earliestDateObject;
};

SightingList.prototype.latestDateObject = function() {
	return this.latestDateObject;
};

SightingList.prototype.filter = function(filterFunc) {
	return this.rows.filter(filterFunc);
};

SightingList.prototype.length = function() {
	return this.rows.length;
};

SightingList.prototype.byYear = function() {
	return this.rowsByYear;
};

SightingList.prototype.byMonth = function() {
	return [
		this.rowsByMonth['01'],
		this.rowsByMonth['02'],
		this.rowsByMonth['03'],
		this.rowsByMonth['04'],
		this.rowsByMonth['05'],
		this.rowsByMonth['06'],
		this.rowsByMonth['07'],
		this.rowsByMonth['08'],
		this.rowsByMonth['09'],
		this.rowsByMonth['10'],
		this.rowsByMonth['11'],
		this.rowsByMonth['12'],
	];
};

SightingList.prototype.getUniqueValues = function(fieldName) {
	if (this._uniqueValuesCache[fieldName]) {
		console.log('returning cached unique values for', fieldName);
	} else {
		console.log('computing unique values for', fieldName);
		var tmpValues = [];
		for (var index = 0; index < this.rows.length; index++) {
			var aValue = this.rows[index][fieldName];
			if (tmpValues.indexOf(aValue) < 0) {
				tmpValues.push(aValue);
			}
		}
		this._uniqueValuesCache[fieldName] = tmpValues;
	}

	return this._uniqueValuesCache[fieldName];
};

SightingList.prototype.getLocationHierarchy = function() {
	var provinces = {};

	for (var index = 0; index < this.rows.length; index++) {
		var aSighting = this.rows[index];
		var province = aSighting['State/Province'];
		var county = aSighting['County'];
		var location = aSighting['Location'];

		if (! provinces[province]) {
			provinces[province] = {};
		}

		if (! provinces[province][county]) {
			provinces[province][county] = [];
		}

		if (provinces[province][county].indexOf(location) < 0) {
			provinces[province][county].push(location);
		}
	}

	return provinces;
};

SightingList.getFamily = function(inTaxonomicOrderID) {
	for (var index = 0; index < SightingList.families.length; index++) {
		var tmp = SightingList.families[index];
		if ((tmp[1] < inTaxonomicOrderID) && (inTaxonomicOrderID < tmp[2])) {
			return tmp[0];
		}
	}

	return null;
}

SightingList.prototype.getTaxonomyHierarchy = function() {
	var byFamily = {};

	console.log(byFamily);

	for (var index = 0; index < this.rows.length; index++) {
		var aSighting = this.rows[index];
		var commonName = aSighting['Common Name'];
		if (aSighting['Taxonomic Order']) {
			var taxoID = parseFloat(aSighting['Taxonomic Order']);
			var aFamily = SightingList.getFamily(taxoID);

			if (aFamily == null) {
				console.log(taxoID, commonName);
				continue;
			}

			if (! byFamily[aFamily]) {
				byFamily[aFamily] = [];
			}

			if (byFamily[aFamily].indexOf(commonName) < 0) {
				byFamily[aFamily].push(commonName);
				// byFamily[aFamily].push(commonName + ' ' + aSighting['Taxonomic Order']);
			}
		} else {
			console.log('no scientific name', aSighting);
		}
	}

	return byFamily;
};

SightingList.prototype.getSpeciesByDate = function() {
	console.log('computing speciesByDate');

	for (var index = 0; index < this.rows.length; index++) {
		var sighting = this.rows[index];

		if (! this._speciesByDate[sighting['Date']]) {
			this._speciesByDate[sighting['Date']] = {
				commonNames: [],
				dateObject: sighting['DateObject'],
			};
		}
		if (this._speciesByDate[sighting['Date']].commonNames.indexOf(sighting['Common Name']) < 0) {
			this._speciesByDate[sighting['Date']].commonNames.push(sighting['Common Name']);
		}
	};

	return this._speciesByDate;
};

SightingList.prototype.getEarliestByCommonName = function() {
	console.log('computing earliestByCommonName');

	this._earliestRowByCommonName = {};

	for (var index = 0; index < this.rows.length; index++) {
		var sighting = this.rows[index];

		if (SightingList.omittedCommonNames.indexOf(sighting['Common Name']) < 0) {
			if (! this._earliestRowByCommonName[sighting['Common Name']]) {
				this._earliestRowByCommonName[sighting['Common Name']] = sighting;
			} else if (sighting.DateObject < this._earliestRowByCommonName[sighting['Common Name']].DateObject) {
				this._earliestRowByCommonName[sighting['Common Name']] = sighting;
			}
		} else {
			console.log('omit', sighting['Common Name']);
		}
	};

	return this._earliestRowByCommonName;
};

if (typeof module != 'undefined') {
	module.exports = SightingList;
}
<|endoftext|>


<commit_before>import choo from 'choo'
import Layout from '../client/views/layout'
import NotFound from '../client/views/404'
import AboutView from '../client/views/about'
import ResourcesView from '../client/views/resources'

const app = choo()
const noop = () => {}

app.route('/', Layout(noop))
app.route('/:bounds', Layout(noop))
app.route('/about', Layout(AboutView))
app.route('/about/:hash', Layout(AboutView))
app.route('/about/:hash/*', Layout(NotFound))
app.route('/resources', Layout(ResourcesView))
app.route('/resources/:hash', Layout(ResourcesView))
app.route('/resources/:hash/*', Layout(NotFound))
app.route('/:bounds/*', Layout(NotFound))

export default { app }
<commit_msg>Layout location changed<commit_after>import choo from 'choo'
import Layout from '../client/elements/layout'
import NotFound from '../client/views/404'
import AboutView from '../client/views/about'
import ResourcesView from '../client/views/resources'

const app = choo()
const noop = () => {}

app.route('/', Layout(noop))
app.route('/:bounds', Layout(noop))
app.route('/about', Layout(AboutView))
app.route('/about/:hash', Layout(AboutView))
app.route('/about/:hash/*', Layout(NotFound))
app.route('/resources', Layout(ResourcesView))
app.route('/resources/:hash', Layout(ResourcesView))
app.route('/resources/:hash/*', Layout(NotFound))
app.route('/:bounds/*', Layout(NotFound))

export default { app }
<|endoftext|>


<commit_before>import { redirect } from 'redux-first-router';
import { userInfoSelector, activeProjectSelector, setActiveProjectAction } from 'controllers/user';
import { fetchProjectAction } from 'controllers/project';
import {
  LOGIN_PAGE,
  REGISTRATION_PAGE,
  PROJECT_DASHBOARD_PAGE,
  PROJECT_PAGE,
  PROJECT_DASHBOARD_ITEM_PAGE,
} from 'controllers/pages';
import { isAuthorizedSelector } from 'controllers/auth';

const redirectRoute = (path, createNewAction) => ({
  path,
  thunk: (dispatch, getState) => {
    const { location } = getState();
    const newAction = createNewAction(location.payload);
    dispatch(redirect(newAction));
  },
});

export const onBeforeRouteChange = (dispatch, getState, { action }) => {
  const authorized = isAuthorizedSelector(getState());
  if (!authorized) {
    return;
  }

  const { projectId: hashProject } = action.payload;
  // TODO: move following after if
  const activeProjectId = activeProjectSelector(getState());
  if (!hashProject) {
    return;
  }

  const userInfo = userInfoSelector(getState());
  const userProjects = userInfo ? userInfo.assigned_projects : {};
  if (
    userProjects &&
    Object.prototype.hasOwnProperty.call(userProjects, hashProject) &&
    hashProject !== activeProjectId
  ) {
    dispatch(setActiveProjectAction(hashProject));
    dispatch(fetchProjectAction(hashProject));
  }
};

export default {
  HOME_PAGE: redirectRoute('/', (payload) => ({ type: LOGIN_PAGE, payload })),

  [LOGIN_PAGE]: '/login',
  [REGISTRATION_PAGE]: '/registration',

  ADMINISTRATE_PAGE: '/administrate',
  USER_PROFILE_PAGE: '/user-profile',

  API_PAGE: '/api',

  [PROJECT_PAGE]: redirectRoute('/:projectId', (payload) => ({
    type: PROJECT_DASHBOARD_PAGE,
    payload: { projectId: payload.projectId || 'default_project' },
  })),
  [PROJECT_DASHBOARD_PAGE]: '/:projectId/dashboard',
  [PROJECT_DASHBOARD_ITEM_PAGE]: '/:projectId/dashboard/:dashboardId',
  PROJECT_LAUNCHES_PAGE: '/:projectId/launches',
  PROJECT_FILTERS_PAGE: '/:projectId/filters',
  PROJECT_USERDEBUG_PAGE: '/:projectId/userdebug',
  PROJECT_MEMBERS_PAGE: '/:projectId/members',
  PROJECT_SETTINGS_PAGE: '/:projectId/settings',
  PROJECT_SANDBOX_PAGE: '/:projectId/sandbox',
};
<commit_msg>[EPMRPP-33984] Cleanup<commit_after>import { redirect } from 'redux-first-router';
import { userInfoSelector, activeProjectSelector, setActiveProjectAction } from 'controllers/user';
import { fetchProjectAction } from 'controllers/project';
import {
  LOGIN_PAGE,
  REGISTRATION_PAGE,
  PROJECT_DASHBOARD_PAGE,
  PROJECT_PAGE,
  PROJECT_DASHBOARD_ITEM_PAGE,
} from 'controllers/pages';
import { isAuthorizedSelector } from 'controllers/auth';

const redirectRoute = (path, createNewAction) => ({
  path,
  thunk: (dispatch, getState) => {
    const { location } = getState();
    const newAction = createNewAction(location.payload);
    dispatch(redirect(newAction));
  },
});

export const onBeforeRouteChange = (dispatch, getState, { action }) => {
  const authorized = isAuthorizedSelector(getState());
  if (!authorized) {
    return;
  }

  const { projectId: hashProject } = action.payload;
  if (!hashProject) {
    return;
  }

  const activeProjectId = activeProjectSelector(getState());
  const userInfo = userInfoSelector(getState());
  const userProjects = userInfo ? userInfo.assigned_projects : {};
  if (
    userProjects &&
    Object.prototype.hasOwnProperty.call(userProjects, hashProject) &&
    hashProject !== activeProjectId
  ) {
    dispatch(setActiveProjectAction(hashProject));
    dispatch(fetchProjectAction(hashProject));
  }
};

export default {
  HOME_PAGE: redirectRoute('/', (payload) => ({ type: LOGIN_PAGE, payload })),

  [LOGIN_PAGE]: '/login',
  [REGISTRATION_PAGE]: '/registration',

  ADMINISTRATE_PAGE: '/administrate',
  USER_PROFILE_PAGE: '/user-profile',

  API_PAGE: '/api',

  [PROJECT_PAGE]: redirectRoute('/:projectId', (payload) => ({
    type: PROJECT_DASHBOARD_PAGE,
    payload: { projectId: payload.projectId || 'default_project' },
  })),
  [PROJECT_DASHBOARD_PAGE]: '/:projectId/dashboard',
  [PROJECT_DASHBOARD_ITEM_PAGE]: '/:projectId/dashboard/:dashboardId',
  PROJECT_LAUNCHES_PAGE: '/:projectId/launches',
  PROJECT_FILTERS_PAGE: '/:projectId/filters',
  PROJECT_USERDEBUG_PAGE: '/:projectId/userdebug',
  PROJECT_MEMBERS_PAGE: '/:projectId/members',
  PROJECT_SETTINGS_PAGE: '/:projectId/settings',
  PROJECT_SANDBOX_PAGE: '/:projectId/sandbox',
};
<|endoftext|>


<commit_before>var keys = Object.keys,
    isArray = Array.isArray,
    toString = ({}.toString),
    getProto = Object.getPrototypeOf,
    hasOwn = ({}.hasOwnProperty),
    fnToString = hasOwn.toString;

function toStringTag (val) {
    return toString.call(val).slice(8, -1);
}

function hasConstructorOf (a, b) {
    if (!a) {
        return false;
    }
    var proto = getProto(a);
    if (!proto) {
        return false;
    }
    var Ctor = hasOwn.call(proto, 'constructor') && proto.constructor;
    if (typeof Ctor !== 'function') {
        return b === null;
    }
    return typeof Ctor === 'function' && b !== null && fnToString.call(Ctor) === fnToString.call(b);
}

function isPlainObject (val) { // Mirrors jQuery's
    if (!val || toStringTag(val) !== 'Object') {
        return false;
    }

    var proto = getProto(val);
    if (!proto) { // `Object.create(null)`
        return true;
    }

    return hasConstructorOf(val, Object);
}

function isUserObject (val) {
    if (!val || toStringTag(val) !== 'Object') {
        return false;
    }

    var proto = getProto(val);
    if (!proto) { // `Object.create(null)`
        return true;
    }
    return hasConstructorOf(val, Object) || isUserObject(proto);
}

function isObject (v) {
    return v && typeof v === 'object'
}

/* Typeson - JSON with types
    * License: The MIT License (MIT)
    * Copyright (c) 2016 David Fahlander
    */

/** An instance of this class can be used to call stringify() and parse().
 * Typeson resolves cyclic references by default. Can also be extended to
 * support custom types using the register() method.
 *
 * @constructor
 * @param {{cyclic: boolean}} [options] - if cyclic (default true), cyclic references will be handled gracefully.
 */
function Typeson (options) {
    // Replacers signature: replace (value). Returns falsy if not replacing. Otherwise ['Date', value.getTime()]
    var replacers = [];
    // Revivers: map {type => reviver}. Sample: {'Date': value => new Date(value)}
    var revivers = {};

    /** Types registered via register() */
    var regTypes = this.types = {};

    /** Seraialize given object to Typeson.
     *
     * Arguments works identical to those of JSON.stringify().
     */
    this.stringify = function (obj, replacer, space, opts) { // replacer here has nothing to do with our replacers.
        opts = Object.assign({}, options, opts, {stringification: true});
        var encapsulated = encapsulate(obj, null, opts);
        if (isArray(encapsulated)) {
            return JSON.stringify(encapsulated[0], replacer, space);
        }
        return encapsulated.then(function (res) {
            return JSON.stringify(res, replacer, space);
        });
    };

    /** Parse Typeson back into an obejct.
     *
     * Arguments works identical to those of JSON.parse().
     */
    this.parse = function (text, reviver, opts) {
        return revive(JSON.parse(text, reviver), opts); // This reviver has nothing to do with our revivers.
    };

    /** Encapsulate a complex object into a plain Object by replacing registered types with
     * plain objects representing the types data.
     *
     * This method is used internally by Typeson.stringify().
     * @param {Object} obj - Object to encapsulate.
     */
    var encapsulate = this.encapsulate = function (obj, stateObj, opts) {
        opts = Object.assign({}, options, opts);
        var forceAsync = opts.forceAsync;
        var types = {},
            refObjs = [], // For checking cyclic references
            refKeys = [], // For checking cyclic references
            promisesDataRoot = [];
        // Clone the object deeply while at the same time replacing any special types or cyclic reference:
        var cyclic = opts && ('cyclic' in opts) ? opts.cyclic : true;
        var ret = _encapsulate('', obj, cyclic, stateObj || {}, promisesDataRoot);
        function finish (ret) {
            // Add $types to result only if we ever bumped into a special type
            if (keys(types).length) {
                // Special if array (or primitive) was serialized because JSON would ignore custom $types prop on it.
                if (!ret || !isPlainObject(ret) || ret.$types) ret = {$:ret, $types: {$: types}};
                else ret.$types = types;
            }
            return ret;
        }
        function checkPromises (ret, promisesData) {
            return Promise.all(
                promisesData.map(function (pd) {return pd[1].p;})
            ).then(function (promResults) {
                return Promise.all(
                    promResults.map(function (promResult) {
                        var newPromisesData = [];
                        var prData = promisesData.splice(0, 1)[0];
                        // var [keypath, , cyclic, stateObj, parentObj, key] = prData;
                        var keyPath = prData[0];
                        var cyclic = prData[2];
                        var stateObj = prData[3];
                        var parentObj = prData[4];
                        var key = prData[5];
                        var encaps = _encapsulate(keyPath, promResult, cyclic, stateObj, newPromisesData);
                        var isTypesonPromise = hasConstructorOf(encaps, TypesonPromise);
                        if (keyPath && isTypesonPromise) { // Handle case where an embedded custom type itself returns a `Typeson.Promise`
                            return encaps.p.then(function (encaps2) {
                                parentObj[key] = encaps2;
                                return checkPromises(ret, newPromisesData);
                            });
                        }
                        if (keyPath) parentObj[key] = encaps;
                        else if (isTypesonPromise) { ret = encaps.p; }
                        else ret = encaps; // If this is itself a `Typeson.Promise` (because the original value supplied was a promise or because the supplied custom type value resolved to one), returning it below will be fine since a promise is expected anyways given current config (and if not a promise, it will be ready as the resolve value)
                        return checkPromises(ret, newPromisesData);
                    })
                ).then(function () {
                    return ret;
                });
            });
        };
        return promisesDataRoot.length ?
            Promise.resolve(checkPromises(ret, promisesDataRoot)).then(finish)
            : (opts.stringification && !forceAsync // If this is a promise, we don't want to resolve as above, so we return an array
                ? [finish(ret)]
                : (forceAsync
                    ? Promise.resolve(finish(ret))
                    : finish(ret)
                ));

        function _encapsulate (keypath, value, cyclic, stateObj, promisesData) {
            var $typeof = typeof value;
            if ($typeof in {string: 1, boolean: 1, number: 1, undefined: 1 })
                return value === undefined || ($typeof === 'number' &&
                    (isNaN(value) || value === -Infinity || value === Infinity)) ?
                        replace(keypath, value, stateObj, promisesData) :
                        value;
            if (value === null) return value;
            if (cyclic) {
                // Options set to detect cyclic references and be able to rewrite them.
                var refIndex = refObjs.indexOf(value);
                if (refIndex < 0) {
                    if (cyclic === true) {
                        refObjs.push(value);
                        refKeys.push(keypath);
                    }
                } else {
                    types[keypath] = '#';
                    return '#' + refKeys[refIndex];
                }
            }
            var isPlainObj = isPlainObject(value);
            var replaced = isPlainObj ?
                value : // Optimization: if plain object, don't try finding a replacer
                replace(keypath, value, stateObj, promisesData);
            if (replaced !== value) return replaced;
            var clone;
            var isArr = isArray(value);
            if (isPlainObj)
                clone = {};
            else if (isArr)
                clone = new Array(value.length);
            else if (keypath === '' && hasConstructorOf(value, TypesonPromise)) {
                promisesData.push([keypath, value, cyclic, stateObj]);
                return value;
            }
            else return value; // Only clone vanilla objects and arrays.
            // Iterate object or array
            keys(value).forEach(function (key, i) {
                var kp = keypath + (keypath ? '.' : '') + key;
                var val = _encapsulate(kp, value[key], cyclic, {ownKeys: true}, promisesData);
                if (hasConstructorOf(val, TypesonPromise)) {
                    promisesData.push([kp, val, cyclic, {ownKeys: true}, clone, key]);
                } else if (val !== undefined) clone[key] = val;
            });
            // Iterate array for non-own properties (we can't replace the prior loop though as it iterates non-integer keys)
            if (isArr) {
                for (var i = 0, vl = value.length; i < vl; i++) {
                    if (!(i in value)) {
                        var kp = keypath + (keypath ? '.' : '') + i;
                        var val = _encapsulate(kp, value[i], cyclic, {ownKeys: false}, promisesData);
                        if (hasConstructorOf(val, TypesonPromise)) {
                            promisesData.push([kp, val, cyclic, {ownKeys: false}, clone, i]);
                        } else if (val !== undefined) clone[i] = val;
                    }
                }
            }
            return clone;
        }

        function replace (key, value, stateObj, promisesData) {
            // Encapsulate registered types
            var i = replacers.length;
            while (i--) {
                if (replacers[i].test(value, stateObj)) {
                    var type = replacers[i].type;
                    if (revivers[type]) {
                        // Record the type only if a corresponding reviver exists.
                        // This is to support specs where only replacement is done.
                        // For example ensuring deep cloning of the object, or
                        // replacing a type to its equivalent without the need to revive it.
                        var existing = types[key];
                        // type can comprise an array of types (see test shouldSupportIntermediateTypes)
                        types[key] = existing ? [type].concat(existing) : type;
                    }
                    // Now, also traverse the result in case it contains it own types to replace
                    return _encapsulate(key, replacers[i].replace(value, stateObj), cyclic && 'readonly', stateObj, promisesData);
                }
            }
            return value;
        }
    };

    /** Revive an encapsulated object.
     * This method is used internally by JSON.parse().
     * @param {Object} obj - Object to revive. If it has $types member, the properties that are listed there
     * will be replaced with its true type instead of just plain objects.
     */
    var revive = this.revive = function (obj) {
        var types = obj && obj.$types,
            ignore$Types = true;
        if (!types) return obj; // No type info added. Revival not needed.
        if (types.$ && isPlainObject(types.$)) {
            // Special when root object is not a trivial Object, it will be encapsulated in $.
            obj = obj.$;
            types = types.$;
            ignore$Types = false;
        }
        var ret = _revive('', obj);
        return hasConstructorOf(ret, Undefined) ? undefined : ret;

        function _revive (keypath, value, target) {
            if (ignore$Types && keypath === '$types') return;
            var type = types[keypath];
            if (value && (isPlainObject(value) || isArray(value))) {
                var clone = isArray(value) ? new Array(value.length) : {};
                // Iterate object or array
                keys(value).forEach(function (key) {
                    var val = _revive(keypath + (keypath ? '.' : '') + key, value[key], target || clone);
                    if (hasConstructorOf(val, Undefined)) clone[key] = undefined;
                    else if (val !== undefined) clone[key] = val;
                });
                value = clone;
            }
            if (!type) return value;
            if (type === '#') return getByKeyPath(target, value.substr(1));
            return [].concat(type).reduce(function (val, type) {
                var reviver = revivers[type];
                if (!reviver) throw new Error ('Unregistered type: ' + type);
                return reviver(val);
            }, value);
        }
    };

    /** Register types.
     * For examples how to use this method, see https://github.com/dfahlander/typeson-registry/tree/master/types
     * @param {Array.<Object.<string,Function[]>>} typeSpec - Types and their functions [test, encapsulate, revive];
     */
    this.register = function (typeSpecSets) {
        [].concat(typeSpecSets).forEach(function R (typeSpec) {
            if (isArray(typeSpec)) return typeSpec.map(R); // Allow arrays of arrays of arrays...
            typeSpec && keys(typeSpec).forEach(function (typeId) {
                var spec = typeSpec[typeId],
                    existingReplacer = replacers.filter(function(r){ return r.type === typeId; });
                if (existingReplacer.length) {
                    // Remove existing spec and replace with this one.
                    replacers.splice(replacers.indexOf(existingReplacer[0]), 1);
                    delete revivers[typeId];
                    delete regTypes[typeId];
                }
                if (spec) {
                    if (typeof spec === 'function') {
                        // Support registering just a class without replacer/reviver
                        var Class = spec;
                        spec = [
                            function (x) { return x.constructor === Class; },
                            function (x) { return assign({}, x); },
                            function (x) { return assign(Object.create(Class.prototype), x); }
                        ];
                    }
                    replacers.push({
                        type: typeId,
                        test: spec[0],
                        replace: spec[1]
                    });
                    if (spec[2]) revivers[typeId] = spec[2];
                    regTypes[typeId] = spec; // Record to be retrieved via public types property.
                }
            });
        });
        return this;
    };
}

function assign(t,s) {
    keys(s).map(function (k) { t[k] = s[k]; });
    return t;
}

/** getByKeyPath() utility */
function getByKeyPath (obj, keyPath) {
    if (keyPath === '') return obj;
    var period = keyPath.indexOf('.');
    if (period !== -1) {
        var innerObj = obj[keyPath.substr(0, period)];
        return innerObj === undefined ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1));
    }
    return obj[keyPath];
}

function Undefined () {}

// With ES6 classes, we may be able to simply use `class TypesonPromise extends Promise` and add a string tag for detection
function TypesonPromise (f) {
    if (!(this instanceof TypesonPromise)) {
        return new TypesonPromise(f);
    }
    this.p = new Promise(f);
};
Object.defineProperties(TypesonPromise.prototype, {
    'then': {
        get: function () {
            var then = this.p.then.bind(this.p);
            return function (result) {
                return new TypesonPromise(function (res) {
                    res(then(result));
                });
            };
        }
    },
    'catch': {
        get: function () {
            return this.p.then.bind(this.p);
        }
    }
});
TypesonPromise.all = function (promArr) {
    return new TypesonPromise(function (res) {
        Promise.all(promArr.map(function (prom) {return prom.p;})).then(res);
    });
};
TypesonPromise.race = function (promArr) {
    return new TypesonPromise(function (res) {
        Promise.race(promArr.map(function (prom) {return prom.p;})).then(res);
    });
};
TypesonPromise.resolve = function (v) {
    return new TypesonPromise(function (res) {
        res(v);
    });
};
TypesonPromise.reject = function (v) {
    return new TypesonPromise(function (res, rej) {
        rej(v);
    });
};

// The following provide classes meant to avoid clashes with other values
Typeson.Undefined = Undefined; // To insist `undefined` should be added
Typeson.Promise = TypesonPromise; // To support async encapsulation/stringification

// Some fundamental type-checking utilities
Typeson.toStringTag = toStringTag;
Typeson.hasConstructorOf = hasConstructorOf;
Typeson.isObject = isObject;
Typeson.isPlainObject = isPlainObject;
Typeson.isUserObject = isUserObject;

module.exports = Typeson;
<commit_msg>- Fix `catch` (API still under discussion)<commit_after>var keys = Object.keys,
    isArray = Array.isArray,
    toString = ({}.toString),
    getProto = Object.getPrototypeOf,
    hasOwn = ({}.hasOwnProperty),
    fnToString = hasOwn.toString;

function toStringTag (val) {
    return toString.call(val).slice(8, -1);
}

function hasConstructorOf (a, b) {
    if (!a) {
        return false;
    }
    var proto = getProto(a);
    if (!proto) {
        return false;
    }
    var Ctor = hasOwn.call(proto, 'constructor') && proto.constructor;
    if (typeof Ctor !== 'function') {
        return b === null;
    }
    return typeof Ctor === 'function' && b !== null && fnToString.call(Ctor) === fnToString.call(b);
}

function isPlainObject (val) { // Mirrors jQuery's
    if (!val || toStringTag(val) !== 'Object') {
        return false;
    }

    var proto = getProto(val);
    if (!proto) { // `Object.create(null)`
        return true;
    }

    return hasConstructorOf(val, Object);
}

function isUserObject (val) {
    if (!val || toStringTag(val) !== 'Object') {
        return false;
    }

    var proto = getProto(val);
    if (!proto) { // `Object.create(null)`
        return true;
    }
    return hasConstructorOf(val, Object) || isUserObject(proto);
}

function isObject (v) {
    return v && typeof v === 'object'
}

/* Typeson - JSON with types
    * License: The MIT License (MIT)
    * Copyright (c) 2016 David Fahlander
    */

/** An instance of this class can be used to call stringify() and parse().
 * Typeson resolves cyclic references by default. Can also be extended to
 * support custom types using the register() method.
 *
 * @constructor
 * @param {{cyclic: boolean}} [options] - if cyclic (default true), cyclic references will be handled gracefully.
 */
function Typeson (options) {
    // Replacers signature: replace (value). Returns falsy if not replacing. Otherwise ['Date', value.getTime()]
    var replacers = [];
    // Revivers: map {type => reviver}. Sample: {'Date': value => new Date(value)}
    var revivers = {};

    /** Types registered via register() */
    var regTypes = this.types = {};

    /** Seraialize given object to Typeson.
     *
     * Arguments works identical to those of JSON.stringify().
     */
    this.stringify = function (obj, replacer, space, opts) { // replacer here has nothing to do with our replacers.
        opts = Object.assign({}, options, opts, {stringification: true});
        var encapsulated = encapsulate(obj, null, opts);
        if (isArray(encapsulated)) {
            return JSON.stringify(encapsulated[0], replacer, space);
        }
        return encapsulated.then(function (res) {
            return JSON.stringify(res, replacer, space);
        });
    };

    /** Parse Typeson back into an obejct.
     *
     * Arguments works identical to those of JSON.parse().
     */
    this.parse = function (text, reviver, opts) {
        return revive(JSON.parse(text, reviver), opts); // This reviver has nothing to do with our revivers.
    };

    /** Encapsulate a complex object into a plain Object by replacing registered types with
     * plain objects representing the types data.
     *
     * This method is used internally by Typeson.stringify().
     * @param {Object} obj - Object to encapsulate.
     */
    var encapsulate = this.encapsulate = function (obj, stateObj, opts) {
        opts = Object.assign({}, options, opts);
        var forceAsync = opts.forceAsync;
        var types = {},
            refObjs = [], // For checking cyclic references
            refKeys = [], // For checking cyclic references
            promisesDataRoot = [];
        // Clone the object deeply while at the same time replacing any special types or cyclic reference:
        var cyclic = opts && ('cyclic' in opts) ? opts.cyclic : true;
        var ret = _encapsulate('', obj, cyclic, stateObj || {}, promisesDataRoot);
        function finish (ret) {
            // Add $types to result only if we ever bumped into a special type
            if (keys(types).length) {
                // Special if array (or primitive) was serialized because JSON would ignore custom $types prop on it.
                if (!ret || !isPlainObject(ret) || ret.$types) ret = {$:ret, $types: {$: types}};
                else ret.$types = types;
            }
            return ret;
        }
        function checkPromises (ret, promisesData) {
            return Promise.all(
                promisesData.map(function (pd) {return pd[1].p;})
            ).then(function (promResults) {
                return Promise.all(
                    promResults.map(function (promResult) {
                        var newPromisesData = [];
                        var prData = promisesData.splice(0, 1)[0];
                        // var [keypath, , cyclic, stateObj, parentObj, key] = prData;
                        var keyPath = prData[0];
                        var cyclic = prData[2];
                        var stateObj = prData[3];
                        var parentObj = prData[4];
                        var key = prData[5];
                        var encaps = _encapsulate(keyPath, promResult, cyclic, stateObj, newPromisesData);
                        var isTypesonPromise = hasConstructorOf(encaps, TypesonPromise);
                        if (keyPath && isTypesonPromise) { // Handle case where an embedded custom type itself returns a `Typeson.Promise`
                            return encaps.p.then(function (encaps2) {
                                parentObj[key] = encaps2;
                                return checkPromises(ret, newPromisesData);
                            });
                        }
                        if (keyPath) parentObj[key] = encaps;
                        else if (isTypesonPromise) { ret = encaps.p; }
                        else ret = encaps; // If this is itself a `Typeson.Promise` (because the original value supplied was a promise or because the supplied custom type value resolved to one), returning it below will be fine since a promise is expected anyways given current config (and if not a promise, it will be ready as the resolve value)
                        return checkPromises(ret, newPromisesData);
                    })
                ).then(function () {
                    return ret;
                });
            });
        };
        return promisesDataRoot.length ?
            Promise.resolve(checkPromises(ret, promisesDataRoot)).then(finish)
            : (opts.stringification && !forceAsync // If this is a promise, we don't want to resolve as above, so we return an array
                ? [finish(ret)]
                : (forceAsync
                    ? Promise.resolve(finish(ret))
                    : finish(ret)
                ));

        function _encapsulate (keypath, value, cyclic, stateObj, promisesData) {
            var $typeof = typeof value;
            if ($typeof in {string: 1, boolean: 1, number: 1, undefined: 1 })
                return value === undefined || ($typeof === 'number' &&
                    (isNaN(value) || value === -Infinity || value === Infinity)) ?
                        replace(keypath, value, stateObj, promisesData) :
                        value;
            if (value === null) return value;
            if (cyclic) {
                // Options set to detect cyclic references and be able to rewrite them.
                var refIndex = refObjs.indexOf(value);
                if (refIndex < 0) {
                    if (cyclic === true) {
                        refObjs.push(value);
                        refKeys.push(keypath);
                    }
                } else {
                    types[keypath] = '#';
                    return '#' + refKeys[refIndex];
                }
            }
            var isPlainObj = isPlainObject(value);
            var replaced = isPlainObj ?
                value : // Optimization: if plain object, don't try finding a replacer
                replace(keypath, value, stateObj, promisesData);
            if (replaced !== value) return replaced;
            var clone;
            var isArr = isArray(value);
            if (isPlainObj)
                clone = {};
            else if (isArr)
                clone = new Array(value.length);
            else if (keypath === '' && hasConstructorOf(value, TypesonPromise)) {
                promisesData.push([keypath, value, cyclic, stateObj]);
                return value;
            }
            else return value; // Only clone vanilla objects and arrays.
            // Iterate object or array
            keys(value).forEach(function (key, i) {
                var kp = keypath + (keypath ? '.' : '') + key;
                var val = _encapsulate(kp, value[key], cyclic, {ownKeys: true}, promisesData);
                if (hasConstructorOf(val, TypesonPromise)) {
                    promisesData.push([kp, val, cyclic, {ownKeys: true}, clone, key]);
                } else if (val !== undefined) clone[key] = val;
            });
            // Iterate array for non-own properties (we can't replace the prior loop though as it iterates non-integer keys)
            if (isArr) {
                for (var i = 0, vl = value.length; i < vl; i++) {
                    if (!(i in value)) {
                        var kp = keypath + (keypath ? '.' : '') + i;
                        var val = _encapsulate(kp, value[i], cyclic, {ownKeys: false}, promisesData);
                        if (hasConstructorOf(val, TypesonPromise)) {
                            promisesData.push([kp, val, cyclic, {ownKeys: false}, clone, i]);
                        } else if (val !== undefined) clone[i] = val;
                    }
                }
            }
            return clone;
        }

        function replace (key, value, stateObj, promisesData) {
            // Encapsulate registered types
            var i = replacers.length;
            while (i--) {
                if (replacers[i].test(value, stateObj)) {
                    var type = replacers[i].type;
                    if (revivers[type]) {
                        // Record the type only if a corresponding reviver exists.
                        // This is to support specs where only replacement is done.
                        // For example ensuring deep cloning of the object, or
                        // replacing a type to its equivalent without the need to revive it.
                        var existing = types[key];
                        // type can comprise an array of types (see test shouldSupportIntermediateTypes)
                        types[key] = existing ? [type].concat(existing) : type;
                    }
                    // Now, also traverse the result in case it contains it own types to replace
                    return _encapsulate(key, replacers[i].replace(value, stateObj), cyclic && 'readonly', stateObj, promisesData);
                }
            }
            return value;
        }
    };

    /** Revive an encapsulated object.
     * This method is used internally by JSON.parse().
     * @param {Object} obj - Object to revive. If it has $types member, the properties that are listed there
     * will be replaced with its true type instead of just plain objects.
     */
    var revive = this.revive = function (obj) {
        var types = obj && obj.$types,
            ignore$Types = true;
        if (!types) return obj; // No type info added. Revival not needed.
        if (types.$ && isPlainObject(types.$)) {
            // Special when root object is not a trivial Object, it will be encapsulated in $.
            obj = obj.$;
            types = types.$;
            ignore$Types = false;
        }
        var ret = _revive('', obj);
        return hasConstructorOf(ret, Undefined) ? undefined : ret;

        function _revive (keypath, value, target) {
            if (ignore$Types && keypath === '$types') return;
            var type = types[keypath];
            if (value && (isPlainObject(value) || isArray(value))) {
                var clone = isArray(value) ? new Array(value.length) : {};
                // Iterate object or array
                keys(value).forEach(function (key) {
                    var val = _revive(keypath + (keypath ? '.' : '') + key, value[key], target || clone);
                    if (hasConstructorOf(val, Undefined)) clone[key] = undefined;
                    else if (val !== undefined) clone[key] = val;
                });
                value = clone;
            }
            if (!type) return value;
            if (type === '#') return getByKeyPath(target, value.substr(1));
            return [].concat(type).reduce(function (val, type) {
                var reviver = revivers[type];
                if (!reviver) throw new Error ('Unregistered type: ' + type);
                return reviver(val);
            }, value);
        }
    };

    /** Register types.
     * For examples how to use this method, see https://github.com/dfahlander/typeson-registry/tree/master/types
     * @param {Array.<Object.<string,Function[]>>} typeSpec - Types and their functions [test, encapsulate, revive];
     */
    this.register = function (typeSpecSets) {
        [].concat(typeSpecSets).forEach(function R (typeSpec) {
            if (isArray(typeSpec)) return typeSpec.map(R); // Allow arrays of arrays of arrays...
            typeSpec && keys(typeSpec).forEach(function (typeId) {
                var spec = typeSpec[typeId],
                    existingReplacer = replacers.filter(function(r){ return r.type === typeId; });
                if (existingReplacer.length) {
                    // Remove existing spec and replace with this one.
                    replacers.splice(replacers.indexOf(existingReplacer[0]), 1);
                    delete revivers[typeId];
                    delete regTypes[typeId];
                }
                if (spec) {
                    if (typeof spec === 'function') {
                        // Support registering just a class without replacer/reviver
                        var Class = spec;
                        spec = [
                            function (x) { return x.constructor === Class; },
                            function (x) { return assign({}, x); },
                            function (x) { return assign(Object.create(Class.prototype), x); }
                        ];
                    }
                    replacers.push({
                        type: typeId,
                        test: spec[0],
                        replace: spec[1]
                    });
                    if (spec[2]) revivers[typeId] = spec[2];
                    regTypes[typeId] = spec; // Record to be retrieved via public types property.
                }
            });
        });
        return this;
    };
}

function assign(t,s) {
    keys(s).map(function (k) { t[k] = s[k]; });
    return t;
}

/** getByKeyPath() utility */
function getByKeyPath (obj, keyPath) {
    if (keyPath === '') return obj;
    var period = keyPath.indexOf('.');
    if (period !== -1) {
        var innerObj = obj[keyPath.substr(0, period)];
        return innerObj === undefined ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1));
    }
    return obj[keyPath];
}

function Undefined () {}

// With ES6 classes, we may be able to simply use `class TypesonPromise extends Promise` and add a string tag for detection
function TypesonPromise (f) {
    if (!(this instanceof TypesonPromise)) {
        return new TypesonPromise(f);
    }
    this.p = new Promise(f);
};
Object.defineProperties(TypesonPromise.prototype, {
    'then': {
        get: function () {
            var then = this.p.then.bind(this.p);
            return function (result) {
                return new TypesonPromise(function (res) {
                    res(then(result));
                });
            };
        }
    },
    'catch': {
        get: function () {
            var ctch = this.p.ctch.bind(this.p);
            return function (result) {
                return new TypesonPromise(function (res) {
                    res(ctch(result));
                });
            };
        }
    }
});
TypesonPromise.all = function (promArr) {
    return new TypesonPromise(function (res) {
        Promise.all(promArr.map(function (prom) {return prom.p;})).then(res);
    });
};
TypesonPromise.race = function (promArr) {
    return new TypesonPromise(function (res) {
        Promise.race(promArr.map(function (prom) {return prom.p;})).then(res);
    });
};
TypesonPromise.resolve = function (v) {
    return new TypesonPromise(function (res) {
        res(v);
    });
};
TypesonPromise.reject = function (v) {
    return new TypesonPromise(function (res, rej) {
        rej(v);
    });
};

// The following provide classes meant to avoid clashes with other values
Typeson.Undefined = Undefined; // To insist `undefined` should be added
Typeson.Promise = TypesonPromise; // To support async encapsulation/stringification

// Some fundamental type-checking utilities
Typeson.toStringTag = toStringTag;
Typeson.hasConstructorOf = hasConstructorOf;
Typeson.isObject = isObject;
Typeson.isPlainObject = isPlainObject;
Typeson.isUserObject = isUserObject;

module.exports = Typeson;
<|endoftext|>


<commit_before>import express from 'express';
import compression from 'compression';
import historyApiFallback from 'express-history-api-fallback';
import {log} from '../common';
import errorHandler from './errorHandler';
import {createRomRouter} from './roms';

export default function createApp(romDb, options) {
  const {
    staticPath = false,
    morganEnabled = false,
    morganFormat = false,
  } = options;

  log.info('Creating application');
  log.info('  static path: %s', staticPath);
  log.info('  morgan enabled: %s', morganEnabled);
  log.info('  morgan format: %s', morganFormat);

  const app = express();
  app.use(compression());
  app.use(configureHeaders);

  if (morganEnabled) {
    const morgan = require('morgan');
    app.use(morgan(morganFormat));
  }

  app.use('/', createRomRouter(romDb));
  app.use('/', express.static(staticPath));
  app.use(historyApiFallback('index.html', {root: staticPath}));
  app.use(errorHandler);

  return app;
}

function configureHeaders(req, res, next) {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.removeHeader('X-Powered-By');
  next();
}
<commit_msg>Redirect forwared HTTP to HTTPS<commit_after>import express from 'express';
import compression from 'compression';
import historyApiFallback from 'express-history-api-fallback';
import {log} from '../common';
import errorHandler from './errorHandler';
import {createRomRouter} from './roms';

export default function createApp(romDb, options) {
  const {
    staticPath = false,
    morganEnabled = false,
    morganFormat = false,
  } = options;

  log.info('Creating application');
  log.info('  static path: %s', staticPath);
  log.info('  morgan enabled: %s', morganEnabled);
  log.info('  morgan format: %s', morganFormat);

  const app = express();
  app.use(compression());
  app.use(configureHeaders);
  app.use(redirectForwardedHttp);

  if (morganEnabled) {
    const morgan = require('morgan');
    app.use(morgan(morganFormat));
  }

  app.use('/', createRomRouter(romDb));
  app.use('/', express.static(staticPath));
  app.use(historyApiFallback('index.html', {root: staticPath}));
  app.use(errorHandler);

  return app;
}

function configureHeaders(req, res, next) {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.removeHeader('X-Powered-By');
  next();
}

function redirectForwardedHttp(req, res, next) {
  const proto = req.headers['X-Forwarded-Proto'];
  if (proto && proto !== 'https') {
    res.redirect(302, 'https://' + req.hostname + req.originalUrl);
  } else {
    next();
  }
}
<|endoftext|>