<commit_before>const qs = require('query-string');
const url = require('url');
const QuickLRU = require('quick-lru');
const _ = require('lodash');

const { fetch } = require('../../util');
const logger = require('../logger');
const config = require('../../config');
const { validatorGconvert } = require('./gprofiler');

const pcCache = new QuickLRU({ maxSize: config.PC_CACHE_MAX_SIZE });

const fetchOptions = {
  method: 'GET',
  headers: {
    'Accept': 'application/json'
  }
};

const _sanitize = (s) => {
  // Escape (with '\'), to treat them literally, symbols, such as '*', ':', or space,
  // which otherwise play special roles in a Lucene query string.
  return s.replace(/([!*+\-&|()[\]{}^~?:/\\"\s])/g, '\\$1');
};

const _processPhrase = (phrase) => {
  return validatorGconvert(phrase.split(' '),{}).then(result => {
    let { unrecognized, alias  } = result;
    const entities = _.keys( alias ).map( initialAlias =>'xrefid:' + _sanitize( initialAlias.toUpperCase()) );
    const otherIds = unrecognized.map(id=>{
      id=id.toUpperCase();
      const recognized = /^SMP\d{5}$/.test(id) // check for a smpdb or chebi id
        ||/^CHEBI:\d+$/.test(id) && (id.length <= ("CHEBI:".length + 6));
      const sanitized = _sanitize(id);
      return recognized ? ( 'xrefid:' + sanitized ) : ( 'name:' + '*' + sanitized + '*' );
    });
    return entities.concat(otherIds);
  });
};

const _processQueryString = async (inputString) => {
  const keywords = await _processPhrase(inputString);
  const phrase = _sanitize(inputString);
  // return three search query candidates: the first one is the fastest, the last - slowest
  return [
    '(name:' + phrase + ') OR (' + 'name:*' + phrase + '*) OR (' + keywords.join(' AND ') + ')',
    '(' + keywords.join(' OR ') + ')',
    inputString //"as is" (won't additionally escape Lucene query syntax, spaces, etc.)
  ];
};

//Pathway Commons HTTP GET request; options.cmd = 'pc2/get', 'pc2/search', 'pc2/traverse', 'pc2/graph', etc.
const query = async (queryObj) => {
  queryObj.user = 'app-ui';
  let cmd = queryObj.cmd || 'pc2/get';
  //TODO: (not critical) client app's sends useless parameters to the PC server: cmd, lt, gt
  const url = config.PC_URL + cmd + '?' + qs.stringify(queryObj);
  return fetch(url, fetchOptions)
    .then(res => (cmd=='pc2/get'||cmd=='pc2/graph')?res.text():res.json())
    .catch((e) => {
      logger.error('query ' + queryObj + ' failed - ' + e);
      return null;
    });
};

// A fine-tuned PC search to improve relevance of full-text search and filter out unwanted hits.
// The argument (query object) has the following fields:
//  - q: user input - search query string
//  - type: BioPAX type to match/filter by
//  - lt: max graph size result returned
//  - gt: min graph size result returned
const _search = async (args) => {
  const minSize = args.gt || 0;
  const maxSize = args.lt || 250;
  //analyse the input string, generate specific (lucene) search sub-queries
  const queryString = args.q.trim();
  const queries = await _processQueryString(queryString);
  for (let q of queries) {
    args.cmd = 'pc2/search'; //PC command
    args.q = q; //override initial query.q string with the sub-query q
    const searchResult = await query(args); //up to 100 hits at once; if we need more, then must use 'page' parameter...
    const searchSuccess = searchResult != null;
    if (searchSuccess && searchResult.searchHit.length > 0) {
      const filteredResults = searchResult.searchHit.filter(hit => {
        const size = hit.numParticipants ? hit.numParticipants : 0;
        return minSize < size && size < maxSize;
      });
      if (filteredResults.length > 0) {
        return filteredResults;
      }
    }
  }

  return [];
};

const sifGraph = async ( queryObj ) => {
  let path;
  const defaults = {
    limit: 1,
    pattern: ['CONTROLS_STATE_CHANGE_OF','CONTROLS_TRANSPORT_OF','CONTROLS_EXPRESSION_OF','CATALYSIS_PRECEDES','INTERACTS_WITH']
  };
  const params = _.assign(defaults, queryObj);

  if ( params.source.length > 1 ){
    path = 'pathsbetween';
    params.directed = 'false';
  } else {
    path = 'neighborhood';
    params.direction = 'UNDIRECTED';
  }

  const url = config.PC_URL + 'sifgraph/v1/' + path + '?' + qs.stringify(params);
  return fetch(url, {
    method: 'GET',
    headers: {
      'Accept': 'text/plain'
    }
  })
  .then( res => res.text() )
  .catch( e => {
    logger.error('sifGraph ' + queryObj + ' failed - ' + e);
    throw e;
  });
};

const handleEntityUriResponse = text => {
  const uri = new url.URL( text ); // Throws TypeError
  const pathParts = _.compact( uri.pathname.split('/') );
  if( _.isEmpty( pathParts ) || pathParts.length !== 2 ) throw new Error( 'Unrecognized URI' );
  const namespace = _.head( pathParts );
  return {
    origin: uri.origin,
    namespace
  };
};

const constructQueryPath = ( name, localId ) => {
  // Edge case - localId has periods e.g. 'enzyme nomenclature/6.1.1.5' gotta add a trailing slash
  const suffix = /\./.test( localId ) ? '/' : '';
  return name + '/' + localId + suffix;
};

/* fetchEntityUriBase
 * Light wrapper around the pc2 service to get the uri given a collection name and local ID for entity
 * http://www.pathwaycommons.org/pc2/swagger-ui.html#!/metadata45controller/identifierOrgUriUsingGET
 * NB: pc2 service returns 200 and empty body if collection name and/or local ID are unrecognized.
 *   If the local ID is empty, throws a 404
 * @return { object } the URL origin and namespace
 */
const fetchEntityUriBase = ( name, localId ) => {
  const url = config.PC_URL + 'pc2/miriam/uri/' + constructQueryPath( name, localId ) ;
  return fetch( url , { method: 'GET', headers: { 'Accept': 'text/plain' } })
    .then( res => res.text() )
    .then( handleEntityUriResponse );
};

const getEntityUriParts = ( name, localId ) => {
  if( pcCache.has( name ) ){
    return pcCache.get( name );
  } else {
    let res = fetchEntityUriBase( name, localId );
    pcCache.set( name, res );
    res.catch( err => {
      pcCache.del( name );
      logger.error(`Failed to fill cache with ${name} and ${localId} - ${err}`);
    });
    return res;
  }
};

/*
 * xref2Uri: Obtain the URI for an xref
 * @param {string} name -  MIRIAM 'name', 'synonym' ?OR MI CV database citation (MI:0444) 'label'
 * @param {string} localId - Entity local entity identifier, should be valid
 * @return {Object} return the origin and 'namespace' in path
 */
const xref2Uri =  ( name, localId ) => {
  return getEntityUriParts( name, localId )
    .then( uriParts => ({
      uri: uriParts.origin + '/' + uriParts.namespace + '/' + localId,
      namespace: uriParts.namespace
    }) );
};

const search = _.memoize(_search, query => JSON.stringify(query));

module.exports = { query, search, sifGraph, xref2Uri };<commit_msg>Changing cache delete syntax<commit_after>const qs = require('query-string');
const url = require('url');
const QuickLRU = require('quick-lru');
const _ = require('lodash');

const { fetch } = require('../../util');
const logger = require('../logger');
const config = require('../../config');
const { validatorGconvert } = require('./gprofiler');

const pcCache = new QuickLRU({ maxSize: config.PC_CACHE_MAX_SIZE });

const fetchOptions = {
  method: 'GET',
  headers: {
    'Accept': 'application/json'
  }
};

const _sanitize = (s) => {
  // Escape (with '\'), to treat them literally, symbols, such as '*', ':', or space,
  // which otherwise play special roles in a Lucene query string.
  return s.replace(/([!*+\-&|()[\]{}^~?:/\\"\s])/g, '\\$1');
};

const _processPhrase = (phrase) => {
  return validatorGconvert(phrase.split(' '),{}).then(result => {
    let { unrecognized, alias  } = result;
    const entities = _.keys( alias ).map( initialAlias =>'xrefid:' + _sanitize( initialAlias.toUpperCase()) );
    const otherIds = unrecognized.map(id=>{
      id=id.toUpperCase();
      const recognized = /^SMP\d{5}$/.test(id) // check for a smpdb or chebi id
        ||/^CHEBI:\d+$/.test(id) && (id.length <= ("CHEBI:".length + 6));
      const sanitized = _sanitize(id);
      return recognized ? ( 'xrefid:' + sanitized ) : ( 'name:' + '*' + sanitized + '*' );
    });
    return entities.concat(otherIds);
  });
};

const _processQueryString = async (inputString) => {
  const keywords = await _processPhrase(inputString);
  const phrase = _sanitize(inputString);
  // return three search query candidates: the first one is the fastest, the last - slowest
  return [
    '(name:' + phrase + ') OR (' + 'name:*' + phrase + '*) OR (' + keywords.join(' AND ') + ')',
    '(' + keywords.join(' OR ') + ')',
    inputString //"as is" (won't additionally escape Lucene query syntax, spaces, etc.)
  ];
};

//Pathway Commons HTTP GET request; options.cmd = 'pc2/get', 'pc2/search', 'pc2/traverse', 'pc2/graph', etc.
const query = async (queryObj) => {
  queryObj.user = 'app-ui';
  let cmd = queryObj.cmd || 'pc2/get';
  //TODO: (not critical) client app's sends useless parameters to the PC server: cmd, lt, gt
  const url = config.PC_URL + cmd + '?' + qs.stringify(queryObj);
  return fetch(url, fetchOptions)
    .then(res => (cmd=='pc2/get'||cmd=='pc2/graph')?res.text():res.json())
    .catch((e) => {
      logger.error('query ' + queryObj + ' failed - ' + e);
      return null;
    });
};

// A fine-tuned PC search to improve relevance of full-text search and filter out unwanted hits.
// The argument (query object) has the following fields:
//  - q: user input - search query string
//  - type: BioPAX type to match/filter by
//  - lt: max graph size result returned
//  - gt: min graph size result returned
const _search = async (args) => {
  const minSize = args.gt || 0;
  const maxSize = args.lt || 250;
  //analyse the input string, generate specific (lucene) search sub-queries
  const queryString = args.q.trim();
  const queries = await _processQueryString(queryString);
  for (let q of queries) {
    args.cmd = 'pc2/search'; //PC command
    args.q = q; //override initial query.q string with the sub-query q
    const searchResult = await query(args); //up to 100 hits at once; if we need more, then must use 'page' parameter...
    const searchSuccess = searchResult != null;
    if (searchSuccess && searchResult.searchHit.length > 0) {
      const filteredResults = searchResult.searchHit.filter(hit => {
        const size = hit.numParticipants ? hit.numParticipants : 0;
        return minSize < size && size < maxSize;
      });
      if (filteredResults.length > 0) {
        return filteredResults;
      }
    }
  }

  return [];
};

const sifGraph = async ( queryObj ) => {
  let path;
  const defaults = {
    limit: 1,
    pattern: ['CONTROLS_STATE_CHANGE_OF','CONTROLS_TRANSPORT_OF','CONTROLS_EXPRESSION_OF','CATALYSIS_PRECEDES','INTERACTS_WITH']
  };
  const params = _.assign(defaults, queryObj);

  if ( params.source.length > 1 ){
    path = 'pathsbetween';
    params.directed = 'false';
  } else {
    path = 'neighborhood';
    params.direction = 'UNDIRECTED';
  }

  const url = config.PC_URL + 'sifgraph/v1/' + path + '?' + qs.stringify(params);
  return fetch(url, {
    method: 'GET',
    headers: {
      'Accept': 'text/plain'
    }
  })
  .then( res => res.text() )
  .catch( e => {
    logger.error('sifGraph ' + queryObj + ' failed - ' + e);
    throw e;
  });
};

const handleEntityUriResponse = text => {
  const uri = new url.URL( text ); // Throws TypeError
  const pathParts = _.compact( uri.pathname.split('/') );
  if( _.isEmpty( pathParts ) || pathParts.length !== 2 ) throw new Error( 'Unrecognized URI' );
  const namespace = _.head( pathParts );
  return {
    origin: uri.origin,
    namespace
  };
};

const constructQueryPath = ( name, localId ) => {
  // Edge case - localId has periods e.g. 'enzyme nomenclature/6.1.1.5' gotta add a trailing slash
  const suffix = /\./.test( localId ) ? '/' : '';
  return name + '/' + localId + suffix;
};

/* fetchEntityUriBase
 * Light wrapper around the pc2 service to get the uri given a collection name and local ID for entity
 * http://www.pathwaycommons.org/pc2/swagger-ui.html#!/metadata45controller/identifierOrgUriUsingGET
 * NB: pc2 service returns 200 and empty body if collection name and/or local ID are unrecognized.
 *   If the local ID is empty, throws a 404
 * @return { object } the URL origin and namespace
 */
const fetchEntityUriBase = ( name, localId ) => {
  const url = config.PC_URL + 'pc2/miriam/uri/' + constructQueryPath( name, localId ) ;
  return fetch( url , { method: 'GET', headers: { 'Accept': 'text/plain' } })
    .then( res => res.text() )
    .then( handleEntityUriResponse );
};

const getEntityUriParts = ( name, localId ) => {
  if( pcCache.has( name ) ){
    return pcCache.get( name );
  } else {
    let res = fetchEntityUriBase( name, localId );
    pcCache.set( name, res );
    res.catch( err => {
      pcCache.delete( name );
      logger.error(`Failed to fill cache with ${name} and ${localId} - ${err}`);
    });
    return res;
  }
};

/*
 * xref2Uri: Obtain the URI for an xref
 * @param {string} name -  MIRIAM 'name', 'synonym' ?OR MI CV database citation (MI:0444) 'label'
 * @param {string} localId - Entity local entity identifier, should be valid
 * @return {Object} return the origin and 'namespace' in path
 */
const xref2Uri =  ( name, localId ) => {
  return getEntityUriParts( name, localId )
    .then( uriParts => ({
      uri: uriParts.origin + '/' + uriParts.namespace + '/' + localId,
      namespace: uriParts.namespace
    }) );
};

const search = _.memoize(_search, query => JSON.stringify(query));

module.exports = { query, search, sifGraph, xref2Uri };<|endoftext|>


<commit_before>/** PrettyView for an period. */
Ext.define('devilry.administrator.period.PrettyView', {
    extend: 'devilry.administrator.PrettyView',
    alias: 'widget.administrator_periodprettyview',
    requires: [
        'devilry.statistics.PeriodAdminLayout'
    ],

    bodyTpl: Ext.create('Ext.XTemplate',
        '
',
        '   <tpl if="is_old">',
        '
',
        '
Expired period
',
        '
',
        '               This period was active from {start_time:date} to ',
        '               {end_time:date}. Examiners do not have ',
        '               access to any data related to the period, including the ',
        '               feedback they have given to students. ',
        '               Students can still view all their deliveries and feedback.',
        '
',
        '
',
        '   </tpl>',
        '   <tpl if="starttime_in_future">',
        '
',
        '
In the future
',
        '
',
        '               This period has not yet started. Students and examiners ',
        '               can not access the period until its start time, which is ',
        '               {start_time:date}',
        '
',
        '
',
        '   </tpl>',
        '   <tpl if="is_active">',
        '
',
        '
Active
',
        '
',
        '               This period is currently active. It started {start_time:date} ',
        '               and it expires {end_time:date}. When the period expires, examiners ',
        '               will not have access to any data related to the period, including the ',
        '               feedback they have given to students.',
        '
',
        '
',
        '   </tpl>',
        '
'
    ),

    getExtraBodyData: function(record) {
        var is_old = record.data.end_time < Ext.Date.now();
        var starttime_in_future = record.data.start_time > Ext.Date.now();
        return {
            is_old: is_old,
            starttime_in_future: starttime_in_future,
            is_active: (!is_old && !starttime_in_future),
        };
    },

    initComponent: function() {
        Ext.apply(this, {
            relatedButtons: [{
                xtype: 'splitbutton',
                scale: 'large',
                text: 'Overview of all students',
                listeners: {
                    scope: this,
                    click: function() {
                        this._onPeriodOverview(false, false);
                    }
                },
                menu: [{
                    text: 'Open in minimal view mode',
                    listeners: {
                        scope: this,
                        click: function() {
                            this._onPeriodOverview(true, true);
                        }
                    }
                }]
            }]
        });
        this.callParent(arguments);
        if(this.record) {
            this._onLoadRecord();
        } else {
            this.addListener('loadmodel', this._onLoadRecord, this);
        }
    },

    _onLoadRecord: function() {
    }
});
<commit_msg>apps.administrator...period: Remove unused button.<commit_after>/** PrettyView for an period. */
Ext.define('devilry.administrator.period.PrettyView', {
    extend: 'devilry.administrator.PrettyView',
    alias: 'widget.administrator_periodprettyview',
    requires: [
        'devilry.statistics.PeriodAdminLayout'
    ],

    bodyTpl: Ext.create('Ext.XTemplate',
        '
',
        '   <tpl if="is_old">',
        '
',
        '
Expired period
',
        '
',
        '               This period was active from {start_time:date} to ',
        '               {end_time:date}. Examiners do not have ',
        '               access to any data related to the period, including the ',
        '               feedback they have given to students. ',
        '               Students can still view all their deliveries and feedback.',
        '
',
        '
',
        '   </tpl>',
        '   <tpl if="starttime_in_future">',
        '
',
        '
In the future
',
        '
',
        '               This period has not yet started. Students and examiners ',
        '               can not access the period until its start time, which is ',
        '               {start_time:date}',
        '
',
        '
',
        '   </tpl>',
        '   <tpl if="is_active">',
        '
',
        '
Active
',
        '
',
        '               This period is currently active. It started {start_time:date} ',
        '               and it expires {end_time:date}. When the period expires, examiners ',
        '               will not have access to any data related to the period, including the ',
        '               feedback they have given to students.',
        '
',
        '
',
        '   </tpl>',
        '
'
    ),

    getExtraBodyData: function(record) {
        var is_old = record.data.end_time < Ext.Date.now();
        var starttime_in_future = record.data.start_time > Ext.Date.now();
        return {
            is_old: is_old,
            starttime_in_future: starttime_in_future,
            is_active: (!is_old && !starttime_in_future),
        };
    },

    initComponent: function() {
        //Ext.apply(this, {
            //relatedButtons: [{
                //xtype: 'splitbutton',
                //scale: 'large',
                //text: 'Overview of all students',
                //listeners: {
                    //scope: this,
                    //click: function() {
                        //this._onPeriodOverview(false, false);
                    //}
                //},
                //menu: [{
                    //text: 'Open in minimal view mode',
                    //listeners: {
                        //scope: this,
                        //click: function() {
                            //this._onPeriodOverview(true, true);
                        //}
                    //}
                //}]
            //}]
        //});
        this.callParent(arguments);
        if(this.record) {
            this._onLoadRecord();
        } else {
            this.addListener('loadmodel', this._onLoadRecord, this);
        }
    },

    _onLoadRecord: function() {
    }
});
<|endoftext|>


<commit_before>'use strict';

const bigint = require('./bigint');

require('../buffertools');

const SHIFT_LEFT_32 = (1 << 16) * (1 << 16);
const SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
const UNKNOWN_PLP_LEN = new Buffer([0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);

/*
  A Buffer-like class that tracks position.

  As values are written, the position advances by the size of the written data.
  When writing, automatically allocates new buffers if there's not enough space.
 */
module.exports = class WritableTrackingBuffer {
  constructor(initialSize, encoding, doubleSizeGrowth) {
    this.initialSize = initialSize;
    this.encoding = encoding;
    this.doubleSizeGrowth = doubleSizeGrowth;
    this.doubleSizeGrowth || (this.doubleSizeGrowth = false);
    this.encoding || (this.encoding = 'ucs2');
    this.buffer = new Buffer(this.initialSize);
    this.position = 0;
  }

  get data() {
    this.newBuffer(0);
    return this.compositeBuffer;
  }

  copyFrom(buffer) {
    const length = buffer.length;
    this.makeRoomFor(length);
    buffer.copy(this.buffer, this.position);
    return this.position += length;
  }

  makeRoomFor(requiredLength) {
    if (this.buffer.length - this.position < requiredLength) {
      if (this.doubleSizeGrowth) {
        let size = this.buffer.length * 2;
        while (size < requiredLength) {
          size *= 2;
        }
        return this.newBuffer(size);
      } else {
        return this.newBuffer(requiredLength);
      }
    }
  }

  newBuffer(size) {
    size || (size = this.initialSize);
    const buffer = this.buffer.slice(0, this.position);
    if (this.compositeBuffer) {
      this.compositeBuffer = Buffer.concat([this.compositeBuffer, buffer]);
    } else {
      this.compositeBuffer = buffer;
    }
    this.buffer = new Buffer(size);
    return this.position = 0;
  }

  writeUInt8(value) {
    const length = 1;
    this.makeRoomFor(length);
    this.buffer.writeUInt8(value, this.position);
    return this.position += length;
  }

  writeUInt16LE(value) {
    const length = 2;
    this.makeRoomFor(length);
    this.buffer.writeUInt16LE(value, this.position);
    return this.position += length;
  }

  writeUShort(value) {
    return this.writeUInt16LE(value);
  }

  writeUInt16BE(value) {
    const length = 2;
    this.makeRoomFor(length);
    this.buffer.writeUInt16BE(value, this.position);
    return this.position += length;
  }

  writeUInt24LE(value) {
    const length = 3;
    this.makeRoomFor(length);
    this.buffer[this.position + 2] = (value >>> 16) & 0xff;
    this.buffer[this.position + 1] = (value >>> 8) & 0xff;
    this.buffer[this.position] = value & 0xff;
    return this.position += length;
  }

  writeUInt32LE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeUInt32LE(value, this.position);
    return this.position += length;
  }

  writeInt64LE(value) {
    const buf = bigint.numberToInt64LE(value);
    return this.copyFrom(buf);
  }

  writeUInt32BE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeUInt32BE(value, this.position);
    return this.position += length;
  }

  writeUInt40LE(value) {
    // inspired by https://github.com/dpw/node-buffer-more-ints
    this.writeInt32LE(value & -1);
    return this.writeUInt8(Math.floor(value * SHIFT_RIGHT_32));
  }

  writeUInt64LE(value) {
    this.writeInt32LE(value & -1);
    return this.writeUInt32LE(Math.floor(value * SHIFT_RIGHT_32));
  }

  writeInt8(value) {
    const length = 1;
    this.makeRoomFor(length);
    this.buffer.writeInt8(value, this.position);
    return this.position += length;
  }

  writeInt16LE(value) {
    const length = 2;
    this.makeRoomFor(length);
    this.buffer.writeInt16LE(value, this.position);
    return this.position += length;
  }

  writeInt16BE(value) {
    const length = 2;
    this.makeRoomFor(length);
    this.buffer.writeInt16BE(value, this.position);
    return this.position += length;
  }

  writeInt32LE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeInt32LE(value, this.position);
    return this.position += length;
  }

  writeInt32BE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeInt32BE(value, this.position);
    return this.position += length;
  }

  writeFloatLE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeFloatLE(value, this.position);
    return this.position += length;
  }

  writeDoubleLE(value) {
    const length = 8;
    this.makeRoomFor(length);
    this.buffer.writeDoubleLE(value, this.position);
    return this.position += length;
  }

  writeString(value, encoding) {
    encoding || (encoding = this.encoding);

    const length = Buffer.byteLength(value, encoding);
    this.makeRoomFor(length);

    const bytesWritten = this.buffer.write(value, this.position, encoding);
    this.position += length;

    return bytesWritten;
  }

  writeBVarchar(value, encoding) {
    this.writeUInt8(value.length);
    return this.writeString(value, encoding);
  }

  writeUsVarchar(value, encoding) {
    this.writeUInt16LE(value.length);
    return this.writeString(value, encoding);
  }

  writeUsVarbyte(value, encoding) {
    if (encoding == null) {
      encoding = this.encoding;
    }

    let length;
    if (Buffer.isBuffer(value)) {
      length = value.length;
    } else {
      value = value.toString();
      length = Buffer.byteLength(value, encoding);
    }
    this.writeUInt16LE(length);

    if (Buffer.isBuffer(value)) {
      return this.writeBuffer(value);
    } else {
      this.makeRoomFor(length);
      this.buffer.write(value, this.position, encoding);
      return this.position += length;
    }
  }

  writePLPBody(value, encoding) {
    if (encoding == null) {
      encoding = this.encoding;
    }

    let length;
    if (Buffer.isBuffer(value)) {
      length = value.length;
    } else {
      value = value.toString();
      length = Buffer.byteLength(value, encoding);
    }

    // Length of all chunks.
    // this.writeUInt64LE(length);
    // unknown seems to work better here - might revisit later.
    this.writeBuffer(UNKNOWN_PLP_LEN);

    // In the UNKNOWN_PLP_LEN case, the data is represented as a series of zero or more chunks.
    if (length > 0) {
      // One chunk.
      this.writeUInt32LE(length);
      if (Buffer.isBuffer(value)) {
        this.writeBuffer(value);
      } else {
        this.makeRoomFor(length);
        this.buffer.write(value, this.position, encoding);
        this.position += length;
      }
    }

    // PLP_TERMINATOR (no more chunks).
    return this.writeUInt32LE(0);
  }

  writeBuffer(value) {
    const length = value.length;
    this.makeRoomFor(length);
    value.copy(this.buffer, this.position);
    return this.position += length;
  }

  writeMoney(value) {
    this.writeInt32LE(Math.floor(value * SHIFT_RIGHT_32));
    return this.writeInt32LE(value & -1);
  }
};
<commit_msg>Avoid unnecessary allocation in WritableTrackingBuffer<commit_after>'use strict';

const bigint = require('./bigint');

require('../buffertools');

const SHIFT_LEFT_32 = (1 << 16) * (1 << 16);
const SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
const UNKNOWN_PLP_LEN = new Buffer([0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
const ZERO_LENGTH_BUFFER = new Buffer(0);

/*
  A Buffer-like class that tracks position.

  As values are written, the position advances by the size of the written data.
  When writing, automatically allocates new buffers if there's not enough space.
 */
module.exports = class WritableTrackingBuffer {
  constructor(initialSize, encoding, doubleSizeGrowth) {
    this.initialSize = initialSize;
    this.encoding = encoding;
    this.doubleSizeGrowth = doubleSizeGrowth;
    this.doubleSizeGrowth || (this.doubleSizeGrowth = false);
    this.encoding || (this.encoding = 'ucs2');
    this.buffer = new Buffer(this.initialSize);
    this.position = 0;
  }

  get data() {
    this.newBuffer(0);
    return this.compositeBuffer;
  }

  copyFrom(buffer) {
    const length = buffer.length;
    this.makeRoomFor(length);
    buffer.copy(this.buffer, this.position);
    return this.position += length;
  }

  makeRoomFor(requiredLength) {
    if (this.buffer.length - this.position < requiredLength) {
      if (this.doubleSizeGrowth) {
        let size = Math.max(128, this.buffer.length * 2);
        while (size < requiredLength) {
          size *= 2;
        }
        return this.newBuffer(size);
      } else {
        return this.newBuffer(requiredLength);
      }
    }
  }

  newBuffer(size) {
    const buffer = this.buffer.slice(0, this.position);
    if (this.compositeBuffer) {
      this.compositeBuffer = Buffer.concat([this.compositeBuffer, buffer]);
    } else {
      this.compositeBuffer = buffer;
    }
    this.buffer = (size === 0) ? ZERO_LENGTH_BUFFER : new Buffer(size);
    return this.position = 0;
  }

  writeUInt8(value) {
    const length = 1;
    this.makeRoomFor(length);
    this.buffer.writeUInt8(value, this.position);
    return this.position += length;
  }

  writeUInt16LE(value) {
    const length = 2;
    this.makeRoomFor(length);
    this.buffer.writeUInt16LE(value, this.position);
    return this.position += length;
  }

  writeUShort(value) {
    return this.writeUInt16LE(value);
  }

  writeUInt16BE(value) {
    const length = 2;
    this.makeRoomFor(length);
    this.buffer.writeUInt16BE(value, this.position);
    return this.position += length;
  }

  writeUInt24LE(value) {
    const length = 3;
    this.makeRoomFor(length);
    this.buffer[this.position + 2] = (value >>> 16) & 0xff;
    this.buffer[this.position + 1] = (value >>> 8) & 0xff;
    this.buffer[this.position] = value & 0xff;
    return this.position += length;
  }

  writeUInt32LE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeUInt32LE(value, this.position);
    return this.position += length;
  }

  writeInt64LE(value) {
    const buf = bigint.numberToInt64LE(value);
    return this.copyFrom(buf);
  }

  writeUInt32BE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeUInt32BE(value, this.position);
    return this.position += length;
  }

  writeUInt40LE(value) {
    // inspired by https://github.com/dpw/node-buffer-more-ints
    this.writeInt32LE(value & -1);
    return this.writeUInt8(Math.floor(value * SHIFT_RIGHT_32));
  }

  writeUInt64LE(value) {
    this.writeInt32LE(value & -1);
    return this.writeUInt32LE(Math.floor(value * SHIFT_RIGHT_32));
  }

  writeInt8(value) {
    const length = 1;
    this.makeRoomFor(length);
    this.buffer.writeInt8(value, this.position);
    return this.position += length;
  }

  writeInt16LE(value) {
    const length = 2;
    this.makeRoomFor(length);
    this.buffer.writeInt16LE(value, this.position);
    return this.position += length;
  }

  writeInt16BE(value) {
    const length = 2;
    this.makeRoomFor(length);
    this.buffer.writeInt16BE(value, this.position);
    return this.position += length;
  }

  writeInt32LE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeInt32LE(value, this.position);
    return this.position += length;
  }

  writeInt32BE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeInt32BE(value, this.position);
    return this.position += length;
  }

  writeFloatLE(value) {
    const length = 4;
    this.makeRoomFor(length);
    this.buffer.writeFloatLE(value, this.position);
    return this.position += length;
  }

  writeDoubleLE(value) {
    const length = 8;
    this.makeRoomFor(length);
    this.buffer.writeDoubleLE(value, this.position);
    return this.position += length;
  }

  writeString(value, encoding) {
    encoding || (encoding = this.encoding);

    const length = Buffer.byteLength(value, encoding);
    this.makeRoomFor(length);

    const bytesWritten = this.buffer.write(value, this.position, encoding);
    this.position += length;

    return bytesWritten;
  }

  writeBVarchar(value, encoding) {
    this.writeUInt8(value.length);
    return this.writeString(value, encoding);
  }

  writeUsVarchar(value, encoding) {
    this.writeUInt16LE(value.length);
    return this.writeString(value, encoding);
  }

  writeUsVarbyte(value, encoding) {
    if (encoding == null) {
      encoding = this.encoding;
    }

    let length;
    if (Buffer.isBuffer(value)) {
      length = value.length;
    } else {
      value = value.toString();
      length = Buffer.byteLength(value, encoding);
    }
    this.writeUInt16LE(length);

    if (Buffer.isBuffer(value)) {
      return this.writeBuffer(value);
    } else {
      this.makeRoomFor(length);
      this.buffer.write(value, this.position, encoding);
      return this.position += length;
    }
  }

  writePLPBody(value, encoding) {
    if (encoding == null) {
      encoding = this.encoding;
    }

    let length;
    if (Buffer.isBuffer(value)) {
      length = value.length;
    } else {
      value = value.toString();
      length = Buffer.byteLength(value, encoding);
    }

    // Length of all chunks.
    // this.writeUInt64LE(length);
    // unknown seems to work better here - might revisit later.
    this.writeBuffer(UNKNOWN_PLP_LEN);

    // In the UNKNOWN_PLP_LEN case, the data is represented as a series of zero or more chunks.
    if (length > 0) {
      // One chunk.
      this.writeUInt32LE(length);
      if (Buffer.isBuffer(value)) {
        this.writeBuffer(value);
      } else {
        this.makeRoomFor(length);
        this.buffer.write(value, this.position, encoding);
        this.position += length;
      }
    }

    // PLP_TERMINATOR (no more chunks).
    return this.writeUInt32LE(0);
  }

  writeBuffer(value) {
    const length = value.length;
    this.makeRoomFor(length);
    value.copy(this.buffer, this.position);
    return this.position += length;
  }

  writeMoney(value) {
    this.writeInt32LE(Math.floor(value * SHIFT_RIGHT_32));
    return this.writeInt32LE(value & -1);
  }
};
<|endoftext|>


<commit_before>import getCorrectUrls from './getCorrectUrls';

it('returns correct urls for one page word "deliberately"', () => {
  return getCorrectUrls("deliberately")
    .then(urls => expect(urls).toEqual([
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Fdeliberately%22&diagnostics=true"
    ]))
});
<commit_msg>feature: add tests for getCorrectUrls for 2, 3 and 4 pages word<commit_after>import getCorrectUrls from './getCorrectUrls';

it('returns correct urls for 1 page word "deliberately"', () => {
  return getCorrectUrls("deliberately")
    .then(urls => expect(urls).toEqual([
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Fdeliberately%22&diagnostics=true"
    ]))
});

it('returns correct urls for 2 pages word "crank"', () => {
  return getCorrectUrls("crank")
    .then(urls => expect(urls).toEqual([
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Fcrank_1%22&diagnostics=true",
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Fcrank_2%22&diagnostics=true"
    ]))
});

it('returns correct urls for 3 pages word "front"', () => {
  return getCorrectUrls("front")
    .then(urls => expect(urls).toEqual([
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Ffront_1%22&diagnostics=true",
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Ffront_2%22&diagnostics=true",
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Ffront_3%22&diagnostics=true"
    ]))
});

it('returns correct urls for 4 pages word "cool"', () => {
  return getCorrectUrls("cool")
    .then(urls => expect(urls).toEqual([
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Fcool_1%22&diagnostics=true",
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Fcool_2%22&diagnostics=true",
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Fcool_3%22&diagnostics=true",
      "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20html%20WHERE%20url%3D%22http%3A%2F%2Fwww.ldoceonline.com%2Fdictionary%2Fcool_4%22&diagnostics=true"
    ]))
});
<|endoftext|>


<commit_before>var cdb = require('cartodb.js');
var $ = require('jquery');
var GeoreferenceView = require('../../../../../../javascripts/cartodb/common/dialogs/georeference/georeference_view');

describe('common/dialog/georeference/georeference_view', function() {
  beforeEach(function() {
    this.table = TestUtil.createTable('a', [
      ['cartodb_id', 'string'],
      ['the_geom', 'geometry'],
      ['lon', 'number'],
      ['lat', 'number'],
      ['cartodb_georef_status', 'string'],
      ['foobar', 'boolean'],
      ['updated_at', 'date'],
      ['created_at', 'date']
    ]);
    this.user = new cdb.admin.User({
      actions: {},
      base_url: 'http://pepe.cartodb.com'
    });
    this.view = new GeoreferenceView({
      table: this.table,
      user: this.user
    });
    this.view.render();
  });

  it('should not have leaks', function() {
    expect(this.view).toHaveNoLeaks();
  });

  it('should render the different tabs', function() {
    expect(this.innerHTML()).toContain('Lon/Lat Columns');
    expect(this.innerHTML()).toContain('City Names');
    expect(this.innerHTML()).toContain('Admin. Regions');
    expect(this.innerHTML()).toContain('Postal Codes');
    expect(this.innerHTML()).toContain('IP Addresses');
    expect(this.innerHTML()).toContain('Street Addresses');
  });

  it('should start on the lon/lat column', function() {
    var $selectedTabs = this.view.$('.js-tabs .is-selected');
    expect($selectedTabs.length).toEqual(1);
    expect($selectedTabs[0].innerHTML).toContain('Lon/Lat');
    expect(this.innerHTML()).toContain('latitude');
  });

  it('should not render the flash message about table not been geocoded yet', function() {
    expect(this.innerHTML()).not.toContain('FlashMessage');
  });

  describe('when selecting another tab', function() {
    beforeEach(function() {
      $(this.view.$('.js-tabs button').get(1)).click();
    });

    it('should unselect current item and select the new one', function() {
      var $selectedTabs = this.view.$('.js-tabs .is-selected');
      expect($selectedTabs.length).toEqual(1);
      expect($selectedTabs[0].innerHTML).toContain('City');
    });

    it('should change the content', function() {
      expect(this.innerHTML()).not.toContain('latitude');
    });
  });

  describe('when clicking continue', function() {
    beforeEach(function() {
      spyOn(this.view.model, 'continue');
      this.view.$('.ok').click();
    });

    it('should call continue on the model', function() {
      expect(this.view.model.continue).toHaveBeenCalled();
    });
  });

  describe('when geocodeData changes on a tab model', function() {
    beforeEach(function() {
      spyOn(this.view, 'close');
      this.geocodingChosenSpy = jasmine.createSpy('geocodingChosen');
      cdb.god.bind('geocodingChosen', this.geocodingChosenSpy);
      this.geocodeData = { foobar: 'baz!' };
      this.view.model.get('options').first().set('geocodeData', this.geocodeData);
    });

    it('should trigger geocodingChosen event with the data set', function() {
      expect(this.geocodingChosenSpy).toHaveBeenCalled();
      expect(this.geocodingChosenSpy.calls.argsFor(0)[0]).toEqual(this.geocodeData);
    });

    it('should hide the view', function() {
      expect(this.view.close).toHaveBeenCalled();
      // should also delete on hide, so assert it's set too
      expect(this.view.options.clean_on_hide).toBe(true);
    });
  });

  afterEach(function() {
    this.view.clean();
  });
});
<commit_msg>Fix test due to prev copy change<commit_after>var cdb = require('cartodb.js');
var $ = require('jquery');
var GeoreferenceView = require('../../../../../../javascripts/cartodb/common/dialogs/georeference/georeference_view');

describe('common/dialog/georeference/georeference_view', function() {
  beforeEach(function() {
    this.table = TestUtil.createTable('a', [
      ['cartodb_id', 'string'],
      ['the_geom', 'geometry'],
      ['lon', 'number'],
      ['lat', 'number'],
      ['cartodb_georef_status', 'string'],
      ['foobar', 'boolean'],
      ['updated_at', 'date'],
      ['created_at', 'date']
    ]);
    this.user = new cdb.admin.User({
      actions: {},
      base_url: 'http://pepe.cartodb.com'
    });
    this.view = new GeoreferenceView({
      table: this.table,
      user: this.user
    });
    this.view.render();
  });

  it('should not have leaks', function() {
    expect(this.view).toHaveNoLeaks();
  });

  it('should render the different tabs', function() {
    expect(this.innerHTML()).toContain('Lon/Lat Columns');
    expect(this.innerHTML()).toContain('City Names');
    expect(this.innerHTML()).toContain('Admin. Regions');
    expect(this.innerHTML()).toContain('Postal Codes');
    expect(this.innerHTML()).toContain('IP Addresses');
    expect(this.innerHTML()).toContain('Street Addresses');
  });

  it('should start on the lon/lat column', function() {
    var $selectedTabs = this.view.$('.js-tabs .is-selected');
    expect($selectedTabs.length).toEqual(1);
    expect($selectedTabs[0].innerHTML).toContain('Lon/Lat');
    expect(this.innerHTML()).toContain('Latitude');
  });

  it('should not render the flash message about table not been geocoded yet', function() {
    expect(this.innerHTML()).not.toContain('FlashMessage');
  });

  describe('when selecting another tab', function() {
    beforeEach(function() {
      $(this.view.$('.js-tabs button').get(1)).click();
    });

    it('should unselect current item and select the new one', function() {
      var $selectedTabs = this.view.$('.js-tabs .is-selected');
      expect($selectedTabs.length).toEqual(1);
      expect($selectedTabs[0].innerHTML).toContain('City');
    });

    it('should change the content', function() {
      expect(this.innerHTML()).not.toContain('Latitude');
    });
  });

  describe('when clicking continue', function() {
    beforeEach(function() {
      spyOn(this.view.model, 'continue');
      this.view.$('.ok').click();
    });

    it('should call continue on the model', function() {
      expect(this.view.model.continue).toHaveBeenCalled();
    });
  });

  describe('when geocodeData changes on a tab model', function() {
    beforeEach(function() {
      spyOn(this.view, 'close');
      this.geocodingChosenSpy = jasmine.createSpy('geocodingChosen');
      cdb.god.bind('geocodingChosen', this.geocodingChosenSpy);
      this.geocodeData = { foobar: 'baz!' };
      this.view.model.get('options').first().set('geocodeData', this.geocodeData);
    });

    it('should trigger geocodingChosen event with the data set', function() {
      expect(this.geocodingChosenSpy).toHaveBeenCalled();
      expect(this.geocodingChosenSpy.calls.argsFor(0)[0]).toEqual(this.geocodeData);
    });

    it('should hide the view', function() {
      expect(this.view.close).toHaveBeenCalled();
      // should also delete on hide, so assert it's set too
      expect(this.view.options.clean_on_hide).toBe(true);
    });
  });

  afterEach(function() {
    this.view.clean();
  });
});
<|endoftext|>


<commit_before>'use strict';

// INCRKURTOSIS //

/**
* Returns an accumulator function which incrementally computes a corrected sample excess kurtosis.
*
* @returns {Function} accumulator function
*
* @example
* var accumulator = incrkurtosis();
*/
function incrkurtosis() {
	var deltaN2 = 0;
	var deltaN = 0;
	var delta = 0;
	var term1 = 0;
	var mean = 0;
	var M2 = 0;
	var M3 = 0;
	var M4 = 0;
	var N = 0;
	var g2;
	/**
	* If provided a value, the accumulator function returns an updated corrected sample excess kurtosis. If not provided a value, the accumulator function returns the current corrected sample excess kurtosis.
	*
	* #### Method
	*
	* This implementation computes the sample excess kurtosis using the formula for `G_2` in
	*
	*   > Jones and Gill (1998). "Comparing measures of sample skewness and kurtosis." *The Statistician*. [DOI: 10.1111/1467-9884.00122]{@link http://onlinelibrary.wiley.com/doi/10.1111/1467-9884.00122/}
	*
	* In contrast to alternatives for calculating a sample kurtosis, `G_2` is an unbiased estimator under normality.
	*
	*
	* @param {number} [x] - new value
	* @returns {number} corrected sample excess kurtosis
	*
	* @example
	* var kurtosis = accumulator();
	* // returns null
	*
	* kurtosis = accumulator( 2.0 );
	* // returns null
	*
	* kurtosis = accumulator( 2.0 );
	* // returns null
	*
	* kurtosis = accumulator( -4.0 );
	* // returns null
	*
	* kurtosis = accumulator( -4.0 );
	* // returns -6.0
	*/
	return function accumulator( x ) {
		if ( arguments.length === 0 ) {
			if ( N < 4 ) {
				return null;
			}
			// Calculate the population excess kurtosis:
			g2 = ( N*M4 ) / ( M2*M2 ) - 3.0;
			// Return the corrected sample excess kurtosis:
			return (N-1) / ( (N-2)*(N-3) ) * ( (N+1)*g2 + 6.0 );
		}
		N += 1;
		delta = x - mean;
		deltaN = delta / N;
		deltaN2 = deltaN * deltaN;

		term1 = delta * deltaN * (N-1);

		M4 += term1 * deltaN2 * (N*N - 3*N + 3) + 6.0 * deltaN2 * M2 - 4.0 * deltaN * M3;
		M3 += term1 * deltaN * (N-2) - 3.0 * deltaN * M2;
		M2 += term1;
		mean += deltaN;
		if ( N < 4 ) {
			return null;
		}
		// Calculate the population excess kurtosis:
		g2 = N*M4 / ( M2*M2 ) - 3.0;

		// Return the corrected sample excess kurtosis:
		return (N-1) / ( (N-2)*(N-3) ) * ( (N+1)*g2 + 6.0 );
	};
} // end FUNCTION incrkurtosis()

// EXPORTS //

module.exports = incrkurtosis;
<commit_msg>Move extended comment to parent function and make internal function private<commit_after>'use strict';

// MAIN //

/**
* Returns an accumulator function which incrementally computes a corrected sample excess kurtosis.
*
* #### Method
*
* The algorithm computes the sample excess kurtosis using the formula for `G_2` in [Joanes and Gill 1998][@joanes:1998]. In contrast to alternatives for calculating a sample kurtosis, `G_2` is an unbiased estimator under normality.
*
* #### References
*
* * Joanes, D. N., and C. A. Gill. 1998. "Comparing measures of sample skewness and kurtosis." *Journal of the Royal Statistical Society: Series D (The Statistician)* 47 (1). Blackwell Publishers Ltd: 183–89. doi:[10.1111/1467-9884.00122][@joanes:1998].
*
* [@joanes:1998]: http://dx.doi.org/10.1111/1467-9884.00122
*
* @returns {Function} accumulator function
*
* @example
* var accumulator = incrkurtosis();
*
* var kurtosis = accumulator();
* // returns null
*
* kurtosis = accumulator( 2.0 );
* // returns null
*
* kurtosis = accumulator( 2.0 );
* // returns null
*
* kurtosis = accumulator( -4.0 );
* // returns null
*
* kurtosis = accumulator( -4.0 );
* // returns -6.0
*/
function incrkurtosis() {
	var deltaN2 = 0;
	var deltaN = 0;
	var delta = 0;
	var term1 = 0;
	var mean = 0;
	var M2 = 0;
	var M3 = 0;
	var M4 = 0;
	var N = 0;
	var g2;
	/**
	* If provided a value, the accumulator function returns an updated corrected sample excess kurtosis. If not provided a value, the accumulator function returns the current corrected sample excess kurtosis.
	*
	*
	* @private
	* @param {number} [x] - new value
	* @returns {number} corrected sample excess kurtosis
	*
	* @example
	* var kurtosis = accumulator();
	* // returns null
	*
	* kurtosis = accumulator( 2.0 );
	* // returns null
	*
	* kurtosis = accumulator( 2.0 );
	* // returns null
	*
	* kurtosis = accumulator( -4.0 );
	* // returns null
	*
	* kurtosis = accumulator( -4.0 );
	* // returns -6.0
	*/
	return function accumulator( x ) {
		if ( arguments.length === 0 ) {
			if ( N < 4 ) {
				return null;
			}
			// Calculate the population excess kurtosis:
			g2 = ( N*M4 ) / ( M2*M2 ) - 3.0;
			// Return the corrected sample excess kurtosis:
			return (N-1) / ( (N-2)*(N-3) ) * ( (N+1)*g2 + 6.0 );
		}
		N += 1;
		delta = x - mean;
		deltaN = delta / N;
		deltaN2 = deltaN * deltaN;

		term1 = delta * deltaN * (N-1);

		M4 += term1 * deltaN2 * (N*N - 3*N + 3) + 6.0 * deltaN2 * M2 - 4.0 * deltaN * M3;
		M3 += term1 * deltaN * (N-2) - 3.0 * deltaN * M2;
		M2 += term1;
		mean += deltaN;
		if ( N < 4 ) {
			return null;
		}
		// Calculate the population excess kurtosis:
		g2 = N*M4 / ( M2*M2 ) - 3.0;

		// Return the corrected sample excess kurtosis:
		return (N-1) / ( (N-2)*(N-3) ) * ( (N+1)*g2 + 6.0 );
	};
} // end FUNCTION incrkurtosis()

// EXPORTS //

module.exports = incrkurtosis;
<|endoftext|>


<commit_before>//Some resources: http://bl.ocks.org/mbostock/4206573
// http://bl.ocks.org/mbostock/4060606
// http://synthesis.sbecker.net/articles/2012/07/18/learning-d3-part-7-choropleth-maps
// topojson: https://github.com/mbostock/topojson/wiki/API-Reference

// County Comparison Interactions

var holdable_county = function(county) {
  var icon = ""
  var el = "" + county.County + ", " + county.State + icon + ""
  return el
}

var check_icon = function() {
  var icon = ""
  return icon
}
var held_county = function(county) {
  icon = check_icon()
  var el = "" + county.County + ", " + county.State + icon + ""
  return el
}

var removeable_county = function(county) {
  var icon = ""
  var el = "" + county.County + ", " + county.State + icon + ""
  return el
}

var not_duplicate = function(el) {
  if (el.attr('class') == 'holdable') {
    return true
  }
  else if(el.hasClass('held')) {
    return false
  }
}

var hold_county = function(county, el) {
  $('#held-counties').append(removeable_county(county)); // Add county name to held box
  el.attr("class", "held") // Change origin's status to held
  .children().removeClass('fa-plus-circle')
             .addClass('fa-check-circle'); // Change origin's icon to checked
}

var remove_county = function(el) {
  id = el.attr('id')
  el.remove(); // Remove from box
  $("#" + id)
    .attr('class', 'holdable')
    .children().removeClass('fa-check-circle').addClass('fa-plus-circle') // Change checkbox to plus sign
}

var find_county = function(data, id) {
  wanted_county = {}
  data.forEach(function(d) {
    if (id == d.id){
      wanted_county = d
    }
  });
  return wanted_county
}

$(document).ready(function() {

  // ******** START D3 **********
  var width = $('#map-container').width(),
  height = 490;

  // Setting color domains(intervals of values) for our map

  var color_domain = [0, 1, 100, 500, 1000, 3000, 5000, 9000, 18000]
  var color = d3.scale.threshold()
  .domain(color_domain)
  .range(["#dcdcdc", "#d0d6cd", "#bdc9be", "#97b0a0", "#4b7e64", "#256546", "#125937", "#004d28"]);

  var div = d3.select("#map-container").append("div")
  .attr("class", "tooltip")
  .style("opacity", 0);

  var svg = d3.select("#map-container").append("svg")
  .attr('id', 'map')
  .attr("width", width)
  .attr("height", height)
  .attr("viewBox", "90 10 " + width + " " + height);

  var path = d3.geo.path()

  //Reading map file and data

  queue()
  .defer(d3.json, "_json/us.json")
  .defer(d3.csv, "_data/food_atlas_local.csv")
  .await(ready);

  //Start of Choropleth drawing

  function ready(error, us, data) {
    // Data to sortable form
    var rateById = {};
    var countyById = {};
    var stateById = {};

    var DirSale07 = []
    var FmrktPTh13 = []

    data.forEach(function(d) {
      rateById[d.id] = +d.DIRSALES07;
      countyById[d.id] = d.County;
      stateById[d.id] = d.State;

      DirSale07.push(+d.DIRSALES07); // num farms with direct sales in 2007
      FmrktPTh13.push(+d.FMRKTPTH13); // num farmer's markets per 1,000 people
    });

    //Get Top County Objects
    var top_list = _.chain(data)
      .sortBy(function(data){return -1 * data.DIRSALES07;})
      // .map(function(data) {return countyById[data.id] + ", " + stateById[data.id] + ": " +
      //     rateById[data.id] + " farms";})
      .first(5)
      .value();

    var med_DirSale07 = d3.median(DirSale07);

    // Append top counties to DOM
    top_list.forEach(function(county) {
      $('#top-list ul').append("
" + holdable_county(county) + "
");
    });

    // Click to add top counties to save box
    $('#top-list ul li span.holdable').on('click', function(){
      id = $(this).attr('id')
      county = find_county(data, id)

      if(not_duplicate($(this))) {
        hold_county(county, $(this))
      }

      $('.removeable').on('click', function(){
        remove_county($(this))
      })

    })

    $('#top-list ul li span.holdable').on('mouseover', function(){

    })

    console.log("median " + med_DirSale07 + " farms");

  //Drawing Choropleth
  svg.append("g")
  .attr("class", "counties")
  .selectAll("path")
    .data(topojson.feature(us, us.objects.counties).features)
    .enter().append("path")
    .attr("d", path)
    .style ( "fill" , function (d) {return color (rateById[d.id]);})
    .style("opacity", 0.8)
    .attr("stroke-dasharray", "round")
    .attr("stroke", "black")
    .attr("stroke-width", "0.1px")
    .attr('class', 'holdable')
    .attr("id", function(d, i){ return 'county-' + d.id })

  // Saving counties for comparison
  .on('click', function(clicked_county) {
    id = clicked_county.id
    county = find_county(data, id)

    if(not_duplicate($(this))) {
      hold_county(county, $(this))
      d3.select(this)
        .transition().duration(300)
        .style("fill", '#0000ff')
        .attr('class', 'removeable');
    }

    $('.removeable').on('click', function(){
      county_id=""
      map_el = ""
      if($(this).is('path')) {
        county_id = parseInt($(this).attr('id').split('-')[1])
        map_el = $('#county-'+county_id)

        $('#held-counties #'+county_id).remove()
        map_el.attr('class', 'holdable')
      }
      else {
        county_id = $(this).attr('id')
        map_el = $('#county-' + county_id)

        $('#held-counties #' + county_id).remove() // remove county from held county box
      }
      console.log(map_el)
      d3.select('#county-'+county_id)
        .transition().duration(300)
        .style('fill', function (d) {return color (rateById[county_id]);}) // change county color back to green

    })

    // if($('#left-county-content').text() == "") {
    //   $.each(wanted_county, function(i, val) {
    //     $('#left-county-content').append(i + ": " + val + "
")
    //   });
    // }
    // else {

    // }

  })

  //Tooltip + mousevents
  .on("mouseover", function(d) {
    d3.select(this)
      .transition().duration(300)
      .style("opacity", 1);

    div.transition().duration(300)
    .style("opacity", 1)
    div.text(countyById[d.id] + " County, " + stateById[d.id] +
        " Direct Sale Farms : " + rateById[d.id])
    .style("background-color", "#deebf7")
    .style("left", (d3.event.pageX) + "px")
    .style("top", (d3.event.pageY -30) + "px");

    // var county_name = countyById[d.id] + " County, " + stateById[d.id]
    // $('#left-county input').val(county_name);
  })
  .on("mouseout", function() {
    d3.select(this)
    .transition().duration(300)
    .style("opacity", 0.8);
    div.transition().duration(300)
    .style("opacity", 0);
  })

  svg.append("g")
    .attr("class", "states")
    .selectAll("path")
      .data(topojson.feature(us, us.objects.states).features)
    .enter().append("path")
    .attr("fill", "none")
    .attr("stroke", "#fff")
    .attr("stroke-linejoin", "round")
    .attr("d", path);

  };

  // $('#autocomplete').autocomplete({
  //   lookup: counties,
  //   onSelect: function (suggestion) {
  // // some function here
  //   }
  // });

});
<commit_msg>Top counties highlight on map on rollover<commit_after>//Some resources: http://bl.ocks.org/mbostock/4206573
// http://bl.ocks.org/mbostock/4060606
// http://synthesis.sbecker.net/articles/2012/07/18/learning-d3-part-7-choropleth-maps
// topojson: https://github.com/mbostock/topojson/wiki/API-Reference

// County Comparison Interactions

var holdable_county = function(county) {
  var icon = ""
  var el = "" + county.County + ", " + county.State + icon + ""
  return el
}

var check_icon = function() {
  var icon = ""
  return icon
}
var held_county = function(county) {
  icon = check_icon()
  var el = "" + county.County + ", " + county.State + icon + ""
  return el
}

var removeable_county = function(county) {
  var icon = ""
  var el = "" + county.County + ", " + county.State + icon + ""
  return el
}

var not_duplicate = function(el) {
  if (el.attr('class') == 'holdable') {
    return true
  }
  else if(el.hasClass('held')) {
    return false
  }
}

var hold_county = function(county, el) {
  $('#held-counties').append(removeable_county(county)); // Add county name to held box
  el.attr("class", "held") // Change origin's status to held
  .children().removeClass('fa-plus-circle')
             .addClass('fa-check-circle'); // Change origin's icon to checked
}

var remove_county = function(el) {
  id = el.attr('id')
  el.remove(); // Remove from box
  $("#" + id)
    .attr('class', 'holdable')
    .children().removeClass('fa-check-circle').addClass('fa-plus-circle') // Change checkbox to plus sign
}

var find_county = function(data, id) {
  wanted_county = {}
  data.forEach(function(d) {
    if (id == d.id){
      wanted_county = d
    }
  });
  return wanted_county
}

$(document).ready(function() {

  // ******** START D3 **********
  var width = $('#map-container').width(),
  height = 490;

  // Setting color domains(intervals of values) for our map

  var color_domain = [0, 1, 100, 500, 1000, 3000, 5000, 9000, 18000]
  var color = d3.scale.threshold()
  .domain(color_domain)
  .range(["#dcdcdc", "#d0d6cd", "#bdc9be", "#97b0a0", "#4b7e64", "#256546", "#125937", "#004d28"]);

  var div = d3.select("#map-container").append("div")
  .attr("class", "tooltip")
  .style("opacity", 0);

  var svg = d3.select("#map-container").append("svg")
  .attr('id', 'map')
  .attr("width", width)
  .attr("height", height)
  .attr("viewBox", "90 10 " + width + " " + height);

  var path = d3.geo.path()

  //Reading map file and data

  queue()
  .defer(d3.json, "_json/us.json")
  .defer(d3.csv, "_data/food_atlas_local.csv")
  .await(ready);

  //Start of Choropleth drawing

  function ready(error, us, data) {
    // Data to sortable form
    var rateById = {};
    var countyById = {};
    var stateById = {};

    var DirSale07 = []
    var FmrktPTh13 = []

    data.forEach(function(d) {
      rateById[d.id] = +d.DIRSALES07;
      countyById[d.id] = d.County;
      stateById[d.id] = d.State;

      DirSale07.push(+d.DIRSALES07); // num farms with direct sales in 2007
      FmrktPTh13.push(+d.FMRKTPTH13); // num farmer's markets per 1,000 people
    });

    //Get Top County Objects
    var top_list = _.chain(data)
      .sortBy(function(data){return -1 * data.DIRSALES07;})
      // .map(function(data) {return countyById[data.id] + ", " + stateById[data.id] + ": " +
      //     rateById[data.id] + " farms";})
      .first(5)
      .value();

    var med_DirSale07 = d3.median(DirSale07);

    // Append top counties to DOM
    top_list.forEach(function(county) {
      $('#top-list ul').append("
" + holdable_county(county) + "
");
    });

    // Click to add top counties to save box

    $('#top-list ul li span.holdable').on('mouseover', function(){
      county_id = $(this).attr('id')
      d3.select('#county-'+county_id)
        .style('fill', 'blue')
    })

    $('#top-list ul li span.holdable').on('mouseout', function(){
      county_id = $(this).attr('id')
      d3.select('#county-'+county_id)
        .style ( "fill" , function (d) {return color (rateById[d.id]);})
    })

    $('#top-list ul li span.holdable').on('click', function(){
      county_id = $(this).attr('id')
      county = find_county(data, county_id)

      if(not_duplicate($(this))) {
        hold_county(county, $(this))
        $(this).on('mouseout', function(){
          d3.select('#county-'+county_id)
            .style('fill', 'blue')
          })
      }

      $('.removeable').on('click', function(){
        county_id = $(this).attr('id')
        remove_county($(this))
        d3.select('#county-'+county_id)
          .style ( "fill" , function (d) {return color (rateById[d.id]);})
      })

    })

    console.log("median " + med_DirSale07 + " farms");

  //Drawing Choropleth
  svg.append("g")
  .attr("class", "counties")
  .selectAll("path")
    .data(topojson.feature(us, us.objects.counties).features)
    .enter().append("path")
    .attr("d", path)
    .style ( "fill" , function (d) {return color (rateById[d.id]);})
    .style("opacity", 0.8)
    .attr("stroke-dasharray", "round")
    .attr("stroke", "black")
    .attr("stroke-width", "0.1px")
    .attr('class', 'holdable')
    .attr("id", function(d, i){ return 'county-' + d.id })

  // Saving counties for comparison
  .on('click', function(clicked_county) {
    id = clicked_county.id
    county = find_county(data, id)

    if(not_duplicate($(this))) {
      hold_county(county, $(this))
      d3.select(this)
        .transition().duration(300)
        .style("fill", '#0000ff')
        .attr('class', 'removeable');
    }

    $('.removeable').on('click', function(){
      county_id=""
      map_el = ""
      if($(this).is('path')) {
        county_id = parseInt($(this).attr('id').split('-')[1])
        map_el = $('#county-'+county_id)

        $('#held-counties #'+county_id).remove()
        map_el.attr('class', 'holdable')
      }
      else {
        county_id = $(this).attr('id')
        map_el = $('#county-' + county_id)

        $('#held-counties #' + county_id).remove() // remove county from held county box
      }
      console.log(map_el)
      d3.select('#county-'+county_id)
        .transition().duration(300)
        .style('fill', function (d) {return color (rateById[county_id]);}) // change county color back to green

    })

    // if($('#left-county-content').text() == "") {
    //   $.each(wanted_county, function(i, val) {
    //     $('#left-county-content').append(i + ": " + val + "
")
    //   });
    // }
    // else {

    // }

  })

  //Tooltip + mousevents
  .on("mouseover", function(d) {
    d3.select(this)
      .transition().duration(300)
      .style("opacity", 1);

    div.transition().duration(300)
    .style("opacity", 1)
    div.text(countyById[d.id] + " County, " + stateById[d.id] +
        " Direct Sale Farms : " + rateById[d.id])
    .style("background-color", "#deebf7")
    .style("left", (d3.event.pageX) + "px")
    .style("top", (d3.event.pageY -30) + "px");

    // var county_name = countyById[d.id] + " County, " + stateById[d.id]
    // $('#left-county input').val(county_name);
  })
  .on("mouseout", function() {
    d3.select(this)
    .transition().duration(300)
    .style("opacity", 0.8);
    div.transition().duration(300)
    .style("opacity", 0);
  })

  svg.append("g")
    .attr("class", "states")
    .selectAll("path")
      .data(topojson.feature(us, us.objects.states).features)
    .enter().append("path")
    .attr("fill", "none")
    .attr("stroke", "#fff")
    .attr("stroke-linejoin", "round")
    .attr("d", path);

  };

  // $('#autocomplete').autocomplete({
  //   lookup: counties,
  //   onSelect: function (suggestion) {
  // // some function here
  //   }
  // });

});
<|endoftext|>


<commit_before>import path from 'path';
import fs from 'fs';

import etch from 'etch';

import Commit from '../../lib/models/commit';

import CommitViewController, {COMMIT_GRAMMAR_SCOPE} from '../../lib/controllers/commit-view-controller';
import {cloneRepository, buildRepository} from '../helpers';

describe('CommitViewController', function() {
  let atomEnvironment, workspace, commandRegistry, notificationManager, grammars, lastCommit;

  beforeEach(function() {
    atomEnvironment = global.buildAtomEnvironment();
    workspace = atomEnvironment.workspace;
    commandRegistry = atomEnvironment.commands;
    notificationManager = atomEnvironment.notifications;
    grammars = atomEnvironment.grammars;

    lastCommit = new Commit('a1e23fd45', 'last commit message');
  });

  afterEach(function() {
    atomEnvironment.destroy();
  });

  it('correctly updates state when switching repos', async function() {
    const workdirPath1 = await cloneRepository('three-files');
    const repository1 = await buildRepository(workdirPath1);
    const workdirPath2 = await cloneRepository('three-files');
    const repository2 = await buildRepository(workdirPath2);
    const controller = new CommitViewController({
      workspace, commandRegistry, notificationManager, lastCommit, repository: repository1,
    });

    assert.equal(controller.regularCommitMessage, '');
    assert.equal(controller.amendingCommitMessage, '');

    controller.regularCommitMessage = 'regular message 1';
    controller.amendingCommitMessage = 'amending message 1';

    await controller.update({repository: repository2});
    assert.equal(controller.regularCommitMessage, '');
    assert.equal(controller.amendingCommitMessage, '');

    await controller.update({repository: repository1});
    assert.equal(controller.regularCommitMessage, 'regular message 1');
    assert.equal(controller.amendingCommitMessage, 'amending message 1');
  });

  describe('the passed commit message', function() {
    let controller, commitView;
    beforeEach(async function() {
      const workdirPath = await cloneRepository('three-files');
      const repository = await buildRepository(workdirPath);
      controller = new CommitViewController({workspace, commandRegistry, notificationManager, lastCommit, repository});
      commitView = controller.refs.commitView;
    });

    it('is set to the regularCommitMessage in the default case', async function() {
      controller.regularCommitMessage = 'regular message';
      await controller.update();
      assert.equal(commitView.props.message, 'regular message');
    });

    describe('when isAmending is true', function() {
      it('is set to the last commits message if amendingCommitMessage is blank', async function() {
        controller.amendingCommitMessage = 'amending commit message';
        await controller.update({isAmending: true, lastCommit});
        assert.equal(commitView.props.message, 'amending commit message');
      });

      it('is set to amendingCommitMessage if it is set', async function() {
        controller.amendingCommitMessage = 'amending commit message';
        await controller.update({isAmending: true, lastCommit});
        assert.equal(commitView.props.message, 'amending commit message');
      });
    });

    describe('when a merge message is defined', function() {
      it('is set to the merge message when merging', async function() {
        await controller.update({isMerging: true, mergeMessage: 'merge conflict!'});
        assert.equal(commitView.props.message, 'merge conflict!');
      });

      it('is set to regularCommitMessage if it is set', async function() {
        controller.regularCommitMessage = 'regular commit message';
        await controller.update({isMerging: true, mergeMessage: 'merge conflict!'});
        assert.equal(commitView.props.message, 'regular commit message');
      });
    });
  });

  describe('committing', function() {
    let controller, resolve, reject, workdirPath, repository;

    beforeEach(async function() {
      workdirPath = await cloneRepository('three-files');
      repository = await buildRepository(workdirPath);
      const commit = () => new Promise((resolver, rejecter) => {
        resolve = resolver;
        reject = rejecter;
      });

      controller = new CommitViewController({
        workspace,
        commandRegistry,
        notificationManager,
        grammars,
        lastCommit,
        repository,
        commit,
      });
    });

    afterEach(() => {
      controller.destroy();
    });

    it('clears the regular and amending commit messages', async function() {
      controller.regularCommitMessage = 'regular';
      controller.amendingCommitMessage = 'amending';

      const promise = controller.commit('message');
      resolve();
      await promise;

      assert.equal(controller.regularCommitMessage, '');
      assert.equal(controller.amendingCommitMessage, '');
    });

    it('issues a notification on failure', async function() {
      controller.regularCommitMessage = 'regular';
      controller.amendingCommitMessage = 'amending';

      sinon.spy(notificationManager, 'addError');

      const promise = controller.commit('message');
      const e = new Error('message');
      e.stdErr = 'stderr';
      reject(e);
      await promise;

      assert.isTrue(notificationManager.addError.called);

      assert.equal(controller.regularCommitMessage, 'regular');
      assert.equal(controller.amendingCommitMessage, 'amending');
    });

    describe('toggling between commit box and commit editor', function() {
      it('transfers the commit message contents of the last editor', async function() {
        controller.refs.commitView.editor.setText('message in box');

        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), controller.getCommitMessagePath());
        await assert.async.isTrue(controller.refs.commitView.props.deactivateCommitBox);
        const editor = workspace.getActiveTextEditor();
        assert.equal(editor.getText(), 'message in box');

        editor.setText('message in editor');
        editor.save();

        commandRegistry.dispatch(atomEnvironment.views.getView(editor), 'pane:split-right-and-copy-active-item');
        await assert.async.notEqual(workspace.getActiveTextEditor(), editor);

        sinon.spy(controller.refs.commitView, 'update');
        editor.destroy();
        await assert.async.isTrue(controller.refs.commitView.update.called);
        assert.equal(controller.refs.commitView.editor.getText(), 'message in box');
        assert.isTrue(controller.refs.commitView.props.deactivateCommitBox);

        workspace.getActiveTextEditor().destroy();
        await assert.async.isFalse(controller.refs.commitView.props.deactivateCommitBox);
        await assert.async.equal(controller.refs.commitView.editor.getText(), 'message in editor');
      });

      it('activates editor if already opened but in background', async function() {
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), controller.getCommitMessagePath());
        const editor = workspace.getActiveTextEditor();

        await workspace.open(path.join(workdirPath, 'a.txt'));
        workspace.getActivePane().splitRight();
        await workspace.open(path.join(workdirPath, 'b.txt'));
        assert.notEqual(workspace.getActiveTextEditor(), editor);

        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor(), editor);
      });

      it('closes all open commit message editors if one is in the foreground of a pane, prompting for unsaved changes', async function() {
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), controller.getCommitMessagePath());

        const editor = workspace.getActiveTextEditor();
        commandRegistry.dispatch(atomEnvironment.views.getView(editor), 'pane:split-right-and-copy-active-item');
        assert.equal(controller.getCommitMessageEditors().length, 2);

        // Activate another editor but keep commit message editor in foreground of inactive pane
        await workspace.open(path.join(workdirPath, 'a.txt'));
        assert.notEqual(workspace.getActiveTextEditor(), editor);

        editor.setText('make some new changes');

        // atom internals calls `confirm` on the ApplicationDelegate instead of the atom environment
        sinon.stub(atomEnvironment.applicationDelegate, 'confirm').returns(0); // Save
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(controller.getCommitMessageEditors().length, 0);
        assert.isTrue(atomEnvironment.applicationDelegate.confirm.called);
        await assert.async.equal(controller.refs.commitView.editor.getText(), 'make some new changes');
      });
    });

    describe('committing from commit editor', function() {
      it('uses git commit grammar in the editor', async function() {
        await atomEnvironment.packages.activatePackage('language-git');
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getGrammar().scopeName, COMMIT_GRAMMAR_SCOPE);
      });

      it('takes the commit message from the editor and deletes the `ATOM_COMMIT_EDITMSG` file', async function() {
        fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'some changes');
        await repository.stageFiles(['a.txt']);

        await controller.update({
          commit: repository.commit.bind(repository),
          prepareToCommit: () => true,
          stagedChangesExist: true,
        });

        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');

        await assert.async.isTrue(controller.refs.commitView.isCommitButtonEnabled());
        const editor = workspace.getActiveTextEditor();
        assert.equal(editor.getPath(), controller.getCommitMessagePath());

        editor.setText('message in editor');
        editor.save();
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:commit');

        await assert.async.equal((await repository.getLastCommit()).getMessage(), 'message in editor');
        await assert.async.isFalse(fs.existsSync(controller.getCommitMessagePath()));
      });

      it('asks user to confirm if commit editor has unsaved changes', async function() {
        const confirm = sinon.stub();
        const commit = sinon.stub();
        await controller.update({confirm, commit, prepareToCommit: () => true, stagedChangesExist: true});
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), controller.getCommitMessagePath());
        const editor = workspace.getActiveTextEditor();

        editor.setText('unsaved changes');
        commandRegistry.dispatch(atomEnvironment.views.getView(editor), 'pane:split-right-and-copy-active-item');
        await assert.async.notEqual(workspace.getActiveTextEditor(), editor);
        assert.equal(workspace.getTextEditors().length, 2);

        confirm.returns(1); // Cancel
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:commit');
        await etch.getScheduler().getNextUpdatePromise();
        assert.equal(commit.callCount, 0);

        confirm.returns(0); // Commit
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:commit');
        await etch.getScheduler().getNextUpdatePromise();
        await assert.async.equal(commit.callCount, 1);
        assert.equal(workspace.getTextEditors().length, 0);
      });
    });
  });
});
<commit_msg>Use `until` to make test more readable<commit_after>import path from 'path';
import fs from 'fs';

import etch from 'etch';
import until from 'test-until';

import Commit from '../../lib/models/commit';

import CommitViewController, {COMMIT_GRAMMAR_SCOPE} from '../../lib/controllers/commit-view-controller';
import {cloneRepository, buildRepository} from '../helpers';

describe('CommitViewController', function() {
  let atomEnvironment, workspace, commandRegistry, notificationManager, grammars, lastCommit;

  beforeEach(function() {
    atomEnvironment = global.buildAtomEnvironment();
    workspace = atomEnvironment.workspace;
    commandRegistry = atomEnvironment.commands;
    notificationManager = atomEnvironment.notifications;
    grammars = atomEnvironment.grammars;

    lastCommit = new Commit('a1e23fd45', 'last commit message');
  });

  afterEach(function() {
    atomEnvironment.destroy();
  });

  it('correctly updates state when switching repos', async function() {
    const workdirPath1 = await cloneRepository('three-files');
    const repository1 = await buildRepository(workdirPath1);
    const workdirPath2 = await cloneRepository('three-files');
    const repository2 = await buildRepository(workdirPath2);
    const controller = new CommitViewController({
      workspace, commandRegistry, notificationManager, lastCommit, repository: repository1,
    });

    assert.equal(controller.regularCommitMessage, '');
    assert.equal(controller.amendingCommitMessage, '');

    controller.regularCommitMessage = 'regular message 1';
    controller.amendingCommitMessage = 'amending message 1';

    await controller.update({repository: repository2});
    assert.equal(controller.regularCommitMessage, '');
    assert.equal(controller.amendingCommitMessage, '');

    await controller.update({repository: repository1});
    assert.equal(controller.regularCommitMessage, 'regular message 1');
    assert.equal(controller.amendingCommitMessage, 'amending message 1');
  });

  describe('the passed commit message', function() {
    let controller, commitView;
    beforeEach(async function() {
      const workdirPath = await cloneRepository('three-files');
      const repository = await buildRepository(workdirPath);
      controller = new CommitViewController({workspace, commandRegistry, notificationManager, lastCommit, repository});
      commitView = controller.refs.commitView;
    });

    it('is set to the regularCommitMessage in the default case', async function() {
      controller.regularCommitMessage = 'regular message';
      await controller.update();
      assert.equal(commitView.props.message, 'regular message');
    });

    describe('when isAmending is true', function() {
      it('is set to the last commits message if amendingCommitMessage is blank', async function() {
        controller.amendingCommitMessage = 'amending commit message';
        await controller.update({isAmending: true, lastCommit});
        assert.equal(commitView.props.message, 'amending commit message');
      });

      it('is set to amendingCommitMessage if it is set', async function() {
        controller.amendingCommitMessage = 'amending commit message';
        await controller.update({isAmending: true, lastCommit});
        assert.equal(commitView.props.message, 'amending commit message');
      });
    });

    describe('when a merge message is defined', function() {
      it('is set to the merge message when merging', async function() {
        await controller.update({isMerging: true, mergeMessage: 'merge conflict!'});
        assert.equal(commitView.props.message, 'merge conflict!');
      });

      it('is set to regularCommitMessage if it is set', async function() {
        controller.regularCommitMessage = 'regular commit message';
        await controller.update({isMerging: true, mergeMessage: 'merge conflict!'});
        assert.equal(commitView.props.message, 'regular commit message');
      });
    });
  });

  describe('committing', function() {
    let controller, resolve, reject, workdirPath, repository;

    beforeEach(async function() {
      workdirPath = await cloneRepository('three-files');
      repository = await buildRepository(workdirPath);
      const commit = () => new Promise((resolver, rejecter) => {
        resolve = resolver;
        reject = rejecter;
      });

      controller = new CommitViewController({
        workspace,
        commandRegistry,
        notificationManager,
        grammars,
        lastCommit,
        repository,
        commit,
      });
    });

    afterEach(() => {
      controller.destroy();
    });

    it('clears the regular and amending commit messages', async function() {
      controller.regularCommitMessage = 'regular';
      controller.amendingCommitMessage = 'amending';

      const promise = controller.commit('message');
      resolve();
      await promise;

      assert.equal(controller.regularCommitMessage, '');
      assert.equal(controller.amendingCommitMessage, '');
    });

    it('issues a notification on failure', async function() {
      controller.regularCommitMessage = 'regular';
      controller.amendingCommitMessage = 'amending';

      sinon.spy(notificationManager, 'addError');

      const promise = controller.commit('message');
      const e = new Error('message');
      e.stdErr = 'stderr';
      reject(e);
      await promise;

      assert.isTrue(notificationManager.addError.called);

      assert.equal(controller.regularCommitMessage, 'regular');
      assert.equal(controller.amendingCommitMessage, 'amending');
    });

    describe('toggling between commit box and commit editor', function() {
      it('transfers the commit message contents of the last editor', async function() {
        controller.refs.commitView.editor.setText('message in box');

        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), controller.getCommitMessagePath());
        await assert.async.isTrue(controller.refs.commitView.props.deactivateCommitBox);
        const editor = workspace.getActiveTextEditor();
        assert.equal(editor.getText(), 'message in box');

        editor.setText('message in editor');
        editor.save();

        commandRegistry.dispatch(atomEnvironment.views.getView(editor), 'pane:split-right-and-copy-active-item');
        await assert.async.notEqual(workspace.getActiveTextEditor(), editor);

        sinon.spy(controller.refs.commitView, 'update');
        editor.destroy();
        await until(() => controller.refs.commitView.update.called);
        assert.equal(controller.refs.commitView.editor.getText(), 'message in box');
        assert.isTrue(controller.refs.commitView.props.deactivateCommitBox);

        workspace.getActiveTextEditor().destroy();
        await assert.async.isFalse(controller.refs.commitView.props.deactivateCommitBox);
        await assert.async.equal(controller.refs.commitView.editor.getText(), 'message in editor');
      });

      it('activates editor if already opened but in background', async function() {
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), controller.getCommitMessagePath());
        const editor = workspace.getActiveTextEditor();

        await workspace.open(path.join(workdirPath, 'a.txt'));
        workspace.getActivePane().splitRight();
        await workspace.open(path.join(workdirPath, 'b.txt'));
        assert.notEqual(workspace.getActiveTextEditor(), editor);

        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor(), editor);
      });

      it('closes all open commit message editors if one is in the foreground of a pane, prompting for unsaved changes', async function() {
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), controller.getCommitMessagePath());

        const editor = workspace.getActiveTextEditor();
        commandRegistry.dispatch(atomEnvironment.views.getView(editor), 'pane:split-right-and-copy-active-item');
        assert.equal(controller.getCommitMessageEditors().length, 2);

        // Activate another editor but keep commit message editor in foreground of inactive pane
        await workspace.open(path.join(workdirPath, 'a.txt'));
        assert.notEqual(workspace.getActiveTextEditor(), editor);

        editor.setText('make some new changes');

        // atom internals calls `confirm` on the ApplicationDelegate instead of the atom environment
        sinon.stub(atomEnvironment.applicationDelegate, 'confirm').returns(0); // Save
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(controller.getCommitMessageEditors().length, 0);
        assert.isTrue(atomEnvironment.applicationDelegate.confirm.called);
        await assert.async.equal(controller.refs.commitView.editor.getText(), 'make some new changes');
      });
    });

    describe('committing from commit editor', function() {
      it('uses git commit grammar in the editor', async function() {
        await atomEnvironment.packages.activatePackage('language-git');
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getGrammar().scopeName, COMMIT_GRAMMAR_SCOPE);
      });

      it('takes the commit message from the editor and deletes the `ATOM_COMMIT_EDITMSG` file', async function() {
        fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'some changes');
        await repository.stageFiles(['a.txt']);

        await controller.update({
          commit: repository.commit.bind(repository),
          prepareToCommit: () => true,
          stagedChangesExist: true,
        });

        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');

        await assert.async.isTrue(controller.refs.commitView.isCommitButtonEnabled());
        const editor = workspace.getActiveTextEditor();
        assert.equal(editor.getPath(), controller.getCommitMessagePath());

        editor.setText('message in editor');
        editor.save();
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:commit');

        await assert.async.equal((await repository.getLastCommit()).getMessage(), 'message in editor');
        await assert.async.isFalse(fs.existsSync(controller.getCommitMessagePath()));
      });

      it('asks user to confirm if commit editor has unsaved changes', async function() {
        const confirm = sinon.stub();
        const commit = sinon.stub();
        await controller.update({confirm, commit, prepareToCommit: () => true, stagedChangesExist: true});
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:toggle-expanded-commit-message-editor');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), controller.getCommitMessagePath());
        const editor = workspace.getActiveTextEditor();

        editor.setText('unsaved changes');
        commandRegistry.dispatch(atomEnvironment.views.getView(editor), 'pane:split-right-and-copy-active-item');
        await assert.async.notEqual(workspace.getActiveTextEditor(), editor);
        assert.equal(workspace.getTextEditors().length, 2);

        confirm.returns(1); // Cancel
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:commit');
        await etch.getScheduler().getNextUpdatePromise();
        assert.equal(commit.callCount, 0);

        confirm.returns(0); // Commit
        commandRegistry.dispatch(atomEnvironment.views.getView(workspace), 'github:commit');
        await etch.getScheduler().getNextUpdatePromise();
        await assert.async.equal(commit.callCount, 1);
        assert.equal(workspace.getTextEditors().length, 0);
      });
    });
  });
});
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var allocCluster = require('../lib/test-cluster.js');

allocCluster.test('register and forward', {
    size: 5,
    namedRemotes: ['mary', 'mary', 'mary']
}, function t(cluster, assert) {
    cluster.logger.whitelist('warn', 'forwarding error frame');

    var steve = cluster.remotes.steve;
    var bob = cluster.remotes.bob;

    var maries = cluster.namedRemotes;

    cluster.checkExitPeers(assert, {
        serviceName: steve.serviceName,
        hostPort: steve.hostPort
    });
    var counter = 0;

    for (var i = 0; i < maries.length; i++) {
        var mary = maries[i];
        mary.serverChannel.register('ping', ping);
    }

    bob.clientChannel.request({
        serviceName: maries[0].serviceName,
        retryFlags: {
            never: true
        }
    }).send('ping', null, JSON.stringify('oh hi lol'), onForwarded);

    function ping(req, res) {
        counter++;
        res.sendError('UnexpectedError', 'oops');
    }

    function onForwarded(err, res, arg2, arg3) {
        assert.ok(err);
        assert.equal(err.message, 'oops');

        assert.equal(counter, 1);

        var logs = cluster.logger.items().filter(function only(log) {
            return log.msg === 'forwarding error frame';
        });
        assert.ok(logs.length >= 1 && logs.length <= 2);

        assert.equal(logs[0].levelName, 'warn');
        assert.equal(logs[0].meta.serviceName, 'mary');
        assert.equal(logs[0].meta.callerName, 'bob');
        assert.equal(logs[0].meta.error.message, 'oops');

        assert.end();
    }
});
<commit_msg>test/forward/forwarding-respects-relay-flags: fix decription<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var allocCluster = require('../lib/test-cluster.js');

allocCluster.test('register and forward with never retry flag', {
    size: 5,
    namedRemotes: ['mary', 'mary', 'mary']
}, function t(cluster, assert) {
    cluster.logger.whitelist('warn', 'forwarding error frame');

    var steve = cluster.remotes.steve;
    var bob = cluster.remotes.bob;

    var maries = cluster.namedRemotes;

    cluster.checkExitPeers(assert, {
        serviceName: steve.serviceName,
        hostPort: steve.hostPort
    });
    var counter = 0;

    for (var i = 0; i < maries.length; i++) {
        var mary = maries[i];
        mary.serverChannel.register('ping', ping);
    }

    bob.clientChannel.request({
        serviceName: maries[0].serviceName,
        retryFlags: {
            never: true
        }
    }).send('ping', null, JSON.stringify('oh hi lol'), onForwarded);

    function ping(req, res) {
        counter++;
        res.sendError('UnexpectedError', 'oops');
    }

    function onForwarded(err, res, arg2, arg3) {
        assert.ok(err);
        assert.equal(err.message, 'oops');

        assert.equal(counter, 1);

        var logs = cluster.logger.items().filter(function only(log) {
            return log.msg === 'forwarding error frame';
        });
        assert.ok(logs.length >= 1 && logs.length <= 2);

        assert.equal(logs[0].levelName, 'warn');
        assert.equal(logs[0].meta.serviceName, 'mary');
        assert.equal(logs[0].meta.callerName, 'bob');
        assert.equal(logs[0].meta.error.message, 'oops');

        assert.end();
    }
});
<|endoftext|>