<commit_before>'use babel';
'use strict';

const {EventEmitter}     = require ('events');
const {Client}           = require ('_debugger');
const {spawn}            = require ('child_process');
const watt               = require ('watt');
const escapeStringRegexp = require ('escape-string-regexp');
const path               = require ('path');
const fs                 = require ('fs');
const tmp                = require ('tmp');

const Extractor = require ('./extractor.js');

class Wannabe extends EventEmitter {
  /**
   * Construct the Wannabe object.
   *
   * @param {string} script - Test script.
   */
  constructor (script) {
    super ();

    this._frames  = {};
    this._script  = script;
    this._client  = new Client ();
    this._mocha   = {
      kill: () => {}
    };

    watt.wrapAll (this);
  }

  /**
   * Spawn mocha with the node debugger.
   *
   * @param {string|RegExp} regtest - Tests cases to grep.
   */
  _spawn (pattern, next) {
    const regex = pattern instanceof RegExp ?
                  pattern.toString ().replace (/^\/(.*)\/.?$/, '$1') :
                  escapeStringRegexp (pattern);

    this._mocha = spawn ('node', [
      '--debug',
      '--debug-brk',
      path.join (__dirname, './mocha.js'),
      this._script,
      regex
    ], {
      detached: false,
      stdio: ['ignore', 'ignore', 'pipe']
    });

    this._mocha.unref ();

    this._mocha.stderr.once ('data', () => next ());

    this._mocha.on ('close', () => {});
    this._mocha.on ('error', (err) => {
      console.error (err);
    });
  }

  /**
   * Inspect the frame on the current breakpoint.
   *
   * It populates this._frames with the local variables.
   * The key of this._frames is the line number.
   *
   * NOTE: setBreakpoint is slow!
   */
  * _inspect (next) {
    const {frames} = yield this._client.reqBacktrace (next);
    const line = `${frames[0].line}`;

    if (!this._frames[line]) {
      this._frames[line] = [];
    }

    const frame = {
      locals: [],
      arguments: [],
      returnValue: null
    };

    frames[0].locals.forEach ((local) => {
      /* Local variables */
      frame.locals.push ({
        name:  local.name,
        type:  local.value.type,
        value: local.value.value || local.value.className
      });
    });

    frames[0].arguments.forEach ((argument) => {
      /* Arguments */
      frame.arguments.push ({
        name:  argument.name,
        type:  argument.value.type,
        value: argument.value.value
      });
    });

    /* Return value */
    if (frames[0].returnValue && frames[0].returnValue.type !== 'undefined') {
      frame.returnValue = {
        type: frames[0].returnValue.type,
        value: frames[0].returnValue.value
      };
    }

    this._frames[line].push (frame);
  }

  _exception (ex, next) {
    const line = `${ex.body.sourceLine}`;

    if (!this._frames[line]) {
      this._frames[line] = [];
    }

    this._frames[line].push ({
      exception: {
        type: ex.body.exception.type,
        text: ex.body.exception.text
      }
    });

    next ();
  }

  /**
   * Initialize the client connection.
   *
   * It waits on the first breakpoint because the child is executed with
   * the --debug-brk argument.
   *
   * @param {string|RegExp} regtest - Tests cases to grep.
   */
  * init (pattern, next) {
    yield this._spawn (pattern, next);

    let first = true;
    this._client.on ('break', () => {
      if (first) {
        this.emit ('ready');
        first = false;
        return;
      }

      this._inspect (() => this._client.reqContinue (() => {}));
    });

    this._client.on ('exception', (ex) => {
      this._exception (ex, () => this._client.reqContinue (() => {}));
    });

    this._client.once ('connect', next.parallel ());
    this._client.connect (5858);
    yield next.sync ();

    yield this.once ('ready', next); // wait for the first breakpoint (--debug-brk)
    yield this._client.reqSetExceptionBreak ('all', next);
  }

  /**
   * Set one or more breakpoints according to the current script.
   *
   * @param {number[]} lines - Lines where put the breakpoints.
   */
  * setBreakpoints (lines, next) {
    for (const line of lines) {
      this._client.setBreakpoint ({
        type:   'script',
        target: this._script,
        line:   line
      }, next.parallel ());
    }

    yield next.sync ();
  }

  /**
   * Run Wannabe.
   *
   * When a new breakpoint is reached, the continue request is automatically
   * sent until that the child is terminated.
   *
   * @returns all frames.
   */
  * run (next) {
    const promise = new Promise ((resolve) => {
      this._client.once ('error', (err) => {
        resolve (this._frames);
      });

      this._client.once ('close', () => {
        resolve (this._frames);
      });
    });

    yield this._client.reqContinue (next);
    return yield promise;
  }

  dispose () {
    this._mocha.kill ('SIGINT');
  }
}

const extract = watt (function * (script, data, extractor, next) {
  let file = null;
  if (data) {
    /* We must create a new script in the same location that {script}.
     * The reason is that we can not pass a buffer to mocha; only files
     * can be passed to the runner.
     */
    file = yield tmp.tmpName ({
      template: path.join (script, '../.wannabe-XXXXXX')
    }, next);
    console.log (file);
    yield fs.writeFile (file, data, next);
  } else {
    file = script;
  }

  let _exThrown = false;
  let frames = null;
  const wannabe = new Wannabe (file);

  try {
    const {lines, pattern} = extractor (file);
    if (!lines.length) {
      return {};
    }

    yield wannabe.init (pattern);

    yield wannabe.setBreakpoints (lines);
    frames = yield wannabe.run ();
  } catch (ex) {
    _exThrown = true;
    throw ex;
  } finally {
    wannabe.dispose ();
    if (data) {
      yield fs.unlink (file, next);
    }
    // FIXME: see watt bug: https://github.com/mappum/watt/issues/16
    if (!_exThrown) {
      return frames;
    }
  }
});

exports.byPattern = watt (function * (script, data, funcName, pattern, next) {
  return yield extract (script, data, (file) => {
    const lines = data ?
      Extractor.byFuncInData (data, funcName, pattern) :
      Extractor.byFuncInFile (file, funcName, pattern);
    return {
      lines,
      pattern
    }
  }, next);
});

exports.byLine = watt (function * (script, data, funcName, line, next) {
  return yield extract (script, data, (file) => {
    return data ?
      Extractor.byLineInData (data, funcName, line) :
      Extractor.byLineInFile (file, funcName, line);
  }, next);
});
<commit_msg>Prefer SIGKILL over SIGINT<commit_after>'use babel';
'use strict';

const {EventEmitter}     = require ('events');
const {Client}           = require ('_debugger');
const {spawn}            = require ('child_process');
const watt               = require ('watt');
const escapeStringRegexp = require ('escape-string-regexp');
const path               = require ('path');
const fs                 = require ('fs');
const tmp                = require ('tmp');

const Extractor = require ('./extractor.js');

class Wannabe extends EventEmitter {
  /**
   * Construct the Wannabe object.
   *
   * @param {string} script - Test script.
   */
  constructor (script) {
    super ();

    this._frames  = {};
    this._script  = script;
    this._client  = new Client ();
    this._mocha   = {
      kill: () => {}
    };

    watt.wrapAll (this);
  }

  /**
   * Spawn mocha with the node debugger.
   *
   * @param {string|RegExp} regtest - Tests cases to grep.
   */
  _spawn (pattern, next) {
    const regex = pattern instanceof RegExp ?
                  pattern.toString ().replace (/^\/(.*)\/.?$/, '$1') :
                  escapeStringRegexp (pattern);

    this._mocha = spawn ('node', [
      '--debug',
      '--debug-brk',
      path.join (__dirname, './mocha.js'),
      this._script,
      regex
    ], {
      detached: false,
      stdio: ['ignore', 'ignore', 'pipe']
    });

    this._mocha.unref ();

    this._mocha.stderr.once ('data', () => next ());

    this._mocha.on ('close', () => {});
    this._mocha.on ('error', (err) => {
      console.error (err);
    });
  }

  /**
   * Inspect the frame on the current breakpoint.
   *
   * It populates this._frames with the local variables.
   * The key of this._frames is the line number.
   *
   * NOTE: setBreakpoint is slow!
   */
  * _inspect (next) {
    const {frames} = yield this._client.reqBacktrace (next);
    const line = `${frames[0].line}`;

    if (!this._frames[line]) {
      this._frames[line] = [];
    }

    const frame = {
      locals: [],
      arguments: [],
      returnValue: null
    };

    frames[0].locals.forEach ((local) => {
      /* Local variables */
      frame.locals.push ({
        name:  local.name,
        type:  local.value.type,
        value: local.value.value || local.value.className
      });
    });

    frames[0].arguments.forEach ((argument) => {
      /* Arguments */
      frame.arguments.push ({
        name:  argument.name,
        type:  argument.value.type,
        value: argument.value.value
      });
    });

    /* Return value */
    if (frames[0].returnValue && frames[0].returnValue.type !== 'undefined') {
      frame.returnValue = {
        type: frames[0].returnValue.type,
        value: frames[0].returnValue.value
      };
    }

    this._frames[line].push (frame);
  }

  _exception (ex, next) {
    const line = `${ex.body.sourceLine}`;

    if (!this._frames[line]) {
      this._frames[line] = [];
    }

    this._frames[line].push ({
      exception: {
        type: ex.body.exception.type,
        text: ex.body.exception.text
      }
    });

    next ();
  }

  /**
   * Initialize the client connection.
   *
   * It waits on the first breakpoint because the child is executed with
   * the --debug-brk argument.
   *
   * @param {string|RegExp} regtest - Tests cases to grep.
   */
  * init (pattern, next) {
    yield this._spawn (pattern, next);

    let first = true;
    this._client.on ('break', () => {
      if (first) {
        this.emit ('ready');
        first = false;
        return;
      }

      this._inspect (() => this._client.reqContinue (() => {}));
    });

    this._client.on ('exception', (ex) => {
      this._exception (ex, () => this._client.reqContinue (() => {}));
    });

    this._client.once ('connect', next.parallel ());
    this._client.connect (5858);
    yield next.sync ();

    yield this.once ('ready', next); // wait for the first breakpoint (--debug-brk)
    yield this._client.reqSetExceptionBreak ('all', next);
  }

  /**
   * Set one or more breakpoints according to the current script.
   *
   * @param {number[]} lines - Lines where put the breakpoints.
   */
  * setBreakpoints (lines, next) {
    for (const line of lines) {
      this._client.setBreakpoint ({
        type:   'script',
        target: this._script,
        line:   line
      }, next.parallel ());
    }

    yield next.sync ();
  }

  /**
   * Run Wannabe.
   *
   * When a new breakpoint is reached, the continue request is automatically
   * sent until that the child is terminated.
   *
   * @returns all frames.
   */
  * run (next) {
    const promise = new Promise ((resolve) => {
      this._client.once ('error', (err) => {
        resolve (this._frames);
      });

      this._client.once ('close', () => {
        resolve (this._frames);
      });
    });

    yield this._client.reqContinue (next);
    return yield promise;
  }

  dispose () {
    this._mocha.kill ('SIGKILL');
  }
}

const extract = watt (function * (script, data, extractor, next) {
  let file = null;
  if (data) {
    /* We must create a new script in the same location that {script}.
     * The reason is that we can not pass a buffer to mocha; only files
     * can be passed to the runner.
     */
    file = yield tmp.tmpName ({
      template: path.join (script, '../.wannabe-XXXXXX')
    }, next);
    console.log (file);
    yield fs.writeFile (file, data, next);
  } else {
    file = script;
  }

  let _exThrown = false;
  let frames = null;
  const wannabe = new Wannabe (file);

  try {
    const {lines, pattern} = extractor (file);
    if (!lines.length) {
      return {};
    }

    yield wannabe.init (pattern);

    yield wannabe.setBreakpoints (lines);
    frames = yield wannabe.run ();
  } catch (ex) {
    _exThrown = true;
    throw ex;
  } finally {
    wannabe.dispose ();
    if (data) {
      yield fs.unlink (file, next);
    }
    // FIXME: see watt bug: https://github.com/mappum/watt/issues/16
    if (!_exThrown) {
      return frames;
    }
  }
});

exports.byPattern = watt (function * (script, data, funcName, pattern, next) {
  return yield extract (script, data, (file) => {
    const lines = data ?
      Extractor.byFuncInData (data, funcName, pattern) :
      Extractor.byFuncInFile (file, funcName, pattern);
    return {
      lines,
      pattern
    }
  }, next);
});

exports.byLine = watt (function * (script, data, funcName, line, next) {
  return yield extract (script, data, (file) => {
    return data ?
      Extractor.byLineInData (data, funcName, line) :
      Extractor.byLineInFile (file, funcName, line);
  }, next);
});
<|endoftext|>


<commit_before>(() => {

  "use strict";

  let settings = {
    domain: "",
    token: {},
    draft: "",
    visibility: "public",
  };

  chrome.storage.local.get(settings, function(storage) {
    settings.domain = storage.domain;
    settings.token = storage.token;
    settings.draft = storage.draft;
    settings.visibility = storage.visibility;

    document.querySelector("#content").value = settings.draft;
    document.querySelector("#visibility").value = settings.visibility;
    init();
  });

  function init() {
    document.querySelector("#content").focus();
    count();
    validateCount();

    document.querySelector("#toot").addEventListener("click", toot);
    document.querySelector("#cw").addEventListener("change", toggleSpoilerField);
    document.addEventListener("keyup", function(event) {
      count();
      validateCount();
      saveDraft();
    });
    document.addEventListener("keydown", function(event) {
      if (((event.ctrlKey && !event.metaKey) || (event.metaKey && !event.ctrlKey)) && event.key === 'Enter' && tootable()) {
        toot();
      }
      else if (event.metaKey && event.ctrlKey && event.keyCode === 32) {
        event.preventDefault();
        document.querySelector(".wdt-emoji-picker").click();
        document.querySelector("#wdt-emoji-search").focus();
      }

      refleshStatus();
    });
    document.querySelector(".wdt-emoji-picker").addEventListener("click", function() {
      document.querySelector("#wdt-emoji-search").focus();
    });
  }

  function count() {
    let content = stringToArray(document.querySelector("#content").value);
    let spoiler = stringToArray(document.querySelector("#spoiler-text").value);
    let left = 500 - (content.length + spoiler.length);

    document.querySelector("#words").textContent = left;
  }

  function validateCount() {
    let content = document.querySelector("#content").value.length;
    let spoiler = document.querySelector("#spoiler-text").value.length;
    let count = content + spoiler;

    if (count <= 0 || count > 500) {
      disableTootButton();
      warningCount();
      return false;
    }
    else if (content <= 0) {
      disableTootButton();
      return false;
    }
    else {
      enableTootButton();
      clearWarningCount();
      return true;
    }
  }

  function toot() {
    let xhr = new XMLHttpRequest();
    let url = settings.domain + "/api/v1/statuses";
    let token = settings.token;
    let content = document.querySelector("#content").value;
    let visibility = document.querySelector("#visibility").value;
    let spoiler = document.querySelector("#spoiler-text").value;

    xhr.open("POST", url, true);
    xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
    xhr.setRequestHeader("Authorization", "Bearer " + token.access_token);

    xhr.onreadystatechange = function(event) {
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        document.querySelector("#content").value = "";
        saveDraft();
        document.querySelector("#spoiler-text").value = "";
        document.querySelector("#cw").checked = false;
        hideSpoiler();

        let status = document.querySelector("#status");
        status.classList.remove("text-danger");
        status.classList.add("text-success");
        status.textContent = "Tooted successfully!";

        validateCount();

        setTimeout(function() {
          status.textContent = "";
          status.classList.remove("text-success");
        }, 1500);
      }
      else if (xhr.readyState === XMLHttpRequest.DONE && xhr.status >= 400 && xhr.status < 600) {
        let status = document.querySelector("#status");
        status.classList.remove("text-success");
        status.classList.add("text-danger");

        if (settings.token.access_token) {
          status.textContent = "Toot failed";
        }
        else {
          status.innerHTML = 'Auth this app from options page';
        }

        validateCount();
      }
      else {
        // this may be OPTIONS method
      }
    }

    let isChecked = document.querySelector("#cw").checked;
    if (isChecked && spoiler === "") {
      document.querySelector("#spoiler-text").style.border = "1px solid #d9534f";
      return false;
    }
    else if (isChecked && spoiler !== "") {
      disableTootButton();
      xhr.send("status=" + encodeURIComponent(content) + "&visibility=" + visibility + "&spoiler_text=" + encodeURIComponent(spoiler));
    }
    else {
      disableTootButton();
      xhr.send("status=" + encodeURIComponent(content) + "&visibility=" + visibility);
    }

  }

  function saveDraft() {
    let content = document.querySelector("#content").value;
    chrome.storage.local.set({
      draft: content
    });
  }

  function validateFocus() {
    let active = document.activeElement;
    let content = document.querySelector("#content");
    let spoiler = document.querySelector("#spoiler-text");

    if (active === content || active === spoiler) {
      return true;
    }
    else {
      return false;
    }
  }

  function toggleSpoilerField() {
    let isChecked = document.querySelector("#cw").checked;
    if (isChecked) {
      showSpoiler();
    }
    else {
      hideSpoiler();
    }
  }

  function showSpoiler() {
    document.querySelector("#spoiler-text").style.display = "block";
    document.querySelector("#cw-hide-area").style.height = "0";
  }

  function hideSpoiler() {
    document.querySelector("#spoiler-text").value = "";
    count();
    validateCount();
    document.querySelector("#spoiler-text").style.display = "none";
    document.querySelector("#spoiler-text").style.border = "";
    document.querySelector("#cw-hide-area").style.height = "48px";
  }

  function disableTootButton() {
    let tootButton = document.querySelector("#toot");
    tootButton.setAttribute("disabled", "");
  }

  function enableTootButton() {
    let tootButton = document.querySelector("#toot");
    tootButton.removeAttribute("disabled");
  }

  function tootable() {
    let tootButton = document.querySelector("#toot");

    if (validateCount === false) {
      return false;
    }
    else if (validateFocus === false) {
      return false;
    }
    else if (tootButton.getAttribute("disabled") !== null) {
      return false;
    }
    else {
      return true;
    }
  }

  function warningCount() {
    let words = document.querySelector("#words");

    if (words.textContent !== "500") {
      words.style.color = "#d9534f";
    }
  }

  function clearWarningCount() {
    let words = document.querySelector("#words");
    words.style.color = "";
  }

  function refleshStatus() {
    let status = document.querySelector("#status");
    status.textContent = "";
    status.classList.remove("text-success");
    status.classList.remove("text-danger");
  }

  function stringToArray(str) {
    return str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || [];
  }

})();
<commit_msg>:bug: Fix a bug that the message "Auth this app..." are not shown<commit_after>(() => {

  "use strict";

  let settings = {
    domain: "",
    token: {},
    draft: "",
    visibility: "public",
  };

  chrome.storage.local.get(settings, function(storage) {
    settings.domain = storage.domain;
    settings.token = storage.token;
    settings.draft = storage.draft;
    settings.visibility = storage.visibility;

    document.querySelector("#content").value = settings.draft;
    document.querySelector("#visibility").value = settings.visibility;
    init();
  });

  function init() {
    document.querySelector("#content").focus();
    count();
    validateCount();

    document.querySelector("#toot").addEventListener("click", toot);
    document.querySelector("#cw").addEventListener("change", toggleSpoilerField);
    document.addEventListener("keyup", function(event) {
      count();
      validateCount();
      saveDraft();
    });
    document.addEventListener("keydown", function(event) {
      if (((event.ctrlKey && !event.metaKey) || (event.metaKey && !event.ctrlKey)) && event.key === 'Enter' && tootable()) {
        toot();
      }
      else if (event.metaKey && event.ctrlKey && event.keyCode === 32) {
        event.preventDefault();
        document.querySelector(".wdt-emoji-picker").click();
        document.querySelector("#wdt-emoji-search").focus();
      }

      if (!event.ctrlKey && !event.metaKey && !event.shiftKey) {
        refleshStatus();
      }
    });
    document.querySelector(".wdt-emoji-picker").addEventListener("click", function() {
      document.querySelector("#wdt-emoji-search").focus();
    });
  }

  function count() {
    let content = stringToArray(document.querySelector("#content").value);
    let spoiler = stringToArray(document.querySelector("#spoiler-text").value);
    let left = 500 - (content.length + spoiler.length);

    document.querySelector("#words").textContent = left;
  }

  function validateCount() {
    let content = document.querySelector("#content").value.length;
    let spoiler = document.querySelector("#spoiler-text").value.length;
    let count = content + spoiler;

    if (count <= 0 || count > 500) {
      disableTootButton();
      warningCount();
      return false;
    }
    else if (content <= 0) {
      disableTootButton();
      return false;
    }
    else {
      enableTootButton();
      clearWarningCount();
      return true;
    }
  }

  function toot() {
    let xhr = new XMLHttpRequest();
    let url = settings.domain + "/api/v1/statuses";
    let token = settings.token;
    let content = document.querySelector("#content").value;
    let visibility = document.querySelector("#visibility").value;
    let spoiler = document.querySelector("#spoiler-text").value;

    if (!settings.token.access_token) {
      let status = document.querySelector("#status");
      status.classList.remove("text-success");
      status.classList.add("text-danger");

      status.innerHTML = 'Auth this app from options page';
      return false;
    }

    xhr.open("POST", url, true);
    xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
    xhr.setRequestHeader("Authorization", "Bearer " + token.access_token);

    xhr.onreadystatechange = function(event) {
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        document.querySelector("#content").value = "";
        saveDraft();
        document.querySelector("#spoiler-text").value = "";
        document.querySelector("#cw").checked = false;
        hideSpoiler();

        let status = document.querySelector("#status");
        status.classList.remove("text-danger");
        status.classList.add("text-success");
        status.textContent = "Tooted successfully!";

        validateCount();

        setTimeout(function() {
          status.textContent = "";
          status.classList.remove("text-success");
        }, 1500);
      }
      else if (xhr.readyState === XMLHttpRequest.DONE && xhr.status >= 400 && xhr.status < 600) {
        let status = document.querySelector("#status");
        status.classList.remove("text-success");
        status.classList.add("text-danger");

        if (settings.token.access_token) {
          status.textContent = "Toot failed";
        }
        else {
          status.innerHTML = 'Auth this app from options page';
        }

        validateCount();
      }
      else {
        // this may be OPTIONS method
      }
    }

    let isChecked = document.querySelector("#cw").checked;
    if (isChecked && spoiler === "") {
      document.querySelector("#spoiler-text").style.border = "1px solid #d9534f";
      return false;
    }
    else if (isChecked && spoiler !== "") {
      disableTootButton();
      xhr.send("status=" + encodeURIComponent(content) + "&visibility=" + visibility + "&spoiler_text=" + encodeURIComponent(spoiler));
    }
    else {
      disableTootButton();
      xhr.send("status=" + encodeURIComponent(content) + "&visibility=" + visibility);
    }

  }

  function saveDraft() {
    let content = document.querySelector("#content").value;
    chrome.storage.local.set({
      draft: content
    });
  }

  function validateFocus() {
    let active = document.activeElement;
    let content = document.querySelector("#content");
    let spoiler = document.querySelector("#spoiler-text");

    if (active === content || active === spoiler) {
      return true;
    }
    else {
      return false;
    }
  }

  function toggleSpoilerField() {
    let isChecked = document.querySelector("#cw").checked;
    if (isChecked) {
      showSpoiler();
    }
    else {
      hideSpoiler();
    }
  }

  function showSpoiler() {
    document.querySelector("#spoiler-text").style.display = "block";
    document.querySelector("#cw-hide-area").style.height = "0";
  }

  function hideSpoiler() {
    document.querySelector("#spoiler-text").value = "";
    count();
    validateCount();
    document.querySelector("#spoiler-text").style.display = "none";
    document.querySelector("#spoiler-text").style.border = "";
    document.querySelector("#cw-hide-area").style.height = "48px";
  }

  function disableTootButton() {
    let tootButton = document.querySelector("#toot");
    tootButton.setAttribute("disabled", "");
  }

  function enableTootButton() {
    let tootButton = document.querySelector("#toot");
    tootButton.removeAttribute("disabled");
  }

  function tootable() {
    let tootButton = document.querySelector("#toot");

    if (validateCount() === false) {
      return false;
    }
    else if (validateFocus() === false) {
      return false;
    }
    else if (tootButton.getAttribute("disabled") !== null) {
      return false;
    }
    else {
      return true;
    }
  }

  function warningCount() {
    let words = document.querySelector("#words");

    if (words.textContent !== "500") {
      words.style.color = "#d9534f";
    }
  }

  function clearWarningCount() {
    let words = document.querySelector("#words");
    words.style.color = "";
  }

  function refleshStatus() {
    let status = document.querySelector("#status");
    status.textContent = "";
    status.classList.remove("text-success");
    status.classList.remove("text-danger");
  }

  function stringToArray(str) {
    return str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || [];
  }

})();
<|endoftext|>


<commit_before>var i = 0;

function addCharacter()
{
	if (i > 0) {
		$('#addcharacter' + (i - 1)).hide();
	}
	var character = $('#characters').clone();
	character.attr('id', 'character' + i);
	character.attr('name', 'character' + i);

	var character_wrapper = $('

');
	character_wrapper.append(character);
	character_wrapper.append('Remove');
	character_wrapper.append('Add');

	$('#selectedcharacters').append(character_wrapper);
	++i;
}

$(document).ready(function() {
	addCharacter();
});
<commit_msg>Javascript is now smarter when adding characters, but still needs to be improved a lot<commit_after>var i = 0;
var available_characters = new Array();

function cloneCharacters(i)
{
	var characters = $('#characters').clone();
	return characters;
}

function addCharacter()
{
	if (i > 0) {
		var selected = $('#character' + (i - 1)).val();
		var removeMe;
		$.each(available_characters, function(i, v) {
			if (v == selected) {
				removeMe = i;
			}
		});
		available_characters.splice(removeMe, 1);
		$('#character' + (i - 1)).hide();
		$('#addcharacter' + (i - 1)).hide();
		$('#removecharacter' + (i - 1)).show();
		$('#characterlabel' + (i - 1)).html($('#character' + (i - 1) + ' option:selected').text());
		$('#characterlabel' + (i - 1)).show();
	}

	if (!available_characters.length) {
		return;
	}

	var character = cloneCharacters(i);
	character.attr('id', 'character' + i);
	character.attr('name', 'character' + i);

	var character_wrapper = $('

');
	character_wrapper.append(character);
	character_wrapper.append('

');
	character_wrapper.append('Remove');
	character_wrapper.append('Add');

	$('#selectedcharacters').append(character_wrapper);
	++i;
}

$(document).ready(function() {
	$('#characters option').each(function() {
		available_characters.push($(this).val());
	});
	addCharacter();
});
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

'use strict';

const BabelPluginRelay = require('../../packages/babel-plugin-relay/BabelPluginRelay.js');

const assign = require('object-assign');
const babel = require('babel-core');
const createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction');
const getBabelOptions = require('../getBabelOptions');
const getBabelRelayPlugin = require('../babel-relay-plugin');
const getSchemaIntrospection = require('../../packages/babel-plugin-relay/getSchemaIntrospection');
const path = require('path');

const SCHEMA_PATH = path.resolve(__dirname, '../../packages/relay-compiler/testutils/testschema.graphql');

const schema = getSchemaIntrospection(SCHEMA_PATH);

const babelOptions = getBabelOptions({
  env: 'test',
  moduleMap: {
    'React': 'react',
    'reactComponentExpect': 'react-dom/lib/reactComponentExpect',
    'ReactDOM': 'react-dom',
    'ReactDOMServer': 'react-dom/server',
    'ReactTestRenderer': 'react-test-renderer',
    'ReactTestUtils': 'react-addons-test-utils',
    'StaticContainer.react': 'react-static-container',
  },
  plugins: [
    [BabelPluginRelay, {compat: true}],
    getBabelRelayPlugin(schema, {substituteVariables: true}),
    require('babel-plugin-transform-async-to-generator'),
    require('babel-plugin-transform-regenerator'),
  ],
});

module.exports = {
  process: function(src, filename) {
    const options = assign({}, babelOptions, {
      filename: filename,
      retainLines: true,
    });
    return babel.transform(src, options).code;
  },

  getCacheKey: createCacheKeyFunction([
    __filename,
    SCHEMA_PATH,
    path.join(__dirname, '..', 'babel-relay-plugin', 'package.json'),
    path.join(path.dirname(require.resolve('babel-preset-fbjs')), 'package.json'),
    path.join(__dirname, '..', 'getBabelOptions.js'),
  ]),
};
<commit_msg>RFC: Use dist/babel-plugin-relay in jest preprocessor<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

'use strict';

const BabelPluginRelay = require('../../dist/babel-plugin-relay');

const assign = require('object-assign');
const babel = require('babel-core');
const createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction');
const getBabelOptions = require('../getBabelOptions');
const getBabelRelayPlugin = require('../babel-relay-plugin');
const getSchemaIntrospection = require('../../packages/babel-plugin-relay/getSchemaIntrospection');
const path = require('path');

const SCHEMA_PATH = path.resolve(__dirname, '../../packages/relay-compiler/testutils/testschema.graphql');

const schema = getSchemaIntrospection(SCHEMA_PATH);

const babelOptions = getBabelOptions({
  env: 'test',
  moduleMap: {
    'React': 'react',
    'reactComponentExpect': 'react-dom/lib/reactComponentExpect',
    'ReactDOM': 'react-dom',
    'ReactDOMServer': 'react-dom/server',
    'ReactTestRenderer': 'react-test-renderer',
    'ReactTestUtils': 'react-addons-test-utils',
    'StaticContainer.react': 'react-static-container',
  },
  plugins: [
    [BabelPluginRelay, {compat: true}],
    getBabelRelayPlugin(schema, {substituteVariables: true}),
    require('babel-plugin-transform-async-to-generator'),
    require('babel-plugin-transform-regenerator'),
  ],
});

module.exports = {
  process: function(src, filename) {
    const options = assign({}, babelOptions, {
      filename: filename,
      retainLines: true,
    });
    return babel.transform(src, options).code;
  },

  getCacheKey: createCacheKeyFunction([
    __filename,
    SCHEMA_PATH,
    path.join(__dirname, '..', 'babel-relay-plugin', 'package.json'),
    path.join(path.dirname(require.resolve('babel-preset-fbjs')), 'package.json'),
    path.join(__dirname, '..', 'getBabelOptions.js'),
  ]),
};
<|endoftext|>


<commit_before>var hagent = require('hagent');
var tls = require('tls');
var ca = require('./ca');

var serverAgent = hagent.serverAgent;
serverAgent.createCertificate = ca.createCertificate;
exports.serverAgent = serverAgent;

var createTLSServer = function(options, listener) {
  return tls.createServer(options, listener);
};
var getTLSServer = hagent.create(createTLSServer, 43900);
var callbacks = [];
var TIMEOUT = 3000;
var sniPort;

var SNICallback = function(serverName, cb) {
  serverName = ca.getDomain(serverName);
  var options = ca.createCertificate(serverName);
  if (!options.ctx) {
    options._ctx = ca.createSecureContext(options);
  }
  cb(null, options._ctx);
};

var removeServer = function() {
  sniPort = null;
  try {
    this.close();
  } catch(e) {} //重复关闭会导致异常
};

exports.getSNIServer = function(listener, callback) {
  if (sniPort) {
    return callback(sniPort);
  }
  getTLSServer({ SNICallback: SNICallback }, listener, function(server, port) {
    server.on('error', removeServer);
    var timeout = setTimeout(removeServer, TIMEOUT);
    var clearup = function() {
      clearTimeout(timeout);
    };
    server.on('tlsClientError', clearup)
    server.once('secureConnection', clearup);
    sniPort = port;
    callbacks.forEach(function(cb) {
      cb(sniPort);
    });
  });
};

exports.getDomain = ca.getDomain;
exports.existsCustomCert = ca.existsCustomCert;
exports.getRootCAFile = ca.getRootCAFile;
<commit_msg>refactor: code<commit_after>var hagent = require('hagent');
var tls = require('tls');
var ca = require('./ca');

var serverAgent = hagent.serverAgent;
serverAgent.createCertificate = ca.createCertificate;
exports.serverAgent = serverAgent;

var createTLSServer = function(options, listener) {
  return tls.createServer(options, listener);
};
var getTLSServer = hagent.create(createTLSServer, 43900);
var callbacks = [];
var TIMEOUT = 3000;
var sniPort;

var SNICallback = function(serverName, cb) {
  serverName = ca.getDomain(serverName);
  var options = ca.createCertificate(serverName);
  if (!options.ctx) {
    options._ctx = ca.createSecureContext(options);
  }
  cb(null, options._ctx);
};

var removeServer = function() {
  sniPort = null;
  try {
    this.close();
  } catch(e) {} //重复关闭会导致异常
};

exports.getSNIServer = function(listener, callback) {
  if (sniPort) {
    return callback(sniPort);
  }
  getTLSServer({ SNICallback: SNICallback }, listener, function(server, port) {
    server.on('error', removeServer);
    var timeout = setTimeout(removeServer, TIMEOUT);
    var clearup = function() {
      clearTimeout(timeout);
    };
    server.once('tlsClientError', clearup)
    server.once('secureConnection', clearup);
    sniPort = port;
    callbacks.forEach(function(cb) {
      cb(sniPort);
    });
  });
};

exports.getDomain = ca.getDomain;
exports.existsCustomCert = ca.existsCustomCert;
exports.getRootCAFile = ca.getRootCAFile;
<|endoftext|>


<commit_before>'use strict';

require('../../app/js/app.js');
require('angular-mocks');

describe('breakController', function() {
  var $controllerConstructor;
  var $httpBackend;
  var scope;

  beforeEach(angular.mock.module('breakApp'));

  beforeEach(angular.mock.inject(function($controller, $rootScope, breakService) {
    scope = $rootScope.$new();
    $controllerConstructor = $controller;
    _breakService = breakService;
  }));

  it('creates a new controller', function() {
    var breakController = $controllerConstructor('breakController', {$scope: scope });
    expect(typeof breakController).toBe('object');
  });

  describe('breakController functions', function() {
    var ctrl;
    // beforeEach(angular.mock.inject(function(_$httpBackend_) {
    //   $httpBackend = _$httpBackend_;
    //   $httpBackend.expectGET('/api/v_0_0_1/breakideas').respond(200);
    // }));

    // afterEach(function() {
    //   $httpBackend.verifyNoOutstandingExpectation();
    //   $httpBackend.verifyNoOutstandingRequest();
    // });

    it('starts with no current break set', function() {
      ctrl = $controllerConstructor('breakController', {$scope: scope});
      expect(scope.currentBreak).toEqual('');
    });

    it('starts with the timer not running', function() {
      ctrl = $controllerConstructor('breakController', {$scope: scope});
      expect(scope.timerRunning).toEqual(false);
    });

    it('starts the timer running when breakTimer is called', function() {
      ctrl = $controllerConstructor('breakController', {$scope: scope, _break});
      scope.breakTimer();
      expect(scope.timerRunning).toBeTruthy();
    });

    // it('should be able to create a new note', function() {
    //   $httpBackend.expectPOST('/api/v_0_0_1/notes').respond(200, {'noteBody': 'test note'});
    //   ctrl = $controllerConstructor('notesController', {$scope: scope});
    //   scope.newNote = {'noteBody': 'test note'};
    //   scope.saveNewNote();

    //   $httpBackend.flush();
    // });

    // it('should be able edit a note', function() {
    //   $httpBackend.expectPUT('/api/v_0_0_1/notes/1').respond(202, {});
    //   $httpBackend.expectGET('/api/v_0_0_1/notes').respond(200, [{}]);
    //   ctrl = $controllerConstructor('notesController', {$scope: scope});
    //   scope.saveNote({_id: '1'});

    //   $httpBackend.flush();
    // });

    // it('should be able to delete a note', function() {
    //   $httpBackend.expectDELETE('/api/v_0_0_1/notes/1').respond(200, {});
    //   $httpBackend.expectGET('/api/v_0_0_1/notes').respond(200, [{}]);
    //   ctrl = $controllerConstructor('notesController', {$scope: scope});
    //   scope.deleteNote({_id: '1'});

    //   $httpBackend.flush();
    // });
  });
});
<commit_msg>working on angular tests<commit_after>'use strict';

require('../../app/js/app.js');
require('angular-mocks');

describe('breakController', function() {
  var $controllerConstructor;
  var $httpBackend;
  var scope;

  beforeEach(angular.mock.module('breakApp'));

  beforeEach(angular.mock.inject(function($controller, $rootScope, breakService) {
    scope = $rootScope.$new();
    $controllerConstructor = $controller;
    _breakService = breakService;
  }));

  it('creates a new controller', function() {
    var breakController = $controllerConstructor('breakController', {$scope: scope });
    expect(typeof breakController).toBe('object');
  });

  describe('breakController functions', function() {
    var ctrl;
    // beforeEach(angular.mock.inject(function(_$httpBackend_) {
    //   $httpBackend = _$httpBackend_;
    //   $httpBackend.expectGET('/api/v_0_0_1/breakideas').respond(200);
    // }));

    // afterEach(function() {
    //   $httpBackend.verifyNoOutstandingExpectation();
    //   $httpBackend.verifyNoOutstandingRequest();
    // });

    it('starts with no current break set', function() {
      ctrl = $controllerConstructor('breakController', {$scope: scope});
      expect(scope.currentBreak).toEqual('');
    });

    it('starts with the timer not running', function() {
      ctrl = $controllerConstructor('breakController', {$scope: scope});
      expect(scope.timerRunning).toEqual(false);
    });

    it('starts the timer running when breakTimer is called', function() {
      ctrl = $controllerConstructor('breakController', {$scope: scope}, {breakService: _breakService});
      scope.breakTimer();
      expect(scope.timerRunning).toBeTruthy();
    });

    // it('should be able to create a new note', function() {
    //   $httpBackend.expectPOST('/api/v_0_0_1/notes').respond(200, {'noteBody': 'test note'});
    //   ctrl = $controllerConstructor('notesController', {$scope: scope});
    //   scope.newNote = {'noteBody': 'test note'};
    //   scope.saveNewNote();

    //   $httpBackend.flush();
    // });

    // it('should be able edit a note', function() {
    //   $httpBackend.expectPUT('/api/v_0_0_1/notes/1').respond(202, {});
    //   $httpBackend.expectGET('/api/v_0_0_1/notes').respond(200, [{}]);
    //   ctrl = $controllerConstructor('notesController', {$scope: scope});
    //   scope.saveNote({_id: '1'});

    //   $httpBackend.flush();
    // });

    // it('should be able to delete a note', function() {
    //   $httpBackend.expectDELETE('/api/v_0_0_1/notes/1').respond(200, {});
    //   $httpBackend.expectGET('/api/v_0_0_1/notes').respond(200, [{}]);
    //   ctrl = $controllerConstructor('notesController', {$scope: scope});
    //   scope.deleteNote({_id: '1'});

    //   $httpBackend.flush();
    // });
  });
});
<|endoftext|>


<commit_before>import {
  MainHtmlCompiler, // body, head
  TemplateHtmlCompiler, // templates
  StaticHtmlCompiler,
  utils,
} from 'meteor/urigo:static-html-compiler';

class AngularTemplatesCompiler extends TemplateHtmlCompiler {
  urlContent(file, result) {
    return `
      var templateUrl = "${file.getPathInPackage()}";
      var template = "${utils.clean(result)}";

      angular.module('angular-templates')
        .run(['$templateCache', function($templateCache) {
          $templateCache.put(templateUrl, template);
        }]);

      module.exports = {};
      module.exports.__esModule = true;
      module.exports.default = templateUrl;
    `;
  }

  htmlContent(file, result) {
    return `
      exports.__esModule = true;
      exports.default = "${utils.clean(result)}";
    `;
  }

  addCompileResult(file, result) {
    const path = file.getPathInPackage();

    // import template from './path.html';
    file.addJavaScript({
      path: path + '!raw',
      data: this.htmlContent(file, result),
      lazy: true,
    });

    // import templateUrl from './path.html';
    file.addJavaScript({
      path,
      data: this.urlContent(file, result),
      lazy: true,
    });
  }
}

Plugin.registerCompiler({
  extensions: ['html']
}, () => new StaticHtmlCompiler(new MainHtmlCompiler, new AngularTemplatesCompiler));
<commit_msg>fix(angular-templates): disable lazy loading for html from packages and fix the templateUrl<commit_after>import {
  MainHtmlCompiler, // body, head
  TemplateHtmlCompiler, // templates
  StaticHtmlCompiler,
  utils,
} from 'meteor/urigo:static-html-compiler';

class AngularTemplatesCompiler extends TemplateHtmlCompiler {
  urlContent(file, result) {
    // XXX Why Meteor.isServer?
    // Because if template is not lazy loaded
    // then it's also included on a server side and there is no `angular`.
    //
    // XXX Why `/` before the url?
    // User expects `/client/index.html` instead of `client/index.html`.
    //
    // XXX Why module.exports = {}?
    // Meteor somehow adds a module with `!raw` suffix inside of this module.
    // The ddition looks like this:
    // module.exports = require("./path/to/template.html!raw");
    // So we have to overwrite this.
    //
    // XXX Why exports.__esModule = true ?
    // Meteor specific property.
    // If sets to `true` then whole object is exported directly
    // instead of using `{ default: obj }`
    // This way the default export works the same way in JS and TS.
    return `
      if (Meteor.isServer) return;

      var templateUrl = "/${file.getTemplateUrl()}";
      var template = "${utils.clean(result)}";

      angular.module('angular-templates')
        .run(['$templateCache', function($templateCache) {
          $templateCache.put(templateUrl, template);
        }]);

      module.exports = {};
      module.exports.__esModule = true;
      module.exports.default = templateUrl;
    `;
  }

  htmlContent(file, result) {
    return `
      exports.__esModule = true;
      exports.default = "${utils.clean(result)}";
    `;
  }

  addCompileResult(file, result) {
    const path = file.getPathInPackage();
    const isPackage = !!file.getPackageName();

    // import template from './path.html';
    file.addJavaScript({
      path: path + '!raw',
      data: this.htmlContent(file, result),
      lazy: true,
    });

    // import templateUrl from './path.html';
    file.addJavaScript({
      path,
      data: this.urlContent(file, result),
      lazy: isPackage ? false : true,
    });
  }
}

Plugin.registerCompiler({
  extensions: ['html']
}, () => new StaticHtmlCompiler(new MainHtmlCompiler, new AngularTemplatesCompiler));
<|endoftext|>


<commit_before>/* global: exports */
define(function (require, exports, module) {
  "use strict";

  var $ = require('jquery');
  var view = require('./view');
  var utils = require('./utils');
  var model = require('./model');

  var ESPRESSO = 100; //mg
  var TEA_BAG = 40; //mg

  var data = model.record;
  var stage = model.stage;

  /* istanbul ignore next */
  var advanceStage = function() {
    stage.advance();
    view.setStage();
    window.location.hash = stage.current;
    var current_stage = $('[data-ct-ui-stage=' + stage.current + ']');
    if (current_stage.data('ct-ui-result')) {
      // This is the result stage
      setTimeout(showResult, 0); //TODO fix hack.
    }
  };

  /* istanbul ignore next */
  var showResult = function() {

    $('#id_time').val(data.timeString());
    $('#id_description').val(data.description);
    $('#id_caffeine').val(Math.round(data.caffeine));

    $('#record-form').trigger('submit');
  };

  /* istanbul ignore next */
  var setStage = function() {

    $('[data-ct-ui-show-target]').on('click', function() {
      var target = $(this).data('ct-ui-show-target');
      $('[data-ct-ui-show-' + target + ']').removeClass('ui--removed');
      $('[data-ct-ui-hide-' + target + ']').addClass('ui--removed');
    });

    $('[data-ct-ui-show-checked]').on('change', function() {
      var target = $(this).data('ct-ui-show-checked');
      if (this.value === 'on') {
        $('[data-ct-ui-checked-show-' + target + ']').removeClass('ui--removed');
        $('[data-ct-ui-checked-hide-' + target + ']').addClass('ui--removed');
      } else {
        $('[data-ct-ui-checked-show-' + target + ']').addClass('ui--removed');
        $('[data-ct-ui-checked-hide-' + target + ']').removeClass('ui--removed');
      }
    });

    $('[data-ct-ui-stage-change=click]').on('click', function() {
      advanceStage();
    });
    $('[data-ct-ui-stage-change=change]').on('change', function() {
      advanceStage();
    });
    $('[data-ct-ui-stage-change=checked]').on('change', function() {
      if (this.value === 'on') {
        advanceStage();
      }
    });
    $('[data-ct-ui-stage-change=complete]').on('blur', function() {
      if (this.value && this.value !== 'off') {
        advanceStage();
      }
    });

    $('[data-ct-ui-restart]').on('click', function() {
      document.location.reload(false);
    });

  };

  /* istanbul ignore next */
  var main = function () {

    $('[data-ct-data-new-item-input]').on('input change', function(e) {

      var modes = $('[data-ct-ui-new-item-mode]');

      if (!$(this).val()) {
        view.resetModeForm();
        return;
      }

      var mode = $(e.target).parents('[data-ct-ui-new-item-mode]');
      modes.each(function() {
        if ($(this).data('ct-ui-new-item-mode') === mode.data('ct-ui-new-item-mode')) {
          $(this).removeClass('disabled');
          $(this).data('ct-ui-data-new-item-active', true);
          $(this).find('input').prop('disabled', false);
        } else {
          $(this).addClass('disabled');
          $(this).data('ct-ui-data-new-item-active', false);
          $(this).find('input').prop('disabled', true);
        }
      });
    });

    $('[data-ct-ui-data-new-item-save]').on('click', function(e) {
      e.preventDefault();
      data.description = $('#new_item').val();
      var modes = $('[data-ct-ui-new-item-mode]');
      modes.each(function() {
        if ($(this).data('ct-ui-data-new-item-active')) {
          var amount = 0;
          var mode = $(this).data('ct-ui-new-item-mode');
          var inputs = $(this).find('[data-ct-data-new-item-input]');
          if (mode === 'absolute') {
            amount = utils.getInt(inputs[0].value);
          }
          if (mode === 'espresso') {
            amount = utils.getInt(inputs[0].value) * ESPRESSO;
          }
          if (mode === 'teabag') {
            amount = utils.getInt(inputs[0].value) * TEA_BAG;
          }
          if (mode === 'concentration') {
            var mg = utils.getInt(inputs[0].value);
            var ml = utils.getInt(inputs[1].value);
            var quantity = utils.getInt(inputs[2].value);
            switch ($(this).find('[name=measure]:checked').val()) {
              case 'ml':
                break;
              case 'cl':
                quantity = quantity * 10;
                break;
              case 'l':
                quantity = quantity * 1000;
                break;
            }
            amount = mg / ml * quantity;
          }
          data.caffeine = amount;
        }
      });
      advanceStage();
    });

    $('[data-ct-data-date]').on('click change', function() {
      data.timeType($(this).val(), $(this).data('ct-data-date'));
    });
  };

  /**
   * Sets the caffeine value from a select list of options.
   */
  var selectValue = function() {
    $(document).on('change input', '[data-ct-data-item]', function() {
      var item = $(this).data('ct-data-item');
      var value = $(this).val();
      /* istanbul ignore else */
      if (item === 'options') {
        var found = false;
        $(this).find('option').each(function() {
          if ($(this).attr('value') === value) {
            data.description = value;
            data.caffeine = utils.getInt($(this).data('ct-data-value'));
            found = true;
          }
        });
        if (!found) {
          data.description = '';
          data.caffeine = 0;
        }
      }
    });
  };

  /**
   * Sets a target when a data list item is selected.
   */
  var datalistValue = function() {
    $(document).on('input', '[data-ct-datalist-value]', function() {
      var value = $(this).val();
      var datalist = $('#' + $(this).attr('list'));
      var target = $('[data-ct-value-target-' + datalist.data('ct-value-target') + ']');

      var found = false;
      datalist.find('option').each(function() {
        if ($(this).val() === value) {
          target.val($(this).data('ct-data-value'));
          found = true;
        }
      });
      if (!found) {
        target.val('');
      }
      target.trigger('input');
    });
  };

  /**
   * Sets up the hash routing.
   */
  var hashRouter = function hashRouter() {
    $(window).on('hashchange', function () {
      if (window.location.hash) {
        var new_stage = parseInt(window.location.hash.slice(1), 10);
        /* istanbul ignore else */
        if (!isNaN(new_stage)) {
          stage.current = new_stage;
        }
      } else {
        stage.current = 1;
      }
      view.setStage();
    });
  };

  /* istanbul ignore next */
  module.exports.ready = function () {
    view.ready();
    setStage();
    datalistValue();
    selectValue();
    hashRouter();
    main();
  };
});
<commit_msg>Trapping radio button bug.<commit_after>/* global: exports */
define(function (require, exports, module) {
  "use strict";

  var $ = require('jquery');
  var view = require('./view');
  var utils = require('./utils');
  var model = require('./model');

  var ESPRESSO = 100; //mg
  var TEA_BAG = 40; //mg

  var data = model.record;
  var stage = model.stage;

  /* istanbul ignore next */
  var advanceStage = function() {
    stage.advance();
    view.setStage();
    window.location.hash = stage.current;
    var current_stage = $('[data-ct-ui-stage=' + stage.current + ']');
    if (current_stage.data('ct-ui-result')) {
      // This is the result stage
      setTimeout(showResult, 0); //TODO fix hack.
    }
  };

  /* istanbul ignore next */
  var showResult = function() {

    $('#id_time').val(data.timeString());
    $('#id_description').val(data.description);
    $('#id_caffeine').val(Math.round(data.caffeine));

    $('#record-form').trigger('submit');
  };

  /* istanbul ignore next */
  var setStage = function() {

    $('[data-ct-ui-show-target]').on('click', function() {
      var target = $(this).data('ct-ui-show-target');
      $('[data-ct-ui-show-' + target + ']').removeClass('ui--removed');
      $('[data-ct-ui-hide-' + target + ']').addClass('ui--removed');
    });

    $('[data-ct-ui-show-checked]').on('change', function() {
      var target = $(this).data('ct-ui-show-checked');
      if (this.value === 'on') {
        $('[data-ct-ui-checked-show-' + target + ']').removeClass('ui--removed');
        $('[data-ct-ui-checked-hide-' + target + ']').addClass('ui--removed');
      } else {
        $('[data-ct-ui-checked-show-' + target + ']').addClass('ui--removed');
        $('[data-ct-ui-checked-hide-' + target + ']').removeClass('ui--removed');
      }
    });

    $('[data-ct-ui-stage-change=click]').on('click', function() {
      advanceStage();
    });
    $('[data-ct-ui-stage-change=change]').on('change', function() {
      advanceStage();
    });
    $('[data-ct-ui-stage-change=checked]').on('change', function() {
      if (this.value === 'on') {
        advanceStage();
      }
    });
    $('[data-ct-ui-stage-change=complete]').on('blur', function() {
      if (this.value && this.value !== 'off') {
        advanceStage();
      }
    });

    $('[data-ct-ui-restart]').on('click', function() {
      document.location.reload(false);
    });

  };

  /* istanbul ignore next */
  var main = function () {

    $('[data-ct-data-new-item-input]').on('input change', function(e) {

      var modes = $('[data-ct-ui-new-item-mode]');

      if (!$(this).val() && !$(this).is(':checked')) {
        view.resetModeForm();
        return;
      }

      var mode = $(e.target).parents('[data-ct-ui-new-item-mode]');
      modes.each(function() {
        if ($(this).data('ct-ui-new-item-mode') === mode.data('ct-ui-new-item-mode')) {
          $(this).removeClass('disabled');
          $(this).data('ct-ui-data-new-item-active', true);
          $(this).find('input').prop('disabled', false);
        } else {
          $(this).addClass('disabled');
          $(this).data('ct-ui-data-new-item-active', false);
          $(this).find('input').prop('disabled', true);
        }
      });
    });

    $('[data-ct-ui-data-new-item-save]').on('click', function(e) {
      e.preventDefault();
      data.description = $('#new_item').val();
      var modes = $('[data-ct-ui-new-item-mode]');
      modes.each(function() {
        if ($(this).data('ct-ui-data-new-item-active')) {
          var amount = 0;
          var mode = $(this).data('ct-ui-new-item-mode');
          var inputs = $(this).find('[data-ct-data-new-item-input]');
          if (mode === 'absolute') {
            amount = utils.getInt(inputs[0].value);
          }
          if (mode === 'espresso') {
            amount = utils.getInt(inputs[0].value) * ESPRESSO;
          }
          if (mode === 'teabag') {
            amount = utils.getInt(inputs[0].value) * TEA_BAG;
          }
          if (mode === 'concentration') {
            var mg = utils.getInt(inputs[0].value);
            var ml = utils.getInt(inputs[1].value);
            var quantity = utils.getInt(inputs[2].value);
            switch ($(this).find('[name=measure]:checked').val()) {
              case 'ml':
                break;
              case 'cl':
                quantity = quantity * 10;
                break;
              case 'l':
                quantity = quantity * 1000;
                break;
            }
            amount = mg / ml * quantity;
          }
          data.caffeine = amount;
        }
      });
      advanceStage();
    });

    $('[data-ct-data-date]').on('click change', function() {
      data.timeType($(this).val(), $(this).data('ct-data-date'));
    });
  };

  /**
   * Sets the caffeine value from a select list of options.
   */
  var selectValue = function() {
    $(document).on('change input', '[data-ct-data-item]', function() {
      var item = $(this).data('ct-data-item');
      var value = $(this).val();
      /* istanbul ignore else */
      if (item === 'options') {
        var found = false;
        $(this).find('option').each(function() {
          if ($(this).attr('value') === value) {
            data.description = value;
            data.caffeine = utils.getInt($(this).data('ct-data-value'));
            found = true;
          }
        });
        if (!found) {
          data.description = '';
          data.caffeine = 0;
        }
      }
    });
  };

  /**
   * Sets a target when a data list item is selected.
   */
  var datalistValue = function() {
    $(document).on('input', '[data-ct-datalist-value]', function() {
      var value = $(this).val();
      var datalist = $('#' + $(this).attr('list'));
      var target = $('[data-ct-value-target-' + datalist.data('ct-value-target') + ']');

      var found = false;
      datalist.find('option').each(function() {
        if ($(this).val() === value) {
          target.val($(this).data('ct-data-value'));
          found = true;
        }
      });
      if (!found) {
        target.val('');
      }
      target.trigger('input');
    });
  };

  /**
   * Sets up the hash routing.
   */
  var hashRouter = function hashRouter() {
    $(window).on('hashchange', function () {
      if (window.location.hash) {
        var new_stage = parseInt(window.location.hash.slice(1), 10);
        /* istanbul ignore else */
        if (!isNaN(new_stage)) {
          stage.current = new_stage;
        }
      } else {
        stage.current = 1;
      }
      view.setStage();
    });
  };

  /* istanbul ignore next */
  module.exports.ready = function () {
    view.ready();
    setStage();
    datalistValue();
    selectValue();
    hashRouter();
    main();
  };
});
<|endoftext|>


<commit_before>"use strict";

var chalk = require('chalk');
var inquirer = require('inquirer');
var fs = require('fs-extra');
var path = require('path');
var extend = require('extend');
var log = require('loglevel');

var fileTemplater = require('../fileTemplater')();
var promptOptions = require('../promptOptions')();
var pkg = require(path.resolve(__dirname, '..', '..' ,'package.json'));

var _currentWorkingDir = process.cwd();
var _promptAnswers;
var _options;

module.exports = function(appDir) {

    return {
        init: init
    };

    function init(options) {
        _options = options;
        setUpLogLevel();
        checkIfWorkingDirIsEmpty();
    }

    function setUpLogLevel() {
        if(_options.silent) {
            log.setLevel(log.levels.SILENT);
        } else if(_options.verbose) {
            log.setLevel(log.levels.TRACE);
        } else {
            log.setLevel(log.levels.INFO);
        }
    }

    function checkIfWorkingDirIsEmpty() {
        fs.readdir(_currentWorkingDir, function(err, files) {
            if (err) return console.error(err);

            if(files.length > 1) {
                log.warn('');
                log.warn(chalk.red('Warning: The directory you are currently in is not empty!'));
                log.warn(chalk.red('Going through the setup will perform a clean slate installation.'));
                log.warn(chalk.red('This will overwrite any user changes'));
                log.warn('');

            } else {
                log.warn('');
                log.warn(chalk.bold('Running through setup for a new project.'));
                log.warn(chalk.bold('This can be exited out by pressing [Ctrl+C]'));
                log.warn('');
            }

            log.warn(chalk.bold('Make sure you are running this command in the folder you want all files copied to'));
            log.warn('')

            inquirer.prompt(promptOptions.getNewCommandPromptOptions(), inquirerCallback);
        })
    }

    function getTemplateData() {
        var date = new Date();

        return {
            projectNameFileName: _promptAnswers.projectName.toLowerCase().replace(/ /g,"-"),
            projectGeneratedDate: [date.getDate(), date.getMonth() + 1, date.getFullYear()].join('/'),
            slateCurrentVersion: pkg.version
        }
    }

    function inquirerCallback(answers) {
        _promptAnswers = answers;

        if(_promptAnswers.isOkToCopyFiles) {
            var filesDirsToExclude = getExcludeList();

            log.info('');
            log.info('Copying over files...');

            fs.copy(appDir, process.cwd(), {
                filter: function(path) {
                    var needToCopyFile = true;

                    for (var i = 0; i < filesDirsToExclude.length; i++) {
                        //needToCopyFile is still true
                        //Hasn't been flipped during loop
                        if(needToCopyFile) {
                            needToCopyFile = path.indexOf(filesDirsToExclude[i]) === -1;
                        }
                    };

                    if(!needToCopyFile) {
                        log.debug(chalk.underline('Skipping path - ' + path));
                    } else {
                        log.debug('Copying path  -', path);
                    }

                    return needToCopyFile;
                }
            }, function (err) {
                if (err) return console.error(err);

                templateCopiedFiles();
            })

        } else {
            log.info('User cancelled - no files copied')
        }
    }

    function getExcludeList() {
        //Default exclude folders / files
        var excludeList = [
            'node_modules'
        ];

        if(_promptAnswers.projectType === "Dot NET") {
            excludeList.push('views');
            excludeList.push('release.js');
        }

        return excludeList;
    }

    function templateCopiedFiles() {
        log.debug('');
        log.info('Templating files...');

        var templateData = extend({}, _promptAnswers, getTemplateData())

        fileTemplater.setConfig({
            data: templateData,
            basePath: process.cwd(),
            files: getTemplateFileList(),
            onEachFile: singleFileCallback,
            onCompleted: templateFinished
        })

        fileTemplater.run();
    }

    function getTemplateFileList() {
        var fileList = [];

        fileList.push(path.join('_config', 'creds.json'));
        fileList.push('package.json')
        fileList.push('.slaterc');

        return fileList;
    }

    function singleFileCallback(templateFilePath) {
        log.debug('Templating file -', templateFilePath);
    }

    function templateFinished() {
        log.info('');
        log.info(chalk.green('Setup complete!'));
        log.info('Slate project ' + chalk.yellow(_promptAnswers.projectName) + ' has been installed!');
        log.info('');
        log.info('Final steps:');
        log.info(' · Run ' + chalk.yellow('npm install') + ' to setup all dependencies');
        log.info(' · Run ' + chalk.yellow('gulp') + ' for initial setup, ' + chalk.yellow('gulp watch') + ' to setup watching of files');
        log.info('');
    }
}<commit_msg>refactor: Extract out behaviour for file copy behaviour into functions<commit_after>"use strict";

var chalk = require('chalk');
var inquirer = require('inquirer');
var fs = require('fs-extra');
var path = require('path');
var extend = require('extend');
var log = require('loglevel');

var fileTemplater = require('../fileTemplater')();
var promptOptions = require('../promptOptions')();
var pkg = require(path.resolve(__dirname, '..', '..' ,'package.json'));

var _currentWorkingDir = process.cwd();
var _promptAnswers;
var _options;

module.exports = function(appDir) {

    return {
        init: init
    };

    function init(options) {
        _options = options;
        setUpLogLevel();
        checkIfWorkingDirIsEmpty();
    }

    function setUpLogLevel() {
        if(_options.silent) {
            log.setLevel(log.levels.SILENT);
        } else if(_options.verbose) {
            log.setLevel(log.levels.TRACE);
        } else {
            log.setLevel(log.levels.INFO);
        }
    }

    function checkIfWorkingDirIsEmpty() {
        fs.readdir(_currentWorkingDir, function(err, files) {
            if (err) return console.error(err);

            if(files.length > 1) {
                log.warn('');
                log.warn(chalk.red('Warning: The directory you are currently in is not empty!'));
                log.warn(chalk.red('Going through the setup will perform a clean slate installation.'));
                log.warn(chalk.red('This will overwrite any user changes'));
                log.warn('');

            } else {
                log.warn('');
                log.warn(chalk.bold('Running through setup for a new project.'));
                log.warn(chalk.bold('This can be exited out by pressing [Ctrl+C]'));
                log.warn('');
            }

            log.warn(chalk.bold('Make sure you are running this command in the folder you want all files copied to'));
            log.warn('')

            inquirer.prompt(promptOptions.getNewCommandPromptOptions(), promptCallback);
        })
    }

    function getTemplateData() {
        var date = new Date();

        return {
            projectNameFileName: _promptAnswers.projectName.toLowerCase().replace(/ /g,"-"),
            projectGeneratedDate: [date.getDate(), date.getMonth() + 1, date.getFullYear()].join('/'),
            slateCurrentVersion: pkg.version
        }
    }

    function promptCallback(answers) {
        _promptAnswers = answers;

        if(_promptAnswers.isOkToCopyFiles) {

            log.info('');
            log.info('Copying over files...');

            fs.copy(appDir, process.cwd(), {
                filter: fileCopyFilter
            }, fileCopyComplete)

        } else {
            log.info('User cancelled - no files copied')
        }
    }

    function fileCopyFilter(path) {
        var needToCopyFile = true;
        var filesDirsToExclude = getExcludeList();

        for (var i = 0; i < filesDirsToExclude.length; i++) {
            //Check if needToCopyFile is still true and
            //hasn't been flipped during loop
            if(needToCopyFile) {
                needToCopyFile = path.indexOf(filesDirsToExclude[i]) === -1;
            }
        };

        if(!needToCopyFile) {
            log.debug(chalk.underline('Skipping path - ' + path));
        } else {
            log.debug('Copying path  -', path);
        }

        return needToCopyFile;
    }

    function fileCopyComplete(err) {
        if (err) return console.error(err);

        templateCopiedFiles();
    }

    function getExcludeList() {
        //Default exclude folders / files
        var excludeList = [
            'node_modules'
        ];

        if(_promptAnswers.projectType === "Dot NET") {
            excludeList.push('views');
            excludeList.push('release.js');
        }

        return excludeList;
    }

    function templateCopiedFiles() {
        log.debug('');
        log.info('Templating files...');

        var templateData = extend({}, _promptAnswers, getTemplateData())

        fileTemplater.setConfig({
            data: templateData,
            basePath: process.cwd(),
            files: getTemplateFileList(),
            onEachFile: singleFileCallback,
            onCompleted: templateFinished
        })

        fileTemplater.run();
    }

    function getTemplateFileList() {
        var fileList = [];

        fileList.push(path.join('_config', 'creds.json'));
        fileList.push('package.json')
        fileList.push('.slaterc');

        return fileList;
    }

    function singleFileCallback(templateFilePath) {
        log.debug('Templating file -', templateFilePath);
    }

    function templateFinished() {
        log.info('');
        log.info(chalk.green('Setup complete!'));
        log.info('Slate project ' + chalk.yellow(_promptAnswers.projectName) + ' has been installed!');
        log.info('');
        log.info('Final steps:');
        log.info(' · Run ' + chalk.yellow('npm install') + ' to setup all dependencies');
        log.info(' · Run ' + chalk.yellow('gulp') + ' for initial setup, ' + chalk.yellow('gulp watch') + ' to setup watching of files');
        log.info('');
    }
}<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict';

const fConfig = require('../net2/config.js').getConfig();
const fs = require('fs');
const Promise = require('bluebird');
Promise.promisifyAll(fs);

class Platform {
  async getNetworkSpeed() {
    const output = await fs.readFileAsync(`/sys/class/net/${fConfig.monitoringInterface}/speed`, {encoding: 'utf8'});
    return output;
  }
}

module.exports = Platform;
<commit_msg>trim before return<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict';

const fConfig = require('../net2/config.js').getConfig();
const fs = require('fs');
const Promise = require('bluebird');
Promise.promisifyAll(fs);

class Platform {
  async getNetworkSpeed() {
    const output = await fs.readFileAsync(`/sys/class/net/${fConfig.monitoringInterface}/speed`, {encoding: 'utf8'});
    return output.trim();
  }
}

module.exports = Platform;
<|endoftext|>


<commit_before>// Meteor
import { Mongo } from 'meteor/mongo';
import { Factory } from 'meteor/factory';

// Utilities
import { _ } from 'meteor/underscore';
import moment from 'moment';

// API
import { relatedRecordReconcileEvent } from '../../api/relatedRecords/relatedRecords.js';
import { Participants } from '../../api/participants/participants.js';

// Methods
import { upsert as upsertLocality } from '../localities/methods.js';
import { upsert as upsertAdministrativeArea } from '../administrativeAreas/methods.js';
import { upsert as upsertCountry } from '../countries/methods.js';

class EventsCollection extends Mongo.Collection {
  insert(event, callback) {
    const ourEvent = event;
    if (ourEvent.startDate && ourEvent.endDate) {
      // Set all the dates to 8pm
      const startMoment = moment(ourEvent.startDate);
      const endMoment = moment(ourEvent.endDate);
      ourEvent.startDate = startMoment.hours(20).toDate();
      ourEvent.endDate = endMoment.hours(20).toDate();
    }

    if (!_.isEmpty(ourEvent.locality)) {
      upsertLocality.call({ locality: ourEvent.locality });
    }
    if (!_.isEmpty(ourEvent.administrativeArea)) {
      upsertAdministrativeArea.call({ administrativeArea: ourEvent.administrativeArea });
    }
    if (!_.isEmpty(ourEvent.country)) {
      upsertCountry.call({ country: ourEvent.country });
    }

    relatedRecordReconcileEvent({
      event: ourEvent,
      profileId: ourEvent.organizations._id,
    });

    // @TODO: Save author information to event
    //        Or maybe check to make sure the correct author is passed in?
    return super.insert(ourEvent, callback);
  }
  update(eventId, event) {
    const ourEvent = event.$set;
    if (ourEvent.startDate && ourEvent.endDate) {
      // Set the dates to 8pm
      const startMoment = moment(ourEvent.startDate);
      const endMoment = moment(ourEvent.endDate);
      ourEvent.startDate = startMoment.hours(20).toDate();
      ourEvent.endDate = endMoment.hours(20).toDate();
    }

    if (!_.isEmpty(ourEvent.locality)) {
      upsertLocality.call({ locality: ourEvent.locality });
    }
    if (!_.isEmpty(ourEvent.administrativeArea)) {
      upsertAdministrativeArea.call({ administrativeArea: ourEvent.administrativeArea });
    }
    if (!_.isEmpty(ourEvent.country)) {
      upsertCountry.call({ country: ourEvent.country });
    }

    relatedRecordReconcileEvent({
      event: event,
      profileId: event.organizations._id,
    });

    // @TODO: Save author information to event
    //        Or maybe check to make sure the correct author is passed in?
    return super.update(eventId, {
      $set: ourEvent,
    });
  }
  remove(selector, callback) {
    return super.remove(selector, callback);
  }
}

export const Events = new EventsCollection('Events');

// Deny all client-side updates since we will be using methods to manage this collection
Events.deny({
  insert() { return true; },
  update() { return true; },
  remove() { return true; },
});

// export const relatedProfileSchema = t.struct({
//   name: t.String,
//   _id: t.String,
// });

// This represents the keys from Events objects that should be published
// to the client. If we add secret properties to Event objects, don't event
// them here to keep them private to the server.
Events.publicFields = {
  show: 1,
  organizations: 1,
  startDate: 1,
  endDate: 1,
  streetAddress: 1,
  locality: 1,
  administrativeArea: 1,
  country: 1,
  lat: 1,
  lon: 1,
  about: 1,
  eventType: 1,
};

Factory.define('event', Events, {});

// Events.helpers({
//   // A event is considered to be private if it has a userId set
//   isPrivate() {
//     return !!this.userId;
//   },
//   isLastPublicEvent() {
//     const publicEventCount = Events.find({ userId: { $exists: false } }).count();
//     return !this.isPrivate() && publicEventCount === 1;
//   },
//   editableBy(userId) {
//     if (!this.userId) {
//       return true;
//     }

//     return this.userId === userId;
//   },
//   todos() {
//     return Todos.find({ eventId: this._id }, { sort: { createdAt: -1 } });
//   },
// });
<commit_msg>Use cloned variable name to prevent undefined errors in event insert/update.<commit_after>// Meteor
import { Mongo } from 'meteor/mongo';
import { Factory } from 'meteor/factory';

// Utilities
import { _ } from 'meteor/underscore';
import moment from 'moment';

// API
import { relatedRecordReconcileEvent } from '../../api/relatedRecords/relatedRecords.js';
import { Participants } from '../../api/participants/participants.js';

// Methods
import { upsert as upsertLocality } from '../localities/methods.js';
import { upsert as upsertAdministrativeArea } from '../administrativeAreas/methods.js';
import { upsert as upsertCountry } from '../countries/methods.js';

class EventsCollection extends Mongo.Collection {
  insert(event, callback) {
    const ourEvent = event;
    if (ourEvent.startDate && ourEvent.endDate) {
      // Set all the dates to 8pm
      const startMoment = moment(ourEvent.startDate);
      const endMoment = moment(ourEvent.endDate);
      ourEvent.startDate = startMoment.hours(20).toDate();
      ourEvent.endDate = endMoment.hours(20).toDate();
    }

    if (!_.isEmpty(ourEvent.locality)) {
      upsertLocality.call({ locality: ourEvent.locality });
    }
    if (!_.isEmpty(ourEvent.administrativeArea)) {
      upsertAdministrativeArea.call({ administrativeArea: ourEvent.administrativeArea });
    }
    if (!_.isEmpty(ourEvent.country)) {
      upsertCountry.call({ country: ourEvent.country });
    }

    relatedRecordReconcileEvent({
      event: ourEvent,
      profileId: ourEvent.organizations._id,
    });

    // @TODO: Save author information to event
    //        Or maybe check to make sure the correct author is passed in?
    return super.insert(ourEvent, callback);
  }
  update(eventId, event) {
    const ourEvent = event.$set;
    if (ourEvent.startDate && ourEvent.endDate) {
      // Set the dates to 8pm
      const startMoment = moment(ourEvent.startDate);
      const endMoment = moment(ourEvent.endDate);
      ourEvent.startDate = startMoment.hours(20).toDate();
      ourEvent.endDate = endMoment.hours(20).toDate();
    }

    if (!_.isEmpty(ourEvent.locality)) {
      upsertLocality.call({ locality: ourEvent.locality });
    }
    if (!_.isEmpty(ourEvent.administrativeArea)) {
      upsertAdministrativeArea.call({ administrativeArea: ourEvent.administrativeArea });
    }
    if (!_.isEmpty(ourEvent.country)) {
      upsertCountry.call({ country: ourEvent.country });
    }

    relatedRecordReconcileEvent({
      event: ourEvent,
      profileId: ourEvent.organizations._id,
    });

    // @TODO: Save author information to event
    //        Or maybe check to make sure the correct author is passed in?
    return super.update(eventId, {
      $set: ourEvent,
    });
  }
  remove(selector, callback) {
    return super.remove(selector, callback);
  }
}

export const Events = new EventsCollection('Events');

// Deny all client-side updates since we will be using methods to manage this collection
Events.deny({
  insert() { return true; },
  update() { return true; },
  remove() { return true; },
});

// export const relatedProfileSchema = t.struct({
//   name: t.String,
//   _id: t.String,
// });

// This represents the keys from Events objects that should be published
// to the client. If we add secret properties to Event objects, don't event
// them here to keep them private to the server.
Events.publicFields = {
  show: 1,
  organizations: 1,
  startDate: 1,
  endDate: 1,
  streetAddress: 1,
  locality: 1,
  administrativeArea: 1,
  country: 1,
  lat: 1,
  lon: 1,
  about: 1,
  eventType: 1,
};

Factory.define('event', Events, {});

// Events.helpers({
//   // A event is considered to be private if it has a userId set
//   isPrivate() {
//     return !!this.userId;
//   },
//   isLastPublicEvent() {
//     const publicEventCount = Events.find({ userId: { $exists: false } }).count();
//     return !this.isPrivate() && publicEventCount === 1;
//   },
//   editableBy(userId) {
//     if (!this.userId) {
//       return true;
//     }

//     return this.userId === userId;
//   },
//   todos() {
//     return Todos.find({ eventId: this._id }, { sort: { createdAt: -1 } });
//   },
// });
<|endoftext|>


<commit_before>/*
 *  Original songs in piano trio repertoire
 */

var pianoTrioSongs = {

	partsInBooks: {
		"Concert Lead Sheets": {
			"Anne's Eyes": [ "Concert Arrangement" ],
			"Aphrodite's Girl Friday": [ "Concert Lead Sheet" ],
			"Big Seven": [ "Concert Lead Sheet" ],
			"Hot Swing": [ "Concert Lead Sheet" ],
			"Kickin' The Can": [ "Concert Lead Sheet" ],
			"Linsanity": [ "Concert Lead Sheet" ],
			"Swampy": [ "Concert Lead Sheet" ],
			"Thick As A Nickle": [ "Concert Lead Sheet" ],
			"Wag That Tail": [ "Concert Lead Sheet" ]
		},
		"Bb Instruments": {
			"Anne's Eyes": [ "Bb Lead Sheet" ],
			"Aphrodite's Girl Friday": [ "Bb Lead Sheet" ],
			"Big Seven": [ "Bb Melody" ],
			"Hot Swing": [ "Clarinet in Bb" ],
			"Kickin' The Can": [ "Bb Lead Sheet", "Bb Melody", "Bb Harmony" ],
			"Linsanity": [ "Bb Lead Sheet", "Bb Harmony" ],
			"Swampy": [ "Bb Lead Sheet" ],
			"Thick As A Nickle": [ "Bb Melody", "Bb Harmony" ],
			"Wag That Tail": [ "Bb Lead Sheet", "Bb Melody", "Bb Harmony" ]
		},
		"Eb Instruments": {
			"Anne's Eyes": [ "Bb Lead Sheet" ],
			"Aphrodite's Girl Friday": [ "Eb Lead Sheet" ],
			"Big Seven": [ "Eb Melody" ],
			"Kickin' The Can": [ "Eb Melody", "Eb Harmony" ],
			"Linsanity": [ "Eb Lead Sheet", "Eb Harmony" ],
			"Swampy": [ "Eb Lead Sheet" ],
			"Thick As A Nickle": [ "Eb Melody", "Eb Harmony" ],
			"Wag That Tail": [ "Eb Lead Sheet", "Eb Melody", "Eb Harmony" ]
		},
		"Bass": {
			"Anne's Eyes": [ "Bass" ],
			"Aphrodite's Girl Friday": [ "Concert Lead Sheet" ],
			"Big Seven": [ "Bass" ],
			"Hot Swing": [ "Violin" ],
			"Kickin' The Can": [ "Bass" ],
			"Linsanity": [ "Concert Lead Sheet" ],
			"Swampy": [ "Bass" ],
			"Thick As A Nickle": [ "Bass" ],
			"Wag That Tail": [ "Bass" ]
		}
	},

	songs: {
		"Anne's Eyes": {
			metadata: {
				title: "Anne's Eyes",
				composer: "D. Elaine Alt",
				arranger: "D. Elaine Alt",
				genre: "Big Band Swing",
				bpm: '152',
				key: "Ab Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "annes-eyes/annes-eyes-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-song-form" },
				"Concert Arrangement": { fileSuffix: "Lead" },
				"Bb Lead Sheet": { fileSuffix: "Lead-in-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
			},
			recordings: {
				trio: {
					description: 'Trio',
					url: 'https://soundcloud.com/david-elaine-alt/annes-eyes-sticks'
				},
				quartet: {
					description: 'Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/lester-eyes'
				}
			}
		},

		"Aphrodite's Girl Friday": {
			metadata: {
				title: "Aphrodite's Girl Friday",
				composer: "Elaine Paul",
				genre: "Ballad",
				bpm: '108',
				key: "Db Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "aphrodites-girl-friday/aphrodites-girl-friday-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Bb Lead Sheet": { fileSuffix: "Lead-Sheet-for-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-Sheet-for-Eb" }
			},
			scores: {
			},
			recordings: {
				midi: {
					description: 'Synth',
					url: 'https://soundcloud.com/david-elaine-alt/aphrodites-girl-friday-midi/s-E1nx5'
				}
			}
		},

		"Big Seven": {
			metadata: {
				title: "Big Seven",
				composer: "Elaine Paul",
				genre: "Hip Hop/Funk",
				bpm: '64',
				key: "Db Major"
			},
			fileLocation: "alt.musica",
			filePrefix: "big-seven/combo/big-seven-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Concert Melody": { fileSuffix: "Melody" },
				"Bb Melody": { fileSuffix: "Melody-for-Bb" },
				"Eb Melody": { fileSuffix: "Melody-for-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
			},
			recordings: {
				midi: {
					description: 'Synth',
					url: 'https://soundcloud.com/david-elaine-alt/big-seven-synth/s-LUEjT'
				}
			}
		},

		"Hot Swing": {
			metadata: {
				title: "Hot Swing",
				composer: "Elaine Paul",
				genre: "Swing",
				bpm: '230',
				key: "D minor"
			},
			fileLocation: "violin-clarinet",
			filePrefix: "hot-swing-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Clarinet in Bb": { fileSuffix: "Bb-Clarinet" },
				"Violin": { fileSuffix: "Violin" },
			},
			scores: {
				"Score": { fileSuffix: "Score" }
			},
			recordings: {
				trio: {
					description: 'Violin & Clarinet (Chart starts at 1:06)',
					url: 'https://soundcloud.com/david-elaine-alt/hot-swing#t=1:06'
				}
			}
		},

		"Kickin' The Can": {
			metadata: {
				title: "Kickin' The Can",
				composer: "D. Elaine Alt",
				genre: "Jive Swing",
				bpm: '210',
				key: "E minor"
			},
			fileLocation: "piano-trio",
			filePrefix: "kickin-the-can/kickin-the-can-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead" },
				"Bb Lead Sheet": { fileSuffix: "Lead-in-Bb" },
				"Bb Melody": { fileSuffix: "Melody-in-Bb" },
				"Bb Harmony": { fileSuffix: "Harmony-in-Bb" },
				"Eb Melody": { fileSuffix: "Melody-in-Eb" },
				"Eb Harmony": { fileSuffix: "Harmony-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Condensed Score": { fileSuffix: "Rhythm" },
				"Score": { fileSuffix: "Score" }
			},
			recordings: {
				trio: {
					description: 'Piano Trio with Scat Vocals',
					url: 'https://soundcloud.com/david-elaine-alt/kickin-the-can-so-woke-brushes'
				}
			}
		},

		"Linsanity": {
			metadata: {
				title: "Linsanity",
				composer: "Elaine Paul",
				genre: "Bebop",
				bpm: '180',
				key: "A Major"
			},
			fileLocation: "jazz-combustion",
			filePrefix: "linsanity/linsanity-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Bb Lead Sheet": { fileSuffix: "Lead-Sheet-for-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-Sheet-for-Eb" },
				"Concert Harmony": { fileSuffix: "Harmony" },
				"Bb Harmony": { fileSuffix: "Harmony-for-Bb" },
				"Eb Harmony": { fileSuffix: "Harmony-for-Eb" }
			},
			scores: {
			},
			recordings: {
				synth: {
					description: 'Synth',
					url: 'https://soundcloud.com/david-elaine-alt/linsanity-midi/s-x9zZv'
				}
			}
		},

		"Swampy": {
			metadata: {
				title: "Swampy",
				composer: "D. Elaine Alt",
				genre: "Ballad",
				bpm: '76',
				key: "Db Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "swampy/swampy-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Bb Lead Sheet": { fileSuffix: "Lead-Sheet-in-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-Sheet-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed Score": { fileSuffix: "Transposed-Score" },
				"Concert Score": { fileSuffix: "Concert-Score" }
			},
			recordings: {
				trio: {
					description: 'Piano Trio',
					url: 'https://soundcloud.com/david-elaine-alt/swampy'
				}
			}
		},

		"Thick As A Nickle": {
			metadata: {
				title: "Thick As A Nickle",
				composer: "D. Elaine Alt",
				genre: "Bebop",
				bpm: '184',
				key: "Bb Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "thick-as-a-nickle/thick-as-a-nickle-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead" },
				"Bb Melody": { fileSuffix: "Melody-in-Bb" },
				"Bb Harmony": { fileSuffix: "Harmony-in-Bb" },
				"Eb Melody": { fileSuffix: "Melody-in-Eb" },
				"Eb Harmony": { fileSuffix: "Harmony-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Condensed Score": { fileSuffix: "Rhythm" },
				"Score": { fileSuffix: "Score" }
			},
			recordings: {
				trio: {
					description: 'Piano Trio (as written)',
					url: 'https://soundcloud.com/david-elaine-alt/thick-as-a-nickle'
				},
				arrangment: {
					description: 'Piano Trio (arrangement)',
					url: 'https://soundcloud.com/david-elaine-alt/thick-as-a-nickle-sticks'
				}
			}
		},

		"Wag That Tail": {
			metadata: {
				title: "Wag That Tail",
				composer: "D. Elaine Alt",
				genre: "Swing Blues",
				bpm: '172',
				key: "F Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "wag-that-tail/wag-that-tail-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead" },
				"Bb Lead Sheet": { fileSuffix: "Lead-in-Bb" },
				"Bb Melody": { fileSuffix: "Melody-in-Bb" },
				"Bb Harmony": { fileSuffix: "Harmony-in-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-in-Eb" },
				"Eb Melody": { fileSuffix: "Melody-in-Eb" },
				"Eb Harmony": { fileSuffix: "Harmony-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Score": { fileSuffix: "Score" }
			},
			recordings: {
				trio: {
					description: 'Piano Trio',
					url: 'https://soundcloud.com/david-elaine-alt/wag-that-tail'
				}
			}
		}
	}
};

<commit_msg>adding links to recordings for piano trio rep, from spring 2019 open studios<commit_after>/*
 *  Original songs in piano trio repertoire
 */

var pianoTrioSongs = {

	partsInBooks: {
		"Concert Lead Sheets": {
			"Anne's Eyes": [ "Concert Arrangement" ],
			"Aphrodite's Girl Friday": [ "Concert Lead Sheet" ],
			"Big Seven": [ "Concert Lead Sheet" ],
			"Hot Swing": [ "Concert Lead Sheet" ],
			"Kickin' The Can": [ "Concert Lead Sheet" ],
			"Linsanity": [ "Concert Lead Sheet" ],
			"Swampy": [ "Concert Lead Sheet" ],
			"Thick As A Nickle": [ "Concert Lead Sheet" ],
			"Wag That Tail": [ "Concert Lead Sheet" ]
		},
		"Bb Instruments": {
			"Anne's Eyes": [ "Bb Lead Sheet" ],
			"Aphrodite's Girl Friday": [ "Bb Lead Sheet" ],
			"Big Seven": [ "Bb Melody" ],
			"Hot Swing": [ "Clarinet in Bb" ],
			"Kickin' The Can": [ "Bb Lead Sheet", "Bb Melody", "Bb Harmony" ],
			"Linsanity": [ "Bb Lead Sheet", "Bb Harmony" ],
			"Swampy": [ "Bb Lead Sheet" ],
			"Thick As A Nickle": [ "Bb Melody", "Bb Harmony" ],
			"Wag That Tail": [ "Bb Lead Sheet", "Bb Melody", "Bb Harmony" ]
		},
		"Eb Instruments": {
			"Anne's Eyes": [ "Bb Lead Sheet" ],
			"Aphrodite's Girl Friday": [ "Eb Lead Sheet" ],
			"Big Seven": [ "Eb Melody" ],
			"Kickin' The Can": [ "Eb Melody", "Eb Harmony" ],
			"Linsanity": [ "Eb Lead Sheet", "Eb Harmony" ],
			"Swampy": [ "Eb Lead Sheet" ],
			"Thick As A Nickle": [ "Eb Melody", "Eb Harmony" ],
			"Wag That Tail": [ "Eb Lead Sheet", "Eb Melody", "Eb Harmony" ]
		},
		"Bass": {
			"Anne's Eyes": [ "Bass" ],
			"Aphrodite's Girl Friday": [ "Concert Lead Sheet" ],
			"Big Seven": [ "Bass" ],
			"Hot Swing": [ "Violin" ],
			"Kickin' The Can": [ "Bass" ],
			"Linsanity": [ "Concert Lead Sheet" ],
			"Swampy": [ "Bass" ],
			"Thick As A Nickle": [ "Bass" ],
			"Wag That Tail": [ "Bass" ]
		}
	},

	songs: {
		"Anne's Eyes": {
			metadata: {
				title: "Anne's Eyes",
				composer: "D. Elaine Alt",
				arranger: "D. Elaine Alt",
				genre: "Big Band Swing",
				bpm: '152',
				key: "Ab Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "annes-eyes/annes-eyes-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-song-form" },
				"Concert Arrangement": { fileSuffix: "Lead" },
				"Bb Lead Sheet": { fileSuffix: "Lead-in-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
			},
			recordings: {
				trio: {
					description: 'Trio',
					url: 'https://soundcloud.com/david-elaine-alt/annes-eyes-sticks'
				},
				quartet: {
					description: 'Studio Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/lester-eyes'
				},
				openstudios: {
					description: 'Live Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/annes-eyes'
				}
			}
		},

		"Aphrodite's Girl Friday": {
			metadata: {
				title: "Aphrodite's Girl Friday",
				composer: "Elaine Paul",
				genre: "Ballad",
				bpm: '108',
				key: "Db Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "aphrodites-girl-friday/aphrodites-girl-friday-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Bb Lead Sheet": { fileSuffix: "Lead-Sheet-for-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-Sheet-for-Eb" }
			},
			scores: {
			},
			recordings: {
				midi: {
					description: 'Synth',
					url: 'https://soundcloud.com/david-elaine-alt/aphrodites-girl-friday-midi/s-E1nx5'
				},
				quartet: {
					description: 'Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/aphrodites-girl-friday'
				}
			}
		},

		"Big Seven": {
			metadata: {
				title: "Big Seven",
				composer: "Elaine Paul",
				genre: "Hip Hop/Funk",
				bpm: '64',
				key: "Db Major"
			},
			fileLocation: "alt.musica",
			filePrefix: "big-seven/combo/big-seven-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Concert Melody": { fileSuffix: "Melody" },
				"Bb Melody": { fileSuffix: "Melody-for-Bb" },
				"Eb Melody": { fileSuffix: "Melody-for-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
			},
			recordings: {
				midi: {
					description: 'Synth',
					url: 'https://soundcloud.com/david-elaine-alt/big-seven-synth/s-LUEjT'
				},
				quartet: {
					description: 'Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/big-seven'
				}
			}
		},

		"Hot Swing": {
			metadata: {
				title: "Hot Swing",
				composer: "Elaine Paul",
				genre: "Swing",
				bpm: '230',
				key: "D minor"
			},
			fileLocation: "violin-clarinet",
			filePrefix: "hot-swing-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Clarinet in Bb": { fileSuffix: "Bb-Clarinet" },
				"Violin": { fileSuffix: "Violin" },
			},
			scores: {
				"Score": { fileSuffix: "Score" }
			},
			recordings: {
				trio: {
					description: 'Violin & Clarinet (Chart starts at 1:06)',
					url: 'https://soundcloud.com/david-elaine-alt/hot-swing#t=1:06'
				},
				quartet: {
					description: 'Jazz quartet',
					url: 'https://soundcloud.com/david-elaine-alt/hot-swing-1'
				}
			}
		},

		"Kickin' The Can": {
			metadata: {
				title: "Kickin' The Can",
				composer: "D. Elaine Alt",
				genre: "Jive Swing",
				bpm: '210',
				key: "E minor"
			},
			fileLocation: "piano-trio",
			filePrefix: "kickin-the-can/kickin-the-can-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead" },
				"Bb Lead Sheet": { fileSuffix: "Lead-in-Bb" },
				"Bb Melody": { fileSuffix: "Melody-in-Bb" },
				"Bb Harmony": { fileSuffix: "Harmony-in-Bb" },
				"Eb Melody": { fileSuffix: "Melody-in-Eb" },
				"Eb Harmony": { fileSuffix: "Harmony-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Condensed Score": { fileSuffix: "Rhythm" },
				"Score": { fileSuffix: "Score" }
			},
			recordings: {
				trio: {
					description: 'Piano Trio with Scat Vocals',
					url: 'https://soundcloud.com/david-elaine-alt/kickin-the-can-so-woke-brushes'
				},
				quartet: {
					description: 'Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/kickin-the-can'
				}
			}
		},

		"Linsanity": {
			metadata: {
				title: "Linsanity",
				composer: "Elaine Paul",
				genre: "Bebop",
				bpm: '180',
				key: "A Major"
			},
			fileLocation: "jazz-combustion",
			filePrefix: "linsanity/linsanity-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Bb Lead Sheet": { fileSuffix: "Lead-Sheet-for-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-Sheet-for-Eb" },
				"Concert Harmony": { fileSuffix: "Harmony" },
				"Bb Harmony": { fileSuffix: "Harmony-for-Bb" },
				"Eb Harmony": { fileSuffix: "Harmony-for-Eb" }
			},
			scores: {
			},
			recordings: {
				synth: {
					description: 'Synth',
					url: 'https://soundcloud.com/david-elaine-alt/linsanity-midi/s-x9zZv'
				},
				quartet: {
					description: 'Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/linsanity'
				}
			}
		},

		"Swampy": {
			metadata: {
				title: "Swampy",
				composer: "D. Elaine Alt",
				genre: "Ballad",
				bpm: '76',
				key: "Db Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "swampy/swampy-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead-Sheet" },
				"Bb Lead Sheet": { fileSuffix: "Lead-Sheet-in-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-Sheet-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed Score": { fileSuffix: "Transposed-Score" },
				"Concert Score": { fileSuffix: "Concert-Score" }
			},
			recordings: {
				trio: {
					description: 'Piano Trio',
					url: 'https://soundcloud.com/david-elaine-alt/swampy'
				},
				quartet: {
					description: 'Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/swampy-1'
				}
			}
		},

		"Thick As A Nickle": {
			metadata: {
				title: "Thick As A Nickle",
				composer: "D. Elaine Alt",
				genre: "Bebop",
				bpm: '184',
				key: "Bb Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "thick-as-a-nickle/thick-as-a-nickle-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead" },
				"Bb Melody": { fileSuffix: "Melody-in-Bb" },
				"Bb Harmony": { fileSuffix: "Harmony-in-Bb" },
				"Eb Melody": { fileSuffix: "Melody-in-Eb" },
				"Eb Harmony": { fileSuffix: "Harmony-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Condensed Score": { fileSuffix: "Rhythm" },
				"Score": { fileSuffix: "Score" }
			},
			recordings: {
				trio: {
					description: 'Piano Trio (as written)',
					url: 'https://soundcloud.com/david-elaine-alt/thick-as-a-nickle'
				},
				arrangment: {
					description: 'Piano Trio (arrangement)',
					url: 'https://soundcloud.com/david-elaine-alt/thick-as-a-nickle-sticks'
				},
				quartet: {
					description: 'Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/thick-as-a-nickle-1'
				}
			}
		},

		"Wag That Tail": {
			metadata: {
				title: "Wag That Tail",
				composer: "D. Elaine Alt",
				genre: "Swing Blues",
				bpm: '172',
				key: "F Major"
			},
			fileLocation: "piano-trio",
			filePrefix: "wag-that-tail/wag-that-tail-",
			parts: {
				"Concert Lead Sheet": { fileSuffix: "Lead" },
				"Bb Lead Sheet": { fileSuffix: "Lead-in-Bb" },
				"Bb Melody": { fileSuffix: "Melody-in-Bb" },
				"Bb Harmony": { fileSuffix: "Harmony-in-Bb" },
				"Eb Lead Sheet": { fileSuffix: "Lead-in-Eb" },
				"Eb Melody": { fileSuffix: "Melody-in-Eb" },
				"Eb Harmony": { fileSuffix: "Harmony-in-Eb" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Score": { fileSuffix: "Score" }
			},
			recordings: {
				trio: {
					description: 'Piano Trio',
					url: 'https://soundcloud.com/david-elaine-alt/wag-that-tail'
				},
				quartet: {
					description: 'Quartet',
					url: 'https://soundcloud.com/david-elaine-alt/wag-that-tail-1'
				}
			}
		}
	}
};

<|endoftext|>