<commit_before>(function () {
	"use strict";

	var bullshits = [
		'24/7',
		'a-b testing',
		'acquisition',
		'action items?',
		'advantages?',
		'agenda',
		'aggregate',
		'agnostic',
		'application service providers?',
		'assets?',
		'at the end of the day',
		'automated',
		'b2b',
		'back to the drawing board',
		'ball.?park',
		'band.aid',
		'bandwidth',
		'basically',
		'benchmark',
		'benefit analysis',
		'best.of.breed',
		'best.practice',
		'big picture',
		'bottom line',
		'bottom.up',
		'brain.?storm',
		'brain.?dump',
		'brand(?:s?|ing|ed)',
		'burn rates?',
		'business cases?',
		'business plans?',
		'business.centricity',
		'business.centric',
		'buzz',
		'call to action',
		'capacity',
		'capitalize',
		'center of excellence',
		'challenges?',
		'change agent',
		'circle the wagons',
		'client focus',
		'client.focused',
		'cloud.based',
		'collaborate',
		'collaboration',
		'communicate',
		'communication',
		'compelling',
		'competitive advantage',
		'competitive',
		'connect the dots',
		'content management',
		'contingency plan',
		'control group',
		'convergent',
		'conversation',
		'conversion',
		'core business',
		'core competenc(?:y|ies)',
		'cost-effective',
		'cost/benefit',
		'craftsmanship',
		'critical path',
		'crm',
		'cross.sell',
		'customer.(?:centric|facing|oriented|service|driven)',
		'cutting.edge',
		'dashboard',
		'data mining',
		'deadlines',
		'deep dive',
		'delight the customer',
		'deliverables?',
		'demographic',
		'discovery',
		'diversity',
		'downsize',
		'drill down',
		'drink the kool-aid',
		'drop the ball',
		'due dilligence',
		'dynamic',
		'e-business',
		'e-commerce',
		'e-tailers',
		'ecosystem',
		'efficiency',
		'efficient',
		'elephant in the room',
		'elevator pitch',
		'emerging markets',
		'empower',
		'empowerment',
		'end of the day',
		'end to end',
		'engage',
		'enhance',
		'enterprise',
		'eta',
		'etched in stone',
		'exceed expectations',
		'expectations',
		'experiences',
		'exposure',
		'eyeballs',
		'facilitate',
		'facilitator',
		'fast track',
		'fault tolerant',
		'first to market',
		'flexibility',
		'focused',
		'followup',
		'foot view',
		'front lines',
		'functionality',
		'full benefit',
		'game plan',
		'gap analysis',
		'go live',
		'go public',
		'go to market',
		'goals',
		'goal.oriented',
		'going forward',
		'google juice',
		'granular',
		'green',
		'growth',
		'grow',
		'guidance',
		'hardball',
		'having said that',
		'heads.up',
		'heavy.lifting',
		'herding cats',
		'high.level',
		'high.impact',
		'impactful',
		'implementation',
		'in a nutshell',
		'incent',
		'incentivize',
		'innovated',
		'innovation',
		'innovative',
		'innovating',
		'innovate',
		'integrated',
		'integration',
		'intellectual property',
		'knowledge base',
		'knowledge transfer',
		'landing page',
		'landing pages',
		'lead the field',
		'leadership',
		'legacy',
		'lessons learned',
		'level playing field',
		'level set',
		'leverage',
		'low-hanging fruit',
		'market driven',
		'market leader',
		'market window',
		'marketing collateral',
		'mass.impact',
		'maximize',
		'measurement',
		'methodolog(?:y|ies)',
		'metrics',
		'middleware',
		'milestone',
		'mind share',
		'mind shower',
		'mindset',
		'mindshare',
		'mission.critical',
		'monetize',
		'moving forward',
		'multitask',
		'multitasking',
		'negotiated',
		'networking',
		'new economy',
		'next level',
		'niche',
		'niches',
		'no-brainer',
		'non-traditional management',
		'objectives',
		'occupy the field',
		'off.site meeting',
		'off.the.(?:radar|shelf)',
		'off-?line',
		'on board',
		'on the (?:back end|radar screen|same page)',
		'one to one',
        'OOP',
        '(?:(?:O|o)bject|(?:A|a)spect|(?:R|r)ole|(?:S|s)ubject)-oriented',
		'opportunit(?:y|ies)',
		'optimization',
		'out.of.the.(?:box|loop)',
		'outsourc(?:e|ed|ing)',
		'ownership',
		'paradigm shift',
		'paradigms?',
		'partnerships?',
		'partner',
		'patent',
		'performance indicators',
		'performance',
		'personalization',
		'perspective',
		'phase',
		'phased approach',
		'pipeline',
		'planning horizon',
		'prioritized?',
		'proactive',
		'problem space',
		'process assessment',
		'processes',
		'process-centric',
		'profitability',
		'profit',
		'promotional collateral',
		'promotion',
		'proof.of.concept',
		'prototype',
		'push the envelope',
		'pushback',
		'quality.driven',
		'quick win',
		'rais(?:e|ing) the bar',
		'ramp.up',
		'relationship management',
		'engagements?',
		'engage',
		'reach out',
		'reactivation',
		'real.time',
		'real.world',
		'reconfigure',
		'red flag',
		'reengineering',
		'reinvent(?:ing)? the wheel',
		'reinvigorate',
		'relevance',
		'repurpose',
		'resource allocation',
		'resources?',
		'restructuring',
		'results.driven',
		'retention',
		'return on investment',
		'reusability',
		'reuse',
		'revenue',
		'reverse.engineer',
		'revisit',
		'road ?map',
		'robust',
		'roi',
		'run the numbers',
		'sales.driven',
		'scale',
		'scenarios?',
		'scope',
		'seamless',
		'search engine optimization',
		'secret sauce',
		'segment',
		'self-managed team',
		'seo',
		'service oriented',
		'shareholder value',
		'single-source responsibility',
		'skill sets?',
		'smoke (?:&|and) mirrors',
		'solutions?',
		'soup to nuts',
		'sow',
		'stakeholder',
		'statement of work',
		'sticky-?ness',
		'strateg(?:y|ic|ize)',
		'streamline',
		'success',
		'sustainab(?:le|ility)',
		'synerg(?:y|ies)',
		'tailwinds?',
		'take offline',
		'talking points',
		'target (?:audience|group)',
		'targeted',
		'tasked',
		'tco',
		'team building',
		'team player',
		'teamwork',
		'technologies',
		'technology solutions',
		'that being said',
		'think(?:ing)? outside the box',
		'time to market',
		'time.critical',
		'timelines?',
		'top.down',
		'top of the game',
		'total cost of ownership',
		'total quality',
		'touch base',
		'touchpoints',
		'traction',
		'turnkey',
		'up to speed',
		'up-?sell',
		'upside',
		'user.friendly',
		'utilize',
		'unique approach',
		'value-added',
		'values?',
		'vertical market',
		'viral',
		'virtual(?:ization)?',
		'visibility',
		'vision statement',
		'walk the talk',
		'web 2\\.0',
		'web 3\\.0',
		'web-enabled',
		'win-win',
		'window of opportunity',
		'workflow',
		'workshop',
		'world.class',
		'wow factor'
	];

	function bulshitize(term) {
		var c = term.charAt(0),
		    bullshit = (c === c.toUpperCase() ? 'B' : 'b') + 'ullshit';

		if (term.substr(term.length - 3) === 'ing') {
			bullshit += 'ing';
		}

		return '' + bullshit + '';
	}

	var re = new RegExp('(' + bullshits.join('|') + ')(?!\\w|[^<]*>)', 'gi'),
	    body = document.body;

	body.innerHTML = body.innerHTML.replace(re, bulshitize);
}());
<commit_msg>usually a code word for "not actually better"<commit_after>(function () {
	"use strict";

	var bullshits = [
		'24/7',
		'a-b testing',
		'acquisition',
		'action items?',
		'advantages?',
		'agenda',
		'aggregate',
		'agnostic',
		'application service providers?',
		'assets?',
		'at the end of the day',
		'automated',
		'b2b',
		'back to the drawing board',
		'ball.?park',
		'band.aid',
		'bandwidth',
		'basically',
		'benchmark',
		'benefit analysis',
		'best.of.breed',
		'best.practice',
		'big picture',
		'bottom line',
		'bottom.up',
		'brain.?storm',
		'brain.?dump',
		'brand(?:s?|ing|ed)',
		'burn rates?',
		'business cases?',
		'business plans?',
		'business.centricity',
		'business.centric',
		'buzz',
		'call to action',
		'capacity',
		'capitalize',
		'center of excellence',
		'challenges?',
		'change agent',
		'circle the wagons',
		'client focus',
		'client.focused',
		'cloud.based',
		'collaborate',
		'collaboration',
		'communicate',
		'communication',
		'compelling',
		'competitive advantage',
		'competitive',
		'connect the dots',
		'content management',
		'contingency plan',
		'control group',
		'convergent',
		'conversation',
		'conversion',
		'core business',
		'core competenc(?:y|ies)',
		'cost-effective',
		'cost/benefit',
		'craftsmanship',
		'critical path',
		'crm',
		'cross.sell',
		'customer.(?:centric|facing|oriented|service|driven)',
		'cutting.edge',
		'dashboard',
		'data mining',
		'deadlines',
		'deep dive',
		'delight the customer',
		'deliverables?',
		'demographic',
		'discovery',
		'diversity',
		'downsize',
		'drill down',
		'drink the kool-aid',
		'drop the ball',
		'due dilligence',
		'dynamic',
		'e-business',
		'e-commerce',
		'e-tailers',
		'ecosystem',
		'efficiency',
		'efficient',
		'elephant in the room',
		'elevator pitch',
		'emerging markets',
		'empower',
		'empowerment',
		'end of the day',
		'end to end',
		'engage',
		'enhance',
		'enterprise',
		'eta',
		'etched in stone',
		'exceed expectations',
		'expectations',
		'experiences',
		'exposure',
		'eyeballs',
		'facilitate',
		'facilitator',
		'fast track',
		'fault tolerant',
		'first to market',
		'flexibility',
		'focused',
		'followup',
		'foot view',
		'front lines',
		'functionality',
		'full benefit',
		'game plan',
		'gap analysis',
		'go live',
		'go public',
		'go to market',
		'goals',
		'goal.oriented',
		'going forward',
		'google juice',
		'granular',
		'green',
		'growth',
		'grow',
		'guidance',
		'hardball',
		'having said that',
		'heads.up',
		'heavy.lifting',
		'herding cats',
		'high.level',
		'high.impact',
		'impactful',
		'implementation',
		'in a nutshell',
		'incent',
		'incentivize',
		'innovated',
		'innovation',
		'innovative',
		'innovating',
		'innovate',
		'integrated',
		'integration',
		'intellectual property',
		'knowledge base',
		'knowledge transfer',
		'landing page',
		'landing pages',
		'lead the field',
		'leadership',
		'legacy',
		'lessons learned',
		'level playing field',
		'level set',
		'leverage',
		'low-hanging fruit',
		'market driven',
		'market leader',
		'market window',
		'marketing collateral',
		'mass.impact',
		'maximize',
		'measurement',
		'methodolog(?:y|ies)',
		'metrics',
		'middleware',
		'milestone',
		'mind share',
		'mind shower',
		'mindset',
		'mindshare',
		'mission.critical',
		'monetize',
		'moving forward',
		'multitask',
		'multitasking',
		'negotiated',
		'networking',
		'new economy',
		'next level',
		'niche',
		'niches',
		'no-brainer',
		'non-traditional management',
		'objectives',
		'occupy the field',
		'off.site meeting',
		'off.the.(?:radar|shelf)',
		'off-?line',
		'on board',
		'on the (?:back end|radar screen|same page)',
		'one to one',
        'OOP',
        '(?:(?:O|o)bject|(?:A|a)spect|(?:R|r)ole|(?:S|s)ubject)-oriented',
		'opportunit(?:y|ies)',
		'optimization',
		'out.of.the.(?:box|loop)',
		'outsourc(?:e|ed|ing)',
		'ownership',
		'paradigm shift',
		'paradigms?',
		'partnerships?',
		'partner',
		'patent',
		'performance indicators',
		'performance',
		'personalization',
		'perspective',
		'phase',
		'phased approach',
		'pipeline',
		'planning horizon',
		'prioritized?',
		'proactive',
		'problem space',
		'process assessment',
		'processes',
		'process-centric',
		'profitability',
		'profit',
		'promotional collateral',
		'promotion',
        'proprietary',
		'proof.of.concept',
		'prototype',
		'push the envelope',
		'pushback',
		'quality.driven',
		'quick win',
		'rais(?:e|ing) the bar',
		'ramp.up',
		'relationship management',
		'engagements?',
		'engage',
		'reach out',
		'reactivation',
		'real.time',
		'real.world',
		'reconfigure',
		'red flag',
		'reengineering',
		'reinvent(?:ing)? the wheel',
		'reinvigorate',
		'relevance',
		'repurpose',
		'resource allocation',
		'resources?',
		'restructuring',
		'results.driven',
		'retention',
		'return on investment',
		'reusability',
		'reuse',
		'revenue',
		'reverse.engineer',
		'revisit',
		'road ?map',
		'robust',
		'roi',
		'run the numbers',
		'sales.driven',
		'scale',
		'scenarios?',
		'scope',
		'seamless',
		'search engine optimization',
		'secret sauce',
		'segment',
		'self-managed team',
		'seo',
		'service oriented',
		'shareholder value',
		'single-source responsibility',
		'skill sets?',
		'smoke (?:&|and) mirrors',
		'solutions?',
		'soup to nuts',
		'sow',
		'stakeholder',
		'statement of work',
		'sticky-?ness',
		'strateg(?:y|ic|ize)',
		'streamline',
		'success',
		'sustainab(?:le|ility)',
		'synerg(?:y|ies)',
		'tailwinds?',
		'take offline',
		'talking points',
		'target (?:audience|group)',
		'targeted',
		'tasked',
		'tco',
		'team building',
		'team player',
		'teamwork',
		'technologies',
		'technology solutions',
		'that being said',
		'think(?:ing)? outside the box',
		'time to market',
		'time.critical',
		'timelines?',
		'top.down',
		'top of the game',
		'total cost of ownership',
		'total quality',
		'touch base',
		'touchpoints',
		'traction',
		'turnkey',
		'up to speed',
		'up-?sell',
		'upside',
		'user.friendly',
		'utilize',
		'unique approach',
		'value-added',
		'values?',
		'vertical market',
		'viral',
		'virtual(?:ization)?',
		'visibility',
		'vision statement',
		'walk the talk',
		'web 2\\.0',
		'web 3\\.0',
		'web-enabled',
		'win-win',
		'window of opportunity',
		'workflow',
		'workshop',
		'world.class',
		'wow factor'
	];

	function bulshitize(term) {
		var c = term.charAt(0),
		    bullshit = (c === c.toUpperCase() ? 'B' : 'b') + 'ullshit';

		if (term.substr(term.length - 3) === 'ing') {
			bullshit += 'ing';
		}

		return '' + bullshit + '';
	}

	var re = new RegExp('(' + bullshits.join('|') + ')(?!\\w|[^<]*>)', 'gi'),
	    body = document.body;

	body.innerHTML = body.innerHTML.replace(re, bulshitize);
}());
<|endoftext|>


<commit_before>import _ from 'underscore';
import DryadTree from './DryadTree';
import CommandMiddleware from './CommandMiddleware';
import {Promise} from 'bluebird';
import hyperscript from './hyperscript';

// TODO: This needs to be opt-in
if (process) {
  process.on('unhandledRejection', function(reason) {
    console.error('Unhandled Rejection:', reason, reason && reason.stack);
    throw new Error(reason);
  });
} else {
  Promise.onPossiblyUnhandledRejection((error) => {
    console.error('Unhandled Rejection', error);
    throw new Error(error);
  });
}

/**
 * Manages play/stop/update for a Dryad tree.
 *
 * A Dryad has no state or functionality until it is played
 * by a DryadPlayer. A Dryad can be played more than once at
 * the same time by creating more DryadPlayers.
 *
 * The DryadPlayer also holds the layers and command middleware
 * which execute the functionality that the Dryads specify.
 */
export default class DryadPlayer {

  constructor(rootDryad, layers, rootContext = {}) {
    this.middleware = new CommandMiddleware();
    this.classes = {};
    if (layers) {
      layers.forEach((layer) => {
        this.use(layer);
      });
    }

    if (!rootContext.log) {
      rootContext.log = console;
    }

    this.log = rootContext.log;

    this._errorHandler = (error) => {
      this.log.error(error.stack);
    };

    this.setRoot(rootDryad, rootContext);
  }

  /**
   * Set a new tree.
   *
   * Behavior while already playing is not yet defined.
   *
   * @param {Dryad} dryad
   */
  setRoot(dryad, rootContext) {
    if (dryad) {
      let classLookup = _.bind(this.getClass, this);
      this.tree = new DryadTree(this.h(dryad), classLookup, rootContext);
    } else {
      this.tree = null;
    }
  }

  /**
   * Convert hyperscript graph to Dryad objects with registered classes
   *
   * @param {Object} hgraph - JSON style object
   * @returns {Dryad}
   */
  h(hgraph) {
    let classLookup = _.bind(this.getClass, this);
    return hyperscript(hgraph, classLookup);
  }

  /**
   * Add a layer of functionality by registering Dryad classes and command middleware.
   *
   * @param {Object} layer - .classes is a list of Dryad classes, .middleware is a list of middleware functions
   */
  use(layer) {
    this.middleware.use(layer.middleware || []);
    (layer.classes || []).forEach((c) => this.addClass(c));
    return this;
  }

  /**
   * Register a Dryad class so it can be located when used in hyperscript.
   * Also needed if a class uses requireParent()
   *
   * @param {Dryad} dryadClass
   */
  addClass(dryadClass) {
    this.classes[dryadClass.name.toLowerCase()] = dryadClass;
  }

  /**
   * Lookup Dryad class by name.
   *
   * Used by hyperscript and requireParent, this requires
   * that layers and their classes were registered and any custom
   * classes that you right are registered. If you aren't using
   * hyperscript then you don't need to register your class.
   * @param {String} className - case-insensitive
   * @returns {Dryad}
   */
  getClass(className) {
    let dryadClass = this.classes[className.toLowerCase()];
    if (!dryadClass) {
      throw new Error(`Dryad class not found: '${className}' in classes: ${Object.keys(this.classes)}`);
    }
    return dryadClass;
  }

  /**
   * @returns {Promise} - that resolves to `this`
   */
  play(dryad) {
    if (dryad) {
      this.setRoot(dryad);
    }
    let prepTree = this._collectCommands('prepareForAdd');
    let addTree = this._collectCommands('add');
    return this._callPrepare(prepTree)
      .then(() => this._call(addTree, 'add'))
      .then(() => this)
      .catch(this._errorHandler);
  }

  /**
   * @returns {Promise} - that resolves to `this`
   */
  stop() {
    let removeTree = this._collectCommands('remove');
    return this._call(removeTree, 'remove').then(() => this).catch(this._errorHandler);
  }

  _collectCommands(commandName) {
    return this.tree.collectCommands(commandName, this.tree.tree, this);
  }

  /**
   * Execute a prepareForAdd tree of command objects.
   *
   * Values of the command objects are functions may return Promises.
   *
   * @param {Object} prepTree - id, commands, context, children
   * @returns {Promise} - resolves when all Promises in the tree have resolved
   */
  _callPrepare(prepTree) {
    var commands = prepTree.commands || {};
    if (_.isFunction(commands)) {
      commands = commands(prepTree.context);
    }
    return callAndResolveValues(commands, prepTree.context).then((resolved) => {
      // save resolved to that node's context
      // and mark that its $prepared: true for debugging
      this.updateContext(prepTree.context, _.assign({state: {prepare: true}}, resolved));
      let childPromises = prepTree.children.map((childPrep) => this._callPrepare(childPrep));
      return Promise.all(childPromises);
    }, (error) => {
      this.updateContext(prepTree.context, {state: {prepare: false, error: error}});
      return Promise.reject(error);
    });
  }

  /**
   * Execute a command tree using middleware.
   *
   * @returns {Promise}
   */
  _call(commandTree, stateTransitionName) {
    const updateContext = (context, update) => {
      this.tree.updateContext(context.id, update);
    };
    return this.middleware.call(commandTree, stateTransitionName, updateContext);
  }

  /**
   * Execute a single command object for a single node using middleware
   * outside the prepareForAdd/add/remove full tree command execution routine.
   *
   * This can be called out of band from a Dryad's add/remove method
   *
   * Its for commands that need to be executed during runtime
   * in response to events, streams etc.
   * eg. spawning synths from an incoming stream of data.
   */
  callCommand(nodeId, command) {
    return this._call(this.tree.makeCommandTree(nodeId, command), 'callCommand');
  }

  /**
   * Allow a Dryad to update its own context.
   *
   * Contexts are immutable - this returns a new context object.
   */
  updateContext(context, update) {
    return this.tree.updateContext(context.id, update);
  }

  /**
   * Get a representation of current state of the tree.
   * Contains add|remove|prepared and may hold errors.
   */
  getDebugState() {
    return this.tree.getDebugState();
  }
}

/**
 * Returns a new object with each value mapped to the called-and-resolved value.
 *
 * For each key/value in commands object,
 * if value is a function then call it
 * if result is a Promise then resolve it.
 *
 * @private
 * @param {Object} commands
 * @returns {Object}
 */
function callAndResolveValues(commands, context) {
  if (_.isEmpty(commands)) {
    return Promise.resolve({});
  }
  const keys = _.keys(commands);
  return Promise.map(keys, (key) => {
    let value = commands[key];
    return Promise.resolve(_.isFunction(value) ? value(context) : value);
  }).then((values) => {
    let result = {};
    keys.forEach((key, i) => {
      result[key] = values[i];
    });
    return result;
  });
}
<commit_msg>documentation, minor internal change to updateContext<commit_after>import _ from 'underscore';
import DryadTree from './DryadTree';
import CommandMiddleware from './CommandMiddleware';
import {Promise} from 'bluebird';
import hyperscript from './hyperscript';

// TODO: This needs to be opt-in
if (process) {
  process.on('unhandledRejection', function(reason) {
    console.error('Unhandled Rejection:', reason, reason && reason.stack);
    throw new Error(reason);
  });
} else {
  Promise.onPossiblyUnhandledRejection((error) => {
    console.error('Unhandled Rejection', error);
    throw new Error(error);
  });
}

/**
 * Manages play/stop/update for a Dryad tree.
 *
 * A Dryad has no state or functionality until it is played
 * by a DryadPlayer. A Dryad can be played more than once at
 * the same time by creating more DryadPlayers.
 *
 * The DryadPlayer also holds the layers and command middleware
 * which execute the functionality that the Dryads specify.
 */
export default class DryadPlayer {

  constructor(rootDryad, layers, rootContext = {}) {
    this.middleware = new CommandMiddleware();
    this.classes = {};
    if (layers) {
      layers.forEach((layer) => {
        this.use(layer);
      });
    }

    if (!rootContext.log) {
      rootContext.log = console;
    }

    this.log = rootContext.log;

    this._errorHandler = (error) => {
      this.log.error(error.stack);
    };

    this.setRoot(rootDryad, rootContext);
  }

  /**
   * Set a new tree.
   *
   * Behavior while already playing is not yet defined.
   *
   * @param {Dryad} dryad
   */
  setRoot(dryad, rootContext) {
    if (dryad) {
      let classLookup = _.bind(this.getClass, this);
      this.tree = new DryadTree(this.h(dryad), classLookup, rootContext);
    } else {
      this.tree = null;
    }
  }

  /**
   * Convert hyperscript graph to Dryad objects with registered classes
   *
   * @param {Object} hgraph - JSON style object
   * @returns {Dryad}
   */
  h(hgraph) {
    let classLookup = _.bind(this.getClass, this);
    return hyperscript(hgraph, classLookup);
  }

  /**
   * Add a layer of functionality by registering Dryad classes and command middleware.
   *
   * @param {Object} layer - .classes is a list of Dryad classes, .middleware is a list of middleware functions
   */
  use(layer) {
    this.middleware.use(layer.middleware || []);
    (layer.classes || []).forEach((c) => this.addClass(c));
    return this;
  }

  /**
   * Register a Dryad class so it can be located when used in hyperscript.
   * Also needed if a class uses requireParent()
   *
   * @param {Dryad} dryadClass
   */
  addClass(dryadClass) {
    this.classes[dryadClass.name.toLowerCase()] = dryadClass;
  }

  /**
   * Lookup Dryad class by name.
   *
   * Used by hyperscript and requireParent, this requires
   * that layers and their classes were registered and any custom
   * classes that you right are registered. If you aren't using
   * hyperscript then you don't need to register your class.
   * @param {String} className - case-insensitive
   * @returns {Dryad}
   */
  getClass(className) {
    let dryadClass = this.classes[className.toLowerCase()];
    if (!dryadClass) {
      throw new Error(`Dryad class not found: '${className}' in classes: ${Object.keys(this.classes)}`);
    }
    return dryadClass;
  }

  /**
   * @returns {Promise} - that resolves to `this`
   */
  play(dryad) {
    if (dryad) {
      this.setRoot(dryad);
    }
    let prepTree = this._collectCommands('prepareForAdd');
    let addTree = this._collectCommands('add');
    return this._callPrepare(prepTree)
      .then(() => this._call(addTree, 'add'))
      .then(() => this)
      .catch(this._errorHandler);
  }

  /**
   * @returns {Promise} - that resolves to `this`
   */
  stop() {
    let removeTree = this._collectCommands('remove');
    return this._call(removeTree, 'remove').then(() => this).catch(this._errorHandler);
  }

  _collectCommands(commandName) {
    return this.tree.collectCommands(commandName, this.tree.tree, this);
  }

  /**
   * Execute a prepareForAdd tree of command objects.
   *
   * Values of the command objects are functions may return Promises.
   *
   * @param {Object} prepTree - id, commands, context, children
   * @returns {Promise} - resolves when all Promises in the tree have resolved
   */
  _callPrepare(prepTree) {
    var commands = prepTree.commands || {};
    if (_.isFunction(commands)) {
      commands = commands(prepTree.context);
    }
    return callAndResolveValues(commands, prepTree.context).then((resolved) => {
      // save resolved to that node's context
      // and mark that its $prepared: true for debugging
      this.updateContext(prepTree.context, _.assign({state: {prepare: true}}, resolved));
      let childPromises = prepTree.children.map((childPrep) => this._callPrepare(childPrep));
      return Promise.all(childPromises);
    }, (error) => {
      this.updateContext(prepTree.context, {state: {prepare: false, error: error}});
      return Promise.reject(error);
    });
  }

  /**
   * Execute a command tree using middleware.
   *
   * @returns {Promise}
   */
  _call(commandTree, stateTransitionName) {
    const updateContext = (context, update) => this.tree.updateContext(context.id, update);
    return this.middleware.call(commandTree, stateTransitionName, updateContext);
  }

  /**
   * Execute a single command object for a single node using middleware
   * outside the prepareForAdd/add/remove full tree command execution routine.
   *
   * This can be called out of band from a Dryad's add/remove method
   *
   * Its for commands that need to be executed during runtime
   * in response to events, streams etc.
   * eg. spawning synths from an incoming stream of data.
   */
  callCommand(nodeId, command) {
    return this._call(this.tree.makeCommandTree(nodeId, command), 'callCommand');
  }

  /**
   * updateContext - Allow a Dryad to update its own context.
   *
   * This can be called during runtime by event handlers,
   * updates via stream etc. when you need to save new values into the context
   * outside of the add/remove/update functions.
   *
   * Contexts are immutable - this returns a new context object.
   *
   * @param  {Object} context to update
   * @param  {Object} update  updated variables
   * @return {Object}         new context object
   */
  updateContext(context, update) {
    return this.tree.updateContext(context.id, update);
  }

  /**
   * Get a representation of current state of the tree.
   * Contains add|remove|prepared and may hold errors.
   */
  getDebugState() {
    return this.tree.getDebugState();
  }
}

/**
 * Returns a new object with each value mapped to the called-and-resolved value.
 *
 * For each key/value in commands object,
 * if value is a function then call it
 * if result is a Promise then resolve it.
 *
 * @private
 * @param {Object} commands
 * @returns {Object}
 */
function callAndResolveValues(commands, context) {
  if (_.isEmpty(commands)) {
    return Promise.resolve({});
  }
  const keys = _.keys(commands);
  return Promise.map(keys, (key) => {
    let value = commands[key];
    return Promise.resolve(_.isFunction(value) ? value(context) : value);
  }).then((values) => {
    let result = {};
    keys.forEach((key, i) => {
      result[key] = values[i];
    });
    return result;
  });
}
<|endoftext|>


<commit_before>var dialer =  {
  dialNumber: function(phone_number, successCallback, errorCallback) {
    cordova.exec(
      successCallback, // success callback function
      errorCallback, // error callback function
      'DialerPlugin', // mapped to our native Java class called "Calendar"
      'dialNumber', // with this action name
      [{                  // and this array of custom arguments to create our entry
        "phone_number": phone_number
      }]
    );
  }
}
module.exports = dialer;
<commit_msg>updated js to expose a global var<commit_after>var exec = require("cordova/exec");

function DialerPlugin() {}

// **Initialize speech kit**
//
// `credentialClassName`  The class name to be loaded to retrieve the app id and key
// `serverName`  The hostname of the server to connect to
// `port`  The port number for connection
// `sslEnabled`  True if SSL is enabled for the connection
// `successCallback`  The callback function for success
// `failureCallback`  The callback function for error
DialerPlugin.prototype.dialNumber =function(phone_number, successCallback, errorCallback) {

  exec(
      successCallback, // success callback function
      errorCallback, // error callback function
      'DialerPlugin', // mapped to our native Java class called "Calendar"
      'dialNumber', // with this action name
      [{                  // and this array of custom arguments to create our entry
        "phone_number": phone_number
      }]
    );
  }

});

 var DialerPlugin = new DialerPlugin();
        module.exports = DialerPlugin;<|endoftext|>


<commit_before>// Copyright (C) 2011 - Texas Instruments, Jason Kridner
//
//
var b = require('../main');
var fs = require('fs');
var url = require('url');
var child_process = require('child_process');
var winston = require('winston');
var socketio = require('socket.io');

var debug = process.env.DEBUG ? true : false;

exports.socketJSReqHandler = function(req, res) {
    function sendFile(err, file) {
        if(err) {
            res.writeHead(500, {"Content-Type": "text/plain"});
            res.end(err + '\n');
            return;
        }
        res.setHeader('Content-Type', 'text/javascript');
        file = file.replace(/___INSERT_HOST___/g, host);
        res.end(file);
    }
    var parsedUrl = url.parse(req.url);
    var uri = parsedUrl.pathname;
    var host = 'http://' + req.headers.host;
    if(uri == '/bonescript.js') {
        var filename = __dirname + '/bonescript.js';
        if(debug) winston.debug('filename = ' + filename)
        fs.readFile(filename, 'utf8', sendFile);
    }
}

exports.addSocketListeners = function(server, serverEmitter) {
    var io = socketio(server);
    if(debug) winston.debug('Listening for new socket.io clients');
    io.on('connection', onconnect);
    function onconnect(socket) {
        winston.debug('Client connected');

        serverEmitter.emit('socket$connect', socket);

        // on disconnect
        socket.on('disconnect', function() {
            if(debug) winston.debug('Client disconnected');
            serverEmitter.emit('socket$disconnect');
        });

        socket.on('message', serverMessage);

        spawn(socket);

        var modmsg = {};
        modmsg.module = 'bonescript';
        modmsg.data = {};

        var callMyFunc = function(name, m) {
            var myCallback = function(resp) {
                if(debug) winston.debug(name + ' replied to ' + JSON.stringify(m) + ' with ' + JSON.stringify(resp));
                if(typeof m.seq == 'undefined') return;
                if(!resp || (typeof resp != 'object')) resp = {'data': resp};
                resp.seq = m.seq;
                // TODO: consider setting 'oneshot'
                if(debug) winston.debug('Sending message "bonescript": ' + JSON.stringify(resp));
                socket.emit('bonescript', resp);
            };
            try {
                var callargs = [];
                for(var arg in b[name].args) {
                    var argname = b[name].args[arg];
                    if(argname == 'callback') {
                        if(typeof m.seq == 'number') callargs.push(myCallback);
                        else callargs.push(null);
                    } else if(typeof m[argname] != 'undefined') {
                        callargs.push(m[argname]);
                    } else {
                        callargs.push(undefined);
                    }
                }
                if(debug) winston.debug('Calling ' + name + '(' + callargs.join(',') + ')');
                b[name].apply(this, callargs);
            } catch(ex) {
                if(debug) winston.debug('Error handing ' + name + ' message: ' + ex);
                if(debug) winston.debug('m = ' + JSON.stringify(m));
            }
        };

        var addSocketX = function(message, name) {
            var onFuncMessage = function(m) {
                callMyFunc(name, m);
            };
            socket.on(message, onFuncMessage);
        };

        var b = require('../main');
        for(var i in b) {
            if(typeof b[i] == 'function') {
                if(typeof b[i].args != 'undefined') {
                    modmsg.data[i] = {};
                    modmsg.data[i].name = i;
                    modmsg.data[i].type = 'function';
                    modmsg.data[i].value = b[i].args;
                    addSocketX('bonescript$' + i, i);
                }
            } else {
                modmsg.data[i] = {};
                modmsg.data[i].name = i;
                modmsg.data[i].type = typeof b[i];
                modmsg.data[i].value = b[i];
            }
        }

        socket.emit('require', modmsg);
    }

    function serverMessage(message) {
        serverEmitter.emit('message', message);
    }
}

// most heavily borrowed from https://github.com/itchyny/browsershell
function spawn(socket) {
    var stream = '';
    var timer;
    var len = 0;
    var c;

    socket.on('shell', receive);
    return(receive);

    function receive(msg) {
        if(!c) {
            try {
                if(debug) winston.debug('Spawning bash');
                c = child_process.spawn('/bin/bash', ['-i'], {customFds: [-1, -1, -1]});
                c.stdout.on('data', send);
                c.stderr.on('data', send);
                c.on('exit', function() {
                    socket.emit('shell', send('\nexited\n'));
                    c = undefined;
                });
                socket.on('disconnect', function () {
                    if(debug) winston.debug('Killing bash');
                    c.kill('SIGHUP');
                });
            } catch(ex) {
                c = undefined;
                send('Error invoking bash');
                winston.error('Error invoking bash');
            }
        }
        if(c) {
            if(msg) {
                c.stdin.write(msg + '\n', 'utf-8');
            }
        } else {
            winston.error('Unable to invoke child process');
        }
    }

    function send(data) {
       // add data to the stream
       stream += data.toString();
       ++len;

       // clear any existing timeout if it exists
       if(timer) clearTimeout(timer);

       // set new timeout
       timer = setTimeout(function () {
           socket.emit('shell', stream);
           stream = '';
           len = 0;
       }, 100);

       // send data if over threshold
       if(len > 1000)
       {
           clearTimeout(timer);
           socket.emit('shell', stream);
           stream = '';
           len = 0;
       }
    }
}
<commit_msg>socket_handlers.js: return socket.io object<commit_after>// Copyright (C) 2011 - Texas Instruments, Jason Kridner
//
//
var b = require('../main');
var fs = require('fs');
var url = require('url');
var child_process = require('child_process');
var winston = require('winston');
var socketio = require('socket.io');

var debug = process.env.DEBUG ? true : false;

exports.socketJSReqHandler = function(req, res) {
    function sendFile(err, file) {
        if(err) {
            res.writeHead(500, {"Content-Type": "text/plain"});
            res.end(err + '\n');
            return;
        }
        res.setHeader('Content-Type', 'text/javascript');
        file = file.replace(/___INSERT_HOST___/g, host);
        res.end(file);
    }
    var parsedUrl = url.parse(req.url);
    var uri = parsedUrl.pathname;
    var host = 'http://' + req.headers.host;
    if(uri == '/bonescript.js') {
        var filename = __dirname + '/bonescript.js';
        if(debug) winston.debug('filename = ' + filename)
        fs.readFile(filename, 'utf8', sendFile);
    }
}

exports.addSocketListeners = function(server, serverEmitter) {
    var io = socketio(server);
    if(debug) winston.debug('Listening for new socket.io clients');
    io.on('connection', onconnect);
    function onconnect(socket) {
        winston.debug('Client connected');

        serverEmitter.emit('socket$connect', socket);

        // on disconnect
        socket.on('disconnect', function() {
            if(debug) winston.debug('Client disconnected');
            serverEmitter.emit('socket$disconnect');
        });

        socket.on('message', serverMessage);

        spawn(socket);

        var modmsg = {};
        modmsg.module = 'bonescript';
        modmsg.data = {};

        var callMyFunc = function(name, m) {
            var myCallback = function(resp) {
                if(debug) winston.debug(name + ' replied to ' + JSON.stringify(m) + ' with ' + JSON.stringify(resp));
                if(typeof m.seq == 'undefined') return;
                if(!resp || (typeof resp != 'object')) resp = {'data': resp};
                resp.seq = m.seq;
                // TODO: consider setting 'oneshot'
                if(debug) winston.debug('Sending message "bonescript": ' + JSON.stringify(resp));
                socket.emit('bonescript', resp);
            };
            try {
                var callargs = [];
                for(var arg in b[name].args) {
                    var argname = b[name].args[arg];
                    if(argname == 'callback') {
                        if(typeof m.seq == 'number') callargs.push(myCallback);
                        else callargs.push(null);
                    } else if(typeof m[argname] != 'undefined') {
                        callargs.push(m[argname]);
                    } else {
                        callargs.push(undefined);
                    }
                }
                if(debug) winston.debug('Calling ' + name + '(' + callargs.join(',') + ')');
                b[name].apply(this, callargs);
            } catch(ex) {
                if(debug) winston.debug('Error handing ' + name + ' message: ' + ex);
                if(debug) winston.debug('m = ' + JSON.stringify(m));
            }
        };

        var addSocketX = function(message, name) {
            var onFuncMessage = function(m) {
                callMyFunc(name, m);
            };
            socket.on(message, onFuncMessage);
        };

        var b = require('../main');
        for(var i in b) {
            if(typeof b[i] == 'function') {
                if(typeof b[i].args != 'undefined') {
                    modmsg.data[i] = {};
                    modmsg.data[i].name = i;
                    modmsg.data[i].type = 'function';
                    modmsg.data[i].value = b[i].args;
                    addSocketX('bonescript$' + i, i);
                }
            } else {
                modmsg.data[i] = {};
                modmsg.data[i].name = i;
                modmsg.data[i].type = typeof b[i];
                modmsg.data[i].value = b[i];
            }
        }

        socket.emit('require', modmsg);
    }

    function serverMessage(message) {
        serverEmitter.emit('message', message);
    }

    return(io);
}

// most heavily borrowed from https://github.com/itchyny/browsershell
function spawn(socket) {
    var stream = '';
    var timer;
    var len = 0;
    var c;

    socket.on('shell', receive);
    return(receive);

    function receive(msg) {
        if(!c) {
            try {
                if(debug) winston.debug('Spawning bash');
                c = child_process.spawn('/bin/bash', ['-i'], {customFds: [-1, -1, -1]});
                c.stdout.on('data', send);
                c.stderr.on('data', send);
                c.on('exit', function() {
                    socket.emit('shell', send('\nexited\n'));
                    c = undefined;
                });
                socket.on('disconnect', function () {
                    if(debug) winston.debug('Killing bash');
                    c.kill('SIGHUP');
                });
            } catch(ex) {
                c = undefined;
                send('Error invoking bash');
                winston.error('Error invoking bash');
            }
        }
        if(c) {
            if(msg) {
                c.stdin.write(msg + '\n', 'utf-8');
            }
        } else {
            winston.error('Unable to invoke child process');
        }
    }

    function send(data) {
       // add data to the stream
       stream += data.toString();
       ++len;

       // clear any existing timeout if it exists
       if(timer) clearTimeout(timer);

       // set new timeout
       timer = setTimeout(function () {
           socket.emit('shell', stream);
           stream = '';
           len = 0;
       }, 100);

       // send data if over threshold
       if(len > 1000)
       {
           clearTimeout(timer);
           socket.emit('shell', stream);
           stream = '';
           len = 0;
       }
    }
}
<|endoftext|>


<commit_before>import DataSource, {NetworkSource, NetworkTileSource} from './data_source';
import {MVTSource} from './mvt';
import Geo from '../geo';

// For tiling GeoJSON client-side
import geojsonvt from 'geojson-vt';

/**
 GeoJSON standalone (non-tiled) source
 Uses geojson-vt split into tiles client-side
*/

export class GeoJSONSource extends NetworkSource {

    constructor(source) {
        super(source);
        this.tiled = true;
        this.load_data = null;
        this.tile_indexes = {}; // geojson-vt tile indices, by layer name
        this.max_zoom = Math.max(this.max_zoom || 0, 15); // TODO: max zoom < 15 causes artifacts/no-draw at 20, investigate
        this.pad_scale = 0; // we don't want padding on auto-tiled sources
    }

    _load(dest) {
        if (!this.load_data) {
            this.load_data = super._load({ source_data: { layers: {} } }).then(data => {
                let layers = data.source_data.layers;
                for (let layer_name in layers) {
                    this.tile_indexes[layer_name] = geojsonvt(layers[layer_name], {
                        maxZoom: this.max_zoom,  // max zoom to preserve detail on
                        tolerance: 3, // simplification tolerance (higher means simpler)
                        extent: Geo.tile_scale, // tile extent (both width and height)
                        buffer: 0     // tile buffer on each side
                    });
                }

                this.loaded = true;
                return data;
            });
        }

        return this.load_data.then(() => {
            for (let layer_name in this.tile_indexes) {
                dest.source_data.layers[layer_name] = this.getTileFeatures(dest, layer_name);
            }
            return dest;
        });
    }

    getTileFeatures(tile, layer_name) {
        let coords = Geo.wrapTile(tile.coords, { x: true });

        // request a particular tile
        let t = this.tile_indexes[layer_name].getTile(coords.z, coords.x, coords.y);

        // Convert from MVT-style JSON struct to GeoJSON
        let collection;
        if (t && t.features) {
            collection = {
                type: 'FeatureCollection',
                features: []
            };

            for (let feature of t.features) {
                // GeoJSON feature
                let f = {
                    type: 'Feature',
                    geometry: {},
                    properties: feature.tags
                };

                if (feature.type === 1) {
                    f.geometry.coordinates = feature.geometry.map(coord => [coord[0], coord[1]]);
                    f.geometry.type = 'MultiPoint';
                }
                else if (feature.type === 2 || feature.type === 3) {
                    f.geometry.coordinates = feature.geometry.map(ring =>
                        ring.map(coord => [coord[0], coord[1]])
                    );

                    if (feature.type === 2) {
                        f.geometry.type = 'MultiLineString';
                    }
                    else  {
                        f.geometry = MVTSource.decodeMultiPolygon(f.geometry); // un-flatten rings
                    }
                }
                else {
                    continue;
                }

                collection.features.push(f);
            }
        }

        return collection;
    }

    formatUrl (dest) {
        return this.url;
    }

    parseSourceData (tile, source, response) {
        source.layers = this.getLayers(JSON.parse(response));
    }

    // Detect single or multiple layers in returned data
    getLayers (data) {
        if (data.type === 'Feature' || data.type === 'FeatureCollection') {
            return { _default: data };
        }
        else {
            return data;
        }
    }

}

/**
 Mapzen/OSM.US-style GeoJSON vector tiles
 @class GeoJSONTileSource
*/
export class GeoJSONTileSource extends NetworkTileSource {

    constructor(source) {
        super(source);

        // Check for URL tile pattern, if not found, treat as standalone GeoJSON/TopoJSON object
        if (!this.urlHasTilePattern(this.url)) {
            // Check instance type from parent class
            if (this instanceof GeoJSONTileSource) {
                // Replace instance type
                return new GeoJSONSource(source);
            }
            else {
                // Pass back to parent class to instantiate
                return null;
            }
        }
        return this;
    }

    parseSourceData (tile, source, response) {
        let data = JSON.parse(response);
        this.prepareGeoJSON(data, tile, source);
    }

    prepareGeoJSON (data, tile, source) {
        // Apply optional data transform
        if (typeof this.transform === 'function') {
            data = this.transform(data, this.extra_data);
        }

        source.layers = GeoJSONSource.prototype.getLayers(data);

        // A "synthetic" tile that adjusts the tile min anchor to account for tile longitude wrapping
        let anchor = {
            coords: tile.coords,
            min: Geo.metersForTile(Geo.wrapTile(tile.coords, { x: true }))
        };

        DataSource.projectData(source); // mercator projection
        DataSource.scaleData(source, anchor); // re-scale from meters to local tile coords
    }

}

DataSource.register(GeoJSONTileSource, 'GeoJSON');      // prefered shorter name
DataSource.register(GeoJSONTileSource, 'GeoJSONTiles'); // for backwards-compatibility
<commit_msg>Add support for GeoJSON polygon centroid labeling<commit_after>import DataSource, {NetworkSource, NetworkTileSource} from './data_source';
import {MVTSource} from './mvt';
import Geo from '../geo';

// For tiling GeoJSON client-side
import geojsonvt from 'geojson-vt';

/**
 GeoJSON standalone (non-tiled) source
 Uses geojson-vt split into tiles client-side
*/

export class GeoJSONSource extends NetworkSource {

    constructor(source) {
        super(source);
        this.tiled = true;
        this.load_data = null;
        this.tile_indexes = {}; // geojson-vt tile indices, by layer name
        this.max_zoom = Math.max(this.max_zoom || 0, 15); // TODO: max zoom < 15 causes artifacts/no-draw at 20, investigate
        this.pad_scale = 0; // we don't want padding on auto-tiled sources
    }

    _load(dest) {
        if (!this.load_data) {
            this.load_data = super._load({ source_data: { layers: {} } }).then(data => {
                let layers = data.source_data.layers;
                for (let layer_name in layers) {
                    this.tile_indexes[layer_name] = geojsonvt(layers[layer_name], {
                        maxZoom: this.max_zoom,  // max zoom to preserve detail on
                        tolerance: 3, // simplification tolerance (higher means simpler)
                        extent: Geo.tile_scale, // tile extent (both width and height)
                        buffer: 0     // tile buffer on each side
                    });
                }

                this.loaded = true;
                return data;
            });
        }

        return this.load_data.then(() => {
            for (let layer_name in this.tile_indexes) {
                dest.source_data.layers[layer_name] = this.getTileFeatures(dest, layer_name);
            }
            return dest;
        });
    }

    getTileFeatures(tile, layer_name) {
        let coords = Geo.wrapTile(tile.coords, { x: true });

        // request a particular tile
        let t = this.tile_indexes[layer_name].getTile(coords.z, coords.x, coords.y);

        // Convert from MVT-style JSON struct to GeoJSON
        let collection;
        if (t && t.features) {
            collection = {
                type: 'FeatureCollection',
                features: []
            };

            for (let feature of t.features) {
                // GeoJSON feature
                let f = {
                    type: 'Feature',
                    geometry: {},
                    properties: feature.tags
                };

                if (feature.type === 1) {
                    f.geometry.coordinates = feature.geometry.map(coord => [coord[0], coord[1]]);
                    f.geometry.type = 'MultiPoint';
                }
                else if (feature.type === 2 || feature.type === 3) {
                    f.geometry.coordinates = feature.geometry.map(ring =>
                        ring.map(coord => [coord[0], coord[1]])
                    );

                    if (feature.type === 2) {
                        f.geometry.type = 'MultiLineString';
                    }
                    else  {
                        f.geometry = MVTSource.decodeMultiPolygon(f.geometry); // un-flatten rings
                    }
                }
                else {
                    continue;
                }

                collection.features.push(f);
            }
        }

        return collection;
    }

    formatUrl (dest) {
        return this.url;
    }

    parseSourceData (tile, source, response) {
        let parsed_response = JSON.parse(response);
        this.preprocessFeatures(parsed_response.features);
        source.layers = this.getLayers(parsed_response);
    }

    // Preprocess features. Currently used to add a new "centroid" feature for polygon labeling
    preprocessFeatures (features){
        // Define centroids for polygons for centroid label placement
        // Avoids redundant label placement for each generated tile at higher zoom levels
        var features_centroid = [];
        var label_key = 'label_centroid';

        features.forEach(feature => {
            let coordinates;
            switch (feature.geometry.type) {
                case 'Polygon':
                    coordinates = feature.geometry.coordinates;
                    features_centroid.push(getCentroidFeatureForPolygon(coordinates, feature.properties));
                    break;
                case 'MultiPolygon':
                    coordinates = feature.geometry.coordinates;
                    for (let i = 0; i < coordinates.length; i++) {
                        features_centroid.push(getCentroidFeatureForPolygon(coordinates[i], feature.properties));
                    }
                    break;
            }
        });

        // create centroid point feature from polygon coordinates and provided feature meta-data
        function getCentroidFeatureForPolygon (coordinates, properties) {
            let centroid = Geo.centroid(coordinates[0]);

            // clone properties and add {label_key : "yes"}
            let centroid_properties = Object.create(properties);
            centroid_properties[label_key] = "yes";

            return {
                type: "Feature",
                properties: centroid_properties,
                geometry: {
                    type: "Point",
                    coordinates: centroid
                }
            };
        }

        // append centroid features to features array
        Array.prototype.push.apply(features, features_centroid);

        return features;
    }

    // Detect single or multiple layers in returned data
    getLayers (data) {
        if (data.type === 'Feature' || data.type === 'FeatureCollection') {
            return { _default: data };
        }
        else {
            return data;
        }
    }

}

/**
 Mapzen/OSM.US-style GeoJSON vector tiles
 @class GeoJSONTileSource
*/
export class GeoJSONTileSource extends NetworkTileSource {

    constructor(source) {
        super(source);

        // Check for URL tile pattern, if not found, treat as standalone GeoJSON/TopoJSON object
        if (!this.urlHasTilePattern(this.url)) {
            // Check instance type from parent class
            if (this instanceof GeoJSONTileSource) {
                // Replace instance type
                return new GeoJSONSource(source);
            }
            else {
                // Pass back to parent class to instantiate
                return null;
            }
        }
        return this;
    }

    parseSourceData (tile, source, response) {
        let data = JSON.parse(response);
        this.prepareGeoJSON(data, tile, source);
    }

    prepareGeoJSON (data, tile, source) {
        // Apply optional data transform
        if (typeof this.transform === 'function') {
            data = this.transform(data, this.extra_data);
        }

        source.layers = GeoJSONSource.prototype.getLayers(data);

        // A "synthetic" tile that adjusts the tile min anchor to account for tile longitude wrapping
        let anchor = {
            coords: tile.coords,
            min: Geo.metersForTile(Geo.wrapTile(tile.coords, { x: true }))
        };

        DataSource.projectData(source); // mercator projection
        DataSource.scaleData(source, anchor); // re-scale from meters to local tile coords
    }

}

DataSource.register(GeoJSONTileSource, 'GeoJSON');      // prefered shorter name
DataSource.register(GeoJSONTileSource, 'GeoJSONTiles'); // for backwards-compatibility
<|endoftext|>


<commit_before>(function(){
	'use strict';

	function showComponents(locale){
		console.log("\n");
		Object.keys(locale).map(function(key){
			console.log(key + " : " + locale[key]);
			if (key === "RightNow"){
				console.log("----------------------------------");
			}
		});
		console.log("-------------------------------------\n");
	}

	var checkInterval = 5000;

	var moment = require('moment');

	function getLocaleData(now){

		var locale = {
			RightNow: now.format('llll'),
			year: now.year(),
			month: now.month(),
			day: now.day(),
			hour: now.hour(),
			minute: now.minute(),
			second: now.second()
		};
		return locale;
	}

	function checkIfEventHasOccurred(now, recordedMoment){
		if (now.second() !== recordedMoment.second()){
			return false;
		}
		else if(now.minute() !== recordedMoment.minute()){
			return false;
		}
		else if(now.hour() !== recordedMoment.hour()){
			return false;
		}
		else if(now.day() !== recordedMoment.day()){
			return false;
		}
		else if(now.month() !== recordedMoment.month()){
			return false;
		}
		else if(now.year() !== recordedMoment.year()){
			return false;
		}
		else{
			return true;
		}
	}

	var now = moment();
	showComponents(getLocaleData(now));

	var intervals = [10, 15, 20];

	var events = intervals.map(function(value){
		return now.clone().add(value.toString(), 'seconds');
	});

	events.forEach(function(element, index){
		setInterval(function(){
			if (checkIfEventHasOccurred(moment(), element)){
				showComponents(getLocaleData(element));
			}
		}, checkInterval);
	});

	//var CoapClient = require('./CoapClient');
	//CoapClient.command();
})();
<commit_msg>Custom events as JSON objects added to 'calendar.js'<commit_after>/*
** Filename - 'calendar.js'
** Author - 'Sayantan Hore'
** Created on - '07.12.2015'
** Description - 'This file holds the calendar events to listen to and fire calls CoAP client with required params'
*/

(function(){
	'use strict';

	var checkInterval = 5000;

	var moment = require('moment');

	function getLocaleData(now){

		var locale = {
			RightNow: now.format('llll'),
			year: now.year(),
			month: now.month(),
			day: now.day(),
			hour: now.hour(),
			minute: now.minute(),
			second: now.second()
		};
		return locale;
	}

	function checkIfEventHasOccurred(now, recordedMoment){
		return now.isSame(recordedMoment, 'second') && now.isSame(recordedMoment, 'minute') && now.isSame(recordedMoment, 'hour') && now.isSame(recordedMoment, 'day') && now.isSame(recordedMoment, 'month') && now.isSame(recordedMoment, 'year');
	}

	var now = moment();
	console.log("Waiting ...");

	var events = [];

	events.push({
		name: 'BLINK_LED',
		flash: true,
		startTime: now.clone().add('5', 'seconds'),
		targetDevice: 'led',
		msg: 'Led lamp colour changes'
	});
	events.push({
		name: 'GLOW_LED',
		flash: false,
		startTime: now.clone().add('10', 'seconds'),
		duration: 5,
		targetDevice: 'led',
		startMsg: 'Led lamp colour changes',
		endMsg: 'Led lamp colour changes'
	})
	events.push({
		name: 'GLOW_LED',
		flash: false,
		startTime: now.clone().add('20', 'seconds'),
		duration: 5,
		targetDevice: 'led',
		startMsg: 'Led lamp colour changes',
		endMsg: 'Led lamp colour changes'
	});

	events.forEach(function(element, index){
		setInterval(function(){
			if (checkIfEventHasOccurred(moment(), element.startTime)){
				console.log("----------------------------------");
				console.log("Event " + element.name + " started");
				console.log(JSON.stringify(element, null, 2));
				setTimeout(function(){
					console.log("Event " + element.name + " ended");
					console.log("----------------------------------");
				}, element.duration * 1000);
			}
		}, checkInterval);
	});

	//var CoapClient = require('./CoapClient');
	//CoapClient.command();
})();
<|endoftext|>


<commit_before>'use strict';

var request = require('request')
    , logger = require('./Logger');

/**
 * Responsável por exibir os logs referentes as interceptações feitas.
 *
 * @param configuration
 * @constructor
 */
function InterceptorLogger(configuration) {

    var self = this;

    self.log = function () {
        logger.log.apply(logger.log, arguments);
    };

    self.success = function () {
        if (configuration.colors()) {
            logger.success.apply(logger.success, arguments);
        } else {
            logger.log.apply(logger.log, arguments);
        }
    };

    self.info = function () {
        if (configuration.colors()) {
            logger.info.apply(logger.info, arguments);
        } else {
            logger.log.apply(logger.log, arguments);
        }
    };

    self.error = function () {
        if (configuration.colors()) {
            logger.error.apply(logger.error, arguments);
        } else {
            logger.log.apply(logger.log, arguments);
        }
    };
}

/**
 * Responsável por todos os tratamentos referentes as rotas.
 *
 * @constructor
 */
function Interceptor(app, express, configuration) {

    var self = this
        , _interceptorLogger = new InterceptorLogger(configuration)
        , _headers = configuration.headers()
        , _baseRequest = _headers ? request.defaults({headers: _headers, jar: true}) : request;

    var _bindObjects = function () {
        configuration.bindObjects().forEach(function (bind) {
            app.use(bind.uri, express.static(configuration.rootFolder() + bind.path));
        });
    };

    var _bindOverrideResponses = function () {
        var overrideResponses = configuration.overrideResponses();

        for (var uri in overrideResponses) {
            var overrides = overrideResponses[uri];

            overrides.forEach(function (override) {

                app.use(uri, function (req, res) {

                    var url = configuration.host() + uri
                        , field = override.field
                        , value = override.value;

                    var _request = _baseRequest(url, function (err, response, body) {

                        body = JSON.parse(body);

                        if (field.indexOf('.') > -1) {

                            var splittedField = field.split('.');
                            var lastField = splittedField[splittedField.length - 1];

                            splittedField.reduce(function (object, property) {
                                if (object && property) {

                                    if (property == lastField) {
                                        object[property] = value;
                                    }

                                    return object[property];
                                }
                            }, body);
                        } else {

                            body[field] = value;
                        }

                        res.json(body);

                        return body;
                    });

                    req.pipe(_request);
                });
            });
        }
    };

    var _makeRequests = function () {

        app.use('/', function (req, response) {

            var url = configuration.host() + req.url;
            var requestStartTime = new Date();

            var _request = _baseRequest(url, {timeout: configuration.timeout()}, function (error, response, body) {
                if (configuration.printRequestUrl()) {
                    _interceptorLogger.info('\n', '------------------------------------');

                    var statusCode = response.statusCode
                        , requestEndTime = (new Date() - requestStartTime) + 'ms'
                        , header = '(' + response.statusCode + ') ' + requestEndTime;

                    if (statusCode < 400) {
                        _interceptorLogger.success(header);
                    } else {
                        _interceptorLogger.error(header);
                    }

                    _interceptorLogger.info("URL: " + url);
                }

                if (configuration.printRequestError() && error) {
                    _interceptorLogger.error(error);
                }

                if (configuration.printResponse() && body) {
                    _interceptorLogger.log('Response');
                    _interceptorLogger.info(body);
                }
            });

            req
                .pipe(_request)
                .pipe(response);
        });
    };

    self.intercept = function () {
        _bindObjects();
        _bindOverrideResponses();
        _makeRequests();
    };
}

module.exports = Interceptor;
<commit_msg>Usign decodeURI<commit_after>'use strict';

var request = require('request')
    , logger = require('./Logger');

/**
 * Responsável por exibir os logs referentes as interceptações feitas.
 *
 * @param configuration
 * @constructor
 */
function InterceptorLogger(configuration) {

    var self = this;

    self.log = function () {
        logger.log.apply(logger.log, arguments);
    };

    self.success = function () {
        if (configuration.colors()) {
            logger.success.apply(logger.success, arguments);
        } else {
            logger.log.apply(logger.log, arguments);
        }
    };

    self.info = function () {
        if (configuration.colors()) {
            logger.info.apply(logger.info, arguments);
        } else {
            logger.log.apply(logger.log, arguments);
        }
    };

    self.error = function () {
        if (configuration.colors()) {
            logger.error.apply(logger.error, arguments);
        } else {
            logger.log.apply(logger.log, arguments);
        }
    };
}

/**
 * Responsável por todos os tratamentos referentes as rotas.
 *
 * @constructor
 */
function Interceptor(app, express, configuration) {

    var self = this
        , _interceptorLogger = new InterceptorLogger(configuration)
        , _headers = configuration.headers()
        , _baseRequest = _headers ? request.defaults({headers: _headers, jar: true}) : request;

    var _bindObjects = function () {
        configuration.bindObjects().forEach(function (bind) {
            app.use(bind.uri, express.static(configuration.rootFolder() + bind.path));
        });
    };

    var _bindOverrideResponses = function () {
        var overrideResponses = configuration.overrideResponses();

        for (var uri in overrideResponses) {
            var overrides = overrideResponses[uri];

            overrides.forEach(function (override) {

                app.use(uri, function (req, res) {

                    var url = configuration.host() + uri
                        , field = override.field
                        , value = override.value;

                    var _request = _baseRequest(url, function (err, response, body) {

                        body = JSON.parse(body);

                        if (field.indexOf('.') > -1) {

                            var splittedField = field.split('.');
                            var lastField = splittedField[splittedField.length - 1];

                            splittedField.reduce(function (object, property) {
                                if (object && property) {

                                    if (property == lastField) {
                                        object[property] = value;
                                    }

                                    return object[property];
                                }
                            }, body);
                        } else {

                            body[field] = value;
                        }

                        res.json(body);

                        return body;
                    });

                    req.pipe(_request);
                });
            });
        }
    };

    var _makeRequests = function () {

        app.use('/', function (req, response) {

            var url = configuration.host() + req.url;
            var requestStartTime = new Date();

            var _request = _baseRequest(url, {timeout: configuration.timeout()}, function (error, response, body) {
                if (configuration.printRequestUrl()) {
                    _interceptorLogger.info('\n', '------------------------------------');

                    var statusCode = response.statusCode
                        , requestEndTime = (new Date() - requestStartTime) + 'ms'
                        , header = '(' + response.statusCode + ') ' + requestEndTime;

                    if (statusCode < 400) {
                        _interceptorLogger.success(header);
                    } else {
                        _interceptorLogger.error(header);
                    }

                    _interceptorLogger.info("URL: " + decodeURI(url));
                }

                if (configuration.printRequestError() && error) {
                    _interceptorLogger.error(error);
                }

                if (configuration.printResponse() && body) {
                    _interceptorLogger.log('Response');
                    _interceptorLogger.info(body);
                }
            });

            req
                .pipe(_request)
                .pipe(response);
        });
    };

    self.intercept = function () {
        _bindObjects();
        _bindOverrideResponses();
        _makeRequests();
    };
}

module.exports = Interceptor;
<|endoftext|>


<commit_before>/*
** Filename - 'calendar.js'
** Author - 'Sayantan Hore'
** Created on - '07.12.2015'
** Description - 'This file holds the calendar events to listen to and calls CoAP client with required params'
*/

'use strict';
/*
** Declares a default interval value
*/
var checkInterval = 5000;

/*
** Requires moment
*/
var moment = require('moment');

/*
** Creates a new moment right now
*/
module.exports.createNewMoment = function(){
	return moment();
}

/*
** Creates new hard coded events
*/
module.exports.loadEvents = function(now){
	var fs = require('fs');
	var events = [];
	try {
		events = JSON.parse(fs.readFileSync('events.json', 'utf8')).events;
	} catch(e) {
		console.log(e);
	}
	return events;
}

/*
** Checks if a scheduled event has occured by comparing the moment of the event with a new moment created just now
** Parameters checked : second, minute, hour, day, month, year
*/
function checkIfEventHasOccurred(now, recordedMoment){
	return now.isSame(recordedMoment, 'second') && now.isSame(recordedMoment, 'minute') && now.isSame(recordedMoment, 'hour') && now.isSame(recordedMoment, 'day') && now.isSame(recordedMoment, 'month') && now.isSame(recordedMoment, 'year');
}

/*
** Schedules events
*/
module.exports.scheduleEvents = function(events){
	events.forEach(function(element, index){
		setInterval(function(){
			if (checkIfEventHasOccurred(moment(), element.startTime)){
				element.triggered = true;
				console.log("----------------------------------");
				console.log("Event " + element.name + " started");
				console.log(JSON.stringify(element, null, 2));
				setTimeout(function(){
					console.log("Event " + element.name + " ended");
					console.log("----------------------------------");
				}, element.duration * 1000);
			}
		}, checkInterval);
	});
}

/*
** Extracts date and time components and encapsulates in an object
*/
module.exports.getLocaleData = function(momentObj){
	if (!momentObj.isValid()){
		throw TypeError;
	}
	else{
		var locale = {
			RightNow: momentObj.format('llll'),
			year: momentObj.year(),
			month: momentObj.month(),
			day: momentObj.day(),
			hour: momentObj.hour(),
			minute: momentObj.minute(),
			second: momentObj.second()
		};
		return locale;
	}
}
<commit_msg>Minor changes in documentation in 'calendar.js'<commit_after>/*
** Filename - 'calendar.js'
** Author - 'Sayantan Hore'
** Created on - '07.12.2015'
** Description - 'This file holds the calendar events to listen to and calls CoAP client with required params'
*/

'use strict';
/*
** Declares a default interval value
*/
var checkInterval = 5000;

/*
** Requires moment
*/
var moment = require('moment');

/*
** Creates a new moment right now
*/
module.exports.createNewMoment = function(){
	return moment();
}

/*
** Load events
*/
module.exports.loadEvents = function(now){
	var fs = require('fs');
	var events = [];
	try {
		events = JSON.parse(fs.readFileSync('events.json', 'utf8')).events;
	} catch(e) {
		console.log(e);
	}
	return events;
}

/*
** Checks if a scheduled event has occured by comparing the moment of the event with a new moment created just now
** Parameters checked : second, minute, hour, day, month, year
*/
function checkIfEventHasOccurred(now, recordedMoment){
	return now.isSame(recordedMoment, 'second') && now.isSame(recordedMoment, 'minute') && now.isSame(recordedMoment, 'hour') && now.isSame(recordedMoment, 'day') && now.isSame(recordedMoment, 'month') && now.isSame(recordedMoment, 'year');
}

/*
** Schedules events
*/
module.exports.scheduleEvents = function(events){
	events.forEach(function(element, index){
		setInterval(function(){
			if (checkIfEventHasOccurred(moment(), element.startTime)){
				element.triggered = true;
				console.log("----------------------------------");
				console.log("Event " + element.name + " started");
				console.log(JSON.stringify(element, null, 2));
				setTimeout(function(){
					console.log("Event " + element.name + " ended");
					console.log("----------------------------------");
				}, element.duration * 1000);
			}
		}, checkInterval);
	});
}

/*
** Extracts date and time components and encapsulates in an object
*/
module.exports.getLocaleData = function(momentObj){
	if (!momentObj.isValid()){
		throw TypeError;
	}
	else{
		var locale = {
			RightNow: momentObj.format('llll'),
			year: momentObj.year(),
			month: momentObj.month(),
			day: momentObj.day(),
			hour: momentObj.hour(),
			minute: momentObj.minute(),
			second: momentObj.second()
		};
		return locale;
	}
}
<|endoftext|>


<commit_before>import "babel/polyfill";
import Must from "assert";
import log from "./logger";
import * as functionRegistry from "./functionRegistry";

class Boundary {
    constructor(value) {
        this.value = value;
        this.name = this.constructor.name;
    }
    toString() {
        return `|${this.value}|@${this.name}`;
    }
    reached() {
        throw new Error(`Not implemented by ${this.constructor.name}`);
    }
    isQuote() {
        return /["'`]/.test(this.value);
    }
}

class BoundaryDot extends Boundary {
    reached() {
        return true;
    }
}

class BoundarySpace extends Boundary {
    reached(inputStream) {
        return inputStream.sawWordBoundary();
    }
}

class BoundaryLine extends Boundary {
    reached(inputStream) {
        return inputStream.sawNewLine();
    }
}

class BoundaryParagraph extends Boundary {
    reached(inputStream) {
        return (inputStream.sawNewLine() && inputStream.peek().consume(/\n/)) || inputStream.atEnd();
    }
}

class BoundaryEOF extends Boundary {
    reached(inputStream) {
        return inputStream.atEnd();
    }
}

const Borders = {
    '{': '}',
    '[': ']',
    '<': '>'
};
class BoundaryOther extends Boundary {
    reached(inputStream) {
        let delimiter = Borders[this.value] ? Borders[this.value] : this.value;
        return inputStream.consume(delimiter);
    }
}

function tag(callName, args, functionBody) {
    let htmlArgs = Object.keys(args.byName).map(argName => {
            return `${argName}="${args.byName[argName].replace(/"/g, "\"")}"`;
        }).join(" ");

    let result = `<${callName}${htmlArgs.length > 0 ? " " : ""}${htmlArgs}>${functionBody}</${callName}>`;
    return result;
}

let interpretCallId = 0;
export default class Interpreter {
    constructor(inputStream) {
        this.inputStream = inputStream;
        this.result = "";
    }

    // Boundary is an optional parameter
    interpret(boundary, callFunctions = true) {
        interpretCallId++;
        let myInterpretCallId = interpretCallId;
        let reachedBoundary = () => {
            if (boundary && boundary.reached(this.inputStream)) {
                log("Reached boundary:", boundary);
                return true;
            } else {
                return false || this.inputStream.atEnd();
            }
        };

        log("Started interpret while loop", myInterpretCallId, "with boundary", boundary);

        while (!reachedBoundary()) {
            log("\niteration starting: ", myInterpretCallId, "with stream", this.inputStream.id);

            if (this.inputStream.atEnd() && boundary) {
                this.inputStream.croak("Unbounded function call");
            }

            let peekingStream = this.inputStream.peek();
            if (callFunctions && peekingStream.consumeDot() && peekingStream.consumeWord()) {
                // this is a function call

                log("found function call");

                this.inputStream.sync(peekingStream);
                const functionName = this.inputStream.consumed;

                this.interpretFunctionCall(functionName);
            }
            // Just text (one char at a time), not a function call
            else {
                this.inputStream.consumeChar();
                this.interpretText(this.inputStream.consumed);
            }

            log("iteration ending: ", myInterpretCallId, "with stream", this.inputStream.id, "\n");
        }

        log("Finished interpret while loop", myInterpretCallId);

        return this.result;
    }

    interpretText(char) {
        log(`Interpret text called with |${char.replace(/\n/g, "\\n")}|`);
        this.result += char;
    }

    callFunction(name, boundary, args) {
        let position = this.inputStream.char;
        log(`calling ${name}, with ${boundary}`);
        try {
            let interpreter = new Interpreter(this.inputStream);
            let functionBody = interpreter.intelligentInterpret(boundary);

            this.result += functionRegistry.exists(name) ?
                functionRegistry.get(name)(name, args, functionBody, interpreter) :
                tag(name, args, functionBody, interpreter);
        }
        catch (e) {
            console.log(`In function ${name} (started at ${position}), at ${this.inputStream.char}:`);
            throw e;
        }
    }

    callFunctionWithBody(name, args, body) {
        let position = this.inputStream.char;
        log(`calling ${name}, with body: ${body}`);
        try {
            return functionRegistry.exists(name) ?
                functionRegistry.get(name)(name, args, body, this) :
                tag(name, args, body, this);
        }
        catch (e) {
            console.log(`In function ${name} (started at ${position}), at ${this.inputStream.char}:`);
            throw e;
        }
    }

    /*
     * If boundary is consumed, returns Boundary,
     * otherwise, returns null *without side effects*.
     */
    consumeBoundary() {
        let peekingStream = this.inputStream.peek();
        let boundary;

        if (peekingStream.consume(".")) {
            boundary = new BoundaryDot(peekingStream.consumed);
        }
        else if (peekingStream.consume(/\s/)) {
            boundary = new BoundarySpace(peekingStream.consumed);
        }
        else if (peekingStream.consume(/:+/)) {
            let consumed = peekingStream.consumed;
            let colonCount = consumed.length;

            if (!peekingStream.consume(/ |(?:\n)/)) {
                peekingStream.croak("':' sequences must end with whitespace");
            }

            consumed += peekingStream.consumed;

            if (colonCount === 1) {
                boundary = new BoundaryLine(consumed);
            }
            else if (colonCount === 2) {
                boundary = new BoundaryParagraph(consumed);
            }
            else if (colonCount === 3) {
                boundary = new BoundaryEOF(consumed);
            }
            else {
                peekingStream.croak("More than three colons in a colon boundary");
            }
        }
        else if (peekingStream.consume(/[{}<>[\]`"'/@#$%|]/)) {
            if (/}]>/.test(peekingStream.consumed)) {
                peekingStream.croak(`Prohibited boundary ${peekingStream.consumed}`);
            }
            boundary = new BoundaryOther(peekingStream.consumed);
        }
        else {
            return null;
        }

        Must(boundary);
        this.inputStream.sync(peekingStream);
        return boundary;
    }

    parseArgument() {
        let name;
        let value;

        this.inputStream.consumeOptionalWhitespace();
        let peekingStream = this.inputStream.peek();
        if (peekingStream.consumeWord() && peekingStream.consumeOptionalWhitespace() && peekingStream.consume("=")) {
            // Named argument
            Must(this.inputStream.consumeWord());
            name = this.inputStream.consumed;

            Must(this.inputStream.consumeOptionalWhitespace());
            Must(this.inputStream.consume("="));
            Must(this.inputStream.consumeOptionalWhitespace());
        }
        // else anonymous argument

        // We cannot prohibit empty arguments by checking for input stream
        // progress
        let boundary = this.consumeBoundary() || new BoundarySpace(" ");

        let interpreter = new Interpreter(this.inputStream);

        log("parsing Argument", name);
        value = interpreter.intelligentInterpret(boundary);

        this.inputStream.consumeOptionalWhitespace();

        return { name, value };
    }

    interpretFunctionCall(name) {
        let args = [];
        args.byName = {};

        if (this.inputStream.consume("(")) {
            // we have an arguments block in this function

            while (!this.inputStream.consume(")")) {
                let argument = this.parseArgument();
                args.push(argument);
                if (argument.name) args.byName[argument.name] = argument.value;

                if (this.inputStream.consume(",")) {
                    continue;
                }
                else if (this.inputStream.consume(")")) {
                    break;
                }
                else {
                    this.inputStream.croak("Expecting comma or closing parenthese in an argument list");
                }
            }
        }

        // Allow a space between the function call and the first boundary:
        // .foo { } is equivalent to .foo{}
        Must(this.inputStream.consumeOptionalWhitespace());
        let whitespaceBoundary = this.inputStream.consumed ?
            new BoundarySpace(this.inputStream.consumed) : null;
        let boundary = this.consumeBoundary() || whitespaceBoundary;

        if (!boundary) this.inputStream.croak("Missing function call boundary");

        this.callFunction(name, boundary, args);
        this.inputStream.sawFunctionCall = true;
    }

    intelligentInterpret(boundary) {
        return boundary.isQuote() ?
            this.interpretFlat(boundary) :
            this.interpretRecursive(boundary);
    }

    interpretRecursive(boundary) {
        return this.interpret(boundary);
    }

    interpretFlat(boundary) {
        return this.interpret(boundary, false);
    }
}<commit_msg>Correctly handle end of file 'unbounded function call' error.<commit_after>import "babel/polyfill";
import Must from "assert";
import log from "./logger";
import * as functionRegistry from "./functionRegistry";

class Boundary {
    constructor(value) {
        this.value = value;
        this.name = this.constructor.name;
    }
    toString() {
        return `|${this.value}|@${this.name}`;
    }
    reached() {
        throw new Error(`Not implemented by ${this.constructor.name}`);
    }
    isQuote() {
        return /["'`]/.test(this.value);
    }
}

class BoundaryDot extends Boundary {
    reached() {
        return true;
    }
}

class BoundarySpace extends Boundary {
    reached(inputStream) {
        return inputStream.sawWordBoundary();
    }
}

class BoundaryLine extends Boundary {
    reached(inputStream) {
        return inputStream.sawNewLine();
    }
}

class BoundaryParagraph extends Boundary {
    reached(inputStream) {
        return (inputStream.sawNewLine() && inputStream.peek().consume(/\n/)) || inputStream.atEnd();
    }
}

class BoundaryEOF extends Boundary {
    reached(inputStream) {
        return inputStream.atEnd();
    }
}

const Borders = {
    '{': '}',
    '[': ']',
    '<': '>'
};
class BoundaryOther extends Boundary {
    reached(inputStream) {
        let delimiter = Borders[this.value] ? Borders[this.value] : this.value;
        return inputStream.consume(delimiter);
    }
}

function tag(callName, args, functionBody) {
    let htmlArgs = Object.keys(args.byName).map(argName => {
            return `${argName}="${args.byName[argName].replace(/"/g, "\"")}"`;
        }).join(" ");

    let result = `<${callName}${htmlArgs.length > 0 ? " " : ""}${htmlArgs}>${functionBody}</${callName}>`;
    return result;
}

let interpretCallId = 0;
export default class Interpreter {
    constructor(inputStream) {
        this.inputStream = inputStream;
        this.result = "";
    }

    // Boundary is an optional parameter
    interpret(boundary, callFunctions = true) {
        interpretCallId++;
        let myInterpretCallId = interpretCallId;
        let reachedBoundary = () => {
            if (boundary && boundary.reached(this.inputStream)) {
                log("Reached boundary:", boundary);
                return true;
            } else {
                return false;
            }
        };

        log("Started interpret while loop", myInterpretCallId, "with boundary", boundary);

        while (!reachedBoundary()) {
            log("\niteration starting: ", myInterpretCallId, "with stream", this.inputStream.id);

            if (this.inputStream.atEnd()) {
                if (boundary) this.inputStream.croak("Unbounded function call");

                break;
            }

            let peekingStream = this.inputStream.peek();
            if (callFunctions && peekingStream.consumeDot() && peekingStream.consumeWord()) {
                // this is a function call

                log("found function call");

                this.inputStream.sync(peekingStream);
                const functionName = this.inputStream.consumed;

                this.interpretFunctionCall(functionName);
            }
            // Just text (one char at a time), not a function call
            else {
                Must(this.inputStream.consumeChar());
                this.interpretText(this.inputStream.consumed);
            }

            log("iteration ending: ", myInterpretCallId, "with stream", this.inputStream.id, "\n");
        }

        log("Finished interpret while loop", myInterpretCallId);

        return this.result;
    }

    interpretText(char) {
        log(`Interpret text called with |${char.replace(/\n/g, "\\n")}|`);
        this.result += char;
    }

    callFunction(name, boundary, args) {
        let position = this.inputStream.char;
        log(`calling ${name}, with ${boundary}`);
        try {
            let interpreter = new Interpreter(this.inputStream);
            let functionBody = interpreter.intelligentInterpret(boundary);

            this.result += functionRegistry.exists(name) ?
                functionRegistry.get(name)(name, args, functionBody, interpreter) :
                tag(name, args, functionBody, interpreter);
        }
        catch (e) {
            console.log(`In function ${name} (started at ${position}), at ${this.inputStream.char}:`);
            throw e;
        }
    }

    callFunctionWithBody(name, args, body) {
        let position = this.inputStream.char;
        log(`calling ${name}, with body: ${body}`);
        try {
            return functionRegistry.exists(name) ?
                functionRegistry.get(name)(name, args, body, this) :
                tag(name, args, body, this);
        }
        catch (e) {
            console.log(`In function ${name} (started at ${position}), at ${this.inputStream.char}:`);
            throw e;
        }
    }

    /*
     * If boundary is consumed, returns Boundary,
     * otherwise, returns null *without side effects*.
     */
    consumeBoundary() {
        let peekingStream = this.inputStream.peek();
        let boundary;

        if (peekingStream.consume(".")) {
            boundary = new BoundaryDot(peekingStream.consumed);
        }
        else if (peekingStream.consume(/\s/)) {
            boundary = new BoundarySpace(peekingStream.consumed);
        }
        else if (peekingStream.consume(/:+/)) {
            let consumed = peekingStream.consumed;
            let colonCount = consumed.length;

            if (!peekingStream.consume(/ |(?:\n)/)) {
                peekingStream.croak("':' sequences must end with whitespace");
            }

            consumed += peekingStream.consumed;

            if (colonCount === 1) {
                boundary = new BoundaryLine(consumed);
            }
            else if (colonCount === 2) {
                boundary = new BoundaryParagraph(consumed);
            }
            else if (colonCount === 3) {
                boundary = new BoundaryEOF(consumed);
            }
            else {
                peekingStream.croak("More than three colons in a colon boundary");
            }
        }
        else if (peekingStream.consume(/[{}<>[\]`"'/@#$%|]/)) {
            if (/}]>/.test(peekingStream.consumed)) {
                peekingStream.croak(`Prohibited boundary ${peekingStream.consumed}`);
            }
            boundary = new BoundaryOther(peekingStream.consumed);
        }
        else {
            return null;
        }

        Must(boundary);
        this.inputStream.sync(peekingStream);
        return boundary;
    }

    parseArgument() {
        let name;
        let value;

        this.inputStream.consumeOptionalWhitespace();
        let peekingStream = this.inputStream.peek();
        if (peekingStream.consumeWord() && peekingStream.consumeOptionalWhitespace() && peekingStream.consume("=")) {
            // Named argument
            Must(this.inputStream.consumeWord());
            name = this.inputStream.consumed;

            Must(this.inputStream.consumeOptionalWhitespace());
            Must(this.inputStream.consume("="));
            Must(this.inputStream.consumeOptionalWhitespace());
        }
        // else anonymous argument

        // We cannot prohibit empty arguments by checking for input stream
        // progress
        let boundary = this.consumeBoundary() || new BoundarySpace(" ");

        let interpreter = new Interpreter(this.inputStream);

        log("parsing Argument", name);
        value = interpreter.intelligentInterpret(boundary);

        this.inputStream.consumeOptionalWhitespace();

        return { name, value };
    }

    interpretFunctionCall(name) {
        let args = [];
        args.byName = {};

        if (this.inputStream.consume("(")) {
            // we have an arguments block in this function

            while (!this.inputStream.consume(")")) {
                let argument = this.parseArgument();
                args.push(argument);
                if (argument.name) args.byName[argument.name] = argument.value;

                if (this.inputStream.consume(",")) {
                    continue;
                }
                else if (this.inputStream.consume(")")) {
                    break;
                }
                else {
                    this.inputStream.croak("Expecting comma or closing parenthese in an argument list");
                }
            }
        }

        // Allow a space between the function call and the first boundary:
        // .foo { } is equivalent to .foo{}
        Must(this.inputStream.consumeOptionalWhitespace());
        let whitespaceBoundary = this.inputStream.consumed ?
            new BoundarySpace(this.inputStream.consumed) : null;
        let boundary = this.consumeBoundary() || whitespaceBoundary;

        if (!boundary) this.inputStream.croak("Missing function call boundary");

        this.callFunction(name, boundary, args);
        this.inputStream.sawFunctionCall = true;
    }

    intelligentInterpret(boundary) {
        return boundary.isQuote() ?
            this.interpretFlat(boundary) :
            this.interpretRecursive(boundary);
    }

    interpretRecursive(boundary) {
        return this.interpret(boundary);
    }

    interpretFlat(boundary) {
        return this.interpret(boundary, false);
    }
}<|endoftext|>