<commit_before>import electron from 'electron';
import {forEach, values} from 'lodash';
import {Observable, Disposable, CompositeDisposable, SerialDisposable} from 'rx-lite';
import {getSubstitutionRegExp, getSmartQuotesRegExp, getSmartDashesRegExp,
  scrubInputString, formatReplacement} from './regular-expressions';
import {isUndoRedoEvent} from './undo-redo-event';

const packageName = 'electron-text-substitutions';
const d = require('debug-electron')(packageName);

const registerForPreferenceChangedIpcMessage = `${packageName}-register-renderer`;
const unregisterForPreferenceChangedIpcMessage = `${packageName}-unregister-renderer`;
const preferenceChangedIpcMessage = `${packageName}-preference-changed`;

const userDefaultsTextSubstitutionsKey = 'NSUserDictionaryReplacementItems';
const userDefaultsSmartQuotesKey = 'NSAutomaticQuoteSubstitutionEnabled';
const userDefaultsSmartDashesKey = 'NSAutomaticDashSubstitutionEnabled';

const textPreferenceChangedKeys = [
  'IMKTextReplacementDidChangeNotification',
  'NSAutomaticQuoteSubstitutionEnabledChanged',
  'NSAutomaticDashSubstitutionEnabledChanged'
];

let ipcMain, ipcRenderer, systemPreferences;
let registeredWebContents = {};

/**
 * Adds an `input` event listener to the given element (an <input> or
 * <textarea>) that will substitute text based on the user's replacements in
 * `NSUserDefaults`.
 *
 * In addition, this method will listen for changes to `NSUserDefaults` and
 * update accordingly.
 *
 * @param  {EventTarget} element          The DOM node to listen to; should fire the `input` event
 * @param  {Object} preferenceOverrides   Used to override text preferences in testing
 *
 * @return {Disposable}                   A `Disposable` that will clean up everything this method did
 */
export default function performTextSubstitution(element, preferenceOverrides = null) {
  if (!element || !element.addEventListener) throw new Error(`Element is null or not an EventTarget`);
  if (!process || !process.type === 'renderer') throw new Error(`Not in an Electron renderer context`);
  if (process.platform !== 'darwin') throw new Error(`Only supported on OS X`);

  ipcRenderer = ipcRenderer || electron.ipcRenderer;
  systemPreferences = systemPreferences || electron.remote.systemPreferences;

  if (!systemPreferences || !systemPreferences.getUserDefault) {
    throw new Error(`Electron ${process.versions.electron} is not supported`);
  }

  ipcRenderer.send(registerForPreferenceChangedIpcMessage);

  window.addEventListener('beforeunload', () => {
    d(`Window unloading, unregister any listeners`);
    ipcRenderer.send(unregisterForPreferenceChangedIpcMessage);
  });

  let currentAttach = assignDisposableToListener(element, preferenceOverrides);

  ipcRenderer.on(preferenceChangedIpcMessage, () => {
    d(`User modified text preferences, reattaching listener`);
    assignDisposableToListener(element, preferenceOverrides, currentAttach);
  });

  return currentAttach;
}

/**
 * Subscribes to text preference changed notifications and notifies listeners
 * in renderer processes. This method must be called from the browser process,
 * and should be called before any renderer process calls
 * `performTextSubstitution`.
 *
 * @return {Disposable}  A `Disposable` that will clean up everything this method did
 */
export function listenForPreferenceChanges() {
  if (!process || !process.type === 'browser') throw new Error(`Not in an Electron browser context`);
  if (process.platform !== 'darwin') throw new Error(`Only supported on OS X`);

  ipcMain = ipcMain || electron.ipcMain;
  systemPreferences = systemPreferences || electron.systemPreferences;

  ipcMain.on(registerForPreferenceChangedIpcMessage, ({sender}) => {
    d(`Registering webContents ${sender.getId()} for preference changes`);
    registeredWebContents[sender.getId()] = sender;
  });

  ipcMain.on(unregisterForPreferenceChangedIpcMessage, ({sender}) => {
    d(`Unregistering webContents ${sender.getId()}`);
    delete registeredWebContents[sender.getId()];
  });

  let notificationDisp = Observable.fromArray(textPreferenceChangedKeys)
    .flatMap((key) => observableForPreferenceNotification(key))
    .debounce(1000)
    .do((key) => d(`Got a ${key}`))
    .subscribe(() => {
      forEach(values(registeredWebContents), (sender) => sender.send(preferenceChangedIpcMessage));
    });

  return new CompositeDisposable(
    notificationDisp,
    new Disposable(() => ipcMain.removeAllListeners(registerForPreferenceChangedIpcMessage)),
    new Disposable(() => ipcMain.removeAllListeners(unregisterForPreferenceChangedIpcMessage))
  );
}

/**
 * Creates an Observable that will `onNext` when the given key in
 * `NSUserDefaults` changes.
 *
 * @param  {String} preferenceChangedKey  The key to listen for
 * @return {Disposable}                   A Disposable that will unsubscribe the listener
 */
function observableForPreferenceNotification(preferenceChangedKey) {
  return Observable.create((subj) => {
    let subscriberId = systemPreferences.subscribeNotification(preferenceChangedKey, () => {
      subj.onNext(preferenceChangedKey);
    });

    return new Disposable(() => systemPreferences.unsubscribeNotification(subscriberId));
  });
}

function assignDisposableToListener(element, preferenceOverrides, currentAttach = null) {
  let textPreferences = preferenceOverrides || readSystemTextPreferences();
  let replacementItems = getReplacementItems(textPreferences);

  currentAttach = currentAttach || new SerialDisposable();
  currentAttach.setDisposable(addInputListener(element, replacementItems));
  return currentAttach;
}

function readSystemTextPreferences() {
  return {
    substitutions: systemPreferences.getUserDefault(userDefaultsTextSubstitutionsKey, 'array') || [],
    useSmartQuotes: systemPreferences.getUserDefault(userDefaultsSmartQuotesKey, 'boolean'),
    useSmartDashes: systemPreferences.getUserDefault(userDefaultsSmartDashesKey, 'boolean')
  };
}

/**
 * @typedef {Object} TextSubstitution
 * @property {String} replace The text to replace
 * @property {String} with    The replacement text
 * @property {Bool}   on      True if this substitution is enabled
 */

 /**
  * Creates a regular expression for each text substitution entry, in addition
  * to expressions for smart quotes and dashes (if they're enabled).
  *
  * @param  {Array<TextSubstitution>} {substitutions  An array of text substitution entries
  * @param  {Bool} useSmartQuotes                     True if smart quotes is on
  * @param  {Bool} useSmartDashes}                    True if smart dashes is on
  * @return {Array<ReplacementItem>}                  An array of replacement items
  */
function getReplacementItems({substitutions, useSmartQuotes, useSmartDashes}) {
  d(`Smart quotes are ${useSmartQuotes ? 'on' : 'off'}`);
  d(`Smart dashes are ${useSmartDashes ? 'on' : 'off'}`);

  let additionalReplacements = [
    ...(useSmartQuotes ? getSmartQuotesRegExp() : []),
    ...(useSmartDashes ? getSmartDashesRegExp() : [])
  ];

  d(`Found ${substitutions.length} substitutions in NSUserDictionaryReplacementItems`);

  // NB: Run each replacement string through our smart quotes & dashes regex,
  // so that an input event doesn't cause chained substitutions.
  let userDictionaryReplacements = substitutions
    .filter((substitution) => substitution.on !== false)
    .map((substitution) => getSubstitutionRegExp(substitution.replace,
      scrubInputString(substitution.with, additionalReplacements)));

  return [
    ...userDictionaryReplacements,
    ...additionalReplacements
  ];
}

/**
 * Subscribes to the `input` event and performs text substitution.
 *
 * @param  {EventTarget} element                      The DOM node to listen to
 * @param  {Array<ReplacementItem>} replacementItems  An array of replacement items
 * @return {Disposable}                               A `Disposable` that will remove the listener
 */
function addInputListener(element, replacementItems) {
  let ignoreEvent = false;

  let inputListener = () => {
    if (ignoreEvent) return;
    ignoreEvent = true;

    for (let {regExp, replacement} of replacementItems) {
      let match = element.value.match(regExp);
      if (match && match.length === 3) {
        d(`Got a match of length ${match[0].length} at index ${match.index}: ${JSON.stringify(match)}`);

        let selection = {
          startIndex: match.index,
          endIndex: match.index + match[0].length
        };

        // Are we inserting characters before the end of input (vs appending?)
        let isInserting = element.selectionEnd !== element.value.length;

        replaceText(element, selection, formatReplacement(match, replacement));

        // Our regex includes trailing whitespace / word boundaries, which,
        // when inserting text, cause the caret position to jump to the end of
        // the match. We subtract everything to the right of the match
        // to preserve the caret position.
        if (isInserting) element.selectionEnd -= match[2].length;
      }
    }

    ignoreEvent = false;
  };

  let keyDownListener = (e) => {
    if (isUndoRedoEvent(e)) {
      d(`Undo or redo text from ${e.target.value}`);
      ignoreEvent = true;
    }
  };

  let keyUpListener = () => {
    ignoreEvent = false;
  };

  element.addEventListener('keydown', keyDownListener, true);
  element.addEventListener('keyup', keyUpListener, true);
  element.addEventListener('input', inputListener);

  d(`Added input listener to ${element.id} matching against ${replacementItems.length} replacements`);

  return new Disposable(() => {
    element.removeEventListener('keydown', keyDownListener);
    element.removeEventListener('keyup', keyUpListener);
    element.removeEventListener('input', inputListener);

    d(`Removed input listener from ${element.id}`);
  });
}

/**
 * Performs the actual text replacement using `dispatchEvent`. We use events to
 * preserve the user's cursor index and make the substitution undoable.
 *
 * @param  {EventTarget} element  The DOM node where text is being substituted
 * @param  {Number} {startIndex   Start index of the text to replace
 * @param  {Number} endIndex}     End index of the text to replace
 * @param  {String} newText       The text being inserted
 */
function replaceText(element, {startIndex, endIndex}, newText) {
  let textEvent = document.createEvent('TextEvent');
  textEvent.initTextEvent('textInput', true, true, null, newText);

  element.selectionStart = startIndex;
  element.selectionEnd = endIndex;

  d(`Replacing ${element.value.substring(startIndex, endIndex)} with ${newText}`);
  element.dispatchEvent(textEvent);
}
<commit_msg>Handle destroyed or crashed webContents.<commit_after>import electron from 'electron';
import {forEach, values} from 'lodash';
import {Observable, Disposable, CompositeDisposable, SerialDisposable} from 'rx-lite';
import {getSubstitutionRegExp, getSmartQuotesRegExp, getSmartDashesRegExp,
  scrubInputString, formatReplacement} from './regular-expressions';
import {isUndoRedoEvent} from './undo-redo-event';

const packageName = 'electron-text-substitutions';
const d = require('debug-electron')(packageName);

const registerForPreferenceChangedIpcMessage = `${packageName}-register-renderer`;
const unregisterForPreferenceChangedIpcMessage = `${packageName}-unregister-renderer`;
const preferenceChangedIpcMessage = `${packageName}-preference-changed`;

const userDefaultsTextSubstitutionsKey = 'NSUserDictionaryReplacementItems';
const userDefaultsSmartQuotesKey = 'NSAutomaticQuoteSubstitutionEnabled';
const userDefaultsSmartDashesKey = 'NSAutomaticDashSubstitutionEnabled';

const textPreferenceChangedKeys = [
  'IMKTextReplacementDidChangeNotification',
  'NSAutomaticQuoteSubstitutionEnabledChanged',
  'NSAutomaticDashSubstitutionEnabledChanged'
];

let ipcMain, ipcRenderer, systemPreferences;
let registeredWebContents = {};

/**
 * Adds an `input` event listener to the given element (an <input> or
 * <textarea>) that will substitute text based on the user's replacements in
 * `NSUserDefaults`.
 *
 * In addition, this method will listen for changes to `NSUserDefaults` and
 * update accordingly.
 *
 * @param  {EventTarget} element          The DOM node to listen to; should fire the `input` event
 * @param  {Object} preferenceOverrides   Used to override text preferences in testing
 *
 * @return {Disposable}                   A `Disposable` that will clean up everything this method did
 */
export default function performTextSubstitution(element, preferenceOverrides = null) {
  if (!element || !element.addEventListener) throw new Error(`Element is null or not an EventTarget`);
  if (!process || !process.type === 'renderer') throw new Error(`Not in an Electron renderer context`);
  if (process.platform !== 'darwin') throw new Error(`Only supported on OS X`);

  ipcRenderer = ipcRenderer || electron.ipcRenderer;
  systemPreferences = systemPreferences || electron.remote.systemPreferences;

  if (!systemPreferences || !systemPreferences.getUserDefault) {
    throw new Error(`Electron ${process.versions.electron} is not supported`);
  }

  ipcRenderer.send(registerForPreferenceChangedIpcMessage);

  window.addEventListener('beforeunload', () => {
    d(`Window unloading, unregister any listeners`);
    ipcRenderer.send(unregisterForPreferenceChangedIpcMessage);
  });

  let currentAttach = assignDisposableToListener(element, preferenceOverrides);

  ipcRenderer.on(preferenceChangedIpcMessage, () => {
    d(`User modified text preferences, reattaching listener`);
    assignDisposableToListener(element, preferenceOverrides, currentAttach);
  });

  return currentAttach;
}

/**
 * Subscribes to text preference changed notifications and notifies listeners
 * in renderer processes. This method must be called from the browser process,
 * and should be called before any renderer process calls
 * `performTextSubstitution`.
 *
 * @return {Disposable}  A `Disposable` that will clean up everything this method did
 */
export function listenForPreferenceChanges() {
  if (!process || !process.type === 'browser') throw new Error(`Not in an Electron browser context`);
  if (process.platform !== 'darwin') throw new Error(`Only supported on OS X`);

  ipcMain = ipcMain || electron.ipcMain;
  systemPreferences = systemPreferences || electron.systemPreferences;

  ipcMain.on(registerForPreferenceChangedIpcMessage, ({sender}) => {
    let id = sender.getId();
    d(`Registering webContents ${id} for preference changes`);
    registeredWebContents[id] = { id, sender };
  });

  ipcMain.on(unregisterForPreferenceChangedIpcMessage, ({sender}) => {
    d(`Unregistering webContents ${sender.getId()}`);
    delete registeredWebContents[sender.getId()];
  });

  let notificationDisp = Observable.fromArray(textPreferenceChangedKeys)
    .flatMap((key) => observableForPreferenceNotification(key))
    .debounce(1000)
    .subscribe(notifyAllListeners);

  return new CompositeDisposable(
    notificationDisp,
    new Disposable(() => ipcMain.removeAllListeners(registerForPreferenceChangedIpcMessage)),
    new Disposable(() => ipcMain.removeAllListeners(unregisterForPreferenceChangedIpcMessage))
  );
}

/**
 * Creates an Observable that will `onNext` when the given key in
 * `NSUserDefaults` changes.
 *
 * @param  {String} preferenceChangedKey  The key to listen for
 * @return {Disposable}                   A Disposable that will unsubscribe the listener
 */
function observableForPreferenceNotification(preferenceChangedKey) {
  return Observable.create((subj) => {
    let subscriberId = systemPreferences.subscribeNotification(preferenceChangedKey, () => {
      d(`Got a ${preferenceChangedKey}`);
      subj.onNext(preferenceChangedKey);
    });

    return new Disposable(() => systemPreferences.unsubscribeNotification(subscriberId));
  });
}

/**
 * Sends an IPC message to each `WebContents` that is doing text substitution,
 * unless it has been destroyed, in which case remove it from our list.
 */
function notifyAllListeners() {
  forEach(values(registeredWebContents), ({id, sender}) => {
    if (sender.isDestroyed() || sender.isCrashed()) {
      d(`WebContents ${id} is gone, removing it`);
      delete registeredWebContents[id];
    } else {
      sender.send(preferenceChangedIpcMessage);
    }
  });
}

function assignDisposableToListener(element, preferenceOverrides, currentAttach = null) {
  let textPreferences = preferenceOverrides || readSystemTextPreferences();
  let replacementItems = getReplacementItems(textPreferences);

  currentAttach = currentAttach || new SerialDisposable();
  currentAttach.setDisposable(addInputListener(element, replacementItems));
  return currentAttach;
}

function readSystemTextPreferences() {
  return {
    substitutions: systemPreferences.getUserDefault(userDefaultsTextSubstitutionsKey, 'array') || [],
    useSmartQuotes: systemPreferences.getUserDefault(userDefaultsSmartQuotesKey, 'boolean'),
    useSmartDashes: systemPreferences.getUserDefault(userDefaultsSmartDashesKey, 'boolean')
  };
}

/**
 * @typedef {Object} TextSubstitution
 * @property {String} replace The text to replace
 * @property {String} with    The replacement text
 * @property {Bool}   on      True if this substitution is enabled
 */

 /**
  * Creates a regular expression for each text substitution entry, in addition
  * to expressions for smart quotes and dashes (if they're enabled).
  *
  * @param  {Array<TextSubstitution>} {substitutions  An array of text substitution entries
  * @param  {Bool} useSmartQuotes                     True if smart quotes is on
  * @param  {Bool} useSmartDashes}                    True if smart dashes is on
  * @return {Array<ReplacementItem>}                  An array of replacement items
  */
function getReplacementItems({substitutions, useSmartQuotes, useSmartDashes}) {
  d(`Smart quotes are ${useSmartQuotes ? 'on' : 'off'}`);
  d(`Smart dashes are ${useSmartDashes ? 'on' : 'off'}`);

  let additionalReplacements = [
    ...(useSmartQuotes ? getSmartQuotesRegExp() : []),
    ...(useSmartDashes ? getSmartDashesRegExp() : [])
  ];

  d(`Found ${substitutions.length} substitutions in NSUserDictionaryReplacementItems`);

  // NB: Run each replacement string through our smart quotes & dashes regex,
  // so that an input event doesn't cause chained substitutions.
  let userDictionaryReplacements = substitutions
    .filter((substitution) => substitution.on !== false)
    .map((substitution) => getSubstitutionRegExp(substitution.replace,
      scrubInputString(substitution.with, additionalReplacements)));

  return [
    ...userDictionaryReplacements,
    ...additionalReplacements
  ];
}

/**
 * Subscribes to the `input` event and performs text substitution.
 *
 * @param  {EventTarget} element                      The DOM node to listen to
 * @param  {Array<ReplacementItem>} replacementItems  An array of replacement items
 * @return {Disposable}                               A `Disposable` that will remove the listener
 */
function addInputListener(element, replacementItems) {
  let ignoreEvent = false;

  let inputListener = () => {
    if (ignoreEvent) return;
    ignoreEvent = true;

    for (let {regExp, replacement} of replacementItems) {
      let match = element.value.match(regExp);
      if (match && match.length === 3) {
        d(`Got a match of length ${match[0].length} at index ${match.index}: ${JSON.stringify(match)}`);

        let selection = {
          startIndex: match.index,
          endIndex: match.index + match[0].length
        };

        // Are we inserting characters before the end of input (vs appending?)
        let isInserting = element.selectionEnd !== element.value.length;

        replaceText(element, selection, formatReplacement(match, replacement));

        // Our regex includes trailing whitespace / word boundaries, which,
        // when inserting text, cause the caret position to jump to the end of
        // the match. We subtract everything to the right of the match
        // to preserve the caret position.
        if (isInserting) element.selectionEnd -= match[2].length;
      }
    }

    ignoreEvent = false;
  };

  let keyDownListener = (e) => {
    if (isUndoRedoEvent(e)) {
      d(`Undo or redo text from ${e.target.value}`);
      ignoreEvent = true;
    }
  };

  let keyUpListener = () => {
    ignoreEvent = false;
  };

  element.addEventListener('keydown', keyDownListener, true);
  element.addEventListener('keyup', keyUpListener, true);
  element.addEventListener('input', inputListener);

  d(`Added input listener to ${element.id} matching against ${replacementItems.length} replacements`);

  return new Disposable(() => {
    element.removeEventListener('keydown', keyDownListener);
    element.removeEventListener('keyup', keyUpListener);
    element.removeEventListener('input', inputListener);

    d(`Removed input listener from ${element.id}`);
  });
}

/**
 * Performs the actual text replacement using `dispatchEvent`. We use events to
 * preserve the user's cursor index and make the substitution undoable.
 *
 * @param  {EventTarget} element  The DOM node where text is being substituted
 * @param  {Number} {startIndex   Start index of the text to replace
 * @param  {Number} endIndex}     End index of the text to replace
 * @param  {String} newText       The text being inserted
 */
function replaceText(element, {startIndex, endIndex}, newText) {
  let textEvent = document.createEvent('TextEvent');
  textEvent.initTextEvent('textInput', true, true, null, newText);

  element.selectionStart = startIndex;
  element.selectionEnd = endIndex;

  d(`Replacing ${element.value.substring(startIndex, endIndex)} with ${newText}`);
  element.dispatchEvent(textEvent);
}
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
// import App from './components/app.jsx';

class App extends React.Component {
  render () {
    return (
      <h1>{"JIM"}</h1>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('mountpoint')
);
<commit_msg>Move app into its own file.<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import data from './data/5e-SRD-Monsters';
import App from './components/app.jsx';

ReactDOM.render(
  <App data={data} />,
  document.getElementById('mountpoint')
);
<|endoftext|>


<commit_before>const movingAverage = require('./lib/moving-average');
const getTicker = require('./lib/get-ticker');
const logger = require('cli-logger');
const _ = require('lodash');
const redis = require('redis');
const config = require('config');
const helper = require('./helper');

const log = logger({ level: logger.INFO });
let previousRecommendation;
let shortPeriod = config.get('strategy.shortPeriod');

const redisClient = redis.createClient();
redisClient.on('error', redisError => log.error(redisError));

const poll = market => new Promise(async (resolvePoll) => {
  try {
    // Get the short moving average from bittrex
    // & longer moving average from the cache or bittrex
    const toTimestamp = new Date().getTime();
    const fromTimestampShort = toTimestamp - shortPeriod;
    const longPeriod = shortPeriod * config.get('strategy.shortLongPeriodRatio');
    const fromTimestampLong = toTimestamp - longPeriod;

    const tasks = [
      getTicker(market),
      movingAverage(market, fromTimestampShort, toTimestamp),
      movingAverage(market, fromTimestampLong, toTimestamp, 'bittrexCache'),
    ];

    const [ticker, movingAverageShort, movingAverageLong] = await Promise.all(tasks);

    // TODO : Publish to events or rules queue
    const recommendation = {};
    if (movingAverageShort > movingAverageLong) {
      recommendation.action = 'BUY';
      recommendation.buyPrice = ticker.Ask;
    } else {
      recommendation.action = 'SELL';
      recommendation.sellPrice = ticker.Bid;
    }

    if (_.isEqual(recommendation, previousRecommendation)) {
      return;
    }

    if (_.has(previousRecommendation, 'action')) {
      log.info(`${previousRecommendation.action}, ${recommendation.action}`);
    }
    if (_.has(previousRecommendation, 'action') && recommendation.action !== previousRecommendation.action) {
      // Cache recommendation
      await redisClient.sadd([`${market}-crossovers`, `${JSON.stringify({ movingAverageShort, movingAverageLong, action: recommendation.action, price: (recommendation.buyPrice || recommendation.sellPrice), timestamp: new Date().getTime() })}`]);

      // Crossover point
      log.info('poll, recommendation:  CROSSOVER');
    }
    previousRecommendation = _.cloneDeep(recommendation);

    log.info(`poll, movingAverageShort(${helper.millisecondsToHours(shortPeriod)}), ${new Date()}:  ${movingAverageShort}`);
    log.info(`poll, movingAverageLong(${helper.millisecondsToHours(longPeriod)}), ${new Date()}:  ${movingAverageLong}`);
    log.info(`poll, recommendation : ${recommendation.action} currency in ${market} at price ${(recommendation.buyPrice || recommendation.sellPrice)}`);
    resolvePoll(recommendation);
  } catch (pollError) {
    log.error(`poll, error: ${pollError}`);
    if (pollError.message === 'Not enough market data') {
      shortPeriod += config.get('strategy.periodIncreaseMilliseconds');
      log.info(`poll, increased short period: ${helper.millisecondsToHours(shortPeriod)}, ${new Date()}`);
    }
  }
});

setInterval(() => poll('USDT-ETH'), 1000);
<commit_msg>The poll just calculates and does not make recommendations<commit_after>const movingAverage = require('./lib/moving-average');
const getTicker = require('./lib/get-ticker');
const logger = require('cli-logger');
const _ = require('lodash');
const redis = require('redis');
const config = require('config');
const helper = require('./helper');

const log = logger({ level: logger.INFO });
let previousMovingAverages;
let shortPeriod = config.get('strategy.shortPeriod');

const redisClient = redis.createClient();
redisClient.on('error', redisError => log.error(redisError));

const poll = market => new Promise(async (resolvePoll) => {
  try {
    // Get the short moving average from bittrex
    // & longer moving average from the cache or bittrex
    const toTimestamp = new Date().getTime();
    const fromTimestampShort = toTimestamp - shortPeriod;
    const longPeriod = shortPeriod * config.get('strategy.shortLongPeriodRatio');
    const fromTimestampLong = toTimestamp - longPeriod;

    const tasks = [
      getTicker(market),
      movingAverage(market, fromTimestampShort, toTimestamp),
      movingAverage(market, fromTimestampLong, toTimestamp, 'bittrexCache'),
    ];

    const [ticker, movingAverageShort, movingAverageLong] = await Promise.all(tasks);

    // TODO : Publish to events or rules queue
    const movingAverages = {};
    if (movingAverageShort > movingAverageLong) {
      movingAverages.trend = 'UP';
      movingAverages.buyPrice = ticker.Ask;
    } else {
      movingAverages.trend = 'DOWN';
      movingAverages.sellPrice = ticker.Bid;
    }

    if (_.isEqual(movingAverages, previousMovingAverages)) {
      return;
    }

    if (_.has(previousMovingAverages, 'trend') && movingAverages.trend !== previousMovingAverages.trend) {
      // Cache recommendation
      await redisClient.sadd([`${market}-crossovers`, `${JSON.stringify({ movingAverageShort, movingAverageLong, trend: movingAverages.trend, price: (movingAverages.buyPrice || movingAverages.sellPrice), timestamp: new Date().getTime() })}`]);

      // Crossover point
      log.info('poll, trend:  Crossover');
    }
    previousMovingAverages = _.cloneDeep(movingAverages);

    log.info(`poll, movingAverageShort(${helper.millisecondsToHours(shortPeriod)}), ${new Date()}:  ${movingAverageShort}`);
    log.info(`poll, movingAverageLong(${helper.millisecondsToHours(longPeriod)}), ${new Date()}:  ${movingAverageLong}`);
    log.info(`poll, movingAverages : ${market} trending ${movingAverages.trend} at price ${(movingAverages.buyPrice || movingAverages.sellPrice)}`);
    resolvePoll(movingAverages);
  } catch (pollError) {
    log.error(`poll, error: ${pollError}`);
    if (pollError.message === 'Not enough market data') {
      shortPeriod += config.get('strategy.periodIncreaseMilliseconds');
      log.info(`poll, increased short period: ${helper.millisecondsToHours(shortPeriod)}, ${new Date()}`);
    }
  }
});

setInterval(() => poll('USDT-ETH'), 1000);
<|endoftext|>


<commit_before>require('es6-promise').polyfill()
require('fetch-everywhere')

const GET = 'GET'
const POST = 'POST'
const POST_FILE = 'POSTFILE'
const PUT = 'PUT'
const DELETE = 'DELETE'

function JSONParseError(status, body) {
  this.name = 'JSONParseError'
  this.status = status
  this.body = body
  this.message = 'Received unexpected response from the server.'
  this.stack = (new Error()).stack
}
JSONParseError.prototype = Object.create(Error.prototype)
JSONParseError.prototype.constructor = JSONParseError

function Non200Error(status, body) {
  this.name = 'Non200Error'
  this.status = status
  this.body = body
  this.message = 'A network error occurred. Please try again.'
  this.stack = (new Error()).stack
}
Non200Error.prototype = Object.create(Error.prototype)
Non200Error.prototype.constructor = Non200Error

const contentType = {
  GET: 'application/x-www-form-urlencoded',
  POST: 'application/json',
  PUT: 'application/json',
  DELETE: 'application/json',
}

const requestFactory = (meth, notify = null) => {
  let methodHeaders = {}

  // We need this to be able to send formData
  if (meth === POST_FILE) {
    meth = POST
  } else {
    methodHeaders['Content-Type'] = contentType[meth]
  }

  return (token, path, body = undefined, options = {}, headers = {}) => {
    let finalHeaders = {
      ...methodHeaders,
      ...headers
    }

    if (token) {
      finalHeaders['Authorization'] = 'Bearer ' + token
    }

    return fetch(path, {
      ...options,
      headers: finalHeaders,
      method: meth,
      body: body
    }).then(resp => {
      return resp.json().catch(() => {
        if (notify)
          notify('Received unexpected response from the server.')
        throw new JSONParseError(resp.status)
      }).then(json => {
        if (resp.status < 200 || resp.status > 300) {
          if (notify)
            notify('Received unexpected response from the server.')
          throw new Non200Error(resp.status, json)
        } else {
          return Promise.resolve(json)
        }
      })
    })
  }
}

module.exports = {requestFactory, GET, POST, POST_FILE, PUT, DELETE, JSONParseError, Non200Error}
<commit_msg>Support for not wanting json<commit_after>require('es6-promise').polyfill()
require('fetch-everywhere')

const GET = 'GET'
const POST = 'POST'
const POST_FILE = 'POSTFILE'
const PUT = 'PUT'
const DELETE = 'DELETE'

function JSONParseError(status, body) {
  this.name = 'JSONParseError'
  this.status = status
  this.body = body
  this.message = 'Received unexpected response from the server.'
  this.stack = (new Error()).stack
}
JSONParseError.prototype = Object.create(Error.prototype)
JSONParseError.prototype.constructor = JSONParseError

function Non200Error(status, body) {
  this.name = 'Non200Error'
  this.status = status
  this.body = body
  this.message = 'A network error occurred. Please try again.'
  this.stack = (new Error()).stack
}
Non200Error.prototype = Object.create(Error.prototype)
Non200Error.prototype.constructor = Non200Error

const contentType = {
  GET: 'application/x-www-form-urlencoded',
  POST: 'application/json',
  PUT: 'application/json',
  DELETE: 'application/json',
}

const requestFactory = (meth, notify = null) => {
  let methodHeaders = {}

  // We need this to be able to send formData
  if (meth === POST_FILE) {
    meth = POST
  } else {
    methodHeaders['Content-Type'] = contentType[meth]
  }

  return (token, path, body = undefined, options = {}, headers = {}) => {
    let finalHeaders = {
      ...methodHeaders,
      ...headers
    }

    if (token) {
      finalHeaders['Authorization'] = 'Bearer ' + token
    }

    return fetch(path, {
      ...options,
      headers: finalHeaders,
      method: meth,
      body: body
    }).then(resp => {
      // If the user does not want response converted to json include a "json": false mapping in the arguments[3]
      if ("json" in options && options["json"] === false){
        if (resp.status < 200 || resp.status > 300) {
          if (notify) notify('Received unexpected response from the server.');
          throw new Non200Error(resp.status, json);
        }
        return Promise.resolve(resp.text());
      }

      return resp.json().catch(() => {
        if (notify)
          notify('Received unexpected response from the server.')
        throw new JSONParseError(resp.status)
      }).then(json => {
        if (resp.status < 200 || resp.status > 300) {
          if (notify)
            notify('Received unexpected response from the server.')
          throw new Non200Error(resp.status, json)
        } else {
          return Promise.resolve(json)
        }
      })
    })
  }
}

module.exports = {requestFactory, GET, POST, POST_FILE, PUT, DELETE, JSONParseError, Non200Error}
<|endoftext|>


<commit_before>import LCARS from 'lcars';
import Immutable from 'immutable';
import {EventEmitter} from 'events';
import {Component} from 'react';
const CHANGE_EVENT = 'change';

class WelpComponent extends Component {

  constructor(props, stores) {
    super(props);
    this.stores = stores;
    this.handleStoresChanged = this.handleStoresChanged.bind(this);
    this.getStateFromStores = this.getStateFromStores.bind(this);
    this.state = this.getStateFromStores();
  }

  componentDidMount() {
    this.mounted = true;
    if (!this.stores) {
      throw new Error('You must have a stores array property');
    }
    if (!this.getStateFromStores) {
      throw new Error('You must implement this.getStateFromStores');
    }
    this.stores.forEach(store => {
      store.addChangeListener(this.handleStoresChanged);
    }, this);
  }

  shouldComponentUpdate(nextProps, nextState) {
    return !Immutable.is(this.props, nextProps) || !Immutable.is(this.state, nextState);
  }

  componentWillUnmount() {
    this.mounted = false;
    this.stores.forEach(store => {
      store.removeChangeListener(this.handleStoresChanged);
    }, this);
  }

  getStateFromStores() {
    if (this.stores.length > 1) {
      return this.stores.reduce((a, b) => a.getDataStructure().merge(b.getDataStructure())).toJS();
    }
    return this.stores[0].getDataStructure().toJS();
  }

  handleStoresChanged() {
    if (this.mounted) {
      return this.setState(this.getStateFromStores());
    }
  }

}

class WelpStore extends EventEmitter {
  constructor(store_data, callback) {
    super();
    if (process.env.NODE_ENV !== 'production') {
      const store_data_type = Object.prototype.toString.call(store_data);
      if (store_data_type !== '[object Object]') {
        throw new Error('Base Store: The first argument must be type object');
      }
      if (typeof callback !== 'function') {
        throw new Error('Base Store: The second argument must be type function');
      }
    }
    this._data = Immutable.fromJS(store_data);
    this.dispatchToken = LCARS.register((action) => {
      callback(action);
    });
  }
  _check_data(current, next) {
    if (current !== next) {
      this._data = next;
      this.emit(CHANGE_EVENT);
    }
    return this._data;
  }
  get(key) {
    return this._data.get(key);
  }
  getStateData() {
    return this._data.toJS();
  }
  set(key, value) {
    return this._check_data(this._data, this._data.set(key, value));
  }
  updateIn(key_path, value) {
    return this._check_data(this._data, this._data.updateIn(key_path, value));
  }
  getDataStructure() {
    return this._data;
  }
  emitChange() {
    this.emit(CHANGE_EVENT);
  }
  addChangeListener(callback) {
    this.on(CHANGE_EVENT, callback);
  }
  removeChangeListener(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }
  delete(key) {
    return this._check_data(this._data, this._data.delete(key));
  }
  clear() {
    return this._check_data(this._data, this._data.clear());
  }
  merge(...maps){
    return this._check_data(this._data, this._data.merge(...maps));
  }
}

export {
  WelpComponent,
  WelpStore
};
<commit_msg>changing to component will mount<commit_after>import LCARS from 'lcars';
import Immutable from 'immutable';
import {EventEmitter} from 'events';
import {Component} from 'react';
const CHANGE_EVENT = 'change';

class WelpComponent extends Component {

  constructor(props, stores) {
    super(props);
    this.stores = stores;
    this.handleStoresChanged = this.handleStoresChanged.bind(this);
    this.getStateFromStores = this.getStateFromStores.bind(this);
    this.state = this.getStateFromStores();
  }

  componentDidMount() {
    this.mounted = true;
  }

  componentWillMount() {
    if (!this.stores) {
      throw new Error('You must have a stores array property');
    }
    if (!this.getStateFromStores) {
      throw new Error('You must implement this.getStateFromStores');
    }
    this.stores.forEach(store => {
      store.addChangeListener(this.handleStoresChanged);
    }, this);
  }

  shouldComponentUpdate(nextProps, nextState) {
    return !Immutable.is(this.props, nextProps) || !Immutable.is(this.state, nextState);
  }

  componentWillUnmount() {
    this.mounted = false;
    this.stores.forEach(store => {
      store.removeChangeListener(this.handleStoresChanged);
    }, this);
  }

  getStateFromStores() {
    if (this.stores.length > 1) {
      return this.stores.reduce((a, b) => a.getDataStructure().merge(b.getDataStructure())).toJS();
    }
    return this.stores[0].getDataStructure().toJS();
  }

  handleStoresChanged() {
    if (this.mounted) {
      return this.setState(this.getStateFromStores());
    }
  }

}

class WelpStore extends EventEmitter {
  constructor(store_data, callback) {
    super();
    if (process.env.NODE_ENV !== 'production') {
      const store_data_type = Object.prototype.toString.call(store_data);
      if (store_data_type !== '[object Object]') {
        throw new Error('Base Store: The first argument must be type object');
      }
      if (typeof callback !== 'function') {
        throw new Error('Base Store: The second argument must be type function');
      }
    }
    this._data = Immutable.fromJS(store_data);
    this.dispatchToken = LCARS.register((action) => {
      callback(action);
    });
  }
  _check_data(current, next) {
    if (current !== next) {
      this._data = next;
      this.emit(CHANGE_EVENT);
    }
    return this._data;
  }
  get(key) {
    return this._data.get(key);
  }
  getStateData() {
    return this._data.toJS();
  }
  set(key, value) {
    return this._check_data(this._data, this._data.set(key, value));
  }
  updateIn(key_path, value) {
    return this._check_data(this._data, this._data.updateIn(key_path, value));
  }
  getDataStructure() {
    return this._data;
  }
  emitChange() {
    this.emit(CHANGE_EVENT);
  }
  addChangeListener(callback) {
    this.on(CHANGE_EVENT, callback);
  }
  removeChangeListener(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }
  delete(key) {
    return this._check_data(this._data, this._data.delete(key));
  }
  clear() {
    return this._check_data(this._data, this._data.clear());
  }
  merge(...maps){
    return this._check_data(this._data, this._data.merge(...maps));
  }
}

export {
  WelpComponent,
  WelpStore
};
<|endoftext|>


<commit_before>import CardStack from './Cardstack';
import Card from './Card';

export { Card, CardStack };
<commit_msg>Update index.js<commit_after>import CardStack from './CardStack';
import Card from './Card';

export { Card, CardStack };
<|endoftext|>


<commit_before>var RECYCLED_NODE = 1
var LAZY_NODE = 2
var TEXT_NODE = 3
var EMPTY_OBJ = {}
var EMPTY_ARR = []
var map = EMPTY_ARR.map
var isArray = Array.isArray
var defer =
  typeof requestAnimationFrame !== "undefined"
    ? requestAnimationFrame
    : setTimeout

var createClass = function(obj) {
  var out = ""

  if (typeof obj === "string") return obj

  if (isArray(obj) && obj.length > 0) {
    for (var k = 0, tmp; k < obj.length; k++) {
      if ((tmp = createClass(obj[k])) !== "") {
        out += (out && " ") + tmp
      }
    }
  } else {
    for (var k in obj) {
      if (obj[k]) {
        out += (out && " ") + k
      }
    }
  }

  return out
}

var merge = function(a, b) {
  var out = {}

  for (var k in a) out[k] = a[k]
  for (var k in b) out[k] = b[k]

  return out
}

var batch = function(list) {
  return list.reduce(function(out, item) {
    return out.concat(
      !item || item === true
        ? 0
        : typeof item[0] === "function"
        ? [item]
        : batch(item)
    )
  }, EMPTY_ARR)
}

var isSameAction = function(a, b) {
  return isArray(a) && isArray(b) && a[0] === b[0] && typeof a[0] === "function"
}

var shouldRestart = function(a, b) {
  if (a !== b) {
    for (var k in merge(a, b)) {
      if (a[k] !== b[k] && !isSameAction(a[k], b[k])) return true
      b[k] = a[k]
    }
  }
}

var patchSubs = function(oldSubs, newSubs, dispatch) {
  for (
    var i = 0, oldSub, newSub, subs = [];
    i < oldSubs.length || i < newSubs.length;
    i++
  ) {
    oldSub = oldSubs[i]
    newSub = newSubs[i]
    subs.push(
      newSub
        ? !oldSub ||
          newSub[0] !== oldSub[0] ||
          shouldRestart(newSub[1], oldSub[1])
          ? [
              newSub[0],
              newSub[1],
              newSub[0](dispatch, newSub[1]),
              oldSub && oldSub[2]()
            ]
          : oldSub
        : oldSub && oldSub[2]()
    )
  }
  return subs
}

var patchProperty = function(node, key, oldValue, newValue, listener, isSvg) {
  if (key === "key") {
  } else if (key === "style") {
    for (var k in merge(oldValue, newValue)) {
      oldValue = newValue == null || newValue[k] == null ? "" : newValue[k]
      if (k[0] === "-") {
        node[key].setProperty(k, oldValue)
      } else {
        node[key][k] = oldValue
      }
    }
  } else if (key[0] === "o" && key[1] === "n") {
    if (
      !((node.actions || (node.actions = {}))[
        (key = key.slice(2).toLowerCase())
      ] = newValue)
    ) {
      node.removeEventListener(key, listener)
    } else if (!oldValue) {
      node.addEventListener(key, listener)
    }
  } else if (!isSvg && key !== "list" && key in node) {
    node[key] = newValue == null ? "" : newValue
  } else if (
    newValue == null ||
    newValue === false ||
    (key === "class" && !(newValue = createClass(newValue)))
  ) {
    node.removeAttribute(key)
  } else {
    node.setAttribute(key, newValue)
  }
}

var createNode = function(vdom, listener, isSvg) {
  var ns = "http://www.w3.org/2000/svg"
  var props = vdom.props
  var node =
    vdom.type === TEXT_NODE
      ? document.createTextNode(vdom.name)
      : (isSvg = isSvg || vdom.name === "svg")
      ? document.createElementNS(ns, vdom.name, { is: props.is })
      : document.createElement(vdom.name, { is: props.is })

  for (var k in props) {
    patchProperty(node, k, null, props[k], listener, isSvg)
  }

  for (var i = 0, len = vdom.children.length; i < len; i++) {
    node.appendChild(
      createNode(
        (vdom.children[i] = getVNode(vdom.children[i])),
        listener,
        isSvg
      )
    )
  }

  return (vdom.node = node)
}

var getKey = function(vdom) {
  return vdom == null ? null : vdom.key
}

var patch = function(parent, node, oldVNode, newVNode, listener, isSvg) {
  if (oldVNode === newVNode) {
  } else if (
    oldVNode != null &&
    oldVNode.type === TEXT_NODE &&
    newVNode.type === TEXT_NODE
  ) {
    if (oldVNode.name !== newVNode.name) node.nodeValue = newVNode.name
  } else if (oldVNode == null || oldVNode.name !== newVNode.name) {
    node = parent.insertBefore(
      createNode((newVNode = getVNode(newVNode)), listener, isSvg),
      node
    )
    if (oldVNode != null) {
      parent.removeChild(oldVNode.node)
    }
  } else {
    var tmpVKid
    var oldVKid

    var oldKey
    var newKey

    var oldVProps = oldVNode.props
    var newVProps = newVNode.props

    var oldVKids = oldVNode.children
    var newVKids = newVNode.children

    var oldHead = 0
    var newHead = 0
    var oldTail = oldVKids.length - 1
    var newTail = newVKids.length - 1

    isSvg = isSvg || newVNode.name === "svg"

    for (var i in merge(oldVProps, newVProps)) {
      if (
        (i === "value" || i === "selected" || i === "checked"
          ? node[i]
          : oldVProps[i]) !== newVProps[i]
      ) {
        patchProperty(node, i, oldVProps[i], newVProps[i], listener, isSvg)
      }
    }

    while (newHead <= newTail && oldHead <= oldTail) {
      if (
        (oldKey = getKey(oldVKids[oldHead])) == null ||
        oldKey !== getKey(newVKids[newHead])
      ) {
        break
      }

      patch(
        node,
        oldVKids[oldHead].node,
        oldVKids[oldHead],
        (newVKids[newHead] = getVNode(
          newVKids[newHead++],
          oldVKids[oldHead++]
        )),
        listener,
        isSvg
      )
    }

    while (newHead <= newTail && oldHead <= oldTail) {
      if (
        (oldKey = getKey(oldVKids[oldTail])) == null ||
        oldKey !== getKey(newVKids[newTail])
      ) {
        break
      }

      patch(
        node,
        oldVKids[oldTail].node,
        oldVKids[oldTail],
        (newVKids[newTail] = getVNode(
          newVKids[newTail--],
          oldVKids[oldTail--]
        )),
        listener,
        isSvg
      )
    }

    if (oldHead > oldTail) {
      while (newHead <= newTail) {
        node.insertBefore(
          createNode(
            (newVKids[newHead] = getVNode(newVKids[newHead++])),
            listener,
            isSvg
          ),
          (oldVKid = oldVKids[oldHead]) && oldVKid.node
        )
      }
    } else if (newHead > newTail) {
      while (oldHead <= oldTail) {
        node.removeChild(oldVKids[oldHead++].node)
      }
    } else {
      for (var i = oldHead, keyed = {}, newKeyed = {}; i <= oldTail; i++) {
        if ((oldKey = oldVKids[i].key) != null) {
          keyed[oldKey] = oldVKids[i]
        }
      }

      while (newHead <= newTail) {
        oldKey = getKey((oldVKid = oldVKids[oldHead]))
        newKey = getKey(
          (newVKids[newHead] = getVNode(newVKids[newHead], oldVKid))
        )

        if (
          newKeyed[oldKey] ||
          (newKey != null && newKey === getKey(oldVKids[oldHead + 1]))
        ) {
          if (oldKey == null) {
            node.removeChild(oldVKid.node)
          }
          oldHead++
          continue
        }

        if (newKey == null || oldVNode.type === RECYCLED_NODE) {
          if (oldKey == null) {
            patch(
              node,
              oldVKid && oldVKid.node,
              oldVKid,
              newVKids[newHead],
              listener,
              isSvg
            )
            newHead++
          }
          oldHead++
        } else {
          if (oldKey === newKey) {
            patch(
              node,
              oldVKid.node,
              oldVKid,
              newVKids[newHead],
              listener,
              isSvg
            )
            newKeyed[newKey] = true
            oldHead++
          } else {
            if ((tmpVKid = keyed[newKey]) != null) {
              patch(
                node,
                node.insertBefore(tmpVKid.node, oldVKid && oldVKid.node),
                tmpVKid,
                newVKids[newHead],
                listener,
                isSvg
              )
              newKeyed[newKey] = true
            } else {
              patch(
                node,
                oldVKid && oldVKid.node,
                null,
                newVKids[newHead],
                listener,
                isSvg
              )
            }
          }
          newHead++
        }
      }

      while (oldHead <= oldTail) {
        if (getKey((oldVKid = oldVKids[oldHead++])) == null) {
          node.removeChild(oldVKid.node)
        }
      }

      for (var i in keyed) {
        if (newKeyed[i] == null) {
          node.removeChild(keyed[i].node)
        }
      }
    }
  }

  return (newVNode.node = node)
}

var propsChanged = function(a, b) {
  for (var k in a) if (a[k] !== b[k]) return true
  for (var k in b) if (a[k] !== b[k]) return true
}

var getVNode = function(newVNode, oldVNode) {
  return newVNode.type === LAZY_NODE
    ? ((!oldVNode || propsChanged(oldVNode.lazy, newVNode.lazy)) &&
        ((oldVNode = newVNode.lazy.view(newVNode.lazy)).lazy = newVNode.lazy),
      oldVNode)
    : newVNode
}

var createVNode = function(name, props, children, node, key, type) {
  return {
    name: name,
    props: props,
    children: children,
    node: node,
    type: type,
    key: key
  }
}

var createTextVNode = function(value, node) {
  return createVNode(value, EMPTY_OBJ, EMPTY_ARR, node, undefined, TEXT_NODE)
}

var recycleNode = function(node) {
  return node.nodeType === TEXT_NODE
    ? createTextVNode(node.nodeValue, node)
    : createVNode(
        node.nodeName.toLowerCase(),
        EMPTY_OBJ,
        map.call(node.childNodes, recycleNode),
        node,
        undefined,
        RECYCLED_NODE
      )
}

export var Lazy = function(props) {
  return {
    lazy: props,
    type: LAZY_NODE
  }
}

export var h = function(name, props) {
  for (var vdom, rest = [], children = [], i = arguments.length; i-- > 2; ) {
    rest.push(arguments[i])
  }

  while (rest.length > 0) {
    if (isArray((vdom = rest.pop()))) {
      for (var i = vdom.length; i-- > 0; ) {
        rest.push(vdom[i])
      }
    } else if (vdom === false || vdom === true || vdom == null) {
    } else {
      children.push(typeof vdom === "object" ? vdom : createTextVNode(vdom))
    }
  }

  props = props || EMPTY_OBJ

  return typeof name === "function"
    ? name(props, children)
    : createVNode(name, props, children, undefined, props.key)
}

export var app = function(props) {
  var state = {}
  var lock = false
  var view = props.view
  var node = props.node
  var vdom = node && recycleNode(node)
  var subscriptions = props.subscriptions
  var subs = []

  var listener = function(event) {
    dispatch(this.actions[event.type], event)
  }

  var setState = function(newState) {
    if (state !== newState) {
      state = newState
      if (subscriptions) {
        subs = patchSubs(subs, batch([subscriptions(state)]), dispatch)
      }
      if (view && !lock) defer(render, (lock = true))
    }
    return state
  }

  var dispatch = (props.middleware ||
    function(obj) {
      return obj
    })(function(action, props) {
    return typeof action === "function"
      ? dispatch(action(state, props))
      : isArray(action)
      ? typeof action[0] === "function"
        ? dispatch(
            action[0],
            typeof action[1] === "function" ? action[1](props) : action[1]
          )
        : (batch(action.slice(1)).map(function(fx) {
            fx && fx[0](dispatch, fx[1])
          }, setState(action[0])),
          state)
      : setState(action)
  })

  var render = function() {
    lock = false
    node = patch(
      node.parentNode,
      node,
      vdom,
      (vdom =
        typeof (vdom = view(state)) === "string"
          ? createTextVNode(vdom)
          : vdom),
      listener
    )
  }

  dispatch(props.init)
}
<commit_msg>allow cond swapping of Lazy vnodes & vnodes; fix #884<commit_after>var RECYCLED_NODE = 1
var LAZY_NODE = 2
var TEXT_NODE = 3
var EMPTY_OBJ = {}
var EMPTY_ARR = []
var map = EMPTY_ARR.map
var isArray = Array.isArray
var defer =
  typeof requestAnimationFrame !== "undefined"
    ? requestAnimationFrame
    : setTimeout

var createClass = function(obj) {
  var out = ""

  if (typeof obj === "string") return obj

  if (isArray(obj) && obj.length > 0) {
    for (var k = 0, tmp; k < obj.length; k++) {
      if ((tmp = createClass(obj[k])) !== "") {
        out += (out && " ") + tmp
      }
    }
  } else {
    for (var k in obj) {
      if (obj[k]) {
        out += (out && " ") + k
      }
    }
  }

  return out
}

var merge = function(a, b) {
  var out = {}

  for (var k in a) out[k] = a[k]
  for (var k in b) out[k] = b[k]

  return out
}

var batch = function(list) {
  return list.reduce(function(out, item) {
    return out.concat(
      !item || item === true
        ? 0
        : typeof item[0] === "function"
        ? [item]
        : batch(item)
    )
  }, EMPTY_ARR)
}

var isSameAction = function(a, b) {
  return isArray(a) && isArray(b) && a[0] === b[0] && typeof a[0] === "function"
}

var shouldRestart = function(a, b) {
  if (a !== b) {
    for (var k in merge(a, b)) {
      if (a[k] !== b[k] && !isSameAction(a[k], b[k])) return true
      b[k] = a[k]
    }
  }
}

var patchSubs = function(oldSubs, newSubs, dispatch) {
  for (
    var i = 0, oldSub, newSub, subs = [];
    i < oldSubs.length || i < newSubs.length;
    i++
  ) {
    oldSub = oldSubs[i]
    newSub = newSubs[i]
    subs.push(
      newSub
        ? !oldSub ||
          newSub[0] !== oldSub[0] ||
          shouldRestart(newSub[1], oldSub[1])
          ? [
              newSub[0],
              newSub[1],
              newSub[0](dispatch, newSub[1]),
              oldSub && oldSub[2]()
            ]
          : oldSub
        : oldSub && oldSub[2]()
    )
  }
  return subs
}

var patchProperty = function(node, key, oldValue, newValue, listener, isSvg) {
  if (key === "key") {
  } else if (key === "style") {
    for (var k in merge(oldValue, newValue)) {
      oldValue = newValue == null || newValue[k] == null ? "" : newValue[k]
      if (k[0] === "-") {
        node[key].setProperty(k, oldValue)
      } else {
        node[key][k] = oldValue
      }
    }
  } else if (key[0] === "o" && key[1] === "n") {
    if (
      !((node.actions || (node.actions = {}))[
        (key = key.slice(2).toLowerCase())
      ] = newValue)
    ) {
      node.removeEventListener(key, listener)
    } else if (!oldValue) {
      node.addEventListener(key, listener)
    }
  } else if (!isSvg && key !== "list" && key in node) {
    node[key] = newValue == null ? "" : newValue
  } else if (
    newValue == null ||
    newValue === false ||
    (key === "class" && !(newValue = createClass(newValue)))
  ) {
    node.removeAttribute(key)
  } else {
    node.setAttribute(key, newValue)
  }
}

var createNode = function(vdom, listener, isSvg) {
  var ns = "http://www.w3.org/2000/svg"
  var props = vdom.props
  var node =
    vdom.type === TEXT_NODE
      ? document.createTextNode(vdom.name)
      : (isSvg = isSvg || vdom.name === "svg")
      ? document.createElementNS(ns, vdom.name, { is: props.is })
      : document.createElement(vdom.name, { is: props.is })

  for (var k in props) {
    patchProperty(node, k, null, props[k], listener, isSvg)
  }

  for (var i = 0, len = vdom.children.length; i < len; i++) {
    node.appendChild(
      createNode(
        (vdom.children[i] = getVNode(vdom.children[i])),
        listener,
        isSvg
      )
    )
  }

  return (vdom.node = node)
}

var getKey = function(vdom) {
  return vdom == null ? null : vdom.key
}

var patch = function(parent, node, oldVNode, newVNode, listener, isSvg) {
  if (oldVNode === newVNode) {
  } else if (
    oldVNode != null &&
    oldVNode.type === TEXT_NODE &&
    newVNode.type === TEXT_NODE
  ) {
    if (oldVNode.name !== newVNode.name) node.nodeValue = newVNode.name
  } else if (oldVNode == null || oldVNode.name !== newVNode.name) {
    node = parent.insertBefore(
      createNode((newVNode = getVNode(newVNode)), listener, isSvg),
      node
    )
    if (oldVNode != null) {
      parent.removeChild(oldVNode.node)
    }
  } else {
    var tmpVKid
    var oldVKid

    var oldKey
    var newKey

    var oldVProps = oldVNode.props
    var newVProps = newVNode.props

    var oldVKids = oldVNode.children
    var newVKids = newVNode.children

    var oldHead = 0
    var newHead = 0
    var oldTail = oldVKids.length - 1
    var newTail = newVKids.length - 1

    isSvg = isSvg || newVNode.name === "svg"

    for (var i in merge(oldVProps, newVProps)) {
      if (
        (i === "value" || i === "selected" || i === "checked"
          ? node[i]
          : oldVProps[i]) !== newVProps[i]
      ) {
        patchProperty(node, i, oldVProps[i], newVProps[i], listener, isSvg)
      }
    }

    while (newHead <= newTail && oldHead <= oldTail) {
      if (
        (oldKey = getKey(oldVKids[oldHead])) == null ||
        oldKey !== getKey(newVKids[newHead])
      ) {
        break
      }

      patch(
        node,
        oldVKids[oldHead].node,
        oldVKids[oldHead],
        (newVKids[newHead] = getVNode(
          newVKids[newHead++],
          oldVKids[oldHead++]
        )),
        listener,
        isSvg
      )
    }

    while (newHead <= newTail && oldHead <= oldTail) {
      if (
        (oldKey = getKey(oldVKids[oldTail])) == null ||
        oldKey !== getKey(newVKids[newTail])
      ) {
        break
      }

      patch(
        node,
        oldVKids[oldTail].node,
        oldVKids[oldTail],
        (newVKids[newTail] = getVNode(
          newVKids[newTail--],
          oldVKids[oldTail--]
        )),
        listener,
        isSvg
      )
    }

    if (oldHead > oldTail) {
      while (newHead <= newTail) {
        node.insertBefore(
          createNode(
            (newVKids[newHead] = getVNode(newVKids[newHead++])),
            listener,
            isSvg
          ),
          (oldVKid = oldVKids[oldHead]) && oldVKid.node
        )
      }
    } else if (newHead > newTail) {
      while (oldHead <= oldTail) {
        node.removeChild(oldVKids[oldHead++].node)
      }
    } else {
      for (var i = oldHead, keyed = {}, newKeyed = {}; i <= oldTail; i++) {
        if ((oldKey = oldVKids[i].key) != null) {
          keyed[oldKey] = oldVKids[i]
        }
      }

      while (newHead <= newTail) {
        oldKey = getKey((oldVKid = oldVKids[oldHead]))
        newKey = getKey(
          (newVKids[newHead] = getVNode(newVKids[newHead], oldVKid))
        )

        if (
          newKeyed[oldKey] ||
          (newKey != null && newKey === getKey(oldVKids[oldHead + 1]))
        ) {
          if (oldKey == null) {
            node.removeChild(oldVKid.node)
          }
          oldHead++
          continue
        }

        if (newKey == null || oldVNode.type === RECYCLED_NODE) {
          if (oldKey == null) {
            patch(
              node,
              oldVKid && oldVKid.node,
              oldVKid,
              newVKids[newHead],
              listener,
              isSvg
            )
            newHead++
          }
          oldHead++
        } else {
          if (oldKey === newKey) {
            patch(
              node,
              oldVKid.node,
              oldVKid,
              newVKids[newHead],
              listener,
              isSvg
            )
            newKeyed[newKey] = true
            oldHead++
          } else {
            if ((tmpVKid = keyed[newKey]) != null) {
              patch(
                node,
                node.insertBefore(tmpVKid.node, oldVKid && oldVKid.node),
                tmpVKid,
                newVKids[newHead],
                listener,
                isSvg
              )
              newKeyed[newKey] = true
            } else {
              patch(
                node,
                oldVKid && oldVKid.node,
                null,
                newVKids[newHead],
                listener,
                isSvg
              )
            }
          }
          newHead++
        }
      }

      while (oldHead <= oldTail) {
        if (getKey((oldVKid = oldVKids[oldHead++])) == null) {
          node.removeChild(oldVKid.node)
        }
      }

      for (var i in keyed) {
        if (newKeyed[i] == null) {
          node.removeChild(keyed[i].node)
        }
      }
    }
  }

  return (newVNode.node = node)
}

var propsChanged = function(a, b) {
  for (var k in a) if (a[k] !== b[k]) return true
  for (var k in b) if (a[k] !== b[k]) return true
}

var getVNode = function(newVNode, oldVNode) {
  return newVNode.type === LAZY_NODE
    ? ((!oldVNode || !oldVNode.lazy || propsChanged(oldVNode.lazy, newVNode.lazy)) &&
        ((oldVNode = toVNode(newVNode.lazy.view(newVNode.lazy))).lazy = newVNode.lazy),
      oldVNode)
    : newVNode
}

var createVNode = function(name, props, children, node, key, type) {
  return {
    name: name,
    props: props,
    children: children,
    node: node,
    type: type,
    key: key
  }
}

var createTextVNode = function(value, node) {
  return createVNode(value, EMPTY_OBJ, EMPTY_ARR, node, undefined, TEXT_NODE)
}

var toVNode = function(vnode){
  return typeof vnode === "object" ? vnode : createTextVNode(vnode)
}

var recycleNode = function(node) {
  return node.nodeType === TEXT_NODE
    ? createTextVNode(node.nodeValue, node)
    : createVNode(
        node.nodeName.toLowerCase(),
        EMPTY_OBJ,
        map.call(node.childNodes, recycleNode),
        node,
        undefined,
        RECYCLED_NODE
      )
}

export var Lazy = function(props) {
  return {
    lazy: props,
    type: LAZY_NODE
  }
}

export var h = function(name, props) {
  for (var vdom, rest = [], children = [], i = arguments.length; i-- > 2; ) {
    rest.push(arguments[i])
  }

  while (rest.length > 0) {
    if (isArray((vdom = rest.pop()))) {
      for (var i = vdom.length; i-- > 0; ) {
        rest.push(vdom[i])
      }
    } else if (vdom === false || vdom === true || vdom == null) {
    } else children.push(toVNode(vdom))
  }

  props = props || EMPTY_OBJ

  return typeof name === "function"
    ? name(props, children)
    : createVNode(name, props, children, undefined, props.key)
}

export var app = function(props) {
  var state = {}
  var lock = false
  var view = props.view
  var node = props.node
  var vdom = node && recycleNode(node)
  var subscriptions = props.subscriptions
  var subs = []

  var listener = function(event) {
    dispatch(this.actions[event.type], event)
  }

  var setState = function(newState) {
    if (state !== newState) {
      state = newState
      if (subscriptions) {
        subs = patchSubs(subs, batch([subscriptions(state)]), dispatch)
      }
      if (view && !lock) defer(render, (lock = true))
    }
    return state
  }

  var dispatch = (props.middleware ||
    function(obj) {
      return obj
    })(function(action, props) {
    return typeof action === "function"
      ? dispatch(action(state, props))
      : isArray(action)
      ? typeof action[0] === "function"
        ? dispatch(
            action[0],
            typeof action[1] === "function" ? action[1](props) : action[1]
          )
        : (batch(action.slice(1)).map(function(fx) {
            fx && fx[0](dispatch, fx[1])
          }, setState(action[0])),
          state)
      : setState(action)
  })

  var render = function() {
    lock = false
    node = patch(
      node.parentNode,
      node,
      vdom,
      (vdom =
        typeof (vdom = view(state)) === "string"
          ? createTextVNode(vdom)
          : vdom),
      listener
    )
  }

  dispatch(props.init)
}
<|endoftext|>


<commit_before>import React from 'react';

class HttpsRedirect extends React.Component {

  isLocalHost(hostname) {
    return !!(hostname === 'localhost' ||
              hostname === '[::1]' ||
              hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));
  }

  render() {
    if (
      typeof window !== 'undefined' &&
      window.location &&
      window.location.protocol === 'http:' &&
      !this.isLocalHost(window.location.hostname)
    ) {
      window.location.href = window.location.href.replace("http", "https")
    }

    return (

        { this.props.children }

    );
  }
}

HttpsRedirect.propTypes = {
  children: React.PropTypes.node,
};

export default HttpsRedirect;
<commit_msg>replace only first http<commit_after>import React from 'react';

class HttpsRedirect extends React.Component {

  isLocalHost(hostname) {
    return !!(hostname === 'localhost' ||
              hostname === '[::1]' ||
              hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));
  }

  render() {
    if (
      typeof window !== 'undefined' &&
      window.location &&
      window.location.protocol === 'http:' &&
      !this.isLocalHost(window.location.hostname)
    ) {
      window.location.href = window.location.href.replace(/^http(?!s)/, 'https');
    }

    return (

        { this.props.children }

    );
  }
}

HttpsRedirect.propTypes = {
  children: React.PropTypes.node,
};

export default HttpsRedirect;
<|endoftext|>


<commit_before>'use strict';

var config = require('./config');
var bustAssetsBootstrapper = require('./bust-assets');

function registration(mimosaConfig, register) {
  var bustAssets = bustAssetsBootstrapper(mimosaConfig);
  var bustAllAssets = bustAssets.bustAllAssets;
  var cleanBustAssets = bustAssets.cleanBustAssets;

  if (mimosaConfig.isOptimize) {
    register(['postBuild'], 'afterOptimize', function (mimosaConfig, worflowInfo, next) {
      bustAllAssets(mimosaConfig, next);
    });
  }

  register(['postClean'], 'init', function (mimosaConfig, workflowInfo, next) {
    cleanBustAssets(mimosaConfig, next);
  });
}

function registerCommand(program, retrieveConfig) {
  program
  .command('bust-assets')
  .description('Renamed all the assets which match with the rules specified in the module configuration from the compiled directory')
  .action(function() {
    retrieveConfig(true, function(config) {
      bustAssetsBootstrapper(config).bustAllAssets(config);
    });
  });
}

module.exports = {
  registration: registration,
  registerCommand: registerCommand,
  defaults: config.defaults,
  placeholder: config.placeholder,
  validate: config.validate
};
<commit_msg>Updated the function signatures which has changed in mimosa v3<commit_after>'use strict';

var config = require('./config');
var bustAssetsBootstrapper = require('./bust-assets');

function registration(mimosaConfig, register) {
  var bustAssets = bustAssetsBootstrapper(mimosaConfig);
  var bustAllAssets = bustAssets.bustAllAssets;
  var cleanBustAssets = bustAssets.cleanBustAssets;

  if (mimosaConfig.isOptimize) {
    register(['postBuild'], 'afterOptimize', function (mimosaConfig, worflowInfo, next) {
      bustAllAssets(mimosaConfig, next);
    });
  }

  register(['postClean'], 'init', function (mimosaConfig, workflowInfo, next) {
    cleanBustAssets(mimosaConfig, next);
  });
}

function registerCommand(program, logger, retrieveConfig) {
  program
  .command('bust-assets')
  .description('Renamed all the assets which match with the rules specified in the module configuration from the compiled directory')
  .action(function(opts) {
    retrieveConfig({
      buildFirst: true,
      mdebug: !!opts.mdebug
    }, function(config) {
      bustAssetsBootstrapper(config).bustAllAssets(config);
    });
  });
}

module.exports = {
  registration: registration,
  registerCommand: registerCommand,
  defaults: config.defaults,
  placeholder: config.placeholder,
  validate: config.validate
};
<|endoftext|>


<commit_before>var Model = codebox.require("hr.model");
var Collection = codebox.require("hr.collection");
var _ = codebox.require("hr.utils");
var commands = codebox.require("core/commands");

var MenuItem = Model.extend({
    defaults: {
        type: "entry",
        caption: "",
        command: "",
        items: [],
        args: {},
        position: 0,
        enabled: true
    },

    // Constructor
    initialize: function() {
        MenuItem.__super__.initialize.apply(this, arguments);

        this.items = new MenuItems();

        this.listenTo(this, "change:items", function() {
            this.items.reset(this.get("items"));
        });
        this.items.reset(this.get("items"));

        this.listenTo(commands, "add remove reset change:enabled change:context", this.checkState);
    },

    // Execute the command associated with the entry
    execute: function() {
        commands.run(this.get("command"), this.get("args"))
    },

    // Check visibility
    checkState: function() {
        if (this.get("command")) {
            var cmd = commands.get(this.get("command"));
            var isEnabled = (
                // Command exits
                cmd &&

                // Command is runnable
                cmd.isRunnable()
            );

            this.set("enabled", isEnabled);
        }

        return this.get("enabled");
    }
});

var MenuItems = Collection.extend({
    model: MenuItem,
    comparator: "position"
});

module.exports = MenuItems;
<commit_msg>Update state when commands context changed<commit_after>var Model = codebox.require("hr.model");
var Collection = codebox.require("hr.collection");
var _ = codebox.require("hr.utils");
var commands = codebox.require("core/commands");

var MenuItem = Model.extend({
    defaults: {
        type: "entry",
        caption: "",
        command: "",
        items: [],
        args: {},
        position: 0,
        enabled: true
    },

    // Constructor
    initialize: function() {
        MenuItem.__super__.initialize.apply(this, arguments);

        this.items = new MenuItems();

        this.listenTo(this, "change:items", function() {
            this.items.reset(this.get("items"));
        });
        this.items.reset(this.get("items"));

        this.listenTo(commands, "add remove reset change:enabled change:context context", this.checkState);
    },

    // Execute the command associated with the entry
    execute: function() {
        commands.run(this.get("command"), this.get("args"))
    },

    // Check visibility
    checkState: function() {
        if (this.get("command")) {
            var cmd = commands.get(this.get("command"));
            var isEnabled = (
                // Command exits
                cmd &&

                // Command is runnable
                cmd.isRunnable()
            );

            this.set("enabled", isEnabled);
        }

        return this.get("enabled");
    }
});

var MenuItems = Collection.extend({
    model: MenuItem,
    comparator: "position"
});

module.exports = MenuItems;
<|endoftext|>


<commit_before>(function (document) {

  /* Configuracao dos simbolos */
  document.getElementById('form_alfabeto').addEventListener('submit', function (e) {
    e.returnValue = false;
    var tabela = document.getElementById('alfabeto'),
      simbolo = document.getElementById('simbolo');
    if (simbolo && simbolo.value.length > 0) {
      simbolos.push(simbolo.value);
      alfabeto.tBodies[0].innerHTML += '
' + simbolo.value + '	Remover
';
      simbolo.value = '';
      simbolo.focus();
    }
  });

  document.getElementById('configurar_estados').addEventListener('click', function () {
    document.getElementById('configuracao_alfabeto').classList.add('hidden');

    var cabecalho = '';
    for (var i = 0; i < simbolos.length; i++) {
      cabecalho += '' + simbolos[i] + '	';
    }

    document.getElementById('automato').tHead.rows[0].innerHTML += cabecalho;

    document.getElementById('configuracao_estados').classList.remove('hidden');
  });

  document.getElementById('alfabeto').addEventListener('click', function (e) {
    var target = e.target;
    if (target.classList.contains('alfabeto-remover')) {
      var valor = target.dataset.val;
      if (valor) {
        for (var i = 0; i < simbolos.length; i++) {
          if (valor === simbolos[i]) {
            simbolos.splice(i, 1);
            break;
          }
        }
        if (target.parentNode && target.parentNode.parentNode) {
          target.parentNode.parentNode.remove();
        }
      }
    }
  });

  var gerarOptionEstado = function (indice) {
    var option = document.createElement('option');
    option.text = '{q' + indice + '}';
    option.value = 'q' + indice;

    return option;
  };

  var popularNovosEstados = function () {
    var selects = document.querySelectorAll('#automato select');
    if (selects && selects.length > 0) {
      var i = 0,
        qtd = selects.length;

      for (; i < qtd; i++) {
        for (var x = 0; x < estados.length; x++) {
          if (document.querySelector('#' + selects[i].id + ' option[value=q' + estados[x].id + ']') === null) {
            selects[i].add(gerarOptionEstado(estados[x].id));
          }
        }
      }
    }
  };

  var novoEstado = function (callback) {
    var qtdEstados = estados.length;
    var linha = '
{q' + qtdEstados + '}	';
    for (var i = 0; i < simbolos.length; i++) {
      linha += '<select id="q' + qtdEstados + '_' + simbolos[i] + '"><option value="">NULL</option><option value="0">Novo estado</option></select>	';
    }
    linha += '
';

    var el = document.createElement('tr');

    el.innerHTML = linha;

    document.getElementById('automato').appendChild(el);

    estados.push({
      id: qtdEstados
    });

    popularNovosEstados();

    if (callback && typeof callback === 'function') {
      callback(qtdEstados);
    }
  };

  /* Configuracao dos estados */
  document.getElementById('novo_estado').addEventListener('click', function () {
    novoEstado();
  });

  var incluirTransicoesEstados = function (target, estado) {
    // Cria vinculo de estado
    var id = target.id.replace('q', ''),
      estadoSimbolo = id.split('_');

    if (estadoSimbolo.length === 2) {
      if (estados[estadoSimbolo[0]]) {
        // Os ids são separados por estado_simbolo, por isso:
        // estadoSimbolo[0] = estado
        // estadoSimbolo[1] = simbolo

        //Vincula no estado com id estadoSimbolo[0], no simbolo estadoSimbolo[1], o estado selecionado
        estados[estadoSimbolo[0]][estadoSimbolo[1]] = estado;
      }
    }
  };

  document.getElementById('automato').addEventListener('change', function (e) {
    var target = e.target;
    if (target && target.tagName === 'SELECT') {
      if (target.value === '0') {
        // Criar novo estado
        novoEstado(function (estado) {
          document.getElementById(target.id).value = 'q' + estado;
          incluirTransicoesEstados(target, 'q' + estado);
        });
      }
      else if (target.value !== '') {
        // Cria transicao em estados
        incluirTransicoesEstados(target, target.value);
      }
      else {
        // Remove completamente transicao
        var id = target.id.replace('q', ''),
          estadoSimbolo = id.split('_');

        delete estados[estadoSimbolo[0]][estadoSimbolo[1]];
      }
    }
  });

  document.getElementById('salvar_estados').addEventListener('click', function () {
    var linhas = document.getElementById('automato').tBodies[0].rows;
  });

  // Execucoes iniciais
  document.getElementById('simbolo').focus();

}(document));
<commit_msg>Removidas transicoes dos objetos de estado, vinculos nos selects e linha do estado na tabela. Este commit eh parte da issue #4<commit_after>(function (document) {

  /* Configuracao dos simbolos */
  document.getElementById('form_alfabeto').addEventListener('submit', function (e) {
    e.returnValue = false;
    var tabela = document.getElementById('alfabeto'),
      simbolo = document.getElementById('simbolo');
    if (simbolo && simbolo.value.length > 0) {
      simbolos.push(simbolo.value);
      alfabeto.tBodies[0].innerHTML += '
' + simbolo.value + '	Remover
';
      simbolo.value = '';
      simbolo.focus();
    }
  });

  document.getElementById('configurar_estados').addEventListener('click', function () {
    document.getElementById('configuracao_alfabeto').classList.add('hidden');

    var cabecalho = '';
    for (var i = 0; i < simbolos.length; i++) {
      cabecalho += '' + simbolos[i] + '	';
    }

    document.getElementById('automato').tHead.rows[0].innerHTML += cabecalho;

    document.getElementById('configuracao_estados').classList.remove('hidden');
  });

  document.getElementById('alfabeto').addEventListener('click', function (e) {
    var target = e.target;
    if (target.classList.contains('alfabeto-remover')) {
      var valor = target.dataset.val;
      if (valor) {
        for (var i = 0; i < simbolos.length; i++) {
          if (valor === simbolos[i]) {
            simbolos.splice(i, 1);
            break;
          }
        }
        if (target.parentNode && target.parentNode.parentNode) {
          target.parentNode.parentNode.remove();
        }
      }
    }
  });

  var gerarOptionEstado = function (indice) {
    var option = document.createElement('option');
    option.text = '{q' + indice + '}';
    option.value = 'q' + indice;

    return option;
  };

  var popularNovosEstados = function () {
    var selects = document.querySelectorAll('#automato select');
    if (selects && selects.length > 0) {
      var i = 0,
        qtd = selects.length;

      for (; i < qtd; i++) {
        for (var x = 0; x < estados.length; x++) {
          if (document.querySelector('#' + selects[i].id + ' option[value=q' + estados[x].id + ']') === null) {
            selects[i].add(gerarOptionEstado(estados[x].id));
          }
        }
      }
    }
  };

  var novoEstado = function (callback) {
    var qtdEstados = estados.length;
    var linha = '
{q' + qtdEstados + '}Remover	';
    for (var i = 0; i < simbolos.length; i++) {
      linha += '<select id="q' + qtdEstados + '_' + simbolos[i] + '"><option value="">NULL</option><option value="0">Novo estado</option></select>	';
    }
    linha += '
';

    var el = document.createElement('tr');

    el.innerHTML = linha;

    document.getElementById('automato').appendChild(el);

    estados.push({
      id: qtdEstados
    });

    popularNovosEstados();

    if (callback && typeof callback === 'function') {
      callback(qtdEstados);
    }
  };

  /* Configuracao dos estados */
  document.getElementById('novo_estado').addEventListener('click', function () {
    novoEstado();
  });

  var incluirTransicoesEstados = function (target, estado) {
    // Cria vinculo de estado
    var id = target.id.replace('q', ''),
      estadoSimbolo = id.split('_');

    if (estadoSimbolo.length === 2) {
      if (estados[estadoSimbolo[0]]) {
        // Os ids são separados por estado_simbolo, por isso:
        // estadoSimbolo[0] = estado
        // estadoSimbolo[1] = simbolo

        //Vincula no estado com id estadoSimbolo[0], no simbolo estadoSimbolo[1], o estado selecionado
        estados[estadoSimbolo[0]][estadoSimbolo[1]] = estado;
      }
    }
  };

  document.getElementById('automato').addEventListener('change', function (e) {
    var target = e.target;
    if (target && target.tagName === 'SELECT') {
      if (target.value === '0') {
        // Criar novo estado
        novoEstado(function (estado) {
          document.getElementById(target.id).value = 'q' + estado;
          incluirTransicoesEstados(target, 'q' + estado);
        });
      }
      else if (target.value !== '') {
        // Cria transicao em estados
        incluirTransicoesEstados(target, target.value);
      }
      else {
        // Remove completamente transicao
        var id = target.id.replace('q', ''),
          estadoSimbolo = id.split('_');

        delete estados[estadoSimbolo[0]][estadoSimbolo[1]];
      }
    }
  });

  document.getElementById('automato').addEventListener('click', function (e) {
    var target = e.target;

    if (target.tagName === 'A') {
      var estado = target.dataset.estado;

      // Remover todos os vinculos a este estado
      for (var i = 0; i < estados.length; i++) {
        if (i != estado) {
          for (attr in estados[i]) {
            if (!isNaN(attr) && estados[i][attr] === 'q' + estado) {
              // Deixa os selects com valor NULL
              document.getElementById('q' + i + '_' + attr).value = '';

              // Deleta propriedade de transicao
              delete estados[i][attr];
            }
          }
        }
      }

      // Deleta linha com estado
      if (target.parentNode && target.parentNode.parentNode) {
        target.parentNode.parentNode.remove();
      }

      // Remove estado do array resposavel por armazenar todos os existentes
      estados.slice(estado, 1);
    }

    e.preventDefault();
    return false;
  });

  document.getElementById('salvar_estados').addEventListener('click', function () {
    var linhas = document.getElementById('automato').tBodies[0].rows;
  });

  // Execucoes iniciais
  document.getElementById('simbolo').focus();

}(document));
<|endoftext|>