<commit_before>/*!
 * Vue Shopify Products
 * ====================
 *
 * Format shopify CSV files into a usable object.
 * https://github.com/mikestreety/vue-shopify-products
 *
 * Copyright 2017 Mike Street
 */

const ShopifyProducts = {
	install(Vue) {
		let generateSlug = function(path) {
			return path
				.toLowerCase()
				.replace(/^\/|\/$/g, '')
				.replace(/ /g, '-')
				.replace(/\//g, '-')
				.replace(/[-]+/g, '-')
				.replace(/[^\w-]+/g, '');
		};

		Vue.prototype.$formatProducts = function(payload) {
			// Create an empty products array
			let products = [];

			// Was it generated using CSV parser?
			// https://github.com/okfn/csv.js
			if (payload.fields) {
				let headers = payload.fields;

				for (let product of payload.records) {
					let output = {};

					headers.forEach((header, index) => {
						output[generateSlug(header)] = product[index];
					});

					products.push(output);
				}
			} else {
				// Or was it d3.js?
				for (let product of payload) {
					let output = {};

					Object.keys(product).forEach(key => {
						output[generateSlug(key)] = product[key];
					});

					products.push(output);
				}
			}

			// Create output, ready to group variations
			let output = {};

			// Loop through each one
			for (let product of products) {
				let handle = product.handle,
					item = output[handle];

				// If it's the first one with this handle, create an entry
				if (!item) {
					item = {
						title: product.title,
						body: product['body-html'],
						handle: handle,
						vendor: {
							title: product.vendor,
							handle: generateSlug(product.vendor)
						},
						type: {
							title: product.type,
							handle: generateSlug(product.type)
						},
						tags: product.tags,
						images: [],
						variationTypes: {},
						variationProducts: []
					};
				}

				// Add any images from itself or variations
				if (product['image-src']) {
					item.images.push({
						source: product['image-src'],
						alt: product['image-alt-text']
					});
				}

				// Create any variations as keys for later accessing, e.g.
				// id: 1, value: Color, slug: color
				for (let i = 1; i < 4; i++) {
					if (product.hasOwnProperty('option' + i + '-name') && product['option' + i + '-name'] !== null) {
						item.variationTypes[i] = {
							id: i,
							value: product['option' + i + '-name'],
							slug: generateSlug(product['option' + i + '-name'])
						};
					}
				}

				// Add all variations with the same handle it finds
				// Even the first product itself would be a varation
				let variation = {
					barcode: product['variant-barcode'],
					comaprePrice: product['variant-compare-at-price'],
					grams: product['variant-grams'],
					quantity: product['variant-inventory-qty'],
					price: product['variant-price'],
					shipping: product['variant-requires-shipping'],
					sku: product['variant-sku'],
					taxable: product['variant-taxable'],
					variant: {}
				};

				if (product['variant-image']) {
					variation.image = {
						source: product['variant-image'],
						alt: product.title
					};
				}

				// Create a key value on the variation of name/value e.g color: blue
				for (let v in item.variationTypes) {
					v = item.variationTypes[v];
					variation.variant[generateSlug(v.value)] = {
						name: v.value,
						value: product['option' + v.id + '-value']
					};
				}

				// Remove any null values from the variatiomn
				Object.keys(variation).forEach(key => variation[key] == null && delete variation[key]);

				item.variationProducts.push(variation);

				// Update the output object
				output[handle] = item;
			}

			return output;
		};
	}
};
<commit_msg>Feat: Explode tags into handles and titles<commit_after>/*!
 * Vue Shopify Products
 * ====================
 *
 * Format shopify CSV files into a usable object.
 * https://github.com/mikestreety/vue-shopify-products
 *
 * Copyright 2017 Mike Street
 */

const ShopifyProducts = {
	install(Vue) {
		let generateSlug = function(path) {
			return path
				.trim()
				.toLowerCase()
				.replace(/^\/|\/$/g, '')
				.replace(/ /g, '-')
				.replace(/\//g, '-')
				.replace(/[-]+/g, '-')
				.replace(/[^\w-]+/g, '');
		};

		Vue.prototype.$formatProducts = function(payload) {
			// Create an empty products array
			let products = [];

			// Was it generated using CSV parser?
			// https://github.com/okfn/csv.js
			if (payload.fields) {
				let headers = payload.fields;

				for (let product of payload.records) {
					let output = {};

					headers.forEach((header, index) => {
						output[generateSlug(header)] = product[index];
					});

					products.push(output);
				}
			} else {
				// Or was it d3.js?
				for (let product of payload) {
					let output = {};

					Object.keys(product).forEach(key => {
						output[generateSlug(key)] = product[key];
					});

					products.push(output);
				}
			}

			// Create output, ready to group variations
			let output = {};

			// Loop through each one
			for (let product of products) {
				let handle = product.handle,
					item = output[handle];

				// If it's the first one with this handle, create an entry
				if (!item) {
					item = {
						title: product.title,
						body: product['body-html'],
						handle: handle,
						vendor: {
							title: product.vendor,
							handle: generateSlug(product.vendor)
						},
						type: {
							title: product.type,
							handle: generateSlug(product.type)
						},
						tags: product.tags.split(',').map(tag => {
							return {
								title: tag.trim(),
								handle: generateSlug(tag)
							};
						}),
						images: [],
						variationTypes: {},
						variationProducts: []
					};
				}

				// Add any images from itself or variations
				if (product['image-src']) {
					item.images.push({
						source: product['image-src'],
						alt: product['image-alt-text']
					});
				}

				// Create any variations as keys for later accessing, e.g.
				// id: 1, value: Color, slug: color
				for (let i = 1; i < 4; i++) {
					if (product.hasOwnProperty('option' + i + '-name') && product['option' + i + '-name'] !== null) {
						item.variationTypes[i] = {
							id: i,
							value: product['option' + i + '-name'],
							slug: generateSlug(product['option' + i + '-name'])
						};
					}
				}

				// Add all variations with the same handle it finds
				// Even the first product itself would be a varation
				let variation = {
					barcode: product['variant-barcode'],
					comaprePrice: product['variant-compare-at-price'],
					grams: product['variant-grams'],
					quantity: product['variant-inventory-qty'],
					price: product['variant-price'],
					shipping: product['variant-requires-shipping'],
					sku: product['variant-sku'],
					taxable: product['variant-taxable'],
					variant: {}
				};

				if (product['variant-image']) {
					variation.image = {
						source: product['variant-image'],
						alt: product.title
					};
				}

				// Create a key value on the variation of name/value e.g color: blue
				for (let v in item.variationTypes) {
					v = item.variationTypes[v];
					variation.variant[generateSlug(v.value)] = {
						name: v.value,
						value: product['option' + v.id + '-value']
					};
				}

				// Remove any null values from the variatiomn
				Object.keys(variation).forEach(key => variation[key] == null && delete variation[key]);

				item.variationProducts.push(variation);

				// Update the output object
				output[handle] = item;
			}

			return output;
		};
	}
};
<|endoftext|>


<commit_before>const nest = require('depnest')
const { assign } = Object

exports.gives = nest('styles.css')

exports.create = function (api) {
  return nest('styles.css', (sofar = {}) => {
    return assign(sofar, { globalStyles })
  })
}

const globalStyles = `
body {
  font-family: sans-serif;
  color: #222;
}

h1, h2, h3, h4, h5, h6, p, ul, ol {
  margin-top: .35em;
}

h1 { font-size: 1.2em; }
h2 { font-size: 1.18em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.12em; }
h5 { font-size: 1.1em; }
h6 { font-size: 1em; }

* {
  word-break: break-word;
}

a:link, a:visited, a:active {
  color: #0088cc;
  text-decoration: none;
}

a:hover,
a:focus {
  color: #005580;
  text-decoration: underline;
}

pre {
  white-space: pre-wrap;
  word-wrap: break-word;
}

p {
  margin-top: .35ex;
}

hr {
  border: solid #eee;
  clear: both;
  border-width: 1px 0 0;
  height: 0;
  margin-bottom: .9em;
}

input, textarea {
  border: none;
  border-radius: .2em;
  font-family: sans-serif;
}

input:focus, .compose:focus, button:focus {
  outline: none;
  border-color: #0088cc;
  box-shadow: 0 0 4px #0088cc;
}

textarea {
  padding: .5em;
  font-size: 1em;
}

textarea:focus {
  outline: none;
  border-color: none;
}

button {
  background: #fff;
  color: #666;
  border: 1px solid #bbb;
  border-radius: .5em;
  padding: .7em;
  margin: .5em;
  cursor: pointer;
  text-transform: uppercase;
  font-weight: bold;
  font-size: .7em;
}

button:hover {
  background: #ccc;
  border: 1px solid #bbb;
}

/* TextNodeSearcher highlights */

highlight {
  background: #ff8;
}

/* lightbox - used in message-confirm */

div.lightbox {
  position: fixed;
  left: 0px;
  right: 0px;
  top: 50px;
  overflow: auto;
  width: 600px;
  max-width: 100%;
  padding: 25px;
  margin: auto;
  z-index: 2;
  background: #f5f5f5;
  border: 1px solid #eee;
  border-radius: .2em;
}
`

<commit_msg>Fix the size of emojis<commit_after>const nest = require('depnest')
const { assign } = Object

exports.gives = nest('styles.css')

exports.create = function (api) {
  return nest('styles.css', (sofar = {}) => {
    return assign(sofar, { globalStyles })
  })
}

const globalStyles = `
body {
  font-family: sans-serif;
  color: #222;
}

.emoji {
  width: 16px;
  height: 16px;
}

h1, h2, h3, h4, h5, h6, p, ul, ol {
  margin-top: .35em;
}

h1 { font-size: 1.2em; }
h2 { font-size: 1.18em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.12em; }
h5 { font-size: 1.1em; }
h6 { font-size: 1em; }

* {
  word-break: break-word;
}

a:link, a:visited, a:active {
  color: #0088cc;
  text-decoration: none;
}

a:hover,
a:focus {
  color: #005580;
  text-decoration: underline;
}

pre {
  white-space: pre-wrap;
  word-wrap: break-word;
}

p {
  margin-top: .35ex;
}

hr {
  border: solid #eee;
  clear: both;
  border-width: 1px 0 0;
  height: 0;
  margin-bottom: .9em;
}

input, textarea {
  border: none;
  border-radius: .2em;
  font-family: sans-serif;
}

input:focus, .compose:focus, button:focus {
  outline: none;
  border-color: #0088cc;
  box-shadow: 0 0 4px #0088cc;
}

textarea {
  padding: .5em;
  font-size: 1em;
}

textarea:focus {
  outline: none;
  border-color: none;
}

button {
  background: #fff;
  color: #666;
  border: 1px solid #bbb;
  border-radius: .5em;
  padding: .7em;
  margin: .5em;
  cursor: pointer;
  text-transform: uppercase;
  font-weight: bold;
  font-size: .7em;
}

button:hover {
  background: #ccc;
  border: 1px solid #bbb;
}

/* TextNodeSearcher highlights */

highlight {
  background: #ff8;
}

/* lightbox - used in message-confirm */

div.lightbox {
  position: fixed;
  left: 0px;
  right: 0px;
  top: 50px;
  overflow: auto;
  width: 600px;
  max-width: 100%;
  padding: 25px;
  margin: auto;
  z-index: 2;
  background: #f5f5f5;
  border: 1px solid #eee;
  border-radius: .2em;
}
`

<|endoftext|>


<commit_before>var _ = require('underscore');
var cdb = require('cartodb.js');

/**
 * Default widget model
 *
 * Note: Currently all widgets have a dependency on a dataview, why it makes sense to have it here.
 * If you need a widget model that's backed up by a dataview model please implement your own model and adhere to the
 * public interface instead of extending/hacking this one.
 */
module.exports = cdb.core.Model.extend({
  defaults: {
    attrsNames: [],
    show_stats: false
  },

  defaultState: {
    'collapsed': false,
    'pinned': false
  },

  initialize: function (attrs, opts) {
    this.dataviewModel = opts.dataviewModel;
  },

  /**
   * @public
   * @param {Object} attrs, not that it should be
   * @return {Boolean} true if at least one attribute was changed
   * @throws {Error} Should throw an error if the attrs are invalid or inconsistent
   */
  update: function (attrs) {
    var wAttrs = _.pick(attrs, this.get('attrsNames'));
    this.set(wAttrs);
    this.dataviewModel.update(attrs);
    return !!(this.changedAttributes() || this.dataviewModel.changedAttributes());
  },

  /**
   * @public
   */
  remove: function () {
    this.dataviewModel.remove();
    this.trigger('destroy', this);
    this.stopListening();
  },

  setState: function (state) {
    this.set(state);
  },

  getState: function () {
    var state = {};
    for (var key in this.defaultState) {
      var attribute = this.get(key);
      var defaultValue = this.defaultState[key];
      if (typeof defaultValue === 'function') {
        defaultValue = defaultValue.call(this);
      }
      if (typeof attribute !== 'undefined' && !_.isEqual(attribute, defaultValue)) {
        state[key] = attribute;
      }
    }
    return state;
  }
});
<commit_msg>rounds default values to do a proper match<commit_after>var _ = require('underscore');
var cdb = require('cartodb.js');

/**
 * Default widget model
 *
 * Note: Currently all widgets have a dependency on a dataview, why it makes sense to have it here.
 * If you need a widget model that's backed up by a dataview model please implement your own model and adhere to the
 * public interface instead of extending/hacking this one.
 */
module.exports = cdb.core.Model.extend({
  defaults: {
    attrsNames: [],
    show_stats: false
  },

  defaultState: {
    'collapsed': false,
    'pinned': false
  },

  initialize: function (attrs, opts) {
    this.dataviewModel = opts.dataviewModel;
  },

  /**
   * @public
   * @param {Object} attrs, not that it should be
   * @return {Boolean} true if at least one attribute was changed
   * @throws {Error} Should throw an error if the attrs are invalid or inconsistent
   */
  update: function (attrs) {
    var wAttrs = _.pick(attrs, this.get('attrsNames'));
    this.set(wAttrs);
    this.dataviewModel.update(attrs);
    return !!(this.changedAttributes() || this.dataviewModel.changedAttributes());
  },

  /**
   * @public
   */
  remove: function () {
    this.dataviewModel.remove();
    this.trigger('destroy', this);
    this.stopListening();
  },

  setState: function (state) {
    this.set(state);
  },

  getState: function () {
    var state = {};
    for (var key in this.defaultState) {
      var attribute = this.get(key);
      if (_.isNumber(attribute)) {
        attribute = Math.round(attribute);
      }
      var defaultValue = this.defaultState[key];
      if (typeof defaultValue === 'function') {
        defaultValue = defaultValue.call(this);
      }
      if (_.isNumber(defaultValue)) {
        defaultValue = Math.round(defaultValue);
      }
      if (typeof defaultValue !== 'undefined' && typeof attribute !== 'undefined' && !_.isEqual(attribute, defaultValue)) {
        state[key] = attribute;
      }
    }
    return state;
  }
});
<|endoftext|>


<commit_before>const ipc = require('electron').ipcRenderer
const {dialog} = require('electron').remote

const mobbersEl = document.getElementById('mobbers')
const minutesEl = document.getElementById('minutes')
const addEl = document.getElementById('add')
const addMobberForm = document.getElementById('addMobberForm')
const fullscreenSecondsEl = document.getElementById('fullscreen-seconds')
const snapToEdgesCheckbox = document.getElementById('snap-to-edges')
const alertAudioCheckbox = document.getElementById('alertAudio')
const replayAudioContainer = document.getElementById('replayAudioContainer')
const replayAlertAudioCheckbox = document.getElementById('replayAlertAudio')
const replayAudioAfterSeconds = document.getElementById('replayAudioAfterSeconds')
const useCustomSoundCheckbox = document.getElementById('useCustomSound')
const customSoundEl = document.getElementById('customSound')
const timerAlwaysOnTopCheckbox = document.getElementById('timerAlwaysOnTop')

function createMobberEl(mobber) {
  const el = document.createElement('div')
  el.classList.add('mobber')
  if (mobber.disabled) {
    el.classList.add('disabled')
  }

  const imgEl = document.createElement('img')
  imgEl.src = mobber.image || '../img/sad-cyclops.png'
  imgEl.classList.add('image')
  el.appendChild(imgEl)

  const nameEl = document.createElement('div')
  nameEl.innerHTML = mobber.name
  nameEl.classList.add('name')
  el.appendChild(nameEl)

  const disableBtn = document.createElement('button')
  disableBtn.classList.add('btn')
  disableBtn.innerHTML = mobber.disabled ? 'Enable' : 'Disable'
  el.appendChild(disableBtn)

  const rmBtn = document.createElement('button')
  rmBtn.classList.add('btn')
  rmBtn.innerHTML = 'Remove'
  el.appendChild(rmBtn)

  imgEl.addEventListener('click', _ => selectImage(mobber))
  disableBtn.addEventListener('click', _ => toggleMobberDisabled(mobber))
  rmBtn.addEventListener('click', _ => ipc.send('removeMobber', mobber))

  return el
}

function selectImage(mobber) {
  var image = dialog.showOpenDialog({
    title: 'Select image',
    filters: [
      {name: 'Images', extensions: ['jpg', 'png', 'gif']}
    ],
    properties: ['openFile']
  })

  if (image) {
    mobber.image = image[0]
    ipc.send('updateMobber', mobber)
  }
}

function toggleMobberDisabled(mobber) {
  mobber.disabled = !mobber.disabled
  ipc.send('updateMobber', mobber)
}

ipc.on('configUpdated', (event, data) => {
  minutesEl.value = Math.ceil(data.secondsPerTurn / 60)
  mobbersEl.innerHTML = ''
  const frag = document.createDocumentFragment()
  data.mobbers.map(mobber => {
    frag.appendChild(createMobberEl(mobber))
  })
  mobbersEl.appendChild(frag)
  fullscreenSecondsEl.value = data.secondsUntilFullscreen
  snapToEdgesCheckbox.checked = data.snapThreshold > 0

  alertAudioCheckbox.checked = data.alertSoundTimes.length > 0
  replayAlertAudioCheckbox.checked = data.alertSoundTimes.length > 1
  replayAudioAfterSeconds.value = data.alertSoundTimes.length > 1 ? data.alertSoundTimes[1] : 30
  updateAlertControls()

  useCustomSoundCheckbox.checked = !!data.alertSound
  customSoundEl.value = data.alertSound

  timerAlwaysOnTopCheckbox.checked = data.timerAlwaysOnTop
})

minutesEl.addEventListener('change', _ => {
  ipc.send('setSecondsPerTurn', minutesEl.value * 60)
})

addMobberForm.addEventListener('submit', event => {
  event.preventDefault()
  let value = addEl.value.trim()
  if (!value) {
    return
  }
  ipc.send('addMobber', { name: value })
  addEl.value = ''
})

shuffle.addEventListener("click", event => {
  event.preventDefault();
  ipc.send('shuffleMobbers');
});

fullscreenSecondsEl.addEventListener('change', _ => {
  ipc.send('setSecondsUntilFullscreen', fullscreenSecondsEl.value * 1)
})

ipc.send('configWindowReady')

snapToEdgesCheckbox.addEventListener('change', _ => {
  ipc.send('setSnapThreshold', snapToEdgesCheckbox.checked ? 25 : 0)
})

alertAudioCheckbox.addEventListener('change', _ => updateAlertTimes())
replayAlertAudioCheckbox.addEventListener('change', _ => updateAlertTimes())
replayAudioAfterSeconds.addEventListener('change', _ => updateAlertTimes())

function updateAlertTimes() {
  updateAlertControls()

  let alertSeconds = []
  if (alertAudioCheckbox.checked) {
    alertSeconds.push(0)
    if (replayAlertAudioCheckbox.checked) {
      alertSeconds.push(replayAudioAfterSeconds.value * 1)
    }
  }

  ipc.send('setAlertSoundTimes', alertSeconds)
}

function updateAlertControls() {
  let replayDisabled = !alertAudioCheckbox.checked
  replayAlertAudioCheckbox.disabled = replayDisabled

  if (replayDisabled) {
    replayAlertAudioCheckbox.checked = false
    replayAudioContainer.classList.add('disabled')
  } else {
    replayAudioContainer.classList.remove('disabled')
  }

  let secondsDisabled = !replayAlertAudioCheckbox.checked
  replayAudioAfterSeconds.disabled = secondsDisabled
}

useCustomSoundCheckbox.addEventListener('change', _ => {
  let mp3 = null

  if (useCustomSoundCheckbox.checked) {
    const selectedMp3 = dialog.showOpenDialog({
      title: 'Select alert sound',
      filters: [
        {name: 'MP3', extensions: ['mp3']}
      ],
      properties: ['openFile']
    })

    if (selectedMp3) {
      mp3 = selectedMp3[0]
    } else {
      useCustomSoundCheckbox.checked = false
    }
  }

  ipc.send('setAlertSound', mp3)
})

timerAlwaysOnTopCheckbox.addEventListener('change', _ => {
  ipc.send('setTimerAlwaysOnTop', timerAlwaysOnTopCheckbox.checked)
})
<commit_msg>Use document.getElementById and avoid global<commit_after>const ipc = require('electron').ipcRenderer
const {dialog} = require('electron').remote

const mobbersEl = document.getElementById('mobbers')
const shuffleEl = document.getElementById('shuffle')
const minutesEl = document.getElementById('minutes')
const addEl = document.getElementById('add')
const addMobberForm = document.getElementById('addMobberForm')
const fullscreenSecondsEl = document.getElementById('fullscreen-seconds')
const snapToEdgesCheckbox = document.getElementById('snap-to-edges')
const alertAudioCheckbox = document.getElementById('alertAudio')
const replayAudioContainer = document.getElementById('replayAudioContainer')
const replayAlertAudioCheckbox = document.getElementById('replayAlertAudio')
const replayAudioAfterSeconds = document.getElementById('replayAudioAfterSeconds')
const useCustomSoundCheckbox = document.getElementById('useCustomSound')
const customSoundEl = document.getElementById('customSound')
const timerAlwaysOnTopCheckbox = document.getElementById('timerAlwaysOnTop')

function createMobberEl(mobber) {
  const el = document.createElement('div')
  el.classList.add('mobber')
  if (mobber.disabled) {
    el.classList.add('disabled')
  }

  const imgEl = document.createElement('img')
  imgEl.src = mobber.image || '../img/sad-cyclops.png'
  imgEl.classList.add('image')
  el.appendChild(imgEl)

  const nameEl = document.createElement('div')
  nameEl.innerHTML = mobber.name
  nameEl.classList.add('name')
  el.appendChild(nameEl)

  const disableBtn = document.createElement('button')
  disableBtn.classList.add('btn')
  disableBtn.innerHTML = mobber.disabled ? 'Enable' : 'Disable'
  el.appendChild(disableBtn)

  const rmBtn = document.createElement('button')
  rmBtn.classList.add('btn')
  rmBtn.innerHTML = 'Remove'
  el.appendChild(rmBtn)

  imgEl.addEventListener('click', _ => selectImage(mobber))
  disableBtn.addEventListener('click', _ => toggleMobberDisabled(mobber))
  rmBtn.addEventListener('click', _ => ipc.send('removeMobber', mobber))

  return el
}

function selectImage(mobber) {
  var image = dialog.showOpenDialog({
    title: 'Select image',
    filters: [
      {name: 'Images', extensions: ['jpg', 'png', 'gif']}
    ],
    properties: ['openFile']
  })

  if (image) {
    mobber.image = image[0]
    ipc.send('updateMobber', mobber)
  }
}

function toggleMobberDisabled(mobber) {
  mobber.disabled = !mobber.disabled
  ipc.send('updateMobber', mobber)
}

ipc.on('configUpdated', (event, data) => {
  minutesEl.value = Math.ceil(data.secondsPerTurn / 60)
  mobbersEl.innerHTML = ''
  const frag = document.createDocumentFragment()
  data.mobbers.map(mobber => {
    frag.appendChild(createMobberEl(mobber))
  })
  mobbersEl.appendChild(frag)
  fullscreenSecondsEl.value = data.secondsUntilFullscreen
  snapToEdgesCheckbox.checked = data.snapThreshold > 0

  alertAudioCheckbox.checked = data.alertSoundTimes.length > 0
  replayAlertAudioCheckbox.checked = data.alertSoundTimes.length > 1
  replayAudioAfterSeconds.value = data.alertSoundTimes.length > 1 ? data.alertSoundTimes[1] : 30
  updateAlertControls()

  useCustomSoundCheckbox.checked = !!data.alertSound
  customSoundEl.value = data.alertSound

  timerAlwaysOnTopCheckbox.checked = data.timerAlwaysOnTop
})

minutesEl.addEventListener('change', _ => {
  ipc.send('setSecondsPerTurn', minutesEl.value * 60)
})

addMobberForm.addEventListener('submit', event => {
  event.preventDefault()
  let value = addEl.value.trim()
  if (!value) {
    return
  }
  ipc.send('addMobber', { name: value })
  addEl.value = ''
})

shuffleEl.addEventListener("click", event => {
  event.preventDefault();
  ipc.send('shuffleMobbers');
});

fullscreenSecondsEl.addEventListener('change', _ => {
  ipc.send('setSecondsUntilFullscreen', fullscreenSecondsEl.value * 1)
})

ipc.send('configWindowReady')

snapToEdgesCheckbox.addEventListener('change', _ => {
  ipc.send('setSnapThreshold', snapToEdgesCheckbox.checked ? 25 : 0)
})

alertAudioCheckbox.addEventListener('change', _ => updateAlertTimes())
replayAlertAudioCheckbox.addEventListener('change', _ => updateAlertTimes())
replayAudioAfterSeconds.addEventListener('change', _ => updateAlertTimes())

function updateAlertTimes() {
  updateAlertControls()

  let alertSeconds = []
  if (alertAudioCheckbox.checked) {
    alertSeconds.push(0)
    if (replayAlertAudioCheckbox.checked) {
      alertSeconds.push(replayAudioAfterSeconds.value * 1)
    }
  }

  ipc.send('setAlertSoundTimes', alertSeconds)
}

function updateAlertControls() {
  let replayDisabled = !alertAudioCheckbox.checked
  replayAlertAudioCheckbox.disabled = replayDisabled

  if (replayDisabled) {
    replayAlertAudioCheckbox.checked = false
    replayAudioContainer.classList.add('disabled')
  } else {
    replayAudioContainer.classList.remove('disabled')
  }

  let secondsDisabled = !replayAlertAudioCheckbox.checked
  replayAudioAfterSeconds.disabled = secondsDisabled
}

useCustomSoundCheckbox.addEventListener('change', _ => {
  let mp3 = null

  if (useCustomSoundCheckbox.checked) {
    const selectedMp3 = dialog.showOpenDialog({
      title: 'Select alert sound',
      filters: [
        {name: 'MP3', extensions: ['mp3']}
      ],
      properties: ['openFile']
    })

    if (selectedMp3) {
      mp3 = selectedMp3[0]
    } else {
      useCustomSoundCheckbox.checked = false
    }
  }

  ipc.send('setAlertSound', mp3)
})

timerAlwaysOnTopCheckbox.addEventListener('change', _ => {
  ipc.send('setTimerAlwaysOnTop', timerAlwaysOnTopCheckbox.checked)
})
<|endoftext|>


<commit_before>'use strict'

let fs = require('fs')
let path = require('path')
let exec = require('child_process').exec
let domain = require('domain')
let logger = require('tracer').colorConsole()
let Pipeline = require('../../core/pipelines/pipeline')
let Stage = require('../../core/pipelines/stage')
let extensionRegistry = require('../../extensions/registry')
let TokenResolver = require('../../core/pipelines/token-resolver')

module.exports = class Job {

  /**
   * @param {object} msg Data passed from the queue
   * @param {function} next Should be called on complete (to clear the message from the queue)
   */
  constructor(msg, next) {

    // Set up the stage numbering
    this.currentStageNumber = 0

    // Assume no stage has failed until we find out otherwise
    this.anyStageHasFailed = false

    // Load the pipeline and start the execution
    this.pipeline = new Pipeline(msg.id)
    this.pipeline.load().then(() => {
      this.pipeline.running().then(() => {
        this.prepareInputAndVariables().then(() => {
          this.run().then(() => {
            next()
          }).catch(err => logger(err))
        })
      }).catch(err => logger.error(err))
    })
    .catch(err => logger.error(err))
  }

  /**
   * Prepare initial values of variables
   */
  prepareInputAndVariables() {
    console.log('job:prepareInputAndVariables')
    return new Promise((resolve, reject) => {

      console.log('job:inputvariables:starting to resolve')
      console.log(this)

      // Validate the variables config snapshot value
      if (!Array.isArray(this.pipeline.config.variables)) {
        this.pipeline.config.variables = []
      }

      // Convert the variable objects into a list of expected input/variable keys
      let expectedKeys = this.pipeline.config.variables.map(variable => variable.name)

      // INPUT VALIDATION
      // NOTE: I'm ok failing validation here. For example, if GitHub were to trigger a job without all of the required
      // input, I would rather that the webhook succeed and we can fail the job here before it begins with a note saying
      // it was missing a required input or provided input we didn't expect.

      // Run through the user/trigger provided input (and make sure there is not unexpected input)
      for (let key in this.pipeline.input) {

        // If we can't find the input key in the expected input/variables...
        if (expectedKeys.indexOf(key) === -1) {

          // Note the event in the server logs
          logger.warn('Input key "' + key + '" was provided but was not expected')

          // Note the event in the pipeline execution logs
          this.pipeline.log('Input key "' + key + '" was provided but was not expected')

          // Fail the pipeline
          this.pipeline.fail()
          reject()
          return
        }
      }

      // Build the initial variable values by combining default values and input provided
      // (also check if a required input is missing)
      let initialVariableValues = {}

      this.tokenResolver = new TokenResolver()
      this.tokenResolver.setKey('workspace_path', this.workspacePath)
      this.tokenResolver.setKey('webhook', this.pipeline.webhook_data)

      this.pipeline.config.variables.forEach(variable => {

        let key = variable.name

        // If the variable is a "required" "input"", make sure it exists (and capture it)
        if (variable.required) {
          if (typeof this.pipeline.input[key] === 'undefined') {

            // Note the event in the server logs
            logger.warn('Missing required input "' + key + '" for pipeline')

            // Note the event in the pipeline execution logs
            this.pipeline.log('Missing required input "' + key + '" for pipeline')

            // Fail the pipeline
            this.pipeline.fail()
            reject()
            return
          }
          initialVariableValues[key] = this.pipeline.input[key]

        } else {
          // Otherwise, if it is optional...

          // Check if an input was provided. If so, use that, otherwise, use the default value
          if (typeof this.pipeline.input[key] !== 'undefined') {
            initialVariableValues[key] = this.tokenResolver.process(this.pipeline.input[key])
          } else {
            initialVariableValues[key] = this.tokenResolver.process(variable.default_value)
          }

        }
      })

      logger.debug('initial variable values')
      logger.debug(initialVariableValues)

      this.tokenResolver.setKey('var', initialVariableValues)

      console.log('resolved variables')

      console.log('job:inputvariables:about to resolve')
      resolve()

    })

  }

  setUpWorkspaceDirectory() {

    logger.debug('setUpWorkspaceDirectory() promise registered')

    return new Promise((resolve, reject) => {

      logger.debug('setUpWorkspaceDirectory() promise running')

      let baseDir = process.env.WORKSPACES_DIR || './storage/workspaces/'
      let fullQualifiedBaseDir = path.resolve(baseDir)
      let workspaceDir = '/pipeline_execution_' + this.pipeline.id
      this.workspacePath = fullQualifiedBaseDir + workspaceDir

      fs.mkdir(this.workspacePath, (err) => {
        if (err) {
          logger.error('workspace directory could not be created')
          logger.error(err)
          reject()
        } else {
          resolve()
        }
      })

    })

  }

  tearDownWorkspaceDirectory() {
    return new Promise((resolve) => {

      logger.debug('tearing down workspace dir')

      let command = 'rm -rf ' + this.workspacePath

      exec(command, (err) => {

        if (err) {
          logger.error(err)
        }

        let title = (err) ? 'Workspace directory could not be deleted' : 'Workspace directory deleted'
        let snippet = 'Path:\n' + this.workspacePath
        this.pipeline.log('mc.basics.logs.snippet', title, [snippet])

        resolve()
      })
    })
  }

  /**
   * Run the pipeline execution
   */
  run() {

    logger.debug('run() promise registered')

    return new Promise(resolve => {

      logger.debug('run() promise executing...')

      this.setUpWorkspaceDirectory().then(() => {

        // log error to pipeline execution logs
        let message = 'Path:\n' + this.workspacePath
        this.pipeline.log('mc.basics.logs.snippet', 'Workspace directory created', [message])

        // Clone the stage configurations to execute
        this.stagesRemaining = this.pipeline.config.stageConfigs.slice(0)

        let onComplete = () => {
          this.tearDownWorkspaceDirectory().then(() => {
            resolve()
          })
        }

        this.executeNextStage(() => {
          if (this.anyStageHasFailed) {
            this.pipeline.fail().then(onComplete)
          } else {
            this.pipeline.succeed().then(onComplete)
          }
        })

      }).catch(() => {

        // If we could not create the directory...

        // log error to pipeline execution logs
        let message = 'Path:\n' + this.workspacePath
        this.pipeline.log('mc.basics.logs.snippet', 'Workspace directory could not created', [message])

        // Mark the pipeline as failed and resolve
        this.pipeline.fail().then(() => {
          resolve()
        })
      })

    })

  }

  executeNextStage(callback) {

    logger.debug('Job:executeNextStage()')

    // If we've completed all the stages, call the callback
    if (this.stagesRemaining.length === 0) {
      callback()
      return
    }

    // Pull off a stage configuration off of the stages remaining
    let stageConfig = this.stagesRemaining.shift()

    // parse the output map for the stage congig
    stageConfig.output_map = JSON.parse(stageConfig.output_map)

    logger.debug('stageConfig')
    logger.debug(stageConfig)

    // Bump the current stage number
    this.currentStageNumber++

    logger.debug('this.currentStageNumber', this.currentStageNumber)

    // Create a stage object for use in the extension execute method
    let stage = new Stage(this.currentStageNumber, stageConfig, this.pipeline, this.tokenResolver, () => {

      stage.on('failed', () => {
        this.anyStageHasFailed = true
        this.executeNextStage(callback)
      })

      stage.on('succeeded', () => {

        // Get the output from the stage
        let output = stage.getOutput()

        logger.debug('output from stage #' + this.currentStageNumber + ':')
        logger.debug(output)

        //logger.debug('stage config output map:')
        //logger.debug(stageConfig.output_map)

        // If output has been mapped for this stage configuration
        if (typeof stageConfig.output_map === 'object') {

          // Loop through the output provided by the extension stage type
          for (let key in output) {

            // If the output is mapped, then update the value in the token resolver
            if (typeof stageConfig.output_map[key] === 'string' && stageConfig.output_map[key].trim() !== '') {
              logger.debug('found a mapped output. setting user variable "' + stageConfig.output_map[key] + '" to "' + output[key] + '"')
              this.tokenResolver.setUserVarValue(stageConfig.output_map[key], output[key])
            }

          }

        }

        this.executeNextStage(callback)
      })

      stage.on('skipped', () => {
        this.executeNextStage(callback)
      })

      if (this.anyStageHasFailed) {
        stage.skip()
        this.executeNextStage(callback)
        return
      }

      // Create a domain in which to execute the stage
      let d = domain.create()

      // Set up an event handler for if the stage fails
      d.on('error', (err) => {
        logger.error(err)

        let logTitle = 'Extension Error: An exception occurred executing stage #' + this.currentStageNumber
        let fullErrorMessage = 'Extension: ' + stageConfig.type + '\n\n' +
          'Error Message: ' + err.message + '\n\n' +
          'Stack Trace: ' + '\n\n' + err.stack

        this.pipeline.log('mc.basics.logs.snippet', logTitle, [fullErrorMessage])
        stage.fail(err)
        this.executeNextStage(callback)
      })

      d.run(() => {

        logger.debug('d.run() for stage #' + this.currentStageNumber)

        // Get the stage type from the extension for this stage
        let stageType = extensionRegistry.get(stageConfig.type)

        // Run the stage
        stageType.execute(stage)

      })

    })

  }

}
<commit_msg>Refactoring so variable is available<commit_after>'use strict'

let fs = require('fs')
let path = require('path')
let exec = require('child_process').exec
let domain = require('domain')
let logger = require('tracer').colorConsole()
let Pipeline = require('../../core/pipelines/pipeline')
let Stage = require('../../core/pipelines/stage')
let extensionRegistry = require('../../extensions/registry')
let TokenResolver = require('../../core/pipelines/token-resolver')

module.exports = class Job {

  /**
   * @param {object} msg Data passed from the queue
   * @param {function} next Should be called on complete (to clear the message from the queue)
   */
  constructor(msg, next) {

    // Set up the stage numbering
    this.currentStageNumber = 0

    // Assume no stage has failed until we find out otherwise
    this.anyStageHasFailed = false

    // Load the pipeline and start the execution
    this.pipeline = new Pipeline(msg.id)
    this.pipeline.load().then(() => {
      this.pipeline.running().then(() => {
        this.setUpWorkspaceDirectory().then(() => {
          this.prepareInputAndVariables().then(() => {
            this.run()
              .then(() => {
                next()
              })
              .catch(err => logger(err))
          }).catch(this.onWorkspaceDirectoryCreationFailure)
        })
      }).catch(err => logger.error(err))
    })
    .catch(err => logger.error(err))
  }

  /**
   * Prepare initial values of variables
   */
  prepareInputAndVariables() {
    console.log('job:prepareInputAndVariables')
    return new Promise((resolve, reject) => {

      console.log('job:inputvariables:starting to resolve')
      console.log(this)

      // Validate the variables config snapshot value
      if (!Array.isArray(this.pipeline.config.variables)) {
        this.pipeline.config.variables = []
      }

      // Convert the variable objects into a list of expected input/variable keys
      let expectedKeys = this.pipeline.config.variables.map(variable => variable.name)

      // INPUT VALIDATION
      // NOTE: I'm ok failing validation here. For example, if GitHub were to trigger a job without all of the required
      // input, I would rather that the webhook succeed and we can fail the job here before it begins with a note saying
      // it was missing a required input or provided input we didn't expect.

      // Run through the user/trigger provided input (and make sure there is not unexpected input)
      for (let key in this.pipeline.input) {

        // If we can't find the input key in the expected input/variables...
        if (expectedKeys.indexOf(key) === -1) {

          // Note the event in the server logs
          logger.warn('Input key "' + key + '" was provided but was not expected')

          // Note the event in the pipeline execution logs
          this.pipeline.log('Input key "' + key + '" was provided but was not expected')

          // Fail the pipeline
          this.pipeline.fail()
          reject()
          return
        }
      }

      // Build the initial variable values by combining default values and input provided
      // (also check if a required input is missing)
      let initialVariableValues = {}

      this.tokenResolver = new TokenResolver()
      this.tokenResolver.setKey('workspace_path', this.workspacePath)
      this.tokenResolver.setKey('webhook', this.pipeline.webhook_data)

      this.pipeline.config.variables.forEach(variable => {

        let key = variable.name

        // If the variable is a "required" "input"", make sure it exists (and capture it)
        if (variable.required) {
          if (typeof this.pipeline.input[key] === 'undefined') {

            // Note the event in the server logs
            logger.warn('Missing required input "' + key + '" for pipeline')

            // Note the event in the pipeline execution logs
            this.pipeline.log('Missing required input "' + key + '" for pipeline')

            // Fail the pipeline
            this.pipeline.fail()
            reject()
            return
          }
          initialVariableValues[key] = this.pipeline.input[key]

        } else {
          // Otherwise, if it is optional...

          // Check if an input was provided. If so, use that, otherwise, use the default value
          if (typeof this.pipeline.input[key] !== 'undefined') {
            initialVariableValues[key] = this.tokenResolver.process(this.pipeline.input[key])
          } else {
            initialVariableValues[key] = this.tokenResolver.process(variable.default_value)
          }

        }
      })

      logger.debug('initial variable values')
      logger.debug(initialVariableValues)

      this.tokenResolver.setKey('var', initialVariableValues)

      console.log('resolved variables')

      console.log('job:inputvariables:about to resolve')
      resolve()

    })

  }

  setUpWorkspaceDirectory() {

    logger.debug('setUpWorkspaceDirectory() promise registered')

    return new Promise((resolve, reject) => {

      logger.debug('setUpWorkspaceDirectory() promise running')

      let baseDir = process.env.WORKSPACES_DIR || './storage/workspaces/'
      let fullQualifiedBaseDir = path.resolve(baseDir)
      let workspaceDir = '/pipeline_execution_' + this.pipeline.id
      this.workspacePath = fullQualifiedBaseDir + workspaceDir

      fs.mkdir(this.workspacePath, (err) => {
        if (err) {
          logger.error('workspace directory could not be created')
          logger.error(err)
          reject()
        } else {
          resolve()
        }
      })

    })

  }

  onWorkspaceDirectoryCreationFailure() {

    // log error to pipeline execution logs
    let message = 'Path:\n' + this.workspacePath
    this.pipeline.log('mc.basics.logs.snippet', 'Workspace directory could not created', [message])

    // Mark the pipeline as failed and resolve
    this.pipeline.fail().then(() => {
      resolve()
    })

  }

  tearDownWorkspaceDirectory() {
    return new Promise((resolve) => {

      logger.debug('tearing down workspace dir')

      let command = 'rm -rf ' + this.workspacePath

      exec(command, (err) => {

        if (err) {
          logger.error(err)
        }

        let title = (err) ? 'Workspace directory could not be deleted' : 'Workspace directory deleted'
        let snippet = 'Path:\n' + this.workspacePath
        this.pipeline.log('mc.basics.logs.snippet', title, [snippet])

        resolve()
      })
    })
  }

  /**
   * Run the pipeline execution
   */
  run() {

    logger.debug('run() promise registered')

    return new Promise(resolve => {

      logger.debug('run() promise executing...')

        // log error to pipeline execution logs
        let message = 'Path:\n' + this.workspacePath
        this.pipeline.log('mc.basics.logs.snippet', 'Workspace directory created', [message])

        // Clone the stage configurations to execute
        this.stagesRemaining = this.pipeline.config.stageConfigs.slice(0)

        let onComplete = () => {
          this.tearDownWorkspaceDirectory().then(() => {
            resolve()
          })
        }

        this.executeNextStage(() => {
          if (this.anyStageHasFailed) {
            this.pipeline.fail().then(onComplete)
          } else {
            this.pipeline.succeed().then(onComplete)
          }
        })

    })

  }

  executeNextStage(callback) {

    logger.debug('Job:executeNextStage()')

    // If we've completed all the stages, call the callback
    if (this.stagesRemaining.length === 0) {
      callback()
      return
    }

    // Pull off a stage configuration off of the stages remaining
    let stageConfig = this.stagesRemaining.shift()

    // parse the output map for the stage congig
    stageConfig.output_map = JSON.parse(stageConfig.output_map)

    logger.debug('stageConfig')
    logger.debug(stageConfig)

    // Bump the current stage number
    this.currentStageNumber++

    logger.debug('this.currentStageNumber', this.currentStageNumber)

    // Create a stage object for use in the extension execute method
    let stage = new Stage(this.currentStageNumber, stageConfig, this.pipeline, this.tokenResolver, () => {

      stage.on('failed', () => {
        this.anyStageHasFailed = true
        this.executeNextStage(callback)
      })

      stage.on('succeeded', () => {

        // Get the output from the stage
        let output = stage.getOutput()

        logger.debug('output from stage #' + this.currentStageNumber + ':')
        logger.debug(output)

        //logger.debug('stage config output map:')
        //logger.debug(stageConfig.output_map)

        // If output has been mapped for this stage configuration
        if (typeof stageConfig.output_map === 'object') {

          // Loop through the output provided by the extension stage type
          for (let key in output) {

            // If the output is mapped, then update the value in the token resolver
            if (typeof stageConfig.output_map[key] === 'string' && stageConfig.output_map[key].trim() !== '') {
              logger.debug('found a mapped output. setting user variable "' + stageConfig.output_map[key] + '" to "' + output[key] + '"')
              this.tokenResolver.setUserVarValue(stageConfig.output_map[key], output[key])
            }

          }

        }

        this.executeNextStage(callback)
      })

      stage.on('skipped', () => {
        this.executeNextStage(callback)
      })

      if (this.anyStageHasFailed) {
        stage.skip()
        this.executeNextStage(callback)
        return
      }

      // Create a domain in which to execute the stage
      let d = domain.create()

      // Set up an event handler for if the stage fails
      d.on('error', (err) => {
        logger.error(err)

        let logTitle = 'Extension Error: An exception occurred executing stage #' + this.currentStageNumber
        let fullErrorMessage = 'Extension: ' + stageConfig.type + '\n\n' +
          'Error Message: ' + err.message + '\n\n' +
          'Stack Trace: ' + '\n\n' + err.stack

        this.pipeline.log('mc.basics.logs.snippet', logTitle, [fullErrorMessage])
        stage.fail(err)
        this.executeNextStage(callback)
      })

      d.run(() => {

        logger.debug('d.run() for stage #' + this.currentStageNumber)

        // Get the stage type from the extension for this stage
        let stageType = extensionRegistry.get(stageConfig.type)

        // Run the stage
        stageType.execute(stage)

      })

    })

  }

}
<|endoftext|>


<commit_before>export default [
    'abort',
    'animationEnd',
    'animationIteration',
    'animationStart',
    'blur',
    'canPlay',
    'canPlayThrough',
    'click',
    'contextMenu',
    'copy',
    'cut',
    'doubleClick',
    'drag',
    'dragEnd',
    'dragEnter',
    'dragExit',
    'dragLeave',
    'dragOver',
    'dragStart',
    'drop',
    'durationChange',
    'emptied',
    'encrypted',
    'ended',
    'error',
    'focus',
    'input',
    'invalid',
    'keyDown',
    'keyPress',
    'keyUp',
    'load',
    'loadedData',
    'loadedMetadata',
    'loadStart',
    'mouseDown',
    'mouseMove',
    'mouseOut',
    'mouseOver',
    'mouseUp',
    'paste',
    'pause',
    'play',
    'playing',
    'progress',
    'rateChange',
    'reset',
    'scroll',
    'seeked',
    'seeking',
    'stalled',
    'submit',
    'suspend',
    'timeUpdate',
    'touchCancel',
    'touchEnd',
    'touchMove',
    'touchStart',
    'transitionEnd',
    'volumeChange',
    'waiting',
    'wheel'
];
<commit_msg>reactEventNames: Add comment explaining how the list was obtained<commit_after>// Extracted from react-dom 3.9.3:
// Object.keys(require('react-dom/lib/SimpleEventPlugin').eventTypes)
export default [
    'abort',
    'animationEnd',
    'animationIteration',
    'animationStart',
    'blur',
    'canPlay',
    'canPlayThrough',
    'click',
    'contextMenu',
    'copy',
    'cut',
    'doubleClick',
    'drag',
    'dragEnd',
    'dragEnter',
    'dragExit',
    'dragLeave',
    'dragOver',
    'dragStart',
    'drop',
    'durationChange',
    'emptied',
    'encrypted',
    'ended',
    'error',
    'focus',
    'input',
    'invalid',
    'keyDown',
    'keyPress',
    'keyUp',
    'load',
    'loadedData',
    'loadedMetadata',
    'loadStart',
    'mouseDown',
    'mouseMove',
    'mouseOut',
    'mouseOver',
    'mouseUp',
    'paste',
    'pause',
    'play',
    'playing',
    'progress',
    'rateChange',
    'reset',
    'scroll',
    'seeked',
    'seeking',
    'stalled',
    'submit',
    'suspend',
    'timeUpdate',
    'touchCancel',
    'touchEnd',
    'touchMove',
    'touchStart',
    'transitionEnd',
    'volumeChange',
    'waiting',
    'wheel'
];
<|endoftext|>


<commit_before>const axios = require('axios')
const cheerio = require('cheerio')
const { getResultsFromSearch } = require('./info.js')

const BASE_URI = 'https://myanimelist.net/anime/'

const parsePage = ($) => {
  const recommendations = $('#content td:nth-child(2) .borderClass table')
  const results = []
  recommendations.each(function () {
    const recommendation = {}
    recommendation.pictureImage = $(this).find('tr td:nth-child(1) .picSurround img').attr('data-src')
    recommendation.animeLink = $(this).find('tr td:nth-child(1) .picSurround a').attr('href')
    recommendation.anime = $(this).find('tr td:nth-child(2) div:nth-child(2) a strong').text().trim()
    recommendation.mainRecommendation = $(this).find('tr td:nth-child(2) .detail-user-recs-text').text().trim()
    recommendation.author = $(this).find('tr td:nth-child(2) > .borderClass .spaceit_pad:nth-child(2) > a').text().trim()
    results.push(recommendation)
  })

  return results
}

const searchPage = (url) => {
  return new Promise((resolve, reject) => {
    axios.get(url)
      .then(({ data }) => {
        const $ = cheerio.load(data)
        const res = parsePage($)
        resolve(res)
      })
      .catch(/* istanbul ignore next */(err) => reject(err))
  })
}

const getRecommendationsFromName = (name) => {
  return new Promise((resolve, reject) => {
    getResultsFromSearch(name)
      .then((items) => {
        const { url } = items[0]

        searchPage(`${encodeURI(url)}/userrecs`)
          .then((data) => resolve(data))
          .catch(/* istanbul ignore next */(err) => reject(err))
      })
      .catch(/* istanbul ignore next */(err) => reject(err))
  })
}

const getRecommendationsFromNameAndId = (id, name) => {
  return new Promise((resolve, reject) => {
    searchPage(`${BASE_URI}${id}/${encodeURI(name)}/userrecs`)
      .then((data) => resolve(data))
      .catch(/* istanbul ignore next */(err) => reject(err))
  })
}

const getRecommendationsList = (obj) => {
  return new Promise((resolve, reject) => {
    if (!obj) {
      reject(new Error('[Mal-Scraper]: No id nor name received.'))
      return
    }

    if (typeof obj === 'object' && !obj[0]) {
      const { id, name } = obj

      if (!id || !name || isNaN(+id) || typeof name !== 'string') {
        reject(new Error('[Mal-Scraper]: Malformed input. ID or name is malformed or missing.'))
        return
      }

      getRecommendationsFromNameAndId(id, name)
        .then((data) => resolve(data))
        .catch(/* istanbul ignore next */(err) => reject(err))
    } else {
      getRecommendationsFromName(obj)
        .then((data) => resolve(data))
        .catch(/* istanbul ignore next */(err) => reject(err))
    }
  })
}

module.exports = {
  getRecommendationsList
}
<commit_msg>Fix #75<commit_after>const axios = require('axios')
const cheerio = require('cheerio')
const { getResultsFromSearch } = require('./info.js')

const BASE_URI = 'https://myanimelist.net/anime/'

const parsePage = ($) => {
  const recommendations = $('#content td:nth-child(2) .borderClass table')
  const results = []
  recommendations.each(function () {
    const recommendation = {}
    recommendation.pictureImage = $(this).find('tr td:nth-child(1) .picSurround img').attr('data-src')
    recommendation.animeLink = $(this).find('tr td:nth-child(1) .picSurround a').attr('href')
    recommendation.anime = $(this).find('tr td:nth-child(2) div:nth-child(2) a strong').text().trim()
    recommendation.mainRecommendation = $(this).find('tr td:nth-child(2) .detail-user-recs-text').text().trim()
    recommendation.author = $(this).find('tr td:nth-child(2) > .borderClass .spaceit_pad:nth-child(2) > a').text().trim()
    results.push(recommendation)
  })

  return results
}

const searchPage = (url) => {
  return new Promise((resolve, reject) => {
    axios.get(url)
      .then(({ data }) => {
        const $ = cheerio.load(data)
        const res = parsePage($)
        resolve(res)
      })
      .catch(/* istanbul ignore next */(err) => reject(err))
  })
}

const getRecommendationsFromName = (name) => {
  return new Promise((resolve, reject) => {
    getResultsFromSearch(name)
      .then((items) => {
        const { url } = items[0]

        searchPage(`${encodeURI(url)}/userrecs`)
          .then((data) => resolve(data))
          .catch(/* istanbul ignore next */(err) => reject(err))
      })
      .catch(/* istanbul ignore next */(err) => reject(err))
  })
}

const getRecommendationsFromNameAndId = (id, name = 'anything') => {
  return new Promise((resolve, reject) => {
    searchPage(`${BASE_URI}${id}/${encodeURI(name)}/userrecs`)
      .then((data) => resolve(data))
      .catch(/* istanbul ignore next */(err) => reject(err))
  })
}

const getRecommendationsList = (obj) => {
  return new Promise((resolve, reject) => {
    if (!obj) {
      reject(new Error('[Mal-Scraper]: No id nor name received.'))
      return
    }

    if (typeof obj === 'object' && !obj[0]) {
      const { id, name } = obj

      if (!id || isNaN(+id) || (name && typeof name !== 'string')) {
        reject(new Error('[Mal-Scraper]: Malformed input. ID or name is malformed or missing.'))
        return
      }

      getRecommendationsFromNameAndId(id, name)
        .then((data) => resolve(data))
        .catch(/* istanbul ignore next */(err) => reject(err))
    } else {
      getRecommendationsFromName(obj)
        .then((data) => resolve(data))
        .catch(/* istanbul ignore next */(err) => reject(err))
    }
  })
}

module.exports = {
  getRecommendationsList
}
<|endoftext|>


<commit_before>var fieldIsAnswered = function(response){
  return response.response != "";
}

Template.submit.helpers({
  prompts: function() {
    return Prompts.inOrder();
  },
  alreadySubmitted: function() {
    return Session.get('submitted');
  },
  error: function(){
    return Session.get("error");
  }
});

Template.submit.events({

  'submit form': function(e) {
    e.preventDefault();

    var responses = Prompts.allPromptIds().map(function(id) {
      return {
        promptId: id,
        response: $('#' + id).val()
      }
    });

    if (responses.some(fieldIsAnswered)){
        Submissions.insert({responses: responses});
        $('.response-input').val('');
        Session.set('submitted', true)
        Router.go('/thanks')
    } else {
        Session.set("error", "Please fill out at least one field");
    }
  },

  'keypress form': function(e) {
    if (e.keyCode != 13) { // Return if the key is not Enter
      return;
    }

    var activeInput = e.target;
    var focusIsNotOnSubmitButton = e.target.type != 'submit';

    var inputs = $(activeInput).closest('form').find(':input');
    var isLastInput = (inputs.index(activeInput) >= inputs.length - 2);

    if (focusIsNotOnSubmitButton && !isLastInput) {
      e.preventDefault();
      inputs.eq(inputs.index(activeInput) + 1).focus();
    }
  }
});

Template.submit.onRendered(function() {
  $('modal-trigger').leanModal();
  $('#submit-form').verify(); // Bind verify.js to the form
});

$.notify.addStyle('redalert', {
  html: "

",
  classes: {
    base: {
      "white-space": "nowrap",
      "color": "red",
      "padding": "0px",
      "position": "absolute",
      "right": "5px",
      "top": "0.8rem"
    }
  }
});

$.notify.defaults({
  style: 'redalert',
  elementPosition: 'right top',
  autoHide: false,
  arrowShow: false
});
<commit_msg>Jack <3 Chandni: allow for multi line response<commit_after>var fieldIsAnswered = function(response){
  return response.response != "";
}

Template.submit.helpers({
  prompts: function() {
    return Prompts.inOrder();
  },
  alreadySubmitted: function() {
    return Session.get('submitted');
  },
  error: function(){
    return Session.get("error");
  }
});

Template.submit.events({

  'submit form': function(e) {
    e.preventDefault();

    var responses = Prompts.allPromptIds().map(function(id) {
      return {
        promptId: id,
        response: $('#' + id).val()
      }
    });

    if (responses.some(fieldIsAnswered)){
        Submissions.insert({responses: responses});
        $('.response-input').val('');
        Session.set('submitted', true)
        Router.go('/thanks')
    } else {
        Session.set("error", "Please fill out at least one field");
    }
  },

  'keypress input': function(e) {
    if (e.keyCode != 13) { // Return if the key is not Enter
      return;
    }

    var activeInput = e.target;
    var focusIsNotOnSubmitButton = e.target.type != 'submit';

    var inputs = $(activeInput).closest('form').find(':input');
    var isLastInput = (inputs.index(activeInput) >= inputs.length - 2);

    if (focusIsNotOnSubmitButton && !isLastInput) {
      e.preventDefault();
      inputs.eq(inputs.index(activeInput) + 1).focus();
    }
  }
});

Template.submit.onRendered(function() {
  $('modal-trigger').leanModal();
  $('#submit-form').verify(); // Bind verify.js to the form
});

$.notify.addStyle('redalert', {
  html: "

",
  classes: {
    base: {
      "white-space": "nowrap",
      "color": "red",
      "padding": "0px",
      "position": "absolute",
      "right": "5px",
      "top": "0.8rem"
    }
  }
});

$.notify.defaults({
  style: 'redalert',
  elementPosition: 'right top',
  autoHide: false,
  arrowShow: false
});
<|endoftext|>


<commit_before>import { Map, List } from 'immutable'

/**
 * The start action for the update reducer
 * @param  {Immutable.Map} state - current reducer state
 * @param  {Object} action - action object
 *
 * @return {Immutable.Map} - updated state
 */
export const start = (state, action) => {

  const uid = action.record.get(action.uidField).toString()

  // Get previous record
  const previousRecord = state.getIn(['raw', uid])

  // Create updated record tuple
  // We have to create a tuple here in order to preserve the Integer typed keys
  // If there is no previous record we add a new record
  const updatedRecord = previousRecord ? previousRecord.merge(action.record) : action.record
  const updatedRecordTuple = Map([[uid, updatedRecord]])

  return state.withMutations(map => {
    if (action.isAsync) {
      // Add updated item to pendingRaw
      map.set('pendingRaw', state.get('pendingRaw').merge(updatedRecordTuple))

      // Add uid to pending.update
      const pendingUpdate = map.getIn(['pending', 'update'], List())
      map.setIn(['pending', 'update'], pendingUpdate.insert(0, uid))
    }
    else {
      // Optimistically update the record
      map.set('raw', state.get('raw').merge(updatedRecordTuple))
    }
  })
}

/**
 * The success action for the update reducer
 * @param  {Immutable.Map} state - current reducer state
 * @param  {Object} action - action object
 *
 * @return {Immutable.Map} - updated state
 */
 export const success = (state, action) => {

   const uid = action.record.get(action.uidField).toString()

   return state.withMutations(map => {
    // Get the pending record
    const updatedRecord = map.getIn(['pendingRaw', uid], Map())
    // Get the response data
    const responseData = action.responseData ? Map(action.responseData) : Map()

    // Merge the new data with the data from the store
    const record = map.getIn(['raw', uid], Map()).merge(updatedRecord).merge(responseData)

    // Update the record in raw
    const updatedRecordTuple = Map([[uid, record]])
    map.set('raw', state.get('raw').merge(updatedRecordTuple))

    // Remove the item from pendingRaw
    map.removeIn(['pendingRaw', uid])

    // Remove uid from pending.update
    const pendingUpdate = map.getIn(['pending', 'update'], List())
    const uidIndexInPendingUpdate = pendingUpdate.indexOf(uid)
    map.removeIn(['pending', 'update', uidIndexInPendingUpdate])
   })
 }

/**
 * The error action for the update reducer
 * Reverts store change on failure
 * @param  {Immutable.Map} state - current reducer state
 * @param  {Object} action - action object
 *
 * @return {Immutable.Map} - updated state
 */
export const error = (state, action) => {

  const uid = action.record.get(action.uidField).toString()

  return state.withMutations(map => {
    // Remove the item from pendingRaw
    map.removeIn(['pendingRaw', uid])

    // Remove uid from pending.update
    const pendingUpdate = map.getIn(['pending', 'update'], List())
    const uidIndexInPendingUpdate = pendingUpdate.indexOf(uid)
    map.removeIn(['pending', 'update', uidIndexInPendingUpdate])
  })
}

export default {
  start,
  success,
  error
}
<commit_msg>updated comment<commit_after>import { Map, List } from 'immutable'

/**
 * The start action for the update reducer
 * @param  {Immutable.Map} state - current reducer state
 * @param  {Object} action - action object
 *
 * @return {Immutable.Map} - updated state
 */
export const start = (state, action) => {

  const uid = action.record.get(action.uidField).toString()

  // Get previous record
  const previousRecord = state.getIn(['raw', uid])

  // Create updated record tuple
  // We have to create a tuple here in order to preserve the Integer typed keys
  // If there is no previous record we add a new record
  const updatedRecord = previousRecord ? previousRecord.merge(action.record) : action.record
  const updatedRecordTuple = Map([[uid, updatedRecord]])

  return state.withMutations(map => {
    if (action.isAsync) {
      // Add updated item to pendingRaw
      map.set('pendingRaw', state.get('pendingRaw').merge(updatedRecordTuple))

      // Add uid to pending.update
      const pendingUpdate = map.getIn(['pending', 'update'], List())
      map.setIn(['pending', 'update'], pendingUpdate.insert(0, uid))
    }
    else {
      // Optimistically update the record
      map.set('raw', state.get('raw').merge(updatedRecordTuple))
    }
  })
}

/**
 * The success action for the update reducer
 * @param  {Immutable.Map} state - current reducer state
 * @param  {Object} action - action object
 *
 * @return {Immutable.Map} - updated state
 */
 export const success = (state, action) => {

   const uid = action.record.get(action.uidField).toString()

   return state.withMutations(map => {
    // Get the pending record
    const updatedRecord = map.getIn(['pendingRaw', uid], Map())
    // Get the response data
    const responseData = action.responseData ? Map(action.responseData) : Map()

    // Success responseData should take precedence over all other changes so we
    // merge that into the updated record, then we merge the updatedRecord into existing raw data
    const record = map.getIn(['raw', uid], Map()).merge(updatedRecord).merge(responseData)

    // Update the record in raw
    const updatedRecordTuple = Map([[uid, record]])
    map.set('raw', state.get('raw').merge(updatedRecordTuple))

    // Remove the item from pendingRaw
    map.removeIn(['pendingRaw', uid])

    // Remove uid from pending.update
    const pendingUpdate = map.getIn(['pending', 'update'], List())
    const uidIndexInPendingUpdate = pendingUpdate.indexOf(uid)
    map.removeIn(['pending', 'update', uidIndexInPendingUpdate])
   })
 }

/**
 * The error action for the update reducer
 * Reverts store change on failure
 * @param  {Immutable.Map} state - current reducer state
 * @param  {Object} action - action object
 *
 * @return {Immutable.Map} - updated state
 */
export const error = (state, action) => {

  const uid = action.record.get(action.uidField).toString()

  return state.withMutations(map => {
    // Remove the item from pendingRaw
    map.removeIn(['pendingRaw', uid])

    // Remove uid from pending.update
    const pendingUpdate = map.getIn(['pending', 'update'], List())
    const uidIndexInPendingUpdate = pendingUpdate.indexOf(uid)
    map.removeIn(['pending', 'update', uidIndexInPendingUpdate])
  })
}

export default {
  start,
  success,
  error
}
<|endoftext|>


<commit_before>/**
 * @class Renderer
 */

EZ3.Renderer = function(canvas, options) {
  this.context = null;
  this.extension = null;
  this.canvas = canvas;
  this.options = options;
  this.state = null;
};

EZ3.Renderer.prototype._processContextLost = function(event) {
  event.preventDefault();
};

EZ3.Renderer.prototype._renderMesh = function(mesh, camera, lights) {
  var gl = this.context;
  var program = mesh.material.program;
  var modelView = new EZ3.Matrix4();
  var i;

  program.bind(gl);
  mesh.material.updateStates(gl, this.state);
  mesh.material.updateUniforms(gl, this.state);

  modelView.mul(camera.view, mesh.world);

  program.loadUniformFloat(gl, 'uEyePosition', camera.position);
  program.loadUniformMatrix(gl, 'uModel', mesh.world);
  program.loadUniformMatrix(gl, 'uModelView', modelView);
  program.loadUniformMatrix(gl, 'uProjection', camera.projection);

  if (this.state.hasLights) {
    mesh.updateNormal();
    mesh.updateNormalData();
    program.loadUniformMatrix(gl, 'uNormal', mesh.normal);
  }

  this.state.activeShadowReceiver = mesh.shadowReceiver;

  for (i = 0; i < lights.spot.length; i++)
    lights.spot[i].updateUniforms(gl, this.state, program, i);

  for (i = 0; i < lights.point.length; i++)
    lights.point[i].updateUniforms(gl, this.state, program, i);

  for (i = 0; i < lights.directional.length; i++)
    lights.directional[i].updateUniforms(gl, this.state, program, i);

  mesh.render(gl, program.attributes, this.state, this.extension);

  if (this.state.usedTextureSlots)
    this.state.usedTextureSlots = 0;
};

EZ3.Renderer.prototype._renderShadowCaster = function(mesh, program, view, projection) {
  var gl = this.context;
  var modelView = new EZ3.Matrix4();

  modelView.mul(view, mesh.world);

  program.loadUniformMatrix(gl, 'uModelView', modelView);
  program.loadUniformMatrix(gl, 'uProjection', projection);

  mesh.render(gl, program.attributes, this.state, this.extension);
};

EZ3.Renderer.prototype._renderDepth = function(lights, shadowCasters) {
  var gl = this.context;
  var color = new EZ3.Vector4(1.0);
<<<<<<< HEAD
=======
  var view;
  var target;
>>>>>>> bb66c0cf4b0802ba88950fc268c664ce9ec2b0cd
  var program;
  var fragment;
  var vertex;
  var origin;
  var light;
<<<<<<< HEAD
  var view;
=======
  var up;
>>>>>>> bb66c0cf4b0802ba88950fc268c664ce9ec2b0cd
  var i;
  var j;
  var k;

  if (this.state.faceCulling !== EZ3.Material.FRONT) {
    if (this.state.faceCulling === EZ3.Material.NONE)
      gl.enable(gl.CULL_FACE);

    gl.cullFace(gl.FRONT);

    this.faceCulling = EZ3.Material.FRONT;
  }

  if (!this.state.programs.depth) {
    vertex = EZ3.ShaderLibrary.depth.vertex;
    fragment = EZ3.ShaderLibrary.depth.fragment;
    this.state.programs.depth = new EZ3.GLSLProgram(gl, vertex, fragment);
  }

  program = this.state.programs.depth;

  program.bind(gl);

  for (i = 0; i < lights.length; i++) {
    light = lights[i];

    light.updateProjection();

    light.depthFramebuffer.bind(gl);

    light.depthFramebuffer.update(gl);

    this.viewport(new EZ3.Vector2(), light.depthFramebuffer.size);

    if (light instanceof EZ3.PointLight) {

      up = new EZ3.Vector3();
      view = new EZ3.Matrix4();
      target = new EZ3.Vector3();

      for (j = 0; j < 6; j++) {

        if (j === EZ3.Cubemap.POSITIVE_X) {
          target.set(1, 0, 0);
          up.set(0, -1, 0);
        } else if (j === EZ3.Cubemap.NEGATIVE_X) {
          target.set(-1, 0, 0);
          up.set(0, -1, 0);
        } else if (j === EZ3.Cubemap.POSITIVE_Y) {
          target.set(0, 1, 0);
          up.set(0, 0, 1);
        } else if (j === EZ3.Cubemap.NEGATIVE_Y) {
          target.set(0, -1, 0);
          up.set(0, 0, -1);
        } else if (j === EZ3.Cubemap.POSITIVE_Z) {
          target.set(0, 0, 1);
          up.set(0, -1, 0);
        } else if (j === EZ3.Cubemap.NEGATIVE_Z) {
          target.set(0, 0, -1);
          up.set(0, -1, 0);
        }

        view.lookAt(light.position, new EZ3.Vector3().add(target, light.position), new EZ3.Vector3(0, 1, 0));
        //view.lookAt(new EZ3.Vector3(), target, up);
        //view.mul(new EZ3.Matrix4().translate(light.position.clone().invert()));

        light.depthFramebuffer.texture.attach(gl, j);

        this.clear(color);

        for (k = 0; k < shadowCasters.length; ++k)
          this._renderShadowCaster(shadowCasters[k], program, view, light.projection);
      }
    } else {

      light.updateView();

      this.clear(color);

      for (j = 0; j < shadowCasters.length; j++)
        this._renderShadowCaster(shadowCasters[j], program, light.view, light.projection);
    }
  }
};

EZ3.Renderer.prototype._defaultFramebuffer = function() {
  var gl = this.context;

  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};

EZ3.Renderer.prototype.initContext = function() {
  var that = this;
  var names = [
    'webgl',
    'experimental-webgl',
    'webkit-3d',
    'moz-webgl'
  ];
  var i;

  for (i = 0; i < names.length; i++) {
    try {
      this.context = this.canvas.getContext(names[i], this.options);
    } catch (e) {}

    if (this.context) {
      this.state = new EZ3.State();
      this.state.maxTextureSlots = this.context.getParameter(this.context.MAX_TEXTURE_IMAGE_UNITS) - 1;
      this.extension = new EZ3.Extension(this.context);
      break;
    }
  }

  if (!this.context)
    throw new Error('Unable to initialize WebGL with selected options.');

  this._onContextLost = function(event) {
    that._processContextLost(event);
  };

  this.canvas.addEventListener('webglcontextlost', this._onContextLost, false);
};

EZ3.Renderer.prototype.clear = function(color) {
  var gl = this.context;

  if (color instanceof EZ3.Vector4)
    gl.clearColor(color.x, color.y, color.z, color.w);
  else
    gl.clearColor(0.0, 0.0, 0.0, 1.0);

  gl.clear(this.context.COLOR_BUFFER_BIT | this.context.DEPTH_BUFFER_BIT);
};

EZ3.Renderer.prototype.viewport = function(position, size) {
  var gl = this.context;

  gl.viewport(position.x, position.y, size.x, size.y);
};

EZ3.Renderer.prototype.render = function(scene, camera) {
  var gl = this.context;
  var entities = [];
  var meshes = {
    common: [],
    opaque: [],
    transparent: [],
    shadowCasters: []
  };
  var lights = {
    point: [],
    directional: [],
    spot: []
  };
  var found = false;
  var entity;
  var mesh;
  var i;

  entities.push(scene);

  while (entities.length) {
    entity = entities.pop();

    if (entity instanceof EZ3.PointLight)
      lights.point.push(entity);
    else if (entity instanceof EZ3.DirectionalLight)
      lights.directional.push(entity);
    else if (entity instanceof EZ3.SpotLight)
      lights.spot.push(entity);
    else if (entity instanceof EZ3.Mesh)
      meshes.common.push(entity);
    else if (entity === camera)
      found = true;

    for (i = entity.children.length - 1; i >= 0; i--)
      entities.push(entity.children[i]);

    entity.updateWorld();
  }

  if (!found)
    camera.updateWorld();

  camera.updateView();
  camera.updateProjection();

  this.state.maxSpotLights = lights.spot.length;
  this.state.maxPointLights = lights.point.length;
  this.state.maxDirectionalLights = lights.directional.length;
  this.state.hasLights = lights.point.length || lights.directional.length || lights.spot.length;

  for (i = 0; i < meshes.common.length; i++) {
    mesh = meshes.common[i];

    mesh.updatePrimitiveData();
    mesh.updateLinearData();

    this.state.activeShadowReceiver = mesh.shadowReceiver;

    mesh.material.updateProgram(gl, this.state);

    if (mesh.material.transparent)
      meshes.transparent.push(mesh);
    else
      meshes.opaque.push(mesh);

    if (mesh.shadowCaster)
      meshes.shadowCasters.push(mesh);
  }

  if (meshes.shadowCasters.length) {
    if (this.state.maxSpotLights)
      this._renderDepth(lights.spot, meshes.shadowCasters);

    if (this.state.maxPointLights)
      this._renderDepth(lights.point, meshes.shadowCasters);

    if (this.state.maxDirectionalLights)
      this._renderDepth(lights.directional, meshes.shadowCasters);

    this._defaultFramebuffer();
    this.viewport(new EZ3.Vector2(), new EZ3.Vector2(this.canvas.width, this.canvas.height));
  }

  for (i = 0; i < meshes.opaque.length; i++)
    this._renderMesh(meshes.opaque[i], camera, lights);

  for (i = 0; i < meshes.transparent.length; i++)
    this._renderMesh(meshes.transparent[i], camera, lights);
};
<commit_msg>Updated<commit_after>/**
 * @class Renderer
 */

EZ3.Renderer = function(canvas, options) {
  this.context = null;
  this.extension = null;
  this.canvas = canvas;
  this.options = options;
  this.state = null;
};

EZ3.Renderer.prototype._processContextLost = function(event) {
  event.preventDefault();
};

EZ3.Renderer.prototype._renderMesh = function(mesh, camera, lights) {
  var gl = this.context;
  var program = mesh.material.program;
  var modelView = new EZ3.Matrix4();
  var i;

  program.bind(gl);
  mesh.material.updateStates(gl, this.state);
  mesh.material.updateUniforms(gl, this.state);

  modelView.mul(camera.view, mesh.world);

  program.loadUniformFloat(gl, 'uEyePosition', camera.position);
  program.loadUniformMatrix(gl, 'uModel', mesh.world);
  program.loadUniformMatrix(gl, 'uModelView', modelView);
  program.loadUniformMatrix(gl, 'uProjection', camera.projection);

  if (this.state.hasLights) {
    mesh.updateNormal();
    mesh.updateNormalData();
    program.loadUniformMatrix(gl, 'uNormal', mesh.normal);
  }

  this.state.activeShadowReceiver = mesh.shadowReceiver;

  for (i = 0; i < lights.spot.length; i++)
    lights.spot[i].updateUniforms(gl, this.state, program, i);

  for (i = 0; i < lights.point.length; i++)
    lights.point[i].updateUniforms(gl, this.state, program, i);

  for (i = 0; i < lights.directional.length; i++)
    lights.directional[i].updateUniforms(gl, this.state, program, i);

  mesh.render(gl, program.attributes, this.state, this.extension);

  if (this.state.usedTextureSlots)
    this.state.usedTextureSlots = 0;
};

EZ3.Renderer.prototype._renderShadowCaster = function(mesh, program, view, projection) {
  var gl = this.context;
  var modelView = new EZ3.Matrix4();

  modelView.mul(view, mesh.world);

  program.loadUniformMatrix(gl, 'uModelView', modelView);
  program.loadUniformMatrix(gl, 'uProjection', projection);

  mesh.render(gl, program.attributes, this.state, this.extension);
};

EZ3.Renderer.prototype._renderDepth = function(lights, shadowCasters) {
  var gl = this.context;
  var color = new EZ3.Vector4(1.0);
  var view;
  var target;
  var program;
  var fragment;
  var vertex;
  var light;
  var up;
  var i;
  var j;
  var k;

  if (this.state.faceCulling !== EZ3.Material.FRONT) {
    if (this.state.faceCulling === EZ3.Material.NONE)
      gl.enable(gl.CULL_FACE);

    gl.cullFace(gl.FRONT);

    this.faceCulling = EZ3.Material.FRONT;
  }

  if (!this.state.programs.depth) {
    vertex = EZ3.ShaderLibrary.depth.vertex;
    fragment = EZ3.ShaderLibrary.depth.fragment;
    this.state.programs.depth = new EZ3.GLSLProgram(gl, vertex, fragment);
  }

  program = this.state.programs.depth;

  program.bind(gl);

  for (i = 0; i < lights.length; i++) {
    light = lights[i];

    light.updateProjection();

    light.depthFramebuffer.bind(gl);

    light.depthFramebuffer.update(gl);

    this.viewport(new EZ3.Vector2(), light.depthFramebuffer.size);

    if (light instanceof EZ3.PointLight) {

      up = new EZ3.Vector3();
      view = new EZ3.Matrix4();
      target = new EZ3.Vector3();

      for (j = 0; j < 6; j++) {

        if (j === EZ3.Cubemap.POSITIVE_X) {
          target.set(1, 0, 0);
          up.set(0, -1, 0);
        } else if (j === EZ3.Cubemap.NEGATIVE_X) {
          target.set(-1, 0, 0);
          up.set(0, -1, 0);
        } else if (j === EZ3.Cubemap.POSITIVE_Y) {
          target.set(0, 1, 0);
          up.set(0, 0, 1);
        } else if (j === EZ3.Cubemap.NEGATIVE_Y) {
          target.set(0, -1, 0);
          up.set(0, 0, -1);
        } else if (j === EZ3.Cubemap.POSITIVE_Z) {
          target.set(0, 0, 1);
          up.set(0, -1, 0);
        } else if (j === EZ3.Cubemap.NEGATIVE_Z) {
          target.set(0, 0, -1);
          up.set(0, -1, 0);
        }

        view.lookAt(light.position, new EZ3.Vector3().add(target, light.position), new EZ3.Vector3(0, 1, 0));
        //view.lookAt(new EZ3.Vector3(), target, up);
        //view.mul(new EZ3.Matrix4().translate(light.position.clone().invert()));

        light.depthFramebuffer.texture.attach(gl, j);

        this.clear(color);

        for (k = 0; k < shadowCasters.length; ++k)
          this._renderShadowCaster(shadowCasters[k], program, view, light.projection);
      }
    } else {

      light.updateView();

      this.clear(color);

      for (j = 0; j < shadowCasters.length; j++)
        this._renderShadowCaster(shadowCasters[j], program, light.view, light.projection);
    }
  }
};

EZ3.Renderer.prototype._defaultFramebuffer = function() {
  var gl = this.context;

  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};

EZ3.Renderer.prototype.initContext = function() {
  var that = this;
  var names = [
    'webgl',
    'experimental-webgl',
    'webkit-3d',
    'moz-webgl'
  ];
  var i;

  for (i = 0; i < names.length; i++) {
    try {
      this.context = this.canvas.getContext(names[i], this.options);
    } catch (e) {}

    if (this.context) {
      this.state = new EZ3.State();
      this.state.maxTextureSlots = this.context.getParameter(this.context.MAX_TEXTURE_IMAGE_UNITS) - 1;
      this.extension = new EZ3.Extension(this.context);
      break;
    }
  }

  if (!this.context)
    throw new Error('Unable to initialize WebGL with selected options.');

  this._onContextLost = function(event) {
    that._processContextLost(event);
  };

  this.canvas.addEventListener('webglcontextlost', this._onContextLost, false);
};

EZ3.Renderer.prototype.clear = function(color) {
  var gl = this.context;

  if (color instanceof EZ3.Vector4)
    gl.clearColor(color.x, color.y, color.z, color.w);
  else
    gl.clearColor(0.0, 0.0, 0.0, 1.0);

  gl.clear(this.context.COLOR_BUFFER_BIT | this.context.DEPTH_BUFFER_BIT);
};

EZ3.Renderer.prototype.viewport = function(position, size) {
  var gl = this.context;

  gl.viewport(position.x, position.y, size.x, size.y);
};

EZ3.Renderer.prototype.render = function(scene, camera) {
  var gl = this.context;
  var entities = [];
  var meshes = {
    common: [],
    opaque: [],
    transparent: [],
    shadowCasters: []
  };
  var lights = {
    point: [],
    directional: [],
    spot: []
  };
  var found = false;
  var entity;
  var mesh;
  var i;

  entities.push(scene);

  while (entities.length) {
    entity = entities.pop();

    if (entity instanceof EZ3.PointLight)
      lights.point.push(entity);
    else if (entity instanceof EZ3.DirectionalLight)
      lights.directional.push(entity);
    else if (entity instanceof EZ3.SpotLight)
      lights.spot.push(entity);
    else if (entity instanceof EZ3.Mesh)
      meshes.common.push(entity);
    else if (entity === camera)
      found = true;

    for (i = entity.children.length - 1; i >= 0; i--)
      entities.push(entity.children[i]);

    entity.updateWorld();
  }

  if (!found)
    camera.updateWorld();

  camera.updateView();
  camera.updateProjection();

  this.state.maxSpotLights = lights.spot.length;
  this.state.maxPointLights = lights.point.length;
  this.state.maxDirectionalLights = lights.directional.length;
  this.state.hasLights = lights.point.length || lights.directional.length || lights.spot.length;

  for (i = 0; i < meshes.common.length; i++) {
    mesh = meshes.common[i];

    mesh.updatePrimitiveData();
    mesh.updateLinearData();

    this.state.activeShadowReceiver = mesh.shadowReceiver;

    mesh.material.updateProgram(gl, this.state);

    if (mesh.material.transparent)
      meshes.transparent.push(mesh);
    else
      meshes.opaque.push(mesh);

    if (mesh.shadowCaster)
      meshes.shadowCasters.push(mesh);
  }

  if (meshes.shadowCasters.length) {
    if (this.state.maxSpotLights)
      this._renderDepth(lights.spot, meshes.shadowCasters);

    if (this.state.maxPointLights)
      this._renderDepth(lights.point, meshes.shadowCasters);

    if (this.state.maxDirectionalLights)
      this._renderDepth(lights.directional, meshes.shadowCasters);

    this._defaultFramebuffer();
    this.viewport(new EZ3.Vector2(), new EZ3.Vector2(this.canvas.width, this.canvas.height));
  }

  for (i = 0; i < meshes.opaque.length; i++)
    this._renderMesh(meshes.opaque[i], camera, lights);

  for (i = 0; i < meshes.transparent.length; i++)
    this._renderMesh(meshes.transparent[i], camera, lights);
};
<|endoftext|>


<commit_before>const Router = require('koa-router')

module.exports = new Router({ prefix: '/facility' })
  .post('/', async ctx => {
    // TODO body->location = Geolocation::getParsedPosition
    // TODO return->[{tbVal, clVal, phVal}][30]
  })
<commit_msg>Working on router<commit_after>const Router = require('koa-router')
const Facility = require('../models/Facility')

module.exports = new Router({ prefix: '/facility' })
  .post('/', async ctx => {
    // TODO body->location = Geolocation::getParsedPosition
    // TODO return->[{tbVal, clVal, phVal}][30]

    const data = ctx.request.body
    if (!data.location) {
      // TODO Bad request
    }

    const point = {type: 'Point', coordinates: [parseFloat(data.location[1]), parseFloat(data.location[0])]}

    const results = (await Facility.geoNear(point, {
      spherical: true,
      near: point.coordinates,
      limit: 1
    }))

    const obj = []
    results.forEach(v => {
      //console.log('v', v.obj)
      //console.log('v.getOwnProperties()', Object.getOwnPropertyNames(v.obj))
      obj.push(v.obj.qualities) // FIXME
    })

    ctx.body = obj
  })
<|endoftext|>


<commit_before>'use strict';

angular.module('classBrowserUHApp.class', ['ngRoute']).
config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/class', {
        templateUrl: 'views/class/class.html',
        controller: 'ClassCtrl'
    });
}]).
controller('ClassCtrl', ['$scope', '$http', '$q', '$rootScope', '$parse', '$window', function ($scope, $http, $q, $rootScope, $parse, $window) {
    $scope.rowCollection = [];

    var setScopeVariableFromJSON = function(filePath, scopeVariable) {
        $rootScope.httpService.getData(filePath).then(function(result) {
            $parse(scopeVariable).assign($scope, result);
        });
    };

    var initializeAllJSONAndScopeNames = function(name) {
        $rootScope.httpService.getData('resources/allScopeVariables.json').then(function(result) {
            $scope.allJSONAndScopeNames = result[name];
            $scope.allJSONAndScopeNames.forEach(function(e) {
                setScopeVariableFromJSON(e.path, e.scopeName);
            });
        });
    };

    initializeAllJSONAndScopeNames("class");

    $scope.populateClasses = function(){
        $scope.isDataLoading = true;
        $scope.isError = false;
        $scope.hasNoResults = false;
        $scope.rowCollection = [];
        $scope.numberOfRows = 0;

        var apiURLs = buildApiUrlsFromModel($scope.subjectModel, $scope.creditHourModel, $scope.coreModel);

        _.each(apiURLs, function(apiURL) {
            $rootScope.httpService.getData(apiURL)
                .then(appendResults)
                .then(populateFields)
                .catch(onError)
                .then(finallyDo)
            }
        );
    };

    var buildApiUrlsFromModel = function(subject, creditHour, core) {
        var baseUrl = $scope.apiUrl + '/information?';
        var allParametersFromScope = [
            {
                model: subject,
                parameterToPluck: 'subject',
                apiParameter: 'department'
            },
            {
                model: creditHour,
                parameterToPluck: 'credit-hours',
                apiParameter: 'credit-hours'
            },
            {
                model: core,
                parameterToPluck: 'categoryNumber',
                apiParameter: 'core'
            }
        ];

        return generateApiURLsFromModel(allParametersFromScope, baseUrl);
    };

    var generateApiURLsFromModel = function(allParametersFromScope, baseUrl) {
        allParametersFromScope.forEach(function(part, index, arr) {
            var apiParameter = part.apiParameter;
            arr[index] = _.pluck(part.model, part.parameterToPluck);
            checkArrayAndExpandInPlace(arr[index], apiParameter);
        });

        var allAPIUrls = allParametersFromScope
            .filter($rootScope.cartesianProductService.nonEmpty)
            .reduce($rootScope.cartesianProductService.productAdd, [""]);

        return $rootScope.apiURLService.appendParametersAndReturnAPIUrls(allAPIUrls, baseUrl);
    };

    var checkArrayAndExpandInPlace = function(arrayValue, apiParameter) {
        if(!$rootScope.arrayService.isArrayUndefinedOrNull(arrayValue)) {
            $rootScope.apiURLService.appendParameterEqualsValueInPlace(arrayValue, apiParameter);
        }
    };

    var appendResults = function(result) {
        $scope.numberOfRows += result.numberOfRows;
        $scope.rowCollection = $scope.rowCollection.concat(result.result);
    };

    var generateMessage = function(model, type, objToPluck) {
        if(model == undefined || model.length == 0) {
            return "";
        }
        else {
            return " " + type + ": " + _.pluck(model, objToPluck).join(', ') + "
";
        }
    };

    var populateFields = function(){
        if($scope.rowCollection.length > 0) {
            $scope.showResults = true;
            $scope.hasNoResults = false;

            $scope.numberOfRowsMessage = "Retrieved " + $scope.numberOfRows + " class";
            $scope.numberOfRowsMessage += $scope.numberOfRows == 1 ? "." : "es.";

            if ([$scope.departmentModel, $scope.creditHourModel, $scope.coreModel].allParametersUndefinedOrNull()) {
                $scope.parametersMessage = "No parameters were chosen, so all classes have been retrieved.";
            }
            else {
                $scope.subjectMessage = generateMessage($scope.subjectModel, 'Subjects', 'subjectFullName');
                $scope.creditHoursMessage = generateMessage($scope.creditHourModel, 'Credit Hours', 'credit-hours');
                $scope.coreMessage = generateMessage($scope.coreModel, 'Core Categories', 'categoryName');
                $scope.parametersMessage = $scope.subjectMessage +
                    $scope.creditHoursMessage +
                    $scope.coreMessage;
            }
        }
    };

    var onError = function(err) {
        $scope.isError = true;
        $scope.errorMessage = "Unable to populate classes for the class directory. Please try again later.";
    };

    var finallyDo = function() {
        $scope.isDataLoading = false;
        if($scope.rowCollection.length == 0 && !$scope.isError) {
            $scope.hasNoResults = true;
            $scope.warningMessage = "There are no classes found with the categories you have specified. Please try again.";
        }
    };

    var deleteModel = function() {
        [].slice.call(arguments).forEach(function(arg) {
            delete $scope[arg];
        });
    };

    $scope.goBack = function() {
        deleteModel('hasNoResults', 'isError', 'isDataLoading', 'showResults',
            'subjectMessage', 'creditHoursMessage', 'coreMessage', 'parametersMessage',
            'rowCollection');
    };

    $scope.clearForms = function() {
        deleteModel('subjectModel', 'creditHourModel', 'coreModel',
            'parametersMessage', 'rowCollection', 'hasNoResults', 'isError',
            'isDataLoading');
    };

}]);<commit_msg>Removed $window. Refactored clearForms to display a popup dialog.<commit_after>'use strict';

angular.module('classBrowserUHApp.class', ['ngRoute']).
config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/class', {
        templateUrl: 'views/class/class.html',
        controller: 'ClassCtrl'
    });
}]).
controller('ClassCtrl', ['$scope', '$http', '$q', '$rootScope', '$parse',function ($scope, $http, $q, $rootScope, $parse) {
    $scope.rowCollection = [];

    var setScopeVariableFromJSON = function(filePath, scopeVariable) {
        $rootScope.httpService.getData(filePath).then(function(result) {
            $parse(scopeVariable).assign($scope, result);
        });
    };

    var initializeAllJSONAndScopeNames = function(name) {
        $rootScope.httpService.getData('resources/allScopeVariables.json').then(function(result) {
            $scope.allJSONAndScopeNames = result[name];
            $scope.allJSONAndScopeNames.forEach(function(e) {
                setScopeVariableFromJSON(e.path, e.scopeName);
            });
        });
    };

    initializeAllJSONAndScopeNames("class");

    $scope.populateClasses = function(){
        $scope.isDataLoading = true;
        $scope.isError = false;
        $scope.hasNoResults = false;
        $scope.rowCollection = [];
        $scope.numberOfRows = 0;

        var apiURLs = buildApiUrlsFromModel($scope.subjectModel, $scope.creditHourModel, $scope.coreModel);

        _.each(apiURLs, function(apiURL) {
            $rootScope.httpService.getData(apiURL)
                .then(appendResults)
                .then(populateFields)
                .catch(onError)
                .then(finallyDo)
            }
        );
    };

    var buildApiUrlsFromModel = function(subject, creditHour, core) {
        var baseUrl = $scope.apiUrl + '/information?';
        var allParametersFromScope = [
            {
                model: subject,
                parameterToPluck: 'subject',
                apiParameter: 'department'
            },
            {
                model: creditHour,
                parameterToPluck: 'credit-hours',
                apiParameter: 'credit-hours'
            },
            {
                model: core,
                parameterToPluck: 'categoryNumber',
                apiParameter: 'core'
            }
        ];

        return generateApiURLsFromModel(allParametersFromScope, baseUrl);
    };

    var generateApiURLsFromModel = function(allParametersFromScope, baseUrl) {
        allParametersFromScope.forEach(function(part, index, arr) {
            var apiParameter = part.apiParameter;
            arr[index] = _.pluck(part.model, part.parameterToPluck);
            checkArrayAndExpandInPlace(arr[index], apiParameter);
        });

        var allAPIUrls = allParametersFromScope
            .filter($rootScope.cartesianProductService.nonEmpty)
            .reduce($rootScope.cartesianProductService.productAdd, [""]);

        return $rootScope.apiURLService.appendParametersAndReturnAPIUrls(allAPIUrls, baseUrl);
    };

    var checkArrayAndExpandInPlace = function(arrayValue, apiParameter) {
        if(!$rootScope.arrayService.isArrayUndefinedOrNull(arrayValue)) {
            $rootScope.apiURLService.appendParameterEqualsValueInPlace(arrayValue, apiParameter);
        }
    };

    var appendResults = function(result) {
        $scope.numberOfRows += result.numberOfRows;
        $scope.rowCollection = $scope.rowCollection.concat(result.result);
    };

    var generateMessage = function(model, type, objToPluck) {
        if(model == undefined || model.length == 0) {
            return "";
        }
        else {
            return " " + type + ": " + _.pluck(model, objToPluck).join(', ') + "
";
        }
    };

    var populateFields = function(){
        if($scope.rowCollection.length > 0) {
            $scope.showResults = true;
            $scope.hasNoResults = false;

            $scope.numberOfRowsMessage = "Retrieved " + $scope.numberOfRows + " class";
            $scope.numberOfRowsMessage += $scope.numberOfRows == 1 ? "." : "es.";

            if ([$scope.departmentModel, $scope.creditHourModel, $scope.coreModel].allParametersUndefinedOrNull()) {
                $scope.parametersMessage = "No parameters were chosen, so all classes have been retrieved.";
            }
            else {
                $scope.subjectMessage = generateMessage($scope.subjectModel, 'Subjects', 'subjectFullName');
                $scope.creditHoursMessage = generateMessage($scope.creditHourModel, 'Credit Hours', 'credit-hours');
                $scope.coreMessage = generateMessage($scope.coreModel, 'Core Categories', 'categoryName');
                $scope.parametersMessage = $scope.subjectMessage +
                    $scope.creditHoursMessage +
                    $scope.coreMessage;
            }
        }
    };

    var onError = function(err) {
        $scope.isError = true;
        $scope.errorMessage = "Unable to populate classes for the class directory. Please try again later.";
    };

    var finallyDo = function() {
        $scope.isDataLoading = false;
        if($scope.rowCollection.length == 0 && !$scope.isError) {
            $scope.hasNoResults = true;
            $scope.warningMessage = "There are no classes found with the categories you have specified. Please try again.";
        }
    };

    var deleteModel = function() {
        [].slice.call(arguments).forEach(function(arg) {
            delete $scope[arg];
        });
    };

    $scope.goBack = function() {
        deleteModel('hasNoResults', 'isError', 'isDataLoading', 'showResults',
            'subjectMessage', 'creditHoursMessage', 'coreMessage', 'parametersMessage',
            'rowCollection');
    };

    $scope.clearForms = function() {
        $rootScope.showConfirmAndReturnPromise().then(function() {
            deleteModel('subjectModel', 'creditHourModel', 'coreModel',
                'parametersMessage', 'rowCollection', 'hasNoResults', 'isError',
                'isDataLoading');
        }, function() {
            console.log("Not deleting...")
        });
    };

}]);<|endoftext|>