<commit_before>const self = require('sdk/self');
const { Cu } = require('chrome');
Cu.import('resource://gre/modules/Console.jsm');
Cu.import('resource://gre/modules/Services.jsm');
const prefs = require('sdk/simple-prefs').prefs;
const store = require('sdk/simple-storage').storage;
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { setTimeout, clearTimeout } = require('sdk/timers');
const saveLocation = require('./save-location');
const sendMetricsData = require('./send-metrics-data');
const topify = require('./topify');
const DraggableElement = require('./dragging-utils');
const tabs = require('sdk/tabs');
const youtubeHelpers = require('./youtube-helpers');

/* global Services */

const DEFAULT_DIMENSIONS = {
  height: prefs.height,
  width: prefs.width,
  minimizedHeight: 100
};

require('sdk/simple-prefs').on('width', () => DEFAULT_DIMENSIONS.width = prefs.width);
require('sdk/simple-prefs').on('height', () => DEFAULT_DIMENSIONS.height = prefs.height);

// TODO: if mvWindow changes, we need to destroy and create the player.
// This must be why we get those dead object errors. Note that mvWindow
// is passed into the DraggableElement constructor, could be a source of
// those errors. Maybe pass a getter instead of a window reference.
let mvWindow;

let commandPollTimer;

// waits till the window is ready, then calls callbacks.
function whenReady(cb) {
  // TODO: instead of setting timeout for each callback, just poll, then call all callbacks.
  if (mvWindow &&
      'AppData' in mvWindow.wrappedJSObject &&
      'YT' in mvWindow.wrappedJSObject &&
      'PlayerState' in mvWindow.wrappedJSObject.YT) return cb();
  setTimeout(() => { whenReady(cb) }, 25);
}

// I can't get frame scripts working, so instead we just set global state directly in react. fml
function send(eventName, msg) {
  whenReady(() => {
    const newData = Object.assign(mvWindow.wrappedJSObject.AppData, msg);
    mvWindow.wrappedJSObject.AppData = newData;
  });
}

function getWindow() {
  return mvWindow;
}

// Detecting when the window is closed is surprisingly difficult. If hotkeys
// close the window, no detectable event is fired. Instead, we have to listen
// for the nsIObserver event fired when _any_ XUL window is closed, then loop
// over all windows and look for the minvid window.
const onWindowClosed = (evt) => {
  // Note: we pass null here because minvid window is not of type 'navigator:browser'
  const enumerator = Services.wm.getEnumerator(null);

  let minvidExists = false;
  while (enumerator.hasMoreElements()) {
    const win = enumerator.getNext();
    if (win.name === 'minvid') {
      minvidExists = true;
      break;
    }
  }
  if (!minvidExists) closeWindow();
};
Services.obs.addObserver(onWindowClosed, 'xul-window-destroyed');

// This handles the case where the min vid window is kept open
// after closing the last firefox window.
function closeRequested() {
  destroy(true);
}
Services.obs.addObserver(closeRequested, 'browser-lastwindow-close-requested');

function closeWindow() {
  // If the window is gone, a 'dead object' error will be thrown; discard it.
  try {
    mvWindow && mvWindow.close();
  } catch (ex) {} // eslint-disable-line no-empty
  // stop communication
  clearTimeout(commandPollTimer);
  commandPollTimer = null;
  // clear the window pointer
  mvWindow = null;
  // TODO: do we need to manually tear down frame scripts?
}

function create() {
  if (mvWindow) return mvWindow;

  const window = getMostRecentBrowserWindow();
  const { x, y } = saveLocation.screenPosition;
  const windowArgs = `left=${x},top=${y},chrome,dialog=no,width=${prefs.width},height=${prefs.height},titlebar=no`;
  // implicit assignment to mvWindow global
  mvWindow = window.open(self.data.url('default.html'), 'minvid', windowArgs);
  // once the window's ready, make it always topmost
  whenReady(() => { topify(mvWindow); });
  initCommunication();
  whenReady(() => { makeDraggable(); });
  return mvWindow;
}

function initCommunication() {
  let errorCount = 0;
  // When the window's ready, start polling for pending commands
  function pollForCommands() {
    let cmd;
    try {
      cmd = mvWindow.wrappedJSObject.pendingCommands;
    } catch (ex) {
      console.error('something happened trying to get pendingCommands: ', ex); // eslint-disable-line no-console
      if (++errorCount > 10) {
        console.error('pendingCommands threw 10 times, giving up');            // eslint-disable-line no-console
        // NOTE: if we can't communicate with the window, we have to close it,
        // since the user cannot.
        closeWindow();
        return;
      }
    }
    commandPollTimer = setTimeout(pollForCommands, 25);
    if (!cmd || !cmd.length) return;
    // We found a command! Erase it, then act on it.
    mvWindow.wrappedJSObject.resetCommands();
    for (let i = 0; i < cmd.length; i++) {
      let parsed;
      try {
        parsed = JSON.parse(cmd[i]);
      } catch (ex) {
        console.error('malformed command sent to addon: ', cmd[i], ex); // eslint-disable-line no-console
        break;
      }
      handleMessage(parsed);
    }
  }
  whenReady(pollForCommands);
}

function makeDraggable() {
  // Based on WindowDraggingElement usage in popup.xml
  // https://dxr.mozilla.org/mozilla-central/source/toolkit/content/widgets/popup.xml#278-288
  const draghandle = new DraggableElement(mvWindow);
  draghandle.mouseDownCheck = () => { return true; };

  // Update the saved position each time the draggable window is dropped.
  // Listening for 'dragend' events doesn't work, so use 'mouseup' instead.
  mvWindow.document.addEventListener('mouseup', () => {
    saveLocation.screenPosition = {x: mvWindow.screenX, y: mvWindow.screenY};
  });
}

function destroy(isUnload) {
  closeWindow();
  if (isUnload) {
    Services.obs.removeObserver(onWindowClosed, 'xul-window-destroyed');
    Services.obs.removeObserver(closeRequested, 'browser-lastwindow-close-requested');
    saveLocation.destroy();
  }
}

function updateWindow() {
  return mvWindow || create();
}

function show() {
  if (!mvWindow) create();
}

function isMinimized() {
  return mvWindow.innerHeight <= DEFAULT_DIMENSIONS.minimizedHeight;
}

function maximize() {
  mvWindow.resizeTo(DEFAULT_DIMENSIONS.width, DEFAULT_DIMENSIONS.height);
  mvWindow.moveBy(0, DEFAULT_DIMENSIONS.minimizedHeight - DEFAULT_DIMENSIONS.height);
  saveLocation.screenPosition = {x: mvWindow.screenX, y: mvWindow.screenY};
}

function handleMessage(msg) {
  const title = msg.action;
  const opts = msg;
  if (title === 'send-to-tab') {
    const pageUrl = opts.launchUrl ? opts.launchUrl : getPageUrl(opts.domain, opts.id, opts.time);
    if (pageUrl) tabs.open(pageUrl);
    else {
      console.error('could not parse page url for ', opts); // eslint-disable-line no-console
      send('set-video', {error: 'Error loading video from ' + opts.domain});
    }
    send('set-video', {domain: '', src: ''});
    closeWindow();
  } else if (title === 'close') {
    store.history.unshift(store.queue.shift());
    send('set-video', {domain: '', src: ''});
    closeWindow();
  } else if (title === 'minimize') {
    mvWindow.resizeTo(DEFAULT_DIMENSIONS.width, DEFAULT_DIMENSIONS.minimizedHeight);
    mvWindow.moveBy(0, DEFAULT_DIMENSIONS.height - DEFAULT_DIMENSIONS.minimizedHeight);
    saveLocation.screenPosition = {x: mvWindow.screenX, y: mvWindow.screenY};
  } else if (title === 'maximize') {
    maximize();
  } else if (title === 'metrics-event') {
    // Note: sending in the window ref to avoid circular imports.
    sendMetricsData(opts.payload, mvWindow);
  } else if (title === 'track-ended') {
    store.history.unshift(store.queue.shift());
    if (store.queue.length) {
      send('set-video', {
        playing: true,
        queue: JSON.stringify(store.queue),
        history: JSON.stringify(store.history)
      });
    }
  } else if (title === 'track-removed') {
    if (opts.isHistory) store.history.splice(opts.index, 1);
    else store.queue.splice(opts.index, 1);

    if (store.queue.length) {
      send('set-video', {
        queue: JSON.stringify(store.queue),
        history: JSON.stringify(store.history)
      });
    } else {
      send('set-video', {domain: '', src: ''});
      closeWindow();
    }
  } else if (title === 'track-added-from-history') {
    // In this case we should duplicate the item from the history
    // array.
    store.queue.push(store.history[opts.index]);
    sendMetricsData({
      object: 'queue',
      method: 'track-added-from-history',
      domain: store.queue[0].domain
    }, mvWindow);
    send('set-video', {queue: JSON.stringify(store.queue)});
  } else if (title === 'track-expedited') {
    /*
     * the goal here is to get the track index, move it to the top
     * of the queue, and play it.
     * We also need to handle the currently playing track correctly.

     * If track 0 in the queue is not playing, and hasn't been
     * played at all(currentTime == 0), we should move the newTrack
     * to the top of the queue and play it.

     * If track 0 in the queue is playing or has been played
     * (currentTime > 0), we should move track 0 into the history
     * array, and then move newTrack to the top of the queue
     */
    if (opts.moveIndexZero) {
      store.history.unshift(store.queue.shift());
      if (opts.isHistory) opts.index++;
      else opts.index--;
    }

    if (opts.isHistory) {
      store.queue.unshift(store.history[opts.index])
    } else store.queue.unshift(store.queue.splice(opts.index, 1)[0])

    sendMetricsData({
      object: 'queue',
      method: 'track-expedited',
      domain: store.queue[0].domain
    }, mvWindow);

    send('set-video', {
      playing: true,
      queue: JSON.stringify(store.queue),
      history: JSON.stringify(store.history)
    });
  } else if (title === 'track-reordered') {
    const newQueue = store.queue.slice();
    newQueue.splice(opts.newIndex, 0, newQueue.splice(opts.oldIndex, 1)[0]);
    store.queue = newQueue;
    sendMetricsData({
      object: 'queue',
      method: 'track-reordered',
      domain: store.queue[0].domain
    }, mvWindow);
    send('set-video', {queue: JSON.stringify(store.queue)});
  } else if (title === 'play-from-history') {
    store.queue.splice(0);
    store.queue = store.history.slice(0, 10);
    send('set-video', {
      playing: true,
      exited: false,
      queue: JSON.stringify(store.queue),
      history: JSON.stringify(store.history)
    });
  } else if (title === 'clear') {
    sendMetricsData({
      object: 'queue',
      method: `clear:${opts.choice}`,
      domain: ''
    }, mvWindow);
    if (opts.choice === 'history') {
      store.history = [];
      send('set-video', {history: JSON.stringify(store.history)});
    } else {
      store.queue = [];
      send('set-video', {domain: '', src: '', queue: JSON.stringify(store.queue)});
      closeWindow();
    }
  } else if (title === 'confirm') {
    if (opts.choice === 'playlist') {
      youtubeHelpers.getPlaylist(opts, playlist => {

        // if the playlist was launched on with a certain video
        // we need to grab the playlist at that playlist
        if (opts.index) playlist = playlist.splice(opts.index - 1);

        if (opts.playerMethod === 'play') {
          // if the 'play-now' option was selected, and min vid has a
          // track playing we need to move that item to history before
          // front loading the playlist results into the queue.
          if (opts.moveIndexZero) store.history.unshift(store.queue.shift());
          store.queue = playlist.concat(store.queue);
        } else store.queue = store.queue.concat(playlist);

        const response = {
          trackAdded: (opts.playerMethod === 'add-to-queue') && (store.queue.length > 1),
          error: false,
          queue: JSON.stringify(store.queue),
          history: JSON.stringify(store.history),
          confirm: false,
          confirmContent: '{}'
        };

        if (opts.playerMethod === 'play') response.playing = true;
        send('set-video', response);
      });
    } else if (opts.choice === 'cancel') {
      if (!store.queue.length) {
        send('set-video', {
          domain: '',
          src: '',
          confirm: false,
          confirmContent: '{}'
        });
        closeWindow();
      } else {
        send('set-video', {
          confirm: false,
          confirmContent: '{}',
          queue: JSON.stringify(store.queue),
          history: JSON.stringify(store.history)
        });
      }
    } else {
      youtubeHelpers.getVideo(opts, video => {
        if (opts.playerMethod === 'play') {
          // if the 'play-now' option was selected, and min vid has a
          // track playing we need to move that item to history before
          // front loading the video into the queue.
          if (opts.moveIndexZero) store.history.unshift(store.queue.shift());
          store.queue.unshift(video);
        } else store.queue.push(video);
        const response = {
          trackAdded: (opts.playerMethod === 'add-to-queue') && (store.queue.length > 1),
          error: false,
          queue: JSON.stringify(store.queue),
          history: JSON.stringify(store.history),
          confirm: false,
          confirmContent: '{}'
        };

        if (opts.playerMethod === 'play') response.playing = true;

        send('set-video', response);
      });
    }
  }
}

function getPageUrl(domain, id, time) {
  let url;
  if (domain.indexOf('youtube') > -1) {
    url = `https://youtube.com/watch?v=${id}&t=${Math.floor(time)}`;
  } else if (domain.indexOf('vimeo') > -1) {
    const min = Math.floor(time / 60);
    const sec = Math.floor(time - min * 60);
    url = `https://vimeo.com/${id}#t=${min}m${sec}s`;
  }

  return url;
}

module.exports = {
  whenReady,
  create,
  destroy,
  getWindow,
  updateWindow,
  // replaces panel.port.emit
  send,
  // replaces panel.show
  show,
  maximize,
  isMinimized
};
<commit_msg>Fix issue with third arg in addObserver call (#878)<commit_after>const self = require('sdk/self');
const { Cu } = require('chrome');
Cu.import('resource://gre/modules/Console.jsm');
Cu.import('resource://gre/modules/Services.jsm');
const prefs = require('sdk/simple-prefs').prefs;
const store = require('sdk/simple-storage').storage;
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { setTimeout, clearTimeout } = require('sdk/timers');
const saveLocation = require('./save-location');
const sendMetricsData = require('./send-metrics-data');
const topify = require('./topify');
const DraggableElement = require('./dragging-utils');
const tabs = require('sdk/tabs');
const youtubeHelpers = require('./youtube-helpers');

/* global Services */

const DEFAULT_DIMENSIONS = {
  height: prefs.height,
  width: prefs.width,
  minimizedHeight: 100
};

require('sdk/simple-prefs').on('width', () => DEFAULT_DIMENSIONS.width = prefs.width);
require('sdk/simple-prefs').on('height', () => DEFAULT_DIMENSIONS.height = prefs.height);

// TODO: if mvWindow changes, we need to destroy and create the player.
// This must be why we get those dead object errors. Note that mvWindow
// is passed into the DraggableElement constructor, could be a source of
// those errors. Maybe pass a getter instead of a window reference.
let mvWindow;

let commandPollTimer;

// waits till the window is ready, then calls callbacks.
function whenReady(cb) {
  // TODO: instead of setting timeout for each callback, just poll, then call all callbacks.
  if (mvWindow &&
      'AppData' in mvWindow.wrappedJSObject &&
      'YT' in mvWindow.wrappedJSObject &&
      'PlayerState' in mvWindow.wrappedJSObject.YT) return cb();
  setTimeout(() => { whenReady(cb) }, 25);
}

// I can't get frame scripts working, so instead we just set global state directly in react. fml
function send(eventName, msg) {
  whenReady(() => {
    const newData = Object.assign(mvWindow.wrappedJSObject.AppData, msg);
    mvWindow.wrappedJSObject.AppData = newData;
  });
}

function getWindow() {
  return mvWindow;
}

// Detecting when the window is closed is surprisingly difficult. If hotkeys
// close the window, no detectable event is fired. Instead, we have to listen
// for the nsIObserver event fired when _any_ XUL window is closed, then loop
// over all windows and look for the minvid window.
const onWindowClosed = (evt) => {
  // Note: we pass null here because minvid window is not of type 'navigator:browser'
  const enumerator = Services.wm.getEnumerator(null);

  let minvidExists = false;
  while (enumerator.hasMoreElements()) {
    const win = enumerator.getNext();
    if (win.name === 'minvid') {
      minvidExists = true;
      break;
    }
  }
  if (!minvidExists) closeWindow();
};
Services.obs.addObserver(onWindowClosed, 'xul-window-destroyed', false); // eslint-disable-line mozilla/no-useless-parameters

// This handles the case where the min vid window is kept open
// after closing the last firefox window.
function closeRequested() {
  destroy(true);
}
Services.obs.addObserver(closeRequested, 'browser-lastwindow-close-requested', false); // eslint-disable-line mozilla/no-useless-parameters

function closeWindow() {
  // If the window is gone, a 'dead object' error will be thrown; discard it.
  try {
    mvWindow && mvWindow.close();
  } catch (ex) {} // eslint-disable-line no-empty
  // stop communication
  clearTimeout(commandPollTimer);
  commandPollTimer = null;
  // clear the window pointer
  mvWindow = null;
  // TODO: do we need to manually tear down frame scripts?
}

function create() {
  if (mvWindow) return mvWindow;

  const window = getMostRecentBrowserWindow();
  const { x, y } = saveLocation.screenPosition;
  const windowArgs = `left=${x},top=${y},chrome,dialog=no,width=${prefs.width},height=${prefs.height},titlebar=no`;
  // implicit assignment to mvWindow global
  mvWindow = window.open(self.data.url('default.html'), 'minvid', windowArgs);
  // once the window's ready, make it always topmost
  whenReady(() => { topify(mvWindow); });
  initCommunication();
  whenReady(() => { makeDraggable(); });
  return mvWindow;
}

function initCommunication() {
  let errorCount = 0;
  // When the window's ready, start polling for pending commands
  function pollForCommands() {
    let cmd;
    try {
      cmd = mvWindow.wrappedJSObject.pendingCommands;
    } catch (ex) {
      console.error('something happened trying to get pendingCommands: ', ex); // eslint-disable-line no-console
      if (++errorCount > 10) {
        console.error('pendingCommands threw 10 times, giving up');            // eslint-disable-line no-console
        // NOTE: if we can't communicate with the window, we have to close it,
        // since the user cannot.
        closeWindow();
        return;
      }
    }
    commandPollTimer = setTimeout(pollForCommands, 25);
    if (!cmd || !cmd.length) return;
    // We found a command! Erase it, then act on it.
    mvWindow.wrappedJSObject.resetCommands();
    for (let i = 0; i < cmd.length; i++) {
      let parsed;
      try {
        parsed = JSON.parse(cmd[i]);
      } catch (ex) {
        console.error('malformed command sent to addon: ', cmd[i], ex); // eslint-disable-line no-console
        break;
      }
      handleMessage(parsed);
    }
  }
  whenReady(pollForCommands);
}

function makeDraggable() {
  // Based on WindowDraggingElement usage in popup.xml
  // https://dxr.mozilla.org/mozilla-central/source/toolkit/content/widgets/popup.xml#278-288
  const draghandle = new DraggableElement(mvWindow);
  draghandle.mouseDownCheck = () => { return true; };

  // Update the saved position each time the draggable window is dropped.
  // Listening for 'dragend' events doesn't work, so use 'mouseup' instead.
  mvWindow.document.addEventListener('mouseup', () => {
    saveLocation.screenPosition = {x: mvWindow.screenX, y: mvWindow.screenY};
  });
}

function destroy(isUnload) {
  closeWindow();
  if (isUnload) {
    Services.obs.removeObserver(onWindowClosed, 'xul-window-destroyed');
    Services.obs.removeObserver(closeRequested, 'browser-lastwindow-close-requested');
    saveLocation.destroy();
  }
}

function updateWindow() {
  return mvWindow || create();
}

function show() {
  if (!mvWindow) create();
}

function isMinimized() {
  return mvWindow.innerHeight <= DEFAULT_DIMENSIONS.minimizedHeight;
}

function maximize() {
  mvWindow.resizeTo(DEFAULT_DIMENSIONS.width, DEFAULT_DIMENSIONS.height);
  mvWindow.moveBy(0, DEFAULT_DIMENSIONS.minimizedHeight - DEFAULT_DIMENSIONS.height);
  saveLocation.screenPosition = {x: mvWindow.screenX, y: mvWindow.screenY};
}

function handleMessage(msg) {
  const title = msg.action;
  const opts = msg;
  if (title === 'send-to-tab') {
    const pageUrl = opts.launchUrl ? opts.launchUrl : getPageUrl(opts.domain, opts.id, opts.time);
    if (pageUrl) tabs.open(pageUrl);
    else {
      console.error('could not parse page url for ', opts); // eslint-disable-line no-console
      send('set-video', {error: 'Error loading video from ' + opts.domain});
    }
    send('set-video', {domain: '', src: ''});
    closeWindow();
  } else if (title === 'close') {
    store.history.unshift(store.queue.shift());
    send('set-video', {domain: '', src: ''});
    closeWindow();
  } else if (title === 'minimize') {
    mvWindow.resizeTo(DEFAULT_DIMENSIONS.width, DEFAULT_DIMENSIONS.minimizedHeight);
    mvWindow.moveBy(0, DEFAULT_DIMENSIONS.height - DEFAULT_DIMENSIONS.minimizedHeight);
    saveLocation.screenPosition = {x: mvWindow.screenX, y: mvWindow.screenY};
  } else if (title === 'maximize') {
    maximize();
  } else if (title === 'metrics-event') {
    // Note: sending in the window ref to avoid circular imports.
    sendMetricsData(opts.payload, mvWindow);
  } else if (title === 'track-ended') {
    store.history.unshift(store.queue.shift());
    if (store.queue.length) {
      send('set-video', {
        playing: true,
        queue: JSON.stringify(store.queue),
        history: JSON.stringify(store.history)
      });
    }
  } else if (title === 'track-removed') {
    if (opts.isHistory) store.history.splice(opts.index, 1);
    else store.queue.splice(opts.index, 1);

    if (store.queue.length) {
      send('set-video', {
        queue: JSON.stringify(store.queue),
        history: JSON.stringify(store.history)
      });
    } else {
      send('set-video', {domain: '', src: ''});
      closeWindow();
    }
  } else if (title === 'track-added-from-history') {
    // In this case we should duplicate the item from the history
    // array.
    store.queue.push(store.history[opts.index]);
    sendMetricsData({
      object: 'queue',
      method: 'track-added-from-history',
      domain: store.queue[0].domain
    }, mvWindow);
    send('set-video', {queue: JSON.stringify(store.queue)});
  } else if (title === 'track-expedited') {
    /*
     * the goal here is to get the track index, move it to the top
     * of the queue, and play it.
     * We also need to handle the currently playing track correctly.

     * If track 0 in the queue is not playing, and hasn't been
     * played at all(currentTime == 0), we should move the newTrack
     * to the top of the queue and play it.

     * If track 0 in the queue is playing or has been played
     * (currentTime > 0), we should move track 0 into the history
     * array, and then move newTrack to the top of the queue
     */
    if (opts.moveIndexZero) {
      store.history.unshift(store.queue.shift());
      if (opts.isHistory) opts.index++;
      else opts.index--;
    }

    if (opts.isHistory) {
      store.queue.unshift(store.history[opts.index])
    } else store.queue.unshift(store.queue.splice(opts.index, 1)[0])

    sendMetricsData({
      object: 'queue',
      method: 'track-expedited',
      domain: store.queue[0].domain
    }, mvWindow);

    send('set-video', {
      playing: true,
      queue: JSON.stringify(store.queue),
      history: JSON.stringify(store.history)
    });
  } else if (title === 'track-reordered') {
    const newQueue = store.queue.slice();
    newQueue.splice(opts.newIndex, 0, newQueue.splice(opts.oldIndex, 1)[0]);
    store.queue = newQueue;
    sendMetricsData({
      object: 'queue',
      method: 'track-reordered',
      domain: store.queue[0].domain
    }, mvWindow);
    send('set-video', {queue: JSON.stringify(store.queue)});
  } else if (title === 'play-from-history') {
    store.queue.splice(0);
    store.queue = store.history.slice(0, 10);
    send('set-video', {
      playing: true,
      exited: false,
      queue: JSON.stringify(store.queue),
      history: JSON.stringify(store.history)
    });
  } else if (title === 'clear') {
    sendMetricsData({
      object: 'queue',
      method: `clear:${opts.choice}`,
      domain: ''
    }, mvWindow);
    if (opts.choice === 'history') {
      store.history = [];
      send('set-video', {history: JSON.stringify(store.history)});
    } else {
      store.queue = [];
      send('set-video', {domain: '', src: '', queue: JSON.stringify(store.queue)});
      closeWindow();
    }
  } else if (title === 'confirm') {
    if (opts.choice === 'playlist') {
      youtubeHelpers.getPlaylist(opts, playlist => {

        // if the playlist was launched on with a certain video
        // we need to grab the playlist at that playlist
        if (opts.index) playlist = playlist.splice(opts.index - 1);

        if (opts.playerMethod === 'play') {
          // if the 'play-now' option was selected, and min vid has a
          // track playing we need to move that item to history before
          // front loading the playlist results into the queue.
          if (opts.moveIndexZero) store.history.unshift(store.queue.shift());
          store.queue = playlist.concat(store.queue);
        } else store.queue = store.queue.concat(playlist);

        const response = {
          trackAdded: (opts.playerMethod === 'add-to-queue') && (store.queue.length > 1),
          error: false,
          queue: JSON.stringify(store.queue),
          history: JSON.stringify(store.history),
          confirm: false,
          confirmContent: '{}'
        };

        if (opts.playerMethod === 'play') response.playing = true;
        send('set-video', response);
      });
    } else if (opts.choice === 'cancel') {
      if (!store.queue.length) {
        send('set-video', {
          domain: '',
          src: '',
          confirm: false,
          confirmContent: '{}'
        });
        closeWindow();
      } else {
        send('set-video', {
          confirm: false,
          confirmContent: '{}',
          queue: JSON.stringify(store.queue),
          history: JSON.stringify(store.history)
        });
      }
    } else {
      youtubeHelpers.getVideo(opts, video => {
        if (opts.playerMethod === 'play') {
          // if the 'play-now' option was selected, and min vid has a
          // track playing we need to move that item to history before
          // front loading the video into the queue.
          if (opts.moveIndexZero) store.history.unshift(store.queue.shift());
          store.queue.unshift(video);
        } else store.queue.push(video);
        const response = {
          trackAdded: (opts.playerMethod === 'add-to-queue') && (store.queue.length > 1),
          error: false,
          queue: JSON.stringify(store.queue),
          history: JSON.stringify(store.history),
          confirm: false,
          confirmContent: '{}'
        };

        if (opts.playerMethod === 'play') response.playing = true;

        send('set-video', response);
      });
    }
  }
}

function getPageUrl(domain, id, time) {
  let url;
  if (domain.indexOf('youtube') > -1) {
    url = `https://youtube.com/watch?v=${id}&t=${Math.floor(time)}`;
  } else if (domain.indexOf('vimeo') > -1) {
    const min = Math.floor(time / 60);
    const sec = Math.floor(time - min * 60);
    url = `https://vimeo.com/${id}#t=${min}m${sec}s`;
  }

  return url;
}

module.exports = {
  whenReady,
  create,
  destroy,
  getWindow,
  updateWindow,
  // replaces panel.port.emit
  send,
  // replaces panel.show
  show,
  maximize,
  isMinimized
};
<|endoftext|>


<commit_before>/* global ReaderState */
"use strict";
/**
 * @module xml2json

 * node -> see if https://github.com/isaacs/sax-js could be used instead
 */

var xml;

// use node-expat for speed if installed
// otherwise fall back to ersatz-node-expat , a 100% javascript xml parser based on node-xml, but slower...
try {
    xml = require("node-expat");
    console.log("FAST: using node-expat");
}
catch (err) {
    require("colors");
    var displayWarnings = false;
    if (displayWarnings) {
        console.warn("\n Warning : your current installation is using ersatz-node-expat a full javascript version of node-expat".yellow);
        console.warn("           This version is slightly slower than node-expat but doesn't require a C++ build during installation".yellow);
        console.warn("");
        console.warn("           You can get slightly better performance if you install node-expat".yellow);
        console.warn("              $ npm install node-expat".yellow.bold);
        console.warn("");
        console.warn("           Note: this may require some additional packages to be installed on your system".yellow);
    }

    xml = require("ersatz-node-expat");
}

var fs = require("fs");
var assert = require("better-assert");
var _ = require("underscore");

/**
 * @static
 * @private
 * @method _coerceParser
 * @param parser {map<ReaderState|options>}
 * @return {map}
 */
function _coerceParser(parser) {

    for (var name in parser) {
        if (parser.hasOwnProperty(name)) {
            if (!(parser[name] instanceof ReaderState)) {
                parser[name] = new ReaderState(parser[name]);
            }
        }
    }
    return parser;
}

/**
 * @class ReaderState
 * @private
 * @param options
 * @param [options.parser=null]  {map<ReaderState|options}}
 * @param [options.init|null]    {Function}
 * @param [options.finish]       {Function}
 * @param [options.startElement] {Function}
 * @param [options.endElement]   {Function}
 * @constructor
 */
function ReaderState(options) {

    // ensure options object has only expected properties
    options.parser = options.parser || {};
    var fields = _.keys(options);
    var invalid_fields = _.difference(fields, ["parser", "init", "finish", "startElement", "endElement"]);

    /* istanbul ignore next*/
    if (invalid_fields.length !== 0) {

        console.log(" Invalid fields detected :" , invalid_fields);

        throw new Error("Invalid filed detected in ReaderState Parser !:" + invalid_fields.join(" - "));
    }

    this._init = options.init;
    this._finish = options.finish;
    this._startElement = options.startElement;
    this._endElement = options.endElement;

    this.parser = _coerceParser(options.parser);
}

/**
 * @method _on_init
 * @param name  {String} - the name of the element
 * @param attrs {Map<String,String>}
 * @protected
 */
ReaderState.prototype._on_init = function (name, attrs) {
    this.attrs = attrs;
    assert(this.attrs);
    if (this._init) {
        this._init(name, attrs);
    }
};

/**
 * @method _on_startElement
 * @param name  {String} - the name of the element
 * @param attrs {Map<String,String>}
 * @protected
 */
ReaderState.prototype._on_startElement = function (name, attrs) {

    this.chunks = [];
    this.text="";
    if (this.parser.hasOwnProperty(name)) {
        this.engine._promote(this.parser[name], name, attrs);
    } else if (this._startElement) {
        this._startElement(name, attrs);
    }
};

/**
 * @method _on_endElement
 * @param name {String}
 * @protected
 */
ReaderState.prototype._on_endElement = function (name) {
    assert(this.attrs);

    //xx if (this._endElement) { this._endElement(name); }
    if (this.parent && this.parent._endElement) {
        this.parent._endElement(name);
    }
    if (name === this.name) {
        if (this._finish) {
            this.text = this.chunks.join("");
            this.chunks = [];
            this._finish();
        }
        // this is the end
        this.engine._demote(this);
    }
};

/**
 * @method _on_text
 * @param text {String} the text found inside the element
 * @protected
 */
ReaderState.prototype._on_text = function (text) {
    this.chunks = this.chunks || [];
    text =text.trim(); if (text.length==0) { return; }
    this.chunks.push(text);
};

/**
 * @class Xml2Json
 * @param options {ReaderState} - the state machine as  a ReaderState node.
 * @param [options.parser=null]  {ReaderState}
 * @param [options.init|null]    {Function}
 * @param [options.finish]       {Function}
 * @param [options.startElement] {Function}
 * @param [options.endElement]   {Function}
 * @constructor
 *
 * @example

 var parser = new Xml2Json({
        parser: {
            'person': {
                init: function(name,attrs) {
                    this.parent.root.obj = {};
                    this.obj =  this.parent.root.obj;
                    this.obj['name'] = attrs['name'];
                },
                parser: {
                    'address': {
                        finish: function(){
                            this.parent.obj['address'] = this.text;
                        }
                    }
                }
            }
        }
    });

 var xml_string =  "<employees>" +
 "   <person name='John'>" +
 "     <address>Paris</address>" +
 "   </person>" +
 "</employees>";

 parser.parseString(xml_string, function() {
        parser.obj.should.eql({name: 'John',address: 'Paris'});
        done();
    });
 */
function Xml2Json(options) {

    var state = new ReaderState(options);

    state.root = this;

    this.state_stack = [];
    this.current_state = null;
    this._promote(state);
}

Xml2Json.prototype._promote = function (new_state, name, attr) {
    attr = attr || {};
    new_state.name = name;
    new_state.parent = this.current_state;
    new_state.engine = this;

    this.state_stack.push(this.current_state);
    this.current_state = new_state;
    this.current_state._on_init(name, attr);
};

Xml2Json.prototype._demote = function (cur_state) {
    assert(this.current_state === cur_state);
    this.current_state = this.state_stack.pop();
};

Xml2Json.prototype._prepareParser = function (callback) {

    var self = this;
    var parser = new xml.Parser();

    parser.on("startElement", function (name, attrs) {
        self.current_state._on_startElement(name, attrs);
    });
    parser.on("endElement", function (name) {
        self.current_state._on_endElement(name);
    });
    parser.on("text", function (text) {
        self.current_state._on_text(text);
    });
    parser.on("close", function () {
        if (callback) {
            callback();
        }
    });
    return parser;
};

/**
 * @method parseString
 * @param xml_text {String} - the xml string to parse.
 * @param callback {Callback}
 * @async
 */
Xml2Json.prototype.parseString = function (xml_text, callback) {
    var parser = this._prepareParser(callback);
    parser.write(xml_text);
    parser.end();

};

/**
 * @method  parse
 * @async
 * @param xmlFile {String} - the name of the xml file to parse.
 * @param callback {Callback}
 */
Xml2Json.prototype.parse = function (xmlFile, callback) {

    var  self = this;
    var readWholeFile = true;
    if (readWholeFile) {

        // slightly faster but reaquire more memory ..
        fs.readFile(xmlFile,function(err, data){
            if (!err) {
                if (data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) {
                    data = data.slice(3);
                }
                data = data.toString();
                var parser = self._prepareParser(callback);
                //xx console.log(data.substr(0,1000).yellow);
                //xx console.log(data.substr(data.length -1000,1000).cyan);
                parser.write(data);
                parser.end();
            } else {
                callback(err);

            }
        });
    } else {
        var Bomstrip = require("bomstrip");
        var parser = self._prepareParser(callback);
        fs.createReadStream(xmlFile, {autoClose: true, encoding: "utf8"})
            .pipe(new Bomstrip())
            .pipe(parser);

        }
    };

exports.Xml2Json = Xml2Json;
<commit_msg>#209 fix chunk initialisation<commit_after>/* global ReaderState */
"use strict";
/**
 * @module xml2json

 * node -> see if https://github.com/isaacs/sax-js could be used instead
 */

var xml;

// use node-expat for speed if installed
// otherwise fall back to ersatz-node-expat , a 100% javascript xml parser based on node-xml, but slower...
try {
    xml = require("node-expat");
    console.log("FAST: using node-expat");
}
catch (err) {
    require("colors");
    var displayWarnings = false;
    if (displayWarnings) {
        console.warn("\n Warning : your current installation is using ersatz-node-expat a full javascript version of node-expat".yellow);
        console.warn("           This version is slightly slower than node-expat but doesn't require a C++ build during installation".yellow);
        console.warn("");
        console.warn("           You can get slightly better performance if you install node-expat".yellow);
        console.warn("              $ npm install node-expat".yellow.bold);
        console.warn("");
        console.warn("           Note: this may require some additional packages to be installed on your system".yellow);
    }

    xml = require("ersatz-node-expat");
}

var fs = require("fs");
var assert = require("better-assert");
var _ = require("underscore");

/**
 * @static
 * @private
 * @method _coerceParser
 * @param parser {map<ReaderState|options>}
 * @return {map}
 */
function _coerceParser(parser) {

    for (var name in parser) {
        if (parser.hasOwnProperty(name)) {
            if (!(parser[name] instanceof ReaderState)) {
                parser[name] = new ReaderState(parser[name]);
            }
        }
    }
    return parser;
}

/**
 * @class ReaderState
 * @private
 * @param options
 * @param [options.parser=null]  {map<ReaderState|options}}
 * @param [options.init|null]    {Function}
 * @param [options.finish]       {Function}
 * @param [options.startElement] {Function}
 * @param [options.endElement]   {Function}
 * @constructor
 */
function ReaderState(options) {

    // ensure options object has only expected properties
    options.parser = options.parser || {};
    var fields = _.keys(options);
    var invalid_fields = _.difference(fields, ["parser", "init", "finish", "startElement", "endElement"]);

    /* istanbul ignore next*/
    if (invalid_fields.length !== 0) {

        console.log(" Invalid fields detected :" , invalid_fields);

        throw new Error("Invalid filed detected in ReaderState Parser !:" + invalid_fields.join(" - "));
    }

    this._init = options.init;
    this._finish = options.finish;
    this._startElement = options.startElement;
    this._endElement = options.endElement;

    this.parser = _coerceParser(options.parser);
}

/**
 * @method _on_init
 * @param name  {String} - the name of the element
 * @param attrs {Map<String,String>}
 * @protected
 */
ReaderState.prototype._on_init = function (name, attrs) {
    this.attrs = attrs;
    assert(this.attrs);
    if (this._init) {
        this._init(name, attrs);
    }
};

/**
 * @method _on_startElement
 * @param name  {String} - the name of the element
 * @param attrs {Map<String,String>}
 * @protected
 */
ReaderState.prototype._on_startElement = function (name, attrs) {

    this.chunks = [];
    this.text="";
    if (this.parser.hasOwnProperty(name)) {
        this.engine._promote(this.parser[name], name, attrs);
    } else if (this._startElement) {
        this._startElement(name, attrs);
    }
};

/**
 * @method _on_endElement
 * @param name {String}
 * @protected
 */
ReaderState.prototype._on_endElement = function (name) {
    assert(this.attrs);

    this.chunks = this.chunks || [];
    //xx if (this._endElement) { this._endElement(name); }
    if (this.parent && this.parent._endElement) {
        this.parent._endElement(name);
    }
    if (name === this.name) {
        if (this._finish) {
            this.text = this.chunks.join("");
            this.chunks = [];
            this._finish();
        }
        // this is the end
        this.engine._demote(this);
    }
};

/**
 * @method _on_text
 * @param text {String} the text found inside the element
 * @protected
 */
ReaderState.prototype._on_text = function (text) {
    this.chunks = this.chunks || [];
    text =text.trim(); if (text.length==0) { return; }
    this.chunks.push(text);
};

/**
 * @class Xml2Json
 * @param options {ReaderState} - the state machine as  a ReaderState node.
 * @param [options.parser=null]  {ReaderState}
 * @param [options.init|null]    {Function}
 * @param [options.finish]       {Function}
 * @param [options.startElement] {Function}
 * @param [options.endElement]   {Function}
 * @constructor
 *
 * @example

 var parser = new Xml2Json({
        parser: {
            'person': {
                init: function(name,attrs) {
                    this.parent.root.obj = {};
                    this.obj =  this.parent.root.obj;
                    this.obj['name'] = attrs['name'];
                },
                parser: {
                    'address': {
                        finish: function(){
                            this.parent.obj['address'] = this.text;
                        }
                    }
                }
            }
        }
    });

 var xml_string =  "<employees>" +
 "   <person name='John'>" +
 "     <address>Paris</address>" +
 "   </person>" +
 "</employees>";

 parser.parseString(xml_string, function() {
        parser.obj.should.eql({name: 'John',address: 'Paris'});
        done();
    });
 */
function Xml2Json(options) {

    var state = new ReaderState(options);

    state.root = this;

    this.state_stack = [];
    this.current_state = null;
    this._promote(state);
}

Xml2Json.prototype._promote = function (new_state, name, attr) {
    attr = attr || {};
    new_state.name = name;
    new_state.parent = this.current_state;
    new_state.engine = this;

    this.state_stack.push(this.current_state);
    this.current_state = new_state;
    this.current_state._on_init(name, attr);
};

Xml2Json.prototype._demote = function (cur_state) {
    assert(this.current_state === cur_state);
    this.current_state = this.state_stack.pop();
};

Xml2Json.prototype._prepareParser = function (callback) {

    var self = this;
    var parser = new xml.Parser();

    parser.on("startElement", function (name, attrs) {
        self.current_state._on_startElement(name, attrs);
    });
    parser.on("endElement", function (name) {
        self.current_state._on_endElement(name);
    });
    parser.on("text", function (text) {
        self.current_state._on_text(text);
    });
    parser.on("close", function () {
        if (callback) {
            callback();
        }
    });
    return parser;
};

/**
 * @method parseString
 * @param xml_text {String} - the xml string to parse.
 * @param callback {Callback}
 * @async
 */
Xml2Json.prototype.parseString = function (xml_text, callback) {
    var parser = this._prepareParser(callback);
    parser.write(xml_text);
    parser.end();

};

/**
 * @method  parse
 * @async
 * @param xmlFile {String} - the name of the xml file to parse.
 * @param callback {Callback}
 */
Xml2Json.prototype.parse = function (xmlFile, callback) {

    var  self = this;
    var readWholeFile = true;
    if (readWholeFile) {

        // slightly faster but reaquire more memory ..
        fs.readFile(xmlFile,function(err, data){
            if (!err) {
                if (data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) {
                    data = data.slice(3);
                }
                data = data.toString();
                var parser = self._prepareParser(callback);
                //xx console.log(data.substr(0,1000).yellow);
                //xx console.log(data.substr(data.length -1000,1000).cyan);
                parser.write(data);
                parser.end();
            } else {
                callback(err);

            }
        });
    } else {
        var Bomstrip = require("bomstrip");
        var parser = self._prepareParser(callback);
        fs.createReadStream(xmlFile, {autoClose: true, encoding: "utf8"})
            .pipe(new Bomstrip())
            .pipe(parser);

        }
    };

exports.Xml2Json = Xml2Json;
<|endoftext|>


<commit_before>var init = function (initialise_callback) {
		var lisp_fns = {
				"+": function (args) {
					var rtn = 0; // CLISP returns 0 if `+` is given no arguments.
					while (args.length) {
						rtn += args.shift();
					};
					return rtn;
				},
				"-": function (args) {
					var rtn = args.shift();
					while (args.length) {
						rtn -= args.shift();
					};
					return rtn;
				},
				"*": function (args) {
					var rtn = 1; // CLISP returns 1 if `*` is given no arguments.
					while (args.length) {
						rtn *= args.shift();
					};
					return rtn;
				},
				"/": function (args) {
					var rtn = args.shift();
					while (args.length) {
						rtn /= args.shift();
					}
					return rtn;
				}
			},

			lisp_vars = {
				"T": {
					value: true,
					locked: true
				},
				"NIL": {
					value: false,
					locked: true
				}
			},

			separate_terms = function (input) {
				var str = input,
					arr = [],
					rst = "",
					rex = /(\s+)|(")|(\()|(\))|(.$)/g,
					begin_term = 0,
					end_term = 0,
					paren_level = 0,
					quote_level = false,
					rfn = function ($m, $1, $2, $3, $4, $5, offset, whole_string) {
						var out;
						if ($2 && !paren_level) {
							console.log(quote_level);
							quote_level = !quote_level;
							if (quote_level) {
								begin_term = offset;
							} else {
								end_term = offset + $m.length;
								out = whole_string.slice(begin_term, end_term);
								console.log(end_term);
								console.log(out);
								begin_term = end_term;
							}
						} else if ($3 && !quote_level) {
							paren_level += 1;
							if (!paren_level) {
								begin_term = offset;
							}
						} else if ($4 && !quote_level) {
							paren_level -= 1;
							if (!paren_level) {
								end_term = offset + $m.length;
								out = whole_string.slice(begin_term, end_term);
								begin_term = end_term;
							}
						} else if ($1 && !paren_level && !quote_level) {
							end_term = offset;
							out = whole_string.slice(begin_term, end_term);
							begin_term = end_term + $m.length;
						} else if ($5 && !quote_level) {
							end_term = whole_string.length;
							out = whole_string.slice(begin_term, end_term);
							begin_term = end_term + $m.length;
						}

						if (out) { arr.push(out); };
						return $m;
					};

				rst = str.replace(rex, rfn);
				return arr;
			},

			evaluate_term = function (term) {
				var rtn,
					arr,
					fun,
					arg = [];
				if (/\D/g.test(term)) {
					if (/^\(.*\)$/.test(term)) {
						if (term.slice(1, -1)) {
							arr = separate_terms(term.slice(1, -1));
							fun = arr.shift();
							while (arr.length) {
								arg.push(evaluate_term(arr.shift()));
							}
							if (lisp_fns.hasOwnProperty(fun)) {
								rtn = lisp_fns[fun](arg);
							} else {
								// TODO: What if there is no such function?
							}
						} else {
							// If parentheses are empty.
							rtn = "NIL";
						}
					} else if (/^".*"$/g.test(term)) {
						console.log(term);
						rtn = term;
					} else {
						term = term.toUpperCase();
						if (lisp_vars.hasOwnProperty(term)) {
							return lisp_vars[term].value;
						}
					}
				} else {
					rtn = +term;
				}
				return rtn;
			},

			// Top-level processing of input.
			process_input = function (input, callback_each_term) {
				var rtn,
					arr = separate_terms(input),
					rst;

				while (arr.length) {
					// Extract first term from array and evaluate.
					rst = evaluate_term(arr.shift());
					// Do something with the input and results.
					callback_each_term(input, print_result(rst));
					// Reset the input. We have already done something with it once.
					input = "";
				}

				return rtn;
			},

			print_result = function (result) {
				var rtn = result;
				if (typeof result === "number") {
					rtn = "" + result;
				} else if (typeof result === "boolean") {
					rtn = (result) ? "T" : "NIL";
				}
				return rtn;
			};

		initialise_callback();

		return {
			process_input: process_input
		};

	};

// Node JS compatibility
if (typeof exports !== "undefined") {
	exports.init = init;
}<commit_msg>Introduced defvar to lisp_fns, so as to allow definition of variables beyond T and NIL. Altered evaluation method to deal with this.<commit_after>var init = function (initialise_callback) {
		var lisp_fns = {
				"+": function (args) {
					var rtn = 0; // CLISP returns 0 if `+` is given no arguments.
					while (args.length) {
						rtn += args.shift();
					};
					return rtn;
				},
				"-": function (args) {
					var rtn = args.shift();
					while (args.length) {
						rtn -= args.shift();
					};
					return rtn;
				},
				"*": function (args) {
					var rtn = 1; // CLISP returns 1 if `*` is given no arguments.
					while (args.length) {
						rtn *= args.shift();
					};
					return rtn;
				},
				"/": function (args) {
					var rtn = args.shift();
					while (args.length) {
						rtn /= args.shift();
					}
					return rtn;
				},
				"defvar": function (symbol, bound_value) {
					// arity: 2
					var symbol = symbol.toUpperCase();
					if (lisp_vars.hasOwnProperty[symbol]) {
						// if locked, throw error; if not, ignore?
					} else {
						lisp_vars[symbol] = { value: bound_value, locked: false };
					};
					return symbol;
				}
			},

			lisp_vars = {
				"T": {
					value: true,
					locked: true
				},
				"NIL": {
					value: false,
					locked: true
				}
			},

			separate_terms = function (input) {
				var str = input,
					arr = [],
					rst = "",
					rex = /(\s+)|(")|(\()|(\))|(.$)/g,
					begin_term = 0,
					end_term = 0,
					paren_level = 0,
					quote_level = false,
					rfn = function ($m, $1, $2, $3, $4, $5, offset, whole_string) {
						var out;
						if ($2 && !paren_level) {
							console.log(quote_level);
							quote_level = !quote_level;
							if (quote_level) {
								begin_term = offset;
							} else {
								end_term = offset + $m.length;
								out = whole_string.slice(begin_term, end_term);
								console.log(end_term);
								console.log(out);
								begin_term = end_term;
							}
						} else if ($3 && !quote_level) {
							paren_level += 1;
							if (!paren_level) {
								begin_term = offset;
							}
						} else if ($4 && !quote_level) {
							paren_level -= 1;
							if (!paren_level) {
								end_term = offset + $m.length;
								out = whole_string.slice(begin_term, end_term);
								begin_term = end_term;
							}
						} else if ($1 && !paren_level && !quote_level) {
							end_term = offset;
							out = whole_string.slice(begin_term, end_term);
							begin_term = end_term + $m.length;
						} else if ($5 && !quote_level) {
							end_term = whole_string.length;
							out = whole_string.slice(begin_term, end_term);
							begin_term = end_term + $m.length;
						}

						if (out) { arr.push(out); };
						return $m;
					};

				rst = str.replace(rex, rfn);
				return arr;
			},

			evaluate_term = function (term) {
				var rtn,
					arr,
					fun,
					arg = [];
				if (/\D/g.test(term)) {
					if (/^\(.*\)$/.test(term)) {
						if (term.slice(1, -1)) {
							arr = separate_terms(term.slice(1, -1));
							fun = arr.shift();

							if (lisp_fns.hasOwnProperty(fun)) {
								if (fun === "defvar") {
									rtn = lisp_fns[fun](arr.shift(), evaluate_term(arr.shift()));
								} else {
									while (arr.length) {
										arg.push(evaluate_term(arr.shift()));
									}
									rtn = lisp_fns[fun](arg);
								}

							} else {
								// TODO: What if there is no such function?
							}
						} else {
							// If parentheses are empty.
							rtn = "NIL";
						}
					} else if (/^".*"$/g.test(term)) {
						console.log(term);
						rtn = term;
					} else {
						term = term.toUpperCase();
						if (lisp_vars.hasOwnProperty(term)) {
							return lisp_vars[term].value;
						}
					}
				} else {
					rtn = +term;
				}
				return rtn;
			},

			// Top-level processing of input.
			process_input = function (input, callback_each_term) {
				var rtn,
					arr = separate_terms(input),
					rst;

				while (arr.length) {
					// Extract first term from array and evaluate.
					rst = evaluate_term(arr.shift());
					// Do something with the input and results.
					callback_each_term(input, print_result(rst));
					// Reset the input. We have already done something with it once.
					input = "";
				}

				return rtn;
			},

			print_result = function (result) {
				var rtn = result;
				if (typeof result === "number") {
					rtn = "" + result;
				} else if (typeof result === "boolean") {
					rtn = (result) ? "T" : "NIL";
				}
				return rtn;
			};

		initialise_callback();

		return {
			process_input: process_input
		};

	};

// Node JS compatibility
if (typeof exports !== "undefined") {
	exports.init = init;
}<|endoftext|>


<commit_before>function(head, req) {
  var row;
  var headerNeeded = true;
  var pairName = [];
  var pairFirstName = "";
  var pairLastName = "";
  var training = "";
  var paid = "";
  var conflict = "";

  start({
    "headers": {
      "Content-Type": "text/csv",
      "Content-disposition": "attachment;filename=FlightInfo.csv"
     }
  });

  while(row = getRow()) {
    pairName = [];
    pairFirstName = "";
    pairLastName = "";
    training = "";
    paid = "";
    r = row.doc;
    if (r.type === "Veteran") {
      pairName = r.guardian.name.split(" ");
      conflict = (r.vet_type || "WWII");
    } else if (r.type === "Guardian") {
      if ((r.veteran) && (r.veteran.pairings) && (r.veteran.pairings.length > 0)) {
        pairName = r.veteran.pairings[0].name.split(" ");
      }

      // Training status
      if (r.flight.training) {
        if (r.flight.training_complete) {
          training = "* ";
        }
        training += r.flight.training;
      }
      // Paid status
      if (r.flight.paid) {
        paid = "Y";
      } else {
        paid = "N";
      }
    }

    // Split the pair name.
    if (pairName.length > 1) {
      pairFirstName = pairName.slice(0,1);
      pairLastName = pairName.slice(1).join(" ");
    } else if (pairName.length === 1) {
      pairLastName = pairName.slice(0,1);
    }

    result = {
        flight_id:             r.flight.id,
        type:                  r.type,
        app_date:              r.app_date,
        conflict:              conflict,
        first_name:            r.name.first,
        middle_name:           r.name.middle,
        last_name:             r.name.last,
        nick_name:             r.name.nickname,
        addr_street:           r.address.street,
        addr_city:             r.address.city,
        addr_county:           r.address.county,
        addr_state:            r.address.state,
        addr_zip:              r.address.zip,
        addr_phone_day:        r.address.phone_day,
        addr_phone_eve:        r.address.phone_eve,
        addr_phone_mbl:        r.address.phone_mbl,
        addr_email:            r.address.email,
        birth_date:            r.birth_date,
        gender:                r.gender,
        shirt_size:            r.shirt.size,
        flight_status:         r.flight.status,
        flight_status_note:    r.flight.status_note,
        flight_confirmed_date: r.flight.confirmed_date,
        flight_confirmed_by:   r.flight.confirmed_by,
        flight_training:       training,
        flight_training_notes: r.flight.training_notes,
        flight_paid:           paid,
        flight_seat:           r.flight.seat,
        pair_first_name:       pairFirstName,
        pair_last_name:        pairLastName,
        flight_bus:            r.flight.bus.replace("Alpha", "Alpha ").replace("Bravo", "Bravo "),
        id:                    r._id,
        rev:                   r._rev,
        created_at:            r.metadata.created_at,
        updated_at:            r.metadata.updated_at,
        created_by:            r.metadata.created_by,
        updated_by:            r.metadata.updated_by
    }

    if (headerNeeded) {
      for (key in result) {
        send("\"");
        send(key);
        send("\",");
      }
      send("\n");
      headerNeeded = false;
    }

    for (key in result) {
      if (result[key]) {
        if (result[key].length > 0) {
          send("\"");
          send(result[key]);
          send("\"");
        } else {
          if (!isNaN(result[key])) {
            send(result[key].toString());
          }
        }
      }
      send(",");
    }
    send("\n");
  }
}

<commit_msg>Add Flight Group number to Flight Export<commit_after>function(head, req) {
  var row;
  var headerNeeded = true;
  var pairName = [];
  var pairFirstName = "";
  var pairLastName = "";
  var training = "";
  var paid = "";
  var conflict = "";
  var medNotesExp = "";

  start({
    "headers": {
      "Content-Type": "text/csv",
      "Content-disposition": "attachment;filename=FlightInfo.csv"
     }
  });

  while(row = getRow()) {
    pairName = [];
    pairFirstName = "";
    pairLastName = "";
    training = "";
    paid = "";
    r = row.doc;
    if (r.type === "Veteran") {
      pairName = r.guardian.name.split(" ");
      conflict = (r.vet_type || "WWII");
      medNotesExp = (r.medical.review || "");
    } else if (r.type === "Guardian") {
      if ((r.veteran) && (r.veteran.pairings) && (r.veteran.pairings.length > 0)) {
        pairName = r.veteran.pairings[0].name.split(" ");
      }

      // Training status
      if (r.flight.training) {
        if (r.flight.training_complete) {
          training = "* ";
        }
        training += r.flight.training;
      }
      // Paid status
      if (r.flight.paid) {
        paid = "Y";
      } else {
        paid = "N";
      }
      medNotesExp = (r.medical.experience || "");
    }

    // Split the pair name.
    if (pairName.length > 1) {
      pairFirstName = pairName.slice(0,1);
      pairLastName = pairName.slice(1).join(" ");
    } else if (pairName.length === 1) {
      pairLastName = pairName.slice(0,1);
    }

    result = {
        flight_id:             r.flight.id,
        type:                  r.type,
        app_date:              r.app_date,
        conflict:              conflict,
        first_name:            r.name.first,
        middle_name:           r.name.middle,
        last_name:             r.name.last,
        nick_name:             r.name.nickname,
        addr_street:           r.address.street,
        addr_city:             r.address.city,
        addr_county:           r.address.county,
        addr_state:            r.address.state,
        addr_zip:              r.address.zip,
        addr_phone_day:        r.address.phone_day,
        addr_phone_eve:        r.address.phone_eve,
        addr_phone_mbl:        r.address.phone_mbl,
        addr_email:            r.address.email,
        birth_date:            r.birth_date,
        gender:                r.gender,
        shirt_size:            r.shirt.size,
        flight_status:         r.flight.status,
        flight_status_note:    r.flight.status_note,
        flight_confirmed_date: r.flight.confirmed_date,
        flight_confirmed_by:   r.flight.confirmed_by,
        flight_training:       training,
        flight_training_notes: r.flight.training_notes,
        flight_paid:           paid,
        flight_seat:           r.flight.seat,
        flight_group:          (r.flight.group || ""),
        medical_limitations:   (r.medical.limitations || ""),
        medical_notes_exp:     medNotesExp,
        pair_first_name:       pairFirstName,
        pair_last_name:        pairLastName,
        flight_bus:            r.flight.bus.replace("Alpha", "Alpha ").replace("Bravo", "Bravo "),
        id:                    r._id,
        rev:                   r._rev,
        created_at:            r.metadata.created_at,
        updated_at:            r.metadata.updated_at,
        created_by:            r.metadata.created_by,
        updated_by:            r.metadata.updated_by
    }

    if (headerNeeded) {
      for (key in result) {
        send("\"");
        send(key);
        send("\",");
      }
      send("\n");
      headerNeeded = false;
    }

    for (key in result) {
      if (result[key]) {
        if (result[key].length > 0) {
          send("\"");
          send(result[key]);
          send("\"");
        } else {
          if (!isNaN(result[key])) {
            send(result[key].toString());
          }
        }
      }
      send(",");
    }
    send("\n");
  }
}

<|endoftext|>


<commit_before>
var ForgePlugins = ForgePlugins || {};

ForgePlugins.Editor = function()
{
    this._ui = null;

    this._history = null;

    this._selected = null;

    this.onSelected = new FORGE.EventDispatcher(this);

    this.onLoadComplete = new FORGE.EventDispatcher(this);
};

ForgePlugins.Editor.HOTSPOT_DEFAULT_CONFIG =
{
    name: "untitled hotspot",
    facingCenter: true
};

ForgePlugins.Editor.prototype =
{
    boot: function()
    {
        this._history = new ForgePlugins.EditorHistory(this);
        this._history.add();

        this._ui = new ForgePlugins.EditorUI(this);
    },

    add: function(config, history)
    {
        if(typeof config === "undefined" || config === null)
        {
            config = this._generateHotspotConfig();
        }

        var hotspot = this.viewer.hotspots.create(config);
        hotspot.onClick.add(this._onClickHandler, this);
        this.reset();
        this.selected = hotspot.uid;

        if(history !== false)
        {
            this._history.add("hotspot add");
        }
    },

    delete: function(uid, history)
    {
        this.viewer.hotspots.remove(uid);
        this.reset();

        this.selected = null;

        if(history !== false)
        {
            this._history.add();
        }
    },

    save: function()
    {
        var dump = this.dump();
        var json = JSON.stringify(dump, null, 4);
        var blob = new Blob([json], {type: "application/json"});

        if(window.navigator.msSaveOrOpenBlob)
        {
            window.navigator.msSaveOrOpenBlob(blob, "hotspots.json");
        }
        else
        {
            var url  = URL.createObjectURL(blob);

            var a = document.createElement('a');
            a.download = "hotspots.json";
            a.href = url;

            if(typeof document.createEvent === "function")
            {
                var event = document.createEvent("MouseEvents");
                event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
                a.dispatchEvent(event);
            }
            else if (typeof a.click === "function")
            {
                a.click();
            }
        }
    },

    load: function(hotspots, history)
    {
        this.viewer.hotspots.clear();
        this.clear();

        for(var i = 0, ii = hotspots.length; i < ii; i++)
        {
            var hotspot = this.viewer.hotspots.create(hotspots[i]);
            hotspot.onClick.add(this._onClickHandler, this);
        }

        this.populate();

        if(history === true)
        {
            this._history.add("hotspot load");
        }

        this.onLoadComplete.dispatch(null, true);
    },

    clear: function()
    {
        this.viewer.renderer.objects.clear();
    },

    populate: function()
    {
        this.viewer.renderer.objects.createRenderScenes();
        this.viewer.renderer.renderPipeline.addRenderScenes(viewer.renderer.objects.renderScenes);
    },

    reset: function()
    {
        this.clear();
        this.populate();
    },

    lookAt: function(uid)
    {
        var hs = FORGE.UID.get(uid);

        if(FORGE.Utils.isTypeOf(hs, "Hotspot3D"))
        {
            var pos = hs.transform.position;
            var spherical = FORGE.Math.cartesianToSpherical(pos.x, pos.y, pos.z, "deg");
            this.viewer.camera.lookAt(spherical.theta, spherical.phi, 0, this.viewer.camera.fov, 100);
        }
    },

    update: function()
    {

    },

    dump: function()
    {
        var dump = this.viewer.hotspots.dump();
        return dump;
    },

    destroy: function()
    {
        this._ui.destroy;
        this._ui = null;
    },

    _generateHotspotConfig: function()
    {
        var config = FORGE.Utils.extendSimpleObject({}, ForgePlugins.Editor.HOTSPOT_DEFAULT_CONFIG);
        config.transform = { position: { theta: viewer.camera.yaw, phi: viewer.camera.pitch } };

        return config;
    },

    _onClickHandler: function(event)
    {
        this.selected = event.emitter.uid;
    }
};

Object.defineProperty(ForgePlugins.Editor.prototype, "history",
{
    get: function()
    {
        return this._history;
    }
});

Object.defineProperty(ForgePlugins.Editor.prototype, "selected",
{
    get: function()
    {
        return this._selected;
    },

    set: function(value)
    {
        if(value === null)
        {
            this._selected = null;
            this.onSelected.dispatch();
            return;
        }

        var hs = FORGE.UID.get(value);

        if(FORGE.Utils.isTypeOf(hs, "Hotspot3D"))
        {
            this._selected = value;
            this.lookAt(this._selected);
            this.onSelected.dispatch();
        }
    }
});<commit_msg>Fix cartesianToSpherical unit parameter<commit_after>
var ForgePlugins = ForgePlugins || {};

ForgePlugins.Editor = function()
{
    this._ui = null;

    this._history = null;

    this._selected = null;

    this.onSelected = new FORGE.EventDispatcher(this);

    this.onLoadComplete = new FORGE.EventDispatcher(this);
};

ForgePlugins.Editor.HOTSPOT_DEFAULT_CONFIG =
{
    name: "untitled hotspot",
    facingCenter: true
};

ForgePlugins.Editor.prototype =
{
    boot: function()
    {
        this._history = new ForgePlugins.EditorHistory(this);
        this._history.add();

        this._ui = new ForgePlugins.EditorUI(this);
    },

    add: function(config, history)
    {
        if(typeof config === "undefined" || config === null)
        {
            config = this._generateHotspotConfig();
        }

        var hotspot = this.viewer.hotspots.create(config);
        hotspot.onClick.add(this._onClickHandler, this);
        this.reset();
        this.selected = hotspot.uid;

        if(history !== false)
        {
            this._history.add("hotspot add");
        }
    },

    delete: function(uid, history)
    {
        this.viewer.hotspots.remove(uid);
        this.reset();

        this.selected = null;

        if(history !== false)
        {
            this._history.add();
        }
    },

    save: function()
    {
        var dump = this.dump();
        var json = JSON.stringify(dump, null, 4);
        var blob = new Blob([json], {type: "application/json"});

        if(window.navigator.msSaveOrOpenBlob)
        {
            window.navigator.msSaveOrOpenBlob(blob, "hotspots.json");
        }
        else
        {
            var url  = URL.createObjectURL(blob);

            var a = document.createElement('a');
            a.download = "hotspots.json";
            a.href = url;

            if(typeof document.createEvent === "function")
            {
                var event = document.createEvent("MouseEvents");
                event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
                a.dispatchEvent(event);
            }
            else if (typeof a.click === "function")
            {
                a.click();
            }
        }
    },

    load: function(hotspots, history)
    {
        this.viewer.hotspots.clear();
        this.clear();

        for(var i = 0, ii = hotspots.length; i < ii; i++)
        {
            var hotspot = this.viewer.hotspots.create(hotspots[i]);
            hotspot.onClick.add(this._onClickHandler, this);
        }

        this.populate();

        if(history === true)
        {
            this._history.add("hotspot load");
        }

        this.onLoadComplete.dispatch(null, true);
    },

    clear: function()
    {
        this.viewer.renderer.objects.clear();
    },

    populate: function()
    {
        this.viewer.renderer.objects.createRenderScenes();
        this.viewer.renderer.renderPipeline.addRenderScenes(viewer.renderer.objects.renderScenes);
    },

    reset: function()
    {
        this.clear();
        this.populate();
    },

    lookAt: function(uid)
    {
        var hs = FORGE.UID.get(uid);

        if(FORGE.Utils.isTypeOf(hs, "Hotspot3D"))
        {
            var pos = hs.transform.position;
            var spherical = FORGE.Math.cartesianToSpherical(pos.x, pos.y, pos.z, FORGE.Math.DEGREES);
            this.viewer.camera.lookAt(spherical.theta, spherical.phi, 0, this.viewer.camera.fov, 100);
        }
    },

    update: function()
    {

    },

    dump: function()
    {
        var dump = this.viewer.hotspots.dump();
        return dump;
    },

    destroy: function()
    {
        this._ui.destroy;
        this._ui = null;
    },

    _generateHotspotConfig: function()
    {
        var config = FORGE.Utils.extendSimpleObject({}, ForgePlugins.Editor.HOTSPOT_DEFAULT_CONFIG);
        config.transform = { position: { theta: viewer.camera.yaw, phi: viewer.camera.pitch } };

        return config;
    },

    _onClickHandler: function(event)
    {
        this.selected = event.emitter.uid;
    }
};

Object.defineProperty(ForgePlugins.Editor.prototype, "history",
{
    get: function()
    {
        return this._history;
    }
});

Object.defineProperty(ForgePlugins.Editor.prototype, "selected",
{
    get: function()
    {
        return this._selected;
    },

    set: function(value)
    {
        if(value === null)
        {
            this._selected = null;
            this.onSelected.dispatch();
            return;
        }

        var hs = FORGE.UID.get(value);

        if(FORGE.Utils.isTypeOf(hs, "Hotspot3D"))
        {
            this._selected = value;
            this.lookAt(this._selected);
            this.onSelected.dispatch();
        }
    }
});<|endoftext|>


<commit_before>'use strict';
let Q = require('q');
let errorWithMetadata = require('./util/error-with-metadata');
let commandQueue = require('./command-queue');
exports = module.exports = function(commands, more) {
	more = more || {};
	this.commands = commands;
	this.labels = more.labels || [];
	this.rewind();
};
exports.prototype.rewind = function() {
	this.done = false;
	this.executionOffset = 0;
};
exports.prototype.run = function(startingLabel) {
	let script = this;
	if(this.running) {
		throw new Error (
			"The script is already running"
		);
	}
	if(startingLabel) {
		this.goTo(startingLabel);
	}
	return Q.async(function*() {
		script.running = true;
		while(!script.done) {
			let nextCommand = getNextCommand(script);
			yield executeCommand(script, nextCommand);
		}
		script.running = false;
	})();
};
function getNextCommand(script) {
	return script.commands[script.executionOffset];
}
function executeCommand(script, command) {
	let deferred = Q.defer();
	commandQueue.push(command, function() {
		if(script.jumping) {
			script.jumping = false;
		}
		else {
			++script.executionOffset;
		}
		deferred.resolve();
		return 'done';
	});
	return deferred.promise;
}
exports.prototype.goTo = function(labelName) {
	this.jumping = true;
	let label = this.labels[labelName];
	if(label === undefined) {
		throw errorWithMetadata (
			new Error (
				"Non-existent label '" + labelName + "'"
			), {
				labelName,
			}
		);
	}
	if(label > this.commands.length) {
		this.executionOffset = this.commands.length;
	}
	else {
		this.executionOffset = label;
	}
};
exports.prototype.exit = function() {
	this.done = true;
};
<commit_msg>fix low-level-script: Break execution loop after last command.<commit_after>'use strict';
let Q = require('q');
let errorWithMetadata = require('./util/error-with-metadata');
let commandQueue = require('./command-queue');
exports = module.exports = function(commands, more) {
	more = more || {};
	this.commands = commands;
	this.labels = more.labels || [];
	this.rewind();
};
exports.prototype.rewind = function() {
	this.done = false;
	this.executionOffset = 0;
};
exports.prototype.run = function(startingLabel) {
	let script = this;
	if(this.running) {
		throw new Error (
			"The script is already running"
		);
	}
	if(startingLabel) {
		this.goTo(startingLabel);
	}
	return Q.async(function*() {
		script.running = true;
		while(!script.done) {
			let nextCommand = getNextCommand(script);
			if(!nextCommand) {
				break;
			}
			yield executeCommand(script, nextCommand);
		}
		script.running = false;
	})();
};
function getNextCommand(script) {
	return script.commands[script.executionOffset];
}
function executeCommand(script, command) {
	let deferred = Q.defer();
	commandQueue.push(command, function() {
		if(script.jumping) {
			script.jumping = false;
		}
		else {
			++script.executionOffset;
		}
		deferred.resolve();
		return 'done';
	});
	return deferred.promise;
}
exports.prototype.goTo = function(labelName) {
	this.jumping = true;
	let label = this.labels[labelName];
	if(label === undefined) {
		throw errorWithMetadata (
			new Error (
				"Non-existent label '" + labelName + "'"
			), {
				labelName,
			}
		);
	}
	if(label > this.commands.length) {
		this.executionOffset = this.commands.length;
	}
	else {
		this.executionOffset = label;
	}
};
exports.prototype.exit = function() {
	this.done = true;
};
<|endoftext|>


<commit_before>/*
 * patterns.js
 * A list of sentence patterns to be parsed by main.js
 *
 * New Age Bullshit Generator
 * © 2014 Seb Pearce (sebpearce.com)
 * Licensed under the MIT License.
 *
 */

var sentencePatterns = new Array();

// explaining
sentencePatterns[0] = [
  'BullshitDrone is powered by two 1 GHz computers for the best and smoothest aerial photography experience imaginable',
  'The only all-in-one drone to offer live video streaming in vivid HD from your GoPro directly to your mobile device',
  'The first drone with a brain, BullshitDrone is powered by twin computers',
  'Only drone to stream live HD video from GoPro direct to iOS/Android devices',
  'Revolutionary, intuitive video game-style controller',
  'Take to the air in seconds with effortless pushbutton flight',
  'Powerful Smart Shot technology makes capturing incredible cinematic footage easy and automatic',
  'Unique safety features like pause and “safety net” mean you can fly with confidence',
  'First with in-flight GoPro® access: Start/stop recording and adjust camera settings from the ground (with BullshitDrone Gimbal)',
  'Full-featured app for iOS and Android puts total craft control at your fingertips',
  'Follow Me mode enables BullshitDrone to automatically track and film any subject',
  'BullshitDrone Gimbal (sold separately) delivers smooth footage, GoPro® power and control and HDMI video',
  'Future-proof accessory and gimbal bays make BullshitDrone ready for tomorrow\’s technology',
  'Free software and feature updates ensure your BullshitDrone evolves at the pace of innovation',
  'The smart drone keeps getting smarter: LiDAR for super stable indoor flight; ballistic parachute system; flight rewind and more, all scheduled for release',
  'World-class customer support means we’ve always got your back',
  'live seven-day-a-week phone support from qualified engineers; in-app service tickets; and a 30-day money-back guarantee',
  'Imagine amazing, immersive footage at your fingertips. Imagine capturing content fit for the big screen',
  'With an integrated, stabilized camera, your BullshitDrone 3 brings your imagination to life.',
  'The BullshitDrone 3 Professional comes with a 4K camera, while the BullshitDrone 3 Advanced shoots 1080p HD.',
  'Experience a new view of your world, over distances up to 1.2 miles (2km) and in real time.',
  'This immersive view streams to you in absolute clarity, enabling you to get that perfect shot and experience flying in a unique way.',
  'Fly while seeing everything in HD, and feel what it\'s like to be above your world without ever leaving the ground.',
  'The BullshitDrone Typhoon is a complete aerial photography and videography (APV) system.',
  'The BullshitDrone System includes the aircraft, a 1080P HD camera, a CGO2-GB 3 axis precision gimbal, the ST10 personal ground station, 2 batteries, a charger and an 8GB micro SD card.',
  'The ST10 personal ground station is a 10-channel 2.4GHz RC transmitter that supports a 5.8GHz video downlink that delivers streaming video to the built-in screen of the transmitter.',
  'While the BullshitDrone offers a superior flying experience, it is the quality of the video and still images that sets it apart.',
  'The CGO2 GB combines the 12 megapixel, 1080p/60FPS camera and the 3-axis brushless gimbal into a single unit that captures smooth, steady aerial video and still images in vivid detail.'

];

// warnings
sentencePatterns[1] = [
  'We can no longer afford to live with nMassBad',
  'Without nMass, one cannot viPerson',
  'Only a nPerson of the nCosmos may vtMass this nOf of nMass',
  'You must take a stand against nMassBad',
  'Yes, it is possible to vtDestroy the things that can vtDestroy us, but not without nMass on our side',
  'nMassBad is the antithesis of nMass',
  'You may be ruled by nMassBad without realizing it. Do not let it vtDestroy the nTheXOf of your nPath',
  'the complexity of the present time seems to demand a ing of our nOurPlural if we are going to survive',
  'nMassBad is born in the gap where nMass has been excluded',
  'Where there is nMassBad, nMass cannot thrive'
];

// future hope
sentencePatterns[2] = [
  'Soon there will be a ing of nMass the likes of which the nCosmos has never seen',
  'It is time to take nMass to the next level',
  'Imagine a ing of what could be',
  'Eons from now, we nPersonPlural will viPerson like never before as we are ppPerson by the nCosmos',
  'It is a sign of things to come',
  'The future will be a adj ing of nMass',
  'This nPath never ends',
  'We must learn how to lead adj lives in the face of nMassBad',
  'We must vtPerson ourselves and vtPerson others',
  'The nOf of nMass is now happening worldwide',
  'We are being called to explore the nCosmos itself as an interface between nMass and nMass',
  'It is in ing that we are ppPerson',
  'The nCosmos is approaching a tipping point'
];

// you and your problems
sentencePatterns[3] = [
  'Although you may not realize it, you are adj',
  'nPerson, look within and vtPerson yourself',
  'Have you found your nPath?',
  'How should you navigate this adj nCosmos?',
  'It can be difficult to know where to begin',
  'If you have never experienced this nOf fixedAdvP, it can be difficult to viPerson',
  'The nCosmos is calling to you via fixedNP. Can you hear it?'
];

// history
sentencePatterns[4] = [
  'Throughout history, humans have been interacting with the nCosmos via fixedNP',
  'Reality has always been adjWith nPersonPlural whose nOurPlural are ppThingPrep nMass',
  'Our conversations with other nPersonPlural have led to a ing of adjPrefix adj consciousness',
  'Humankind has nothing to lose',
  'We are in the midst of a adj ing of nMass that will vOpenUp the nCosmos itself',
  'Who are we? Where on the great nPath will we be ppPerson?',
  'We are at a crossroads of nMass and nMassBad'
  // 'Even nProphet '
];

// selling point
sentencePatterns[5] = [
  'Through nSubject, our nOurPlural are ppThingPrep nMass',
  'nSubject may be the solution to what’s holding you back from a adjBig nOf of nMass',
  'You will soon be ppPerson by a power deep within yourself — a power that is adj, adj',
  'As you viPerson, you will enter into infinite nMass that transcends understanding'
];

/*
nCosmos
nPerson
nPersonPlural
nOurPlural
nMass
nMassBad
nPath
nOf
ing
adj
adjBig
adjWidth
adjPrefix
vtMass
vtPerson
viPerson
nTheXOf
ppPerson
ppThingPrep
fixedAdvP
fixedNP
nSubject
 */
<commit_msg>Replace CGO2, and ST10 with BullshitDrone<commit_after>/*
 * patterns.js
 * A list of sentence patterns to be parsed by main.js
 *
 * New Age Bullshit Generator
 * © 2014 Seb Pearce (sebpearce.com)
 * Licensed under the MIT License.
 *
 */

var sentencePatterns = new Array();

// explaining
sentencePatterns[0] = [
  'BullshitDrone is powered by two 1 GHz computers for the best and smoothest aerial photography experience imaginable',
  'The only all-in-one drone to offer live video streaming in vivid HD from your GoPro directly to your mobile device',
  'The first drone with a brain, BullshitDrone is powered by twin computers',
  'Only drone to stream live HD video from GoPro direct to iOS/Android devices',
  'Revolutionary, intuitive video game-style controller',
  'Take to the air in seconds with effortless pushbutton flight',
  'Powerful Smart Shot technology makes capturing incredible cinematic footage easy and automatic',
  'Unique safety features like pause and “safety net” mean you can fly with confidence',
  'First with in-flight GoPro® access: Start/stop recording and adjust camera settings from the ground (with BullshitDrone Gimbal)',
  'Full-featured app for iOS and Android puts total craft control at your fingertips',
  'Follow Me mode enables BullshitDrone to automatically track and film any subject',
  'BullshitDrone Gimbal (sold separately) delivers smooth footage, GoPro® power and control and HDMI video',
  'Future-proof accessory and gimbal bays make BullshitDrone ready for tomorrow\’s technology',
  'Free software and feature updates ensure your BullshitDrone evolves at the pace of innovation',
  'The smart drone keeps getting smarter: LiDAR for super stable indoor flight; ballistic parachute system; flight rewind and more, all scheduled for release',
  'World-class customer support means we’ve always got your back',
  'live seven-day-a-week phone support from qualified engineers; in-app service tickets; and a 30-day money-back guarantee',
  'Imagine amazing, immersive footage at your fingertips. Imagine capturing content fit for the big screen',
  'With an integrated, stabilized camera, your BullshitDrone 3 brings your imagination to life.',
  'The BullshitDrone 3 Professional comes with a 4K camera, while the BullshitDrone 3 Advanced shoots 1080p HD.',
  'Experience a new view of your world, over distances up to 1.2 miles (2km) and in real time.',
  'This immersive view streams to you in absolute clarity, enabling you to get that perfect shot and experience flying in a unique way.',
  'Fly while seeing everything in HD, and feel what it\'s like to be above your world without ever leaving the ground.',
  'The BullshitDrone Typhoon is a complete aerial photography and videography (APV) system.',
  'The BullshitDrone System includes the aircraft, a 1080P HD camera, a BullshitDrone-GB 3 axis precision gimbal, the BullshitDrone personal ground station, 2 batteries, a charger and an 8GB micro SD card.',
  'The BullshitDrone personal ground station is a 10-channel 2.4GHz RC transmitter that supports a 5.8GHz video downlink that delivers streaming video to the built-in screen of the transmitter.',
  'While the BullshitDrone offers a superior flying experience, it is the quality of the video and still images that sets it apart.',
  'The BullshitDrone GB combines the 12 megapixel, 1080p/60FPS camera and the 3-axis brushless gimbal into a single unit that captures smooth, steady aerial video and still images in vivid detail.'

];

// warnings
sentencePatterns[1] = [
  'We can no longer afford to live with nMassBad',
  'Without nMass, one cannot viPerson',
  'Only a nPerson of the nCosmos may vtMass this nOf of nMass',
  'You must take a stand against nMassBad',
  'Yes, it is possible to vtDestroy the things that can vtDestroy us, but not without nMass on our side',
  'nMassBad is the antithesis of nMass',
  'You may be ruled by nMassBad without realizing it. Do not let it vtDestroy the nTheXOf of your nPath',
  'the complexity of the present time seems to demand a ing of our nOurPlural if we are going to survive',
  'nMassBad is born in the gap where nMass has been excluded',
  'Where there is nMassBad, nMass cannot thrive'
];

// future hope
sentencePatterns[2] = [
  'Soon there will be a ing of nMass the likes of which the nCosmos has never seen',
  'It is time to take nMass to the next level',
  'Imagine a ing of what could be',
  'Eons from now, we nPersonPlural will viPerson like never before as we are ppPerson by the nCosmos',
  'It is a sign of things to come',
  'The future will be a adj ing of nMass',
  'This nPath never ends',
  'We must learn how to lead adj lives in the face of nMassBad',
  'We must vtPerson ourselves and vtPerson others',
  'The nOf of nMass is now happening worldwide',
  'We are being called to explore the nCosmos itself as an interface between nMass and nMass',
  'It is in ing that we are ppPerson',
  'The nCosmos is approaching a tipping point'
];

// you and your problems
sentencePatterns[3] = [
  'Although you may not realize it, you are adj',
  'nPerson, look within and vtPerson yourself',
  'Have you found your nPath?',
  'How should you navigate this adj nCosmos?',
  'It can be difficult to know where to begin',
  'If you have never experienced this nOf fixedAdvP, it can be difficult to viPerson',
  'The nCosmos is calling to you via fixedNP. Can you hear it?'
];

// history
sentencePatterns[4] = [
  'Throughout history, humans have been interacting with the nCosmos via fixedNP',
  'Reality has always been adjWith nPersonPlural whose nOurPlural are ppThingPrep nMass',
  'Our conversations with other nPersonPlural have led to a ing of adjPrefix adj consciousness',
  'Humankind has nothing to lose',
  'We are in the midst of a adj ing of nMass that will vOpenUp the nCosmos itself',
  'Who are we? Where on the great nPath will we be ppPerson?',
  'We are at a crossroads of nMass and nMassBad'
  // 'Even nProphet '
];

// selling point
sentencePatterns[5] = [
  'Through nSubject, our nOurPlural are ppThingPrep nMass',
  'nSubject may be the solution to what’s holding you back from a adjBig nOf of nMass',
  'You will soon be ppPerson by a power deep within yourself — a power that is adj, adj',
  'As you viPerson, you will enter into infinite nMass that transcends understanding'
];

/*
nCosmos
nPerson
nPersonPlural
nOurPlural
nMass
nMassBad
nPath
nOf
ing
adj
adjBig
adjWidth
adjPrefix
vtMass
vtPerson
viPerson
nTheXOf
ppPerson
ppThingPrep
fixedAdvP
fixedNP
nSubject
 */
<|endoftext|>


<commit_before>'use strict';

var compo = require('compo'),
    GameData = require('../shared/game-data'),
    CameraTarget = require('../../behavior/camera-target'),
    Controller = require('../../behavior/keyboard-controller'),
    Character = require('../shared/character'),
    PlayerStateMachine = require('./state/player-machine'),
    Gun = require('./gun'),
    buildGraphics = require('./animation'),
    buildPhysics = require('../shared/character-physics');

module.exports = function(engine, world) {
  var entity = world.entity();
  var data = new GameData(0, 0, 4, 9, 20 - 4, 24 - 9);

  var physics = buildPhysics(engine, entity, data, 'player');
  var graphics = buildGraphics(engine, entity, data);

  var character = new Character(data, physics, graphics);
  var gun = new Gun(engine, world, character);
  engine.behavior.table.attach(entity, gun);

  var states = new PlayerStateMachine({
    world: world,
    engine: engine,
    character: character,
    gun: gun
  });

  var cameraTarget = new CameraTarget(engine, character.data.loc);
  engine.behavior.table.attach(entity, cameraTarget);

  var controller = new Controller(character, states);
  engine.behavior.table.attach(entity, controller);

  physics.emitter.on('collide:bottom', function() {
    states.land();
  });

  physics.emitter.on('collide:left', npcDamage(states));
  physics.emitter.on('collide:right', npcDamage(states));
  physics.emitter.on('collide:top', npcDamage(states));

  return character;
};

function npcDamage(state) {
  return function(collider) {
    if(collider.type === 'npc') {
      console.log(collider.type);
      state.takeDamage();
    }
  }
}
<commit_msg>Nuke stray console.log<commit_after>'use strict';

var compo = require('compo'),
    GameData = require('../shared/game-data'),
    CameraTarget = require('../../behavior/camera-target'),
    Controller = require('../../behavior/keyboard-controller'),
    Character = require('../shared/character'),
    PlayerStateMachine = require('./state/player-machine'),
    Gun = require('./gun'),
    buildGraphics = require('./animation'),
    buildPhysics = require('../shared/character-physics');

module.exports = function(engine, world) {
  var entity = world.entity();
  var data = new GameData(0, 0, 4, 9, 20 - 4, 24 - 9);

  var physics = buildPhysics(engine, entity, data, 'player');
  var graphics = buildGraphics(engine, entity, data);

  var character = new Character(data, physics, graphics);
  var gun = new Gun(engine, world, character);
  engine.behavior.table.attach(entity, gun);

  var states = new PlayerStateMachine({
    world: world,
    engine: engine,
    character: character,
    gun: gun
  });

  var cameraTarget = new CameraTarget(engine, character.data.loc);
  engine.behavior.table.attach(entity, cameraTarget);

  var controller = new Controller(character, states);
  engine.behavior.table.attach(entity, controller);

  physics.emitter.on('collide:bottom', function() {
    states.land();
  });

  physics.emitter.on('collide:left', npcDamage(states));
  physics.emitter.on('collide:right', npcDamage(states));
  physics.emitter.on('collide:top', npcDamage(states));

  return character;
};

function npcDamage(state) {
  return function(collider) {
    if(collider.type === 'npc') {
      state.takeDamage();
    }
  }
}
<|endoftext|>


<commit_before>import makeOrganism from '../../../src'
import Counter from '../components/Counter'

export default makeOrganism(Counter, {
  initial: ({ initialCount = 0 }) => ({ count: initialCount }),
  increment: ({ stride = 1 }) => ({ count }) => ({ count: count + stride }),
  decrement: ({ stride = 1 }) => ({ count }) => ({ count: count - stride })
})
<commit_msg>Example: use props passed to state change handler<commit_after>import makeOrganism from '../../../src'
import Counter from '../components/Counter'

export default makeOrganism(Counter, {
  initial: ({ initialCount = 0 }) => ({ count: initialCount }),
  increment: () => ({ count }, { stride = 1 }) => ({ count: count + stride }),
  decrement: () => ({ count }, { stride = 1 }) => ({ count: count - stride })
})
<|endoftext|>