<commit_before>

fc.addDays = addDays;
fc.cloneDate = cloneDate;
fc.parseDate = parseDate;
fc.parseISO8601 = parseISO8601;
fc.parseTime = parseTime;
fc.formatDate = formatDate;
fc.formatDates = formatDates;

/* Date Math
-----------------------------------------------------------------------------*/

var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
	DAY_MS = 86400000,
	HOUR_MS = 3600000,
	MINUTE_MS = 60000;

function addYears(d, n, keepTime) {
	d.setFullYear(d.getFullYear() + n);
	if (!keepTime) {
		clearTime(d);
	}
	return d;
}

function addMonths(d, n, keepTime) { // prevents day overflow/underflow
	if (+d) { // prevent infinite looping on invalid dates
		var m = d.getMonth() + n,
			check = cloneDate(d);
		check.setDate(1);
		check.setMonth(m);
		d.setMonth(m);
		if (!keepTime) {
			clearTime(d);
		}
		while (d.getMonth() != check.getMonth()) {
			d.setDate(d.getDate() + (d < check ? 1 : -1));
		}
	}
	return d;
}

function addDays(d, n, keepTime) { // deals with daylight savings
	if (+d) {
		var dd = d.getDate() + n,
			check = cloneDate(d);
		check.setHours(9); // set to middle of day
		check.setDate(dd);
		d.setDate(dd);
		if (!keepTime) {
			clearTime(d);
		}
		fixDate(d, check);
	}
	return d;
}

function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes
	if (+d) { // prevent infinite looping on invalid dates
		while (d.getDate() != check.getDate()) {
			d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
		}
	}
}

function addMinutes(d, n) {
	d.setMinutes(d.getMinutes() + n);
	return d;
}

function clearTime(d) {
	d.setHours(0);
	d.setMinutes(0);
	d.setSeconds(0);
	d.setMilliseconds(0);
	return d;
}

function cloneDate(d, dontKeepTime) {
	if (dontKeepTime) {
		return clearTime(new Date(+d));
	}
	return new Date(+d);
}

function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
	var i=0, d;
	do {
		d = new Date(1970, i++, 1);
	} while (d.getHours()); // != 0
	return d;
}

function skipWeekend(date, inc, excl) {
	inc = inc || 1;
	while (!date.getDay() || (excl && date.getDay()==1 || !excl && date.getDay()==6)) {
		addDays(date, inc);
	}
	return date;
}

function dayDiff(d1, d2) { // d1 - d2
	return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS);
}

function setYMD(date, y, m, d) {
	if (y !== undefined && y != date.getFullYear()) {
		date.setDate(1);
		date.setMonth(0);
		date.setFullYear(y);
	}
	if (m !== undefined && m != date.getMonth()) {
		date.setDate(1);
		date.setMonth(m);
	}
	if (d !== undefined) {
		date.setDate(d);
	}
}

/* Date Parsing
-----------------------------------------------------------------------------*/

function parseDate(s, ignoreTimezone) { // ignoreTimezone defaults to true
	if (typeof s == 'object') { // already a Date object
		return s;
	}
	if (typeof s == 'number') { // a UNIX timestamp
		return new Date(s * 1000);
	}
	if (typeof s == 'string') {
		if (s.match(/^\d+(\.\d+)?$/)) { // a UNIX timestamp
			return new Date(parseFloat(s) * 1000);
		}
		if (ignoreTimezone === undefined) {
			ignoreTimezone = true;
		}
		return parseISO8601(s, ignoreTimezone) || (s ? new Date(s) : null);
	}
	// TODO: never return invalid dates (like from new Date(<string>)), return null instead
	return null;
}

function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false
	// derived from http://delete.me.uk/2005/03/iso8601.html
	// TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
	var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);
	if (!m) {
		return null;
	}
	var date = new Date(m[1], 0, 1);
	if (ignoreTimezone || !m[13]) {
		var check = new Date(m[1], 0, 1, 9, 0);
		if (m[3]) {
			date.setMonth(m[3] - 1);
			check.setMonth(m[3] - 1);
		}
		if (m[5]) {
			date.setDate(m[5]);
			check.setDate(m[5]);
		}
		fixDate(date, check);
		if (m[7]) {
			date.setHours(m[7]);
		}
		if (m[8]) {
			date.setMinutes(m[8]);
		}
		if (m[10]) {
			date.setSeconds(m[10]);
		}
		if (m[12]) {
			date.setMilliseconds(Number("0." + m[12]) * 1000);
		}
		fixDate(date, check);
	}else{
		date.setUTCFullYear(
			m[1],
			m[3] ? m[3] - 1 : 0,
			m[5] || 1
		);
		date.setUTCHours(
			m[7] || 0,
			m[8] || 0,
			m[10] || 0,
			m[12] ? Number("0." + m[12]) * 1000 : 0
		);
		if (m[14]) {
			var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0);
			offset *= m[15] == '-' ? 1 : -1;
			date = new Date(+date + (offset * 60 * 1000));
		}
	}
	return date;
}

function parseTime(s) { // returns minutes since start of day
	if (typeof s == 'number') { // an hour
		return s * 60;
	}
	if (typeof s == 'object') { // a Date object
		return s.getHours() * 60 + s.getMinutes();
	}
	var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
	if (m) {
		var h = parseInt(m[1], 10);
		if (m[3]) {
			h %= 12;
			if (m[3].toLowerCase().charAt(0) == 'p') {
				h += 12;
			}
		}
		return h * 60 + (m[2] ? parseInt(m[2], 10) : 0);
	}
}

/* Date Formatting
-----------------------------------------------------------------------------*/
// TODO: use same function formatDate(date, [date2], format, [options])

function formatDate(date, format, options) {
	return formatDates(date, null, format, options);
}

function formatDates(date1, date2, format, options) {
	options = options || defaults;
	var date = date1,
		otherDate = date2,
		i, len = format.length, c,
		i2, formatter,
		res = '';
	for (i=0; i<len; i++) {
		c = format.charAt(i);
		if (c == "'") {
			for (i2=i+1; i2<len; i2++) {
				if (format.charAt(i2) == "'") {
					if (date) {
						if (i2 == i+1) {
							res += "'";
						}else{
							res += format.substring(i+1, i2);
						}
						i = i2;
					}
					break;
				}
			}
		}
		else if (c == '(') {
			for (i2=i+1; i2<len; i2++) {
				if (format.charAt(i2) == ')') {
					var subres = formatDate(date, format.substring(i+1, i2), options);
					if (parseInt(subres.replace(/\D/, ''), 10)) {
						res += subres;
					}
					i = i2;
					break;
				}
			}
		}
		else if (c == '[') {
			for (i2=i+1; i2<len; i2++) {
				if (format.charAt(i2) == ']') {
					var subformat = format.substring(i+1, i2);
					var subres = formatDate(date, subformat, options);
					if (subres != formatDate(otherDate, subformat, options)) {
						res += subres;
					}
					i = i2;
					break;
				}
			}
		}
		else if (c == '{') {
			date = date2;
			otherDate = date1;
		}
		else if (c == '}') {
			date = date1;
			otherDate = date2;
		}
		else {
			for (i2=len; i2>i; i2--) {
				if (formatter = dateFormatters[format.substring(i, i2)]) {
					if (date) {
						res += formatter(date, options);
					}
					i = i2 - 1;
					break;
				}
			}
			if (i2 == i) {
				if (date) {
					res += c;
				}
			}
		}
	}
	return res;
};

var dateFormatters = {
	s	: function(d)	{ return d.getSeconds() },
	ss	: function(d)	{ return zeroPad(d.getSeconds()) },
	m	: function(d)	{ return d.getMinutes() },
	mm	: function(d)	{ return zeroPad(d.getMinutes()) },
	h	: function(d)	{ return d.getHours() % 12 || 12 },
	hh	: function(d)	{ return zeroPad(d.getHours() % 12 || 12) },
	H	: function(d)	{ return d.getHours() },
	HH	: function(d)	{ return zeroPad(d.getHours()) },
	d	: function(d)	{ return d.getDate() },
	dd	: function(d)	{ return zeroPad(d.getDate()) },
	ddd	: function(d,o)	{ return o.dayNamesShort[d.getDay()] },
	dddd: function(d,o)	{ return o.dayNames[d.getDay()] },
	M	: function(d)	{ return d.getMonth() + 1 },
	MM	: function(d)	{ return zeroPad(d.getMonth() + 1) },
	MMM	: function(d,o)	{ return o.monthNamesShort[d.getMonth()] },
	MMMM: function(d,o)	{ return o.monthNames[d.getMonth()] },
	yy	: function(d)	{ return (d.getFullYear()+'').substring(2) },
	yyyy: function(d)	{ return d.getFullYear() },
	t	: function(d)	{ return d.getHours() < 12 ? 'a' : 'p' },
	tt	: function(d)	{ return d.getHours() < 12 ? 'am' : 'pm' },
	T	: function(d)	{ return d.getHours() < 12 ? 'A' : 'P' },
	TT	: function(d)	{ return d.getHours() < 12 ? 'AM' : 'PM' },
	u	: function(d)	{ return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") },
	S	: function(d)	{
		var date = d.getDate();
		if (date > 10 && date < 20) {
			return 'th';
		}
		return ['st', 'nd', 'rd'][date%10-1] || 'th';
	},
	w   : function(d, o) { // local
		return o.weekNumberCalculation(d);
	},
	W   : function(d) { // ISO
		return iso8601Week(d);
	}
};

/* thanks jQuery UI (https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js)
 *
 * Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
 * @param  date  Date - the date to get the week for
 * @return  number - the number of the week within the year that contains this date
 */
function iso8601Week(date) {
	var time;
	var checkDate = new Date(date.getTime());

	// Find Thursday of this week starting on Monday
	checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));

	time = checkDate.getTime();
	checkDate.setMonth(0); // Compare with Jan 1
	checkDate.setDate(1);
	return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
}

<commit_msg>expose dateFormatters for customization<commit_after>

fc.addDays = addDays;
fc.cloneDate = cloneDate;
fc.parseDate = parseDate;
fc.parseISO8601 = parseISO8601;
fc.parseTime = parseTime;
fc.formatDate = formatDate;
fc.formatDates = formatDates;

/* Date Math
-----------------------------------------------------------------------------*/

var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
	DAY_MS = 86400000,
	HOUR_MS = 3600000,
	MINUTE_MS = 60000;

function addYears(d, n, keepTime) {
	d.setFullYear(d.getFullYear() + n);
	if (!keepTime) {
		clearTime(d);
	}
	return d;
}

function addMonths(d, n, keepTime) { // prevents day overflow/underflow
	if (+d) { // prevent infinite looping on invalid dates
		var m = d.getMonth() + n,
			check = cloneDate(d);
		check.setDate(1);
		check.setMonth(m);
		d.setMonth(m);
		if (!keepTime) {
			clearTime(d);
		}
		while (d.getMonth() != check.getMonth()) {
			d.setDate(d.getDate() + (d < check ? 1 : -1));
		}
	}
	return d;
}

function addDays(d, n, keepTime) { // deals with daylight savings
	if (+d) {
		var dd = d.getDate() + n,
			check = cloneDate(d);
		check.setHours(9); // set to middle of day
		check.setDate(dd);
		d.setDate(dd);
		if (!keepTime) {
			clearTime(d);
		}
		fixDate(d, check);
	}
	return d;
}

function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes
	if (+d) { // prevent infinite looping on invalid dates
		while (d.getDate() != check.getDate()) {
			d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
		}
	}
}

function addMinutes(d, n) {
	d.setMinutes(d.getMinutes() + n);
	return d;
}

function clearTime(d) {
	d.setHours(0);
	d.setMinutes(0);
	d.setSeconds(0);
	d.setMilliseconds(0);
	return d;
}

function cloneDate(d, dontKeepTime) {
	if (dontKeepTime) {
		return clearTime(new Date(+d));
	}
	return new Date(+d);
}

function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
	var i=0, d;
	do {
		d = new Date(1970, i++, 1);
	} while (d.getHours()); // != 0
	return d;
}

function skipWeekend(date, inc, excl) {
	inc = inc || 1;
	while (!date.getDay() || (excl && date.getDay()==1 || !excl && date.getDay()==6)) {
		addDays(date, inc);
	}
	return date;
}

function dayDiff(d1, d2) { // d1 - d2
	return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS);
}

function setYMD(date, y, m, d) {
	if (y !== undefined && y != date.getFullYear()) {
		date.setDate(1);
		date.setMonth(0);
		date.setFullYear(y);
	}
	if (m !== undefined && m != date.getMonth()) {
		date.setDate(1);
		date.setMonth(m);
	}
	if (d !== undefined) {
		date.setDate(d);
	}
}

/* Date Parsing
-----------------------------------------------------------------------------*/

function parseDate(s, ignoreTimezone) { // ignoreTimezone defaults to true
	if (typeof s == 'object') { // already a Date object
		return s;
	}
	if (typeof s == 'number') { // a UNIX timestamp
		return new Date(s * 1000);
	}
	if (typeof s == 'string') {
		if (s.match(/^\d+(\.\d+)?$/)) { // a UNIX timestamp
			return new Date(parseFloat(s) * 1000);
		}
		if (ignoreTimezone === undefined) {
			ignoreTimezone = true;
		}
		return parseISO8601(s, ignoreTimezone) || (s ? new Date(s) : null);
	}
	// TODO: never return invalid dates (like from new Date(<string>)), return null instead
	return null;
}

function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false
	// derived from http://delete.me.uk/2005/03/iso8601.html
	// TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
	var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);
	if (!m) {
		return null;
	}
	var date = new Date(m[1], 0, 1);
	if (ignoreTimezone || !m[13]) {
		var check = new Date(m[1], 0, 1, 9, 0);
		if (m[3]) {
			date.setMonth(m[3] - 1);
			check.setMonth(m[3] - 1);
		}
		if (m[5]) {
			date.setDate(m[5]);
			check.setDate(m[5]);
		}
		fixDate(date, check);
		if (m[7]) {
			date.setHours(m[7]);
		}
		if (m[8]) {
			date.setMinutes(m[8]);
		}
		if (m[10]) {
			date.setSeconds(m[10]);
		}
		if (m[12]) {
			date.setMilliseconds(Number("0." + m[12]) * 1000);
		}
		fixDate(date, check);
	}else{
		date.setUTCFullYear(
			m[1],
			m[3] ? m[3] - 1 : 0,
			m[5] || 1
		);
		date.setUTCHours(
			m[7] || 0,
			m[8] || 0,
			m[10] || 0,
			m[12] ? Number("0." + m[12]) * 1000 : 0
		);
		if (m[14]) {
			var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0);
			offset *= m[15] == '-' ? 1 : -1;
			date = new Date(+date + (offset * 60 * 1000));
		}
	}
	return date;
}

function parseTime(s) { // returns minutes since start of day
	if (typeof s == 'number') { // an hour
		return s * 60;
	}
	if (typeof s == 'object') { // a Date object
		return s.getHours() * 60 + s.getMinutes();
	}
	var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
	if (m) {
		var h = parseInt(m[1], 10);
		if (m[3]) {
			h %= 12;
			if (m[3].toLowerCase().charAt(0) == 'p') {
				h += 12;
			}
		}
		return h * 60 + (m[2] ? parseInt(m[2], 10) : 0);
	}
}

/* Date Formatting
-----------------------------------------------------------------------------*/
// TODO: use same function formatDate(date, [date2], format, [options])

function formatDate(date, format, options) {
	return formatDates(date, null, format, options);
}

function formatDates(date1, date2, format, options) {
	options = options || defaults;
	var date = date1,
		otherDate = date2,
		i, len = format.length, c,
		i2, formatter,
		res = '';
	for (i=0; i<len; i++) {
		c = format.charAt(i);
		if (c == "'") {
			for (i2=i+1; i2<len; i2++) {
				if (format.charAt(i2) == "'") {
					if (date) {
						if (i2 == i+1) {
							res += "'";
						}else{
							res += format.substring(i+1, i2);
						}
						i = i2;
					}
					break;
				}
			}
		}
		else if (c == '(') {
			for (i2=i+1; i2<len; i2++) {
				if (format.charAt(i2) == ')') {
					var subres = formatDate(date, format.substring(i+1, i2), options);
					if (parseInt(subres.replace(/\D/, ''), 10)) {
						res += subres;
					}
					i = i2;
					break;
				}
			}
		}
		else if (c == '[') {
			for (i2=i+1; i2<len; i2++) {
				if (format.charAt(i2) == ']') {
					var subformat = format.substring(i+1, i2);
					var subres = formatDate(date, subformat, options);
					if (subres != formatDate(otherDate, subformat, options)) {
						res += subres;
					}
					i = i2;
					break;
				}
			}
		}
		else if (c == '{') {
			date = date2;
			otherDate = date1;
		}
		else if (c == '}') {
			date = date1;
			otherDate = date2;
		}
		else {
			for (i2=len; i2>i; i2--) {
				if (formatter = dateFormatters[format.substring(i, i2)]) {
					if (date) {
						res += formatter(date, options);
					}
					i = i2 - 1;
					break;
				}
			}
			if (i2 == i) {
				if (date) {
					res += c;
				}
			}
		}
	}
	return res;
};

var dateFormatters = {
	s	: function(d)	{ return d.getSeconds() },
	ss	: function(d)	{ return zeroPad(d.getSeconds()) },
	m	: function(d)	{ return d.getMinutes() },
	mm	: function(d)	{ return zeroPad(d.getMinutes()) },
	h	: function(d)	{ return d.getHours() % 12 || 12 },
	hh	: function(d)	{ return zeroPad(d.getHours() % 12 || 12) },
	H	: function(d)	{ return d.getHours() },
	HH	: function(d)	{ return zeroPad(d.getHours()) },
	d	: function(d)	{ return d.getDate() },
	dd	: function(d)	{ return zeroPad(d.getDate()) },
	ddd	: function(d,o)	{ return o.dayNamesShort[d.getDay()] },
	dddd: function(d,o)	{ return o.dayNames[d.getDay()] },
	M	: function(d)	{ return d.getMonth() + 1 },
	MM	: function(d)	{ return zeroPad(d.getMonth() + 1) },
	MMM	: function(d,o)	{ return o.monthNamesShort[d.getMonth()] },
	MMMM: function(d,o)	{ return o.monthNames[d.getMonth()] },
	yy	: function(d)	{ return (d.getFullYear()+'').substring(2) },
	yyyy: function(d)	{ return d.getFullYear() },
	t	: function(d)	{ return d.getHours() < 12 ? 'a' : 'p' },
	tt	: function(d)	{ return d.getHours() < 12 ? 'am' : 'pm' },
	T	: function(d)	{ return d.getHours() < 12 ? 'A' : 'P' },
	TT	: function(d)	{ return d.getHours() < 12 ? 'AM' : 'PM' },
	u	: function(d)	{ return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") },
	S	: function(d)	{
		var date = d.getDate();
		if (date > 10 && date < 20) {
			return 'th';
		}
		return ['st', 'nd', 'rd'][date%10-1] || 'th';
	},
	w   : function(d, o) { // local
		return o.weekNumberCalculation(d);
	},
	W   : function(d) { // ISO
		return iso8601Week(d);
	}
};
fc.dateFormatters = dateFormatters;

/* thanks jQuery UI (https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js)
 *
 * Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
 * @param  date  Date - the date to get the week for
 * @return  number - the number of the week within the year that contains this date
 */
function iso8601Week(date) {
	var time;
	var checkDate = new Date(date.getTime());

	// Find Thursday of this week starting on Monday
	checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));

	time = checkDate.getTime();
	checkDate.setMonth(0); // Compare with Jan 1
	checkDate.setDate(1);
	return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
}

<|endoftext|>


<commit_before>/** Represents a single project in the work carousel */
import Parallax3D from '../parallax';

function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }

export class CarouselTag {
  constructor(elem) {
    this.elem = elem;
    this.name = elem.textContent;
    this.carousel = undefined; // Will be set when added to a carousel
    this.selected = false;

    this.elem.addEventListener('click', () => this.toggle());
  }

  select() {
    this.selected = true;
    if (this.carousel) this.carousel.filter(this);
  }
  deselect() {
    if (this.selected) {
      this.selected = false;
      if (this.carousel) this.carousel.clearFilter();
    }
  }
  toggle() {
    this[this.selected ? 'deselect' : 'select']();
  }

  fade() { this.elem.classList.add('faded'); }
  opacify() { this.elem.classList.remove('faded'); }
}

export class CarouselCard {
  constructor(elem, tagNames) {
    this.elem = elem;
    this.elem.classList.add('carousel-card');
    // Move elem into a wrapper div
    this.wrapper = document.createElement('div');
    this.wrapper.className = 'carousel-card-wrapper';
    this.elem.parentNode.insertBefore(this.wrapper, this.elem);
    this.wrapper.appendChild(this.elem);

    this.tagNames = tagNames || elem.dataset.tags.split(',').map(t => t.trim());
    this.titleElem = elem.getElementsByTagName('h1')[0];

    this.carousel = undefined; // Will be set when added to a carousel
    this.hidden = false;
    this.transitionTime = this.baseTransitionTime = 0.5;
    this.parallax = new Parallax3D(this.elem);

    this.elem.addEventListener('click', () => this.clickHandler());
    this.wrapper.addEventListener('scroll', () => this.scrollHandler());

    // Title manipulation interface

    const _this2 = this;
    /**
     * Interface for updating styles on the title.
     * Examples:
     *   this.title.translate = '-10px'
     *   this.title.opacity = 0.5
     */
    this.title = {
      _elem: _this2.titleElem,
      display() { this._elem.className = 'in-initial'; },
      fadeIn() { this._elem.className = 'fade-in'; },
      fadeOut() { this._elem.className = 'fade-out'; },
      fadeInLeft() { this._elem.className = 'in-from-left'; },
      fadeInRight() { this._elem.className = 'in-from-right'; },
      fadeOutLeft() { this._elem.className = 'out-to-left'; },
      fadeOutRight() { this._elem.className = 'out-to-right'; },
      fadeOutTop() { this._elem.className = 'out-to-top'; },
      moveDown() { this._elem.className = 'down'; },
      moveUp() { this._elem.className = 'up'; },
    };
  }

  /**
   * Update the card wrapper's CSS in order to reflect changes that were made to its JS
   * representation.
   */
  _applyTransform() {
    this.wrapper.style.transform = [
      'translateX(-50%)',
      `translateX(${this._translate})`,
    ].join(' ');
  }

  // Translation

  get translate() { return this._translate; }
  set translate(val) { this._translate = val; this._applyTransform(); }

  // Transition time

  get transitionTime() { return this._transitionTime; }
  set transitionTime(secs) {
    this._transitionTime = secs;
    this.wrapper.style.transition = `transform ${secs}s`;
  }

  setEnabled(enable = true) {
    const totalTime = this.transitionTime * 1000;
    const startTime = new Date();

    const existingRot = this.parallax.rotmax;
    const goal = enable ? 10 : 0;
    const changeNeeded = goal - existingRot;

    // Recursively adjust using setAnimationFrame until transitionTime is reached
    function step(parallax) {
      const timeElapsed = new Date() - startTime;
      // Still going
      if (timeElapsed < totalTime) {
        const progress = (timeElapsed / totalTime);
        parallax.rotmax = existingRot + (progress * changeNeeded);
        parallax.rotate();
        requestAnimationFrame(() => step(parallax));
      } else parallax.rotmax = goal;
    }
    step(this.parallax); // Start
  }
  disable() { this.setEnabled(false); }
  enable() { this.setEnabled(true); }

  hide() {
    this.hidden = true;
    this.elem.classList.add('hidden');
    // If this is centered
    const thisIndex = this.carousel.cards.indexOf(this);
    if (this.carousel.position === this.carousel.adjustIndex(thisIndex)) this.title.fadeOutTop();
  }
  show() {
    this.hidden = false;
    this.elem.classList.remove('hidden');
  }

  // Move the carousel so that this card is in the center
  centerSelf() {
    const thisIndex = this.carousel.cards.indexOf(this);
    this.carousel.position = this.carousel.adjustIndex(thisIndex);
  }

  // Bigger!
  expand() {
    // Prevents incorrect result when expanding and collapsing rapidly. Ensures that a pre-existing
    // timeout won't remove the expanded-x class after it's been added by this method.
    clearInterval(this._expansionTimeout);

    this.elem.classList.add('expanded-x');
    this.title.moveDown();
    this.disable();
    this._expansionTimeout = setTimeout(() => {
      this.elem.classList.add('expanded-y');
      this._expansionTimeout = setTimeout(() => { this.elem.style.maxHeight = 'none'; this.elem.classList.remove('expanded-y'); }, 500);
      document.getElementById('email').classList.add('hidden');
      window.nav.disableScrollBinding = true;
    }, 500);

    this.expanded = true;
    if (this.carousel) this.carousel.expanded(this);
  }
  collapse() {
    clearInterval(this._expansionTimeout);

    this.elem.style.maxHeight = '';
    this.elem.classList.add('expanded-y');
    setTimeout(() => this.elem.classList.remove('expanded-y'), 10);
    document.getElementById('email').classList.remove('hidden');
    window.nav.disableScrollBinding = false;

    this._expansionTimeout = setTimeout(() => {
      this.elem.classList.remove('expanded-x');
      this.title.moveUp();
      this.enable();
    }, 500);

    this.expanded = false;
    if (this.carousel) this.carousel.collapsed(this);
  }
  toggleExpand() {
    this[this.expanded ? 'collapse' : 'expand']();
  }

  fade() { this.elem.style.opacity = 0.5; }
  opacify() { this.elem.style.opacity = ''; }

  clickHandler() {
    if (this.carousel && !this.hidden) {
      const i1 = this.carousel.cards.indexOf(this);
      const i2 = this.carousel.adjustIndex(i1);
      if (this.carousel.position !== i2) this.centerSelf();
      else this.toggleExpand();
    } else if (!this.hidden) {
      this.toggleExpand();
    }
  }

  scrollHandler() {
    const transitionDistance = window.innerHeight / 10;
    const progress = Math.min(this.wrapper.scrollTop / transitionDistance, 1);

    const nav = document.getElementById('navigation');
    const tags = document.querySelector('.tags');
    nav.style.opacity = 0.5 * (1 - progress);
    tags.style.opacity = 1 - progress;

    // Turn off pointer events when halfway faded
    nav.style.pointerEvents = tags.style.pointerEvents = progress < 0.5 ? '' : 'none';
  }
}

/** Collects and orchestrates multiple CarouselCards */
export class Carousel {
  constructor(cards, tags, emptyElem) {
    this.tags = tags;
    this.tags.forEach((t) => { t.carousel = this; });
    this.cards = cards;
    this.cards.forEach((c) => { c.carousel = this; });
    this.emptyElem = emptyElem;
    this.hiddenIndices = [];

    this._position = 0;
    this.cards[this._position].title.display();
    // Trigger immediate layout of cards
    this.position = this._position;

    window.addEventListener('keydown', (e) => {
      // Carousel left/right with arrow keys, but only if carousel is expanded.
      // Carousel is expanded when the parent is #work-wrapper, the parent is .screen if it's not
      const projectsContainer = document.getElementsByClassName('projects-container')[0];
      if (projectsContainer.parentNode.id === 'work-wrapper') {
        if (e.key === 'ArrowRight') this.right();
        if (e.key === 'ArrowLeft') this.left();
      }
    });
  }

  // Transition time logic

  get baseTransitionTime() {
    const answer = this.cards[0].baseTransitionTime;
    // Warn if not every card has the same baseTransitionTime
    if (!this.cards.every(card => card.baseTransitionTime === answer)) console.warn('Carousel.baseTransitionTime called but baseTransitionTime of cards is inconsistent');
    return answer;
  }
  get transitionTime() {
    const answer = this.cards[0].transitionTime;
    // Warn if not every card has the same transitionTime
    if (!this.cards.every(card => card.transitionTime === answer)) console.warn('Carousel.transitionTime called but transitionTime of cards is inconsistent');
    return answer;
  }
  set transitionTime(secs) {
    this.cards.forEach((c) => { c.transitionTime = secs; });
  }

  // Rearrange

  // Adjust an index based on the number of hidden cards that come before it
  adjustIndex(index) {
    return index - this.hiddenIndices.filter(i => i < index).length;
  }

  get position() { return this._position; }
  set position(pos) {
    (async () => {
      const delta = Math.abs(this._position - pos);

      // Adjust transition time based on how many cards we're jumping
      const btt = this.baseTransitionTime;
      this.transitionTime = ((0.5 * btt) * ((delta || 1) - 1)) + btt;

      // If any card is open, collapse it and wait
      if (delta > 0 && typeof this.expandedIndex !== 'undefined') {
        this.cards[this.expandedIndex].collapse();
        await delay(1000);
      }

      // Apply certain changes to each card
      this.cards.forEach((card, index) => {
        const adj = i => this.adjustIndex(i);
        // Calculate position we're going to, and parse old position from old transform
        const cardPosition = adj(index) - pos;
        const oldTranslation = (card.translate || `${index * 48}vw`).slice(0, -2);
        const oldPosition = [
          oldTranslation / 48, // Could be in its normal location
          (oldTranslation - 12.5) / 48, // Could be to the right of an expanded card
          (oldTranslation + 12.5) / 48, // Could be to the left of an expanded card
        ].find(i => Math.floor(i) === i); // Whichever case yields an integer solution for position
        // Calculate an offset based on position relative to any expanded card
        let cardOffset = 0;
        if (adj(index) > adj(this.expandedIndex)) cardOffset = 12.5;
        if (adj(index) < adj(this.expandedIndex)) cardOffset = -12.5;
        // Set new translation
        const cardTranslation = `${(48 * cardPosition) + cardOffset}vw`;
        card.translate = cardTranslation;

        // Expand / disable cards based on whether they're centered
        if (cardPosition === 0 && !card.expanded) card.enable();
        else card.disable(); // Not center card

        // Apply animations to titles
        if (cardPosition === 0) { // Center card
          if (oldPosition < 0) card.title.fadeInLeft(); // coming from left
          if (oldPosition > 0) card.title.fadeInRight(); // coming from right
        } else if (oldPosition === 0) {
          if (cardPosition < 0) card.title.fadeOutLeft(); // going left
          if (cardPosition > 0) card.title.fadeOutRight(); // going right
        }
      });
      // Update internally-stored position tracker
      this._position = pos;
    })();
  }

  expanded(card) {
    this.expandedIndex = this.cards.indexOf(card);
    this.position = this.position; // Re-layout
    this.cards.forEach((c) => {
      if (c !== card) c.fade();
    });
  }
  collapsed() {
    this.expandedIndex = undefined;
    setTimeout(() => {
      this.position = this.position; // Re-layout
      this.cards.forEach(c => c.opacify());
    }, 500);
  }

  get minIndex() { return 0; } // eslint-disable-line class-methods-use-this
  get maxIndex() { return Math.max(this.cards.length - this.hiddenIndices.length - 1, 0); }

  left() {
    if (this.position > this.minIndex) this.position -= 1;
  }
  right() {
    if (this.position < this.maxIndex) this.position += 1;
  }

  async filter(tag) {
    tag.opacify();
    this.tags.filter(t => t !== tag).forEach(t => t.fade());
    const cardsToRemove = this.cards.filter(c => !c.tagNames.includes(tag.name));
    // If the expanded card needs to be hidden, collapse it first
    if (typeof this.expandedIndex !== 'undefined') {
      this.cards[this.expandedIndex].collapse();
      await delay(1000);
    }
    // Make sure the empty indicator isn't displayed if it shouldn't be
    if (cardsToRemove.length < this.cards.length) this.emptyElem.classList.remove('displayed');
    // Hide cards
    this.cards.forEach(c => c.show()); // Clean slate
    cardsToRemove.forEach(c => c.hide()); // Play removed animation on affected cards
    if (cardsToRemove.length) await delay(1000);
    // Re-layout
    this.hiddenIndices = cardsToRemove.map(c => this.cards.indexOf(c));
    this.position = this.adjustIndex(this.position);
    // Show message if no cards are displayed
    if (this.hiddenIndices.length === this.cards.length) this.emptyElem.classList.add('displayed');
  }

  async clearFilter() {
    this.tags.forEach(t => t.opacify());
    if (this.hiddenIndices.length) {
      // Collapse all cards
      if (typeof this.expandedIndex !== 'undefined') {
        this.cards[this.expandedIndex].collapse();
        await delay(1000);
      }
      // Adjust position to prevent shift when showing cards, unless EVERY card is hidden
      let newPos;
      if (this.hiddenIndices.length !== this.cards.length) {
        newPos = this.position + this.hiddenIndices.filter(i => i <= this.position).length;
      } else {
        newPos = this.position;
      }
      // Hide 'No projects found' message
      this.emptyElem.classList.remove('displayed');
      // Re-layout assuming no cards are hidden
      this.hiddenIndices = [];
      this.position = newPos;
      await delay(500);
      // Actually visually reveal the previously hidden cards
      this.cards.forEach(c => c.show());
    }
  }
}
<commit_msg>Mark other tags as deselected when selecting a tag<commit_after>/** Represents a single project in the work carousel */
import Parallax3D from '../parallax';

function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }

export class CarouselTag {
  constructor(elem) {
    this.elem = elem;
    this.name = elem.textContent;
    this.carousel = undefined; // Will be set when added to a carousel
    this.selected = false;

    this.elem.addEventListener('click', () => this.toggle());
  }

  select() {
    this.selected = true;
    if (this.carousel) this.carousel.filter(this);
  }
  deselect() {
    if (this.selected) {
      this.selected = false;
      if (this.carousel) this.carousel.clearFilter();
    }
  }
  toggle() {
    this[this.selected ? 'deselect' : 'select']();
  }

  fade() { this.elem.classList.add('faded'); }
  opacify() { this.elem.classList.remove('faded'); }
}

export class CarouselCard {
  constructor(elem, tagNames) {
    this.elem = elem;
    this.elem.classList.add('carousel-card');
    // Move elem into a wrapper div
    this.wrapper = document.createElement('div');
    this.wrapper.className = 'carousel-card-wrapper';
    this.elem.parentNode.insertBefore(this.wrapper, this.elem);
    this.wrapper.appendChild(this.elem);

    this.tagNames = tagNames || elem.dataset.tags.split(',').map(t => t.trim());
    this.titleElem = elem.getElementsByTagName('h1')[0];

    this.carousel = undefined; // Will be set when added to a carousel
    this.hidden = false;
    this.transitionTime = this.baseTransitionTime = 0.5;
    this.parallax = new Parallax3D(this.elem);

    this.elem.addEventListener('click', () => this.clickHandler());
    this.wrapper.addEventListener('scroll', () => this.scrollHandler());

    // Title manipulation interface

    const _this2 = this;
    /**
     * Interface for updating styles on the title.
     * Examples:
     *   this.title.translate = '-10px'
     *   this.title.opacity = 0.5
     */
    this.title = {
      _elem: _this2.titleElem,
      display() { this._elem.className = 'in-initial'; },
      fadeIn() { this._elem.className = 'fade-in'; },
      fadeOut() { this._elem.className = 'fade-out'; },
      fadeInLeft() { this._elem.className = 'in-from-left'; },
      fadeInRight() { this._elem.className = 'in-from-right'; },
      fadeOutLeft() { this._elem.className = 'out-to-left'; },
      fadeOutRight() { this._elem.className = 'out-to-right'; },
      fadeOutTop() { this._elem.className = 'out-to-top'; },
      moveDown() { this._elem.className = 'down'; },
      moveUp() { this._elem.className = 'up'; },
    };
  }

  /**
   * Update the card wrapper's CSS in order to reflect changes that were made to its JS
   * representation.
   */
  _applyTransform() {
    this.wrapper.style.transform = [
      'translateX(-50%)',
      `translateX(${this._translate})`,
    ].join(' ');
  }

  // Translation

  get translate() { return this._translate; }
  set translate(val) { this._translate = val; this._applyTransform(); }

  // Transition time

  get transitionTime() { return this._transitionTime; }
  set transitionTime(secs) {
    this._transitionTime = secs;
    this.wrapper.style.transition = `transform ${secs}s`;
  }

  setEnabled(enable = true) {
    const totalTime = this.transitionTime * 1000;
    const startTime = new Date();

    const existingRot = this.parallax.rotmax;
    const goal = enable ? 10 : 0;
    const changeNeeded = goal - existingRot;

    // Recursively adjust using setAnimationFrame until transitionTime is reached
    function step(parallax) {
      const timeElapsed = new Date() - startTime;
      // Still going
      if (timeElapsed < totalTime) {
        const progress = (timeElapsed / totalTime);
        parallax.rotmax = existingRot + (progress * changeNeeded);
        parallax.rotate();
        requestAnimationFrame(() => step(parallax));
      } else parallax.rotmax = goal;
    }
    step(this.parallax); // Start
  }
  disable() { this.setEnabled(false); }
  enable() { this.setEnabled(true); }

  hide() {
    this.hidden = true;
    this.elem.classList.add('hidden');
    // If this is centered
    const thisIndex = this.carousel.cards.indexOf(this);
    if (this.carousel.position === this.carousel.adjustIndex(thisIndex)) this.title.fadeOutTop();
  }
  show() {
    this.hidden = false;
    this.elem.classList.remove('hidden');
  }

  // Move the carousel so that this card is in the center
  centerSelf() {
    const thisIndex = this.carousel.cards.indexOf(this);
    this.carousel.position = this.carousel.adjustIndex(thisIndex);
  }

  // Bigger!
  expand() {
    // Prevents incorrect result when expanding and collapsing rapidly. Ensures that a pre-existing
    // timeout won't remove the expanded-x class after it's been added by this method.
    clearInterval(this._expansionTimeout);

    this.elem.classList.add('expanded-x');
    this.title.moveDown();
    this.disable();
    this._expansionTimeout = setTimeout(() => {
      this.elem.classList.add('expanded-y');
      this._expansionTimeout = setTimeout(() => { this.elem.style.maxHeight = 'none'; this.elem.classList.remove('expanded-y'); }, 500);
      document.getElementById('email').classList.add('hidden');
      window.nav.disableScrollBinding = true;
    }, 500);

    this.expanded = true;
    if (this.carousel) this.carousel.expanded(this);
  }
  collapse() {
    clearInterval(this._expansionTimeout);

    this.elem.style.maxHeight = '';
    this.elem.classList.add('expanded-y');
    setTimeout(() => this.elem.classList.remove('expanded-y'), 10);
    document.getElementById('email').classList.remove('hidden');
    window.nav.disableScrollBinding = false;

    this._expansionTimeout = setTimeout(() => {
      this.elem.classList.remove('expanded-x');
      this.title.moveUp();
      this.enable();
    }, 500);

    this.expanded = false;
    if (this.carousel) this.carousel.collapsed(this);
  }
  toggleExpand() {
    this[this.expanded ? 'collapse' : 'expand']();
  }

  fade() { this.elem.style.opacity = 0.5; }
  opacify() { this.elem.style.opacity = ''; }

  clickHandler() {
    if (this.carousel && !this.hidden) {
      const i1 = this.carousel.cards.indexOf(this);
      const i2 = this.carousel.adjustIndex(i1);
      if (this.carousel.position !== i2) this.centerSelf();
      else this.toggleExpand();
    } else if (!this.hidden) {
      this.toggleExpand();
    }
  }

  scrollHandler() {
    const transitionDistance = window.innerHeight / 10;
    const progress = Math.min(this.wrapper.scrollTop / transitionDistance, 1);

    const nav = document.getElementById('navigation');
    const tags = document.querySelector('.tags');
    nav.style.opacity = 0.5 * (1 - progress);
    tags.style.opacity = 1 - progress;

    // Turn off pointer events when halfway faded
    nav.style.pointerEvents = tags.style.pointerEvents = progress < 0.5 ? '' : 'none';
  }
}

/** Collects and orchestrates multiple CarouselCards */
export class Carousel {
  constructor(cards, tags, emptyElem) {
    this.tags = tags;
    this.tags.forEach((t) => { t.carousel = this; });
    this.cards = cards;
    this.cards.forEach((c) => { c.carousel = this; });
    this.emptyElem = emptyElem;
    this.hiddenIndices = [];

    this._position = 0;
    this.cards[this._position].title.display();
    // Trigger immediate layout of cards
    this.position = this._position;

    window.addEventListener('keydown', (e) => {
      // Carousel left/right with arrow keys, but only if carousel is expanded.
      // Carousel is expanded when the parent is #work-wrapper, the parent is .screen if it's not
      const projectsContainer = document.getElementsByClassName('projects-container')[0];
      if (projectsContainer.parentNode.id === 'work-wrapper') {
        if (e.key === 'ArrowRight') this.right();
        if (e.key === 'ArrowLeft') this.left();
      }
    });
  }

  // Transition time logic

  get baseTransitionTime() {
    const answer = this.cards[0].baseTransitionTime;
    // Warn if not every card has the same baseTransitionTime
    if (!this.cards.every(card => card.baseTransitionTime === answer)) console.warn('Carousel.baseTransitionTime called but baseTransitionTime of cards is inconsistent');
    return answer;
  }
  get transitionTime() {
    const answer = this.cards[0].transitionTime;
    // Warn if not every card has the same transitionTime
    if (!this.cards.every(card => card.transitionTime === answer)) console.warn('Carousel.transitionTime called but transitionTime of cards is inconsistent');
    return answer;
  }
  set transitionTime(secs) {
    this.cards.forEach((c) => { c.transitionTime = secs; });
  }

  // Rearrange

  // Adjust an index based on the number of hidden cards that come before it
  adjustIndex(index) {
    return index - this.hiddenIndices.filter(i => i < index).length;
  }

  get position() { return this._position; }
  set position(pos) {
    (async () => {
      const delta = Math.abs(this._position - pos);

      // Adjust transition time based on how many cards we're jumping
      const btt = this.baseTransitionTime;
      this.transitionTime = ((0.5 * btt) * ((delta || 1) - 1)) + btt;

      // If any card is open, collapse it and wait
      if (delta > 0 && typeof this.expandedIndex !== 'undefined') {
        this.cards[this.expandedIndex].collapse();
        await delay(1000);
      }

      // Apply certain changes to each card
      this.cards.forEach((card, index) => {
        const adj = i => this.adjustIndex(i);
        // Calculate position we're going to, and parse old position from old transform
        const cardPosition = adj(index) - pos;
        const oldTranslation = (card.translate || `${index * 48}vw`).slice(0, -2);
        const oldPosition = [
          oldTranslation / 48, // Could be in its normal location
          (oldTranslation - 12.5) / 48, // Could be to the right of an expanded card
          (oldTranslation + 12.5) / 48, // Could be to the left of an expanded card
        ].find(i => Math.floor(i) === i); // Whichever case yields an integer solution for position
        // Calculate an offset based on position relative to any expanded card
        let cardOffset = 0;
        if (adj(index) > adj(this.expandedIndex)) cardOffset = 12.5;
        if (adj(index) < adj(this.expandedIndex)) cardOffset = -12.5;
        // Set new translation
        const cardTranslation = `${(48 * cardPosition) + cardOffset}vw`;
        card.translate = cardTranslation;

        // Expand / disable cards based on whether they're centered
        if (cardPosition === 0 && !card.expanded) card.enable();
        else card.disable(); // Not center card

        // Apply animations to titles
        if (cardPosition === 0) { // Center card
          if (oldPosition < 0) card.title.fadeInLeft(); // coming from left
          if (oldPosition > 0) card.title.fadeInRight(); // coming from right
        } else if (oldPosition === 0) {
          if (cardPosition < 0) card.title.fadeOutLeft(); // going left
          if (cardPosition > 0) card.title.fadeOutRight(); // going right
        }
      });
      // Update internally-stored position tracker
      this._position = pos;
    })();
  }

  expanded(card) {
    this.expandedIndex = this.cards.indexOf(card);
    this.position = this.position; // Re-layout
    this.cards.forEach((c) => {
      if (c !== card) c.fade();
    });
  }
  collapsed() {
    this.expandedIndex = undefined;
    setTimeout(() => {
      this.position = this.position; // Re-layout
      this.cards.forEach(c => c.opacify());
    }, 500);
  }

  get minIndex() { return 0; } // eslint-disable-line class-methods-use-this
  get maxIndex() { return Math.max(this.cards.length - this.hiddenIndices.length - 1, 0); }

  left() {
    if (this.position > this.minIndex) this.position -= 1;
  }
  right() {
    if (this.position < this.maxIndex) this.position += 1;
  }

  async filter(tag) {
    tag.opacify();
    this.tags.filter(t => t !== tag).forEach((t) => { t.selected = false; t.fade(); });
    const cardsToRemove = this.cards.filter(c => !c.tagNames.includes(tag.name));
    // If the expanded card needs to be hidden, collapse it first
    if (typeof this.expandedIndex !== 'undefined') {
      this.cards[this.expandedIndex].collapse();
      await delay(1000);
    }
    // Make sure the empty indicator isn't displayed if it shouldn't be
    if (cardsToRemove.length < this.cards.length) this.emptyElem.classList.remove('displayed');
    // Hide cards
    this.cards.forEach(c => c.show()); // Clean slate
    cardsToRemove.forEach(c => c.hide()); // Play removed animation on affected cards
    if (cardsToRemove.length) await delay(1000);
    // Re-layout
    this.hiddenIndices = cardsToRemove.map(c => this.cards.indexOf(c));
    this.position = this.adjustIndex(this.position);
    // Show message if no cards are displayed
    if (this.hiddenIndices.length === this.cards.length) this.emptyElem.classList.add('displayed');
  }

  async clearFilter() {
    this.tags.forEach(t => t.opacify());
    if (this.hiddenIndices.length) {
      // Collapse all cards
      if (typeof this.expandedIndex !== 'undefined') {
        this.cards[this.expandedIndex].collapse();
        await delay(1000);
      }
      // Adjust position to prevent shift when showing cards, unless EVERY card is hidden
      let newPos;
      if (this.hiddenIndices.length !== this.cards.length) {
        newPos = this.position + this.hiddenIndices.filter(i => i <= this.position).length;
      } else {
        newPos = this.position;
      }
      // Hide 'No projects found' message
      this.emptyElem.classList.remove('displayed');
      // Re-layout assuming no cards are hidden
      this.hiddenIndices = [];
      this.position = newPos;
      await delay(500);
      // Actually visually reveal the previously hidden cards
      this.cards.forEach(c => c.show());
    }
  }
}
<|endoftext|>


<commit_before>$(function () {
    // JavaScript to automate acknowledgements, so they don't need to be put in one at a time. To add someone, make a new element in the array, insert their name and their position.
    // Author: Trevor Murphy
    text = '';
    var people = [
        ['Dr. Ben Ruddell', 'Project Director','Northern Arizona University','NAU'],
        ['Dr. Robert Pastel', 'Development Lead','Michigan Technological University','MTU'],
        ['Paul Bahle', 'Former Human Factors Specialist','Michigan Technological University','MTU'],
        ['Tom Berg','Web Developer (2019-present)','Michigan Technological University','MTU'],
        ['Kyle Bray', 'Former Web/Sensor Developer (2016-2018)','Michigan Technological University','MTU'],
        ['Payton Dunning', 'Former Web Developer (2017-2018)','Michigan Technological University','MTU'],
        ['Piper Dougherty', 'Former Web Developer (2016-2017)','Michigan Technological University','MTU'],
        ['Paul Esch-Laurent', 'Former Sensor/Web Developer (2016-2018)','Michigan Technological University','MTU'],
        ['Connor Gessner', 'Former Circuits Developer','Michigan Technological University','MTU'],
        ['Justin Havely', 'Former Web Developer (2017-2018)','Northern Arizona University','NAU'],
        ['Alex Israels','Former Sensor Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Alejandro Jeronimo Ayala Perez','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Zachary Johnson','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Will Kirkconnell','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Nick Lanter', 'Former Sensor Developer (2016-2017)','Michigan Technological University','MTU'],
        ['Gary Lord', 'Former Sensor Developer','Michigan Technological University','MTU'],
        ['Caleb Melnychenko','Web Developer (2018-present)','Michigan Technological University','MTU'],
        ['Trevor Murphy', 'Former Web Developer (2017-2018)','Michigan Technological University','MTU'],
        ['Abby Myers', 'Web Developer (2020-present)','Michigan Technological University','MTU'],
        ['Collin Palmer','Web/Sensor Developer (2018-present)','Michigan Technological University','MTU'],
        ['Cody Reightley', 'Web Developer (2019-present)','Michigan Technological University','MTU'],
        ['Mason Sayles','Web Developer (2019-present)','Michigan Technological University','MTU'],
       ['Jake Schmitz', 'Former Web Developer','Northern Arizona University','NAU'],
        ['Benjamin Slade','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Caden Sumner', 'Former Web Developer (2016-2017)','Michigan Technological University','MTU'],
        ['Reo Tang','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Brendan Zondlak','Former Sensor Developer (2018-2019)','Michigan Technological University','MTU']
    ];
    for ( i = 0; i < people.length; i++ ) {
        if (i % 3 == 0) {
            text += '

';
        }
        if(people[i][3]=='NAU')
        {
            text += '

' + people[i][0] + '
 ' + people[i][1] + '
  ' + 'Institution: '+ people[i][2] + '

';
        }
        else if(people[i][3]=='MTU')
        {
            text += '

' + people[i][0] + '
 ' + people[i][1] + '
  ' + 'Institution: ' + people[i][2] + '

';
        }
    }
    document.getElementById('acknowledgeStart').innerHTML = text;
});<commit_msg>Tom Berg 4/9/2020 -- Updated acknowledgement.js so that the MTU logo is no longer vertically stretched.<commit_after>$(function () {
    // JavaScript to automate acknowledgements, so they don't need to be put in one at a time. To add someone, make a new element in the array, insert their name and their position.
    // Author: Trevor Murphy
    text = '';
    var people = [
        ['Dr. Ben Ruddell', 'Project Director','Northern Arizona University','NAU'],
        ['Dr. Robert Pastel', 'Development Lead','Michigan Technological University','MTU'],
        ['Paul Bahle', 'Former Human Factors Specialist','Michigan Technological University','MTU'],
        ['Tom Berg','Web Developer (2019-present)','Michigan Technological University','MTU'],
        ['Kyle Bray', 'Former Web/Sensor Developer (2016-2018)','Michigan Technological University','MTU'],
        ['Payton Dunning', 'Former Web Developer (2017-2018)','Michigan Technological University','MTU'],
        ['Piper Dougherty', 'Former Web Developer (2016-2017)','Michigan Technological University','MTU'],
        ['Paul Esch-Laurent', 'Former Sensor/Web Developer (2016-2018)','Michigan Technological University','MTU'],
        ['Connor Gessner', 'Former Circuits Developer','Michigan Technological University','MTU'],
        ['Justin Havely', 'Former Web Developer (2017-2018)','Northern Arizona University','NAU'],
        ['Alex Israels','Former Sensor Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Alejandro Jeronimo Ayala Perez','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Zachary Johnson','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Will Kirkconnell','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Nick Lanter', 'Former Sensor Developer (2016-2017)','Michigan Technological University','MTU'],
        ['Gary Lord', 'Former Sensor Developer','Michigan Technological University','MTU'],
        ['Caleb Melnychenko','Web Developer (2018-present)','Michigan Technological University','MTU'],
        ['Trevor Murphy', 'Former Web Developer (2017-2018)','Michigan Technological University','MTU'],
        ['Abby Myers', 'Web Developer (2020-present)','Michigan Technological University','MTU'],
        ['Collin Palmer','Web/Sensor Developer (2018-present)','Michigan Technological University','MTU'],
        ['Cody Reightley', 'Web Developer (2019-present)','Michigan Technological University','MTU'],
        ['Mason Sayles','Web Developer (2019-present)','Michigan Technological University','MTU'],
       ['Jake Schmitz', 'Former Web Developer','Northern Arizona University','NAU'],
        ['Benjamin Slade','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Caden Sumner', 'Former Web Developer (2016-2017)','Michigan Technological University','MTU'],
        ['Reo Tang','Former Web Developer (2018-2019)','Michigan Technological University','MTU'],
        ['Brendan Zondlak','Former Sensor Developer (2018-2019)','Michigan Technological University','MTU']
    ];
    for ( i = 0; i < people.length; i++ ) {
        if (i % 3 == 0) {
            text += '

';
        }
        if(people[i][3]=='NAU')
        {
            text += '

' + people[i][0] + '
 ' + people[i][1] + '
  ' + 'Institution: '+ people[i][2] + '

';
        }
        else if(people[i][3]=='MTU')
        {
            text += '

' + people[i][0] + '
 ' + people[i][1] + '
  ' + 'Institution: ' + people[i][2] + '

';
        }
    }
    document.getElementById('acknowledgeStart').innerHTML = text;
});<|endoftext|>


<commit_before>var uuid = require('node-uuid'),
    jsface = require('jsface'),
    url = require('url'),
    META_KEY = 'x-postman-meta',

    ConvertResult = function (status, message) {
        this.status = status;
        this.message = message;
    },

    Swagger2Postman = jsface.Class({
        constructor: function (options) {
            this.collectionJson = {
                'id': '',
                'name': '',
                'description': '',
                'order': [],
                'folders': [],
                'timestamp': 1413302258635,
                'synced': false,
                'requests': []
            };
            this.basePath = '';
            this.collectionId = '';
            this.folders = {};
            this.baseParams = {};
            this.logger = function () {
            };

            this.options = options || {};

            this.options.includeQueryParams = typeof (this.options.includeQueryParams) == 'undefined' ?
                true : this.options.includeQueryParams;
        },

        setLogger: function (func) {
            this.logger = func;
        },

        validate: function (json) {
            if (!json.hasOwnProperty('swagger') || json.swagger !== '2.0') {
                return new ConvertResult('failed', 'Must contain a swagger field (2.0)');
            }

            if (!json.hasOwnProperty('info')) {
                return new ConvertResult('failed', 'Must contain an info object');
            }
            else {
                var info = json.info;
                if (!info || !info.title) {
                    return new ConvertResult('failed', 'Must contain info.title');
                }
            }

            return new ConvertResult('passed', '');
        },

        setBasePath: function (json) {
            this.basePath = '';
            if (json.host) {
                this.basePath = json.host;
            }
            if (json.basePath) {
                this.basePath += json.basePath;
            }

            if (json.schemes && json.schemes.indexOf('https') != -1) {
                this.basePath = 'https://' + this.basePath;
            }
            else {
                this.basePath = 'http://' + this.basePath;
            }

            if (!this.endsWith(this.basePath, '/')) {
                this.basePath += '/';
            }
        },

        getFolderNameForPath: function (pathUrl) {
            if (pathUrl == '/') {
                return null;
            }
            var segments = pathUrl.split('/'),
                numSegments = segments.length,
                folderName = null;
            this.logger('Getting folder name for path: ' + pathUrl);
            this.logger('Segments: ' + JSON.stringify(segments));
            if (numSegments > 1) {
                folderName = segments[1];

                // create a folder for this path url
                if (!this.folders[folderName]) {
                    this.folders[folderName] = this.createNewFolder(folderName);
                }
                this.logger('For path ' + pathUrl + ', returning folderName ' + this.folders[folderName].name);
                return this.folders[folderName].name;
            }
            else {
                this.logger('Error - path MUST begin with /');
                return null;
            }
        },

        createNewFolder: function (name) {
            var newFolder = {
                'id': uuid.v4(),
                'name': name,
                'description': 'Folder for ' + name,
                'order': [],
                'collection_name': this.collectionJson.name,
                'collection_id': this.collectionId,
                'collection': this.collectionId
            };
            this.logger('Created folder ' + newFolder.name);
            return newFolder;
        },

        handleInfo: function (json) {
            this.collectionJson.name = json.info.title;
            this.collectionJson.description = json.info.description;
        },

        getParamsForPathItem: function (oldParams, newParams) {
            var retVal = {},
                numOldParams,
                numNewParams,
                i,
                parts,
                lastPart,
                getBaseParam;

            oldParams = oldParams || [];
            newParams = newParams || [];

            numOldParams = oldParams.length;
            numNewParams = newParams.length;

            for (i = 0; i < numOldParams; i++) {
                if (oldParams[i].$ref) {
                    // this is a ref
                    if (oldParams[i].$ref.indexOf('#/parameters') === 0) {
                        parts = oldParams[i].$ref.split('/');
                        lastPart = parts[parts.length - 1];
                        getBaseParam = this.baseParams[lastPart];
                        retVal[lastPart] = getBaseParam;
                    }
                }
                else {
                    retVal[oldParams[i].name] = oldParams[i];
                }
            }

            for (i = 0; i < numNewParams; i++) {
                if (newParams[i].$ref) {
                    // this is a ref
                    if (newParams[i].$ref.indexOf('#/parameters') === 0) {
                        parts = newParams[i].$ref.split('/');
                        lastPart = parts[parts.length - 1];
                        getBaseParam = this.baseParams[lastPart];
                        retVal[lastPart] = getBaseParam;
                    }
                }
                else {
                    retVal[newParams[i].name] = newParams[i];
                }
            }

            return retVal;
        },

        addOperationToFolder: function (path, method, operation, folderName, params) {
            var root = this,
                request = {
                    'id': uuid.v4(),
                    'headers': '',
                    'url': '',
                    'pathVariables': {},
                    'preRequestScript': '',
                    'method': 'GET',
                    'data': [],
                    'dataMode': 'params',
                    'description': operation.description || '',
                    'descriptionFormat': 'html',
                    'time': '',
                    'version': 2,
                    'responses': [],
                    'tests': '',
                    'collectionId': root.collectionId,
                    'synced': false
                },
                thisParams = this.getParamsForPathItem(params, operation.parameters),
                hasQueryParams = false,
                param,
                requestAttr,
                defaultVal,
                thisConsumes = root.globalConsumes,
                thisProduces = root.globalProduces,
                tempBasePath;

            if (path.length > 0 && path[0] === '/') {
                path = path.substring(1);
            }

            // Problem here
            // url.resolve("http://host.com/", "/api") returns "http://host.com/api"
            // but url.resolve("http://{{host}}.com/", "/api") returns "http:///%7B..host.com/api"
            // (note the extra slash after http:)
            // request.url = decodeURI(url.resolve(this.basePath, path));
            tempBasePath = this.basePath
                .replace(/{{/g, 'POSTMAN_VARIABLE_OPEN_DB')
                .replace(/}}/g, 'POSTMAN_VARIABLE_CLOSE_DB');

            request.url = decodeURI(url.resolve(tempBasePath, path))
                .replace(/POSTMAN_VARIABLE_OPEN_DB/gi, '{{')
                .replace(/POSTMAN_VARIABLE_CLOSE_DB/gi, '}}');

            request.method = method;
            request.name = operation.summary;
            request.time = (new Date()).getTime();

            // Handle custom swagger attributes for postman aws integration
            if (operation[META_KEY]) {
                for (requestAttr in operation[META_KEY]) {
                    if (operation[META_KEY].hasOwnProperty(requestAttr)) {
                        request[requestAttr] = operation[META_KEY][requestAttr];
                    }
                }
            }

            if (operation.produces) {
                thisProduces = operation.produces;
            }

            if (operation.consumes) {
                thisConsumes = operation.consumes;
            }

            // set the default dataMode for this request, even if it doesn't have a body
            // eg. for GET requests
            if (thisConsumes.indexOf('application/x-www-form-urlencoded') > -1) {
                request.dataMode = 'urlencoded';
            }

            if (thisProduces.length > 0) {
                request.headers += 'Accept: ' + thisProduces.join(', ') + '\n';
            }
            if (thisConsumes.length > 0) {
                request.headers += 'Content-Type: ' + thisConsumes[0] + '\n';
            }

            // set data and headers
            for (param in thisParams) {
                if (thisParams.hasOwnProperty(param) && thisParams[param]) {
                    this.logger('Processing param: ' + JSON.stringify(param));

                    // Get default value for .in = query/header/path/formData
                    defaultVal = '{{' + thisParams[param].name + '}}';
                    if (thisParams[param].hasOwnProperty('default')) {
                        defaultVal = thisParams[param].default;
                    }

                    if (thisParams[param].in === 'query' && this.options.includeQueryParams !== false) {
                        if (!hasQueryParams) {
                            hasQueryParams = true;
                            request.url += '?';
                        }
                        request.url += thisParams[param].name + '=' + defaultVal + '&';
                    }

                    else if (thisParams[param].in === 'header') {
                        request.headers += thisParams[param].name + ': ' + defaultVal + '\n';
                    }

                    else if (thisParams[param].in === 'body') {
                        request.dataMode = 'raw';
                        request.data = thisParams[param].description;
                    }

                    else if (thisParams[param].in === 'formData') {
                        if (thisConsumes.indexOf('application/x-www-form-urlencoded') > -1) {
                            request.dataMode = 'urlencoded';
                        }
                        else {
                            request.dataMode = 'params';
                        }
                        request.data.push({
                            'key': thisParams[param].name,
                            'value': defaultVal,
                            'type': 'text',
                            'enabled': true
                        });
                    }
                    else if (thisParams[param].in === 'path') {
                        if (!request.hasOwnProperty('pathVariables')) {
                            request.pathVariables = {};
                        }
                        request.pathVariables[thisParams[param].name] = defaultVal;
                    }
                }
            }

            if (hasQueryParams && this.endsWith(request.url, '&')) {
                request.url = request.url.slice(0, -1);
            }

            this.collectionJson.requests.push(request);
            if (folderName !== null) {
                this.folders[folderName].order.push(request.id);
            }
            else {
                this.collectionJson.order.push(request.id);
            }
        },

        addPathItemToFolder: function (path, pathItem, folderName) {
            if (pathItem.$ref) {
                this.logger('Error - cannot handle $ref attributes');
                return;
            }

            var paramsForPathItem = this.getParamsForPathItem(this.baseParams, pathItem.parameters),
                acceptedPostmanVerbs = [
                    'get', 'put', 'post', 'patch', 'delete', 'copy', 'head', 'options',
                    'link', 'unlink', 'purge', 'lock', 'unlock', 'propfind', 'view'],
                numVerbs = acceptedPostmanVerbs.length,
                i,
                verb;

            // replace path variables {petId} with :petId
            if (path) {
                path = path.replace(/{/g, ':').replace(/}/g, '');
            }

            for (i = 0; i < numVerbs; i++) {
                verb = acceptedPostmanVerbs[i];
                if (pathItem[verb]) {
                    this.addOperationToFolder(
                        path,
                        verb.toUpperCase(),
                        pathItem[verb],
                        folderName,
                        paramsForPathItem
                    );
                }
            }
        },

        handlePaths: function (json) {
            var paths = json.paths,
                path,
                folderName;

            // Add a folder for each path
            for (path in paths) {
                if (paths.hasOwnProperty(path)) {
                    folderName = this.getFolderNameForPath(path);
                    this.logger('Adding path item. path = ' + path + '   folder = ' + folderName);
                    this.addPathItemToFolder(path, paths[path], folderName);
                }
            }
        },

        handleParams: function (params, level) {
            if (!params) {
                return;
            }
            if (level === 'collection') {
                // base params
                for (var param in params) {
                    if (params.hasOwnProperty(param)) {
                        this.logger('Adding collection param: ' + param);
                        this.baseParams[param] = params[param];
                    }
                }
            }
        },

        addFoldersToCollection: function () {
            var folderName;
            for (folderName in this.folders) {
                if (this.folders.hasOwnProperty(folderName)) {
                    this.collectionJson.folders.push(this.folders[folderName]);
                }
            }
        },

        convert: function (json) {
            var validationResult = this.validate(json);
            if (validationResult.status === 'failed') {
                // error
                return validationResult;
            }

            this.collectionId = uuid.v4();

            this.globalConsumes = json.consumes || [];

            this.globalProduces = json.produces || [];

            this.handleParams(json.parameters, 'collection');

            this.handleInfo(json);

            this.setBasePath(json);

            this.handlePaths(json);

            this.addFoldersToCollection();

            this.collectionJson.id = this.collectionId;
            // this.logger(JSON.stringify(this.collectionJson));
            this.logger('Swagger converted successfully');

            validationResult.collection = this.collectionJson;

            return validationResult;
        },

        // since travis doesnt support es6
        endsWith: function (str, suffix) {
            return str.indexOf(suffix, str.length - suffix.length) !== -1;
        }
    });

module.exports = Swagger2Postman;
<commit_msg>Setting rawModeData if type is raw<commit_after>var uuid = require('node-uuid'),
    jsface = require('jsface'),
    url = require('url'),
    META_KEY = 'x-postman-meta',

    ConvertResult = function (status, message) {
        this.status = status;
        this.message = message;
    },

    Swagger2Postman = jsface.Class({
        constructor: function (options) {
            this.collectionJson = {
                'id': '',
                'name': '',
                'description': '',
                'order': [],
                'folders': [],
                'timestamp': 1413302258635,
                'synced': false,
                'requests': []
            };
            this.basePath = '';
            this.collectionId = '';
            this.folders = {};
            this.baseParams = {};
            this.logger = function () {
            };

            this.options = options || {};

            this.options.includeQueryParams = typeof (this.options.includeQueryParams) == 'undefined' ?
                true : this.options.includeQueryParams;
        },

        setLogger: function (func) {
            this.logger = func;
        },

        validate: function (json) {
            if (!json.hasOwnProperty('swagger') || json.swagger !== '2.0') {
                return new ConvertResult('failed', 'Must contain a swagger field (2.0)');
            }

            if (!json.hasOwnProperty('info')) {
                return new ConvertResult('failed', 'Must contain an info object');
            }
            else {
                var info = json.info;
                if (!info || !info.title) {
                    return new ConvertResult('failed', 'Must contain info.title');
                }
            }

            return new ConvertResult('passed', '');
        },

        setBasePath: function (json) {
            this.basePath = '';
            if (json.host) {
                this.basePath = json.host;
            }
            if (json.basePath) {
                this.basePath += json.basePath;
            }

            if (json.schemes && json.schemes.indexOf('https') != -1) {
                this.basePath = 'https://' + this.basePath;
            }
            else {
                this.basePath = 'http://' + this.basePath;
            }

            if (!this.endsWith(this.basePath, '/')) {
                this.basePath += '/';
            }
        },

        getFolderNameForPath: function (pathUrl) {
            if (pathUrl == '/') {
                return null;
            }
            var segments = pathUrl.split('/'),
                numSegments = segments.length,
                folderName = null;
            this.logger('Getting folder name for path: ' + pathUrl);
            this.logger('Segments: ' + JSON.stringify(segments));
            if (numSegments > 1) {
                folderName = segments[1];

                // create a folder for this path url
                if (!this.folders[folderName]) {
                    this.folders[folderName] = this.createNewFolder(folderName);
                }
                this.logger('For path ' + pathUrl + ', returning folderName ' + this.folders[folderName].name);
                return this.folders[folderName].name;
            }
            else {
                this.logger('Error - path MUST begin with /');
                return null;
            }
        },

        createNewFolder: function (name) {
            var newFolder = {
                'id': uuid.v4(),
                'name': name,
                'description': 'Folder for ' + name,
                'order': [],
                'collection_name': this.collectionJson.name,
                'collection_id': this.collectionId,
                'collection': this.collectionId
            };
            this.logger('Created folder ' + newFolder.name);
            return newFolder;
        },

        handleInfo: function (json) {
            this.collectionJson.name = json.info.title;
            this.collectionJson.description = json.info.description;
        },

        getParamsForPathItem: function (oldParams, newParams) {
            var retVal = {},
                numOldParams,
                numNewParams,
                i,
                parts,
                lastPart,
                getBaseParam;

            oldParams = oldParams || [];
            newParams = newParams || [];

            numOldParams = oldParams.length;
            numNewParams = newParams.length;

            for (i = 0; i < numOldParams; i++) {
                if (oldParams[i].$ref) {
                    // this is a ref
                    if (oldParams[i].$ref.indexOf('#/parameters') === 0) {
                        parts = oldParams[i].$ref.split('/');
                        lastPart = parts[parts.length - 1];
                        getBaseParam = this.baseParams[lastPart];
                        retVal[lastPart] = getBaseParam;
                    }
                }
                else {
                    retVal[oldParams[i].name] = oldParams[i];
                }
            }

            for (i = 0; i < numNewParams; i++) {
                if (newParams[i].$ref) {
                    // this is a ref
                    if (newParams[i].$ref.indexOf('#/parameters') === 0) {
                        parts = newParams[i].$ref.split('/');
                        lastPart = parts[parts.length - 1];
                        getBaseParam = this.baseParams[lastPart];
                        retVal[lastPart] = getBaseParam;
                    }
                }
                else {
                    retVal[newParams[i].name] = newParams[i];
                }
            }

            return retVal;
        },

        addOperationToFolder: function (path, method, operation, folderName, params) {
            var root = this,
                request = {
                    'id': uuid.v4(),
                    'headers': '',
                    'url': '',
                    'pathVariables': {},
                    'preRequestScript': '',
                    'method': 'GET',
                    'data': [],
                    'rawModeData': null,
                    'dataMode': 'params',
                    'description': operation.description || '',
                    'descriptionFormat': 'html',
                    'time': '',
                    'version': 2,
                    'responses': [],
                    'tests': '',
                    'collectionId': root.collectionId,
                    'synced': false
                },
                thisParams = this.getParamsForPathItem(params, operation.parameters),
                hasQueryParams = false,
                param,
                requestAttr,
                defaultVal,
                thisConsumes = root.globalConsumes,
                thisProduces = root.globalProduces,
                tempBasePath;

            if (path.length > 0 && path[0] === '/') {
                path = path.substring(1);
            }

            // Problem here
            // url.resolve("http://host.com/", "/api") returns "http://host.com/api"
            // but url.resolve("http://{{host}}.com/", "/api") returns "http:///%7B..host.com/api"
            // (note the extra slash after http:)
            // request.url = decodeURI(url.resolve(this.basePath, path));
            tempBasePath = this.basePath
                .replace(/{{/g, 'POSTMAN_VARIABLE_OPEN_DB')
                .replace(/}}/g, 'POSTMAN_VARIABLE_CLOSE_DB');

            request.url = decodeURI(url.resolve(tempBasePath, path))
                .replace(/POSTMAN_VARIABLE_OPEN_DB/gi, '{{')
                .replace(/POSTMAN_VARIABLE_CLOSE_DB/gi, '}}');

            request.method = method;
            request.name = operation.summary;
            request.time = (new Date()).getTime();

            // Handle custom swagger attributes for postman aws integration
            if (operation[META_KEY]) {
                for (requestAttr in operation[META_KEY]) {
                    if (operation[META_KEY].hasOwnProperty(requestAttr)) {
                        request[requestAttr] = operation[META_KEY][requestAttr];
                    }
                }
            }

            if (operation.produces) {
                thisProduces = operation.produces;
            }

            if (operation.consumes) {
                thisConsumes = operation.consumes;
            }

            // set the default dataMode for this request, even if it doesn't have a body
            // eg. for GET requests
            if (thisConsumes.indexOf('application/x-www-form-urlencoded') > -1) {
                request.dataMode = 'urlencoded';
            }

            if (thisProduces.length > 0) {
                request.headers += 'Accept: ' + thisProduces.join(', ') + '\n';
            }
            if (thisConsumes.length > 0) {
                request.headers += 'Content-Type: ' + thisConsumes[0] + '\n';
            }

            // set data and headers
            for (param in thisParams) {
                if (thisParams.hasOwnProperty(param) && thisParams[param]) {
                    this.logger('Processing param: ' + JSON.stringify(param));

                    // Get default value for .in = query/header/path/formData
                    defaultVal = '{{' + thisParams[param].name + '}}';
                    if (thisParams[param].hasOwnProperty('default')) {
                        defaultVal = thisParams[param].default;
                    }

                    if (thisParams[param].in === 'query' && this.options.includeQueryParams !== false) {
                        if (!hasQueryParams) {
                            hasQueryParams = true;
                            request.url += '?';
                        }
                        request.url += thisParams[param].name + '=' + defaultVal + '&';
                    }

                    else if (thisParams[param].in === 'header') {
                        request.headers += thisParams[param].name + ': ' + defaultVal + '\n';
                    }

                    else if (thisParams[param].in === 'body') {
                        request.dataMode = 'raw';
                        request.rawModeData = thisParams[param].description;
                    }

                    else if (thisParams[param].in === 'formData') {
                        if (thisConsumes.indexOf('application/x-www-form-urlencoded') > -1) {
                            request.dataMode = 'urlencoded';
                        }
                        else {
                            request.dataMode = 'params';
                        }
                        request.data.push({
                            'key': thisParams[param].name,
                            'value': defaultVal,
                            'type': 'text',
                            'enabled': true
                        });
                    }
                    else if (thisParams[param].in === 'path') {
                        if (!request.hasOwnProperty('pathVariables')) {
                            request.pathVariables = {};
                        }
                        request.pathVariables[thisParams[param].name] = defaultVal;
                    }
                }
            }

            if (hasQueryParams && this.endsWith(request.url, '&')) {
                request.url = request.url.slice(0, -1);
            }

            this.collectionJson.requests.push(request);
            if (folderName !== null) {
                this.folders[folderName].order.push(request.id);
            }
            else {
                this.collectionJson.order.push(request.id);
            }
        },

        addPathItemToFolder: function (path, pathItem, folderName) {
            if (pathItem.$ref) {
                this.logger('Error - cannot handle $ref attributes');
                return;
            }

            var paramsForPathItem = this.getParamsForPathItem(this.baseParams, pathItem.parameters),
                acceptedPostmanVerbs = [
                    'get', 'put', 'post', 'patch', 'delete', 'copy', 'head', 'options',
                    'link', 'unlink', 'purge', 'lock', 'unlock', 'propfind', 'view'],
                numVerbs = acceptedPostmanVerbs.length,
                i,
                verb;

            // replace path variables {petId} with :petId
            if (path) {
                path = path.replace(/{/g, ':').replace(/}/g, '');
            }

            for (i = 0; i < numVerbs; i++) {
                verb = acceptedPostmanVerbs[i];
                if (pathItem[verb]) {
                    this.addOperationToFolder(
                        path,
                        verb.toUpperCase(),
                        pathItem[verb],
                        folderName,
                        paramsForPathItem
                    );
                }
            }
        },

        handlePaths: function (json) {
            var paths = json.paths,
                path,
                folderName;

            // Add a folder for each path
            for (path in paths) {
                if (paths.hasOwnProperty(path)) {
                    folderName = this.getFolderNameForPath(path);
                    this.logger('Adding path item. path = ' + path + '   folder = ' + folderName);
                    this.addPathItemToFolder(path, paths[path], folderName);
                }
            }
        },

        handleParams: function (params, level) {
            if (!params) {
                return;
            }
            if (level === 'collection') {
                // base params
                for (var param in params) {
                    if (params.hasOwnProperty(param)) {
                        this.logger('Adding collection param: ' + param);
                        this.baseParams[param] = params[param];
                    }
                }
            }
        },

        addFoldersToCollection: function () {
            var folderName;
            for (folderName in this.folders) {
                if (this.folders.hasOwnProperty(folderName)) {
                    this.collectionJson.folders.push(this.folders[folderName]);
                }
            }
        },

        convert: function (json) {
            var validationResult = this.validate(json);
            if (validationResult.status === 'failed') {
                // error
                return validationResult;
            }

            this.collectionId = uuid.v4();

            this.globalConsumes = json.consumes || [];

            this.globalProduces = json.produces || [];

            this.handleParams(json.parameters, 'collection');

            this.handleInfo(json);

            this.setBasePath(json);

            this.handlePaths(json);

            this.addFoldersToCollection();

            this.collectionJson.id = this.collectionId;
            // this.logger(JSON.stringify(this.collectionJson));
            this.logger('Swagger converted successfully');

            validationResult.collection = this.collectionJson;

            return validationResult;
        },

        // since travis doesnt support es6
        endsWith: function (str, suffix) {
            return str.indexOf(suffix, str.length - suffix.length) !== -1;
        }
    });

module.exports = Swagger2Postman;
<|endoftext|>


<commit_before>//
// Tests configuration files.
//
var fs = require('fs')
var path = require('path')
var should = require('should')
var chai = require('chai')
var expect = require('chai').expect
var http = require('http')
var chaiHttp = require('chai-http')

//
// Configuring Chai to use http.
//
chai.use(chaiHttp)

describe('Application routes.', function () {
  var application = chai.request('http://localhost:9000')

  it('GET [/status] should return 200 status.', function (done) {
    application
      .get('/status')
      .end(function (err, res) {
        expect(res.status).to.equal(200)
        done()
      })
  })

  it('GET [/status] to have complete status object.', function (done) {
    application
      .get('/status')
      .end(function (err, res) {
        expect(res.body).to.have.a.property('online')
        expect(res.body).to.have.a.property('message')
        expect(res.body).to.have.a.property('version')
        expect(res.body).to.have.a.property('repository')
        done()
      })
  })

  it('GET [/] should return 200 status.', function (done) {
    application
      .get('/')
      .end(function (err, res) {
        expect(res.status).to.equal(200)
        done()
      })
  })

  it('GET [/] should return complete object.', function (done) {
    application
      .get('/')
      .end(function (err, res) {
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('count')
        expect(res.body).to.have.a.property('records')
        done()
      })
  })

  it('GET [/] should return a records array.', function (done) {
    application
      .get('/')
      .end(function (err, res) {
        expect(typeof res.body.records).to.equal(typeof [])
        done()
      })
  })

  it('GET [/] should return false if id is sent with request.', function (done) {
    application
      .get('/')
      .send({ id: 'foo' })
      .end(function (err, res) {
        expect(res.body.success).to.equal(false)
        done()
      })
  })

  it('GET [/] should return a complete false object when queried with an id.', function (done) {
    application
      .get('/')
      .send({ id: 'foo' })
      .end(function (err, res) {
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('message')
        done()
      })
  })

  it('POST [/] with a complete object should return success.', function (done) {
    application
      .post('/')
      .send({
        id: 'foo',
        status: 'error',
        message: 'This failed because of love.',
        time: '2015-01-01T14:30:00'
      })
      .end(function (err, res) {
        expect(res.body.success).to.equal(true)
        done()
      })
  })

  it('POST [/] with a complete object should return a complete message.', function (done) {
    application
      .post('/')
      .send({
        id: 'foo',
        status: 'error',
        message: 'This failed because of love.',
        time: '2015-01-01T14:30:00'
      })
      .end(function (err, res) {
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('message')
        expect(res.body).to.have.a.property('record')
        expect(res.body.record).to.have.a.property('_id')
        expect(res.body.record).to.have.a.property('__v')
        expect(res.body.record).to.have.a.property('time')
        expect(res.body.record).to.have.a.property('status')
        expect(res.body.record).to.have.a.property('message')
        expect(res.body.record).to.have.a.property('id')
        done()
      })
  })

  it('DELETE [/] with a complete object should return success', function (done) {
    application
      .delete('/')
      .send({
        id: 'foo'
      })
      .end(function (err, res) {
        expect(res.body.success).to.equal(true)
        done()
      })
  })

  it('DELETE [/] with a complete object should return a complete message', function (done) {
    application
      .delete('/')
      .send({
        id: 'foo'
      })
      .end(function (err, res) {
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('message')
        expect(res.body).to.have.a.property('count')
        expect(res.body).to.have.a.property('log')
        done()
      })
  })

  it('GET [/overview] should return success.', function (done) {
    application
      .get('/overview')
      .end(function (err, res) {
        expect(res.body.success).to.equal(true)
        done()
      })
  })

  it('GET [/series] without an id should return fail.', function (done) {
    application
      .get('/series')
      .end(function (err, res) {
        expect(res.body.success).to.equal(false)
        done()
      })
  })

  it('GET [/series] with an id should return success.', function (done) {
    application
      .get('/series')
      .send({
        id: 'foo'
      })
      .end(function (err, res) {
        expect(res.body.success).to.equal(true)
        done()
      })
  })

  it('GET [/404] should return a 404 error.', function (done) {
    application
      .get('/404')
      .end(function (err, res) {
        expect(res.status).to.equal(404)
        done()
      })
  })

  it('GET [/404] should return a page not found message.', function (done) {
    application
      .get('/404')
      .end(function (err, res) {
        expect(res.body.success).to.equal(false)
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('message')
        done()
      })
  })

})
<commit_msg>improving routes test<commit_after>//
// Tests configuration files.
//
var fs = require('fs')
var path = require('path')
var should = require('should')
var chai = require('chai')
var expect = require('chai').expect
var http = require('http')
var chaiHttp = require('chai-http')

//
// Configuring Chai to use http.
//
chai.use(chaiHttp)

describe('Application routes.', function () {
  var application = chai.request('http://localhost:9000')

  it('GET [/status] should return 200 status.', function (done) {
    application
      .get('/status')
      .end(function (err, res) {
        expect(res.status).to.equal(200)
        done()
      })
  })

  it('GET [/status] to have complete status object.', function (done) {
    application
      .get('/status')
      .end(function (err, res) {
        expect(res.body).to.have.a.property('online')
        expect(res.body).to.have.a.property('message')
        expect(res.body).to.have.a.property('version')
        expect(res.body).to.have.a.property('repository')
        done()
      })
  })

  it('GET [/] should return 200 status.', function (done) {
    application
      .get('/')
      .end(function (err, res) {
        expect(res.status).to.equal(200)
        done()
      })
  })

  it('GET [/] should return complete object.', function (done) {
    application
      .get('/')
      .end(function (err, res) {
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('count')
        expect(res.body).to.have.a.property('records')
        done()
      })
  })

  it('GET [/] should return a records array.', function (done) {
    application
      .get('/')
      .end(function (err, res) {
        expect(typeof res.body.records).to.equal(typeof [])
        done()
      })
  })

  it('GET [/] should return false if id is sent with request.', function (done) {
    application
      .get('/')
      .send({ id: 'foo' })
      .end(function (err, res) {
        expect(res.body.success).to.equal(false)
        done()
      })
  })

  it('GET [/] should return a complete false object when queried with an id.', function (done) {
    application
      .get('/')
      .send({ id: 'foo' })
      .end(function (err, res) {
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('message')
        done()
      })
  })

  it('POST [/] with a complete object should return success.', function (done) {
    application
      .post('/')
      .send({
        id: 'foo',
        status: 'error',
        message: 'This failed because of love.',
        time: '2015-01-01T14:30:00'
      })
      .end(function (err, res) {
        expect(res.body.success).to.equal(true)
        done()
      })
  })

  it('POST [/] with a complete object should return a complete message.', function (done) {
    application
      .post('/')
      .send({
        id: 'foo',
        status: 'error',
        message: 'This failed because of love.',
        time: '2015-01-01T14:30:00'
      })
      .end(function (err, res) {
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('message')
        expect(res.body).to.have.a.property('record')
        expect(res.body.record).to.have.a.property('_id')
        expect(res.body.record).to.have.a.property('__v')
        expect(res.body.record).to.have.a.property('time')
        expect(res.body.record).to.have.a.property('status')
        expect(res.body.record).to.have.a.property('message')
        expect(res.body.record).to.have.a.property('id')
        done()
      })
  })

  it('DELETE [/] with a complete object should return success', function (done) {
    application
      .delete('/')
      .send({
        id: 'foo'
      })
      .end(function (err, res) {
        expect(res.body.success).to.equal(true)
        done()
      })
  })

  it('DELETE [/] with a complete object should return a complete message', function (done) {
    application
      .delete('/')
      .send({
        id: 'collector-ny-01'
      })
      .end(function (err, res) {
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('message')
        expect(res.body).to.have.a.property('count')
        expect(res.body).to.have.a.property('log')
        done()
      })
  })

  it('GET [/overview] should return success.', function (done) {
    application
      .get('/overview')
      .end(function (err, res) {
        expect(res.body.success).to.equal(true)
        done()
      })
  })

  it('GET [/series] without an id should return fail.', function (done) {
    application
      .get('/series')
      .end(function (err, res) {
        expect(res.body.success).to.equal(false)
        done()
      })
  })

  it('GET [/series] with an id should return success.', function (done) {
    application
      .get('/series')
      .send({
        id: 'foo'
      })
      .end(function (err, res) {
        expect(res.body.success).to.equal(true)
        done()
      })
  })

  it('GET [/404] should return a 404 error.', function (done) {
    application
      .get('/404')
      .end(function (err, res) {
        expect(res.status).to.equal(404)
        done()
      })
  })

  it('GET [/404] should return a page not found message.', function (done) {
    application
      .get('/404')
      .end(function (err, res) {
        expect(res.body.success).to.equal(false)
        expect(res.body).to.have.a.property('success')
        expect(res.body).to.have.a.property('message')
        done()
      })
  })

})
<|endoftext|>