<commit_before>import ThreadFactory from './ThreadFactory';
import RingaObject from './RingaObject';
import HashArray from 'hasharray';
import RingaEvent from './RingaEvent';
import ModelWatcher from './ModelWatcher';
import {inspectorDispatch} from './debug/InspectorController';
import {ringaGlobalBus} from './Bus';
import snakeCase from 'snake-case';
import {buildArgumentsFromRingaEvent} from './util/executors';
import {getArgNames} from './util/function';

/**
 * Controller is the hub that links event types through a bus (e.g. a DOM node) to an async executor tree.
 */
class Controller extends RingaObject {
  //-----------------------------------
  // Constructor
  //-----------------------------------
  /**
   * Constructs a new controller.
   *
   * @param id The id of this controller, primarily used for internal hashes and debugging. Must be unique.
   * @param bus The native browser DOMNode element (not a React Node) to attach event listeners to OR a custom event bus.
   * @param options See documentation on Controller options. Defaults are provided, so this is optional.
   */
  constructor(name, bus, options) {
    super(name);

    // We want to error if bus is defined but is not actually a bus.
    // If Bus is an object, though, and options is undefined, this means
    // the user is probably doing this:
    // new Controller('id', {options: true});
    // And skipping providing a bus, and that is okay.
    if (bus && !(typeof bus.addEventListener === 'function')) {
      if (__DEV__ && options) {
        throw Error('Controller(): invalid bus or DOM node passed into constructor!');
      }

      options = bus;
    }

    this.bus = bus;
    this.modelWatcher = undefined;

    this.options = options || {};
    this.options.timeout = this.options.timeout || 5000;
    this.options.throwKillsThread = this.options.throwKillsThread === undefined ? true : this.options.throwKillsThread;
    this.options.consoleLogFails = this.options.consoleLogFails === undefined ? true : this.options.consoleLogFails;
    this.options.injections = this.options.injections || {};
    this.options.warnOnDetailOverwrite = this.options.warnOnDetailOverwrite === undefined ? false : this.options.warnOnDetailOverwrite;

    this.threads = new HashArray('id');

    this.eventTypeToThreadFactory = new Map();
    this.eventTypeToWatchers = new Map();
    this.watcherToArgNames = new Map();

    this._eventHandler = this._eventHandler.bind(this);
  }

  //-----------------------------------
  // Properties
  //-----------------------------------
  get injections() {
    return this.options.injections;
  }

  set bus(value) {
    // If someone tries to reset the same bus, just ignore it because it would be dumb to detach all the listeners
    // and reattach them.
    if (this._bus === value) {
      return;
    }

    // If we are switching busses, we need to remove the listeners from the old bus
    if (this._bus) {
      this.detachAllListeners();
    }

    this._bus = value;

    // If the developer has called to attach previous listeners we need to make sure all of those
    // get attached now to our new bus.
    if (this._bus) {
      this.attachAllListeners();

      // We want to wait here. The reason is that if someone dispatches an event in the busMounted section
      // we want to make sure that all other Controllers within the same context in the dom have also
      // had time to mount before secondary events might be dispatched.
      setTimeout(() => {
        this.busMounted(this.bus);
      }, 0);
    }
  }

  get bus() {
    return this._bus;
  }

  //-----------------------------------
  // Methods
  //-----------------------------------
  /**
   * Special method that attaches a model to this Controller through a ModelWatcher.
   *
   * At the time this was built, it was only functional to support injection for the react-ringa project and dependency
   * injection.
   *
   * If no internal ModelWatcher exists, the Controller builds one and then adds the model to that watcher.
   *
   * This method also adds the model as an injection by the models id to be available to all executors.
   *
   * @param model An instance that must be an extension of the Ringa.Model class.
   * @parma injectionId A custom name to use for the injection id if you don't want to use the default one on the Model.
   */
  addModel(model, injectionId = undefined) {
    if (!this.modelWatcher) {
      this.modelWatcher = new ModelWatcher(this.id + '_ModelWatcher');
    }

    this.modelWatcher.addModel(model, injectionId);

    this.injections[injectionId || model.id] = model;
    this.injections[model.name] = model;
  }

  /**
   * Remove a model that was previously added.
   *
   * @param model The model to be removed.
   */
  removeModel(model) {
    this.modelWatcher.removeModel(model, injectionId);

    delete this.injections[injectionId || model.id];
  }

  /**
   * Called when there is safely an event bus to attach events to. This is where you could redispatch certain initialization
   * events.
   */
  busMounted(bus) {
    // To be overridden
  }

  /**
   * Takes in a series of event types and converts them to static properties on the parent class.
   *
   * For example:
   *
   *   controller = new MyController();
   *   controller.addEventTypeStatics(['my event', 'otherEvent', 'YAY']);
   *
   *   MyController.MY_EVENT === 'my event';
   *   MyController.OTHER_EVENT = 'otherEvent'
   *   MyController.YAY = 'YAY;
   *
   * @param types An array of event types.
   */
  addEventTypeStatics(types) {
    types.forEach(type => {
      let TYPE_SNAKE_CASE = snakeCase(type).toUpperCase();

      if (this.constructor[TYPE_SNAKE_CASE]) {
        return;
      }

      this.constructor[TYPE_SNAKE_CASE] = this[TYPE_SNAKE_CASE] = type;
    });
  }

  /**
   * Returns the ThreadFactory associated with the provided eventType.
   *
   * @param eventType The event type - a String.
   * @returns {ThreadFactory} The ThreadFactory associated with the eventType.
   */
  getThreadFactoryFor(eventType) {
    return this.eventTypeToThreadFactory[eventType];
  }

  /**
   * Listens for the event type on the attached event bus (or DOM node) and runs the provided executor or async tree.
   *
   * See online documentation for more details.
   *
   * @param eventType The event type, expected to be a String.
   * @param executor A single executor (function, event type, Promise, Ringa Command) or an array of these.
   * @param useCapture True if you want to use capture to listen to the event.
   * @returns {*} The ThreadFactory instance that will be run when the event is received.
   */
  addListener(eventType, executor, useCapture) {
    let threadFactory;

    if (__DEV__ && arguments[2] && typeof arguments[2] !== 'boolean') {
      throw Error(`Controller::addListener(): you provided a third argument which was not a boolean for useCapture (was ${useCapture}). This does nothing and probably means you forgot to wrap your second executor in an array.`);
    }

    if (executor && !(executor instanceof ThreadFactory) && !(executor instanceof Array)) {
      executor = [executor];
    }

    if (!executor || executor instanceof Array) {
      threadFactory = new ThreadFactory(this.id + '_' + eventType + '_ThreadFactory', executor);
    } else if (executor instanceof ThreadFactory) {
      threadFactory = executor;
    } else if (__DEV__) {
      throw Error('Controller::addListener(): the provided executor is not valid! Did you forget to wrap in []?');
    }

    if (__DEV__ && !threadFactory || !(threadFactory instanceof ThreadFactory)) {
      throw Error('Controller::addListener(): threadFactory not an instance of ThreadFactory');
    }

    if (__DEV__ && threadFactory.controller) {
      throw Error('Controller::addListener(): threadFactory cannot have two parent controllers!');
    }

    if (__DEV__ && this.eventTypeToThreadFactory[eventType]) {
      throw Error('Controller.addListener(): the event \'' + eventType + '\' has already been added! Use getThreadFactoryFor() to make modifications.');
    }

    this.addEventTypeStatics([eventType]);

    threadFactory.controller = this;

    this.eventTypeToThreadFactory[eventType] = threadFactory;

    this._attachListenerToBus(eventType, useCapture);

    return threadFactory;
  }

  _attachListenerToBus(eventType, useCapture) {
    if (!this.bus) {
      return; // We will be attached once the bus comes in to the station.
    }

    if (typeof eventType === 'string') {
      this.bus.addEventListener(eventType, this._eventHandler, useCapture);
    } else {
      let _eventType = undefined;

      if (eventType.hasOwnProperty('toString')) {
        _eventType = eventType.toString();
      }

      if (_eventType) {
        this.bus.addEventListener(_eventType, this._eventHandler.bind(this));
      } else {
        throw Error('Controller::addListener(): provided eventType is invalid.', eventType);
      }
    }
  }

  removeAllListeners() {
    for (let eventType in this.eventTypeToThreadFactory) {
      this.removeListener(eventType);
    }
  }

  detachAllListeners() {
    if (this.bus) {
      for (let eventType in this.eventTypeToThreadFactory) {
        this.bus.removeEventListener(eventType, this._eventHandler);
      }
    }
  }

  attachAllListeners() {
    for (let eventType in this.eventTypeToThreadFactory) {
      this._attachListenerToBus(eventType);
    }
  }

  /**
   * Safely removes an event listener for a particular event type.
   *
   * @param eventType The event type to remove.
   * @returns {*} The ThreadFactory that the event type used to trigger.
   */
  removeListener(eventType) {
    let threadFactory = this.eventTypeToThreadFactory[eventType];

    if (threadFactory) {
      delete this.eventTypeToThreadFactory[eventType];

      this.bus.removeEventListener(eventType, this._eventHandler);

      return threadFactory;
    }

    if (__DEV__) {
      throw Error('Controller:removeListener(): could not find a listener for \'' + eventType + '\'', this);
    }
  }

  /**
   * Returns true if the event bus (or DOM node) that this controller is attached to has a listener for the provided
   * event type.
   *
   * @param eventType The event type
   * @returns {boolean} True if the bus says it has a listener for the provided event type.
   */
  isListening(eventType) {
    if (this.bus && this.bus.hasListener) {
      return this.bus.hasListener(eventType);
    } else if (this.bus) {
      // Kinda risky, but there is no built-in way on DOM node to see if an event has been attached
      // without working some magic by overriding addEventListener or using jQuery, which we shall
      // not do because we are not barbarians.
      return true;
    }

    return false;
  }

  /**
   * Returns true if addListener has been called for the provided eventType. Note this will return true even if the bus
   * is not set.
   *
   * @param eventType The event type
   * @returns {boolean} True if addListener has been called with the provided event.
   */
  hasListener(eventType) {
    return this.getThreadFactoryFor(eventType) !== undefined;
  }

  /**
   * Attaches a RingaEvent to a ThreadFactory and runs the ThreadFactory.
   *
   * @param ringaEvent A RingaEvent instance, properly initialized.
   * @param threadFactory A ThreadFactory instance, properly initialized.
   * @returns {*}
   */
  invoke(ringaEvent, threadFactory) {
    let thread = threadFactory.build(ringaEvent);

    this.threads.add(thread);

    ringaEvent._threads.push(thread);

    ringaEvent._dispatchEvent(RingaEvent.PREHOOK);

    if (__DEV__ && !this.__blockRingaEvents) {
      inspectorDispatch('ringaThreadStart', {
        thread
      });
    }

    // TODO PREHOOK should allow the handler to cancel running of the thread.
    thread.run(ringaEvent, this.threadDoneHandler.bind(this), this.threadFailHandler.bind(this));

    return thread;
  }

  //-----------------------------------
  // Events
  //-----------------------------------
  _eventHandler(event) {
    if (event instanceof RingaEvent) {
      return this.__eventHandler(event);
    }

    let ringaEvent;

    // This event might be a something like 'click' which does not have
    // an attached ringaEvent yet!
    if (!event.detail.ringaEvent){
      ringaEvent = new RingaEvent(event.type, typeof event.detail === 'object' ? event.detail : {}, event.bubbles, event.cancellable, event);

      if (typeof event.detail === 'object') {
        event.detail.ringaEvent = ringaEvent;
      }
    } else {
      ringaEvent = event.detail.ringaEvent;
    }

    return this.__eventHandler(ringaEvent);
  }

  __eventHandler(ringaEvent) {
    let threadFactory = this.eventTypeToThreadFactory[ringaEvent.type];

    ringaEvent._caught(this);

    if (__DEV__ && !threadFactory) {
      throw Error('Controller::__eventHandler(): caught an event but there is no associated ThreadFactory! Fatal error.');
    }

    let abort;
    try {
      abort = this.preInvokeHandler(ringaEvent);
    } catch (error) {
      // At this point we don't have a thread yet, so this is all kinds of whack.
      if (this.options.consoleLogFails) {
        console.error(error);
      }

      ringaEvent._fail(this, error, true);
      ringaEvent.destroy(true);
      return;
    }

    if (abort === true) {
      return;
    }

    let thread;

    try {
      thread = this.invoke(ringaEvent, threadFactory);

      this.postInvokeHandler(ringaEvent, thread);
    } catch (error) {
      this.threadFailHandler(thread, error);
    }
  }

  preInvokeHandler(ringaEvent) {
    // Can be extended by a subclass
    return false;
  }

  postInvokeHandler(ringaEvent, thread) {
    // Can be extended by a subclass
  }

  _threadFinalized(thread) {
    thread.destroy(true);

    this.threads.remove(thread);

    if (__DEV__ && !this.__blockRingaEvents) {
      inspectorDispatch('ringaThreadKill', {
        thread
      });
    }
  }

  threadDoneHandler(thread) {
    if (__DEV__ && !this.threads.has(thread.id)) {
      throw Error(`Controller::threadDoneHandler(): could not find thread with id ${thread.id}`);
    }

    this._threadFinalized(thread);

    this.notify(thread.ringaEvent);

    thread.ringaEvent._done(this)
    thread.ringaEvent.destroy(true);
  }

  threadFailHandler(thread, error, kill) {
    if (this.options.consoleLogFails) {
      console.error(error, `In thread ${thread ? thread.toString() : ''}`);
    }

    thread.ringaEvent._fail(this, error, kill);

    if (kill) {
      thread.ringaEvent.destroy();

      if (this.threads.has(thread.id)) {
        this._threadFinalized(thread);
      } else if (__DEV__) {
        throw Error(`Controller:threadFailHandler(): the CommandThread with the id ${thread.id} was not found.`)
      }
    }
  }

  dispatch(eventType, details, requireCatch = true) {
    if (!this.bus) {
      throw new Error(`Controller::dispatch(): bus has not yet been set so you cannot dispatch ('${eventType}')! Wait for Controller::busMounted().`);
    }

    return new RingaEvent(eventType, details, true, true, undefined, requireCatch).dispatch(this.bus);
  }

  toString() {
    return this.id;
  }

  notify(ringaEvent, eventType) {
    eventType = eventType || ringaEvent.type;

    let watchers = this.eventTypeToWatchers[eventType];
    if (watchers && watchers.length) {
      let executor = {
        controller: this,
        toString: () => {
          return 'Controller::watch() for ' + eventType;
        }
      };

      watchers.forEach(watcher => {
        let argNames = this.watcherToArgNames[watcher];
        let args = buildArgumentsFromRingaEvent(executor, argNames, ringaEvent);

        watcher.apply(undefined, args);
      });
    }
  }

  watch(eventTypes, injectableCallback) {
    if (__DEV__ && !(eventTypes instanceof Array)) {
      throw new Error('Controller::watch(): eventTypes is not an Array!');
    }

    eventTypes.forEach(eventType => {
      this.eventTypeToWatchers[eventType] = this.eventTypeToWatchers[eventType] || [];
      this.eventTypeToWatchers[eventType].push(injectableCallback);
      this.watcherToArgNames[injectableCallback] = getArgNames(injectableCallback);
    });
  }

  unwatch(eventTypes, injectableCallback) {
    if (__DEV__ && !(eventTypes instanceof Array)) {
      throw new Error('Controller::watch(): eventTypes is not an Array!');
    }

    let ix;

    eventTypes.forEach(eventType => {
      if ((ix = this.eventTypeToWatchers[eventType].indexOf(injectableCallback)) !== -1) {
        this.eventTypeToWatchers[eventType].splice(ix, 1);
      }
    });
  }
}

export default Controller;<commit_msg>Fix small options null check<commit_after>import ThreadFactory from './ThreadFactory';
import RingaObject from './RingaObject';
import HashArray from 'hasharray';
import RingaEvent from './RingaEvent';
import ModelWatcher from './ModelWatcher';
import {inspectorDispatch} from './debug/InspectorController';
import {ringaGlobalBus} from './Bus';
import snakeCase from 'snake-case';
import {buildArgumentsFromRingaEvent} from './util/executors';
import {getArgNames} from './util/function';

/**
 * Controller is the hub that links event types through a bus (e.g. a DOM node) to an async executor tree.
 */
class Controller extends RingaObject {
  //-----------------------------------
  // Constructor
  //-----------------------------------
  /**
   * Constructs a new controller.
   *
   * @param id The id of this controller, primarily used for internal hashes and debugging. Must be unique.
   * @param bus The native browser DOMNode element (not a React Node) to attach event listeners to OR a custom event bus.
   * @param options See documentation on Controller options. Defaults are provided, so this is optional.
   */
  constructor(name, bus, options) {
    super(name);

    // We want to error if bus is defined but is not actually a bus.
    // If Bus is an object, though, and options is undefined, this means
    // the user is probably doing this:
    // new Controller('id', {options: true});
    // And skipping providing a bus, and that is okay.
    if (bus && !(typeof bus.addEventListener === 'function')) {
      if (__DEV__ && options) {
        throw Error('Controller(): invalid bus or DOM node passed into constructor!');
      }

      options = bus;
    }

    this.bus = bus;
    this.modelWatcher = undefined;

    this.options = options || {};
    this.options.timeout = this.options.timeout || 5000;
    this.options.throwKillsThread = this.options.throwKillsThread === undefined ? true : this.options.throwKillsThread;
    this.options.consoleLogFails = this.options.consoleLogFails === undefined ? true : this.options.consoleLogFails;
    this.options.injections = this.options.injections || {};
    this.options.warnOnDetailOverwrite = this.options.warnOnDetailOverwrite === undefined ? false : this.options.warnOnDetailOverwrite;

    this.threads = new HashArray('id');

    this.eventTypeToThreadFactory = new Map();
    this.eventTypeToWatchers = new Map();
    this.watcherToArgNames = new Map();

    this._eventHandler = this._eventHandler.bind(this);
  }

  //-----------------------------------
  // Properties
  //-----------------------------------
  get injections() {
    return this.options.injections;
  }

  set bus(value) {
    // If someone tries to reset the same bus, just ignore it because it would be dumb to detach all the listeners
    // and reattach them.
    if (this._bus === value) {
      return;
    }

    // If we are switching busses, we need to remove the listeners from the old bus
    if (this._bus) {
      this.detachAllListeners();
    }

    this._bus = value;

    // If the developer has called to attach previous listeners we need to make sure all of those
    // get attached now to our new bus.
    if (this._bus) {
      this.attachAllListeners();

      // We want to wait here. The reason is that if someone dispatches an event in the busMounted section
      // we want to make sure that all other Controllers within the same context in the dom have also
      // had time to mount before secondary events might be dispatched.
      setTimeout(() => {
        this.busMounted(this.bus);
      }, 0);
    }
  }

  get bus() {
    return this._bus;
  }

  //-----------------------------------
  // Methods
  //-----------------------------------
  /**
   * Special method that attaches a model to this Controller through a ModelWatcher.
   *
   * At the time this was built, it was only functional to support injection for the react-ringa project and dependency
   * injection.
   *
   * If no internal ModelWatcher exists, the Controller builds one and then adds the model to that watcher.
   *
   * This method also adds the model as an injection by the models id to be available to all executors.
   *
   * @param model An instance that must be an extension of the Ringa.Model class.
   * @parma injectionId A custom name to use for the injection id if you don't want to use the default one on the Model.
   */
  addModel(model, injectionId = undefined) {
    if (!this.modelWatcher) {
      this.modelWatcher = new ModelWatcher(this.id + '_ModelWatcher');
    }

    this.modelWatcher.addModel(model, injectionId);

    this.injections[injectionId || model.id] = model;
    this.injections[model.name] = model;
  }

  /**
   * Remove a model that was previously added.
   *
   * @param model The model to be removed.
   */
  removeModel(model) {
    this.modelWatcher.removeModel(model, injectionId);

    delete this.injections[injectionId || model.id];
  }

  /**
   * Called when there is safely an event bus to attach events to. This is where you could redispatch certain initialization
   * events.
   */
  busMounted(bus) {
    // To be overridden
  }

  /**
   * Takes in a series of event types and converts them to static properties on the parent class.
   *
   * For example:
   *
   *   controller = new MyController();
   *   controller.addEventTypeStatics(['my event', 'otherEvent', 'YAY']);
   *
   *   MyController.MY_EVENT === 'my event';
   *   MyController.OTHER_EVENT = 'otherEvent'
   *   MyController.YAY = 'YAY;
   *
   * @param types An array of event types.
   */
  addEventTypeStatics(types) {
    types.forEach(type => {
      let TYPE_SNAKE_CASE = snakeCase(type).toUpperCase();

      if (this.constructor[TYPE_SNAKE_CASE]) {
        return;
      }

      this.constructor[TYPE_SNAKE_CASE] = this[TYPE_SNAKE_CASE] = type;
    });
  }

  /**
   * Returns the ThreadFactory associated with the provided eventType.
   *
   * @param eventType The event type - a String.
   * @returns {ThreadFactory} The ThreadFactory associated with the eventType.
   */
  getThreadFactoryFor(eventType) {
    return this.eventTypeToThreadFactory[eventType];
  }

  /**
   * Listens for the event type on the attached event bus (or DOM node) and runs the provided executor or async tree.
   *
   * See online documentation for more details.
   *
   * @param eventType The event type, expected to be a String.
   * @param executor A single executor (function, event type, Promise, Ringa Command) or an array of these.
   * @param useCapture True if you want to use capture to listen to the event.
   * @returns {*} The ThreadFactory instance that will be run when the event is received.
   */
  addListener(eventType, executor, useCapture) {
    let threadFactory;

    if (__DEV__ && arguments[2] && typeof arguments[2] !== 'boolean') {
      throw Error(`Controller::addListener(): you provided a third argument which was not a boolean for useCapture (was ${useCapture}). This does nothing and probably means you forgot to wrap your second executor in an array.`);
    }

    if (executor && !(executor instanceof ThreadFactory) && !(executor instanceof Array)) {
      executor = [executor];
    }

    if (!executor || executor instanceof Array) {
      threadFactory = new ThreadFactory(this.id + '_' + eventType + '_ThreadFactory', executor);
    } else if (executor instanceof ThreadFactory) {
      threadFactory = executor;
    } else if (__DEV__) {
      throw Error('Controller::addListener(): the provided executor is not valid! Did you forget to wrap in []?');
    }

    if (__DEV__ && !threadFactory || !(threadFactory instanceof ThreadFactory)) {
      throw Error('Controller::addListener(): threadFactory not an instance of ThreadFactory');
    }

    if (__DEV__ && threadFactory.controller) {
      throw Error('Controller::addListener(): threadFactory cannot have two parent controllers!');
    }

    if (__DEV__ && this.eventTypeToThreadFactory[eventType]) {
      throw Error('Controller.addListener(): the event \'' + eventType + '\' has already been added! Use getThreadFactoryFor() to make modifications.');
    }

    this.addEventTypeStatics([eventType]);

    threadFactory.controller = this;

    this.eventTypeToThreadFactory[eventType] = threadFactory;

    this._attachListenerToBus(eventType, useCapture);

    return threadFactory;
  }

  _attachListenerToBus(eventType, useCapture) {
    if (!this.bus) {
      return; // We will be attached once the bus comes in to the station.
    }

    if (typeof eventType === 'string') {
      this.bus.addEventListener(eventType, this._eventHandler, useCapture);
    } else {
      let _eventType = undefined;

      if (eventType.hasOwnProperty('toString')) {
        _eventType = eventType.toString();
      }

      if (_eventType) {
        this.bus.addEventListener(_eventType, this._eventHandler.bind(this));
      } else {
        throw Error('Controller::addListener(): provided eventType is invalid.', eventType);
      }
    }
  }

  removeAllListeners() {
    for (let eventType in this.eventTypeToThreadFactory) {
      this.removeListener(eventType);
    }
  }

  detachAllListeners() {
    if (this.bus) {
      for (let eventType in this.eventTypeToThreadFactory) {
        this.bus.removeEventListener(eventType, this._eventHandler);
      }
    }
  }

  attachAllListeners() {
    for (let eventType in this.eventTypeToThreadFactory) {
      this._attachListenerToBus(eventType);
    }
  }

  /**
   * Safely removes an event listener for a particular event type.
   *
   * @param eventType The event type to remove.
   * @returns {*} The ThreadFactory that the event type used to trigger.
   */
  removeListener(eventType) {
    let threadFactory = this.eventTypeToThreadFactory[eventType];

    if (threadFactory) {
      delete this.eventTypeToThreadFactory[eventType];

      this.bus.removeEventListener(eventType, this._eventHandler);

      return threadFactory;
    }

    if (__DEV__) {
      throw Error('Controller:removeListener(): could not find a listener for \'' + eventType + '\'', this);
    }
  }

  /**
   * Returns true if the event bus (or DOM node) that this controller is attached to has a listener for the provided
   * event type.
   *
   * @param eventType The event type
   * @returns {boolean} True if the bus says it has a listener for the provided event type.
   */
  isListening(eventType) {
    if (this.bus && this.bus.hasListener) {
      return this.bus.hasListener(eventType);
    } else if (this.bus) {
      // Kinda risky, but there is no built-in way on DOM node to see if an event has been attached
      // without working some magic by overriding addEventListener or using jQuery, which we shall
      // not do because we are not barbarians.
      return true;
    }

    return false;
  }

  /**
   * Returns true if addListener has been called for the provided eventType. Note this will return true even if the bus
   * is not set.
   *
   * @param eventType The event type
   * @returns {boolean} True if addListener has been called with the provided event.
   */
  hasListener(eventType) {
    return this.getThreadFactoryFor(eventType) !== undefined;
  }

  /**
   * Attaches a RingaEvent to a ThreadFactory and runs the ThreadFactory.
   *
   * @param ringaEvent A RingaEvent instance, properly initialized.
   * @param threadFactory A ThreadFactory instance, properly initialized.
   * @returns {*}
   */
  invoke(ringaEvent, threadFactory) {
    let thread = threadFactory.build(ringaEvent);

    this.threads.add(thread);

    ringaEvent._threads.push(thread);

    ringaEvent._dispatchEvent(RingaEvent.PREHOOK);

    if (__DEV__ && !this.__blockRingaEvents) {
      inspectorDispatch('ringaThreadStart', {
        thread
      });
    }

    // TODO PREHOOK should allow the handler to cancel running of the thread.
    thread.run(ringaEvent, this.threadDoneHandler.bind(this), this.threadFailHandler.bind(this));

    return thread;
  }

  //-----------------------------------
  // Events
  //-----------------------------------
  _eventHandler(event) {
    if (event instanceof RingaEvent) {
      return this.__eventHandler(event);
    }

    let ringaEvent;

    // This event might be a something like 'click' which does not have
    // an attached ringaEvent yet!
    if (!event.detail.ringaEvent){
      ringaEvent = new RingaEvent(event.type, typeof event.detail === 'object' ? event.detail : {}, event.bubbles, event.cancellable, event);

      if (typeof event.detail === 'object') {
        event.detail.ringaEvent = ringaEvent;
      }
    } else {
      ringaEvent = event.detail.ringaEvent;
    }

    return this.__eventHandler(ringaEvent);
  }

  __eventHandler(ringaEvent) {
    let threadFactory = this.eventTypeToThreadFactory[ringaEvent.type];

    ringaEvent._caught(this);

    if (__DEV__ && !threadFactory) {
      throw Error('Controller::__eventHandler(): caught an event but there is no associated ThreadFactory! Fatal error.');
    }

    let abort;
    try {
      abort = this.preInvokeHandler(ringaEvent);
    } catch (error) {
      // At this point we don't have a thread yet, so this is all kinds of whack.
      if (this.options.consoleLogFails) {
        console.error(error);
      }

      ringaEvent._fail(this, error, true);
      ringaEvent.destroy(true);
      return;
    }

    if (abort === true) {
      return;
    }

    let thread;

    try {
      thread = this.invoke(ringaEvent, threadFactory);

      this.postInvokeHandler(ringaEvent, thread);
    } catch (error) {
      this.threadFailHandler(thread, error);
    }
  }

  preInvokeHandler(ringaEvent) {
    // Can be extended by a subclass
    return false;
  }

  postInvokeHandler(ringaEvent, thread) {
    // Can be extended by a subclass
  }

  _threadFinalized(thread) {
    thread.destroy(true);

    this.threads.remove(thread);

    if (__DEV__ && !this.__blockRingaEvents) {
      inspectorDispatch('ringaThreadKill', {
        thread
      });
    }
  }

  threadDoneHandler(thread) {
    if (__DEV__ && !this.threads.has(thread.id)) {
      throw Error(`Controller::threadDoneHandler(): could not find thread with id ${thread.id}`);
    }

    this._threadFinalized(thread);

    this.notify(thread.ringaEvent);

    thread.ringaEvent._done(this)
    thread.ringaEvent.destroy(true);
  }

  threadFailHandler(thread, error, kill) {
    if (!this.options || this.options.consoleLogFails) {
      console.error(error, `In thread ${thread ? thread.toString() : ''}`);
    }

    thread.ringaEvent._fail(this, error, kill);

    if (kill) {
      thread.ringaEvent.destroy();

      if (this.threads.has(thread.id)) {
        this._threadFinalized(thread);
      } else if (__DEV__) {
        throw Error(`Controller:threadFailHandler(): the CommandThread with the id ${thread.id} was not found.`)
      }
    }
  }

  dispatch(eventType, details, requireCatch = true) {
    if (!this.bus) {
      throw new Error(`Controller::dispatch(): bus has not yet been set so you cannot dispatch ('${eventType}')! Wait for Controller::busMounted().`);
    }

    return new RingaEvent(eventType, details, true, true, undefined, requireCatch).dispatch(this.bus);
  }

  toString() {
    return this.id;
  }

  notify(ringaEvent, eventType) {
    eventType = eventType || ringaEvent.type;

    let watchers = this.eventTypeToWatchers[eventType];
    if (watchers && watchers.length) {
      let executor = {
        controller: this,
        toString: () => {
          return 'Controller::watch() for ' + eventType;
        }
      };

      watchers.forEach(watcher => {
        let argNames = this.watcherToArgNames[watcher];
        let args = buildArgumentsFromRingaEvent(executor, argNames, ringaEvent);

        watcher.apply(undefined, args);
      });
    }
  }

  watch(eventTypes, injectableCallback) {
    if (__DEV__ && !(eventTypes instanceof Array)) {
      throw new Error('Controller::watch(): eventTypes is not an Array!');
    }

    eventTypes.forEach(eventType => {
      this.eventTypeToWatchers[eventType] = this.eventTypeToWatchers[eventType] || [];
      this.eventTypeToWatchers[eventType].push(injectableCallback);
      this.watcherToArgNames[injectableCallback] = getArgNames(injectableCallback);
    });
  }

  unwatch(eventTypes, injectableCallback) {
    if (__DEV__ && !(eventTypes instanceof Array)) {
      throw new Error('Controller::watch(): eventTypes is not an Array!');
    }

    let ix;

    eventTypes.forEach(eventType => {
      if ((ix = this.eventTypeToWatchers[eventType].indexOf(injectableCallback)) !== -1) {
        this.eventTypeToWatchers[eventType].splice(ix, 1);
      }
    });
  }
}

export default Controller;<|endoftext|>


<commit_before>var _ = require('underscore');
var winston = require('winston');
var mongoose = require('mongoose');
var Slack = require('slack-client');
var nconf = require('nconf');
var winston = require('winston');

function AddonSlack (app, server, io, passport){
    var self = this;
	this.name = 'Slack';
	this.description = 'Slack integration addon';
    this.listDependencies = ['slack-robot'];
    var Testcase = mongoose.model('Testcase')
    var Result = mongoose.model('Result');

    var allowAllUsers = true;
    var allowedUsers = [/jussi/, /wirkus/];

    var allowAllChannels = false;
    var allowedChannels = [/jussiva/, /wirkus/, /thread/, /6lowpan/, /clitest/];

  if( !nconf.get('slack')) {
    winston.error('slack configuration missing!');
    return;
  }

	this.register = function(){
    var slackOpts = {
      token: nconf.get('slack').token,
      autoReconnect: true,
      //autoMark: false
    }

    var allowedChannel = function(channel) {
        if( allowAllChannels ) return true;
        if( !channel || !channel.name ) return false;
        winston.log('SLACK: check if channel is valid: '+channel.name);
        return allowedChannels.some( function(ch) {
            if( channel.name.toLowerCase().match(ch) ){
                return true;
            }
        });
    }
    var allowedUser = function(user) {
        if( allowAllUsers ) return true;
        if( !user || !user.real_name ) return false;
        winston.log('SLACK: check if user is valid: '+user.real_name);
        return allowedUsers.some( function(usr) {
            if( user.real_name.toLowerCase().match(usr) ){
                return true;
            }
        });
    }

    var allowMessage = function(channel, user) {
        if( !allowedChannel(channel) ) return false;
        if( !allowedUser(user) ) return false;
        return true;
    }

    var help = function(message, channel, user, m) {
        channel.send("```Test Management Service\n:list testcases\tList all available cases\n:list results\tList 10 latest results\n:run [testcase]\t:run individual case\n:help\tThis help```");
    }
    var list = function(message, channel, user, m) {
        q = {f: 'tcid', t: 'distinct'};
        if( m=message.text.match(/^\:list\Wtestcases\W(.*)/)  ) {
            try {
                _.extend(q, JSON.parse(m[1]));
            } catch(e) {
                console.log(e);
                channel.send('Invalid format');
                return;
            }
        }
        Testcase.query(q, function(error, list){
          channel.send('```'+JSON.stringify(list, ' ', '\n')+'```');
        })
    }
    var results = function(message, channel, user, m) {
        q = {l: 10, s: '{"cre.time": -1}', q: {}};
        if( m=message.text.match(/thread/) || channel.name.match(/thread/) ) {
            if(!q.q['$or']) q.q['$or'] = [];
            q.q['$or'].push({'exec.sut.cut': 'thread'});
        }
        else if( m=message.text.match(/6lowpan/) || channel.name.match(/6lowpan/)  ) {
            if(!q.q['$or']) q.q['$or'] = [];
            q.q['$or'].push({'exec.sut.cut': '6LoWPAN'});
        }
        else if( m=message.text.match(/^\:list\Wresults\W(.*)/)  ) {
            try {
                _.extend(q, JSON.parse(m[1]));
            } catch(e) {
                console.log(e);
                channel.send('Invalid format');
                return;
            }
        }
        q.q = JSON.stringify(q.q);
        console.log('SLACK: result query: '+JSON.stringify(q));
        Result.query(q, function(error, list){
            if( error ) {
                channel.send('Error while query');
                return;
            }
            var str = 'TC\t\tResult\n';
            list.forEach( function (result){
                if( result.exec && result.exec.verdict ) {
                    str += result.tcid+'\t'+result.exec.verdict+'\n';
                }
            });
            channel.send('```'+str+'```');
        })
    }
    var run = function(message, channel, user, m) {
        channel.send('Starting execute TC: '+m[1]+'...');
    }

    var commands = [
        { m: /^\:help/, f: help },
        { m: /^\:list\Wtestcases/, f: list},
        { m: /^\:list\Wresults/, f: results },
        { m: /^\:run\W(.*)/, f: run }
    ]

    var execute = function(message, channel, user) {
        if( !message.text.match(/^\:/) ) return false;
        console.log('SLACK: check if cmd is valid: '+message.text);
        return commands.some( function(cmd) {
            m =  message.text.match(cmd.m);
            if(m) {
                cmd.f(message, channel, user, m);
                return true;
            }
        });
    }

    var handleMessage = function(message) {
        try {
            channel = slack.getChannelGroupOrDMByID(message.channel)
            user = slack.getUserByID(message.user)
            if( !allowMessage(channel, user) ) {
                winston.warn('SLACK: message not allowed');
                return;
            }
            if( !user.hasOwnProperty("real_name") ) {
                console.log(user);
                console.log(message.text);
                return;
            }
            winston.log('SLACK: '+channel.name+'|'+user.real_name+''+message.text);
            if( !execute(message, channel, user) ) {
                console('SLACK: Unknown command: '+message.text);
            }
        } catch( e ) {
            console.log(e);
            console.log(message.text);
        }
    }

    var slack = new Slack(slackOpts.token, slackOpts.autoReconnect, slackOpts.automark);

    slack
      .on('open', function(){
      channels = []
      groups = []
      unreads = slack.getUnreadCount()
      winston.info('Slack -connection open');
      // Get all the channels that bot is a member of
      /*channels = {}
      for( var id, channel in slack.channels ) {
         if( channel.is_member )
          channels[channel.id] = channel.name;
      }*/
      // Get all groups that are open and not archived
      //groups = (group.name for id, group of slack.groups when group.is_open and not group.is_archived)
    })
    .on('error', winston.error);
    .on('message', handleMessage);
    slack.login();
    /*
    var robotOpts = {
      ignoreMessageInGeneral: true,
      //mentionToRespond: true,
      //skipDMMention: false
    }
    var robot = new Robot(slackOpts, robotOpts);

    robot.listen('hello') //:results([a-z\-]+)
      .handler(function(req, res) {
      // you can get named-param inside Request object
      console.log(req.param);
      console.log("hello received");
      res.sendText("Hi There");
      // {animal: 'sheep', year: '2010'}
      // note that req.param always returns Map<string, string>
    });

    app.get('/api/v0/slack', function(req, res){
		  res.json({ok: 1});
		});*/
	}

  return this;
}

exports = module.exports = AddonSlack;<commit_msg>move slack configuration to config file<commit_after>var _ = require('underscore');
var winston = require('winston');
var mongoose = require('mongoose');
var Slack = require('slack-client');
var nconf = require('nconf');
var winston = require('winston');

function AddonSlack (app, server, io, passport){
    var self = this;
	this.name = 'Slack';
	this.description = 'Slack integration addon';
    this.listDependencies = ['slack-robot'];
    var Testcase = mongoose.model('Testcase')
    var Result = mongoose.model('Result');

    var allowAllUsers = true;
    var allowedUsers = [/jussi/, /wirkus/];

    var allowAllChannels = false;
    var allowedChannels = [/jussiva/, /wirkus/, /thread/, /6lowpan/, /clitest/];

    var cfg = nconf.get('slack');

	this.register = function(){

        if( !cfg || !cfg.token ) {
            winston.error('slack configuration missing!');
            return;
        }

        var slackOpts = {
          token: nconf.get('slack').token,
          autoReconnect: true,
          //autoMark: false
        }
        var slack = new Slack(slackOpts.token, slackOpts.autoReconnect, slackOpts.automark);

        slack
          .on('open', function(){
          channels = []
          groups = []
          unreads = slack.getUnreadCount()
          winston.info('Slack -connection open');
          // Get all the channels that bot is a member of
          /*channels = {}
          for( var id, channel in slack.channels ) {
             if( channel.is_member )
              channels[channel.id] = channel.name;
          }*/
          // Get all groups that are open and not archived
          //groups = (group.name for id, group of slack.groups when group.is_open and not group.is_archived)
        })
        .on('error', winston.error)
        .on('message', handleMessage);
        slack.login();
    }

    var allowedChannel = function(channel) {
        if( allowAllChannels ) return true;
        if( !channel || !channel.name ) return false;
        winston.log('SLACK: check if channel is valid: '+channel.name);
        return allowedChannels.some( function(ch) {
            if( channel.name.toLowerCase().match(ch) ){
                return true;
            }
        });
    }
    var allowedUser = function(user) {
        if( allowAllUsers ) return true;
        if( !user || !user.real_name ) return false;
        winston.log('SLACK: check if user is valid: '+user.real_name);
        return allowedUsers.some( function(usr) {
            if( user.real_name.toLowerCase().match(usr) ){
                return true;
            }
        });
    }

    var allowMessage = function(channel, user) {
        if( !allowedChannel(channel) ) return false;
        if( !allowedUser(user) ) return false;
        return true;
    }

    var help = function(message, channel, user, m) {
        channel.send("```Test Management Service\n:list testcases\tList all available cases\n:list results\tList 10 latest results\n:run [testcase]\t:run individual case\n:help\tThis help```");
    }
    var list = function(message, channel, user, m) {
        q = {f: 'tcid', t: 'distinct'};
        if( m=message.text.match(/^\:list\Wtestcases\W(.*)/)  ) {
            try {
                _.extend(q, JSON.parse(m[1]));
            } catch(e) {
                console.log(e);
                channel.send('Invalid format');
                return;
            }
        }
        Testcase.query(q, function(error, list){
          channel.send('```'+JSON.stringify(list, ' ', '\n')+'```');
        })
    }
    var results = function(message, channel, user, m) {
        q = {l: 10, s: '{"cre.time": -1}', q: {}};
        if( m=message.text.match(/thread/) || channel.name.match(/thread/) ) {
            if(!q.q['$or']) q.q['$or'] = [];
            q.q['$or'].push({'exec.sut.cut': 'thread'});
        }
        else if( m=message.text.match(/6lowpan/) || channel.name.match(/6lowpan/)  ) {
            if(!q.q['$or']) q.q['$or'] = [];
            q.q['$or'].push({'exec.sut.cut': '6LoWPAN'});
        }
        else if( m=message.text.match(/^\:list\Wresults\W(.*)/)  ) {
            try {
                _.extend(q, JSON.parse(m[1]));
            } catch(e) {
                console.log(e);
                channel.send('Invalid format');
                return;
            }
        }
        q.q = JSON.stringify(q.q);
        console.log('SLACK: result query: '+JSON.stringify(q));
        Result.query(q, function(error, list){
            if( error ) {
                channel.send('Error while query');
                return;
            }
            var str = 'TC\t\tResult\n';
            list.forEach( function (result){
                if( result.exec && result.exec.verdict ) {
                    str += result.tcid+'\t'+result.exec.verdict+'\n';
                }
            });
            channel.send('```'+str+'```');
        })
    }
    var run = function(message, channel, user, m) {
        channel.send('Starting execute TC: '+m[1]+'...');
    }

    var commands = [
        { m: /^\:help/, f: help },
        { m: /^\:list\Wtestcases/, f: list},
        { m: /^\:list\Wresults/, f: results },
        { m: /^\:run\W(.*)/, f: run }
    ]

    var execute = function(message, channel, user) {
        if( !message.text.match(/^\:/) ) return false;
        console.log('SLACK: check if cmd is valid: '+message.text);
        return commands.some( function(cmd) {
            m =  message.text.match(cmd.m);
            if(m) {
                cmd.f(message, channel, user, m);
                return true;
            }
        });
    }

    var handleMessage = function(message) {
        try {
            channel = slack.getChannelGroupOrDMByID(message.channel)
            user = slack.getUserByID(message.user)
            if( !allowMessage(channel, user) ) {
                winston.warn('SLACK: message not allowed');
                return;
            }
            if( !user.hasOwnProperty("real_name") ) {
                console.log(user);
                console.log(message.text);
                return;
            }
            winston.log('SLACK: '+channel.name+'|'+user.real_name+''+message.text);
            if( !execute(message, channel, user) ) {
                console('SLACK: Unknown command: '+message.text);
            }
        } catch( e ) {
            console.log(e);
            console.log(message.text);
        }
    }
    /*
    var robotOpts = {
      ignoreMessageInGeneral: true,
      //mentionToRespond: true,
      //skipDMMention: false
    }
    var robot = new Robot(slackOpts, robotOpts);

    robot.listen('hello') //:results([a-z\-]+)
      .handler(function(req, res) {
      // you can get named-param inside Request object
      console.log(req.param);
      console.log("hello received");
      res.sendText("Hi There");
      // {animal: 'sheep', year: '2010'}
      // note that req.param always returns Map<string, string>
    });

    app.get('/api/v0/slack', function(req, res){
		  res.json({ok: 1});
		});*/

  return this;
}

exports = module.exports = AddonSlack;<|endoftext|>


<commit_before>import {
  DECREMENT_FETCH,
  FETCH_ERROR,
  INCREMENT_FETCH
} from '@conveyal/woonerf/fetch'

export const reducers = {
  [DECREMENT_FETCH] (state, action) {
    return {
      ...state,
      outstandingRequests: state.outstandingRequests - 1
    }
  },
  [INCREMENT_FETCH] (state, action) {
    return {
      ...state,
      outstandingRequests: state.outstandingRequests + 1
    }
  },
  [FETCH_ERROR] (state, {payload}) {
    if (
      payload.url &&
      payload.url.endsWith('/api/analysis/enqueue/single') &&
      payload.status === 400
    ) {
      // analysis error due to malformed scenario, handled specifically in actions/analysis, don't
      // lock ui
      return state
    } else if (payload.value && Array.isArray(payload.value)) {
      const value = payload.value[0]
      return {
        ...state,
        error: {
          error: value.message || 'Server Error',
          detailMessage: value.exception
            ? exceptionToString(value.exception)
            : ''
        }
      }
    } else {
      return {
        ...state,
        error: {
          error: 'Fetch Error',
          detailMessage: payload.value ? payload.value.message : payload.message
        }
      }
    }
  },
  'decrement outstanding requests' (state, action) {
    return {
      ...state,
      outstandingRequests: state.outstandingRequests - 1
    }
  },
  'increment outstanding requests' (state, action) {
    return {
      ...state,
      outstandingRequests: state.outstandingRequests + 1
    }
  },
  'lock ui with error' (state, action) {
    return {
      ...state,
      error: action.payload
    }
  }
}

export const initialState = {
  error: null,
  outstandingRequests: 0
}

function exceptionToString (exception) {
  return `Caused by: ${exception.message}
   ${exception.stackTrace
    .map(traceToLine).join('
   ')}`
}

const traceToLine = (trace) =>
  `${trace.className}.${trace.methodName}(${trace.fileName}:${trace.lineNumber})`
<commit_msg>fix(errors): Sometimes the cause is empty, dont show anything in that case.<commit_after>import {
  DECREMENT_FETCH,
  FETCH_ERROR,
  INCREMENT_FETCH
} from '@conveyal/woonerf/fetch'

export const reducers = {
  [DECREMENT_FETCH] (state, action) {
    return {
      ...state,
      outstandingRequests: state.outstandingRequests - 1
    }
  },
  [INCREMENT_FETCH] (state, action) {
    return {
      ...state,
      outstandingRequests: state.outstandingRequests + 1
    }
  },
  [FETCH_ERROR] (state, {payload}) {
    if (
      payload.url &&
      payload.url.endsWith('/api/analysis/enqueue/single') &&
      payload.status === 400
    ) {
      // analysis error due to malformed scenario, handled specifically in actions/analysis, don't
      // lock ui
      return state
    } else if (payload.value && Array.isArray(payload.value)) {
      const value = payload.value[0]
      return {
        ...state,
        error: {
          error: value.message || 'Server Error',
          detailMessage: value.exception
            ? exceptionToString(value.exception)
            : ''
        }
      }
    } else {
      return {
        ...state,
        error: {
          error: 'Fetch Error',
          detailMessage: payload.value ? payload.value.message : payload.message
        }
      }
    }
  },
  'decrement outstanding requests' (state, action) {
    return {
      ...state,
      outstandingRequests: state.outstandingRequests - 1
    }
  },
  'increment outstanding requests' (state, action) {
    return {
      ...state,
      outstandingRequests: state.outstandingRequests + 1
    }
  },
  'lock ui with error' (state, action) {
    return {
      ...state,
      error: action.payload
    }
  }
}

export const initialState = {
  error: null,
  outstandingRequests: 0
}

function exceptionToString (exception) {
  return `Caused by: ${exception.message || ''}
   ${exception.stackTrace
    .map(traceToLine).join('
   ')}`
}

const traceToLine = (trace) =>
  `${trace.className}.${trace.methodName}(${trace.fileName}:${trace.lineNumber})`
<|endoftext|>


<commit_before>/* global module */
'use strict';

module.exports = {
  name: 'ticketfly-css',

  inputCSSFileName: 'index.css',

  getCSSOutputFileName() {
    return `${this.name}.css`;
  },

  getPostCSSPlugins() {
    const cssImport = require('postcss-import');
    const cssNext = require('postcss-cssnext');

    return [
      { module: cssImport },
      {
        module: cssNext,
        options: {
          features: {
            customProperties: { preserve: true }  // 📋 TODO: Make this (along with a lot of other things) dynamic and configurable
          }
        }
      }
    ];
  },

  isAddon() {
    const keywords = this.project.pkg.keywords;

    return Array.isArray(keywords) && keywords.indexOf('ember-addon') !== -1;
  },

  isDummyApp() {
    return this._target.name === 'dummy';
  },

  isOurProject() {
    return this._target.project.name() === this.name;
  },

  shouldImportStyles() {
    return !this.isAddon() || this.isDummyApp();
  },

  importStyles() {
    this._target.import(`vendor/${this.getCSSOutputFileName()}`);
  },

  included: function(app, parentAddon) {
    this._super.included(...arguments);

    // Ensures that imports work for nested addons and engines
    // @see: https://github.com/ember-cli/ember-cli/issues/3718
    const parentApp = (typeof app.import !== 'function' && app.app) ? app.app : app;
    const target = parentAddon || parentApp;

    this._target = target;
    this._targetConfig = this.project.config(this.app.env)['ticketfly-css'] || {};

    if (this.shouldImportStyles()) {
      this.importStyles();
    }
  },

  treeForVendor(node) {
    if (!this.shouldImportStyles()) {
      return node;
    }

    const path = require('path');
    const Funnel = require('broccoli-funnel');
    const mergeTrees = require('broccoli-merge-trees');
    const compileCSS = require('broccoli-postcss-single');

    const stylesPathRoot = this.isDevelopingAddon() ? '' : path.join(this.project.nodeModulesPath, this.name);
    const stylesPath = path.join(stylesPathRoot, 'src');

    const inputTree = new Funnel(stylesPath, {
      include: [/\.css$/]
    });

    const inputFileName = this.inputCSSFileName;
    const outputFileName = this.getCSSOutputFileName();
    const postCSSPlugins = this.getPostCSSPlugins();
    const sourceMapOpts = { inline: true };

    const compiledCSS = compileCSS([inputTree], inputFileName, outputFileName, postCSSPlugins, sourceMapOpts);

    const outputNode = node ? mergeTrees([node, compiledCSS]) : compiledCSS;

    return outputNode;
  }
};
<commit_msg>Fix 'Cannot read property env of undefined' in index.js<commit_after>/* global module */
'use strict';

module.exports = {
  name: 'ticketfly-css',

  inputCSSFileName: 'index.css',

  getCSSOutputFileName() {
    return `${this.name}.css`;
  },

  getPostCSSPlugins() {
    const cssImport = require('postcss-import');
    const cssNext = require('postcss-cssnext');

    return [
      { module: cssImport },
      {
        module: cssNext,
        options: {
          features: {
            customProperties: { preserve: true }  // 📋 TODO: Make this (along with a lot of other things) dynamic and configurable
          }
        }
      }
    ];
  },

  isAddon() {
    const keywords = this.project.pkg.keywords;

    return Array.isArray(keywords) && keywords.indexOf('ember-addon') !== -1;
  },

  isDummyApp() {
    return this._target.name === 'dummy';
  },

  isOurProject() {
    return this._target.project.name() === this.name;
  },

  shouldImportStyles() {
    return !this.isAddon() || this.isDummyApp();
  },

  importStyles() {
    this._target.import(`vendor/${this.getCSSOutputFileName()}`);
  },

  included: function(app, parentAddon) {
    this._super.included(...arguments);

    // Ensures that imports work for nested addons and engines
    // @see: https://github.com/ember-cli/ember-cli/issues/3718

    let target = parentAddon || app;
    if (target.app) {
      target = target.app;
    }

    this._target = target;
    this._targetConfig = this.project.config(this._target.env)['ticketfly-css'] || {};

    if (this.shouldImportStyles()) {
      this.importStyles();
    }
  },

  treeForVendor(node) {
    if (!this.shouldImportStyles()) {
      return node;
    }

    const path = require('path');
    const Funnel = require('broccoli-funnel');
    const mergeTrees = require('broccoli-merge-trees');
    const compileCSS = require('broccoli-postcss-single');

    const stylesPathRoot = this.isDevelopingAddon() ? '' : path.join(this.project.nodeModulesPath, this.name);
    const stylesPath = path.join(stylesPathRoot, 'src');

    const inputTree = new Funnel(stylesPath, {
      include: [/\.css$/]
    });

    const inputFileName = this.inputCSSFileName;
    const outputFileName = this.getCSSOutputFileName();
    const postCSSPlugins = this.getPostCSSPlugins();
    const sourceMapOpts = { inline: true };

    const compiledCSS = compileCSS([inputTree], inputFileName, outputFileName, postCSSPlugins, sourceMapOpts);

    const outputNode = node ? mergeTrees([node, compiledCSS]) : compiledCSS;

    return outputNode;
  }
};
<|endoftext|>


<commit_before>'use strict';

/*eslint camelcase: 0*/

var async = require('async');
var _ = require('lodash');

module.exports = function(config, cb) {
  var dbConv = this,
    hosts = dbConv.connections,
    fields = config.fields,
    tables = config.tables;
  console.log(fields);
  //tasks;

  async.auto({
      from: function(callback) {
        var async_data = {
          fromRows: [],
          fromValues: []
        };
        hosts.toHost.useCollection(tables.tableFrom);
        hosts.toHost.findAll(function(data) {
          if (data && data.length > 0) {
            data.forEach(function(row) {
              fields.forEach(function(field) {
                if (row.hasOwnProperty(field.fieldFrom)) {
                  async_data.fromRows.push(row);
                  async_data.fromValues.push({
                    field: field.fieldWith,
                    search: row[field.fieldFrom],
                    replace: row[hosts.toHost.pk],
                    type: field.type
                  });
                }
              });
            });
          }

          callback(null, async_data);
        });
      },
      to: ['from', function(callback, async_data) {
        hosts.toHost.useCollection(tables.tableWith);
        var tasks = [],
          data = _.uniq(async_data.from.fromValues);
        if (data.length > 0) {
          dbConv.log.info(
            'Found %s entries in `%s` to relate. This may take a while...',
            data.length,
            tables.tableFrom
          );

          data.forEach(function(values) {
            var finder = {},
              update = {};

            if (!values.type || values.type === null) {
              finder[values.field] = values.search;
              update[values.field] = values.replace;
            } else if (values.type === 'array') {
              finder[values.field] = values.search;
              update[hosts.toHost.searchInArray(values.field)] =
                values.replace;
            }

            tasks.push({
              finder: finder,
              update: update
            });
          });

          if (tables.tableFrom === 'categoryentry') {
            console.log(tasks)
          }

          hosts.toHost.batchUpdate(tasks, callback);
        } else {
          dbConv.log.info('Nothing to relate from `%s` to `%s`.',
            tables.tableFrom, tables.tableWith);
          return callback(null);
        }
      }]
    },
    function() {
      cb(null);
    });
};
<commit_msg>added missing semicolon<commit_after>'use strict';

/*eslint camelcase: 0*/

var async = require('async');
var _ = require('lodash');

module.exports = function(config, cb) {
  var dbConv = this,
    hosts = dbConv.connections,
    fields = config.fields,
    tables = config.tables;
  console.log(fields);
  //tasks;

  async.auto({
      from: function(callback) {
        var async_data = {
          fromRows: [],
          fromValues: []
        };
        hosts.toHost.useCollection(tables.tableFrom);
        hosts.toHost.findAll(function(data) {
          if (data && data.length > 0) {
            data.forEach(function(row) {
              fields.forEach(function(field) {
                if (row.hasOwnProperty(field.fieldFrom)) {
                  async_data.fromRows.push(row);
                  async_data.fromValues.push({
                    field: field.fieldWith,
                    search: row[field.fieldFrom],
                    replace: row[hosts.toHost.pk],
                    type: field.type
                  });
                }
              });
            });
          }

          callback(null, async_data);
        });
      },
      to: ['from', function(callback, async_data) {
        hosts.toHost.useCollection(tables.tableWith);
        var tasks = [],
          data = _.uniq(async_data.from.fromValues);
        if (data.length > 0) {
          dbConv.log.info(
            'Found %s entries in `%s` to relate. This may take a while...',
            data.length,
            tables.tableFrom
          );

          data.forEach(function(values) {
            var finder = {},
              update = {};

            if (!values.type || values.type === null) {
              finder[values.field] = values.search;
              update[values.field] = values.replace;
            } else if (values.type === 'array') {
              finder[values.field] = values.search;
              update[hosts.toHost.searchInArray(values.field)] =
                values.replace;
            }

            tasks.push({
              finder: finder,
              update: update
            });
          });

          if (tables.tableFrom === 'categoryentry') {
            console.log(tasks);
          }

          hosts.toHost.batchUpdate(tasks, callback);
        } else {
          dbConv.log.info('Nothing to relate from `%s` to `%s`.',
            tables.tableFrom, tables.tableWith);
          return callback(null);
        }
      }]
    },
    function() {
      cb(null);
    });
};
<|endoftext|>


<commit_before>#! /usr/bin/env node
var request   = require('request');
var fs        = require('fs');
var mkdirp    = require('mkdirp');
var jsdom     = require('jsdom');
var urlParser = require('url');
var lodash    = require('lodash');

var defaultModule =  {
	////-----------------------------------------------------------------------------------------
	// crawls the link and searches for others, and saves those to the cache
	crawler: {
		////-----------------------------------------------------------------------------------------
		// the index of the queue
		current: 0,
		////-----------------------------------------------------------------------------------------
		// list of url-strings
		queue:   [],
		////-----------------------------------------------------------------------------------------
		// says what type of url it is, but this is optional for some cases
		context: [],
		////-----------------------------------------------------------------------------------------
		// whats the first index to use
		startContext: 'index',
		////-----------------------------------------------------------------------------------------
		// custom headers
		headers: {
		},
		load: function() {
			while(this.current < this.queue.length) {
				this.request();
			}
		},
		////-----------------------------------------------------------------------------------------
		// Handles requests, or takes the cached version
		request: function() {
			var self    = this;
			var current = self.current;
			var url     = urlParser.parse(this.queue[current]);
			var cb      = serious.scraper.response.bind(serious.scraper, current, url);
			var cache = serious.cache.get(url);
			if(cache === false) {
				var opt = {
					url: url.href,
					headers: this.headers
				};
				request(opt, function (error, response, body) {
					console.log('Loading: ' + url.href);
					if (!error && response.statusCode == 200) {
						// added index to scraper call
						serious.cache.set(url, body);
						jsdom.env(body, serious.scraper.includes, cb);
					} else {
						if(!response) response = {};
						console.error(url.href + ' Failed', error, response.statusCode, body);
					}
				});
			} else {
				jsdom.env(cache, serious.scraper.includes, cb);
			}
			self.current++;
		},
		////-----------------------------------------------------------------------------------------
		// Checks if a url is already processed
		addRequest: function(url, context) {
			if(!this.isLoaded(url)) {
				// @TODO add host check
				this.queue.push(url);
				this.context.push(context);
			} else {
				console.warn('The url was already in the system ' + url);
			}
		},
		////-----------------------------------------------------------------------------------------
		// Checks if url already got processed, does not check filesystem cache
		isLoaded: function(url) {
			if( this.queue.indexOf(url) === -1 ) {
				return false;
			} else {
				return true;
			}
		},
	},
	////-----------------------------------------------------------------------------------------
	// parses data from the body
	scraper: {
		////-----------------------------------------------------------------------------------------
		// external libraries like jquery can be added here
		includes: [],
		////-----------------------------------------------------------------------------------------
		// the place where your scraper module should put data in
		data: {},
		////-----------------------------------------------------------------------------------------
		// prefix where scraper data should be saved
		prefix: 'dist/scraper',
		////-----------------------------------------------------------------------------------------
		// main function for scraping the relevant informations
		response: function(index, url, err, window) {
			console.log(index, window.document.links.length, window.document.getElementsByTagName('li').length);

		},
		////-----------------------------------------------------------------------------------------
		// When everything is crawled, this function gets triggered
		done: function() {
			console.log(this.data);
		},
		////-----------------------------------------------------------------------------------------
		// saves scraper data
		save: function(opt, data) {
			serious.cache.set(opt, data, this.prefix);
		}
	},
	module: {
		////-----------------------------------------------------------------------------------------
		// overwrites this function by your module
		init: function(parent) {
			throw 'Overwrite this function to get the scope of the parent';
		},
		////-----------------------------------------------------------------------------------------
		// overwrites the module, depending on the host
		load: function(host) {
			try {
				var mod = require('./modules/' + host);
				lodash.merge(serious, mod);
				serious.module.init(serious);
			} catch(err) {
				console.warn('Could not load a module for ' + host, err);
			}
		}
	},
	////-----------------------------------------------------------------------------------------
	// handles the caching of the requests
	cache: {
		////-----------------------------------------------------------------------------------------
		// delimiter
		delimiter: '/',
		////-----------------------------------------------------------------------------------------
		// where should the cached be saved in
		prefix: 'dist/cache/crawler',
		////-----------------------------------------------------------------------------------------
		// checks if a request got cached, then it returns the body, else it returns false
		get: function(url) {
			try {
				var path = this.prefix + this.delimiter + url.host + this.delimiter + url.path;
				var file = fs.readFileSync(path, 'utf8');
				return file;
			} catch(err){
				return false;
			}
		},
		////-----------------------------------------------------------------------------------------
		// saves a body to the cache
		set: function(url, body, prefix) {
			if(!prefix) prefix = this.prefix;
			var delimiter = this.delimiter;
			var pathParts = url.path.split(delimiter);
			var fileName  = pathParts.pop();
			var path = prefix + delimiter + url.host + delimiter + pathParts.join(delimiter);
			mkdirp(path, function(err) {
				fs.writeFileSync(path + delimiter + fileName, body);
			});
		}
	},
	////-----------------------------------------------------------------------------------------
	// helper function for cli interface
	process: {
		////-----------------------------------------------------------------------------------------
		// parses the command line interface
		parse: function(args) {
			var result = null;
			var key = null;
			var opt = {};
			for(var i = 2; i < process.argv.length; i++) {
				var para = process.argv[i];
				if(para.indexOf('--') === 0) {
					if(key) throw key + ' had no corresponding value';
					key = para.slice(2, para.length);
				} else if(key) {
					opt[key] = para; // @TODO implement --header.cookie
					key = null;
				} else {
					if(result) throw 'There was already an url set ' + url;
					result = urlParser.parse(para, true);
				}
			}

			if(key) {
				throw 'There was no value set for ' + key;
			}
			return {url: result, opt: opt};
		}
	}
};

var serious = {
	crawler: {
		////-----------------------------------------------------------------------------------------
		// Sets up the initial request and overwrites functions from the module depending of host
		init: function(opt) {
			lodash.merge(serious, defaultModule);
			this.host = opt.url.host;
			serious.module.load(opt.url.host);
			this.addRequest(opt.url.href, this.startContext);
			this.load();
		}
	}
};

if(module.parent === null) { // Called via cli
	var url = defaultModule.process.parse(process.argv, true);

	if(url) {
		serious.crawler.init(url);
	}
} else { // Called via require
	// currently is not useful, but maybe later on
	lodash.merge(serious, defaultModule);
	module.exports = serious;
}
<commit_msg>[refactoring] removed some unneded stuff<commit_after>#! /usr/bin/env node
var request   = require('request');
var fs        = require('fs');
var mkdirp    = require('mkdirp');
var jsdom     = require('jsdom');
var urlParser = require('url');
var lodash    = require('lodash');

var defaultModule =  {
	////-----------------------------------------------------------------------------------------
	// crawls the link and searches for others, and saves those to the cache
	crawler: {
		////-----------------------------------------------------------------------------------------
		// the index of the queue
		current: 0,
		////-----------------------------------------------------------------------------------------
		// list of url-strings
		queue:   [],
		////-----------------------------------------------------------------------------------------
		// says what type of url it is, but this is optional for some cases
		context: [],
		////-----------------------------------------------------------------------------------------
		// whats the first index to use
		startContext: 'index',
		////-----------------------------------------------------------------------------------------
		// custom headers
		headers: {
		},
		load: function() {
			while(this.current < this.queue.length) {
				this.request();
			}
		},
		////-----------------------------------------------------------------------------------------
		// Handles requests, or takes the cached version
		request: function() {
			var url     = urlParser.parse(this.queue[this.current]);
			var cb      = serious.scraper.response.bind(serious.scraper, this.current, url);
			var cache = serious.cache.get(url);
			if(cache === false) {
				var opt = {
					url: url.href,
					headers: this.headers
				};
				request(opt, function (error, response, body) {
					console.log('Loading: ' + url.href);
					if (!error && response.statusCode == 200) {
						// added index to scraper call
						serious.cache.set(url, body);
						jsdom.env(body, serious.scraper.includes, cb);
					} else {
						if(!response) response = {};
						console.error(url.href + ' Failed', error, response.statusCode, body);
					}
				});
			} else {
				jsdom.env(cache, serious.scraper.includes, cb);
			}
			this.current++;
		},
		////-----------------------------------------------------------------------------------------
		// Checks if a url is already processed
		addRequest: function(url, context) {
			if(!this.isLoaded(url)) {
				// @TODO add host check
				this.queue.push(url);
				this.context.push(context);
			} else {
				console.warn('The url was already in the system ' + url);
			}
		},
		////-----------------------------------------------------------------------------------------
		// Checks if url already got processed, does not check filesystem cache
		isLoaded: function(url) {
			if( this.queue.indexOf(url) === -1 ) {
				return false;
			} else {
				return true;
			}
		},
	},
	////-----------------------------------------------------------------------------------------
	// parses data from the body
	scraper: {
		////-----------------------------------------------------------------------------------------
		// external libraries like jquery can be added here
		includes: [],
		////-----------------------------------------------------------------------------------------
		// the place where your scraper module should put data in
		data: {},
		////-----------------------------------------------------------------------------------------
		// prefix where scraper data should be saved
		prefix: 'dist/scraper',
		////-----------------------------------------------------------------------------------------
		// main function for scraping the relevant informations
		response: function(index, url, err, window) {
			console.log(index, window.document.links.length, window.document.getElementsByTagName('li').length);

		},
		////-----------------------------------------------------------------------------------------
		// When everything is crawled, this function gets triggered
		done: function() {
			console.log(this.data);
		},
		////-----------------------------------------------------------------------------------------
		// saves scraper data
		save: function(opt, data) {
			serious.cache.set(opt, data, this.prefix);
		}
	},
	module: {
		////-----------------------------------------------------------------------------------------
		// overwrites this function by your module
		init: function(parent) {
			throw 'Overwrite this function to get the scope of the parent';
		},
		////-----------------------------------------------------------------------------------------
		// overwrites the module, depending on the host
		load: function(host) {
			try {
				var mod = require('./modules/' + host);
				lodash.merge(serious, mod);
				serious.module.init(serious);
			} catch(err) {
				console.warn('Could not load a module for ' + host, err);
			}
		}
	},
	////-----------------------------------------------------------------------------------------
	// handles the caching of the requests
	cache: {
		////-----------------------------------------------------------------------------------------
		// delimiter
		delimiter: '/',
		////-----------------------------------------------------------------------------------------
		// where should the cached be saved in
		prefix: 'dist/cache/crawler',
		////-----------------------------------------------------------------------------------------
		// checks if a request got cached, then it returns the body, else it returns false
		get: function(url) {
			try {
				var path = this.prefix + this.delimiter + url.host + this.delimiter + url.path;
				var file = fs.readFileSync(path, 'utf8');
				return file;
			} catch(err){
				return false;
			}
		},
		////-----------------------------------------------------------------------------------------
		// saves a body to the cache
		set: function(url, body, prefix) {
			if(!prefix) prefix = this.prefix;
			var delimiter = this.delimiter;
			var pathParts = url.path.split(delimiter);
			var fileName  = pathParts.pop();
			var path = prefix + delimiter + url.host + delimiter + pathParts.join(delimiter);
			mkdirp(path, function(err) {
				fs.writeFileSync(path + delimiter + fileName, body);
			});
		}
	},
	////-----------------------------------------------------------------------------------------
	// helper function for cli interface
	process: {
		////-----------------------------------------------------------------------------------------
		// parses the command line interface
		parse: function(args) {
			var result = null;
			var key = null;
			var opt = {};
			for(var i = 2; i < process.argv.length; i++) {
				var para = process.argv[i];
				if(para.indexOf('--') === 0) {
					if(key) throw key + ' had no corresponding value';
					key = para.slice(2, para.length);
				} else if(key) {
					opt[key] = para; // @TODO implement --header.cookie
					key = null;
				} else {
					if(result) throw 'There was already an url set ' + url;
					result = urlParser.parse(para, true);
				}
			}

			if(key) {
				throw 'There was no value set for ' + key;
			}
			return {url: result, opt: opt};
		}
	}
};

var serious = {
	crawler: {
		////-----------------------------------------------------------------------------------------
		// Sets up the initial request and overwrites functions from the module depending of host
		init: function(opt) {
			lodash.merge(serious, defaultModule);
			this.host = opt.url.host;
			serious.module.load(opt.url.host);
			this.addRequest(opt.url.href, this.startContext);
			this.load();
		}
	}
};

if(module.parent === null) { // Called via cli
	var url = defaultModule.process.parse(process.argv, true);

	if(url) {
		serious.crawler.init(url);
	}
} else { // Called via require
	// currently is not useful, but maybe later on
	lodash.merge(serious, defaultModule);
	module.exports = serious;
}
<|endoftext|>


<commit_before>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
    var prev = old && old != CodeMirror.Init;
    if (val && !prev) {
      cm.on("blur", onBlur);
      cm.on("change", onChange);
      cm.on("swapDoc", onChange);
      CodeMirror.on(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose = function() { onComposition(cm) })
      onChange(cm);
    } else if (!val && prev) {
      cm.off("blur", onBlur);
      cm.off("change", onChange);
      cm.off("swapDoc", onChange);
      CodeMirror.off(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose)
      clearPlaceholder(cm);
      var wrapper = cm.getWrapperElement();
      wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
    }

    if (val && !cm.hasFocus()) onBlur(cm);
  });

  function clearPlaceholder(cm) {
    if (cm.state.placeholder) {
      cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
      cm.state.placeholder = null;
    }
  }
  function setPlaceholder(cm) {
    clearPlaceholder(cm);
    var elt = cm.state.placeholder = document.createElement("pre");
    elt.style.cssText = "height: 0; overflow: visible";
    elt.style.direction = cm.getOption("direction");
    elt.className = "CodeMirror-placeholder CodeMirror-line-like";
    var placeHolder = cm.getOption("placeholder")
    if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
    elt.appendChild(placeHolder)
    cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
  }

  function onComposition(cm) {
    var empty = true, input = cm.getInputField()
    if (input.nodeName == "TEXTAREA")
      empty = !input.value
    else if (cm.lineCount() == 1)
      empty = !/[^\u200b]/.test(input.querySelector(".CodeMirror-line").textContent)
    if (empty) clearPlaceholder(cm)
    else setPlaceholder(cm)
  }

  function onBlur(cm) {
    if (isEmpty(cm)) setPlaceholder(cm);
  }
  function onChange(cm) {
    var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
    wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");

    if (empty) setPlaceholder(cm);
    else clearPlaceholder(cm);
  }

  function isEmpty(cm) {
    return (cm.lineCount() === 1) && (cm.getLine(0) === "");
  }
});
<commit_msg>[placeholder addon] Fix composition handling<commit_after>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
    var prev = old && old != CodeMirror.Init;
    if (val && !prev) {
      cm.on("blur", onBlur);
      cm.on("change", onChange);
      cm.on("swapDoc", onChange);
      CodeMirror.on(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose = function() { onComposition(cm) })
      onChange(cm);
    } else if (!val && prev) {
      cm.off("blur", onBlur);
      cm.off("change", onChange);
      cm.off("swapDoc", onChange);
      CodeMirror.off(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose)
      clearPlaceholder(cm);
      var wrapper = cm.getWrapperElement();
      wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
    }

    if (val && !cm.hasFocus()) onBlur(cm);
  });

  function clearPlaceholder(cm) {
    if (cm.state.placeholder) {
      cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
      cm.state.placeholder = null;
    }
  }
  function setPlaceholder(cm) {
    clearPlaceholder(cm);
    var elt = cm.state.placeholder = document.createElement("pre");
    elt.style.cssText = "height: 0; overflow: visible";
    elt.style.direction = cm.getOption("direction");
    elt.className = "CodeMirror-placeholder CodeMirror-line-like";
    var placeHolder = cm.getOption("placeholder")
    if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
    elt.appendChild(placeHolder)
    cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
  }

  function onComposition(cm) {
    setTimeout(function() {
      var empty = false, input = cm.getInputField()
      if (input.nodeName == "TEXTAREA")
        empty = !input.value
      else if (cm.lineCount() == 1)
        empty = !/[^\u200b]/.test(input.querySelector(".CodeMirror-line").textContent)
      if (empty) setPlaceholder(cm)
      else clearPlaceholder(cm)
    }, 20)
  }

  function onBlur(cm) {
    if (isEmpty(cm)) setPlaceholder(cm);
  }
  function onChange(cm) {
    var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
    wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");

    if (empty) setPlaceholder(cm);
    else clearPlaceholder(cm);
  }

  function isEmpty(cm) {
    return (cm.lineCount() === 1) && (cm.getLine(0) === "");
  }
});
<|endoftext|>


<commit_before>import Ember from 'ember';

var proxyGenerator = function(name){
  return function(msg = '', title = '') {
    window.toastr[name](msg.toString(), title.toString());
  };
};

export function initialize(container, application, options) {
  var injectAs = options.injectAs;
  window.toastr.options = options.toastrOptions;

  var proxiedToastr = Ember.Object.extend(toastr).create({
    success: proxyGenerator('success'),
    info: proxyGenerator('info'),
    warning: proxyGenerator('warning'),
    error: proxyGenerator('error')
  });

  application.register('notify:main', proxiedToastr, { instantiate: false, singleton: true });
  application.inject('route', injectAs, 'notify:main');
  application.inject('controller', injectAs, 'notify:main');
  application.inject('component', injectAs, 'notify:main');
}
<commit_msg>Fix initializer deprecation for 2.x<commit_after>import Ember from 'ember';

var proxyGenerator = function(name){
  return function(msg = '', title = '') {
    window.toastr[name](msg.toString(), title.toString());
  };
};

export function initialize() {
  // support 1.x and 2.x
  var application = arguments[1] || arguments[0];
  var injectAs = options.injectAs;

  window.toastr.options = options.toastrOptions;

  var proxiedToastr = Ember.Object.extend(toastr).create({
    success: proxyGenerator('success'),
    info: proxyGenerator('info'),
    warning: proxyGenerator('warning'),
    error: proxyGenerator('error')
  });

  application.register('notify:main', proxiedToastr, { instantiate: false, singleton: true });
  application.inject('route', injectAs, 'notify:main');
  application.inject('controller', injectAs, 'notify:main');
  application.inject('component', injectAs, 'notify:main');
}
<|endoftext|>


<commit_before>import area from "./src/area";
import centroid from "./src/centroid";
import perimeter from "./src/perimeter";

export {
  area,
  centroid,
  perimeter
};
<commit_msg>Concise exports.<commit_after>export {default as area} from "./src/area";
export {default as centroid} from "./src/centroid";
export {default as perimeter} from "./src/perimeter";
<|endoftext|>


<commit_before>/**
  @module ember-audio
*/
import Ember from 'ember';

/**
  @class ProcessorIoMixin
  @namespace EmberAudio
*/
export default Ember.Mixin.create({

  /**
    This represents the input of the `processor`.

    @property input
    @type {Object}
  */
  input: null,

  /**
    This represents the output of the `processor`.

    @property output
    @type {Object}
  */
  output: null,

  /**
    This is the audio processing module.

    @property processor
    @type {Object}
  */
  processor: null,

  /**
    This is the `audioService` service.  It represents the audio system and gives
    us acces to the `AudioContext` object which among other things provides the
    audio output.

    @property audioService
    @type {Object}
    @private
  */
  audioService: Ember.inject.service(),

  /**
    The component should override this method to implement the processor and set
    the processor's default values.

    @method createProcessor
  */
  createProcessor() {
    return null;
  },

  /**
    Create the processor and connect it to the input and output.

    @method init
  */
  init() {
    this._super(...arguments);
    var processor = this.createProcessor();
    if (processor) {
      this.set('processor', processor);
      this.connectProcessor(processor);
    }
  },

  /**
    Connect the processor to the input and output.

    @method connectProcessor
    @param {Object} processor
  */
  connectProcessor(processor) {
    var input = this.get('input');
    var output = this.get('output');
    if (input){
      input.connect(processor);
    }
    if (output) {
      processor.connect(output);
    }
  },

  /**
    If the input changes, connect it to the processor.

    @method inputChanged
  */
  inputChanged: Ember.observer('input', 'processor', function() {
    var input = this.get('input');
    var processor = this.get('processor');
    if ( input ) {
      input.disconnect(0);
      if ( processor ) {
        input.connect(processor);
      }
    }
  }),

  /**
    If the output changes, connect the processor to it.

    @method outputChanged
  */
  ouputChanged: Ember.observer('output', 'processor', function() {
    var output = this.get('output');
    var processor = this.get('processor');
    if ( processor ) {
      processor.disconnect(0);
      if ( output ) {
        processor.connect(output);
      }
    }
  })
});
<commit_msg>add processor-io mixin<commit_after>/**
  @module ember-audio
*/
import Ember from 'ember';

/**
  @class ProcessorIoMixin
  @namespace EmberAudio
*/
export default Ember.Mixin.create({

  /**
    This represents the input of the `processor`.

    @property input
    @type {Object}
  */
  input: null,

  /**
    This represents the output of the `processor`.

    @property output
    @type {Object}
  */
  output: null,

  /**
    This is the audio processing module.

    @property processor
    @type {Object}
  */
  processor: null,

  /**
    The component should override this method to implement the processor and set
    the processor's default values.

    @method createProcessor
  */
  createProcessor() {
    return null;
  },

  /**
    Create the processor and connect it to the input and output.

    @method init
  */
  init() {
    this._super(...arguments);
    var processor = this.createProcessor();
    if (processor) {
      this.set('processor', processor);
      this.connectProcessor(processor);
    }
  },

  /**
    Connect the processor to the input and output.

    @method connectProcessor
    @param {Object} processor
  */
  connectProcessor(processor) {
    var input = this.get('input');
    var output = this.get('output');
    if (input){
      input.connect(processor);
    }
    if (output) {
      processor.connect(output);
    }
  },

  /**
    ## connect

    Connect this `audioNode` to another `audioNode`.  This method is a proxy for
    the processor's `connect` method.

    @method connect
  */
  connect() {
    var processor = this.get('processor');
    processor.connect(...arguments);
  },

  /**
    ## disconnect

    Disconnect the `audioNode` from another `audioNode`.  This method is a proxy
    for the processor's `disconnect` method.

    @method disconnect
  */
  disconnect() {
    var processor = this.get('processor');
    processor.disconnect(...arguments);
  },

  /**
    If the input changes, connect it to the processor.

    @method inputChanged
  */
  inputChanged: Ember.observer('input', 'processor', function() {
    var input = this.get('input');
    var processor = this.get('processor');
    if ( input ) {
      input.disconnect(0);
      if ( processor ) {
        input.connect(processor);
      }
    }
  }),

  /**
    If the output changes, connect the processor to it.

    @method outputChanged
  */
  ouputChanged: Ember.observer('output', 'processor', function() {
    var output = this.get('output');
    var processor = this.get('processor');
    if ( processor ) {
      processor.disconnect(0);
      if ( output ) {
        processor.connect(output);
      }
    }
  })
});
<|endoftext|>