<commit_before>define(["Tone/core/Tone", "Tone/component/Follower", "Tone/signal/ScaleExp",
"Tone/effect/Effect", "Tone/component/Filter"],
function(Tone){

	"use strict";

	/**
	 *  @class  AutoWah connects an envelope follower to a bandpass filter.
	 *          Some inspiration from Tuna.js https://github.com/Dinahmoe/tuna
	 *
	 *  @constructor
	 *  @extends {Tone.Effect}
	 *  @param {number} [baseFrequency=100] the frequency the filter is set
	 *                                       to at the low point of the wah
	 *  @param {number} [octaves=5] the number of octaves above the baseFrequency
	 *                               the filter will sweep to when fully open
	 *  @param {number} [sensitivity=0] the decibel threshold sensitivity for
	 *                                   the incoming signal. Normal range of -40 to 0.
	 */
	Tone.AutoWah = function(){

		var options = this.optionsObject(arguments, ["baseFrequency", "octaves", "sensitivity"], Tone.AutoWah.defaults);
		Tone.Effect.call(this, options);

		/**
		 *  the envelope follower
		 *  @type {Tone.Follower}
		 *  @private
		 */
		this._follower = new Tone.Follower(options.follower);

		/**
		 *  scales the follower value to the frequency domain
		 *  @type {Tone}
		 *  @private
		 */
		this._sweepRange = new Tone.ScaleExp(0, 1, 0.5);

		/**
		 *  @type {number}
		 *  @private
		 */
		this._baseFrequency = options.baseFrequency;

		/**
		 *  @type {number}
		 *  @private
		 */
		this._octaves = options.octaves;

		/**
		 *  @type {BiquadFilterNode}
		 *  @private
		 */
		this._bandpass = new Tone.Filter(0, "bandpass");
		this._bandpass.setRolloff(options.rolloff);
		// this._bandpass.type = "bandpass";
		// this._bandpass.Q.value = options.Q;

		/**
		 *  @type {BiquadFilterNode}
		 *  @private
		 */
		this._peaking = this.context.createBiquadFilter();
		this._peaking.type = "peaking";
		this._peaking.gain.value = options.gain;

		//the control signal path
		this.effectSend.chain(this._follower, this._sweepRange);
		this._sweepRange.connect(this._bandpass.frequency);
		this._sweepRange.connect(this._peaking.frequency);
		//the filtered path
		this.effectSend.chain(this._bandpass, this._peaking, this.effectReturn);
		//set the initial value
		this._setSweepRange();
		this.setSensitivity(options.sensitivity);
	};

	Tone.extend(Tone.AutoWah, Tone.Effect);

	/**
	 *  @static
	 *  @type {Object}
	 */
	Tone.AutoWah.defaults = {
		"baseFrequency" : 100,
		"octaves" : 6,
		"sensitivity" : 0,
		"Q" : 2,
		"gain" : 2,
		"rolloff" : -48,
		/** attributes for the envelope follower */
		"follower" : {
			"attack" : 0.3,
			"release" : 0.5
		}
	};

	/**
	 *  set the number of octaves that the filter will sweep
	 *  @param {number} octaves the number of octaves above the base frequency the filter will sweep
	 */
	Tone.AutoWah.prototype.setOctaves = function(octaves){
		this._octaves = octaves;
		this._setSweepRange();
	};

	/**
	 *  set the number of octaves that the filter will sweep
	 *  @param {number} octaves the number of octaves above the base frequency the filter will sweep
	 */
	Tone.AutoWah.prototype.setBaseFrequency = function(baseFreq){
		this._baseFrequency = baseFreq;
		this._setSweepRange();
	};

	/**
	 *  set the sensitivity to control how responsive to the input signal
	 *  the wah is.
	 *
	 *  @param {number} sensitivy the sensitivity to the input signal in dB
	 */
	Tone.AutoWah.prototype.setSensitivity = function(sensitivity){
		this._sweepRange.setMax(this.dbToGain(sensitivity));
	};

	/**
	 *  sets the sweep range of the scaler
	 *  @private
	 */
	Tone.AutoWah.prototype._setSweepRange = function(){
		this._sweepRange.setMin(this._baseFrequency);
		this._sweepRange.setMax(Math.min(this._baseFrequency * Math.pow(2, this._octaves), this.context.sampleRate / 2));
	};

	/**
	 *  set all of the parameters with an object
	 *  @param {Object} params
	 */
	Tone.AutoWah.prototype.set = function(params){
		if (!this.isUndef(params.baseFrequency)) this.setBaseFrequency(params.baseFrequency);
		if (!this.isUndef(params.sensitivity)) this.setSensitivity(params.sensitivity);
		if (!this.isUndef(params.octaves)) this.setOctaves(params.octaves);
		if (!this.isUndef(params.follower)) this._follower.set(params.follower);
		if (!this.isUndef(params.rolloff)) this._bandpass.setRolloff(params.rolloff);
		if (!this.isUndef(params.Q)) this._bandpass.Q.value = params.Q;
		if (!this.isUndef(params.gain)) this._peaking.gain.value = params.gain;
		Tone.Effect.prototype.set.call(this, params);
	};

	/**
	 *  clean up
	 */
	Tone.AutoWah.prototype.dispose = function(){
		Tone.Effect.prototype.dispose.call(this);
		this._follower.dispose();
		this._sweepRange.dispose();
		this._bandpass.disconnect();
		this._peaking.disconnect();
		this._follower = null;
		this._sweepRange = null;
		this._bandpass = null;
		this._peaking = null;
	};

	return Tone.AutoWah;
});<commit_msg>AutoWah getter functions<commit_after>define(["Tone/core/Tone", "Tone/component/Follower", "Tone/signal/ScaleExp",
"Tone/effect/Effect", "Tone/component/Filter"],
function(Tone){

	"use strict";

	/**
	 *  @class  AutoWah connects an envelope follower to a bandpass filter.
	 *          Some inspiration from Tuna.js https://github.com/Dinahmoe/tuna
	 *
	 *  @constructor
	 *  @extends {Tone.Effect}
	 *  @param {number} [baseFrequency=100] the frequency the filter is set
	 *                                       to at the low point of the wah
	 *  @param {number} [octaves=5] the number of octaves above the baseFrequency
	 *                               the filter will sweep to when fully open
	 *  @param {number} [sensitivity=0] the decibel threshold sensitivity for
	 *                                   the incoming signal. Normal range of -40 to 0.
	 */
	Tone.AutoWah = function(){

		var options = this.optionsObject(arguments, ["baseFrequency", "octaves", "sensitivity"], Tone.AutoWah.defaults);
		Tone.Effect.call(this, options);

		/**
		 *  the envelope follower
		 *  @type {Tone.Follower}
		 *  @private
		 */
		this._follower = new Tone.Follower(options.follower);

		/**
		 *  scales the follower value to the frequency domain
		 *  @type {Tone}
		 *  @private
		 */
		this._sweepRange = new Tone.ScaleExp(0, 1, 0.5);

		/**
		 *  @type {number}
		 *  @private
		 */
		this._baseFrequency = options.baseFrequency;

		/**
		 *  @type {number}
		 *  @private
		 */
		this._octaves = options.octaves;

		/**
		 *  @type {BiquadFilterNode}
		 *  @private
		 */
		this._bandpass = new Tone.Filter(0, "bandpass");
		this._bandpass.setRolloff(options.rolloff);
		// this._bandpass.type = "bandpass";
		// this._bandpass.Q.value = options.Q;

		/**
		 *  @type {BiquadFilterNode}
		 *  @private
		 */
		this._peaking = this.context.createBiquadFilter();
		this._peaking.type = "peaking";
		this._peaking.gain.value = options.gain;

		//the control signal path
		this.effectSend.chain(this._follower, this._sweepRange);
		this._sweepRange.connect(this._bandpass.frequency);
		this._sweepRange.connect(this._peaking.frequency);
		//the filtered path
		this.effectSend.chain(this._bandpass, this._peaking, this.effectReturn);
		//set the initial value
		this._setSweepRange();
		this.setSensitivity(options.sensitivity);
	};

	Tone.extend(Tone.AutoWah, Tone.Effect);

	/**
	 *  @static
	 *  @type {Object}
	 */
	Tone.AutoWah.defaults = {
		"baseFrequency" : 100,
		"octaves" : 6,
		"sensitivity" : 0,
		"Q" : 2,
		"gain" : 2,
		"rolloff" : -48,
		/** attributes for the envelope follower */
		"follower" : {
			"attack" : 0.3,
			"release" : 0.5
		}
	};

	/**
	 *  set the number of octaves that the filter will sweep
	 *  @param {number} octaves the number of octaves above the base frequency the filter will sweep
	 */
	Tone.AutoWah.prototype.setOctaves = function(octaves){
		this._octaves = octaves;
		this._setSweepRange();
	};

	/**
	 * @return {number} the the number of octaves that the filter will sweep
	 */
	Tone.AutoWah.prototype.getOctaves = function(){
		return this._octaves;
	};

	/**
	 *  set the base frequency
	 *  @param {number} octaves the number of octaves above the base frequency the filter will sweep
	 */
	Tone.AutoWah.prototype.setBaseFrequency = function(baseFreq){
		this._baseFrequency = baseFreq;
		this._setSweepRange();
	};

	/**
	 * @return {number} the base frequency
	 */
	Tone.AutoWah.prototype.getBaseFrequency = function(){
		return this._baseFrequency;
	};

	/**
	 *  set the sensitivity to control how responsive to the input signal
	 *  the wah is.
	 *
	 *  @param {number} sensitivity the sensitivity to the input signal in dB
	 */
	Tone.AutoWah.prototype.setSensitivity = function(sensitivity){
		this._sweepRange.setMax(this.dbToGain(sensitivity));
	};

	/**
	 * @return {number} the the sensitivity
	 */
	Tone.AutoWah.prototype.getSensitivity = function(){
		return this._sweepRange.getMax();
	};

	/**
	 *  sets the sweep range of the scaler
	 *  @private
	 */
	Tone.AutoWah.prototype._setSweepRange = function(){
		this._sweepRange.setMin(this._baseFrequency);
		this._sweepRange.setMax(Math.min(this._baseFrequency * Math.pow(2, this._octaves), this.context.sampleRate / 2));
	};

	/**
	 *  set all of the parameters with an object
	 *  @param {Object} params
	 */
	Tone.AutoWah.prototype.set = function(params){
		if (!this.isUndef(params.baseFrequency)) this.setBaseFrequency(params.baseFrequency);
		if (!this.isUndef(params.sensitivity)) this.setSensitivity(params.sensitivity);
		if (!this.isUndef(params.octaves)) this.setOctaves(params.octaves);
		if (!this.isUndef(params.follower)) this._follower.set(params.follower);
		if (!this.isUndef(params.rolloff)) this._bandpass.setRolloff(params.rolloff);
		if (!this.isUndef(params.Q)) this._bandpass.Q.value = params.Q;
		if (!this.isUndef(params.gain)) this._peaking.gain.value = params.gain;
		Tone.Effect.prototype.set.call(this, params);
	};

	/**
	 *  clean up
	 */
	Tone.AutoWah.prototype.dispose = function(){
		Tone.Effect.prototype.dispose.call(this);
		this._follower.dispose();
		this._sweepRange.dispose();
		this._bandpass.disconnect();
		this._peaking.disconnect();
		this._follower = null;
		this._sweepRange = null;
		this._bandpass = null;
		this._peaking = null;
	};

	return Tone.AutoWah;
});<|endoftext|>


<commit_before>import superagent from 'superagent'
import store from '../store'
import {
  USER_LOGIN,
  USER_LOGOUT,
  USER_LOGIN_FAIL,
  DATA_LOADING_START
} from '../store/mutation-types.js'

const auth = {

  logIn (email, password, callback) {
    superagent
      .post('/api/auth/login')
      .send({ email, password })
      .end((err, res) => {
        if (err) return callback(err)
        else {
          if (res.body.login) {
            const user = res.body.user
            store.commit(DATA_LOADING_START, user)
            callback(null, user)
          } else {
            store.commit(USER_LOGIN_FAIL)
            callback(new Error('Login failed'))
          }
        }
      })
  },

  logout (callback) {
    superagent
      .get('/api/auth/logout')
      .end((err, res) => {
        if (err) return callback(err)
        store.commit(USER_LOGOUT)
        callback()
      })
  },

  resetPassword (email) {
    return new Promise((resolve, reject) => {
      superagent
        .post('/api/auth/reset-password')
        .send({ email })
        .end((err, res) => {
          if (err) reject(err)
          else resolve()
        })
    })
  },

  resetChangePassword (token, password, password2) {
    return new Promise((resolve, reject) => {
      superagent
        .put('/api/auth/reset-password')
        .send({ token, password, password2 })
        .end((err, res) => {
          if (err) reject(err)
          else resolve()
        })
    })
  },

  isServerLoggedIn (callback) {
    superagent
      .get('/api/auth/authenticated')
      .end((err, res) => {
        if (err && res.statusCode === 401) {
          store.commit(USER_LOGIN_FAIL)
          callback(null)
        } else if (err) {
          store.commit(USER_LOGIN_FAIL)
          callback(err)
        } else if (res.body === null) {
          store.commit(USER_LOGIN_FAIL)
          callback(err)
        } else {
          const user = res.body.user
          store.commit(USER_LOGIN, user)
          callback(null, user)
        }
      })
  },

  // Needed for router to know if a redirection to login page is required or
  // not.
  requireAuth (to, from, next) {
    const finalize = () => {
      if (!store.state.user.isAuthenticated) {
        next({
          path: '/login',
          query: { redirect: to.fullPath }
        })
      } else {
        store.commit(DATA_LOADING_START)
        next()
      }
    }

    if (store.state.user.user === null) {
      auth.isServerLoggedIn((err) => {
        if (err) {
          next({
            path: '/server-down',
            query: { redirect: to.fullPath }
          })
        } else {
          finalize()
        }
      })
    } else {
      finalize()
    }
  },

  isPasswordValid (password, password2) {
    return password.length > 6 && password === password2
  }
}
export default auth
<commit_msg>Fix auth checking behaviour<commit_after>import superagent from 'superagent'
import store from '../store'
import {
  USER_LOGIN,
  USER_LOGOUT,
  USER_LOGIN_FAIL,
  DATA_LOADING_START
} from '../store/mutation-types.js'

const auth = {

  logIn (email, password, callback) {
    superagent
      .post('/api/auth/login')
      .send({ email, password })
      .end((err, res) => {
        if (err) return callback(err)
        else {
          if (res.body.login) {
            const user = res.body.user
            store.commit(DATA_LOADING_START, user)
            callback(null, user)
          } else {
            store.commit(USER_LOGIN_FAIL)
            callback(new Error('Login failed'))
          }
        }
      })
  },

  logout (callback) {
    superagent
      .get('/api/auth/logout')
      .end((err, res) => {
        if (err) return callback(err)
        store.commit(USER_LOGOUT)
        callback()
      })
  },

  resetPassword (email) {
    return new Promise((resolve, reject) => {
      superagent
        .post('/api/auth/reset-password')
        .send({ email })
        .end((err, res) => {
          if (err) reject(err)
          else resolve()
        })
    })
  },

  resetChangePassword (token, password, password2) {
    return new Promise((resolve, reject) => {
      superagent
        .put('/api/auth/reset-password')
        .send({ token, password, password2 })
        .end((err, res) => {
          if (err) reject(err)
          else resolve()
        })
    })
  },

  isServerLoggedIn (callback) {
    superagent
      .get('/api/auth/authenticated')
      .end((err, res) => {
        if (err && [401, 422].includes(res.statusCode)) {
          store.commit(USER_LOGIN_FAIL)
          callback(null)
        } else if (err) {
          store.commit(USER_LOGIN_FAIL)
          callback(err)
        } else if (res.body === null) {
          store.commit(USER_LOGIN_FAIL)
          callback(err)
        } else {
          const user = res.body.user
          store.commit(USER_LOGIN, user)
          callback(null, user)
        }
      })
  },

  // Needed for router to know if a redirection to login page is required or
  // not.
  requireAuth (to, from, next) {
    const finalize = () => {
      if (!store.state.user.isAuthenticated) {
        next({
          path: '/login',
          query: { redirect: to.fullPath }
        })
      } else {
        store.commit(DATA_LOADING_START)
        next()
      }
    }

    if (store.state.user.user === null) {
      auth.isServerLoggedIn((err) => {
        if (err) {
          next({
            path: '/server-down',
            query: { redirect: to.fullPath }
          })
        } else {
          finalize()
        }
      })
    } else {
      finalize()
    }
  },

  isPasswordValid (password, password2) {
    return password.length > 6 && password === password2
  }
}
export default auth
<|endoftext|>


<commit_before>"use strict";

var _ = Npm.require('lodash'),
    fs = Npm.require('fs'),
    path = Npm.require('path'),
    chokidar = Npm.require('chokidar'),
    DEBUG = !!process.env.VELOCITY_DEBUG,
    VELOCITY_CONFIG_FILE = 'velocity.json',
    defaultConfig = {
      frameworks: ['jasmine-unit', 'mocha-web'],
      regex: '.(js|coffee)',
      testDirs: ['tests']
    },
    config,
    watcher;

DEBUG && console.log('PWD', process.env.PWD);

config = _loadConfig();
DEBUG && console.log('velocity config =', JSON.stringify(config, null, 2));

// kick-off everything
_reset();

//////////////////////////////////////////////////////////////////////
// Meteor Methods
//

Meteor.methods({

  reset: function () {
    _reset();
  },

  resetReports: function (options) {
    var query = {};
    if (options.framework) {
      query.framework = options.framework;
    }
    if (options.notIn) {
      query = _.assign(query, {_id: {$nin: options.notIn }});
    }
    VelocityTestReports.remove(query);
    _updateAggregateReports();
  },

  postLog: function (options) {
    if (!options || !options.type || !options.message || !options.framework) {
      throw new Error('type, message and framework are required fields.')
    }
    VelocityLogs.insert({
      timestamp: options.timestamp ? options.timestamp : Date.now(),
      type: options.type,
      message: options.message,
      framework: options.framework
    });
  },

  postResult: function (options) {
    var requiredFields = ['id', 'name', 'framework', 'result'],
        optionalFields = [
          'timestamp',
          'time',
          'async',
          'timeOut',
          'pending',
          'failureType',
          'failureMessage',
          'failureStackTrace',
          'ancestors'
        ],
        result;

    options = options || {};

    _checkRequired (requiredFields, options);

    result = {
      name: options.name,
      framework: options.framework,
      result: options.result
    };

    _.each(optionalFields, function (option) {
      result[option] = options[option] ? options[option] : '';
    });

    VelocityTestReports.upsert(options.id, {$set: result});
    _updateAggregateReports();
  }  // end postResult

});  // end Meteor methods

//////////////////////////////////////////////////////////////////////
// Private functions
//

function _checkRequired (requiredFields, target) {
  _.each(requiredFields, function (name) {
    if (!target[name]) {
      throw new Error("Required field '" + name + "' is missing." +
                      "Result not posted.")
    }
  })
}

function _loadConfig () {
  var config,
      velocityConfigFile,
      Meteor = Meteor || {settings: {}};

  velocityConfigFile = path.join(process.env.PWD, VELOCITY_CONFIG_FILE);

  try {
    config = fs.readFileSync(velocityConfigFile).toString();
    config = JSON.parse(config);
    DEBUG && console.log("Checking for velocity config file '",
                          VELOCITY_CONFIG_FILE, "'...found!");
  }
  catch (ex) {
    DEBUG && console.log(ex);
    DEBUG && console.log("Checking for velocity config file '",
                          VELOCITY_CONFIG_FILE, "'...not found");
  }

  if (!config) {
    config = Meteor.settings.velocity;
    if (config) {
      DEBUG && console.log('  checking Meteor.settings.velocity...found!');
    } else {
      DEBUG && console.log('  checking Meteor.settings.velocity...not found');
    }
  }

  if (!config) {
    DEBUG && console.log('  using default velocity config');
    config = defaultConfig;
  }

  return config;
}  // end _loadConfig

function _initWatcher () {
  var absoluteTestDirs,
      _watcher,
      testFileRegex = '-(' + config.frameworks.join('|') + ')' +
                      config.regex;

  DEBUG && console.log('Velocity testFileRegex', testFileRegex);

  absoluteTestDirs = _.map(config.testDirs, function (testDir) {
    return path.join(process.env.PWD, testDir);
  });

  _watcher = chokidar.watch(absoluteTestDirs, {ignored: /[\/\\]\./});

  _watcher.on('add', Meteor.bindEnvironment(function (filePath) {
    'use strict';

    var relativePath,
        filename,
        targetFramework,

    filePath = path.normalize(filePath);

    DEBUG && console.log('File added:', filePath);

    filename = path.basename(filePath);

    if (filename.match(testFileRegex)) {
      targetFramework = _.filter(config.frameworks, function (framework) {
        return filename.match('-' + framework + config.regex);
      })[0];

      DEBUG && console.log(targetFramework, ' <= ', filePath)

      relativePath = filePath.substring(process.env.PWD.length);
      if (relativePath[0] === path.sep) {
        relativePath = relativePath.substring(1)
      }

      VelocityTestFiles.insert({
        _id: filePath,
        name: filename,
        absolutePath: filePath,
        relativePath: relativePath,
        targetFramework: targetFramework,
        lastModified: Date.now()
      });
    }
  }));  // end watcher.on 'add'

  _watcher.on('change', Meteor.bindEnvironment(function (filePath) {
    DEBUG && console.log('File changed:', filePath);

    // Since we key on filePath and we only add files we're interested in,
    // we don't have to worry about inadvertently updating records for files
    // we don't care about.
    VelocityTestFiles.update(filePath, { $set: {lastModified: Date.now()}});
  }));

  _watcher.on('unlink', Meteor.bindEnvironment(function (filePath) {
    DEBUG && console.log('File removed:', filePath);
    // If we only remove the file, we also need to remove the test results for
    // just that file. This required changing the postResult API and we could
    // do it, but the brute force method of reset() will do the trick until we
    // want to optimize VelocityTestFiles.remove(filePath);
    _reset();
  }));

  return _watcher;
}  // end _initWatcher

function _reset () {
  if (watcher) {
    watcher.close();
  }

  VelocityTestFiles.remove({});
  VelocityTestReports.remove({});
  VelocityAggregateReports.remove({});
  VelocityAggregateReports.insert({
    _id: 'result',
    name: 'Aggregate Result',
    result: 'pending'
  });

  watcher = _initWatcher();
}

function _updateAggregateReports () {
  var failedResult,
      result;

  if (!VelocityTestReports.findOne({result: ''})) {
    failedResult = VelocityTestReports.findOne({result: 'failed'})
    result = failedResult ?  'failed' : 'passed'

    VelocityAggregateReports.update('result', {$set: {result: result}});
  }
}
<commit_msg>manually pass config object to internal functions. document functions<commit_after>"use strict";

var _ = Npm.require('lodash'),
    fs = Npm.require('fs'),
    path = Npm.require('path'),
    chokidar = Npm.require('chokidar'),
    DEBUG = !!process.env.VELOCITY_DEBUG,
    VELOCITY_CONFIG_FILE = 'velocity.json',
    defaultConfig = {
      frameworks: ['jasmine-unit', 'mocha-web'],
      regex: '.(js|coffee)',
      testDirs: ['tests']
    },
    _config,
    watcher;

DEBUG && console.log('PWD', process.env.PWD);

_config = _loadConfig();
DEBUG && console.log('velocity config =', JSON.stringify(_config, null, 2));

// kick-off everything
_reset(_config);

//////////////////////////////////////////////////////////////////////
// Meteor Methods
//

Meteor.methods({

  /**
   * Meteor method: reset
   * Re-init file watcher and clear all test results.
   *
   * @method reset
   */
  reset: function () {
    _reset(_config);
  },

  /**
   * Meteor method: resetReports
   * Clear all test results.
   *
   * @method resetReports
   * @param {Object} [options] Optional, specify specific framework to clear
   *                 and/or define a list of tests to keep.
   *                 ex.
   *                 {
   *                   framework: "jasmine-unit",
   *                   notIn: ['tests/auth-jasmine-unit.js']
   *                 }
   */
  resetReports: function (options) {
    var query = {};
    if (options.framework) {
      query.framework = options.framework;
    }
    if (options.notIn) {
      query = _.assign(query, {_id: {$nin: options.notIn }});
    }
    VelocityTestReports.remove(query);
    _updateAggregateReports();
  },

  /**
   * Meteor method: postLog
   * Log a method to the central Velocity log store.
   *
   * @method postLog
   * @param {Object} options Required parameters:
   *                   type - String
   *                   message - String
   *                   framework - String  ex. 'jasmine-unit'
   *
   *                 Optional parameters:
   *                   timestamp - Date
   */
  postLog: function (options) {
    var requiredFields = ['type', 'message', 'framework']

    _checkRequired (requiredFields, options);

    VelocityLogs.insert({
      timestamp: options.timestamp ? options.timestamp : Date.now(),
      type: options.type,
      message: options.message,
      framework: options.framework
    });
  },

  /**
   * Meteor method: postResult
   *
   * @method postResult
   * @param {Object} options Required parameters:
   *                   id - String
   *                   name - String
   *                   framework - String  ex. 'jasmine-unit'
   *                   result - String
   *
   *                 Suggested optional parameters:
   *                   timestamp - Date
   *                   time
   *                   async
   *                   timeOut
   *                   pending
   *                   failureType
   *                   failureMessage
   *                   failureStackTrace
   *                   ancestors
   */
  postResult: function (options) {
    var requiredFields = ['id', 'name', 'framework', 'result'],
        data = {};

    options = options || {};

    _checkRequired (requiredFields, options);

    for (var fieldName in options) {
      data[fieldName] = options[fieldName];
    }

    VelocityTestReports.upsert(options.id, {$set: data});
    _updateAggregateReports();
  }  // end postResult

});  // end Meteor methods

//////////////////////////////////////////////////////////////////////
// Private functions
//

/**
 * Ensures that each require field is found on the target object.
 * Throws exception if a required field is undefined, null or an empty string.
 *
 * @method _checkRequired
 * @param {Array} requiredFields - list of required field names
 * @param {Object} target - target object to check
 * @private
 */
function _checkRequired (requiredFields, target) {
  _.each(requiredFields, function (name) {
    if (!target[name]) {
      throw new Error("Required field '" + name + "' is missing." +
                      "Result not posted.")
    }
  })
}

/**
 * Attempts to load velocity configuration information.
 * First checks for a `velocity.json` file in the app directory.
 * Then checks `Meteor.settings.velocity`.  Then uses the default config
 * if none of the others are found.
 *
 * NOTE: Contents of this config are subject to frequent change until
 *       `velocity`s design is more stable.
 *
 * @example
 *     {
 *       "frameworks": ["jasmine-unit", "mocha-web"],
 *       "regex": ".(js|coffee)",
 *       "testDirs": ["tests"]
 *     }
 *
 * @method _loadConfig
 * @return {Object} the configuration object
 * @private
 */
function _loadConfig () {
  var config,
      velocityConfigFile,
      Meteor = Meteor || {settings: {}};

  velocityConfigFile = path.join(process.env.PWD, VELOCITY_CONFIG_FILE);

  try {
    config = fs.readFileSync(velocityConfigFile).toString();
    config = JSON.parse(config);
    DEBUG && console.log("Checking for velocity config file '",
                          VELOCITY_CONFIG_FILE, "'...found!");
  }
  catch (ex) {
    DEBUG && console.log(ex);
    DEBUG && console.log("Checking for velocity config file '",
                          VELOCITY_CONFIG_FILE, "'...not found");
  }

  if (!config) {
    config = Meteor.settings.velocity;
    if (config) {
      DEBUG && console.log('  checking Meteor.settings.velocity...found!');
    } else {
      DEBUG && console.log('  checking Meteor.settings.velocity...not found');
    }
  }

  if (!config) {
    DEBUG && console.log('  using default velocity config');
    config = defaultConfig;
  }

  return config;
}  // end _loadConfig

/**
 * Initialize the directory/file watcher.
 *
 * @method _initWatcher
 * @param {Object} config  See `_loadConfig`.
 * @private
 */
function _initWatcher (config) {
  var absoluteTestDirs,
      _watcher,
      testFileRegex = '-(' + config.frameworks.join('|') + ')' +
                      config.regex;

  DEBUG && console.log('Velocity testFileRegex', testFileRegex);

  absoluteTestDirs = _.map(config.testDirs, function (testDir) {
    return path.join(process.env.PWD, testDir);
  });

  _watcher = chokidar.watch(absoluteTestDirs, {ignored: /[\/\\]\./});

  _watcher.on('add', Meteor.bindEnvironment(function (filePath) {
    var relativePath,
        filename,
        targetFramework,

    filePath = path.normalize(filePath);

    DEBUG && console.log('File added:', filePath);

    filename = path.basename(filePath);

    if (filename.match(testFileRegex)) {
      targetFramework = _.filter(config.frameworks, function (framework) {
        return filename.match('-' + framework + config.regex);
      })[0];

      DEBUG && console.log(targetFramework, ' <= ', filePath)

      relativePath = filePath.substring(process.env.PWD.length);
      if (relativePath[0] === path.sep) {
        relativePath = relativePath.substring(1)
      }

      VelocityTestFiles.insert({
        _id: filePath,
        name: filename,
        absolutePath: filePath,
        relativePath: relativePath,
        targetFramework: targetFramework,
        lastModified: Date.now()
      });
    }
  }));  // end watcher.on 'add'

  _watcher.on('change', Meteor.bindEnvironment(function (filePath) {
    DEBUG && console.log('File changed:', filePath);

    // Since we key on filePath and we only add files we're interested in,
    // we don't have to worry about inadvertently updating records for files
    // we don't care about.
    VelocityTestFiles.update(filePath, { $set: {lastModified: Date.now()}});
  }));

  _watcher.on('unlink', Meteor.bindEnvironment(function (filePath) {
    DEBUG && console.log('File removed:', filePath);
    // If we only remove the file, we also need to remove the test results for
    // just that file. This required changing the postResult API and we could
    // do it, but the brute force method of reset() will do the trick until we
    // want to optimize VelocityTestFiles.remove(filePath);
    _reset(config);
  }));

  return _watcher;
}  // end _initWatcher

/**
 * Re-init file watcher and clear all test results.
 *
 * @method _reset
 * @param {Object} config  See `_loadConfig`.
 * @private
 */
function _reset (config) {
  if (watcher) {
    watcher.close();
  }

  VelocityTestFiles.remove({});
  VelocityTestReports.remove({});
  VelocityAggregateReports.remove({});
  VelocityAggregateReports.insert({
    _id: 'result',
    name: 'Aggregate Result',
    result: 'pending'
  });

  watcher = _initWatcher(config);
}

/**
 * If any one test has failed, mark the aggregate test result as failed.
 *
 * @method _updateAggregateReports
 * @private
 */
function _updateAggregateReports () {
  var failedResult,
      result;

  if (!VelocityTestReports.findOne({result: ''})) {
    failedResult = VelocityTestReports.findOne({result: 'failed'})
    result = failedResult ?  'failed' : 'passed'

    VelocityAggregateReports.update('result', {$set: {result: result}});
  }
}
<|endoftext|>


<commit_before>import config from '../config'
import set from './set'
import screenview from './screenview'
import query from './query'
import {
  getQueryString,
  isRouteIgnored,
  isRoute,
  isRouter,
  getBasePath
} from '../helpers'

export default function page (...args) {
  if (args.length && !args[0]) {
    // allows to dynamically prevent tracking in pageviewTemplate proxy
    return
  }

  let route

  if (args.length && isRouter(args[0])) {
    route = args[0].currentRoute
  }

  if (args.length && isRoute(args[0])) {
    route = args[0]
  }

  if (route) {
    trackRoute(route)
  } else {
    // We can call with `page('/my/path')`
    let page = typeof args[0] === 'object' ? args[0].page : args[0]
    set('page', page)
    query('send', 'pageview', ...args)
  }
}

export function trackRoute (route) {
  if (isRouteIgnored(route)) {
    return
  }

  const { autoTracking } = config
  const { meta: { analytics = {} } } = route
  const proxy = analytics.pageviewTemplate || autoTracking.pageviewTemplate

  if (autoTracking.screenview && !route.name) {
    throw new Error(
      '[vue-analytics] Route name is mandatory when using screenview.'
    )
  }

  if (autoTracking.screenview) {
    screenview(route.name)
    return
  }

  if (proxy) {
    page(proxy(route))
  } else {
    const {
      router,
      autoTracking: {
        transformQueryString,
        prependBase
      }
    } = config

    const queryString = getQueryString(route.query)
    const base = router && router.options.base
    const needsBase = prependBase && base

    let path = route.path + (transformQueryString ? queryString : '')
    path = needsBase ? getBasePath(base, path) : path

    page(path)
  }
}

export function autoTracking () {
  const { router, autoTracking } = config

  if (!autoTracking.page || !router) {
    return
  }

  if (autoTracking.pageviewOnLoad && router.history.ready) {
    trackRoute(router.currentRoute)
  }

  config.router.afterEach(function (to, from) {
    const { skipSamePath, shouldRouterUpdate } = autoTracking

    // Default behaviour of the router when the `skipSamePath` is turned on.
    // Skip router change when current and previous route have the same path
    // https://github.com/MatteoGabriele/vue-analytics/issues/73
    if (skipSamePath && to.path === from.path) {
      return
    }

    // Adds a custom way to define when the router should track
    if (typeof shouldRouterUpdate === 'function' && !shouldRouterUpdate(to, from)) {
      return
    }

    // Fire tracking after the nextTick or it will still register the previous route
    // https://github.com/MatteoGabriele/vue-analytics/issues/44
    config.$vue.nextTick().then(() => {
      trackRoute(router.currentRoute)
    })
  })
}
<commit_msg>fix(page): pageview resolved before routes ready<commit_after>import config from '../config'
import set from './set'
import screenview from './screenview'
import query from './query'
import {
  getQueryString,
  isRouteIgnored,
  isRoute,
  isRouter,
  getBasePath
} from '../helpers'

export default function page (...args) {
  if (args.length && !args[0]) {
    // allows to dynamically prevent tracking in pageviewTemplate proxy
    return
  }

  let route

  if (args.length && isRouter(args[0])) {
    route = args[0].currentRoute
  }

  if (args.length && isRoute(args[0])) {
    route = args[0]
  }

  if (route) {
    trackRoute(route)
  } else {
    // We can call with `page('/my/path')`
    let page = typeof args[0] === 'object' ? args[0].page : args[0]
    set('page', page)
    query('send', 'pageview', ...args)
  }
}

export function trackRoute (route) {
  if (isRouteIgnored(route)) {
    return
  }

  const { autoTracking } = config
  const { meta: { analytics = {} } } = route
  const proxy = analytics.pageviewTemplate || autoTracking.pageviewTemplate

  if (autoTracking.screenview && !route.name) {
    throw new Error(
      '[vue-analytics] Route name is mandatory when using screenview.'
    )
  }

  if (autoTracking.screenview) {
    screenview(route.name)
    return
  }

  if (proxy) {
    page(proxy(route))
  } else {
    const {
      router,
      autoTracking: {
        transformQueryString,
        prependBase
      }
    } = config

    const queryString = getQueryString(route.query)
    const base = router && router.options.base
    const needsBase = prependBase && base

    let path = route.path + (transformQueryString ? queryString : '')
    path = needsBase ? getBasePath(base, path) : path

    page(path)
  }
}

export function autoTracking () {
  const { router, autoTracking } = config

  if (!autoTracking.page || !router) {
    return
  }

  router.onReady(() => {
    if (autoTracking.pageviewOnLoad && router.history.ready) {
      trackRoute(router.currentRoute)
    }

    router.afterEach(function (to, from) {
      const { skipSamePath, shouldRouterUpdate } = autoTracking

      // Default behaviour of the router when the `skipSamePath` is turned on.
      // Skip router change when current and previous route have the same path
      // https://github.com/MatteoGabriele/vue-analytics/issues/73
      if (skipSamePath && to.path === from.path) {
        return
      }

      // Adds a custom way to define when the router should track
      if (typeof shouldRouterUpdate === 'function' && !shouldRouterUpdate(to, from)) {
        return
      }

      trackRoute(router.currentRoute)
    })
  })
}
<|endoftext|>


<commit_before>import appConf from 'app-conf'
import get from 'lodash.get'
import highland from 'highland'
import levelup from 'level-party'
import ndjson from 'ndjson'
import parseArgs from 'minimist'
import sublevel from 'level-sublevel'
import util from 'util'

import {forEach} from './utils'
import globMatcher from './glob-matcher'

// ===================================================================

async function printLogs (db, args) {
  let stream = highland(db.createReadStream({reverse: true}))

  if (args.since) {
    stream = stream.filter(({value}) => (value.time >= args.since))
  }

  if (args.until) {
    stream = stream.filter(({value}) => (value.time <= args.until))
  }

  const fields = Object.keys(args.matchers)

  if (fields.length > 0) {
    stream = stream.filter(({value}) => {
      for (const field of fields) {
        const fieldValue = get(value, field)
        if (fieldValue === undefined || !args.matchers[field](fieldValue)) {
          return false
        }
      }

      return true
    })
  }

  stream = stream.take(args.limit)

  if (args.json) {
    stream = highland(stream.pipe(ndjson.serialize()))
      .each(value => {
        process.stdout.write(value)
      })
  } else {
    stream = stream.each(value => {
      console.log(util.inspect(value, { depth: null }))
    })
  }

  return new Promise(resolve => {
    stream.done(resolve)
  })
}

// ===================================================================

function helper () {
  console.error(
  `Usage:
  xo-server-logs --help, -h
  xo-server-logs [--json] [--limit=<limit>] [--since=<date>] [--until=<date>] [<pattern>...]

    --help
        Display this help message.

    --json
        Display the results as new line delimited JSON for consumption by another program.

    --limit=<limit>, -n <limit>
        Limit the number of results to be displayed (default 100)

    --since=<date>, --until=<date>
        Start showing entries on or newer than the specified date, or on or older than the specified date.
        <date> should use the format \`YYYY-MM-DD\`.

    <pattern>
        Patterns can be used to filter the entries.
        <pattern> have the following format \`<field>=<value>\`/\`<field>\`.`
  )
}

// ===================================================================

function getArgs () {
  const stringArgs = ['since', 'until', 'limit']
  const args = parseArgs(process.argv.slice(2), {
    string: stringArgs,
    boolean: ['help', 'json'],
    default: {
      limit: 100,
      json: false,
      help: false
    },
    alias: {
      limit: 'n',
      help: 'h'
    }
  })

  const patterns = {}

  for (let value of args._) {
    value = String(value)

    const i = value.indexOf('=')

    if (i !== -1) {
      const field = value.slice(0, i)
      const pattern = value.slice(i + 1)

      patterns[pattern]
        ? patterns[field].push(pattern)
        : patterns[field] = [ pattern ]
    } else if (!patterns[value]) {
      patterns[value] = null
    }
  }

  const trueFunction = () => true
  args.matchers = {}

  for (const field in patterns) {
    const values = patterns[field]
    args.matchers[field] = (values === null) ? trueFunction : globMatcher(values)
  }

  // Warning: minimist makes one array of values if the same option is used many times.
  // (But only for strings args, not boolean)
  forEach(stringArgs, arg => {
    if (args[arg] instanceof Array) {
      throw new Error(`error: too many values for ${arg} argument`)
    }
  })

  ;['since', 'until'].forEach(arg => {
    if (args[arg] !== undefined) {
      args[arg] = Date.parse(args[arg])

      if (isNaN(args[arg])) {
        throw new Error(`error: bad ${arg} timestamp format`)
      }
    }
  })

  if (isNaN(args.limit = +args.limit)) {
    throw new Error('error: limit is not a valid number')
  }

  return args
}

// ===================================================================

export default async function main () {
  const args = getArgs()

  if (args.help) {
    helper()
    return
  }

  const config = await appConf.load('xo-server', {
    ignoreUnknownFormats: true
  })

  const db = sublevel(levelup(
    `${config.datadir}/leveldb`,
    { valueEncoding: 'json' }
  )).sublevel('logs')

  return printLogs(db, args)
}
<commit_msg>Initial --repair command in xo-server-logs.<commit_after>import appConf from 'app-conf'
import get from 'lodash.get'
import highland from 'highland'
import levelup from 'level-party'
import ndjson from 'ndjson'
import parseArgs from 'minimist'
import sublevel from 'level-sublevel'
import util from 'util'
import { repair as repairDb } from 'leveldown'

import {forEach} from './utils'
import globMatcher from './glob-matcher'

// ===================================================================

async function printLogs (db, args) {
  let stream = highland(db.createReadStream({reverse: true}))

  if (args.since) {
    stream = stream.filter(({value}) => (value.time >= args.since))
  }

  if (args.until) {
    stream = stream.filter(({value}) => (value.time <= args.until))
  }

  const fields = Object.keys(args.matchers)

  if (fields.length > 0) {
    stream = stream.filter(({value}) => {
      for (const field of fields) {
        const fieldValue = get(value, field)
        if (fieldValue === undefined || !args.matchers[field](fieldValue)) {
          return false
        }
      }

      return true
    })
  }

  stream = stream.take(args.limit)

  if (args.json) {
    stream = highland(stream.pipe(ndjson.serialize()))
      .each(value => {
        process.stdout.write(value)
      })
  } else {
    stream = stream.each(value => {
      console.log(util.inspect(value, { depth: null }))
    })
  }

  return new Promise(resolve => {
    stream.done(resolve)
  })
}

// ===================================================================

function helper () {
  console.error(`
xo-server-logs --help, -h

    Display this help message.

xo-server-logs [--json] [--limit=<limit>] [--since=<date>] [--until=<date>] [<pattern>...]

    Prints the logs.

    --json
      Display the results as new line delimited JSON for consumption
      by another program.

    --limit=<limit>, -n <limit>
      Limit the number of results to be displayed (default 100)

    --since=<date>, --until=<date>
      Start showing entries on or newer than the specified date, or on
      or older than the specified date.

      <date> should use the format \`YYYY-MM-DD\`.

    <pattern>
      Patterns can be used to filter the entries.

      Patterns have the following format \`<field>=<value>\`/\`<field>\`.

xo-server-logs --repair

    Repair/compact the database.

    This is an advanced operation and should be used only when necessary and offline (xo-server should be stopped).
`)
}

// ===================================================================

function getArgs () {
  const stringArgs = ['since', 'until', 'limit']
  const args = parseArgs(process.argv.slice(2), {
    string: stringArgs,
    boolean: ['help', 'json', 'repair'],
    default: {
      limit: 100,
      json: false,
      help: false
    },
    alias: {
      limit: 'n',
      help: 'h'
    }
  })

  const patterns = {}

  for (let value of args._) {
    value = String(value)

    const i = value.indexOf('=')

    if (i !== -1) {
      const field = value.slice(0, i)
      const pattern = value.slice(i + 1)

      patterns[pattern]
        ? patterns[field].push(pattern)
        : patterns[field] = [ pattern ]
    } else if (!patterns[value]) {
      patterns[value] = null
    }
  }

  const trueFunction = () => true
  args.matchers = {}

  for (const field in patterns) {
    const values = patterns[field]
    args.matchers[field] = (values === null) ? trueFunction : globMatcher(values)
  }

  // Warning: minimist makes one array of values if the same option is used many times.
  // (But only for strings args, not boolean)
  forEach(stringArgs, arg => {
    if (args[arg] instanceof Array) {
      throw new Error(`error: too many values for ${arg} argument`)
    }
  })

  ;['since', 'until'].forEach(arg => {
    if (args[arg] !== undefined) {
      args[arg] = Date.parse(args[arg])

      if (isNaN(args[arg])) {
        throw new Error(`error: bad ${arg} timestamp format`)
      }
    }
  })

  if (isNaN(args.limit = +args.limit)) {
    throw new Error('error: limit is not a valid number')
  }

  return args
}

// ===================================================================

export default async function main () {
  const args = getArgs()

  if (args.help) {
    helper()
    return
  }

  const config = await appConf.load('xo-server', {
    ignoreUnknownFormats: true
  })

  if (args.repair) {
    await new Promise((resolve, reject) => {
      repairDb(`${config.datadir}/leveldb`, error => {
        if (error) {
          reject(error)
        } else {
          resolve()
        }
      })
    })

    return
  }

  const db = sublevel(levelup(
    `${config.datadir}/leveldb`,
    { valueEncoding: 'json' }
  )).sublevel('logs')

  return printLogs(db, args)
}
<|endoftext|>


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

/**
 * An abstraction for an items set (kernel plus added),
 * known as a "closure". Recursively closes over
 * all added items, eventually forming an LR-parsing state.
 *
 * Usually there is one kernel item in a state, however there are
 * cases when kernel may contain several items. E.g. being in the state:
 *
 * S' -> • S
 * S  -> • S "a"
 *     | • "b"
 *
 * and having a transition on S, we get both first two items in the
 * kernel of the next state:
 *
 * S' -> S •
 * S  -> S • "a"
 */

import LRItem from './lr-item';

export default class State {

  /**
   * A closure state may have several kernel items. An initial kernel
   * item can be passed in the constructor, other kernel items can
   * be added later via `add` method.
   */
  constructor(
    kernelItems,
    grammar,
    canonicalCollection,
  ) {
    this._kernelItems = kernelItems;
    this._items = [];
    this._grammar = grammar;
    this._canonicalCollection = canonicalCollection;
    this._number = null;

    // A map from transition symbol to the next state.
    this._transitionsForSymbol = null;

    // To avoid infinite recursion in case if an added item
    // is for a recursive production, S -> S "a".
    this._itemsMap = {};

    // Also items map, but by LR(0) key.
    this._lr0ItemsMap = {};

    // Add initial items, and closure them if needed.
    this.addItems(this._kernelItems);

    // And register the state in the collection.
    this._canonicalCollection.registerState(this);
  }

  /**
   * State number in the canonical collection.
   */
  getNumber() {
    return this._number;
  }

  /**
   * Canonical collection can assign a specific
   * number to this state.
   */
  setNumber(number) {
    this._number = number;
  }

  /**
   * Kernel items for which the closure is built.
   */
  getKernelItems() {
    return this._kernelItems;
  }

  /**
   * All items in this closure (kernel plus all expanded).
   */
  getItems() {
    return this._items;
  }

  /**
   * Whether this state is final.
   */
  isFinal() {
    return this.getItems().length === 1 && this.getItems()[0].isFinal();
  }

  /**
   * Whether the state is accepting.
   */
  isAccept() {
    return this.isFinal() && this.getItems()[0].getProduction().isAugmented();
  }

  /**
   * Returns all reduce items in this set.
   */
  getReduceItems() {
    return this.getItems().filter(item => item.isReduce());
  }

  /**
   * Calculates all the conflicts in the state, marking the
   * state as inadequate in case of "s/r" or "r/r" conflicts.
   *
   * Note: conflicts only possible in states that contain
   * reduce items, plus another reduce or shift item.
   */
  analyzeConflicts() {
    this._conflicts = {sr: [], rr: []};

    let reduceItems = this.getReduceItems();

    // "reduce-reduce" conflicts.
    if (reduceItems.length > 1) {
      reduceSet: for (let reduceItem of reduceItems) {
        for (let reduceItemToCheck of reduceItems) {
          if (reduceItem !== reduceItemToCheck &&
              reduceItem.conflictsWithReduceItem(reduceItemToCheck)) {
            this._conflicts.rr = reduceItems;
            break reduceSet;
          }
        }
      }
    }

    // "shift-reduce" conflicts.
    this.getItems().forEach(item => {
      if (!item.isShift()) {
        return;
      }
      reduceItems.forEach(reduceItem => {
        if (reduceItem.conflictsWithShiftSymbol(item.getCurrentSymbol())) {
          this._conflicts.sr.push([item, reduceItem]);
        }
      });
    });

    return this._conflicts;
  }

  /**
   * Gets conflicts of this set.
   */
  getConflicts() {
    if (!this._conflicts) {
      this._conflicts = this.analyzeConflicts();
    }
    return this._conflicts;
  }

  /**
   * Gets "reduce-reduce" conflicts.
   */
  getRRConflicts() {
    return this.getConflicts().rr;
  }

  /**
   * Gets "shift-reduce" conflicts.
   */
  getSRConflicts() {
    return this.getConflicts().sr;
  }

  /**
   * Whether the state is inadequate, i.e. has conflicts.
   */
  isInadequate() {
    return this.getConflicts().sr.length > 0 ||
      this.getConflicts().rr.length > 0;
  }

  hasTransitionOnSymbol(symbol) {
    return this._transitionsForSymbol.hasOwnProperty(symbol);
  }

  getTransitionOnSymbol(symbol) {
    if (!this.hasTransitionOnSymbol(symbol)) {
      return null;
    }
    return this._transitionsForSymbol[symbol];
  }

  setSymbolTransition(item, state) {
    let symbol = item.getCurrentSymbol().getSymbol();

    if (!this.hasTransitionOnSymbol(symbol)) {
      this._transitionsForSymbol[symbol] = {
        items: [],
        itemsMap: {},
        state,
      };
    }

    let transitionsForSymbol = this._transitionsForSymbol[symbol];

    if (!transitionsForSymbol.itemsMap.hasOwnProperty(item.getKey())) {
      transitionsForSymbol.itemsMap[item.getKey()] = item;
      transitionsForSymbol.items.push(item);
    }

    if (state) {
      transitionsForSymbol.state = state;
      item.connect(state);
    }
  }

  getItemTransion(itemKey) {
    return this.getItemTransionInfo(itemKey).state;
  }

  getItemTransionInfo(itemKey) {
    if (!this._itemsMap.hasOwnProperty(itemKey)) {
      throw new Error(`Item ${itemKey} is not in the state ${this._number}.`);
    }

    let item = this._itemsMap[itemKey];

    if (item.isFinal()) {
      throw new Error(`Item ${itemKey} is final.`);
    }

    return this.getTransitionOnSymbol(item.getCurrentSymbol().getSymbol());
  }

  /**
   * `Goto` operation from the items set. If the state has several items with
   * the same transition symbol, they all go to the same outer state. If
   * a transition for this kernel items set was already calculated in some
   * previous state, then a new state is not created, instead the items are
   * connected to the calculated state.
   */
  goto() {
    if (this._visited) {
      return;
    }

    // Init the transition to null, it will be set
    // new state in the `goto` operation.
    if (!this._transitionsForSymbol) {
      this._transitionsForSymbol = {};
      for (let i = 0; i < this._items.length; i++) {
        const item = this._items[i];
        if (item.isFinal()) {
          continue;
        }
        this.setSymbolTransition(
          item,
          /* state */ null,
        )
      }
    }

    // Build the outer states if needed.
    for (const symbol in this._transitionsForSymbol) {
      const transitionsForSymbol = this.getTransitionOnSymbol(symbol);

      // Already calculated the outer state, exit.
      if (transitionsForSymbol.state) {
        return;
      }

      const items = transitionsForSymbol.items;

      // See if we already calculated transition for this kernel set.
      let outerState = this._canonicalCollection
        .getTranstionForItems(items);

      // If not, create a new outer state with advanced kernel items.
      if (!outerState) {

        outerState = new State(
          /* kernelItems */ items.map(item => item.advance()),
          /* grammar */ this._grammar,
          /* canonicalCollection */ this._canonicalCollection,
        );

        this._canonicalCollection
          .registerTranstionForItems(items, outerState);
      }

      // And connect our items to it.
      for (let i = 0; i < items.length; i++) {
        this.setSymbolTransition(
          items[i],
          /* state */ outerState
        );
      }
    }

    this._visited = true;

    // Recursively goto further in the graph.
    for (const symbol in this._transitionsForSymbol) {
      this.getTransitionOnSymbol(symbol).state.goto();
    }
  }

  isKernelItem(item) {
    return this._kernelItems.indexOf(item) !== -1;
  }

  /**
   * Adds items to this state, and closures them if needed.
   */
  addItems(items) {
    items
      .map(item => this.addItem(item))
      .forEach(item => item && item.closure());
  }

  /**
   * Adds an item to this state.
   */
  addItem(item) {
    if (this._itemsMap.hasOwnProperty(item.getKey())) {
      return;
    }

    this._items.push(item);
    this._itemsMap[item.getKey()] = item;

    if (!this._lr0ItemsMap[item.getLR0Key()]) {
      this._lr0ItemsMap[item.getLR0Key()] = [];
    }
    this._lr0ItemsMap[item.getLR0Key()].push(item);

    item.setState(this);

    return item;
  }

  /**
   * Returns an item by key.
   */
  getItemByKey(key) {
    return this._itemsMap[key];
  }

  /**
   * Returns an item by LR(0) key.
   */
  getItemByLR0Key(lr0Key) {
    let lr0ItemsCount = this._lr0ItemsMap[lr0Key].length;

    if (lr0ItemsCount !== 1) {
      throw new Error(
        `Number of LR0 items for ${lr0Key} is not 1 in ${this.getNumber()}. ` +
        `Call mergeLR0Items before accessing this method.`
      );
    }

    return this._lr0ItemsMap[lr0Key][lr0ItemsCount - 1];
  }

  /**
   * Merges items with the same LR(0) parts for LALR(1).
   */
  mergeLR0Items() {
    for (let lr0Key in this._lr0ItemsMap) {
      const items = this._lr0ItemsMap[lr0Key];
      const rootItem = items[0];

      // Merge the items, keeping only one.
      while (items.length > 1) {
        this.mergeTwoItems(rootItem, items.pop());
      }
    }
  }

  mergeTwoItems(first, second) {
    let transition = !first.isFinal()
      ? this.getItemTransionInfo(first.getKey())
      : null;

    delete this._itemsMap[first.getKey()];

    if (transition) {
      delete transition.itemsMap[first.getKey()];
    }

    let secondKey = second.getKey();

    first.mergeLookaheadSet(second.getLookaheadSet());

    // And remove it from all collections.
    delete this._itemsMap[secondKey];

    let itemIndex = this._items.indexOf(second);
    if (itemIndex !== -1) {
      this._items.splice(itemIndex, 1);
    }

    itemIndex = this._kernelItems.indexOf(second);
    if (itemIndex !== -1) {
      this._kernelItems.splice(itemIndex, 1);
    }

    if (transition) {
      delete transition.itemsMap[secondKey];

      itemIndex = transition.items.indexOf(second);
      if (itemIndex !== -1) {
        transition.items.splice(itemIndex, 1);
      }
    }

    this._itemsMap[first.getKey()] = first;

    if (transition) {
      transition.itemsMap[first.getKey()] = first;
    }
  }

  /**
   * Merges the state with another one, that has the same
   * LR(0) items, but which differs only in lookaheads.
   * This is used in LALR(1) mode when is compressed from CLR(1).
   */
  mergeWithState(state) {
    if (!this._grammar.getMode().isLALR1()) {
      throw new Error(
        `States can be merged only in LALR(1) mode. ` +
        `This mode is ${this._grammar.getMode().toString()}.`
      );
    }

    if (state.getItems().length !== this.getItems().length) {
      throw new Error(
        `LALR(1): State ${state.getNumber()} is not compatible ` +
        `with state ${this.getNumber()}.`
      );
    }

    if (LRItem.keyForItems(this.getKernelItems()) !==
        LRItem.keyForItems(state.getKernelItems())) {

      this.getItems().forEach(item => {
        let thatItem = state.getItemByLR0Key(item.getLR0Key());

        if (!thatItem) {
          throw new Error(
            `Item ${item.toString()} presents in state ${this.getNumber()}, ` +
            `but is absent in the ${state.getNumber()}.`
          );
        }

        this.mergeTwoItems(item, thatItem);
      });
    }

    // After merging lookaheads, we should un-register this new
    // idential state, since it was registered in the constructor.
    this._canonicalCollection.unregisterState(state);

    return this;
  }
};
<commit_msg>Remove more method calls<commit_after>/**
 * The MIT License (MIT)
 * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

/**
 * An abstraction for an items set (kernel plus added),
 * known as a "closure". Recursively closes over
 * all added items, eventually forming an LR-parsing state.
 *
 * Usually there is one kernel item in a state, however there are
 * cases when kernel may contain several items. E.g. being in the state:
 *
 * S' -> • S
 * S  -> • S "a"
 *     | • "b"
 *
 * and having a transition on S, we get both first two items in the
 * kernel of the next state:
 *
 * S' -> S •
 * S  -> S • "a"
 */

import LRItem from './lr-item';

export default class State {

  /**
   * A closure state may have several kernel items. An initial kernel
   * item can be passed in the constructor, other kernel items can
   * be added later via `add` method.
   */
  constructor(
    kernelItems,
    grammar,
    canonicalCollection,
  ) {
    this._kernelItems = kernelItems;
    this._items = [];
    this._grammar = grammar;
    this._canonicalCollection = canonicalCollection;
    this._number = null;

    // A map from transition symbol to the next state.
    this._transitionsForSymbol = null;

    // To avoid infinite recursion in case if an added item
    // is for a recursive production, S -> S "a".
    this._itemsMap = {};

    // Also items map, but by LR(0) key.
    this._lr0ItemsMap = {};

    // Add initial items, and closure them if needed.
    this.addItems(this._kernelItems);

    // And register the state in the collection.
    this._canonicalCollection.registerState(this);
  }

  /**
   * State number in the canonical collection.
   */
  getNumber() {
    return this._number;
  }

  /**
   * Canonical collection can assign a specific
   * number to this state.
   */
  setNumber(number) {
    this._number = number;
  }

  /**
   * Kernel items for which the closure is built.
   */
  getKernelItems() {
    return this._kernelItems;
  }

  /**
   * All items in this closure (kernel plus all expanded).
   */
  getItems() {
    return this._items;
  }

  /**
   * Whether this state is final.
   */
  isFinal() {
    return this._items.length === 1 && this._items[0].isFinal();
  }

  /**
   * Whether the state is accepting.
   */
  isAccept() {
    return this.isFinal() && this._items[0].getProduction().isAugmented();
  }

  /**
   * Returns all reduce items in this set.
   */
  getReduceItems() {
    return this._items.filter(item => item.isReduce());
  }

  /**
   * Calculates all the conflicts in the state, marking the
   * state as inadequate in case of "s/r" or "r/r" conflicts.
   *
   * Note: conflicts only possible in states that contain
   * reduce items, plus another reduce or shift item.
   */
  analyzeConflicts() {
    this._conflicts = {sr: [], rr: []};

    let reduceItems = this.getReduceItems();

    // "reduce-reduce" conflicts.
    if (reduceItems.length > 1) {
      reduceSet: for (let reduceItem of reduceItems) {
        for (let reduceItemToCheck of reduceItems) {
          if (reduceItem !== reduceItemToCheck &&
              reduceItem.conflictsWithReduceItem(reduceItemToCheck)) {
            this._conflicts.rr = reduceItems;
            break reduceSet;
          }
        }
      }
    }

    // "shift-reduce" conflicts.
    this._items.forEach(item => {
      if (!item.isShift()) {
        return;
      }
      reduceItems.forEach(reduceItem => {
        if (reduceItem.conflictsWithShiftSymbol(item.getCurrentSymbol())) {
          this._conflicts.sr.push([item, reduceItem]);
        }
      });
    });

    return this._conflicts;
  }

  /**
   * Gets conflicts of this set.
   */
  getConflicts() {
    if (!this._conflicts) {
      this._conflicts = this.analyzeConflicts();
    }
    return this._conflicts;
  }

  /**
   * Gets "reduce-reduce" conflicts.
   */
  getRRConflicts() {
    return this.getConflicts().rr;
  }

  /**
   * Gets "shift-reduce" conflicts.
   */
  getSRConflicts() {
    return this.getConflicts().sr;
  }

  /**
   * Whether the state is inadequate, i.e. has conflicts.
   */
  isInadequate() {
    return this.getConflicts().sr.length > 0 ||
      this.getConflicts().rr.length > 0;
  }

  hasTransitionOnSymbol(symbol) {
    return this._transitionsForSymbol.hasOwnProperty(symbol);
  }

  getTransitionOnSymbol(symbol) {
    if (!this.hasTransitionOnSymbol(symbol)) {
      return null;
    }
    return this._transitionsForSymbol[symbol];
  }

  setSymbolTransition(item, state) {
    let symbol = item.getCurrentSymbol().getSymbol();

    if (!this.hasTransitionOnSymbol(symbol)) {
      this._transitionsForSymbol[symbol] = {
        items: [],
        itemsMap: {},
        state,
      };
    }

    let transitionsForSymbol = this._transitionsForSymbol[symbol];

    if (!transitionsForSymbol.itemsMap.hasOwnProperty(item.getKey())) {
      transitionsForSymbol.itemsMap[item.getKey()] = item;
      transitionsForSymbol.items.push(item);
    }

    if (state) {
      transitionsForSymbol.state = state;
      item.connect(state);
    }
  }

  getItemTransion(itemKey) {
    return this.getItemTransionInfo(itemKey).state;
  }

  getItemTransionInfo(itemKey) {
    if (!this._itemsMap.hasOwnProperty(itemKey)) {
      throw new Error(`Item ${itemKey} is not in the state ${this._number}.`);
    }

    let item = this._itemsMap[itemKey];

    if (item.isFinal()) {
      throw new Error(`Item ${itemKey} is final.`);
    }

    return this.getTransitionOnSymbol(item.getCurrentSymbol().getSymbol());
  }

  /**
   * `Goto` operation from the items set. If the state has several items with
   * the same transition symbol, they all go to the same outer state. If
   * a transition for this kernel items set was already calculated in some
   * previous state, then a new state is not created, instead the items are
   * connected to the calculated state.
   */
  goto() {
    if (this._visited) {
      return;
    }

    // Init the transition to null, it will be set
    // new state in the `goto` operation.
    if (!this._transitionsForSymbol) {
      this._transitionsForSymbol = {};
      for (let i = 0; i < this._items.length; i++) {
        const item = this._items[i];
        if (item.isFinal()) {
          continue;
        }
        this.setSymbolTransition(
          item,
          /* state */ null,
        )
      }
    }

    // Build the outer states if needed.
    for (const symbol in this._transitionsForSymbol) {
      const transitionsForSymbol = this.getTransitionOnSymbol(symbol);

      // Already calculated the outer state, exit.
      if (transitionsForSymbol.state) {
        return;
      }

      const items = transitionsForSymbol.items;

      // See if we already calculated transition for this kernel set.
      let outerState = this._canonicalCollection
        .getTranstionForItems(items);

      // If not, create a new outer state with advanced kernel items.
      if (!outerState) {

        outerState = new State(
          /* kernelItems */ items.map(item => item.advance()),
          /* grammar */ this._grammar,
          /* canonicalCollection */ this._canonicalCollection,
        );

        this._canonicalCollection
          .registerTranstionForItems(items, outerState);
      }

      // And connect our items to it.
      for (let i = 0; i < items.length; i++) {
        this.setSymbolTransition(
          items[i],
          /* state */ outerState
        );
      }
    }

    this._visited = true;

    // Recursively goto further in the graph.
    for (const symbol in this._transitionsForSymbol) {
      this.getTransitionOnSymbol(symbol).state.goto();
    }
  }

  isKernelItem(item) {
    return this._kernelItems.indexOf(item) !== -1;
  }

  /**
   * Adds items to this state, and closures them if needed.
   */
  addItems(items) {
    items
      .map(item => this.addItem(item))
      .forEach(item => item && item.closure());
  }

  /**
   * Adds an item to this state.
   */
  addItem(item) {
    if (this._itemsMap.hasOwnProperty(item.getKey())) {
      return;
    }

    this._items.push(item);
    this._itemsMap[item.getKey()] = item;

    if (!this._lr0ItemsMap[item.getLR0Key()]) {
      this._lr0ItemsMap[item.getLR0Key()] = [];
    }
    this._lr0ItemsMap[item.getLR0Key()].push(item);

    item.setState(this);

    return item;
  }

  /**
   * Returns an item by key.
   */
  getItemByKey(key) {
    return this._itemsMap[key];
  }

  /**
   * Returns an item by LR(0) key.
   */
  getItemByLR0Key(lr0Key) {
    let lr0ItemsCount = this._lr0ItemsMap[lr0Key].length;

    if (lr0ItemsCount !== 1) {
      throw new Error(
        `Number of LR0 items for ${lr0Key} is not 1 in ${this.getNumber()}. ` +
        `Call mergeLR0Items before accessing this method.`
      );
    }

    return this._lr0ItemsMap[lr0Key][lr0ItemsCount - 1];
  }

  /**
   * Merges items with the same LR(0) parts for LALR(1).
   */
  mergeLR0Items() {
    for (let lr0Key in this._lr0ItemsMap) {
      const items = this._lr0ItemsMap[lr0Key];
      const rootItem = items[0];

      // Merge the items, keeping only one.
      while (items.length > 1) {
        this.mergeTwoItems(rootItem, items.pop());
      }
    }
  }

  mergeTwoItems(first, second) {
    let transition = !first.isFinal()
      ? this.getItemTransionInfo(first.getKey())
      : null;

    delete this._itemsMap[first.getKey()];

    if (transition) {
      delete transition.itemsMap[first.getKey()];
    }

    let secondKey = second.getKey();

    first.mergeLookaheadSet(second.getLookaheadSet());

    // And remove it from all collections.
    delete this._itemsMap[secondKey];

    let itemIndex = this._items.indexOf(second);
    if (itemIndex !== -1) {
      this._items.splice(itemIndex, 1);
    }

    itemIndex = this._kernelItems.indexOf(second);
    if (itemIndex !== -1) {
      this._kernelItems.splice(itemIndex, 1);
    }

    if (transition) {
      delete transition.itemsMap[secondKey];

      itemIndex = transition.items.indexOf(second);
      if (itemIndex !== -1) {
        transition.items.splice(itemIndex, 1);
      }
    }

    this._itemsMap[first.getKey()] = first;

    if (transition) {
      transition.itemsMap[first.getKey()] = first;
    }
  }

  /**
   * Merges the state with another one, that has the same
   * LR(0) items, but which differs only in lookaheads.
   * This is used in LALR(1) mode when is compressed from CLR(1).
   */
  mergeWithState(state) {
    if (this._items.length !== this._items.length) {
      throw new Error(
        `LALR(1): State ${state.getNumber()} is not compatible ` +
        `with state ${this.getNumber()}.`
      );
    }

    if (LRItem.keyForItems(this.getKernelItems()) !==
        LRItem.keyForItems(state.getKernelItems())) {

      this._items.forEach(item => {
        let thatItem = state.getItemByLR0Key(item.getLR0Key());

        if (!thatItem) {
          throw new Error(
            `Item ${item.toString()} presents in state ${this.getNumber()}, ` +
            `but is absent in the ${state.getNumber()}.`
          );
        }

        this.mergeTwoItems(item, thatItem);
      });
    }

    // After merging lookaheads, we should un-register this new
    // idential state, since it was registered in the constructor.
    this._canonicalCollection.unregisterState(state);

    return this;
  }
};
<|endoftext|>


<commit_before>// @flow
var regLib = {
    title: /^#{1,6}\s+.+$/,
    ul: /^[\.\-\*]\s+.+$/,
    ol: /^\d+\.\s?.+$/,
    blockquote: /^!?>.+?$/,
    code: /^\`{3}.*$/,
    table: /^(\|[^|]+)+\|\s*$/,
    align: /^(\|\s*:?-+:?\s*)+\|\s*$/,
    hr: /^(\*|\_|\-){3,}$/,
    img: /\!\[(.*?)\]\((.*?)\)/g,
    a: /\[((?:[^\(\)\[\]]|\\\[|\\\]|\\\(|\\\))+)\]\((.+?)\)/g,
    b: /\*\*(.+?)\*\*/g,
    i: /\*(.+?)\*/g,
    inlineCode: /\`(.+?)\`/g
}

function toLi(tag: string, rows: Array<string>): Array<MdTree> {
    var tree: Array<MdTree> = []

    for (var i = 0; i < rows.length; i++) {
        var row = rows[i]
        if (/^\s+/.test(row)) {

            var _blank = []
            for (; i < rows.length; i++) {
                if (/^\s+/.test(rows[i])) {
                    _blank.push(rows[i].replace(/^\s+/, ''))
                } else {
                    i--
                    break
                }
            }
            tree[tree.length - 1].children = toTree(_blank)

        } else {
            tree.push({
                tag: 'li',
                md: rows[i],
                children: [],
                html: handleInlineSet(tag == 'ul' ? rows[i].replace(/^[\.\*\-]\s*/, '') : rows[i].replace(/^\d\.\s*/, ''))
            })
        }
    }
    return tree
}

function handleInlineSet(txt: string): string {
    txt = replaceHtmlTag(txt)
    txt = handleImg(txt)
    txt = handleInlineCode(txt)
    txt = handleLink(txt)
    txt = handleBold(txt)
    txt = handleItalic(txt)
    return txt
}

function handleImg(txt: string): string {
    return txt.replace(regLib.img, '')
}

function handleLink(txt: string): string {
    return txt.replace(regLib.a, function (txt, $1, $2) {
        return '' + handleInlineSet($1.replace(/\\([\(\)\[\])])/g, '$1')) + ''
    })
}

function handleBold(txt: string): string {
    return txt.replace(regLib.b, '$1')
}

function handleItalic(txt: string): string {
    return txt.replace(regLib.i, '$1')
}

function handleInlineCode(txt: string): string {
    return txt.replace(regLib.inlineCode, '$1')
}

function replaceHtmlTag(txt: string): string {
    return txt.replace(/\</g, '<').replace(/\>/g, '>')
}

function toBlockquote(rows: Array<string>) {
    var tree = []

    for (var i = 0; i < rows.length; i++) {
        var row = rows[i]
        if (/^\s+/.test(row)) {

            var _blank = []
            for (; i < rows.length; i++) {
                if (/^\s+/.test(rows[i])) {
                    _blank.push(rows[i].replace(/^\s+/, ''))
                } else {
                    i--
                    break
                }
            }
            tree[tree.length - 1].children = toTree(_blank)

        } else {
            tree.push({
                tag: 'p',
                md: row,
                children: [],
                html: handleInlineSet(row.replace(/^(!)?>/, ''))
            })
        }

    }
    return tree
}

function toTable(rows: Array<string>) {

    var tdSplit = /[^|]+/g

    var alignRaw = rows[1].match(tdSplit) || []
    var align = []

    for (let i = 0, j = alignRaw.length; i < j; i++) {
        if (/^\s*-+:\s*$/.test(alignRaw[i])) {
            align.push('right')
        } else if (/^\s*:-+:\s*$/.test(alignRaw[i])) {
            align.push('center')
        } else {
            align.push('left')
        }
    }

    var thead = {
        tag: 'tr',
        children: []
    }

    function _tdStyle(i) {
        if (i < align.length && align[i] !== 'left') {
            return {
                'text-align': align[i]
            }
        }
    }

    var thRaw = rows[0].match(tdSplit) || []
    var tdCount = thRaw.length
    for (let i = 0; i < tdCount; i++) {
        thead.children.push({
            tag: 'th',
            style: _tdStyle(i),
            md: thRaw[i],
            html: handleInlineSet(thRaw[i].trim())
        })
    }

    var tbody = []
    for (let i = 2, j = rows.length; i < j; i++) {
        var tbodyTr = {
            tag: 'tr',
            children: []
        }
        var tdCountCopy = tdCount
        var tbodyTds = rows[i].match(tdSplit) || []
        while (tdCountCopy--) {
            var tdMd = tbodyTds[tdCountCopy]
            tbodyTr.children.unshift({
                tag: 'td',
                style: _tdStyle(tdCountCopy),
                md: tdMd,
                html: tdMd ? handleInlineSet(tdMd.trim()) : ''
            })
        }
        tbody.push(tbodyTr)
    }

    return [thead].concat(tbody)
}

function toTree(rows: Array<string>) {
    var html = []
    var rowsCount = rows.length
    var tag = ''

    for (var i = 0; i < rowsCount; i++) {
        var row = rows[i]
        if (regLib.title.test(row)) {
            var hFlagReg = /^#{1,6}/
            var hno = row.match(hFlagReg)
            html.push({
                tag: 'h' + hno[0].length,
                md: row,
                html: handleInlineSet(row.replace(hFlagReg, ''))
            })

        } else if (regLib.hr.test(row)) {
            html.push({
                tag: 'hr',
                md: row
            })

        } else if ((tag = regLib.ul.test(row) ? 'ul' : false) || (tag = regLib.ol.test(row) ? 'ol' : false)) {
            var _raw = []

            for (; i < rowsCount; i++) {
                var _rawRow = rows[i]
                if (!regLib[tag].test(_rawRow) && !/^\s+.+/.test(_rawRow)) {
                    i--
                    break
                }
                _raw.push(_rawRow)
            }

            html.push({
                tag: tag,
                children: toLi(tag, _raw),
                md: _raw.join('\n')
            })

        } else if (regLib.table.test(row) && rows[i + 1] && regLib.align.test(rows[i + 1])) {

            var _raw = [row, rows[i + 1]]
            i += 2
            for (; i < rowsCount; i++) {
                var _rawRow = rows[i]
                if (!regLib.table.test(_rawRow)) {
                    i--
                    break
                }
                _raw.push(_rawRow)
            }

            html.push({
                tag: 'table',
                children: toTable(_raw),
                md: _raw.join('\n')
            })

        } else if (regLib.blockquote.test(row)) {
            var _raw = [row]
            var _class = row.indexOf('!') == 0 ? 'warning' : ''
            i++
            for (; i < rowsCount; i++) {
                var _rawRow = rows[i]
                if (!regLib.blockquote.test(_rawRow) && !/^\s+.+/.test(_rawRow)) {
                    i--
                    break
                }
                _raw.push(_rawRow)
            }
            html.push({
                tag: 'blockquote',
                class: _class,
                children: toBlockquote(_raw),
                md: _raw.join('\n')
            })

        } else if (regLib.code.test(row)) {

            var codeType = row.match(/[^`\s]+/)
            codeType = codeType ? codeType[0] : ''
            var _code = ''
            for (i++; i < rowsCount; i++) {
                var _rawRow = replaceHtmlTag(rows[i])
                if (regLib.code.test(_rawRow)) {
                    break
                }
                _code += _rawRow
                if (!/^\n/.test(_rawRow)) {
                    _code += '\n'
                }
            }

            html.push({
                tag: 'pre',
                children: [{
                    tag: 'code',
                    html: _code
                }],
                attr: {
                    'data-lang': codeType
                },
                class: codeType,
                md: row + '\n' + _code + '```'
            })

        } else if (!/^\n+/.test(row)) {
            html.push({
                tag: 'p',
                md: row,
                html: handleInlineSet(row)
            })
        }
    }
    return html
}

function treeToHtml(nodes: Array<MdTree>): string {
    var html = []
    for (var i = 0; i < nodes.length; i++) {
        var node = nodes[i]

        if (node.tag == 'text') {
            html.push(node.html)
            if (node.children) {
                html.push(treeToHtml(node.children))
            }
        } else {
            html.push('<' + node.tag)
            if (node.class) {
                html.push(' class="' + node.class + '" ')
            }
            if (node.style) {
                html.push(' style="')
                for (var p in node.style) {
                    html.push(p + ':' + node.style[p] + ';')
                }
                html.push('" ')
            }
            if (node.attr) {
                for (var a in node.attr) {
                    html.push(' ' + a + '="' + node.attr[a] + '" ')
                }
            }
            html.push('>')
            if (node.html) {
                html.push(node.html)
            }
            if (node.children) {
                html.push(treeToHtml(node.children))
            }
            html.push('</' + node.tag + '>')
        }
    }
    return html.join('')
}

function mdToTree(md: string): Array<MdTree> {
    var rows = md.match(/.+|^\n/mg) || [];
    return toTree(rows)
}

function mdToHtml(md: string): string {
    return treeToHtml(mdToTree(md))
}

export {mdToTree, mdToHtml, treeToHtml}
<commit_msg>title 解析<commit_after>// @flow
var regLib = {
    title: /^#{1,6}\s+.+$/,
    ul: /^[\.\-\*]\s+.+$/,
    ol: /^\d+\.\s?.+$/,
    blockquote: /^!?>.+?$/,
    code: /^\`{3}.*$/,
    table: /^(\|[^|]+)+\|\s*$/,
    align: /^(\|\s*:?-+:?\s*)+\|\s*$/,
    hr: /^(\*|\_|\-){3,}$/,
    img: /\!\[(.*?)\]\((.*?)\)/g,
    a: /\[((?:[^\(\)\[\]]|\\\[|\\\]|\\\(|\\\))+)\]\((.+?)\)/g,
    b: /\*\*(.+?)\*\*/g,
    i: /\*(.+?)\*/g,
    inlineCode: /\`(.+?)\`/g
}

function toLi(tag: string, rows: Array<string>): Array<MdTree> {
    var tree: Array<MdTree> = []

    for (var i = 0; i < rows.length; i++) {
        var row = rows[i]
        if (/^\s+/.test(row)) {

            var _blank = []
            for (; i < rows.length; i++) {
                if (/^\s+/.test(rows[i])) {
                    _blank.push(rows[i].replace(/^\s+/, ''))
                } else {
                    i--
                    break
                }
            }
            tree[tree.length - 1].children = toTree(_blank)

        } else {
            tree.push({
                tag: 'li',
                md: rows[i],
                children: [],
                html: handleInlineSet(tag == 'ul' ? rows[i].replace(/^[\.\*\-]\s*/, '') : rows[i].replace(/^\d\.\s*/, ''))
            })
        }
    }
    return tree
}

function handleInlineSet(txt: string): string {
    txt = replaceHtmlTag(txt)
    txt = handleImg(txt)
    txt = handleInlineCode(txt)
    txt = handleLink(txt)
    txt = handleBold(txt)
    txt = handleItalic(txt)
    return txt
}

function handleImg(txt: string): string {
    return txt.replace(regLib.img, '<img alt="$1" src="$2">')
}

function handleLink(txt: string): string {
    return txt.replace(regLib.a, function (txt, $1, $2) {
        return '<a href="' + $2 + '" target="_blank">' + handleInlineSet($1.replace(/\\([\(\)\[\])])/g, '$1')) + '</a>'
    })
}

function handleBold(txt: string): string {
    return txt.replace(regLib.b, '<b>$1</b>')
}

function handleItalic(txt: string): string {
    return txt.replace(regLib.i, '<i>$1</i>')
}

function handleInlineCode(txt: string): string {
    return txt.replace(regLib.inlineCode, '<code>$1</code>')
}

function replaceHtmlTag(txt: string): string {
    return txt.replace(/\</g, '<').replace(/\>/g, '>')
}

function toBlockquote(rows: Array<string>) {
    var tree = []

    for (var i = 0; i < rows.length; i++) {
        var row = rows[i]
        if (/^\s+/.test(row)) {

            var _blank = []
            for (; i < rows.length; i++) {
                if (/^\s+/.test(rows[i])) {
                    _blank.push(rows[i].replace(/^\s+/, ''))
                } else {
                    i--
                    break
                }
            }
            tree[tree.length - 1].children = toTree(_blank)

        } else {
            tree.push({
                tag: 'p',
                md: row,
                children: [],
                html: handleInlineSet(row.replace(/^(!)?>/, ''))
            })
        }

    }
    return tree
}

function toTable(rows: Array<string>) {

    var tdSplit = /[^|]+/g

    var alignRaw = rows[1].match(tdSplit) || []
    var align = []

    for (let i = 0, j = alignRaw.length; i < j; i++) {
        if (/^\s*-+:\s*$/.test(alignRaw[i])) {
            align.push('right')
        } else if (/^\s*:-+:\s*$/.test(alignRaw[i])) {
            align.push('center')
        } else {
            align.push('left')
        }
    }

    var thead = {
        tag: 'tr',
        children: []
    }

    function _tdStyle(i) {
        if (i < align.length && align[i] !== 'left') {
            return {
                'text-align': align[i]
            }
        }
    }

    var thRaw = rows[0].match(tdSplit) || []
    var tdCount = thRaw.length
    for (let i = 0; i < tdCount; i++) {
        thead.children.push({
            tag: 'th',
            style: _tdStyle(i),
            md: thRaw[i],
            html: handleInlineSet(thRaw[i].trim())
        })
    }

    var tbody = []
    for (let i = 2, j = rows.length; i < j; i++) {
        var tbodyTr = {
            tag: 'tr',
            children: []
        }
        var tdCountCopy = tdCount
        var tbodyTds = rows[i].match(tdSplit) || []
        while (tdCountCopy--) {
            var tdMd = tbodyTds[tdCountCopy]
            tbodyTr.children.unshift({
                tag: 'td',
                style: _tdStyle(tdCountCopy),
                md: tdMd,
                html: tdMd ? handleInlineSet(tdMd.trim()) : ''
            })
        }
        tbody.push(tbodyTr)
    }

    return [thead].concat(tbody)
}

function toTree(rows: Array<string>) {
    var html = []
    var rowsCount = rows.length
    var tag = ''

    for (var i = 0; i < rowsCount; i++) {
        var row = rows[i]
        if (regLib.title.test(row)) {
            var hFlagReg = /^#{1,6}/
            var hno = row.match(hFlagReg)
            html.push({
                tag: 'h' + hno[0].length,
                md: row,
                html: handleInlineSet(row.replace(hFlagReg, '').slice(1))  // slice(1) 去除空格
            })

        } else if (regLib.hr.test(row)) {
            html.push({
                tag: 'hr',
                md: row
            })

        } else if ((tag = regLib.ul.test(row) ? 'ul' : false) || (tag = regLib.ol.test(row) ? 'ol' : false)) {
            var _raw = []

            for (; i < rowsCount; i++) {
                var _rawRow = rows[i]
                if (!regLib[tag].test(_rawRow) && !/^\s+.+/.test(_rawRow)) {
                    i--
                    break
                }
                _raw.push(_rawRow)
            }

            html.push({
                tag: tag,
                children: toLi(tag, _raw),
                md: _raw.join('\n')
            })

        } else if (regLib.table.test(row) && rows[i + 1] && regLib.align.test(rows[i + 1])) {

            var _raw = [row, rows[i + 1]]
            i += 2
            for (; i < rowsCount; i++) {
                var _rawRow = rows[i]
                if (!regLib.table.test(_rawRow)) {
                    i--
                    break
                }
                _raw.push(_rawRow)
            }

            html.push({
                tag: 'table',
                children: toTable(_raw),
                md: _raw.join('\n')
            })

        } else if (regLib.blockquote.test(row)) {
            var _raw = [row]
            var _class = row.indexOf('!') == 0 ? 'warning' : ''
            i++
            for (; i < rowsCount; i++) {
                var _rawRow = rows[i]
                if (!regLib.blockquote.test(_rawRow) && !/^\s+.+/.test(_rawRow)) {
                    i--
                    break
                }
                _raw.push(_rawRow)
            }
            html.push({
                tag: 'blockquote',
                class: _class,
                children: toBlockquote(_raw),
                md: _raw.join('\n')
            })

        } else if (regLib.code.test(row)) {

            var codeType = row.match(/[^`\s]+/)
            codeType = codeType ? codeType[0] : ''
            var _code = ''
            for (i++; i < rowsCount; i++) {
                var _rawRow = replaceHtmlTag(rows[i])
                if (regLib.code.test(_rawRow)) {
                    break
                }
                _code += _rawRow
                if (!/^\n/.test(_rawRow)) {
                    _code += '\n'
                }
            }

            html.push({
                tag: 'pre',
                children: [{
                    tag: 'code',
                    html: _code
                }],
                attr: {
                    'data-lang': codeType
                },
                class: codeType,
                md: row + '\n' + _code + '```'
            })

        } else if (!/^\n+/.test(row)) {
            html.push({
                tag: 'p',
                md: row,
                html: handleInlineSet(row)
            })
        }
    }
    return html
}

function treeToHtml(nodes: Array<MdTree>): string {
    var html = []
    for (var i = 0; i < nodes.length; i++) {
        var node = nodes[i]

        if (node.tag == 'text') {
            html.push(node.html)
            if (node.children) {
                html.push(treeToHtml(node.children))
            }
        } else {
            html.push('<' + node.tag)
            if (node.class) {
                html.push(' class="' + node.class + '" ')
            }
            if (node.style) {
                html.push(' style="')
                for (var p in node.style) {
                    html.push(p + ':' + node.style[p] + ';')
                }
                html.push('" ')
            }
            if (node.attr) {
                for (var a in node.attr) {
                    html.push(' ' + a + '="' + node.attr[a] + '" ')
                }
            }
            html.push('>')
            if (node.html) {
                html.push(node.html)
            }
            if (node.children) {
                html.push(treeToHtml(node.children))
            }
            html.push('</' + node.tag + '>')
        }
    }
    return html.join('')
}

function mdToTree(md: string): Array<MdTree> {
    var rows = md.match(/.+|^\n/mg) || [];
    return toTree(rows)
}

function mdToHtml(md: string): string {
    return treeToHtml(mdToTree(md))
}

export {mdToTree, mdToHtml, treeToHtml}
<|endoftext|>


<commit_before>define( [ 'Config', 'WebGLRendererModule' ], function ( config, renderer ) {

	return function () {

		var camera = new THREE.PerspectiveCamera( 60, config.width / config.height, 1, 1000 );

		var scene = new THREE.Scene();

		var texts = {};
		var currentText = null;

		var material = new THREE.LineBasicMaterial( { depthTest: false, opacity: 0.9, transparent: true } );

		//

		var startPosition = new THREE.Vector3();
		var endPosition = new THREE.Vector3();
		var deltaPosition = new THREE.Vector3();

		//

		return new FRAME.Module( {

			parameters: {

				text:          new FRAME.Parameter.String( 'Text', 'text' ),
				startPosition: new FRAME.Parameter.Vector3( 'Camera Start', [ 0, 0, 20 ] ),
				endPosition:   new FRAME.Parameter.Vector3( 'Camera End', [ 0, 0, 40 ] )

			},

			init: function () {

				var string = this.parameters.text.value;

				if ( texts[ string ] === undefined ) {

					var shapes = THREE.FontUtils.generateShapes( string, {
						font: "helvetiker",
						size: 2
					} );

					var text = new THREE.Object3D();

					var offset = new THREE.Box3();

					for ( var i = 0; i < shapes.length; i ++ ) {

						var shape = shapes[ i ];

						var geometry = shape.createPointsGeometry();
						geometry.computeBoundingBox();

						offset.union( geometry.boundingBox );

						var mesh = new THREE.Line( geometry, material );
						text.add( mesh );

						if ( shape.holes.length > 0 ) {

							for ( var j = 0; j < shape.holes.length; j ++ ) {

								var hole = shape.holes[ j ];
								shapes.push( hole.toShapes()[ 0 ] );

							}

						}

					}

					text.position.addVectors( offset.min, offset.max ).multiplyScalar( -0.5 );

					texts[ this.parameters.text.value ] = text;

				}

			},

			start: function () {

				// TODO: renderer should be able to render single objects

				if ( currentText !== null ) {

					scene.remove( currentText );

				}

				var string = this.parameters.text.value;

				if ( texts[ string ] === undefined ) {

					init();

				}

				currentText = texts[ string ];
				scene.add( currentText );

				startPosition.fromArray( this.parameters.startPosition.value );
				endPosition.fromArray( this.parameters.endPosition.value );
				deltaPosition.subVectors( endPosition, startPosition );

			},

			update: function ( t ) {

				camera.position.copy( deltaPosition );
				camera.position.multiplyScalar( t );
				camera.position.add( startPosition );
				camera.lookAt( scene.position );

				renderer.render( scene, camera );

			}

		} );

	};

} );<commit_msg>Fixed TextModule.<commit_after>define( [ 'Config', 'WebGLRendererModule' ], function ( config, renderer ) {

	return function () {

		var camera = new THREE.PerspectiveCamera( 60, config.width / config.height, 1, 1000 );

		var scene = new THREE.Scene();

		var texts = {};
		var currentText = null;

		var material = new THREE.LineBasicMaterial( { depthTest: false, opacity: 0.9, transparent: true } );

		//

		var startPosition = new THREE.Vector3();
		var endPosition = new THREE.Vector3();
		var deltaPosition = new THREE.Vector3();

		//

		return new FRAME.Module( {

			parameters: {

				text:          new FRAME.Parameter.String( 'Text', 'text' ),
				startPosition: new FRAME.Parameter.Vector3( 'Camera Start', [ 0, 0, 20 ] ),
				endPosition:   new FRAME.Parameter.Vector3( 'Camera End', [ 0, 0, 40 ] )

			},

			init: function () {

				var string = this.parameters.text.value;

				if ( texts[ string ] === undefined ) {

					var shapes = THREE.FontUtils.generateShapes( string, {
						font: "helvetiker",
						size: 2
					} );

					var text = new THREE.Object3D();

					var offset = new THREE.Box3();

					for ( var i = 0; i < shapes.length; i ++ ) {

						var shape = shapes[ i ];

						var geometry = shape.createPointsGeometry();
						geometry.computeBoundingBox();

						offset.union( geometry.boundingBox );

						var mesh = new THREE.Line( geometry, material );
						text.add( mesh );

						if ( shape.holes.length > 0 ) {

							for ( var j = 0; j < shape.holes.length; j ++ ) {

								var hole = shape.holes[ j ];
								shapes.push( hole.toShapes()[ 0 ] );

							}

						}

					}

					text.position.addVectors( offset.min, offset.max ).multiplyScalar( -0.5 );

					texts[ this.parameters.text.value ] = text;

				}

			},

			start: function () {

				// TODO: renderer should be able to render single objects

				if ( currentText !== null ) {

					scene.remove( currentText );

				}

				var string = this.parameters.text.value;

				if ( texts[ string ] === undefined ) {

					this.init();

				}

				currentText = texts[ string ];
				scene.add( currentText );

				startPosition.fromArray( this.parameters.startPosition.value );
				endPosition.fromArray( this.parameters.endPosition.value );
				deltaPosition.subVectors( endPosition, startPosition );

			},

			update: function ( t ) {

				camera.position.copy( deltaPosition );
				camera.position.multiplyScalar( t );
				camera.position.add( startPosition );
				camera.lookAt( scene.position );

				renderer.render( scene, camera );

			}

		} );

	};

} );<|endoftext|>