<commit_before>/*
 * Shared by the server and client
 */

var lang = {
	anon: 'Anônimo',
	search: 'Pesquisa',
	show: 'Exibir',
	hide: 'Esconder',
	report: 'Reportar',
	focus: 'Focar',
	expand: 'Expandir',
	last: 'Últimos',
	see_all: 'Ver todos',
	bottom: 'Rodapé',
	expand_images: 'Expandir Imagens',
	live: 'ao vivo',
	catalog: 'Catálogo',
	return: 'Retornar',
	top: 'Topo',
	reply: 'Postar',
	newThread: 'Novo tópico',
	locked_to_bottom: 'Travado ao rodapé',
	you: '(You)',
	done: 'Feito',
	send: 'Enviar',
	locked: 'trancado',
	thread_locked: 'Este tópico está trancado.',
	langApplied: 'Configurações de linguagem foram mudadas. Esta página recarregará agora.',
	googleSong: 'Clique para pesquisar (google) a música',
	quoted: 'Você foi quotado',
	syncwatchStarting: 'Syncwatch começará em 10 segundos',
	finished: 'Terminado.',
	expander: ['Expandir imagens', 'Contrair imagens'],
	uploading: 'Enviando...',
	subject: 'Assunto',
	cancel: 'Cancelar',
	unknownUpload: 'Erro de upload desconhecido',
	unknownResult: 'Resultado desconhecido',

	reports: {
		post: 'Reportando post',
		reporting: 'Reportando...',
		submitted: 'Denúncia enviada!',
		setup: 'Obtendo reCAPTCHA...',
		loadError: 'Não foi possível carregar o reCATPCHA'
	},

	// Time-related
	week: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'],
	year: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set',
		'Out', 'Nov', 'Dez'],
	just_now: 'agora mesmo',
	unit_minute: 'minuto',
	unit_hour: 'hora',
	unit_day: 'dia',
	unit_month: 'mês',
	unit_year: 'ano',

	// Websocket syncronisation status
	sync: {
		notSynced: 'Dessincronizado',
		connecting: 'Conectando',
		syncing: 'Sincronizando',
		synced: 'Sincronizado',
		dropped: 'Caiu',
		reconnecting: 'Reconectando'
	},

	// Moderation language map
	mod: {
		title: ['Título', 'Mostra o título de staff nos posts'],
		clearSelection: ['Limpar', 'Limpa a seleção de posts'],
		spoilerImages: ['Spoiler', 'Adiciona spoiler na imagem dos posts selecionados'],
		deleteImages: ['Del Img', 'Deleta a imagem do post selecionado'],
		deletePosts: ['Del Post', 'Deleta o post selecionado'],
		lockThreads: ['Trancar', 'Tranca/destranca o tópico selecionado'],
		toggleMnemonics: ['Mnemonics', 'Ativa a exibição de mnemonics'],
		sendNotification: [
			'Notificação',
			'Envia uma mensagem de notificação para todos os clientes'
		],
		renderPanel: ['Painel', 'Ativa a exibição do painel de administração'],
		ban: ['Ban', 'Ban o(s) usuário(s) pelos posts selecionados'],
		modLog: ['Reg', 'Mostra o registro de moderação'],
		displayBan: [
			'Exibir',
			'Adiciona uma mensagem \'O USUÁRIO FOI BANIDO POR ESTA POSTAGEM\' publicamente'
		],
		banMessage: 'O USUÁRIO FOI BANIDO POR ESTA POSTAGEM',
		unban: 'Desbanir',
		placeholders: {
			msg: 'Mensagem',
			days: 'd',
			hours: 'h',
			minutes: 'min',
			reason: 'Razão'
		},
		needReason: 'É necessário especificar uma razão',

		// Correspond to websocket calls in common/index.js
		7: 'Spoiler adicionado à imagem',
		8: 'Imagem deletada',
		9: 'Postagem deletada',
		10: 'Tópico trancado',
		11: 'Tópico destrancado',
		12: 'Usuário banido',
		53: 'Usuário desbanido',

		// Formatting function for moderation messages
		formatLog: function (act) {
			var msg = lang.mod[act.kind] + ' por ' + act.ident;
			if (act.reason)
				msg += ' pela razão de ' + act.reason;
			return msg;
		}
	},

	// Format functions
	pluralize: function(n, noun) {
		// For words ending with 'y' and not a vovel before that
		if (n != 1
			&& noun.slice(-1) == 'y'
			&& ['a', 'e', 'i', 'o', 'u'].indexOf(noun.slice(-2, -1)
				.toLowerCase()) < 0) {
			noun = noun.slice(0, -1) + 'ie';
		}
		return n + ' ' + noun + (n == 1 ? '' : 's');
	},
	capitalize: function(word) {
		return word[0].toUpperCase() + word.slice(1);
	},
	// 56 minutos atrás
	ago: function(time, unit, isFuture) {
		var res = lang.pluralize(time, unit);
		if (isFuture)
			res = 'in ' + res;
		else
			res += ' atrás';
		return res;
	},
	// 47 respostas and 21 images omited
	abbrev_msg:  function(omit, img_omit, url) {
		var html = lang.pluralize(omit, 'postagen');
		if (img_omit)
			html += ' e ' + lang.pluralize(img_omit, 'imagen');
		html += ' omitidas';
		if (url) {
			html += ' '
				+ lang.see_all + '';
		}
		return html;
	}
};

module.exports = lang;
<commit_msg>Fix ago() and (You)<commit_after>/*
 * Shared by the server and client
 */

var lang = {
	anon: 'Anônimo',
	search: 'Pesquisa',
	show: 'Exibir',
	hide: 'Esconder',
	report: 'Reportar',
	focus: 'Focar',
	expand: 'Expandir',
	last: 'Últimos',
	see_all: 'Ver todos',
	bottom: 'Rodapé',
	expand_images: 'Expandir Imagens',
	live: 'ao vivo',
	catalog: 'Catálogo',
	return: 'Retornar',
	top: 'Topo',
	reply: 'Postar',
	newThread: 'Novo tópico',
	locked_to_bottom: 'Travado ao rodapé',
	you: '(Tu)',
	done: 'Feito',
	send: 'Enviar',
	locked: 'trancado',
	thread_locked: 'Este tópico está trancado.',
	langApplied: 'Configurações de linguagem foram mudadas. Esta página recarregará agora.',
	googleSong: 'Clique para pesquisar (google) a música',
	quoted: 'Você foi quotado',
	syncwatchStarting: 'Syncwatch começará em 10 segundos',
	finished: 'Terminado.',
	expander: ['Expandir imagens', 'Contrair imagens'],
	uploading: 'Enviando...',
	subject: 'Assunto',
	cancel: 'Cancelar',
	unknownUpload: 'Erro de upload desconhecido',
	unknownResult: 'Resultado desconhecido',

	reports: {
		post: 'Reportando post',
		reporting: 'Reportando...',
		submitted: 'Denúncia enviada!',
		setup: 'Obtendo reCAPTCHA...',
		loadError: 'Não foi possível carregar o reCATPCHA'
	},

	// Time-related
	week: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'],
	year: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set',
		'Out', 'Nov', 'Dez'],
	just_now: 'agora mesmo',
	unit_minute: 'minuto',
	unit_hour: 'hora',
	unit_day: 'dia',
	unit_month: 'mês',
	unit_year: 'ano',

	// Websocket syncronisation status
	sync: {
		notSynced: 'Dessincronizado',
		connecting: 'Conectando',
		syncing: 'Sincronizando',
		synced: 'Sincronizado',
		dropped: 'Caiu',
		reconnecting: 'Reconectando'
	},

	// Moderation language map
	mod: {
		title: ['Título', 'Mostra o título de staff nos posts'],
		clearSelection: ['Limpar', 'Limpa a seleção de posts'],
		spoilerImages: ['Spoiler', 'Adiciona spoiler na imagem dos posts selecionados'],
		deleteImages: ['Del Img', 'Deleta a imagem do post selecionado'],
		deletePosts: ['Del Post', 'Deleta o post selecionado'],
		lockThreads: ['Trancar', 'Tranca/destranca o tópico selecionado'],
		toggleMnemonics: ['Mnemonics', 'Ativa a exibição de mnemonics'],
		sendNotification: [
			'Notificação',
			'Envia uma mensagem de notificação para todos os clientes'
		],
		renderPanel: ['Painel', 'Ativa a exibição do painel de administração'],
		ban: ['Ban', 'Ban o(s) usuário(s) pelos posts selecionados'],
		modLog: ['Reg', 'Mostra o registro de moderação'],
		displayBan: [
			'Exibir',
			'Adiciona uma mensagem \'O USUÁRIO FOI BANIDO POR ESTA POSTAGEM\' publicamente'
		],
		banMessage: 'O USUÁRIO FOI BANIDO POR ESTA POSTAGEM',
		unban: 'Desbanir',
		placeholders: {
			msg: 'Mensagem',
			days: 'd',
			hours: 'h',
			minutes: 'min',
			reason: 'Razão'
		},
		needReason: 'É necessário especificar uma razão',

		// Correspond to websocket calls in common/index.js
		7: 'Spoiler adicionado à imagem',
		8: 'Imagem deletada',
		9: 'Postagem deletada',
		10: 'Tópico trancado',
		11: 'Tópico destrancado',
		12: 'Usuário banido',
		53: 'Usuário desbanido',

		// Formatting function for moderation messages
		formatLog: function (act) {
			var msg = lang.mod[act.kind] + ' por ' + act.ident;
			if (act.reason)
				msg += ' pela razão de ' + act.reason;
			return msg;
		}
	},

	// Format functions
	pluralize: function(n, noun) {
		// For words ending with 'y' and not a vovel before that
		if (n != 1
			&& noun.slice(-1) == 'y'
			&& ['a', 'e', 'i', 'o', 'u'].indexOf(noun.slice(-2, -1)
				.toLowerCase()) < 0) {
			noun = noun.slice(0, -1) + 'ie';
		}
		return n + ' ' + noun + (n == 1 ? '' : 's');
	},
	capitalize: function(word) {
		return word[0].toUpperCase() + word.slice(1);
	},
	// 56 minutos atrás
	ago: function(time, unit, isFuture) {
		var res = lang.pluralize(time, unit);
		if (isFuture)
			res = 'em ' + res;
		else
			res += ' atrás';
		return res;
	},
	// 47 respostas and 21 images omited
	abbrev_msg:  function(omit, img_omit, url) {
		var html = lang.pluralize(omit, 'postagen');
		if (img_omit)
			html += ' e ' + lang.pluralize(img_omit, 'imagen');
		html += ' omitidas';
		if (url) {
			html += ' '
				+ lang.see_all + '';
		}
		return html;
	}
};

module.exports = lang;
<|endoftext|>


<commit_before>var Stream = require('stream').Stream;

/**
 * StreamStack
 * -----------
 * Turns low-level `Stream` objects into stackable stream, meant
 * to fill out your desired protocol stack. But also allow for
 * the protocol to be implemented independent of the underlying
 * transport.
 *   An example overall stack could look like:
 *     - net.Stream                           <- TCP Layer
 *     - HttpRequestStack                     <- HTTP Layer
 *       - `write()`s an HTTP request upstream
 *       - Response comes back with 'gzip' transfer-encoding
 *     - GzipDecoderStack                     <- Decoding Layer
 *     - `.pipe()` into a 'fs.WriteStream'    <- Save to a File
 */
function StreamStack(stream, events) {
  if (!(stream instanceof Stream)) {
    throw new Error("StreamStack expects an instance of 'Stream' as an argument!");
  }
  if (!(this instanceof StreamStack)) {
    return new StreamStack(stream, events);
  }

  Stream.call(this);

  // If this is the first time the parent stream has been used in a
  // StreamStack, then a StackEmitter will need to be injected into the stream.
  if (!stream.__stackEmitter) {
    StackEmitter.call(stream);
  }
  stream._stacks.push(this);

  // A reference to the parent stream for event handlers, etc.
  this.stream = stream;

  // TODO: Remove, if I can find a good reason to.
  events = events || {};
  if (!('data' in events))
    events.data = proxyEvent('data');
  if (!('end' in events))
    events.end = proxyEvent('end');
  if (!('error' in events))
    events.error = proxyEvent('error');
  if (!('close' in events))
    events.close = proxyEvent('close');
  if (!('fd' in events))
    events.fd = proxyEvent('fd');
  if (!('drain' in events))
    events.drain = proxyEvent('drain');

  // If the StreamStack instance intends on intercepting events emitted from
  // the parent stream, then the handlers need to be passed as a second 'events'
  // object in the constructor. It takes care of attaching them to the parent
  // stream. Handlers are invoked in 'this' StreamStack instance.
  if (events) {
    this._stackEvents = {};
    for (var ev in events) {
      this._stackEvents[ev] = events[ev].bind(this);
      stream.on(ev, this._stackEvents[ev]);
    }
  }

}
require('util').inherits(StreamStack, Stream);
exports.StreamStack = StreamStack;

// By default, just proxy all the standard ReadStream and WriteStream
// functions upstream. If the StreamStack implementation needs to overwrite
// or augment any of the behavior, then simply overwrite that function.
//   The most common is to augment the 'write()' function, such that the
//   passed data goes through some kind of filter before being passed to
//   the parent stream.
StreamStack.prototype.write = function(buf, type) {
  return this.stream.write(buf, type);
}
StreamStack.prototype.end = function(buf, type) {
  if (buf) {
    this.write(buf, type);
  }
  return this.stream.end();
}
StreamStack.prototype.pause = function() {
  return this.stream.pause();
}
StreamStack.prototype.resume = function() {
  return this.stream.resume();
}
StreamStack.prototype.destroy = function(error) {
  return this.stream.destory(error);
}

// By default, the 'readable' and 'writable' property lookups get proxied
// to the parent stream. You can set the variables if needed, and to relinquish
// control of the variable back upstream, set it to `undefined`.
Object.defineProperty(StreamStack.prototype, "readable", {
  get: function() {
    if (this._readable != undefined) {
      return this._readable;
    }
    return this.stream.readable;
  },
  set: function(value) {
    this._readable = value;
  },
  enumerable: true
});
Object.defineProperty(StreamStack.prototype, "writable", {
  get: function() {
    if (this._writable != undefined) {
      return this._writable;
    }
    return this.stream.writable;
  },
  set: function(value) {
    this._writable = value;
  },
  enumerable: true
});

// Walks up the 'stream' properties until it finds and returns the top-most
// stream. i.e. it gets the low-level stream this stack is based on.
Object.defineProperty(StreamStack.prototype, "topStream", {
  get: function() {
    var rtn = this.stream;
    while (rtn.stream) {
      rtn = rtn.stream;
    }
    return rtn;
  },
  enumerable: true
});

// Stupid workaround. Attach a listener for the given 'eventName'.
// The callback simply re-emits the events and all arguments on 'this'.
function proxyEvent(eventName) {
  return function() {
    var args = [eventName];
    args = args.concat(args.slice.call(arguments));
    this.emit.apply(this, args);
  }
}

// Parent streams need to have StackEmitter called on them the first time a
// StreamStack instance is attempting to use it. The __proto__ of the parent
// stream will be injected with StackEmitter's prototype, to benefit from
// the overwritten 'emit()' function.
function StackEmitter() {
  for (var prop in StackEmitter.prototype) {
    this[prop] = StackEmitter.prototype[prop];
  }
  // The Array that holds the active StreamStack instances on a parent Stream.
  this._stacks = [];
}

// A flag to indicate that the parent stream has already been injected.
StackEmitter.prototype._stackEmitter = true;

// The custom 'emit()' function is responsible for iterating through the list
// of active StreamStack instances, and IFF the StreamStack instance didn't
// pass a handler to the current event, it should re-emit on the child as well.
StackEmitter.prototype.emit = function(eventName) {
  var stack;
  // Emit on the parent Stream first
  var rtn = this.__proto__.emit.apply(this, arguments);
  // Next re-emit on all the active StreamStack instances (if any)
  for (var i=0, l=this._stacks.length; i<l; i++) {
    stack = this._stacks[i];
    if (!stack._stackEvents || !(eventName in stack._stackEvents)) {
      if (!stack.emit.apply(stack, arguments)) {
        rtn = false;
      }
    }
  }
  return rtn;
}
<commit_msg>Add `StreamStack#cleanup()`. Which is a function that should be called at the end of the StreamStack instance's life.<commit_after>var Stream = require('stream').Stream;

/**
 * StreamStack
 * -----------
 * Turns low-level `Stream` objects into stackable stream, meant
 * to fill out your desired protocol stack. But also allow for
 * the protocol to be implemented independent of the underlying
 * transport.
 *   An example overall stack could look like:
 *     - net.Stream                           <- TCP Layer
 *     - HttpRequestStack                     <- HTTP Layer
 *       - `write()`s an HTTP request upstream
 *       - Response comes back with 'gzip' transfer-encoding
 *     - GzipDecoderStack                     <- Decoding Layer
 *     - `.pipe()` into a 'fs.WriteStream'    <- Save to a File
 */
function StreamStack(stream, events) {
  if (!(stream instanceof Stream)) {
    throw new Error("StreamStack expects an instance of 'Stream' as an argument!");
  }
  if (!(this instanceof StreamStack)) {
    return new StreamStack(stream, events);
  }

  Stream.call(this);

  // If this is the first time the parent stream has been used in a
  // StreamStack, then a StackEmitter will need to be injected into the stream.
  if (!stream.__stackEmitter) {
    StackEmitter.call(stream);
  }
  stream._stacks.push(this);

  // A reference to the parent stream for event handlers, etc.
  this.stream = stream;

  // TODO: Remove, if I can find a good reason to.
  events = events || {};
  if (!('data' in events))
    events.data = proxyEvent('data');
  if (!('end' in events))
    events.end = proxyEvent('end');
  if (!('error' in events))
    events.error = proxyEvent('error');
  if (!('close' in events))
    events.close = proxyEvent('close');
  if (!('fd' in events))
    events.fd = proxyEvent('fd');
  if (!('drain' in events))
    events.drain = proxyEvent('drain');

  // If the StreamStack instance intends on intercepting events emitted from
  // the parent stream, then the handlers need to be passed as a second 'events'
  // object in the constructor. It takes care of attaching them to the parent
  // stream. Handlers are invoked in 'this' StreamStack instance.
  if (events) {
    this._stackEvents = {};
    for (var ev in events) {
      this._stackEvents[ev] = events[ev].bind(this);
      stream.on(ev, this._stackEvents[ev]);
    }
  }

}
require('util').inherits(StreamStack, Stream);
exports.StreamStack = StreamStack;

// By default, just proxy all the standard ReadStream and WriteStream
// functions upstream. If the StreamStack implementation needs to overwrite
// or augment any of the behavior, then simply overwrite that function.
//   The most common is to augment the 'write()' function, such that the
//   passed data goes through some kind of filter before being passed to
//   the parent stream.
StreamStack.prototype.write = function(buf, type) {
  return this.stream.write(buf, type);
}
StreamStack.prototype.end = function(buf, type) {
  if (buf) {
    this.write(buf, type);
  }
  return this.stream.end();
}
StreamStack.prototype.pause = function() {
  return this.stream.pause();
}
StreamStack.prototype.resume = function() {
  return this.stream.resume();
}
StreamStack.prototype.destroy = function(error) {
  return this.stream.destory(error);
}

// The 'cleanup()' function should be called after a StreamStack instance is
// finished doing it's "thing", to cleanly allow another new StreamStack
// instance to be attached to the parent Stream.
StreamStack.prototype.cleanup = function() {
  // Remove 'this' from the parent Stream's '_stacks' Array
  var index = this.stream._stacks.indexOf(this);
  process.assert(index >= 0);
  this.stream._stacks.splice(index, 1);
  // Set 'this.stream' to null.
  // If any events were binded through the constructor, they get unbinded here
  if (this._stackEvents) {
    for (var ev in this._stackEvents) {
      this.stream.removeListener(ev, this._stackEvents[ev]);
    }
    this._stackEvents = null;
  }
  // TODO: Maybe 'delete' instead? Is there any benefit?
  this.stream = null;
}

// By default, the 'readable' and 'writable' property lookups get proxied
// to the parent stream. You can set the variables if needed, and to relinquish
// control of the variable back upstream, set it to `undefined`.
Object.defineProperty(StreamStack.prototype, "readable", {
  get: function() {
    if (this._readable != undefined) {
      return this._readable;
    }
    return this.stream.readable;
  },
  set: function(value) {
    this._readable = value;
  },
  enumerable: true
});
Object.defineProperty(StreamStack.prototype, "writable", {
  get: function() {
    if (this._writable != undefined) {
      return this._writable;
    }
    return this.stream.writable;
  },
  set: function(value) {
    this._writable = value;
  },
  enumerable: true
});

// Walks up the 'stream' properties until it finds and returns the top-most
// stream. i.e. it gets the low-level stream this stack is based on.
Object.defineProperty(StreamStack.prototype, "topStream", {
  get: function() {
    var rtn = this.stream;
    while (rtn.stream) {
      rtn = rtn.stream;
    }
    return rtn;
  },
  enumerable: true
});

// Stupid workaround. Attach a listener for the given 'eventName'.
// The callback simply re-emits the events and all arguments on 'this'.
function proxyEvent(eventName) {
  return function() {
    var args = [eventName];
    args = args.concat(args.slice.call(arguments));
    this.emit.apply(this, args);
  }
}

// Parent streams need to have StackEmitter called on them the first time a
// StreamStack instance is attempting to use it. The __proto__ of the parent
// stream will be injected with StackEmitter's prototype, to benefit from
// the overwritten 'emit()' function.
function StackEmitter() {
  for (var prop in StackEmitter.prototype) {
    this[prop] = StackEmitter.prototype[prop];
  }
  // The Array that holds the active StreamStack instances on a parent Stream.
  this._stacks = [];
}

// A flag to indicate that the parent stream has already been injected.
StackEmitter.prototype._stackEmitter = true;

// The custom 'emit()' function is responsible for iterating through the list
// of active StreamStack instances, and IFF the StreamStack instance didn't
// pass a handler to the current event, it should re-emit on the child as well.
StackEmitter.prototype.emit = function(eventName) {
  var stack;
  // Emit on the parent Stream first
  var rtn = this.__proto__.emit.apply(this, arguments);
  // Next re-emit on all the active StreamStack instances (if any)
  for (var i=0, l=this._stacks.length; i<l; i++) {
    stack = this._stacks[i];
    if (!stack._stackEvents || !(eventName in stack._stackEvents)) {
      if (!stack.emit.apply(stack, arguments)) {
        rtn = false;
      }
    }
  }
  return rtn;
}
<|endoftext|>


<commit_before>import Vue from 'vue'
import Vuex from 'vuex'
import {remote} from 'electron'
import OAuthElectron from 'salesforce/oauth'
import access from 'salesforce/access'
import storage from '../storage'
import papa from 'papaparse'
let {app, dialog, BrowserWindow} = remote

import {checkLevels} from '../checks'
import babyTeethUrl from '../assets/primary_teeth.jpg'
import permanentTeethUrl from '../assets/permanent_teeth.jpg'

let rosters = {}
let surveys = {}

Vue.use(Vuex)
function generatePDFs (exportPath, sessionUrl) {
  return new Promise(function (resolve, reject) {
    let win = new BrowserWindow({useContentSize: true, show: true})
    win.loadURL(sessionUrl)
    win.on('page-title-updated', function (event, title) {
      if (title === 'finish') {
        win.close()
        win = null
        resolve()
      }
      let titleParts = title.split(':')

      if (titleParts[0] === 'print') {
        const followUp = titleParts[1] == 'y'
        const surveyName = titleParts[2]
        win.webContents.printToPDF({}, function (err, data) {
          if (err) {
            win.close()
            win = null
            reject(err)
          }
          storage.savePDF(exportPath, followUp, surveyName, data)
          win.webContents.send('status', 'ready')
        })
      }
    })
  })
}

function getChildAge (survey, record) {
  if (survey.birthDate) {
    const ageSeconds = Date.now() - Date.parse(survey.birthDate)
    console.log(ageSeconds)
    const age = new Date(ageSeconds).getUTCFullYear() - 1970
    return age
  }
  if (record && record.childsAge) {
    return record.childsAge
  }
  return null
}

function getOutputRecord (siteId, siteName, survey, record) {
  let prioritySet = new Set(survey.checkList.map(c => parseInt(c[0])))
  const needsFollowUp = prioritySet.has(2) || prioritySet.has(3)

  let output = {
    'Lead type': 'DentalFirst',
    'Student ID': survey.studentId,
    'School Site:Account ID': siteId,
    'School Site:Account Name': siteName,
    'First Name': survey.firstName,
    'Last Name': survey.lastName,
    'Teacher': survey.teacher,
    'Date of Birth': survey.birthDate,
    'Priority': [...prioritySet].sort().reverse()[0],
    'Follow-up Required': needsFollowUp ? 'Yes' : 'No',
    'Status': needsFollowUp ? 'Open - Outreach Required' : 'Closed - Passed screening',
    'PDF Name': `${survey.studentId}.${new Date(survey.date).toISOString().split('T')[0]}.pdf`,
    'Screening Outreach': '',
    'Date Child Screened': new Date(survey.date).toISOString().split('T')[0],
    'Dentist Name': survey.dentist
  }

  const childsAge = getChildAge(survey, record)
  if (childsAge) {
    output["Child's Age"] = childsAge
  }
  if (record && record.zipCode) {
    output['Zip Code'] = record.zipCode
  }
  if (record && record.gender) {
    output['Gender'] = record.gender
  }
  if (record && record.preferredLanguage) {
    output['Preferred Language'] = record.preferredLanguage
  }
  if (record && record.stateId) {
    output['State Identifier'] = record.stateId
  }
  return output
}
function getSurveySummary(survey){
  return {
    date:survey.date,
    studentId:survey.studentId,
    name:`${survey.firstName} ${survey.lastName}`
  }
}

function cleanSurvey () {
  return {
    firstName: '',
    lastName: '',
    studentId: '',
    birthDate: '',
    comment: '',
    checkList: [],
    babyTeeth: [],
    permanentTeeth: [],
    signature: [],
    date: 0
  }
}
function cleanFullSurvey () {
  return Object.assign(cleanSurvey(), {
    room: '',
    teacher: '',
    grade: ''
  })
}

let handleError = function (commit) {
  return function (error) {
    commit('setAlert', {state: 'danger', message: error.message})
  }
}

let login = function (auth) {
  let oauth = new OAuthElectron(
    {
      appId: auth.appId,
      oauthCallbackURL: auth.oauthCallbackURL
    })
  return oauth.login()
}
let getAccessToken = function (auth) {
  access.init(auth)
  if (!auth.accessToken) {
    return access.refreshAccessToken().then(function (accessToken) {
      const newAuth = Object.assign({}, auth, {'accessToken': accessToken})
      access.init(newAuth)
      return newAuth
    })
  }
  return auth
}

let store = new Vuex.Store({
  state: {
    editMode: false,
    unique: true,
    survey: {
      babyTeethUrl: babyTeethUrl,
      permanentTeethUrl: permanentTeethUrl,
      grade: '',
      room: '',
      teacher: '',
      firstName: '',
      lastName: '',
      comment: '',
      studentId: '',
      birthDate: '',
      checkList: [],
      babyTeeth: [],
      permanentTeeth: [],
      signature: [],
      dentist: '',
      date: 0
    },
    checkLevels: checkLevels,
    alert: {state: 'success', message: ''},
    auth: {
      appId: null,
      instanceURL: null,
      accessToken: null,
      refreshToken: null,
      userId: null,
      oauthCallbackURL: null
    },
    sites: {downloadDate: null, list: []},
    newSession: {
      selectedItem: null,
      selectedFile: []
    },
    sessions: [],
    activeSession: -1,
    loaded: false
  },
  mutations: {
    setEditMode(state, payload){
      state.editMode = payload
    },
    finishLoading (state) {
      state.loaded = true
    },
    addLine (state, lineMap) {
      for (var key in lineMap) {
        if (lineMap.hasOwnProperty(key)) {
          state.survey[key].push(lineMap[key])
        }
      }
    },
    updateSurvey (state, update) {
      if (update.studentId && state.activeSession >= 0) {
        state.unique = !surveys[state.activeSession].some(s => s.studentId === update.studentId)
      }
      Object.assign(state.survey, update)
    },
    loadSurvey (state, {session, index}) {
      console.log('Loading Survey')
      Object.assign(state.survey, surveys[session][index])
    },
    activateSession (state, id) {
      state.activeSession = id
    },
    addSession (state, session) {
      state.sessions.splice(0, 0, session)
    },
    addSurveySummary (state, survey) {
      let session = state.sessions.find(s => s.date == state.activeSession)
      let index = session.surveySummaries.findIndex(s=>s.date == survey.date)
      if(index >=0){
        session.surveySummaries.splice(index,1,getSurveySummary(survey))
      } else{
        session.surveySummaries.push(getSurveySummary(survey))
      }
      surveys[state.activeSession].push(survey)
    },
    updateFile (state, file) {
      state.newSession.selectedFile = file
    },
    selectSite (state, site) {
      state.newSession.selectedItem = site
    },
    replaceSites (state, sites) {
      state.sites = sites
    },
    clearAlert (state) {
      console.log('clear Alert')
      state.alert.message = ''
    },
    setAlert (state, alert) {
      console.log(`Set alert:${alert.state}-${alert.message}`)
      state.alert.state = alert.state
      state.alert.message = alert.message
    },
    clearAuthParams (state, params) {
      console.log(`clearing ${params}`)
      for (const key of params) {
        state.auth[key] = null
      }
    },
    updateAuth (state, payload) {
      for (const prop in payload) {
        if ([
          'instanceURL',
          'accessToken',
          'appId',
          'userId',
          'refreshToken',
          'oauthCallbackURL'
        ].indexOf(prop) >= 0) {
          state.auth[prop] = payload[prop] || state.auth[prop]
        }
      }
    },
    removeSurvey(state, {sessionId, surveyId}){
      const activeSession = state.sessions.find(s => s.date === sessionId)
      activeSession.surveySummaries.forEach( (s,i)=>{
        if(s.date === surveyId){
          activeSession.surveySummaries.splice(i,1)
        }
      })
    }
  },
  actions: {
    cancelEdit({commit,state}){
      commit('setEditMode', false)
      commit('updateSurvey', cleanFullSurvey())
    },
    editSurvey({commit, state}, {surveyId}){
      let survey = surveys[state.activeSession].find(s=>s.date===surveyId)
      commit('setEditMode', true)
      commit('updateSurvey', survey)
    },
    deleteSurvey({commit,state}, surveyInfo){
      return storage.deleteSurvey(surveyInfo.sessionId, surveyInfo.surveyId).then(()=>{
          commit('removeSurvey', surveyInfo)
      })
    },
    searchId ({commit, state}) {
      const activeSession = state.sessions.find(s => s.date === state.activeSession)
      const record = rosters[state.activeSession].find(r => r.studentId === state.survey.studentId)
      if (!record) {
        commit('setAlert', {state: 'danger', message: 'Student id not found'})
      } else {
        let update = {}
        for (const key in record) {
          if (['firstName', 'lastName', 'birthDate', 'teacher', 'grade', 'room'].indexOf(key) >= 0) {
            update[key] = record[key]
          }
        }
        commit('updateSurvey', update)
      }
    },
    selectFile ({commit}, file) {
      return new Promise((resolve, reject) => {
        papa.parse(file, {
          header: true,
          skipEmptyLines: true,
          complete: function (results) {
            resolve(results.data)
          },
          error: function (err) {
            reject(err)
          }
        })
      }).then(function (results) {
        console.log(results)
        commit('updateFile', results)
      }).catch(handleError(commit))
    },
    newSession ({state, commit, dispatch}) {
      let session = {
        date: Date.now(),
        siteId: state.newSession.selectedItem.id,
        siteName: state.newSession.selectedItem.name,
        surveySummaries: [],
        recordsLength: state.newSession.selectedFile.length
      }
      rosters[session.date] = state.newSession.selectedFile.slice(0)
      surveys[session.date] = []
      commit('updateFile', [])
      commit('addSession', session)
      dispatch('activateSession', {id: session.date})
      return dispatch('saveSession', {id: session.date})
    },
    activateSession ({commit}, {id}) {
      commit('activateSession', id)
      commit('selectSite', null)
      commit('updateFile', [])
      commit('updateSurvey', cleanFullSurvey())
      commit('updateSurvey', {dentist: ''})
      commit('setEditMode', false)
    },
    saveSession ({state, commit}, {id}) {
      const session = Vue.util.extend({}, state.sessions.find(session => session.date === id))
      session.records = rosters[session.date]
      return storage.saveSession({date:session.date, siteId:session.siteId, siteName:session.siteName}).catch(handleError(commit))
    },
    saveSurvey ({state, commit}) {
      let survey = {...state.survey}
      let newSurvey = {
        date: Date.now(),
        sessionId: state.activeSession,
        grade: survey.grade,
        room: survey.room,
        teacher: survey.teacher,
        firstName: survey.firstName,
        lastName: survey.lastName,
        comment: survey.comment,
        studentId: survey.studentId,
        birthDate: survey.birthDate,
        checkList: survey.checkList.slice(0),
        babyTeeth: survey.babyTeeth.slice(0),
        permanentTeeth: survey.permanentTeeth.slice(0),
        signature: survey.signature.slice(0),
        dentist: survey.dentist
      }
      commit('updateSurvey', cleanFullSurvey())
      let updateSurvey = state.editMode?survey:newSurvey
      commit('setEditMode', false)
      commit('addSurveySummary', updateSurvey)
      return storage.saveSurvey(updateSurvey).then({})

    },
    loadSessions ({commit}) {
      storage.loadSessions().then(function (sessions) {
        sessions.map(function (session) {
          rosters[session.date] = session.records.slice(0)
          surveys[session.date] = session.surveys.slice(0)
          session.recordsLength = rosters[session.date].length
          session.surveySummaries = session.surveys.map(getSurveySummary)
          delete session.records
          delete session.surveys
          commit('addSession', session)
        })
        commit('finishLoading')
      }).catch(handleError(commit))
    },
    loadConfig ({commit}) {
      storage.loadConfig().then((config) => {
        commit('updateAuth', config)
      }).catch(handleError(commit))
    },
    saveConfig ({state, commit}) {
      return storage.saveConfig(state.auth).catch(handleError(commit))
    },
    loadSites ({commit}) {
      storage.loadSites().then(sites => {
        commit('replaceSites', sites)
      }).catch(handleError(commit))
    },
    saveSites ({state, commit}) {
      return storage.saveSites(state.sites).catch(handleError(commit))
    },
    async getAuth ({commit, dispatch, state}) {
      let auth = state.auth
      if (!auth.refreshToken) {
        auth = await login(auth)
        commit('updateAuth', auth)
        dispatch('saveConfig')
      }
      auth = await getAccessToken(auth)
      commit('updateAuth', auth)
      dispatch('saveConfig')
      return auth
    },
    getSites ({dispatch, commit}) {
      dispatch('_getSites').catch(handleError(commit))
    },
    async _getSites ({commit, dispatch, state}) {
      let auth = await dispatch('getAuth')
      let response = null
      try {
        response = await access.getSites()
      } catch (error) {
        if (error.length &&
            error[0].errorCode &&
            error[0].errorCode == 'INVALID_SESSION_ID') {
          commit('clearAuthParams', ['accessToken', 'refreshToken'])
          auth = await dispatch('getAuth')
          response = await access.getSites()
        } else {
          throw error
        }
      }
      let sites = response.records.map(function (record) {
        return {
          id: record.Id,
          name: record.Name,
          owner: record.Owner ? record.Owner.Name : 'None',
          type: record.Type
        }
      })
      commit('replaceSites', {downloadDate: Date.now(), list: sites})
      dispatch('saveSites')
      return sites
    },
    setAlertWithTimeout ({commit}, alertInfo) {
      commit('setAlert', alertInfo)
      setTimeout(function () {
        commit('clearAlert')
      }, 3000)
    },
    exportSession ({state, commit}, {id, sessionUrl}) {
      try {
        let selectedSession = state.sessions.find(s => s.date == id)
        const files = dialog.showOpenDialog({
          title: 'Export Directory',
          properties: ['openDirectory', 'createDirectory']
        })
        if (!files) {
          return Promise.reject(new Error('User canceled.'))
        }
        const exportPath = files[0]
        const outputRecords = surveys[id].map(function (survey) {
          const record = rosters[id].find(r => r.studentId == survey.studentId)
          return getOutputRecord(selectedSession.siteId, selectedSession.siteName, survey, record)
        })
        let csvPromise = storage.saveCSV(`${exportPath}/output.csv`, outputRecords)
        let pdfPromise = generatePDFs(exportPath, sessionUrl)
        return Promise.all([csvPromise, pdfPromise])
      } catch (error) {
        return Promise.reject(error)
      }
    }
  },
  strict: process.env.NODE_ENV !== 'production'
})

store.dispatch('loadConfig')
store.dispatch('loadSites')
store.dispatch('loadSessions')
export default store
<commit_msg>Fix issue with how we edit files<commit_after>import Vue from 'vue'
import Vuex from 'vuex'
import {remote} from 'electron'
import OAuthElectron from 'salesforce/oauth'
import access from 'salesforce/access'
import storage from '../storage'
import papa from 'papaparse'
let {app, dialog, BrowserWindow} = remote

import {checkLevels} from '../checks'
import babyTeethUrl from '../assets/primary_teeth.jpg'
import permanentTeethUrl from '../assets/permanent_teeth.jpg'

let rosters = {}
let surveys = {}

Vue.use(Vuex)
function generatePDFs (exportPath, sessionUrl) {
  return new Promise(function (resolve, reject) {
    let win = new BrowserWindow({useContentSize: true, show: true})
    win.loadURL(sessionUrl)
    win.on('page-title-updated', function (event, title) {
      if (title === 'finish') {
        win.close()
        win = null
        resolve()
      }
      let titleParts = title.split(':')

      if (titleParts[0] === 'print') {
        const followUp = titleParts[1] == 'y'
        const surveyName = titleParts[2]
        win.webContents.printToPDF({}, function (err, data) {
          if (err) {
            win.close()
            win = null
            reject(err)
          }
          storage.savePDF(exportPath, followUp, surveyName, data)
          win.webContents.send('status', 'ready')
        })
      }
    })
  })
}

function getChildAge (survey, record) {
  if (survey.birthDate) {
    const ageSeconds = Date.now() - Date.parse(survey.birthDate)
    console.log(ageSeconds)
    const age = new Date(ageSeconds).getUTCFullYear() - 1970
    return age
  }
  if (record && record.childsAge) {
    return record.childsAge
  }
  return null
}

function getOutputRecord (siteId, siteName, survey, record) {
  let prioritySet = new Set(survey.checkList.map(c => parseInt(c[0])))
  const needsFollowUp = prioritySet.has(2) || prioritySet.has(3)

  let output = {
    'Lead type': 'DentalFirst',
    'Student ID': survey.studentId,
    'School Site:Account ID': siteId,
    'School Site:Account Name': siteName,
    'First Name': survey.firstName,
    'Last Name': survey.lastName,
    'Teacher': survey.teacher,
    'Date of Birth': survey.birthDate,
    'Priority': [...prioritySet].sort().reverse()[0],
    'Follow-up Required': needsFollowUp ? 'Yes' : 'No',
    'Status': needsFollowUp ? 'Open - Outreach Required' : 'Closed - Passed screening',
    'PDF Name': `${survey.studentId}.${new Date(survey.date).toISOString().split('T')[0]}.pdf`,
    'Screening Outreach': '',
    'Date Child Screened': new Date(survey.date).toISOString().split('T')[0],
    'Dentist Name': survey.dentist
  }

  const childsAge = getChildAge(survey, record)
  if (childsAge) {
    output["Child's Age"] = childsAge
  }
  if (record && record.zipCode) {
    output['Zip Code'] = record.zipCode
  }
  if (record && record.gender) {
    output['Gender'] = record.gender
  }
  if (record && record.preferredLanguage) {
    output['Preferred Language'] = record.preferredLanguage
  }
  if (record && record.stateId) {
    output['State Identifier'] = record.stateId
  }
  return output
}
function getSurveySummary(survey){
  return {
    date:survey.date,
    studentId:survey.studentId,
    name:`${survey.firstName} ${survey.lastName}`
  }
}

function cleanSurvey () {
  return {
    firstName: '',
    lastName: '',
    studentId: '',
    birthDate: '',
    comment: '',
    checkList: [],
    babyTeeth: [],
    permanentTeeth: [],
    signature: [],
    date: 0
  }
}
function cleanFullSurvey () {
  return Object.assign(cleanSurvey(), {
    room: '',
    teacher: '',
    grade: ''
  })
}

let handleError = function (commit) {
  return function (error) {
    commit('setAlert', {state: 'danger', message: error.message})
  }
}

let login = function (auth) {
  let oauth = new OAuthElectron(
    {
      appId: auth.appId,
      oauthCallbackURL: auth.oauthCallbackURL
    })
  return oauth.login()
}
let getAccessToken = function (auth) {
  access.init(auth)
  if (!auth.accessToken) {
    return access.refreshAccessToken().then(function (accessToken) {
      const newAuth = Object.assign({}, auth, {'accessToken': accessToken})
      access.init(newAuth)
      return newAuth
    })
  }
  return auth
}

let store = new Vuex.Store({
  state: {
    editMode: false,
    unique: true,
    survey: {
      babyTeethUrl: babyTeethUrl,
      permanentTeethUrl: permanentTeethUrl,
      grade: '',
      room: '',
      teacher: '',
      firstName: '',
      lastName: '',
      comment: '',
      studentId: '',
      birthDate: '',
      checkList: [],
      babyTeeth: [],
      permanentTeeth: [],
      signature: [],
      dentist: '',
      date: 0
    },
    checkLevels: checkLevels,
    alert: {state: 'success', message: ''},
    auth: {
      appId: null,
      instanceURL: null,
      accessToken: null,
      refreshToken: null,
      userId: null,
      oauthCallbackURL: null
    },
    sites: {downloadDate: null, list: []},
    newSession: {
      selectedItem: null,
      selectedFile: []
    },
    sessions: [],
    activeSession: -1,
    loaded: false
  },
  mutations: {
    setEditMode(state, payload){
      state.editMode = payload
    },
    finishLoading (state) {
      state.loaded = true
    },
    addLine (state, lineMap) {
      for (var key in lineMap) {
        if (lineMap.hasOwnProperty(key)) {
          state.survey[key].push(lineMap[key])
        }
      }
    },
    updateSurvey (state, update) {
      if (update.studentId && state.activeSession >= 0) {
        state.unique = !surveys[state.activeSession].some(s => s.studentId === update.studentId)
      }
      Object.assign(state.survey, update)
    },
    loadSurvey (state, {session, index}) {
      console.log('Loading Survey')
      Object.assign(state.survey, surveys[session][index])
    },
    activateSession (state, id) {
      state.activeSession = id
    },
    addSession (state, session) {
      state.sessions.splice(0, 0, session)
    },
    addSurvey (state, survey) {
      let session = state.sessions.find(s => s.date == state.activeSession)
      let index = session.surveySummaries.findIndex(s=>s.date == survey.date)
      if(index >=0){
        session.surveySummaries.splice(index,1,getSurveySummary(survey))
      } else{
        session.surveySummaries.push(getSurveySummary(survey))
      }
      let surveyIndex = surveys[state.activeSession].findIndex(s=>s.date == survey.date)
      if(surveyIndex >=0){
        surveys[state.activeSession].splice(surveyIndex,1, survey)
      } else {
        surveys[state.activeSession].push(survey)
      }

    },
    updateFile (state, file) {
      state.newSession.selectedFile = file
    },
    selectSite (state, site) {
      state.newSession.selectedItem = site
    },
    replaceSites (state, sites) {
      state.sites = sites
    },
    clearAlert (state) {
      console.log('clear Alert')
      state.alert.message = ''
    },
    setAlert (state, alert) {
      console.log(`Set alert:${alert.state}-${alert.message}`)
      state.alert.state = alert.state
      state.alert.message = alert.message
    },
    clearAuthParams (state, params) {
      console.log(`clearing ${params}`)
      for (const key of params) {
        state.auth[key] = null
      }
    },
    updateAuth (state, payload) {
      for (const prop in payload) {
        if ([
          'instanceURL',
          'accessToken',
          'appId',
          'userId',
          'refreshToken',
          'oauthCallbackURL'
        ].indexOf(prop) >= 0) {
          state.auth[prop] = payload[prop] || state.auth[prop]
        }
      }
    },
    removeSurvey(state, {sessionId, surveyId}){
      const activeSession = state.sessions.find(s => s.date === sessionId)
      activeSession.surveySummaries.forEach( (s,i)=>{
        if(s.date === surveyId){
          activeSession.surveySummaries.splice(i,1)
        }
      })
    }
  },
  actions: {
    cancelEdit({commit,state}){
      commit('setEditMode', false)
      commit('updateSurvey', cleanFullSurvey())
    },
    editSurvey({commit, state}, {surveyId}){
      let survey = surveys[state.activeSession].find(s=>s.date===surveyId)
      commit('setEditMode', true)
      commit('updateSurvey', survey)
    },
    deleteSurvey({commit,state}, surveyInfo){
      return storage.deleteSurvey(surveyInfo.sessionId, surveyInfo.surveyId).then(()=>{
          commit('removeSurvey', surveyInfo)
      })
    },
    searchId ({commit, state}) {
      const activeSession = state.sessions.find(s => s.date === state.activeSession)
      const record = rosters[state.activeSession].find(r => r.studentId === state.survey.studentId)
      if (!record) {
        commit('setAlert', {state: 'danger', message: 'Student id not found'})
      } else {
        let update = {}
        for (const key in record) {
          if (['firstName', 'lastName', 'birthDate', 'teacher', 'grade', 'room'].indexOf(key) >= 0) {
            update[key] = record[key]
          }
        }
        commit('updateSurvey', update)
      }
    },
    selectFile ({commit}, file) {
      return new Promise((resolve, reject) => {
        papa.parse(file, {
          header: true,
          skipEmptyLines: true,
          complete: function (results) {
            resolve(results.data)
          },
          error: function (err) {
            reject(err)
          }
        })
      }).then(function (results) {
        console.log(results)
        commit('updateFile', results)
      }).catch(handleError(commit))
    },
    newSession ({state, commit, dispatch}) {
      let session = {
        date: Date.now(),
        siteId: state.newSession.selectedItem.id,
        siteName: state.newSession.selectedItem.name,
        surveySummaries: [],
        recordsLength: state.newSession.selectedFile.length
      }
      rosters[session.date] = state.newSession.selectedFile.slice(0)
      surveys[session.date] = []
      commit('updateFile', [])
      commit('addSession', session)
      dispatch('activateSession', {id: session.date})
      return dispatch('saveSession', {id: session.date})
    },
    activateSession ({commit}, {id}) {
      commit('activateSession', id)
      commit('selectSite', null)
      commit('updateFile', [])
      commit('updateSurvey', cleanFullSurvey())
      commit('updateSurvey', {dentist: ''})
      commit('setEditMode', false)
    },
    saveSession ({state, commit}, {id}) {
      const session = Vue.util.extend({}, state.sessions.find(session => session.date === id))
      session.records = rosters[session.date]
      return storage.saveSession({date:session.date, siteId:session.siteId, siteName:session.siteName}).catch(handleError(commit))
    },
    saveSurvey ({state, commit}) {
      let survey = {...state.survey}
      let newSurvey = {
        date: Date.now(),
        sessionId: state.activeSession,
        grade: survey.grade,
        room: survey.room,
        teacher: survey.teacher,
        firstName: survey.firstName,
        lastName: survey.lastName,
        comment: survey.comment,
        studentId: survey.studentId,
        birthDate: survey.birthDate,
        checkList: survey.checkList.slice(0),
        babyTeeth: survey.babyTeeth.slice(0),
        permanentTeeth: survey.permanentTeeth.slice(0),
        signature: survey.signature.slice(0),
        dentist: survey.dentist
      }
      commit('updateSurvey', cleanFullSurvey())
      let updateSurvey = state.editMode?survey:newSurvey
      commit('setEditMode', false)
      commit('addSurvey', updateSurvey)
      return storage.saveSurvey(updateSurvey)
    },
    loadSessions ({commit}) {
      storage.loadSessions().then(function (sessions) {
        sessions.map(function (session) {
          rosters[session.date] = session.records.slice(0)
          surveys[session.date] = session.surveys.slice(0)
          session.recordsLength = rosters[session.date].length
          session.surveySummaries = session.surveys.map(getSurveySummary)
          delete session.records
          delete session.surveys
          commit('addSession', session)
        })
        commit('finishLoading')
      }).catch(handleError(commit))
    },
    loadConfig ({commit}) {
      storage.loadConfig().then((config) => {
        commit('updateAuth', config)
      }).catch(handleError(commit))
    },
    saveConfig ({state, commit}) {
      return storage.saveConfig(state.auth).catch(handleError(commit))
    },
    loadSites ({commit}) {
      storage.loadSites().then(sites => {
        commit('replaceSites', sites)
      }).catch(handleError(commit))
    },
    saveSites ({state, commit}) {
      return storage.saveSites(state.sites).catch(handleError(commit))
    },
    async getAuth ({commit, dispatch, state}) {
      let auth = state.auth
      if (!auth.refreshToken) {
        auth = await login(auth)
        commit('updateAuth', auth)
        dispatch('saveConfig')
      }
      auth = await getAccessToken(auth)
      commit('updateAuth', auth)
      dispatch('saveConfig')
      return auth
    },
    getSites ({dispatch, commit}) {
      dispatch('_getSites').catch(handleError(commit))
    },
    async _getSites ({commit, dispatch, state}) {
      let auth = await dispatch('getAuth')
      let response = null
      try {
        response = await access.getSites()
      } catch (error) {
        if (error.length &&
            error[0].errorCode &&
            error[0].errorCode == 'INVALID_SESSION_ID') {
          commit('clearAuthParams', ['accessToken', 'refreshToken'])
          auth = await dispatch('getAuth')
          response = await access.getSites()
        } else {
          throw error
        }
      }
      let sites = response.records.map(function (record) {
        return {
          id: record.Id,
          name: record.Name,
          owner: record.Owner ? record.Owner.Name : 'None',
          type: record.Type
        }
      })
      commit('replaceSites', {downloadDate: Date.now(), list: sites})
      dispatch('saveSites')
      return sites
    },
    setAlertWithTimeout ({commit}, alertInfo) {
      commit('setAlert', alertInfo)
      setTimeout(function () {
        commit('clearAlert')
      }, 3000)
    },
    exportSession ({state, commit}, {id, sessionUrl}) {
      try {
        let selectedSession = state.sessions.find(s => s.date == id)
        const files = dialog.showOpenDialog({
          title: 'Export Directory',
          properties: ['openDirectory', 'createDirectory']
        })
        if (!files) {
          return Promise.reject(new Error('User canceled.'))
        }
        const exportPath = files[0]
        const outputRecords = surveys[id].map(function (survey) {
          const record = rosters[id].find(r => r.studentId == survey.studentId)
          return getOutputRecord(selectedSession.siteId, selectedSession.siteName, survey, record)
        })
        let csvPromise = storage.saveCSV(`${exportPath}/output.csv`, outputRecords)
        let pdfPromise = generatePDFs(exportPath, sessionUrl)
        return Promise.all([csvPromise, pdfPromise])
      } catch (error) {
        return Promise.reject(error)
      }
    }
  },
  strict: process.env.NODE_ENV !== 'production'
})

store.dispatch('loadConfig')
store.dispatch('loadSites')
store.dispatch('loadSessions')
export default store
<|endoftext|>


<commit_before>$(function() {
    /***************
        getTop8
    ***************/
    // On page load: Ajax GET request to '/api/getTop8'
    $.ajax({
        type: 'GET',
        url: '/api/getTop8',
        data: 'json',
        datatype: 'application/json',
        success: function(data) {
            // On success below jquery fills rows of Top8 table with data retrieved from /api/getTop8
            var hash = JSON.parse(data);
            for (var i = 0; i <= 7; i++) {
                if (typeof hash[i] === 'undefined') {
                    $('td.playlist-name:eq(' + i + ')').text('N/A');
                    $('td.playlist-popularity:eq(' + i + ')').text('N/A');
                } else {
                    $('td.playlist-name:eq(' + i + ')').text(formatPlaylist(hash[i]['title']));
                    // $('td.playlist-name:eq(' + i + ')').text(hash[i]['title'].replace(/##/g,', ').replace(/\\/g,''));
                    $('td.playlist-popularity:eq(' + i + ')').text(hash[i]['popularity']);
                }
            }
        },
        error: function(data) {
            // alert('Failed to retrieve Top8 content');
            console.log('Failed to retrieve Top8 content');
        }
    });
    /********************
        suggestPlaylist
    *********************/
    function suggestPlaylist() {
        var data = {};
        data['song'] = $('#enter-song').val(); // Gets the song name text
        $.ajax({
            type: 'POST',
            url: '/api/suggestPlaylist',
            data: JSON.stringify(data),
            dataType: "text",
            contentType: 'application/json',
            success: function(data) {
                // On success, below jquery suggests playlist with highest popularity containing entered song
                console.log('Success!');
                var hash = JSON.parse(data);
                $('#suggested-playlist').text(formatPlaylist(hash['mostPopular']));
            },
            error: function(data) {
                // alert('Failed to retrieve Top8 content');
                console.log('Failed to retrieve suggestPlaylist content');
                $('#suggested-playlist').text('No Playlist Found');
            }
        });
    }
    /********************
        getAutocomplete
    *********************/
    function autocomplete() {
        var data = {};
        data['song'] = $('#enter-song').val(); // Gets the song name text
        $.ajax({
            type: 'POST',
            url: '/api/getAutocomplete',
            data: JSON.stringify(data),
            dataType: "text",
            contentType: 'application/json',
            success: function(data) {
                // On success, below jquery suggests playlist with highest popularity containing entered song
                console.log('Success!');
                var hash = JSON.parse(data);
                console.log(hash); // Helps to know if the proper songs are being returned
                for (var i = 0; i <= 3; i++) {
                    if (typeof hash[i] === 'undefined') {
                        // An autocomplete option is empty if the hash did not return the
                        // specified index
                        $('option.song-autocomplete:eq(' + i + ')').text('');
                    } else { // Otherwise fill the autocomplete option with the corresponding song name
                        $('option.song-autocomplete:eq(' + i + ')').text(hash[i].replace(/\\/g, ''));
                    }
                }
            },
            error: function(data) {
                console.log('Failed to retrieve getAutocomplete content');
                for (var i = 0; i <= 3; i++) {
                    $('option.song-autocomplete:eq(' + i + ')').text('');
                }
            }
        });
    }
    /*************************************
     * Helper functions
     **************************************/
    // keypress jquery event handler
    $('#enter-song').bind("keyup", function(e) {
        if (e.keyCode == 13) { // start post method after enter key pressed
            e.preventDefault();
            suggestPlaylist();
        }
        autocomplete();
    });
    // Formats the playlist to remove ##'s and \'s
    function formatPlaylist(playlist) {
        playlist = playlist.slice(0, -2).replace(/##/g, ', ').replace(/\\/g, '');
        return playlist;
    }
});
<commit_msg>Suggested playlist now updates on every keyup, not just enter key.<commit_after>$(function() {
    /***************
        getTop8
    ***************/
    // On page load: Ajax GET request to '/api/getTop8'
    $.ajax({
        type: 'GET',
        url: '/api/getTop8',
        data: 'json',
        datatype: 'application/json',
        success: function(data) {
            // On success below jquery fills rows of Top8 table with data retrieved from /api/getTop8
            var hash = JSON.parse(data);
            for (var i = 0; i <= 7; i++) {
                if (typeof hash[i] === 'undefined') {
                    $('td.playlist-name:eq(' + i + ')').text('N/A');
                    $('td.playlist-popularity:eq(' + i + ')').text('N/A');
                } else {
                    $('td.playlist-name:eq(' + i + ')').text(formatPlaylist(hash[i]['title']));
                    // $('td.playlist-name:eq(' + i + ')').text(hash[i]['title'].replace(/##/g,', ').replace(/\\/g,''));
                    $('td.playlist-popularity:eq(' + i + ')').text(hash[i]['popularity']);
                }
            }
        },
        error: function(data) {
            // alert('Failed to retrieve Top8 content');
            console.log('Failed to retrieve Top8 content');
        }
    });
    /********************
        suggestPlaylist
    *********************/
    function suggestPlaylist() {
        var data = {};
        data['song'] = $('#enter-song').val(); // Gets the song name text
        $.ajax({
            type: 'POST',
            url: '/api/suggestPlaylist',
            data: JSON.stringify(data),
            dataType: "text",
            contentType: 'application/json',
            success: function(data) {
                // On success, below jquery suggests playlist with highest popularity containing entered song
                console.log('Success!');
                var hash = JSON.parse(data);
                $('#suggested-playlist').text(formatPlaylist(hash['mostPopular']));
            },
            error: function(data) {
                // alert('Failed to retrieve Top8 content');
                console.log('Failed to retrieve suggestPlaylist content');
                $('#suggested-playlist').text('No Playlist Found');
            }
        });
    }
    /********************
        getAutocomplete
    *********************/
    function autocomplete() {
        var data = {};
        data['song'] = $('#enter-song').val(); // Gets the song name text
        $.ajax({
            type: 'POST',
            url: '/api/getAutocomplete',
            data: JSON.stringify(data),
            dataType: "text",
            contentType: 'application/json',
            success: function(data) {
                // On success, below jquery suggests playlist with highest popularity containing entered song
                console.log('Success!');
                var hash = JSON.parse(data);
                console.log(hash); // Helps to know if the proper songs are being returned
                for (var i = 0; i <= 3; i++) {
                    if (typeof hash[i] === 'undefined') {
                        // An autocomplete option is empty if the hash did not return the
                        // specified index
                        $('option.song-autocomplete:eq(' + i + ')').text('');
                    } else {
                        // Otherwise fill the autocomplete option with the corresponding song name
                        $('option.song-autocomplete:eq(' + i + ')').text(hash[i].replace(/\\/g, ''));
                    }
                }
            },
            error: function(data) {
                console.log('Failed to retrieve getAutocomplete content');
                for (var i = 0; i <= 3; i++) {
                    $('option.song-autocomplete:eq(' + i + ')').text('');
                }
            }
        });
    }
    /*************************************
     * Helper functions
     **************************************/
    // keypress jquery event handler
    $('#enter-song').bind("keyup", function(e) {
        // if (e.keyCode == 13) { // start post method after enter key pressed
        //     e.preventDefault();
        //     suggestPlaylist();
        // }
        suggestPlaylist();
        autocomplete();
    });
    // Formats the playlist to remove ##'s and \'s
    function formatPlaylist(playlist) {
        playlist = playlist.slice(0, -2).replace(/##/g, ', ').replace(/\\/g, '');
        return playlist;
    }
});
<|endoftext|>


<commit_before>
/**
 * Node shims.
 *
 * These are meant only to allow
 * mocha.js to run untouched, not
 * to allow running node code in
 * the browser.
 */

process = {};
process.exit = function(status){};
process.stdout = {};
global = window;

/**
 * next tick implementation.
 */

process.nextTick = (function(){
  // postMessage behaves badly on IE8
  if (window.ActiveXObject || !window.postMessage) {
    return function(fn){ fn() };
  }

  // based on setZeroTimeout by David Baron
  // - http://dbaron.org/log/20100309-faster-timeouts
  var timeouts = []
    , name = 'mocha-zero-timeout'

  return function(fn){
    timeouts.push(fn);
    window.postMessage(name, '*');
    window.addEventListener('message', function(e){
      if (e.source == window && e.data == name) {
        if (e.stopPropagation) e.stopPropagation();
        if (timeouts.length) timeouts.shift()();
      }
    }, true);
  }
})();

/**
 * Remove uncaughtException listener.
 */

process.removeListener = function(e){
  if ('uncaughtException' == e) {
    window.onerror = null;
  }
};

/**
 * Implements uncaughtException listener.
 */

process.on = function(e, fn){
  if ('uncaughtException' == e) {
    window.onerror = fn;
  }
};

/**
 * Expose mocha.
 */

window.mocha = require('mocha');

// boot
;(function(){
  var suite = new mocha.Suite('', new mocha.Context)
    , utils = mocha.utils
    , Reporter = mocha.reporters.HTML

  $(function(){
    $('code').each(function(){
      $(this).html(highlight($(this).text()));
    });
  });

  /**
   * Highlight the given string of `js`.
   */

  function highlight(js) {
    return js
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/\/\/(.*)/gm, '//$1')
      .replace(/('.*')/gm, '$1')
      .replace(/(\d+\.\d+)/gm, '$1')
      .replace(/(\d+)/gm, '$1')
      .replace(/\bnew *(\w+)/gm, 'new $1')
      .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1')
  }

  /**
   * Parse the given `qs`.
   */

  function parse(qs) {
    return utils.reduce(qs.replace('?', '').split('&'), function(obj, pair){
        var i = pair.indexOf('=')
          , key = pair.slice(0, i)
          , val = pair.slice(++i);

        obj[key] = decodeURIComponent(val);
        return obj;
      }, {});
  }

  /**
   * Setup mocha with the give `ui` name.
   */

  mocha.setup = function(ui){
    ui = mocha.interfaces[ui];
    if (!ui) throw new Error('invalid mocha interface "' + ui + '"');
    ui(suite);
    suite.emit('pre-require', window);
  };

  /**
   * Run mocha, returning the Runner.
   */

  mocha.run = function(){
    suite.emit('run');
    var runner = new mocha.Runner(suite);
    var reporter = new Reporter(runner);
    var query = parse(window.location.search || "");
    if (query.grep) runner.grep(new RegExp(query.grep));
    runner.on('end', function(){
      $('code').each(function(){
        $(this).html(highlight($(this).text()));
      });
    });
    return runner.run();
  };
})();
<commit_msg>Fixed syntax highlighting of strings, non-greedy<commit_after>
/**
 * Node shims.
 *
 * These are meant only to allow
 * mocha.js to run untouched, not
 * to allow running node code in
 * the browser.
 */

process = {};
process.exit = function(status){};
process.stdout = {};
global = window;

/**
 * next tick implementation.
 */

process.nextTick = (function(){
  // postMessage behaves badly on IE8
  if (window.ActiveXObject || !window.postMessage) {
    return function(fn){ fn() };
  }

  // based on setZeroTimeout by David Baron
  // - http://dbaron.org/log/20100309-faster-timeouts
  var timeouts = []
    , name = 'mocha-zero-timeout'

  return function(fn){
    timeouts.push(fn);
    window.postMessage(name, '*');
    window.addEventListener('message', function(e){
      if (e.source == window && e.data == name) {
        if (e.stopPropagation) e.stopPropagation();
        if (timeouts.length) timeouts.shift()();
      }
    }, true);
  }
})();

/**
 * Remove uncaughtException listener.
 */

process.removeListener = function(e){
  if ('uncaughtException' == e) {
    window.onerror = null;
  }
};

/**
 * Implements uncaughtException listener.
 */

process.on = function(e, fn){
  if ('uncaughtException' == e) {
    window.onerror = fn;
  }
};

/**
 * Expose mocha.
 */

window.mocha = require('mocha');

// boot
;(function(){
  var suite = new mocha.Suite('', new mocha.Context)
    , utils = mocha.utils
    , Reporter = mocha.reporters.HTML

  $(function(){
    $('code').each(function(){
      $(this).html(highlight($(this).text()));
    });
  });

  /**
   * Highlight the given string of `js`.
   */

  function highlight(js) {
    return js
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/\/\/(.*)/gm, '//$1')
      .replace(/('.*?')/gm, '$1')
      .replace(/(\d+\.\d+)/gm, '$1')
      .replace(/(\d+)/gm, '$1')
      .replace(/\bnew *(\w+)/gm, 'new $1')
      .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1')
  }

  /**
   * Parse the given `qs`.
   */

  function parse(qs) {
    return utils.reduce(qs.replace('?', '').split('&'), function(obj, pair){
        var i = pair.indexOf('=')
          , key = pair.slice(0, i)
          , val = pair.slice(++i);

        obj[key] = decodeURIComponent(val);
        return obj;
      }, {});
  }

  /**
   * Setup mocha with the give `ui` name.
   */

  mocha.setup = function(ui){
    ui = mocha.interfaces[ui];
    if (!ui) throw new Error('invalid mocha interface "' + ui + '"');
    ui(suite);
    suite.emit('pre-require', window);
  };

  /**
   * Run mocha, returning the Runner.
   */

  mocha.run = function(){
    suite.emit('run');
    var runner = new mocha.Runner(suite);
    var reporter = new Reporter(runner);
    var query = parse(window.location.search || "");
    if (query.grep) runner.grep(new RegExp(query.grep));
    runner.on('end', function(){
      $('code').each(function(){
        $(this).html(highlight($(this).text()));
      });
    });
    return runner.run();
  };
})();
<|endoftext|>


<commit_before>/*
 * Context.js
 * Fabian Irsara
 * Copyright 2015, Licensed GPL & MIT
 */
define(function(){
  /**
   * helper class for handling event contexts
   * @namespace Context
   * @memberof utils
   */
  var Context = {};

  /**
   * mixes context values in to defined class
   *
   * @method mixin
   * @memberof utils.Context
   * @param {class} target where context should be mixed in
   **/
  Context.mixin = function(target){
    // cache class prototype
    var p = target.prototype;
    var i, _len, bound;

    /**
     * binds a function to the target
     *
     * @method __bind
     * @memberof utils.Context
     * @param {function} fct that should be bound
     **/
    p.__bind = function(fct){
      // initialize context storage if not found yet
      if (! this.__context) {
        /**
         * stored bound function contexts
         * @memberof utils.Context
         * @instance
         * @public
         * @var {Array} __context
         **/
        this.__context = [];
      }

      // find stored method
      for (i = 0, _len = this.__context.length; i < _len; i++) {
        if (this.__context[i].src === fct || this.__context[i].bound === fct) {
          return this.__context[i].bound;
        }
      }

      bound = fct.bind(this);

      this.__context.push({src: fct, bound: bound});

      return bound;
    };

    /**
     * delays a function call on the target instance
     *
     * @method delay
     * @memberof utils.Context
     * @param {function} fct that should be called after the delay
     * @param {Number} timeout after which function should be called
     * @param {Array} params that should be called on callback after invocation
     **/
    p.delay = function(fct, timeout, params){
      if (! this.__delays) {
        /**
         * stored delays
         * @memberof utils.Context
         * @instance
         * @public
         * @var {Array} __delays
         **/
        this.__delays = [];
      }

      this.stopDelay(fct);

      var bound = this.__bind(fct);
      var _this = this;

      var callback = function(){
        _this.stopDelay(fct);
        bound.apply(_this, params);
      };

      var timeoutId = setTimeout(callback, timeout);
      this.__delays.push({fct: bound, uid: timeoutId});

      return timeoutId;
    };

    /**
     * stops the delay of a specific function call on the target instance.

     * if no fct is defined automatically stops all delays
     *
     * @method stopDelay
     * @memberof utils.Context
     * @param {function} fct that should be called after the delay (optional)
     **/
    p.stopDelay = function(fct){
      if (this.__delays) {
        var i, _len;

        if (fct) {
          var bound = this.__bind(fct);

          for (i = 0, _len = this.__delays.length; i < _len; i++) {
            if (this.__delays[i].fct === bound) {
              clearTimeout(this.__delays[i].uid);
              this.__delays.splice(i, 1);
              break;
            }
          }
        } else {
          for (i = 0, _len = this.__delays.length; i < _len; i++) {
            clearTimeout(this.__delays[i].uid);
          }

          this.__delays = [];
        }
      }
    };
  };

  return Context;
});
<commit_msg>optimize<commit_after>/*
 * Context.js
 * Fabian Irsara
 * Copyright 2015, Licensed GPL & MIT
 */
define(function(){
  /**
   * helper class for handling event contexts
   * @namespace Context
   * @memberof utils
   */
  var Context = {};

  /**
   * mixes context values in to defined class
   *
   * @method mixin
   * @memberof utils.Context
   * @param {class} target where context should be mixed in
   **/
  Context.mixin = function(target){
    // cache class prototype
    var p = target.prototype;

    /**
     * binds a function to the target
     *
     * @method __bind
     * @memberof utils.Context
     * @param {function} fct that should be bound
     **/
    p.__bind = function(fct){
      var i, _len, bound;

      // initialize context storage if not found yet
      if (! this.__context) {
        /**
         * stored bound function contexts
         * @memberof utils.Context
         * @instance
         * @public
         * @var {Array} __context
         **/
        this.__context = [];
      }

      // find stored method
      for (i = 0, _len = this.__context.length; i < _len; i++) {
        if (this.__context[i].src === fct || this.__context[i].bound === fct) {
          return this.__context[i].bound;
        }
      }

      bound = fct.bind(this);

      this.__context.push({src: fct, bound: bound});

      return bound;
    };

    /**
     * delays a function call on the target instance
     *
     * @method delay
     * @memberof utils.Context
     * @param {function} fct that should be called after the delay
     * @param {Number} timeout after which function should be called
     * @param {Array} params that should be called on callback after invocation
     **/
    p.delay = function(fct, timeout, params){
      if (! this.__delays) {
        /**
         * stored delays
         * @memberof utils.Context
         * @instance
         * @public
         * @var {Array} __delays
         **/
        this.__delays = [];
      }

      this.stopDelay(fct);

      var bound = this.__bind(fct);
      var _this = this;

      var callback = function(){
        _this.stopDelay(fct);
        bound.apply(_this, params);
      };

      var timeoutId = setTimeout(callback, timeout);
      this.__delays.push({fct: bound, uid: timeoutId});

      return timeoutId;
    };

    /**
     * stops the delay of a specific function call on the target instance.

     * if no fct is defined automatically stops all delays
     *
     * @method stopDelay
     * @memberof utils.Context
     * @param {function} fct that should be called after the delay (optional)
     **/
    p.stopDelay = function(fct){
      if (this.__delays) {
        var i, _len;

        if (fct) {
          var bound = this.__bind(fct);

          for (i = 0, _len = this.__delays.length; i < _len; i++) {
            if (this.__delays[i].fct === bound) {
              clearTimeout(this.__delays[i].uid);
              this.__delays.splice(i, 1);
              break;
            }
          }
        } else {
          for (i = 0, _len = this.__delays.length; i < _len; i++) {
            clearTimeout(this.__delays[i].uid);
          }

          this.__delays = [];
        }
      }
    };
  };

  return Context;
});
<|endoftext|>


<commit_before>/* ===========================================================
 * trumbowyg.pasteurl.js v1.0
 * Url paste handling with noembed. Plugin for Trumbowyg
 * http://alex-d.github.com/Trumbowyg
 * ===========================================================
 * Author : Max Seelig
 *          Facebook : https://facebook.com/maxse
 *          Website : https://www.maxmade.nl/
 */

(function($) {
    "use strict";

    $.extend(true, $.trumbowyg, {
        plugins: {
            pasteImage: {
                init: function(trumbowyg) {
                    trumbowyg.pasteHandlers.push(function(pasteEvent) {
                        try {
                            var clipboardData = (pasteEvent.originalEvent || pasteEvent).clipboardData;
                            var pastedData = clipboardData.getData("Text");
                            var request = null;
                            if (pastedData.startsWith("http")) {
                                pasteEvent.stopPropagation();
                                pasteEvent.preventDefault();

                                var url = pastedData;
                                var query = {
                                    url: url.trim()
                                };
                                var content = "";

                                if (request && request.transport) request.transport.abort();

                                request = $.ajax({
                                    crossOrigin: true,
                                    url: "https://noembed.com/embed?nowrap=on",
                                    type: "GET",
                                    data: query,
                                    cache: false,
                                    dataType: "json",
                                    success: function(res) {
                                        if (res.html) {
                                            content = res.html;
                                        } else {
                                            content = $("<a>", {
                                                href: pastedData,
                                                text: pastedData
                                            }).prop('outerHTML');
                                        }
                                    },
                                    error: function() {
                                        content = $("<a>", {
                                            href: pastedData,
                                            text: pastedData
                                        }).prop('outerHTML');
                                    },
                                    complete: function() {
                                        trumbowyg.execCmd("insertHTML", content);
                                    }
                                });
                            }
                        } catch (c) {}
                    });
                }
            }
        }
    });
})(jQuery);<commit_msg>Added my url2iframe api so that I can add support for more iframes in the future. Spotify is now embeddable ;-)<commit_after>/* ===========================================================
 * trumbowyg.pasteurl.js v1.0
 * Url paste handling with noembed. Plugin for Trumbowyg
 * http://alex-d.github.com/Trumbowyg
 * ===========================================================
 * Author : Max Seelig
 *          Facebook : https://facebook.com/maxse
 *          Website : https://www.maxmade.nl/
 */

(function($) {
    "use strict";

    $.extend(true, $.trumbowyg, {
        plugins: {
            pasteImage: {
                init: function(trumbowyg) {
                    trumbowyg.pasteHandlers.push(function(pasteEvent) {
                        try {
                            var clipboardData = (pasteEvent.originalEvent || pasteEvent).clipboardData;
                            var pastedData = clipboardData.getData("Text");
                            var request = null;

                            if (pastedData.startsWith("http")) {

                                pasteEvent.stopPropagation();
                                pasteEvent.preventDefault();

                                var query = {
                                    url: pastedData.trim()
                                };
                                var content = "";
                                var fails = 0;

                                if (request && request.transport) request.transport.abort();

                                request = $.ajax({
                                    crossOrigin: true,
                                    url: "https://noembed.com/embed?nowrap=on",
                                    url2: "https://api.maxmade.nl/url2iframe/embed",
                                    type: "GET",
                                    data: query,
                                    cache: false,
                                    dataType: "jsonp",
                                    success: function(res) {
                                        if (res.html) {
                                            fails = 0;
                                            content = res.html;
                                        } else {
                                            fails++;
                                        }
                                    },
                                    error: function(res) {
                                        fails++;
                                    },
                                    complete: function() {
                                        console.log(this.url);
                                        if (fails === 1) {
                                            console.log(fails);
                                            this.url = this.url2;
                                            this.data = query;
                                            $.ajax(this);
                                        }
                                        if (fails === 2) {
                                            console.log(fails);
                                            content = $("<a>", {
                                                href: pastedData,
                                                text: pastedData
                                            }).prop('outerHTML');
                                        }
                                        if (content.length > 0) {
                                            fails = 0;
                                            trumbowyg.execCmd("insertHTML", content);
                                        }
                                    }
                                });
                            }
                        } catch (c) {}
                    });
                }
            }
        }
    });
})(jQuery);<|endoftext|>


<commit_before>/**
 * Get value of an object property/path even if it's nested
 */
export function getValueByPath(obj, path) {
    const value = path.split('.').reduce((o, i) => o ? o[i] : null, obj)
    return value
}

/**
 * Extension of indexOf method by equality function if specified
 */
export function indexOf(array, obj, fn) {
    if (!array) return -1

    if (!fn || typeof fn !== 'function') return array.indexOf(obj)

    for (let i = 0; i < array.length; i++) {
        if (fn(array[i], obj)) {
            return i
        }
    }

    return -1
}

/**
* Merge function to replace Object.assign with deep merging possibility
*/
const isObject = (item) => typeof item === 'object' && !Array.isArray(item)
const mergeFn = (target, source, deep = false) => {
    if (deep || !Object.assign) {
        const isDeep = (prop) =>
            isObject(source[prop]) && target.hasOwnProperty(prop) && isObject(target[prop])
        const replaced = Object.getOwnPropertyNames(source)
            .map((prop) =>
                ({ [prop]: isDeep(prop) ? mergeFn(target[prop], source[prop]) : source[prop] }))
            .reduce((a, b) => ({ ...a, ...b }), {})

        return {
            ...target,
            ...replaced
        }
    } else {
        return Object.assign(target, source)
    }
}
export const merge = mergeFn

/**
 * Mobile detection
 * https://www.abeautifulsite.net/detecting-mobile-devices-with-javascript
 */
export const isMobile = {
    Android: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/Android/i)
        )
    },
    BlackBerry: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/BlackBerry/i)
        )
    },
    iOS: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/iPhone|iPad|iPod/i)
        )
    },
    Opera: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/Opera Mini/i)
        )
    },
    Windows: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/IEMobile/i)
        )
    },
    any: function () {
        return (
            isMobile.Android() ||
            isMobile.BlackBerry() ||
            isMobile.iOS() ||
            isMobile.Opera() ||
            isMobile.Windows()
        )
    }
}

export function removeElement(el) {
    if (typeof el.remove !== 'undefined') {
        el.remove()
    } else if (typeof el.parentNode !== 'undefined') {
        el.parentNode.removeChild(el)
    }
}

/**
 * Escape regex characters
 * http://stackoverflow.com/a/6969486
 */
export function escapeRegExpChars(value) {
    if (!value) return value

    // eslint-disable-next-line
    return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')
}
<commit_msg>Improve merge function to prevent weird issue (#2008)<commit_after>/**
 * Get value of an object property/path even if it's nested
 */
export function getValueByPath(obj, path) {
    const value = path.split('.').reduce((o, i) => o ? o[i] : null, obj)
    return value
}

/**
 * Extension of indexOf method by equality function if specified
 */
export function indexOf(array, obj, fn) {
    if (!array) return -1

    if (!fn || typeof fn !== 'function') return array.indexOf(obj)

    for (let i = 0; i < array.length; i++) {
        if (fn(array[i], obj)) {
            return i
        }
    }

    return -1
}

/**
 * Merge function to replace Object.assign with deep merging possibility
 */
const isObject = (item) => typeof item === 'object' && !Array.isArray(item)
const mergeFn = (target, source, deep = false) => {
    if (deep || !Object.assign) {
        const isDeep = (prop) =>
            isObject(source[prop]) &&
            target !== null &&
            target.hasOwnProperty(prop) &&
            isObject(target[prop])
        const replaced = Object.getOwnPropertyNames(source)
            .map((prop) => ({ [prop]: isDeep(prop)
                ? mergeFn(target[prop], source[prop], deep)
                : source[prop] }))
            .reduce((a, b) => ({ ...a, ...b }), {})

        return {
            ...target,
            ...replaced
        }
    } else {
        return Object.assign(target, source)
    }
}
export const merge = mergeFn

/**
 * Mobile detection
 * https://www.abeautifulsite.net/detecting-mobile-devices-with-javascript
 */
export const isMobile = {
    Android: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/Android/i)
        )
    },
    BlackBerry: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/BlackBerry/i)
        )
    },
    iOS: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/iPhone|iPad|iPod/i)
        )
    },
    Opera: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/Opera Mini/i)
        )
    },
    Windows: function () {
        return (
            typeof window !== 'undefined' &&
            window.navigator.userAgent.match(/IEMobile/i)
        )
    },
    any: function () {
        return (
            isMobile.Android() ||
            isMobile.BlackBerry() ||
            isMobile.iOS() ||
            isMobile.Opera() ||
            isMobile.Windows()
        )
    }
}

export function removeElement(el) {
    if (typeof el.remove !== 'undefined') {
        el.remove()
    } else if (typeof el.parentNode !== 'undefined') {
        el.parentNode.removeChild(el)
    }
}

/**
 * Escape regex characters
 * http://stackoverflow.com/a/6969486
 */
export function escapeRegExpChars(value) {
    if (!value) return value

    // eslint-disable-next-line
    return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')
}
<|endoftext|>


<commit_before>import axios from 'axios'
import { Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'

// create an axios instance
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 5000 // request timeout
})

// request interceptor
service.interceptors.request.use(config => {
  // Do something before request is sent
  if (store.getters.token) {
    config.headers['X-Token'] = getToken() // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
  }
  return config
}, error => {
  // Do something with request error
  console.log(error) // for debug
  Promise.reject(error)
})

// respone interceptor
service.interceptors.response.use(
  response => response,
  /**
  * 下面的注释为通过response自定义code来标示请求状态，当code返回如下情况为权限有问题，登出并返回到登录页
  * 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
  */
  //  const res = response.data;
  //     if (res.code !== 20000) {
  //       Message({
  //         message: res.message,
  //         type: 'error',
  //         duration: 5 * 1000
  //       });
  //       // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
  //       if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
  //         MessageBox.confirm('你已被登出，可以取消继续留在该页面，或者重新登录', '确定登出', {
  //           confirmButtonText: '重新登录',
  //           cancelButtonText: '取消',
  //           type: 'warning'
  //         }).then(() => {
  //           store.dispatch('FedLogOut').then(() => {
  //             location.reload();// 为了重新实例化vue-router对象 避免bug
  //           });
  //         })
  //       }
  //       return Promise.reject('error');
  //     } else {
  //       return response.data;
  //     }
  error => {
    console.log('err' + error)// for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  })

export default service
<commit_msg>doc: add  code comments to request.js<commit_after>import axios from 'axios'
import { Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'

// create an axios instance
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 5000 // request timeout
})

// request interceptor
service.interceptors.request.use(config => {
  // Do something before request is sent
  if (store.getters.token) {
    // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
    config.headers['X-Token'] = getToken()
  }
  return config
}, error => {
  // Do something with request error
  console.log(error) // for debug
  Promise.reject(error)
})

// respone interceptor
service.interceptors.response.use(
  response => response,
  /**
   * 下面的注释为通过在response里，自定义code来标示请求状态
   * 当code返回如下情况则说明权限有问题，登出并返回到登录页
   * 如想通过xmlhttprequest来状态码标识 逻辑可写在下面error中
   * 以下代码均为样例，请结合自生需求加以修改，若不需要，则可删除
   */
  // response => {
  //   const res = response.data
  //   if (res.code !== 20000) {
  //     Message({
  //       message: res.message,
  //       type: 'error',
  //       duration: 5 * 1000
  //     })
  //     // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
  //     if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
  //       // 请自行在引入 MessageBox
  //       // import { Message, MessageBox } from 'element-ui'
  //       MessageBox.confirm('你已被登出，可以取消继续留在该页面，或者重新登录', '确定登出', {
  //         confirmButtonText: '重新登录',
  //         cancelButtonText: '取消',
  //         type: 'warning'
  //       }).then(() => {
  //         store.dispatch('FedLogOut').then(() => {
  //           location.reload() // 为了重新实例化vue-router对象 避免bug
  //         })
  //       })
  //     }
  //     return Promise.reject('error')
  //   } else {
  //     return response.data
  //   }
  // },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  })

export default service
<|endoftext|>


<commit_before>/* configure jshint globals */
/* globals numeral */
(function ($) {
    'use strict';

    // Set langauge for Numeral library.
    numeral.language('es');

    var $center = $('center');

    var first_table = $('center').prev().detach();
    $center.after(first_table);

    var dinero_ya = $('.containerDineroYa').detach();
    $center.append(dinero_ya);

    $center.find('>div:eq(0)>br').remove();

    $center.find('table table:eq(5) tr:gt(1):lt(-2)').each(function () {
        var $tr = $(this);
        var $total = $tr.find('td:eq(2) .pesetas');
        var $disponible = $tr.find('td:last .pesetas');

        numeral.language('es');
        var saldo = numeral($total.html()).subtract(numeral($disponible.html()));

        $disponible.append(' (' + saldo.format('0,0.00') + ')');

    });

})(jQuery);
<commit_msg>Update to new BBVA html<commit_after>/* configure jshint globals */
/* globals numeral */
(function ($) {
    'use strict';

    // Set langauge for Numeral library.
    numeral.language('es');

    // Move main table to the beginning of the page.
    var $main = $('body>div:eq(-1)');
    $main.children('table:eq(-1)').detach().prependTo($main);

    // Add current balance to Credit Cards.
    $('p:contains("Tarjetas de Crédito")').closest('table').find('tr:gt(1):lt(-2)').each(function () {
        var $tr = $(this);
        var $total = $tr.find('td:eq(2) .pesetas');
        var $disponible = $tr.find('td:last .pesetas');

        numeral.language('es');
        var saldo = numeral($total.html()).subtract(numeral($disponible.html()));

        $disponible.append('' + saldo.format('0,0.00') + '');
    });

})(jQuery);
<|endoftext|>


<commit_before>var roleArchitect = {

  ableToBuild:function(controllerLvl, structureType){
    switch(controllerLvl){
      case 1:
        return (structureType==="Container");
      case 2:
        return (structureType === "Extensions1" || structureType==="Container");
      case 3:
        return (structureType === "Extensions1" || structureType === "Extensions2" || structureType==="Container" || structureType === "Tower1");
      default:
        return false;
    }
  },

  markExtensions1: function(creep){
    var x = creep.pos.x;
    var y = creep.pos.y;
    creep.room.createConstructionSite(x, y, STRUCTURE_EXTENSION);
    creep.room.createConstructionSite(x+1, y, STRUCTURE_EXTENSION);
    creep.room.createConstructionSite(x-1, y, STRUCTURE_EXTENSION);
    creep.room.createConstructionSite(x, y+1, STRUCTURE_EXTENSION);
    creep.room.createConstructionSite(x, y-1, STRUCTURE_EXTENSION);
  },

  markContainer:function(creep){
      creep.room.createConstructionSite(creep.pos, STRUCTURE_CONTAINER);
  },

  markTower: function(creep){
      creep.room.createConstructionSite(creep.pos, STRUCTURE_TOWER);
  },

  //TODO
  markWall:function(creep){

  },

  /** @param {Creep} creep **/
  run: function(creep) {
    var controllerLvl = creep.room.controller.level;
    var flags = _.filter(creep.room.find(FIND_FLAGS), (flag) => this.ableToBuild(controllerLvl, flag.name));
    if(creep.memory.working && flags.length<1) {
        creep.memory.working = false;
    }
    if(!creep.memory.working && flags.length) {
        creep.memory.working = true;
    }

    if(!creep.memory.working){
      creep.memory.role = 'builder';
      if(controllerLvl<=1){
        creep.memory.selfHarvest = true;
      }
    }else{
      var flag = flags[0];
      if(!creep.pos.isEqualTo(flag.pos)){
        creep.moveTo(flag);
      }else{
        if(flag.name === "Container"){
          this.markContainer(creep);
          flag.remove();
        }else if(flag.name === "Extensions1" || flage.name ==="Extensions2"){
            this.markExtensions1(creep);
            flag.remove();
        }else if(flag.name === "Tower1"){
            this.markTower(creep);
            flag.remove();
        }
      }
    }
  }
};

module.exports = roleArchitect;
<commit_msg>changed priority in construction site order<commit_after>var roleArchitect = {

  ableToBuild:function(controllerLvl, structureType){
    switch(controllerLvl){
      case 1:
        return (structureType==="Container");
      case 2:
        return (structureType === "Extensions1" || structureType==="Container");
      case 3:
        return (structureType === "Extensions1" || structureType === "Extensions2" || structureType==="Container" || structureType === "Tower1");
      default:
        return false;
    }
  },

  markExtensions1: function(creep){
    var x = creep.pos.x;
    var y = creep.pos.y;
    creep.room.createConstructionSite(x, y, STRUCTURE_EXTENSION);
    creep.room.createConstructionSite(x+1, y, STRUCTURE_EXTENSION);
    creep.room.createConstructionSite(x-1, y, STRUCTURE_EXTENSION);
    creep.room.createConstructionSite(x, y+1, STRUCTURE_EXTENSION);
    creep.room.createConstructionSite(x, y-1, STRUCTURE_EXTENSION);
  },

  markContainer:function(creep){
      creep.room.createConstructionSite(creep.pos, STRUCTURE_CONTAINER);
  },

  markTower: function(creep){
      creep.room.createConstructionSite(creep.pos, STRUCTURE_TOWER);
  },

  //TODO
  markWall:function(creep){

  },

  /** @param {Creep} creep **/
  run: function(creep) {
    var controllerLvl = creep.room.controller.level;
    var flags = _.filter(creep.room.find(FIND_FLAGS), (flag) => this.ableToBuild(controllerLvl, flag.name));
    if(creep.memory.working && flags.length<1) {
        creep.memory.working = false;
    }
    if(!creep.memory.working && flags.length) {
        creep.memory.working = true;
    }

    if(!creep.memory.working){
      creep.memory.role = 'builder';
      if(controllerLvl<=1){
        creep.memory.selfHarvest = true;
      }
    }else{
      var flag = flags[0];
      if(!creep.pos.isEqualTo(flag.pos)){
        creep.moveTo(flag);
      }else{
        if(flag.name === "Container"){
          this.markContainer(creep);
          flag.remove();
        }else if(flag.name === "Tower1"){
            this.markTower(creep);
            flag.remove();
        }else if(flag.name === "Extensions1" || flag.name ==="Extensions2"){
            this.markExtensions1(creep);
            flag.remove();
        }
      }
    }
  }
};

module.exports = roleArchitect;
<|endoftext|>