<commit_before>import React, { Component } from 'react';
import sinon from 'sinon';

export default chai => {
  const Assertion = chai.Assertion;

  /**
   * @example
   * expect(ComponentClass).to.have.been.rendered;
   */
  Assertion.addProperty('rendered', function expectComponentRendered() {
    const ComponentClass = this._obj;

    function constructMessage({ not }) {
      return `Expected component '${ComponentClass.displayName}' ` +
             `to ${not ? 'not ' : ''}have been rendered`;
    }

    this.assert(ComponentClass.rendered,
                constructMessage({ not: false }),
                constructMessage({ not: true }));
  });

  /**
   * @param {Object} props
   *
   * @example
   * expect(ComponentClass).to.have.been.renderedWith({foo: 'bar'});
   */
  Assertion.addMethod('renderedWith', function expectComponentRenderedWith(
    props) {
    const ComponentClass = this._obj;

    const renderSpy = ComponentClass.renderSpy;

    function constructMessage({ not }) {
      let msg = `Expected component '${ComponentClass.displayName}' ` +
                `to ${not ? 'not ' : ''}have been rendered with ` +
                `${renderSpy.printf('%*', props)}`;

      if (ComponentClass.rendered) {
        const renders = renderSpy.args.map(render =>
          // Each render is an array with a single element, the props.
          renderSpy.printf('%*', render[0])).join('\n');

        msg += `\n\nThe component has so far been rendered with:\n${renders}`;
      }

      return msg;
    }

    this.assert(renderSpy.calledWithMatch(props),
                constructMessage({ not: false }),
                constructMessage({ not: true }));
  });
};

export function createSpy({ name } = { name: 'Spy' }) {
  const _renderSpy = sinon.spy();

  return class Spy extends Component {
    static displayName = name;

    // TODO: this is used only by the chai plugin; figure out how to remove it
    static renderSpy = _renderSpy;

    /**
     * Get whether the component was rendered at least once.
     *
     * @return {Boolean}
     */
    static get rendered() {
      return _renderSpy.called;
    }

    /**
     * Get the props used in the last render.
     *
     * @return {Object}
     */
    static get lastProps() {
      if (!_renderSpy.called) {
        throw new Error('Component was never rendered');
      }

      return _renderSpy.args[_renderSpy.callCount - 1][0];
    }

    /**
     * Get the props used in every render ever.
     *
     * @returns {Object[]}
     */
    static get renders() {
      if (!_renderSpy.called) {
        throw new Error('Component was never rendered');
      }

      return _renderSpy.args.map(args => args[0]);
    }

    static reset() {
      _renderSpy.reset();
    }

    render() {
      _renderSpy(this.props);

      return
I am a fake component, here to spy on you!
;
    }
  };
}
<commit_msg>Use `util.inspect` instead of `spy.printf`<commit_after>import React, { Component } from 'react';
import sinon from 'sinon';
import { inspect } from 'util';

export default chai => {
  const Assertion = chai.Assertion;

  /**
   * @example
   * expect(ComponentClass).to.have.been.rendered;
   */
  Assertion.addProperty('rendered', function expectComponentRendered() {
    const ComponentClass = this._obj;

    function constructMessage({ not }) {
      return `Expected component '${ComponentClass.displayName}' ` +
             `to ${not ? 'not ' : ''}have been rendered`;
    }

    this.assert(ComponentClass.rendered,
                constructMessage({ not: false }),
                constructMessage({ not: true }));
  });

  /**
   * @param {Object} expectedProps
   *
   * @example
   * expect(ComponentClass).to.have.been.renderedWith({foo: 'bar'});
   */
  Assertion.addMethod('renderedWith', function expectComponentRenderedWith(
    expectedProps) {
    const ComponentClass = this._obj;

    const renderSpy = ComponentClass.renderSpy;

    function constructMessage({ not }) {
      let msg = `Expected component '${ComponentClass.displayName}' to ${not ? 'not ' : ''}have been rendered with ${inspect(expectedProps, { depth: 0 })}`;

      if (ComponentClass.rendered) {
        const renders = ComponentClass.renders.map(
          // Printing a single level of nesting should be enough for almost
          // everyone (TM).
          props => inspect(props, { depth: 0 })
        ).join('\n');

        msg += `\n\nThe component has so far been rendered with:\n${renders}`;
      }

      return msg;
    }

    this.assert(renderSpy.calledWithMatch(expectedProps),
                constructMessage({ not: false }),
                constructMessage({ not: true }));
  });
};

export function createSpy({ name } = { name: 'Spy' }) {
  const _renderSpy = sinon.spy();

  return class Spy extends Component {
    static displayName = name;

    // TODO: this is used only by the chai plugin; figure out how to remove it
    static renderSpy = _renderSpy;

    /**
     * Get whether the component was rendered at least once.
     *
     * @return {Boolean}
     */
    static get rendered() {
      return _renderSpy.called;
    }

    /**
     * Get the props used in the last render.
     *
     * @return {Object}
     */
    static get lastProps() {
      if (!_renderSpy.called) {
        throw new Error('Component was never rendered');
      }

      return _renderSpy.args[_renderSpy.callCount - 1][0];
    }

    /**
     * Get the props used in every render ever.
     *
     * @returns {Object[]}
     */
    static get renders() {
      if (!_renderSpy.called) {
        throw new Error('Component was never rendered');
      }

      return _renderSpy.args.map(args => args[0]);
    }

    static reset() {
      _renderSpy.reset();
    }

    render() {
      _renderSpy(this.props);

      return
I am a fake component, here to spy on you!
;
    }
  };
}
<|endoftext|>


<commit_before>/*global define*/
define(
	function()
	{
		var values = { };
		var is_initialized = false;
		var signals;
		var timeout_id;

		function init( shared )
		{
			signals = shared.signals;

			if ( shared.feature['query-selector-all'] )
			{
				var wrapper = document.getElementById( 'controls' );
				var controls = document.querySelectorAll( '.control-input' );

				wrapper.className += ' is-active';

				for ( var i = 0; i < controls.length; i++ )
				{
					var control = controls[i];

					control.addEventListener( 'change', controlUpdated, false );
					updateValue( control.id, control.value );
				}

				is_initialized = true;

				signals['control-updated'].dispatch( values );
			}
		}

		function controlUpdated( event )
		{
			var target = event.target;

			clearTimeout( timeout_id );

			timeout_id = setTimeout(
				function()
				{
					updateValue( target.id, target.value );
				},
				200
			);
		}

		function updateValue( key, value )
		{
			values[key] = value;

			if ( is_initialized )
			{
				signals['control-updated'].dispatch( values );
			}
		}

		return { init: init };
	}
);<commit_msg>convert all input values to integers<commit_after>/*global define*/
define(
	function()
	{
		var values = { };
		var is_initialized = false;
		var signals;
		var timeout_id;

		function init( shared )
		{
			signals = shared.signals;

			if ( shared.feature['query-selector-all'] )
			{
				var wrapper = document.getElementById( 'controls' );
				var controls = document.querySelectorAll( '.control-input' );

				wrapper.className += ' is-active';

				for ( var i = 0; i < controls.length; i++ )
				{
					var control = controls[i];

					control.addEventListener( 'change', controlUpdated, false );
					updateValue( control.id, control.value );
				}

				is_initialized = true;

				signals['control-updated'].dispatch( values );
			}
		}

		function controlUpdated( event )
		{
			var target = event.target;

			clearTimeout( timeout_id );

			timeout_id = setTimeout(
				function()
				{
					updateValue( target.id, target.value );
				},
				200
			);
		}

		function updateValue( key, value )
		{
			values[key] = parseInt( value, 10 );

			if ( is_initialized )
			{
				signals['control-updated'].dispatch( values );
			}
		}

		return { init: init };
	}
);<|endoftext|>


<commit_before>const Telegraf = require('telegraf')
const session = require('telegraf/session')
const Stage = require('telegraf/stage')
const WizardScene = require('telegraf/scenes/wizard')

const superWizard = new WizardScene('super-wizard',
  (ctx) => {
    ctx.reply('Step 1')
    ctx.wizard.next()
  },
  (ctx) => {
    if (ctx.message && ctx.message.text !== 'ok') {
      return ctx.replyWithMarkdown('Send `ok`')
    }
    ctx.reply('Step 2 ')
    ctx.wizard.next()
  },
  (ctx) => {
    ctx.reply('Step 3')
    ctx.wizard.next()
  },
  (ctx) => {
    ctx.reply('Step 4')
    ctx.wizard.next()
  },
  (ctx) => {
    ctx.reply('Done')
    ctx.scene.leave()
  }
)

const bot = new Telegraf(process.env.BOT_TOKEN)
const stage = new Stage([superWizard], { default: 'super-wizard' })
bot.use(session())
bot.use(stage.middleware())
bot.startPolling()
<commit_msg>update example<commit_after>const Telegraf = require('telegraf')
const Composer = require('telegraf/composer')
const session = require('telegraf/session')
const Stage = require('telegraf/stage')
const Markup = require('telegraf/markup')
const WizardScene = require('telegraf/scenes/wizard')

const stepHandler = new Composer()
stepHandler.action('next', (ctx) => {
  ctx.reply('Step 2 ')
  ctx.wizard.next()
})
stepHandler.use((ctx) => ctx.replyWithMarkdown('Press `Next` button to contunie'))

const superWizard = new WizardScene('super-wizard',
  (ctx) => {
    ctx.reply('Step 1', Markup.inlineKeyboard([
      Markup.urlButton('❤', 'http://telegraf.js.org'),
      Markup.callbackButton('➡ Next', 'next')
    ]).extra())
    ctx.wizard.next()
  },
  stepHandler.middleware(),
  (ctx) => {
    ctx.reply('Step 3')
    ctx.wizard.next()
  },
  (ctx) => {
    ctx.reply('Step 4')
    ctx.wizard.next()
  },
  (ctx) => {
    ctx.reply('Done')
    ctx.scene.leave()
  }
)

const bot = new Telegraf(process.env.BOT_TOKEN)
const stage = new Stage([superWizard], { default: 'super-wizard' })
bot.use(session())
bot.use(stage.middleware())
bot.startPolling()
<|endoftext|>


<commit_before>"use strict";

var request = require("request");
var Subscriber = require('../libs/Subscriber.js');

var WifiGuestItem = function(platform,homebridge,fritzBoxApi) {
    var self=this;
    this.name ="WiFi Guest";
    this.manufacturer = "AVM Computersysteme Vertriebs GmbH";
    this.model="WiFi Guest";
    this.serialNumber ="123456789";
    this.fritzBoxApi = fritzBoxApi;
    this.endpoint = "/wlan/guest_access.lua";
    this.fritzBoxApi.getSid(function(sid){
        self.subscriber = new Subscriber(
            "http://"+platform.config["host"] + self.endpoint,
            sid,
            5000,
            self.updateCharacteristics.bind(self)
        );
        self.sid = sid;
    });

    WifiGuestItem.super_.call(this,platform,homebridge);
};

WifiGuestItem.prototype.getOtherServices = function() {
    var otherService = new this.homebridge.hap.Service.Switch();
    this.setInitialState = true;

    otherService.getCharacteristic(this.homebridge.hap.Characteristic.On)
        .on('set', this.setItemState.bind(this))
        .on('get', this.getItemState.bind(this))
        .setValue(this.state === 'ON');

    return otherService;
};

WifiGuestItem.prototype.updateCharacteristics = function(message) {

    this.setFromFritzBox = true;

    this.otherService
        .getCharacteristic(this.homebridge.hap.Characteristic.On)
        .setValue(this.checkItemState(message));
};

WifiGuestItem.prototype.checkItemState = function(response) {
    var re = /\["wlan:settings\/guest_ap_enabled"\] = "([0-1])+",/;
    var matches = response.match(re);
    if (null !== matches){
        return (1 == parseInt(matches[1]));
    } else {
        self.log('WifiGuestItem - Invalid response.');
        return false;
    }
};

WifiGuestItem.prototype.getItemState = function(callback) {
    var self=this;
    var formfields = {getpage: this.endpoint};

    this.fritzBoxApi.getSid(function(sid){
        if ('0000000000000000' !== sid){
            self.subscriber.setSid(sid);
            self.fritzBoxApi.doGetRequest(formfields,function(response){
                callback(undefined,self.checkItemState(response));
            });
        } else {
            self.log('WifiGuestItem - There was a problem connecting to FRITZ!Box.');
        }
    });
};

WifiGuestItem.prototype.setItemState = function(value, callback) {

    var self = this;

    if (this.setInitialState) {
        this.setInitialState = false;
        callback();
        return;
    }

    if (this.setFromFritzBox) {
        this.setFromFritzBox = false;
        callback();
        return;
    }

    this.log("iOS - send message to " + this.name + ": " + value);

    this.fritzBoxApi.getSid(function(sid){
        if ('0000000000000000' !== sid){
            self.subscriber.setSid(sid);

            var formfields = {getpage: self.endpoint};
            self.fritzBoxApi.doGetRequest(formfields,function(response){

                formfields["getpage"] = self.endpoint;

                //fetch guest_ssid
                var re = /\["wlan:settings\/guest_ssid"\] = "(.+)",/;
                var matches = response.match(re);
                if (null !== matches){
                    formfields["guest_ssid"] = matches[1];
                } else {
                    self.log('WifiGuestItem - Invalid response during fetch of guest_ssid.');
                    return;
                }

                //fetch wpa_key
                re = /\["wlan:settings\/guest_pskvalue"\] = "(.+)",/;
                matches = response.match(re);
                if (null !== matches){
                    formfields["wpa_key"] = matches[1];
                } else {
                    self.log('WifiGuestItem - Invalid response during fetch of wpa_key.');
                    return;
                }

                //fetch sec_mode
                re = /\["wlan:settings\/guest_encryption"\] = "([0-9])+",/;
                matches = response.match(re);
                if (null !== matches){
                    formfields["sec_mode"] = matches[1];
                } else {
                    self.log('WifiGuestItem - Invalid response during fetch of sec_mode.');
                    return;
                }

                if (value == true ) formfields['activate_guest_access'] = 'on';

                formfields["btnSave"] = "";

                self.fritzBoxApi.doPostForm(formfields,function(response){
                    callback();
                });

            });
        } else {
            self.log('WifiGuestItem - There was a problem connecting to FRITZ!Box.');
        }
    });
};

module.exports = WifiGuestItem;<commit_msg>Fix #9<commit_after>"use strict";

var request = require("request");
var Subscriber = require('../libs/Subscriber.js');

var WifiGuestItem = function(platform,homebridge,fritzBoxApi) {
    var self=this;
    this.name ="WiFi Guest";
    this.manufacturer = "AVM Computersysteme Vertriebs GmbH";
    this.model="WiFi Guest";
    this.serialNumber ="123456789";
    this.fritzBoxApi = fritzBoxApi;
    this.endpoint = "/wlan/guest_access.lua";
    this.fritzBoxApi.getSid(function(sid){
        self.subscriber = new Subscriber(
            "http://"+platform.config["host"] + self.endpoint,
            sid,
            5000,
            self.updateCharacteristics.bind(self)
        );
        self.sid = sid;
    });

    WifiGuestItem.super_.call(this,platform,homebridge);
};

WifiGuestItem.prototype.getOtherServices = function() {
    var otherService = new this.homebridge.hap.Service.Switch();
    this.setInitialState = true;

    otherService.getCharacteristic(this.homebridge.hap.Characteristic.On)
        .on('set', this.setItemState.bind(this))
        .on('get', this.getItemState.bind(this))
        .setValue(this.state === 'ON');

    return otherService;
};

WifiGuestItem.prototype.updateCharacteristics = function(message) {

    this.setFromFritzBox = true;

    this.otherService
        .getCharacteristic(this.homebridge.hap.Characteristic.On)
        .setValue(this.checkItemState(message));
};

WifiGuestItem.prototype.checkItemState = function(response) {
    var re = /\["wlan:settings\/guest_ap_enabled"\] = "([0-1])+",/;
    var matches = response.match(re);
    if (null !== matches){
        return (1 == parseInt(matches[1]));
    } else {
        this.log('WifiGuestItem - Invalid response.');
        return false;
    }
};

WifiGuestItem.prototype.getItemState = function(callback) {
    var self=this;
    var formfields = {getpage: this.endpoint};

    this.fritzBoxApi.getSid(function(sid){
        if ('0000000000000000' !== sid){
            self.subscriber.setSid(sid);
            self.fritzBoxApi.doGetRequest(formfields,function(response){
                callback(undefined,self.checkItemState(response));
            });
        } else {
            self.log('WifiGuestItem - There was a problem connecting to FRITZ!Box.');
        }
    });
};

WifiGuestItem.prototype.setItemState = function(value, callback) {

    var self = this;

    if (this.setInitialState) {
        this.setInitialState = false;
        callback();
        return;
    }

    if (this.setFromFritzBox) {
        this.setFromFritzBox = false;
        callback();
        return;
    }

    this.log("iOS - send message to " + this.name + ": " + value);

    this.fritzBoxApi.getSid(function(sid){
        if ('0000000000000000' !== sid){
            self.subscriber.setSid(sid);

            var formfields = {getpage: self.endpoint};
            self.fritzBoxApi.doGetRequest(formfields,function(response){

                formfields["getpage"] = self.endpoint;

                //fetch guest_ssid
                var re = /\["wlan:settings\/guest_ssid"\] = "(.+)",/;
                var matches = response.match(re);
                if (null !== matches){
                    formfields["guest_ssid"] = matches[1];
                } else {
                    self.log('WifiGuestItem - Invalid response during fetch of guest_ssid.');
                    return;
                }

                //fetch wpa_key
                re = /\["wlan:settings\/guest_pskvalue"\] = "(.+)",/;
                matches = response.match(re);
                if (null !== matches){
                    formfields["wpa_key"] = matches[1];
                } else {
                    self.log('WifiGuestItem - Invalid response during fetch of wpa_key.');
                    return;
                }

                //fetch sec_mode
                re = /\["wlan:settings\/guest_encryption"\] = "([0-9])+",/;
                matches = response.match(re);
                if (null !== matches){
                    formfields["sec_mode"] = matches[1];
                } else {
                    self.log('WifiGuestItem - Invalid response during fetch of sec_mode.');
                    return;
                }

                if (value == true ) formfields['activate_guest_access'] = 'on';

                formfields["btnSave"] = "";

                self.fritzBoxApi.doPostForm(formfields,function(response){
                    callback();
                });

            });
        } else {
            self.log('WifiGuestItem - There was a problem connecting to FRITZ!Box.');
        }
    });
};

module.exports = WifiGuestItem;
<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

import fs from 'fs';

import colors from 'colors';

/**
 * Rust tokenizer template.
 */
const RUST_TOKENIZER_TEMPLATE = fs.readFileSync(
  `${__dirname}/templates/tokenizer.template.rs`,
  'utf-8'
);

/**
 * The trait is used by parser generators (LL/LR) for Rust.
 */
const RustParserGeneratorTrait = {

  /**
   * Generates parsing table in Rust vector format.
   */
  generateParseTable() {
    this.writeData(
      'TABLE',
      this._buildTable(this.generateParseTableData()),
    );
  },

  /**
   * Converts JS object into Rust HashMap.
   *
   * In Rust we represent a table as a vector, where index is a state number,
   * and a value is a hashmap of LR entries (shift/reduce/etc).
   *
   * Example:
   *
   * vec![
   *     hashmap! { 1 => TE::Shift(4), 3 => TE::Reduce(1), ... },
   * ]
   */
  _buildTable(table) {
    const entries = Object.keys(table).map(state => {
      const row = table[state];

      // Transform to Rust enum format: "s3" => TE::Shift(3), etc
      Object.keys(row).forEach(key => {
        const entry = row[key];
        if (entry[0] === 's') {
          row[key] = `TE::Shift(${entry.slice(1)})`;
        } else if (entry[0] === 'r') {
          row[key] = `TE::Reduce(${entry.slice(1)})`;
        } else if (entry === 'acc') {
          row[key] = `TE::Accept`;
        } else {
          row[key] = `TE::Transit(${entry})`;
        }
      });

      return this._toRustHashMap(table[state], 'number')
    });

    return `vec![\n    ${entries.join(',\n    ')}\n]`;
  },

  /**
   * Generates tokens table in Rust hashmap format.
   */
  generateTokensTable() {
    this.writeData(
      'TOKENS',
      this._toRustHashMap(this._tokens, 'string', 'number'),
    );
  },

  /**
   * Production handlers are implemented as methods on the parser class.
   */
  buildSemanticAction(production) {
    let originalAction = this.getSemanticActionCode(production);

    let {action, types} = this._extractDataTypes(originalAction);

    action = this._actionFromHandler(action, '.tokenizer');

    action = this._generateArgsPrologue(
      action,
      types,
      // Total number of args.
      production.isEpsilon() ? 0 : production.getRHS().length
    );

    // Append return value.
    const returnValue = types.hasOwnProperty('__')
      ? `SV::_${this._allTypes[types.__]}(__)`
      : `__`;

    action = action + `\n${returnValue}`;

    // Save the action, they are injected later.
    this._productionHandlers.push({args: '&mut self', action});
    return null;
  },

  /**
   * Builds SV (stack value) enum from all the used types in handlers.
   */
  generateStackValueEnum() {
    const svEnum = Object.keys(this._allTypes).map(
      (typeName, idx) => `_${idx}(${typeName})`
    );
    this.writeData('SV_ENUM', svEnum.join(',\n    '));
  },

  /**
   * Generates prologue for fetching arguments from the parsing stack.
   */
  _generateArgsPrologue(action, types, totalArgsCount) {
    const argsPrologue = [];

    for (let i = totalArgsCount; i > 0; i--) {
      const arg = '_' + i;

      if (!types.hasOwnProperty(arg)) {
        // Just pop if arg is not used in the handler.
        argsPrologue.push(`self.values_stack.pop();`);
      } else {
        const typeInfo = types[arg];

        if (typeInfo) {
          argsPrologue.push(
            `let mut ${arg} = pop!(self.values_stack, ` +
            `_${this._allTypes[typeInfo]});`
          );
        } else {
          argsPrologue.push(
            `let mut ${arg} = self.values_stack.pop().unwrap();`
          );
        }
      }
    }

    return (
      '// Semantic values prologue.\n' +
      argsPrologue.join('\n') + '\n\n' +
      action
    );
  },

  /**
   * Extracts types of the used arguments, and return types.
   * If a type is defined, a popped stack value is casted,
   * otherwise, it's just popped (if doesn't participate in an operation
   * and just propagated).
   */
  _extractDataTypes(action) {
    const types = {};

    if (!action) {
      return {action: '', types};
    }

    const typesRe = /\s*\|([^|]*)\|\s*->\s*(\w+);/g;
    const typesData = typesRe.exec(action);

    if (typesData) {
      // '$1:i32, $3:i32'
      if (typesData[1].trim().length > 0) {
        const argTypes = typesData[1].trim().split(/\s*[,;]\s*/);
        argTypes.forEach(argData => {
          const data = argData.split(/\s*:\s*/);
          // types[$1] = 'i32';
          types[data[0]] = data[1];

          // Save the type to all types data to generate enum.
          if (!this._allTypes.hasOwnProperty(data[1])) {
            this._allTypes[data[1]] = this._allTypesIndex++;
          }
        });
      }

      // Result type.
      if (typesData[2]) {
        types.__ = typesData[2];

        if (!this._allTypes.hasOwnProperty(typesData[2])) {
          this._allTypes[typesData[2]] = this._allTypesIndex++;
        }
      }
    }

    // Strip the types info.
    action = action.replace(typesRe, '');

    // Extract other args, which do not use types.
    const argsRe = /_\d+/g;

    const usedArgs = action.match(argsRe);
    if (usedArgs) {
      usedArgs.forEach(usedArg => {
        if (!types.hasOwnProperty(usedArg)) {
          // Arg is used, but without a type.
          types[usedArg] = null;
        }
      });
    }

    return {types, action};
  },

  /**
   * Default format in the [ ] array notation.
   */
  generateProductionsData() {
    return this.generateRawProductionsData()
      .map(data => {
        // Remove the semantic action handler, since in Rust
        // we use a different structure to hold it.
        delete data[2];
        return `[${data.join(', ')}]`;
      });
  },

  /**
   * Generates built-in tokenizer instance.
   */
  generateBuiltInTokenizer() {
    this.writeData('TOKENIZER', RUST_TOKENIZER_TEMPLATE);
  },

  /**
   * Creates an action from raw handler.
   */
  _actionFromHandler(handler, context = '') {
    let action = (this._scopeVars(handler, context) || '').trim();

    if (!action) {
      return 'let __ = SV::Undefined;';
    }

    // From parser hooks, append ; at the end.
    if (context === '.tokenizer' && !/;\s*$/.test(action)) {
      action += ';';
    }

    return action;
  },

  /**
   * Generates rules for tokenizer.
   */
  generateLexRules() {
    const lexRulesArray = [];

    const lexRules = this._grammar.getLexGrammar().getRules().map((rule, i) => {
      let action = this._actionFromHandler(rule.getRawHandler());

      this._lexHandlers.push({args: '&mut self', action});

      let flags = [];

      if (rule.isCaseInsensitive()) {
        flags.push('i');
      }

      if (flags.length > 0) {
        flags = `(?${flags.join('')})`
      } else {
        flags = '';
      }

      lexRulesArray.push(`Tokenizer::_lex_rule${i}`);

      let matcher = rule.getRawMatcher();

      // there is no need for escape sequence for character '/'
      // (actually you cannot have such escape sequence as '\/', whihc causes rust's regex parser to panic!)
      matcher = matcher.replace("\\/", "/");

      // well, maybe so many # is enough
      // this is for handling character '"' correctly
      // reference: rust's raw string literals https://rahul-thakoor.github.io/rust-raw-string-literals
      return `r##########"${flags}${matcher}"##########`;
    });

    this.writeData('LEX_RULE_HANDLERS_COUNT', lexRules.length);
    this.writeData(
      'LEX_RULE_HANDLERS_ARRAY',
      `[\n    ${lexRulesArray.join(',\n    ')}\n],`
    );

    this.writeData(
      'LEX_RULES',
      `[&'static str; ${lexRules.length}] = ` +
      `[\n    ${lexRules.join(',\n    ')}\n]`
    );
  },

  generateLexRulesByStartConditions() {
    const lexGrammar = this._grammar.getLexGrammar();
    const lexRulesByConditions = lexGrammar.getRulesByStartConditions();
    const result = [];

    for (const condition in lexRulesByConditions) {
      result[condition] = lexRulesByConditions[condition].map(lexRule =>
        lexGrammar.getRuleIndex(lexRule)
      );
    }

    this.writeData(
      'LEX_RULES_BY_START_CONDITIONS',
      `${this._toRustHashMap(result, 'string')}`,
    );
  },

  /**
   * Replaces global vars like `yytext`, `$$`, etc. to be
   * referred from `yyparse`.
   */
  _scopeVars(code, context = '') {
    return code
      .replace(/yytext/g, `self${context}.yytext`)
      .replace(/yyleng/g, `self${context}.yyleng`)
      .replace(/__\s*=/g, `let __ =`)
      .replace(/yyloc/g, 'Loc::from_tokens_range');
  },

  /**
   * Type-converts a key of a Rust HashMap, e.g. string or number, etc.
   */
  _hashKey(key, keyType) {
    switch (keyType) {
      case 'string': return `"${key}"`;
      case 'number': return Number(key);
      default:
        throw new Error('_hashKey: Incorrect type ' + keyType);
    }
  },

  /**
   * Type-converts a value of a Rust hashmap, e.g. string or number, etc.
   */
  _hashValue(value, valueType) {
    if (Array.isArray(value)) {
      // Support only int arrays here for simplicity.
      return `vec! [ ${value.join(', ')} ]`;
    }

    switch (valueType) {
      case 'string': return `"${value}"`;
      case 'number': return Number(value);
      default: return value;
    }
  },

  /**
   * Converts JS object to Rust HashMap representation.
   */
  _toRustHashMap(object, keyType, valueType) {
    let result = [];
    for (let k in object) {
      let value = object[k];
      let key = k.replace(/"/g, '\\"');
      result.push(
        `${this._hashKey(key, keyType)} => ` +
        `${this._hashValue(value, valueType)}`
      );
    }
    return `hashmap! { ${result.join(', ')} }`;
  },

  /**
   * Rust-specific lex rules handler declarations.
   */
  generateLexHandlers() {
    const handlers = this._generateHandlers(
      this._lexHandlers,
      '_lex_rule',
      "&'static str"
    );
    this.writeData('LEX_RULE_HANDLERS', handlers.join('\n\n'));
  },

  /**
   * Rust-specific handler declarations.
   */
  generateProductionHandlers() {
    const handlers = this._generateHandlers(
      this._productionHandlers,
      '_handler',
      'SV'
    );

    this.writeData('PRODUCTION_HANDLERS_COUNT', handlers.length);

    const handlersArray = [];
    for (let i = 0; i < handlers.length; i++) {
      handlersArray.push(`Parser::_handler${i}`);
    }

    this.writeData(
      'PRODUCTION_HANDLERS_ARRAY',
      `[\n    ${handlersArray.join(',\n    ')}\n],`
    );

    this.writeData('PRODUCTION_HANDLERS', handlers.join('\n\n'));
  },

  /**
   * Productions array in Rust format.
   */
  generateProductions() {
    const productionsData = this.generateProductionsData();
    const productionsCount = productionsData.length;
    this.writeData(
      'PRODUCTIONS',
      `[[i32; 2]; ${productionsCount}] = ` +
      `[\n    ${productionsData.join(',\n    ')}\n]`
    );
  },

  /**
   * Module include.
   */
  generateModuleInclude() {
    const moduleInclude = this._grammar.getModuleInclude();

    const resultTypeData = /type\s+TResult\s*=\s*([^;]+);/.exec(moduleInclude);

    if (!resultTypeData) {
      throw new Error(
        `\n\nRust plugin should provide module include, and define at least ` +
        `result type:\n\n  ${colors.bold('type TResult = <...>;\n')}`
      );
    }

    // Result type.
    const resultType = resultTypeData[1];

    if (!this._allTypes.hasOwnProperty(resultType)) {
      throw new Error(
        `Result type ${colors.bold(resultType)} is not found in ` +
        `handled types. Make sure your productions return it.\n`
      );
    }

    this.writeData('RESULT_TYPE', `_${this._allTypes[resultType]}`);

    // Parser hooks.
    const onParseBegin = moduleInclude.indexOf('fn on_parse_begin') !== -1
      ? 'on_parse_begin(self, string);'
      : '';

    const onParseEnd = moduleInclude.indexOf('fn on_parse_end') !== -1
      ? 'on_parse_end(self, &result);'
      : '';

    const onParseError = moduleInclude.indexOf('fn on_parse_error') !== -1
      ? 'on_parse_error(self, &token);'
      : 'self.tokenizer.panic_unexpected_token(token.value, token.start_line, token.start_column);';

    this.writeData('ON_PARSE_BEGIN_CALL', onParseBegin);
    this.writeData('ON_PARSE_END_CALL', onParseEnd);
    this.writeData('ON_PARSE_ERROR_CALL', onParseError);

    this.writeData('MODULE_INCLUDE', moduleInclude);
  },

  /**
   * Generates Rust function declarations for handlers.
   */
  _generateHandlers(handlers, name, returnType) {
    return handlers.map(({args, action}, index) => {
      return `fn ${name}${index}` +
        `(${args}) -> ${returnType} {\n${action}\n}`
    });
  },
};

module.exports = RustParserGeneratorTrait;
<commit_msg>Update rust-parser-generator-trait.js<commit_after>/**
 * The MIT License (MIT)
 * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

import fs from 'fs';

import colors from 'colors';

/**
 * Rust tokenizer template.
 */
const RUST_TOKENIZER_TEMPLATE = fs.readFileSync(
  `${__dirname}/templates/tokenizer.template.rs`,
  'utf-8'
);

/**
 * The trait is used by parser generators (LL/LR) for Rust.
 */
const RustParserGeneratorTrait = {

  /**
   * Generates parsing table in Rust vector format.
   */
  generateParseTable() {
    this.writeData(
      'TABLE',
      this._buildTable(this.generateParseTableData()),
    );
  },

  /**
   * Converts JS object into Rust HashMap.
   *
   * In Rust we represent a table as a vector, where index is a state number,
   * and a value is a hashmap of LR entries (shift/reduce/etc).
   *
   * Example:
   *
   * vec![
   *     hashmap! { 1 => TE::Shift(4), 3 => TE::Reduce(1), ... },
   * ]
   */
  _buildTable(table) {
    const entries = Object.keys(table).map(state => {
      const row = table[state];

      // Transform to Rust enum format: "s3" => TE::Shift(3), etc
      Object.keys(row).forEach(key => {
        const entry = row[key];
        if (entry[0] === 's') {
          row[key] = `TE::Shift(${entry.slice(1)})`;
        } else if (entry[0] === 'r') {
          row[key] = `TE::Reduce(${entry.slice(1)})`;
        } else if (entry === 'acc') {
          row[key] = `TE::Accept`;
        } else {
          row[key] = `TE::Transit(${entry})`;
        }
      });

      return this._toRustHashMap(table[state], 'number')
    });

    return `vec![\n    ${entries.join(',\n    ')}\n]`;
  },

  /**
   * Generates tokens table in Rust hashmap format.
   */
  generateTokensTable() {
    this.writeData(
      'TOKENS',
      this._toRustHashMap(this._tokens, 'string', 'number'),
    );
  },

  /**
   * Production handlers are implemented as methods on the parser class.
   */
  buildSemanticAction(production) {
    let originalAction = this.getSemanticActionCode(production);

    let {action, types} = this._extractDataTypes(originalAction);

    action = this._actionFromHandler(action, '.tokenizer');

    action = this._generateArgsPrologue(
      action,
      types,
      // Total number of args.
      production.isEpsilon() ? 0 : production.getRHS().length
    );

    // Append return value.
    const returnValue = types.hasOwnProperty('__')
      ? `SV::_${this._allTypes[types.__]}(__)`
      : `__`;

    action = action + `\n${returnValue}`;

    // Save the action, they are injected later.
    this._productionHandlers.push({args: '&mut self', action});
    return null;
  },

  /**
   * Builds SV (stack value) enum from all the used types in handlers.
   */
  generateStackValueEnum() {
    const svEnum = Object.keys(this._allTypes).map(
      (typeName, idx) => `_${idx}(${typeName})`
    );
    this.writeData('SV_ENUM', svEnum.join(',\n    '));
  },

  /**
   * Generates prologue for fetching arguments from the parsing stack.
   */
  _generateArgsPrologue(action, types, totalArgsCount) {
    const argsPrologue = [];

    for (let i = totalArgsCount; i > 0; i--) {
      const arg = '_' + i;

      if (!types.hasOwnProperty(arg)) {
        // Just pop if arg is not used in the handler.
        argsPrologue.push(`self.values_stack.pop();`);
      } else {
        const typeInfo = types[arg];

        if (typeInfo) {
          argsPrologue.push(
            `let mut ${arg} = pop!(self.values_stack, ` +
            `_${this._allTypes[typeInfo]});`
          );
        } else {
          argsPrologue.push(
            `let mut ${arg} = self.values_stack.pop().unwrap();`
          );
        }
      }
    }

    return (
      '// Semantic values prologue.\n' +
      argsPrologue.join('\n') + '\n\n' +
      action
    );
  },

  /**
   * Extracts types of the used arguments, and return types.
   * If a type is defined, a popped stack value is casted,
   * otherwise, it's just popped (if doesn't participate in an operation
   * and just propagated).
   */
  _extractDataTypes(action) {
    const types = {};

    if (!action) {
      return {action: '', types};
    }

    const typesRe = /\s*\|([^|]*)\|\s*->\s*(\w+);/g;
    const typesData = typesRe.exec(action);

    if (typesData) {
      // '$1:i32, $3:i32'
      if (typesData[1].trim().length > 0) {
        const argTypes = typesData[1].trim().split(/\s*[,;]\s*/);
        argTypes.forEach(argData => {
          const data = argData.split(/\s*:\s*/);
          // types[$1] = 'i32';
          types[data[0]] = data[1];

          // Save the type to all types data to generate enum.
          if (!this._allTypes.hasOwnProperty(data[1])) {
            this._allTypes[data[1]] = this._allTypesIndex++;
          }
        });
      }

      // Result type.
      if (typesData[2]) {
        types.__ = typesData[2];

        if (!this._allTypes.hasOwnProperty(typesData[2])) {
          this._allTypes[typesData[2]] = this._allTypesIndex++;
        }
      }
    }

    // Strip the types info.
    action = action.replace(typesRe, '');

    // Extract other args, which do not use types.
    const argsRe = /_\d+/g;

    const usedArgs = action.match(argsRe);
    if (usedArgs) {
      usedArgs.forEach(usedArg => {
        if (!types.hasOwnProperty(usedArg)) {
          // Arg is used, but without a type.
          types[usedArg] = null;
        }
      });
    }

    return {types, action};
  },

  /**
   * Default format in the [ ] array notation.
   */
  generateProductionsData() {
    return this.generateRawProductionsData()
      .map(data => {
        // Remove the semantic action handler, since in Rust
        // we use a different structure to hold it.
        delete data[2];
        return `[${data.join(', ')}]`;
      });
  },

  /**
   * Generates built-in tokenizer instance.
   */
  generateBuiltInTokenizer() {
    this.writeData('TOKENIZER', RUST_TOKENIZER_TEMPLATE);
  },

  /**
   * Creates an action from raw handler.
   */
  _actionFromHandler(handler, context = '') {
    let action = (this._scopeVars(handler, context) || '').trim();

    if (!action) {
      return 'let __ = SV::Undefined;';
    }

    // From parser hooks, append ; at the end.
    if (context === '.tokenizer' && !/;\s*$/.test(action)) {
      action += ';';
    }

    return action;
  },

  /**
   * Generates rules for tokenizer.
   */
  generateLexRules() {
    const lexRulesArray = [];

    const lexRules = this._grammar.getLexGrammar().getRules().map((rule, i) => {
      let action = this._actionFromHandler(rule.getRawHandler());

      this._lexHandlers.push({args: '&mut self', action});

      let flags = [];

      if (rule.isCaseInsensitive()) {
        flags.push('i');
      }

      if (flags.length > 0) {
        flags = `(?${flags.join('')})`
      } else {
        flags = '';
      }

      lexRulesArray.push(`Tokenizer::_lex_rule${i}`);

      let matcher = rule.getRawMatcher();

      // there is no need for escape sequence for character '/'
      // (actually you cannot have such escape sequence as '\/', whihc causes rust's regex parser to panic!)
      matcher = matcher.replace("\\/", "/");

      // well, maybe so many # is enough
      // this is for handling character '"' correctly
      // reference: rust's raw string literals https://rahul-thakoor.github.io/rust-raw-string-literals
      return `r##########"${flags}${matcher}"##########`;
    });

    this.writeData('LEX_RULE_HANDLERS_COUNT', lexRules.length);
    this.writeData(
      'LEX_RULE_HANDLERS_ARRAY',
      `[\n    ${lexRulesArray.join(',\n    ')}\n],`
    );

    this.writeData(
      'LEX_RULES',
      `[&'static str; ${lexRules.length}] = ` +
      `[\n    ${lexRules.join(',\n    ')}\n]`
    );
  },

  generateLexRulesByStartConditions() {
    const lexGrammar = this._grammar.getLexGrammar();
    const lexRulesByConditions = lexGrammar.getRulesByStartConditions();
    const result = [];

    for (const condition in lexRulesByConditions) {
      result[condition] = lexRulesByConditions[condition].map(lexRule =>
        lexGrammar.getRuleIndex(lexRule)
      );
    }

    this.writeData(
      'LEX_RULES_BY_START_CONDITIONS',
      `${this._toRustHashMap(result, 'string')}`,
    );
  },

  /**
   * Replaces global vars like `yytext`, `$$`, etc. to be
   * referred from `yyparse`.
   */
  _scopeVars(code, context = '') {
    return code
      .replace(/yytext/g, `self${context}.yytext`)
      .replace(/yyleng/g, `self${context}.yyleng`)
      .replace(/__\s*=/g, `let __ =`)
      .replace(/yyloc/g, 'Loc::from_tokens_range');
  },

  /**
   * Type-converts a key of a Rust HashMap, e.g. string or number, etc.
   */
  _hashKey(key, keyType) {
    switch (keyType) {
      case 'string': return `"${key}"`;
      case 'number': return Number(key);
      default:
        throw new Error('_hashKey: Incorrect type ' + keyType);
    }
  },

  /**
   * Type-converts a value of a Rust hashmap, e.g. string or number, etc.
   */
  _hashValue(value, valueType) {
    if (Array.isArray(value)) {
      // Support only int arrays here for simplicity.
      return `vec! [ ${value.join(', ')} ]`;
    }

    switch (valueType) {
      case 'string': return `"${value}"`;
      case 'number': return Number(value);
      default: return value;
    }
  },

  /**
   * Converts JS object to Rust HashMap representation.
   */
  _toRustHashMap(object, keyType, valueType) {
    let result = [];
    for (let k in object) {
      let value = object[k];
      let key = k.replace(/"/g, '\\"');
      result.push(
        `${this._hashKey(key, keyType)} => ` +
        `${this._hashValue(value, valueType)}`
      );
    }
    return `hashmap! { ${result.join(', ')} }`;
  },

  /**
   * Rust-specific lex rules handler declarations.
   */
  generateLexHandlers() {
    const handlers = this._generateHandlers(
      this._lexHandlers,
      '_lex_rule',
      "&'static str"
    );
    this.writeData('LEX_RULE_HANDLERS', handlers.join('\n\n'));
  },

  /**
   * Rust-specific handler declarations.
   */
  generateProductionHandlers() {
    const handlers = this._generateHandlers(
      this._productionHandlers,
      '_handler',
      'SV'
    );

    this.writeData('PRODUCTION_HANDLERS_COUNT', handlers.length);

    const handlersArray = [];
    for (let i = 0; i < handlers.length; i++) {
      handlersArray.push(`Parser::_handler${i}`);
    }

    this.writeData(
      'PRODUCTION_HANDLERS_ARRAY',
      `[\n    ${handlersArray.join(',\n    ')}\n],`
    );

    this.writeData('PRODUCTION_HANDLERS', handlers.join('\n\n'));
  },

  /**
   * Productions array in Rust format.
   */
  generateProductions() {
    const productionsData = this.generateProductionsData();
    const productionsCount = productionsData.length;
    this.writeData(
      'PRODUCTIONS',
      `[[i32; 2]; ${productionsCount}] = ` +
      `[\n    ${productionsData.join(',\n    ')}\n]`
    );
  },

  /**
   * Module include.
   */
  generateModuleInclude() {
    const moduleInclude = this._grammar.getModuleInclude();

    const resultTypeData = /type\s+TResult\s*=\s*([^;]+);/.exec(moduleInclude);

    if (!resultTypeData) {
      throw new Error(
        `\n\nRust plugin should provide module include, and define at least ` +
        `result type:\n\n  ${colors.bold('type TResult = <...>;\n')}`
      );
    }

    // Result type.
    const resultType = resultTypeData[1];

    if (!this._allTypes.hasOwnProperty(resultType)) {
      throw new Error(
        `Result type ${colors.bold(resultType)} is not found in ` +
        `handled types. Make sure your productions return it.\n`
      );
    }

    this.writeData('RESULT_TYPE', `_${this._allTypes[resultType]}`);

    // Parser hooks.
    const onParseBegin = moduleInclude.indexOf('fn on_parse_begin') !== -1
      ? 'on_parse_begin(self, string);'
      : '';

    const onParseEnd = moduleInclude.indexOf('fn on_parse_end') !== -1
      ? 'on_parse_end(self, &result);'
      : '';

    const onParseError = moduleInclude.indexOf('fn on_parse_error') !== -1
      ? 'on_parse_error(self, &token);'
      :
'if token.value == EOF && !self.tokenizer.has_more_tokens() {\n\
    panic!("\\n\\nUnexpected end of input.\\n\\n");\n\
}\n\
self.tokenizer.panic_unexpected_token(token.value, token.start_line, token.start_column);';

    this.writeData('ON_PARSE_BEGIN_CALL', onParseBegin);
    this.writeData('ON_PARSE_END_CALL', onParseEnd);
    this.writeData('ON_PARSE_ERROR_CALL', onParseError);

    this.writeData('MODULE_INCLUDE', moduleInclude);
  },

  /**
   * Generates Rust function declarations for handlers.
   */
  _generateHandlers(handlers, name, returnType) {
    return handlers.map(({args, action}, index) => {
      return `fn ${name}${index}` +
        `(${args}) -> ${returnType} {\n${action}\n}`
    });
  },
};

module.exports = RustParserGeneratorTrait;
<|endoftext|>


<commit_before>import queryString from "query-string";
import * as types from "./types";
import { getServerAddress } from "../util/globals";
import { goTo404 } from "./navigation";
import { createStateFromQueryOrJSONs, createTreeTooState, getNarrativePageFromQuery } from "./recomputeReduxState";
import { loadFrequencies } from "./frequencies";
import { loadMeasurements } from "./measurements";
import { fetchJSON, fetchWithErrorHandling } from "../util/serverInteraction";
import { warningNotification, errorNotification } from "./notifications";
import { parseMarkdownNarrativeFile } from "../util/parseNarrative";
import { NoContentError } from "../util/exceptions";
import { parseMarkdown } from "../util/parseMarkdown";
import { updateColorByWithRootSequenceData } from "../actions/colors";
import { explodeTree } from "./tree";
import { togglePanelDisplay } from "./panelDisplay";

export function getDatasetNamesFromUrl(url) {
  let secondTreeUrl;
  if (url.includes(":")) {
    const parts = url.replace(/^\//, '')
      .replace(/\/$/, '')
      .split(":");
    url = parts[0]; // eslint-disable-line no-param-reassign
    secondTreeUrl = parts[1];
  }
  if (url.startsWith('/')) url = url.slice(1); // eslint-disable-line no-param-reassign
  if (secondTreeUrl && secondTreeUrl.startsWith('/')) {
    secondTreeUrl = secondTreeUrl.slice(1);
  }
  return [url, secondTreeUrl];
}

export const loadSecondTree = (secondTreeUrl, firstTreeUrl) => async (dispatch, getState) => {

  const dataset = new Dataset(secondTreeUrl);
  let secondJson;
  try {
    dataset.fetchMain();
    secondJson = await dataset.main;
  } catch (err) {
    console.error("Failed to fetch additional tree", err.message);
    dispatch(warningNotification({message: "Failed to fetch second tree"}));
    return;
  }

  const oldState = getState();

  /* if the first tree is exploded, then reconstruct it before loading a second tree */
  if (oldState.controls.explodeAttr) {
    dispatch(explodeTree(undefined));
  }

  const newState = createTreeTooState({treeTooJSON: secondJson.tree, oldState, originalTreeUrl: firstTreeUrl, secondTreeUrl: secondTreeUrl, dispatch});
  dispatch({type: types.TREE_TOO_DATA, ...newState});
};

export const loadJSONs = ({url = window.location.pathname, search = window.location.search} = {}) => {
  return async (dispatch, getState) => {
    const { tree } = getState();
    if (tree.loaded) {
      dispatch({type: types.DATA_INVALID}); // "resets" state
    }
    const query = queryString.parse(search);
    if (url.includes("/narratives/")) {
      loadNarrative(dispatch, url, query);
    } else {
      warnDeprecatedQuerySyntax(dispatch, query);
      const [mainName, secondName] = getDatasetNamesFromUrl(url);
      const mainDataset = new Dataset(mainName);
      const secondDataset = secondName ? new Dataset(secondName) : undefined;
      try {
        mainDataset.fetchMain();
        if (secondDataset) secondDataset.fetchMain();
        await dispatchCleanStart(dispatch, mainDataset, secondDataset, query);
      } catch (err) {
        const msg = `Couldn't load JSONs for ${mainDataset.name} ${secondDataset ? `and/or ${secondDataset.name}` : ''}`;
        handleFetchErrors(err, dispatch, msg);
        return;
      }
      await mainDataset.fetchSidecars();
      mainDataset.loadSidecars(dispatch);
      try {
        mainDataset.fetchAvailable();
        dispatch({type: types.SET_AVAILABLE, data: await mainDataset.available});
      } catch (err) {
        console.error("Failed to fetch available datasets", err.message);
        dispatch(warningNotification({message: "Failed to fetch available datasets"}));
      }
    }
  };
};

/* ------------------------------------------------------------------------------------ */

function warnDeprecatedQuerySyntax(dispatch, query) {
  /* Once upon a time one could specify a second tree via a `?tt=tree_name`.
  This is no longer supported, however we still display an error message. */
  if (query.tt) {
    dispatch(errorNotification({
      message: `Specifing a second tree via '?tt=${query.tt}' is no longer supported.`,
      details: "The new syntax requires the complete name for both trees. " +
        "For example, instead of 'flu/seasonal/h3n2/ha/2y?tt=na' you must " +
        "specify 'flu/seasonal/h3n2/ha/2y:flu/seasonal/h3n2/na/2y' "
    }));
  }
}

function narrativeFetchingErrorNotification(err) {
  return errorNotification({
    message: `Error fetching one of the datasets. Using the YAML-defined dataset instead.`,
    details: `Could not fetch dataset(s)specified for this slide.
      Make sure these exist and are spelled correctly.
      Error details:
        status: ${err.status};
        message: ${err.message}`
  });
}

/**
 * Dispatch the action which will result in Auspice displaying the dataset(s).
 */
async function dispatchCleanStart(dispatch, main, second, query, narrativeBlocks) {
  const json = await main.main;
  const secondTreeDataset = second ? (await second.main) : undefined;
  const pathnameShouldBe = second ? `${main.pathname}:${second.pathname}` : main.pathname;
  dispatch({
    type: types.CLEAN_START,
    pathnameShouldBe: narrativeBlocks ? undefined : pathnameShouldBe,
    ...createStateFromQueryOrJSONs({
      json,
      secondTreeDataset,
      query,
      narrativeBlocks,
      mainTreeName: main.pathname,
      secondTreeName: second ? second.pathname : null,
      dispatch
    })
  });
}

async function loadNarrative(dispatch, url, query) {
  /* parse the narrative file itself */
  let blocks;
  try {
    blocks = await fetchAndParseNarrative(url);
  } catch (err) {
    console.error("Error obtaining narratives", err.message);
    return dispatch(goTo404(`Couldn't load narrative for ${url}`));
  }

  /* extract all dataset names defined in the narrative, and create `Dataset` objects for each */
  const {datasets, initialNames, frontmatterNames} = parseNarrativeDatasets(blocks, query);

  /* trigger fetches for the initial dataset(s) & start the visualisation */
  let initialSuccess = true;
  try {
    datasets[initialNames[0]].fetchMain();
    if (initialNames[1]) datasets[initialNames[1]].fetchMain();
    await dispatchCleanStart(dispatch, datasets[initialNames[0]], datasets[initialNames[1]], query, blocks);
  } catch (err1) {
    initialSuccess = false;
    try {
      // if the initial dataset call fails, we try the frontmatter one.
      datasets[frontmatterNames[0]].fetchMain();
      if (frontmatterNames[1]) datasets[frontmatterNames[1]].fetchMain();
      await dispatchCleanStart(dispatch, datasets[frontmatterNames[0]], datasets[frontmatterNames[1]], query, blocks);
      dispatch(narrativeFetchingErrorNotification(err1));
    } catch (err2) {
      return handleFetchErrors(err2, dispatch, "Couldn't load the datasets defined in the narrative.");
    }
  }
  /* fetch & load sidecar files for the main dataset currently being displayed */
  if (initialSuccess) {
    await datasets[initialNames[0]].fetchSidecars();
    datasets[initialNames[0]].loadSidecars(dispatch);
  } else {
    await datasets[frontmatterNames[0]].fetchSidecars();
    datasets[frontmatterNames[0]].loadSidecars(dispatch);
  }

  /* trigger dataset / fetches fill in cache with pending promises */
  Object.values(datasets).forEach((dataset) => {
    dataset.fetchMain();
    dataset.fetchSidecars();
  });
  return dispatch({type: types.CACHE_JSONS, jsons: datasets});
}

function handleFetchErrors(err, dispatch, msg) {
  /* No Content (204) errors are special cases where there is no dataset, but the URL is valid */
  if (err instanceof NoContentError) {
    /* TODO: add more helper functions for moving between pages in auspice */
    dispatch({
      type: types.PAGE_CHANGE,
      displayComponent: "splash",
      pushState: true
    });
  } else {
    console.error(err, err.message);
    dispatch(goTo404(msg));
  }
}

/**
 * @returns {Promise}
 */
function fetchAndParseNarrative(url) {
  const apiCall = `${getServerAddress()}/getNarrative?prefix=${url}&type=md`;
  return fetchWithErrorHandling(apiCall)
    .then((res) => res.text())
    .then((res) => parseMarkdownNarrativeFile(res, parseMarkdown))
    .catch((err) => {
      /* Note that up until v2.16 the client expected the narrative to have been converted
      to JSON by the server. To facilitate backward compatibility (e.g. in the case where
      the client is >2.16, but the server is using an older version of the API)
      if the file doesn't look like markdown we will attempt to parse it as JSON.
      Errors from `parseMarkdownNarrativeFile` indicating that the file doesn't look
      like markdown will have the fileContents attached to them */
      if (!err.fileContents) throw err;
      console.error("Narrative file doesn't appear to be markdown! Attempting to parse as JSON.");
      return JSON.parse(err.fileContents);
    })
    .then((blocks) => {
      addEndOfNarrativeBlock(blocks);
      return blocks;
    });
}

function addEndOfNarrativeBlock(narrativeBlocks) {
  const lastContentSlide = narrativeBlocks[narrativeBlocks.length-1];
  const endOfNarrativeSlide = Object.assign({}, lastContentSlide, {
    __html: undefined,
    isEndOfNarrativeSlide: true
  });
  narrativeBlocks.push(endOfNarrativeSlide);
}

/**
 * This function returns `Dataset` objects for each of the different datasets in a narrative.
 * Note: "initial": starting page, potentially different to frontmatter
 */
function parseNarrativeDatasets(blocks, query) {
  let [datasets, initialNames, frontmatterNames] = [{}, [], []];
  const initialBlockIdx = getNarrativePageFromQuery(query, blocks);
  blocks.forEach((block, idx) => {
    const [main, second] = getDatasetNamesFromUrl(block.dataset);
    if (!datasets[main]) datasets[main] = new Dataset(main);
    if (second && !datasets[second]) datasets[second] = new Dataset(second);
    if (idx===0) {frontmatterNames = [main, second];}
    if (idx===initialBlockIdx) {initialNames = [main, second];}
  });
  return {datasets, initialNames, frontmatterNames};
}

/**
 * The Dataset object holds information about a single dataset (e.g. "zika") and
 * corresponding sidecar files. It contains prototypes to help with fetching and loading
 * of these data.
 */
function Dataset(name) {
  this.name = name;
  this.pathname = undefined; // the dataset name to show. May be different from the name.
  this.apiCalls = {};
  this.apiCalls.main = `${getServerAddress()}/getDataset?prefix=${name}`;
  this.apiCalls.tipFrequencies = `${this.apiCalls.main}&type=tip-frequencies`;
  this.apiCalls.rootSequence = `${this.apiCalls.main}&type=root-sequence`;
  this.apiCalls.measurements = `${this.apiCalls.main}&type=measurements`;
  this.apiCalls.getAvailable = `${getServerAddress()}/getAvailable?prefix=${name}`;
}
Dataset.prototype.fetchMain = function fetchMain() {
  if (this.main) return;
  this.main = fetchWithErrorHandling(this.apiCalls.main)
    .then((res) => {
      this.pathname = queryString.parse(res.url.split("?")[1]).prefix;
      if (this.pathname!==this.name) {
        // eslint-disable-next-line no-console
        console.log(`Pathname for "${this.name}" changing to "${this.pathname}".`);
      }
      return res;
    })
    .then((res) => res.json());
};
Dataset.prototype.fetchSidecars = async function fetchSidecars() {
  const mainJson = await this.main;
  if (!mainJson) throw new Error("Cannot fetch sidecar files since the main JSON didn't succeed.");

  if (mainJson.meta.panels && mainJson.meta.panels.includes("frequencies") && !this.tipFrequencies) {
    this.tipFrequencies = fetchJSON(this.apiCalls.tipFrequencies)
      .catch((err) => {
        console.error("Failed to fetch frequencies", err.message);
      });
  }
  if (!this.rootSequence) {
    this.rootSequence = fetchJSON(this.apiCalls.rootSequence)
      .catch(() => {}); // it's not unexpected to be missing the root-sequence JSON
  }
  if (mainJson.meta.panels && mainJson.meta.panels.includes("measurements") && !this.measurements) {
    this.measurements = fetchJSON(this.apiCalls.measurements)
      .catch((err) => {
        console.error("Failed to fetch measurements collections", err.message);
      });
  }
};
Dataset.prototype.loadSidecars = function loadSidecars(dispatch) {
  // Helper function to load (dispatch) the visualisation of sidecar files
  if (this.tipFrequencies) {
    this.tipFrequencies
      .then((data) => dispatch(loadFrequencies(data)))
      .catch(() => {
        dispatch(warningNotification({message: "Failed to fetch frequencies"}));
      });
  }
  this.rootSequence.then((data) => {
    dispatch({type: types.SET_ROOT_SEQUENCE, data});
    dispatch(updateColorByWithRootSequenceData());
  });
  if (this.measurements) {
    this.measurements
      .then((data) => dispatch(loadMeasurements(data)))
      .catch((err) => {
        const errorMessage = "Failed to load measurements collections";
        console.error(errorMessage, err.message);
        dispatch(warningNotification({message: errorMessage}));
        // Hide measurements panel
        dispatch(togglePanelDisplay("measurements"));
        // Save error message to display if user toggles panel again
        dispatch({ type: types.UPDATE_MEASUREMENTS_ERROR, data: errorMessage });
      });
  }
};
Dataset.prototype.fetchAvailable = async function fetchSidecars() {
  this.available = fetchJSON(this.apiCalls.getAvailable);
};
<commit_msg>Export functions<commit_after>import queryString from "query-string";
import * as types from "./types";
import { getServerAddress } from "../util/globals";
import { goTo404 } from "./navigation";
import { createStateFromQueryOrJSONs, createTreeTooState, getNarrativePageFromQuery } from "./recomputeReduxState";
import { loadFrequencies } from "./frequencies";
import { loadMeasurements } from "./measurements";
import { fetchJSON, fetchWithErrorHandling } from "../util/serverInteraction";
import { warningNotification, errorNotification } from "./notifications";
import { parseMarkdownNarrativeFile } from "../util/parseNarrative";
import { NoContentError } from "../util/exceptions";
import { parseMarkdown } from "../util/parseMarkdown";
import { updateColorByWithRootSequenceData } from "../actions/colors";
import { explodeTree } from "./tree";
import { togglePanelDisplay } from "./panelDisplay";

export function getDatasetNamesFromUrl(url) {
  let secondTreeUrl;
  if (url.includes(":")) {
    const parts = url.replace(/^\//, '')
      .replace(/\/$/, '')
      .split(":");
    url = parts[0]; // eslint-disable-line no-param-reassign
    secondTreeUrl = parts[1];
  }
  if (url.startsWith('/')) url = url.slice(1); // eslint-disable-line no-param-reassign
  if (secondTreeUrl && secondTreeUrl.startsWith('/')) {
    secondTreeUrl = secondTreeUrl.slice(1);
  }
  return [url, secondTreeUrl];
}

export const loadSecondTree = (secondTreeUrl, firstTreeUrl) => async (dispatch, getState) => {

  const dataset = new Dataset(secondTreeUrl);
  let secondJson;
  try {
    dataset.fetchMain();
    secondJson = await dataset.main;
  } catch (err) {
    console.error("Failed to fetch additional tree", err.message);
    dispatch(warningNotification({message: "Failed to fetch second tree"}));
    return;
  }

  const oldState = getState();

  /* if the first tree is exploded, then reconstruct it before loading a second tree */
  if (oldState.controls.explodeAttr) {
    dispatch(explodeTree(undefined));
  }

  const newState = createTreeTooState({treeTooJSON: secondJson.tree, oldState, originalTreeUrl: firstTreeUrl, secondTreeUrl: secondTreeUrl, dispatch});
  dispatch({type: types.TREE_TOO_DATA, ...newState});
};

export const loadJSONs = ({url = window.location.pathname, search = window.location.search} = {}) => {
  return async (dispatch, getState) => {
    const { tree } = getState();
    if (tree.loaded) {
      dispatch({type: types.DATA_INVALID}); // "resets" state
    }
    const query = queryString.parse(search);
    if (url.includes("/narratives/")) {
      loadNarrative(dispatch, url, query);
    } else {
      warnDeprecatedQuerySyntax(dispatch, query);
      const [mainName, secondName] = getDatasetNamesFromUrl(url);
      const mainDataset = new Dataset(mainName);
      const secondDataset = secondName ? new Dataset(secondName) : undefined;
      try {
        mainDataset.fetchMain();
        if (secondDataset) secondDataset.fetchMain();
        await dispatchCleanStart(dispatch, mainDataset, secondDataset, query);
      } catch (err) {
        const msg = `Couldn't load JSONs for ${mainDataset.name} ${secondDataset ? `and/or ${secondDataset.name}` : ''}`;
        handleFetchErrors(err, dispatch, msg);
        return;
      }
      await mainDataset.fetchSidecars();
      mainDataset.loadSidecars(dispatch);
      try {
        mainDataset.fetchAvailable();
        dispatch({type: types.SET_AVAILABLE, data: await mainDataset.available});
      } catch (err) {
        console.error("Failed to fetch available datasets", err.message);
        dispatch(warningNotification({message: "Failed to fetch available datasets"}));
      }
    }
  };
};

/* ------------------------------------------------------------------------------------ */

function warnDeprecatedQuerySyntax(dispatch, query) {
  /* Once upon a time one could specify a second tree via a `?tt=tree_name`.
  This is no longer supported, however we still display an error message. */
  if (query.tt) {
    dispatch(errorNotification({
      message: `Specifing a second tree via '?tt=${query.tt}' is no longer supported.`,
      details: "The new syntax requires the complete name for both trees. " +
        "For example, instead of 'flu/seasonal/h3n2/ha/2y?tt=na' you must " +
        "specify 'flu/seasonal/h3n2/ha/2y:flu/seasonal/h3n2/na/2y' "
    }));
  }
}

function narrativeFetchingErrorNotification(err) {
  return errorNotification({
    message: `Error fetching one of the datasets. Using the YAML-defined dataset instead.`,
    details: `Could not fetch dataset(s)specified for this slide.
      Make sure these exist and are spelled correctly.
      Error details:
        status: ${err.status};
        message: ${err.message}`
  });
}

/**
 * Dispatch the action which will result in Auspice displaying the dataset(s).
 */
async function dispatchCleanStart(dispatch, main, second, query, narrativeBlocks) {
  const json = await main.main;
  const secondTreeDataset = second ? (await second.main) : undefined;
  const pathnameShouldBe = second ? `${main.pathname}:${second.pathname}` : main.pathname;
  dispatch({
    type: types.CLEAN_START,
    pathnameShouldBe: narrativeBlocks ? undefined : pathnameShouldBe,
    ...createStateFromQueryOrJSONs({
      json,
      secondTreeDataset,
      query,
      narrativeBlocks,
      mainTreeName: main.pathname,
      secondTreeName: second ? second.pathname : null,
      dispatch
    })
  });
}

async function loadNarrative(dispatch, url, query) {
  /* parse the narrative file itself */
  let blocks;
  try {
    blocks = await fetchAndParseNarrative(url);
  } catch (err) {
    console.error("Error obtaining narratives", err.message);
    return dispatch(goTo404(`Couldn't load narrative for ${url}`));
  }

  /* extract all dataset names defined in the narrative, and create `Dataset` objects for each */
  const {datasets, initialNames, frontmatterNames} = parseNarrativeDatasets(blocks, query);

  /* trigger fetches for the initial dataset(s) & start the visualisation */
  let initialSuccess = true;
  try {
    datasets[initialNames[0]].fetchMain();
    if (initialNames[1]) datasets[initialNames[1]].fetchMain();
    await dispatchCleanStart(dispatch, datasets[initialNames[0]], datasets[initialNames[1]], query, blocks);
  } catch (err1) {
    initialSuccess = false;
    try {
      // if the initial dataset call fails, we try the frontmatter one.
      datasets[frontmatterNames[0]].fetchMain();
      if (frontmatterNames[1]) datasets[frontmatterNames[1]].fetchMain();
      await dispatchCleanStart(dispatch, datasets[frontmatterNames[0]], datasets[frontmatterNames[1]], query, blocks);
      dispatch(narrativeFetchingErrorNotification(err1));
    } catch (err2) {
      return handleFetchErrors(err2, dispatch, "Couldn't load the datasets defined in the narrative.");
    }
  }
  /* fetch & load sidecar files for the main dataset currently being displayed */
  if (initialSuccess) {
    await datasets[initialNames[0]].fetchSidecars();
    datasets[initialNames[0]].loadSidecars(dispatch);
  } else {
    await datasets[frontmatterNames[0]].fetchSidecars();
    datasets[frontmatterNames[0]].loadSidecars(dispatch);
  }

  /* trigger dataset / fetches fill in cache with pending promises */
  Object.values(datasets).forEach((dataset) => {
    dataset.fetchMain();
    dataset.fetchSidecars();
  });
  return dispatch({type: types.CACHE_JSONS, jsons: datasets});
}

function handleFetchErrors(err, dispatch, msg) {
  /* No Content (204) errors are special cases where there is no dataset, but the URL is valid */
  if (err instanceof NoContentError) {
    /* TODO: add more helper functions for moving between pages in auspice */
    dispatch({
      type: types.PAGE_CHANGE,
      displayComponent: "splash",
      pushState: true
    });
  } else {
    console.error(err, err.message);
    dispatch(goTo404(msg));
  }
}

/**
 * @returns {Promise}
 */
function fetchAndParseNarrative(url) {
  const apiCall = `${getServerAddress()}/getNarrative?prefix=${url}&type=md`;
  return fetchWithErrorHandling(apiCall)
    .then((res) => res.text())
    .then((res) => parseMarkdownNarrativeFile(res, parseMarkdown))
    .catch((err) => {
      /* Note that up until v2.16 the client expected the narrative to have been converted
      to JSON by the server. To facilitate backward compatibility (e.g. in the case where
      the client is >2.16, but the server is using an older version of the API)
      if the file doesn't look like markdown we will attempt to parse it as JSON.
      Errors from `parseMarkdownNarrativeFile` indicating that the file doesn't look
      like markdown will have the fileContents attached to them */
      if (!err.fileContents) throw err;
      console.error("Narrative file doesn't appear to be markdown! Attempting to parse as JSON.");
      return JSON.parse(err.fileContents);
    })
    .then((blocks) => {
      addEndOfNarrativeBlock(blocks);
      return blocks;
    });
}

/* this function is exported as auspice.us uses it */
export function addEndOfNarrativeBlock(narrativeBlocks) {
  const lastContentSlide = narrativeBlocks[narrativeBlocks.length-1];
  const endOfNarrativeSlide = Object.assign({}, lastContentSlide, {
    __html: undefined,
    isEndOfNarrativeSlide: true
  });
  narrativeBlocks.push(endOfNarrativeSlide);
}

/**
 * This function returns `Dataset` objects for each of the different datasets in a narrative.
 * Note: "initial": starting page, potentially different to frontmatter
 */
function parseNarrativeDatasets(blocks, query) {
  let [datasets, initialNames, frontmatterNames] = [{}, [], []];
  const initialBlockIdx = getNarrativePageFromQuery(query, blocks);
  blocks.forEach((block, idx) => {
    const [main, second] = getDatasetNamesFromUrl(block.dataset);
    if (!datasets[main]) datasets[main] = new Dataset(main);
    if (second && !datasets[second]) datasets[second] = new Dataset(second);
    if (idx===0) {frontmatterNames = [main, second];}
    if (idx===initialBlockIdx) {initialNames = [main, second];}
  });
  return {datasets, initialNames, frontmatterNames};
}

/**
 * The Dataset object holds information about a single dataset (e.g. "zika") and
 * corresponding sidecar files. It contains prototypes to help with fetching and loading
 * of these data.
 * This function is exported as auspice.us uses it.
 */
export function Dataset(name) {
  this.name = name;
  this.pathname = undefined; // the dataset name to show. May be different from the name.
  this.apiCalls = {};
  this.apiCalls.main = `${getServerAddress()}/getDataset?prefix=${name}`;
  this.apiCalls.tipFrequencies = `${this.apiCalls.main}&type=tip-frequencies`;
  this.apiCalls.rootSequence = `${this.apiCalls.main}&type=root-sequence`;
  this.apiCalls.measurements = `${this.apiCalls.main}&type=measurements`;
  this.apiCalls.getAvailable = `${getServerAddress()}/getAvailable?prefix=${name}`;
}
Dataset.prototype.fetchMain = function fetchMain() {
  if (this.main) return;
  this.main = fetchWithErrorHandling(this.apiCalls.main)
    .then((res) => {
      this.pathname = queryString.parse(res.url.split("?")[1]).prefix;
      if (this.pathname!==this.name) {
        // eslint-disable-next-line no-console
        console.log(`Pathname for "${this.name}" changing to "${this.pathname}".`);
      }
      return res;
    })
    .then((res) => res.json());
};
Dataset.prototype.fetchSidecars = async function fetchSidecars() {
  const mainJson = await this.main;
  if (!mainJson) throw new Error("Cannot fetch sidecar files since the main JSON didn't succeed.");

  if (mainJson.meta.panels && mainJson.meta.panels.includes("frequencies") && !this.tipFrequencies) {
    this.tipFrequencies = fetchJSON(this.apiCalls.tipFrequencies)
      .catch((err) => {
        console.error("Failed to fetch frequencies", err.message);
      });
  }
  if (!this.rootSequence) {
    this.rootSequence = fetchJSON(this.apiCalls.rootSequence)
      .catch(() => {}); // it's not unexpected to be missing the root-sequence JSON
  }
  if (mainJson.meta.panels && mainJson.meta.panels.includes("measurements") && !this.measurements) {
    this.measurements = fetchJSON(this.apiCalls.measurements)
      .catch((err) => {
        console.error("Failed to fetch measurements collections", err.message);
      });
  }
};
Dataset.prototype.loadSidecars = function loadSidecars(dispatch) {
  // Helper function to load (dispatch) the visualisation of sidecar files
  if (this.tipFrequencies) {
    this.tipFrequencies
      .then((data) => dispatch(loadFrequencies(data)))
      .catch(() => {
        dispatch(warningNotification({message: "Failed to fetch frequencies"}));
      });
  }
  if (this.rootSequence) {
    this.rootSequence.then((data) => {
      dispatch({type: types.SET_ROOT_SEQUENCE, data});
      dispatch(updateColorByWithRootSequenceData());
    });
  }
  if (this.measurements) {
    this.measurements
      .then((data) => dispatch(loadMeasurements(data)))
      .catch((err) => {
        const errorMessage = "Failed to load measurements collections";
        console.error(errorMessage, err.message);
        dispatch(warningNotification({message: errorMessage}));
        // Hide measurements panel
        dispatch(togglePanelDisplay("measurements"));
        // Save error message to display if user toggles panel again
        dispatch({ type: types.UPDATE_MEASUREMENTS_ERROR, data: errorMessage });
      });
  }
};
Dataset.prototype.fetchAvailable = async function fetchSidecars() {
  this.available = fetchJSON(this.apiCalls.getAvailable);
};
<|endoftext|>


<commit_before>// @flow

import 'babel-polyfill'
import $ from 'jquery'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap'

const apiBaseUri = 'http://localhost:8080/rest.php'

async function sendAjaxRequest(ajaxOptions) {
  // Convert jQuery thenables into native Promises
  const data = await Promise.resolve($.ajax(ajaxOptions))
  // if (data.status !== 'OK') {
  //   throw new Error('Server status !== OK')
  // }
  return data
}

async function submitCredentials(username, password) {
  const ajaxOptions = {
    method: 'POST',
    url: `${apiBaseUri}/v1/user`,
    data: JSON.stringify({
      user: username,
      password,
    }),
    contentType: 'application/json',
    dataType: 'json'
  }

  const userToken = await sendAjaxRequest(ajaxOptions)

  const {
    status,
    msg,
    token,
  } = userToken

  if (status !== 'OK') {
    throw new Error(msg);
  }

  return token
}

async function getItems() {
  const itemDemand = {
    method: 'GET',
    url: `${apiBaseUri}/v1/items`,
    contentType: 'application/json',
    dataType: 'json'
  }
  const holder = sendAjaxRequest(itemDemand)

  const {
    status,
    msg,
    items,
  } = objectHold

  return items
}

async function getConsumedItems() {
  const itemsConsumed = {
    method: 'GET',
    url: `${apiBaseUri}/items/token`,
    contentType: 'application/json',
    dataType: 'json'
  }
  const consumed = sendAjaxRequest(itemsConsumed)

  const {
    status,
    pk,
    msg: text,
    item,
    timestamp,
  } = consumedCollection

  if(status !=='OK'){
    throw new Error(msg)
  }
  return itemCollection
}

async function getItemSummary() {
  const itemsSummarized = {
    method: 'GET',
    url: `${apiBaseUri}/v1/itemsSummary/token`,
    contentType: 'application/json',
    dataType: 'json'
  }

  const summarized = sendAjaxRequest(itemsSummarized);

  const {
status,
    msg,
    items[item, count =0]

  } = summarizedCollection
  return summarizedCollection
}

async function updateItem() {
  const itemsUpdated= {
    method: 'POST',
    url: `${apiBaseUri}/v1/items`,
    data: JSON.stringify({
      token,
      itemFK
    }),
    contentType: 'application/json',
    dataType: 'json'
  }

  const updated = sendAjaxRequest(itemsUpdated);
  const {
    status,
    msg
  } = updateCollection
  if(status !== 'OK'){
    throw new Error(msg)
  }
}

$(document).ready(() => {
  // TODO: Add event handlers
  $('#login-form').submit((evt) => {
    evt.preventDefault()
    const formData = {}
    for (const {name, value} of $('#login-form').serializeArray()) {
      formData[name] = value
    }
    const {
      username,
      password
    } = formData

    const errorMessageElem = $('#error-message')

    const login = submitCredentials(username, password)
      .catch(() => errorMessageElem.text('Invalid login').show())
      .then(() => errorMessageElem.hide())
    Promise.all([
      login.then(() => getItems())
      //append to the following line.
        .then(items => items.forEach(({ pk, item }) => $('<button></button>').text(item).click(() => updateItem(pk)))),
      login.then(() => getConsumedItems())
        .then(() => $()),
      login.then(() => getItemSummary())
        .then(() => $()),
    ])
      .catch(() => errorMessageElem.text('Error getting data').show())
  })
})
<commit_msg>Adding skeleton to Rest API ajax calls<commit_after>// @flow

import 'babel-polyfill'
import $ from 'jquery'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap'

const apiBaseUri = 'http://localhost:8080/rest.php'

async function sendAjaxRequest(ajaxOptions) {
  // Convert jQuery thenables into native Promises
  const data = await Promise.resolve($.ajax(ajaxOptions))
  // if (data.status !== 'OK') {
  //   throw new Error('Server status !== OK')
  // }
  return data
}

async function submitCredentials(username, password) {
  const ajaxOptions = {
    method: 'POST',
    url: `${apiBaseUri}/v1/user`,
    data: JSON.stringify({
      user: username,
      password,
    }),
    contentType: 'application/json',
    dataType: 'json'
  }

  const userToken = await sendAjaxRequest(ajaxOptions)

  const {
    status,
    msg,
    token,
  } = userToken

  if (status !== 'OK') {
    throw new Error(msg);
  }

  return token
}

async function getItems() {
  const itemDemand = {
    method: 'GET',
    url: `${apiBaseUri}/v1/items`,
    contentType: 'application/json',
    dataType: 'json'
  }
  const holder = sendAjaxRequest(itemDemand)

  const {
    status,
    msg,
    items,
  } = objectHold

  return items
}

async function getConsumedItems() {
  const itemsConsumed = {
    method: 'GET',
    url: `${apiBaseUri}/items/token`,
    contentType: 'application/json',
    dataType: 'json'
  }
  const consumed = sendAjaxRequest(itemsConsumed)

  const {
    status,
    pk,
    msg: text,
    item,
    timestamp,
  } = consumedCollection

  if(status !=='OK'){
    throw new Error(msg)
  }
  return itemCollection
}

async function getItemSummary() {
  const itemsSummarized = {
    method: 'GET',
    url: `${apiBaseUri}/v1/itemsSummary/token`,
    contentType: 'application/json',
    dataType: 'json'
  }

  const summarized = sendAjaxRequest(itemsSummarized);

  const {
status,
    msg,
 //   items[item, count =0]

  } = summarizedCollection
  return summarizedCollection
}

async function updateItem() {
  const itemsUpdated= {
    method: 'POST',
    url: `${apiBaseUri}/v1/items`,
    data: JSON.stringify({
      token,
      itemFK
    }),
    contentType: 'application/json',
    dataType: 'json'
  }

  const updated = sendAjaxRequest(itemsUpdated);
  const {
    status,
    msg
  } = updateCollection
  if(status !== 'OK'){
    throw new Error(msg)
  }
}

$(document).ready(() => {
  // TODO: Add event handlers
  $('#login-form').submit((evt) => {
    evt.preventDefault()
    const formData = {}
    for (const {name, value} of $('#login-form').serializeArray()) {
      formData[name] = value
    }
    const {
      username,
      password
    } = formData

    const errorMessageElem = $('#error-message')

    const login = submitCredentials(username, password)
      .catch(() => errorMessageElem.text('Invalid login').show())
      .then(() => errorMessageElem.hide())
    Promise.all([
      login.then(() => getItems())
      //append to the following line.
        .then(items => items.forEach(({ pk, item }) => $('<button></button>').text(item).click(() => updateItem(pk)))),
      login.then(() => getConsumedItems())
        .then(() => $()),
      login.then(() => getItemSummary())
        .then(() => $()),
    ])
      .catch(() => errorMessageElem.text('Error getting data').show())
  })
})
<|endoftext|>


<commit_before>'use strict';
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var yosay = require('yosay');
var path = require('path');
var _ = require('lodash');

module.exports = yeoman.generators.Base.extend({
  initializing: function () {
    var done = this.async();

    // Have Yeoman greet the user.
    this.log(yosay(
      'Welcome to the amazing ' + chalk.red('Node TypeScript') + ' generator!'
      ));

    this.log(
      chalk.cyan('I simply get down to business of generating, no questions asked!')
      + '\n'
      + chalk.yellow('Libraries you ask? I use gulp, jasmine, tslint and I compile TypeScript to CommonJS modules using tsconfig.json.')
      + '\n'
      + chalk.gray('Can you change these? Of course, it\'s your library. I get out of the way after scaffolding.')
      );

    done();
  },

  writing: {
    dir: function () {
      this.directory('src', 'src');
      this.directory('test', 'test');
      this.directory('_vscode', '.vscode');
    },

    app: function () {
      this.fs.copyTpl(
        this.templatePath('_package.json'),
        this.destinationPath('package.json'),
        { appname: _.kebabCase(path.basename(process.cwd())) }
        );
    },

    projectfiles: function () {
      this.fs.copy(
        this.templatePath('_gulpfile.js'),
        this.destinationPath('gulpfile.js'),
        { appname: _.kebabCase(path.basename(process.cwd())) }
        );
      this.fs.copy(
        this.templatePath('_tsconfig.json'),
        this.destinationPath('tsconfig.json')
        );
      this.fs.copy(
        this.templatePath('_tslint.json'),
        this.destinationPath('tslint.json')
        );
      this.fs.copy(
        this.templatePath('editorconfig'),
        this.destinationPath('.editorconfig')
        );
      this.fs.copy(
        this.templatePath('gitignore'),
        this.destinationPath('.gitignore')
        );
    }
  },

  install: {
    npmInstall: function () {
      var generator = this;
      generator.npmInstall(null, { skipInstall: this.options['skip-install'] }, function () {
        //generator.spawnCommandSync('typings', ['init']); //typings init
        //generator.spawnCommandSync('typings', ['install', 'dt~node', '--save', '--global']) //typings install --save dt~node --global
      });
  }
  }
});
<commit_msg>Reformat<commit_after>'use strict';
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var yosay = require('yosay');
var path = require('path');
var _ = require('lodash');

module.exports = yeoman.generators.Base.extend({
  initializing: function () {
    var done = this.async();

    // Have Yeoman greet the user.
    this.log(yosay(
      'Welcome to the amazing ' + chalk.red('Node TypeScript') + ' generator!'
    ));

    this.log(
      chalk.cyan('I simply get down to business of generating, no questions asked!')
      + '\n'
      + chalk.yellow('Libraries you ask? I use gulp, jasmine, tslint and I compile TypeScript to CommonJS modules using tsconfig.json.')
      + '\n'
      + chalk.gray('Can you change these? Of course, it\'s your library. I get out of the way after scaffolding.')
    );

    done();
  },

  writing: {
    dir: function () {
      this.directory('src', 'src');
      this.directory('test', 'test');
      this.directory('_vscode', '.vscode');
    },

    app: function () {
      this.fs.copyTpl(
        this.templatePath('_package.json'),
        this.destinationPath('package.json'),
        { appname: _.kebabCase(path.basename(process.cwd())) }
      );
    },

    projectfiles: function () {
      this.fs.copy(
        this.templatePath('_gulpfile.js'),
        this.destinationPath('gulpfile.js'),
        { appname: _.kebabCase(path.basename(process.cwd())) }
      );
      this.fs.copy(
        this.templatePath('_tsconfig.json'),
        this.destinationPath('tsconfig.json')
      );
      this.fs.copy(
        this.templatePath('_tslint.json'),
        this.destinationPath('tslint.json')
      );
      this.fs.copy(
        this.templatePath('editorconfig'),
        this.destinationPath('.editorconfig')
      );
      this.fs.copy(
        this.templatePath('gitignore'),
        this.destinationPath('.gitignore')
      );
    }
  },

  install: {
    npmInstall: function () {
      var generator = this;
      generator.npmInstall(null, { skipInstall: this.options['skip-install'] }, function () {
        //generator.spawnCommandSync('typings', ['init']); //typings init
        //generator.spawnCommandSync('typings', ['install', 'dt~node', '--save', '--global']) //typings install --save dt~node --global
      });
    }
  }
});
<|endoftext|>


<commit_before>
<commit_msg>Update timeline.js<commit_after>function timeline(BandId, Totalalbums)
{
  // removing the timeline container
  d3.select('#container').remove()

  // changing the text for the timeline
  document.getElementById("TimelineText").innerHTML = BandId + " timeline";

  // adding the legend when the timeline function starts
  document.getElementById("legend").style.visibility = "visible";

  // loading in the artistlist
  d3.json("artistlist.json", function(error, data) {

  data.forEach(function(d) {
    d.id = d.id;
    d.lane = d.lane;
    d.lanenr = +d.lanenr;
    d.start = +d.start;
    d.end = +d.end;
    d.band = d.band;
    d.number = +d.number;
  });

  // defining the artists in a band
  var lanes = []
  var Allitems = []
  var item = {}
  var highest = 0;
  var lowest = 3000;

  for (var i = 0; i < data.length; i++)
  {
    if (BandId == data[i].band)
    {
      item[i] =
      {
        'lane': data[i].lanenr,
        'id': data[i].id,
        'start': data[i].start,
        'end': data[i].end
      }
      Allitems.push(item[i]);
      if (highest < data[i].end)
      {
        highest = data[i].end;
      }
      if (lowest > data[i].start)
      {
        lowest = data[i].start;
      }
      if (data[i].number == 1)
      {
        lanes.push(data[i].lane);
      }
    }
  }

  // change the years of the timeline
  var begin = lowest - 1;
  var end = highest + 1;

  // adding the lanelength, items, begintime and endtime
  laneLength = lanes.length,
  items = Allitems;
  timeBegin = begin,
  timeEnd = end;

  // defining the width, height and margins
  var m = [20, 15, 10, 120], //top right bottom lef
  w = 960 - m[1] - m[3],
  h = 920 - m[0] - m[2],
  timelineHeight = laneLength * 12 + 50,
  mainHeight = h - timelineHeight - 50;

  // scales
  var x = d3.scale.linear()
  .domain([timeBegin, timeEnd])
  .range([0, w]);
  var x1 = d3.scale.linear()
  .range([0, w]);
  var y1 = d3.scale.linear()
  .domain([0, laneLength])
  .range([0, timelineHeight]);
  var y2 = d3.scale.linear()
  .domain([0, laneLength])
  .range([0, timelineHeight]);

  // text for the tooltip
  var tip = d3.tip()
  .attr('class', 'd3-tip')
  .offset([-10, 0])
  .html(function(d) {
  return "" + lanes[d.lane] + ", " + d.id + " (" + d.start + ", " + d.end + ")" + "";
  });

  // container for the scroll function
  var container = d3.select('body').append('div')
  .attr('id','container');

  // adding the timelinesvg to the container
  var chart = container.append("svg")
    .attr("id", "timeLine")
    .attr("width", w + m[1] + m[3])
    .attr("height", h + m[0] + m[2])
    .attr("class", "chart");

  // call tip
  chart.call(tip);

  // adding the x axis
  var xAxis = d3.svg.axis()
        .orient("bottom")
        .scale(x)
        .tickFormat(d3.format("d"));

  chart.append("g")
          .attr("class", "xaxis")
          .attr("transform", "translate(" + m[3] + "," + (h) + ")")
          .call(xAxis);

  var timeline = chart.append("g")
    .attr("transform", "translate(" + m[3] + "," + (mainHeight + m[0]) + ")")
    .attr("width", w)
    .attr("height", timelineHeight)
    .attr("class", "timeline");

  // timeline lanes and texts
  timeline.append("g").selectAll(".laneLines")
  .data(items)
  .enter().append("line")
  .attr("x1", m[1])
  .attr("y1", function(d) {return y2(d.lane);})
  .attr("x2", w)
  .attr("y2", function(d) {return y2(d.lane);})
  .attr("stroke", "lightgray");

  // adding the names to the timeline
  timeline.append("g").selectAll(".laneText")
  .data(lanes)
  .enter().append("text")
  .text(function(d) {return d;})
  .attr("x", -m[1])
  .attr("y", function(d, i) {return y2(i + .5);})
  .attr("dy", ".5ex")
  .attr("text-anchor", "end")
  .attr("class", "laneText");

  // adding the timeline item rects
  timeline.append("g").selectAll("timelineItems")
  .data(items)
  .enter().append("rect")
  .attr("class", function(d) {return "timelineItem" + d.id;})
  .attr("x", function(d) {return x(d.start);})
  .attr("y", function(d) {return y2(d.lane + .5) - 5;})
  .attr("width", function(d) {return ((x(d.end)) - (x(d.start)));})
  .attr("height", function(d) {if (['voc','flute2','key2','bass2','harmonica2','piano2'].indexOf(d.id) >= 0)
    {
      return 4;
    }
    else
    {
      return 10;
    }
  })
  .on('mouseover', tip.show)
  .on('mouseout', tip.hide);

  // scroll down the container
  var element = document.getElementById("container");
  element.scrollTop = element.scrollHeight;

  // when the visualisations is made go to the bottom of the page
  window.scrollTo(0,document.body.scrollHeight);
  });
}

<|endoftext|>


<commit_before>import { runInDebug } from '@ember/debug';
import { test } from 'qunit';

let canRunDebugFunctions = false;

runInDebug(() => (canRunDebugFunctions = true));

export default function debugTest(description, callback) {
  return test(description, function (assert) {
    if (!canRunDebugFunctions) {
      assert.ok(true, 'debug functions are disabled');
      return;
    }

    return callback.apply(this, arguments);
  });
}
<commit_msg>Refactor debug check<commit_after>import { DEBUG } from '@glimmer/env'
import { test } from 'qunit';

export default function debugTest(description, callback) {
  return test(description, function (assert) {
    if (!DEBUG) {
      assert.ok(true, 'debug functions are disabled');
      return;
    }

    return callback.apply(this, arguments);
  });
}
<|endoftext|>


<commit_before>// run grunt --help for help on how to run
define([
	"./intern"
], function (intern) {
	intern.useSauceConnect = false;
	intern.webdriver = {
		host: "localhost",
		port: 4444
	};

	intern.environments = [
		{ browserName: "firefox" }
	];

	intern.maxConcurrency = 1;

	return intern;
});<commit_msg>Revert intern.local.js: environments mistakenly removed<commit_after>// run grunt --help for help on how to run
define([
	"./intern"
], function (intern) {
	intern.useSauceConnect = false;
	intern.webdriver = {
		host: "localhost",
		port: 4444
	};

	intern.environments = [
		{ browserName: "firefox" },
		{ browserName: "safari" },
		{ browserName: "chrome" },
		{ browserName: "internet explorer", requireWindowFocus: "true" }
	];

	intern.maxConcurrency = 1;

	return intern;
});<|endoftext|>


<commit_before>Core.Object.extend('Core.HttpRequest',
/**@lends Core.HttpRequest#*/
{
	url: undefined,
	method: 'GET',
	binary: false,
	request: undefined,
	arguments: undefined,

	/**
	*	@constructs
	*	@class
	*	@extends Core.Object
	*/
	constructor: function(config) {
		this.addEvents('error', 'done');

		this.request = new XMLHttpRequest();

		var wrapper = function() {
			var httprequest = arguments.callee.httprequest;
			if(httprequest.request.readyState == 4) {
				if(httprequest.request.status == 200)
					httprequest.fireEvent('done', httprequest);
				else
					httprequest.fireEvent('error', httprequest, httprequest.request.status);
			}
		}
		wrapper.httprequest = this;
		this.request.onreadystatechange = wrapper;
	},

	setUrl: function(url) {
		this.url = url;
	},

	setBinary: function(binary) {
		this.binary = binary;
	},

	setMethod: function(method) {
		this.method = method;
	},

	setRequestHeader: function(header, value) {
		this.request.setRequestHeader(header, value);
	},

	setArguments: function(arguments) {
		this.arguments = arguments;
	},

	addArgument: function(argName, argValue) {
		if(this.arguments == undefined)
			this.arguments = {};
		this.arguments[argName] = argValue;
	},

	abort: function() {
		this.request.abort();
	},

	send: function() {
		if(this.url == undefined)
			throw('url MUST be given for an HttpRequest');
		// encode arguments
		var args = '';
		if(this.arguments != undefined) {
			args = Core.Util.encodeURIQuery(this.arguments);
		}
		var url = this.url;
		if(((this.method == 'GET') || (this.method == 'DELETE')) && (args != '')) {
			if(this.url.indexOf('?') == -1)
				url += '?'+args;
			else
				url += '&'+args;
		}
		this.request.open(this.method, url, true);
		if(this.binary)
			this.request.overrideMimeType('text/plain; charset=x-user-defined');
		this.request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
		if(this.method == 'POST')
			this.request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
		if((this.method == 'POST') && (args != ''))
			this.request.send(args);
		else
			this.request.send();
	},

	getResponseText: function() {
		return this.request.responseText;
	},

	getResponseBase64: function() {
		var value = this.request.responseText;
		var code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		var res = '';
		var i = 0;
		while(i + 2 < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var val2 = value.charCodeAt(i++) & 0xff;
			var val3 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[((val1 & 3) << 4) | (val2 >> 4)];
			var enc3 = code[((val2 & 15) << 2) | (val3 >> 6)];
			var enc4 = code[val3 & 63];
			res += enc1+enc2+enc3+enc4;
		}
		// 2 bytes
		if(i + 1 < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var val2 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[((val1 & 3) << 4) | (val2 >> 4)];
			var enc3 = code[(val2 & 15) << 2];
			res += enc1+enc2+enc3+'=';
		}
		// 1 byte
		else if(i < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[(val1 & 3) << 4];
			res += enc1+enc2+'==';
		}
		return res;
	},

	getResponseJSON: function() {
		var res;
		try {
			res = JSON.parse(this.getResponseText());
		}
		catch(err) {
			res = undefined;
		}
		return res;
	},

	getResponseXML: function() {
		var parser=new DOMParser();
		try {
			var xmlDoc = parser.parseFromString(this.getResponseText(), 'text/xml');
			return xmlDoc;
		} catch(e) {}
		return undefined;
	}
});

<commit_msg>add setContent for POST and PUT methods<commit_after>Core.Object.extend('Core.HttpRequest',
/**@lends Core.HttpRequest#*/
{
	url: undefined,
	method: 'GET',
	binary: false,
	request: undefined,
	arguments: undefined,
	content: undefined,

	/**
	*	@constructs
	*	@class
	*	@extends Core.Object
	*/
	constructor: function(config) {
		this.addEvents('error', 'done');

		this.request = new XMLHttpRequest();

		var wrapper = function() {
			var httprequest = arguments.callee.httprequest;
			if(httprequest.request.readyState == 4) {
				if(httprequest.request.status == 200)
					httprequest.fireEvent('done', httprequest);
				else
					httprequest.fireEvent('error', httprequest, httprequest.request.status);
			}
		}
		wrapper.httprequest = this;
		this.request.onreadystatechange = wrapper;
	},

	setUrl: function(url) {
		this.url = url;
	},

	setBinary: function(binary) {
		this.binary = binary;
	},

	setMethod: function(method) {
		this.method = method;
	},

	setRequestHeader: function(header, value) {
		this.request.setRequestHeader(header, value);
	},

	setArguments: function(arguments) {
		this.arguments = arguments;
	},

	addArgument: function(argName, argValue) {
		if(this.arguments == undefined)
			this.arguments = {};
		this.arguments[argName] = argValue;
	},

	setContent: function(content) {
		this.content = content;
	},

	abort: function() {
		this.request.abort();
	},

	send: function() {
		if(this.url == undefined)
			throw('url MUST be given for an HttpRequest');
		// encode arguments
		var args = '';
		if(this.arguments != undefined) {
			args = Core.Util.encodeURIQuery(this.arguments);
		}
		var url = this.url;

		if(((this.method == 'GET') || (this.method == 'DELETE') || (this.content != undefined)) && (args != '')) {
			if(this.url.indexOf('?') == -1)
				url += '?'+args;
			else
				url += '&'+args;
		}
		this.request.open(this.method, url, true);
		if(this.binary)
			this.request.overrideMimeType('text/plain; charset=x-user-defined');
		this.request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
		if((this.method == 'POST') || (this.method == 'PUT')) {
			if(this.content == undefined) {
				this.request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
				this.request.send(args);
			}
			else {
				this.request.setRequestHeader('Content-type', 'text/plain; charset=utf-8');
				this.request.send(this.content);
			}
		}
		// GET, DELETE... method
		else {
			this.request.send();
		}
	},

	getResponseText: function() {
		return this.request.responseText;
	},

	getResponseBase64: function() {
		var value = this.request.responseText;
		var code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		var res = '';
		var i = 0;
		while(i + 2 < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var val2 = value.charCodeAt(i++) & 0xff;
			var val3 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[((val1 & 3) << 4) | (val2 >> 4)];
			var enc3 = code[((val2 & 15) << 2) | (val3 >> 6)];
			var enc4 = code[val3 & 63];
			res += enc1+enc2+enc3+enc4;
		}
		// 2 bytes
		if(i + 1 < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var val2 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[((val1 & 3) << 4) | (val2 >> 4)];
			var enc3 = code[(val2 & 15) << 2];
			res += enc1+enc2+enc3+'=';
		}
		// 1 byte
		else if(i < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[(val1 & 3) << 4];
			res += enc1+enc2+'==';
		}
		return res;
	},

	getResponseJSON: function() {
		var res;
		try {
			res = JSON.parse(this.getResponseText());
		}
		catch(err) {
			res = undefined;
		}
		return res;
	},

	getResponseXML: function() {
		var parser=new DOMParser();
		try {
			var xmlDoc = parser.parseFromString(this.getResponseText(), 'text/xml');
			return xmlDoc;
		} catch(e) {}
		return undefined;
	}
});

<|endoftext|>