<commit_before>/** Rasters are defined from registry scripts, one for each source/layer.
 * - each raster defines the projection/resolutions/extent for that source
 * - a different view is created for each projection
 * - only 1 raster can currently be displayed at one time
 * Options are set in mapDef, in querystring, or in the html form.
 * valid options are:
 * - map options:
 * -- target (creates one if not present)
 * -- widgets: currently scaleline, latlonmouse (mousePosition in latlons),
 *    projectedmouse (mousePosition in projected coords), tooltip, popup;
 *    a layerswitcher is always included
 * -- noKeyboardPan: true (by default, keyboard pan/zoom are enabled on the
 *    viewport div; use this to override)
 * - layers options:
 * -- rasters: an array listing raster ids
 *    ids are defined in the registry scripts, included with a script tag,
 *    so each raster id should have a corresponding script.
 * -- vectors: an array of objects with the following options:
 * --- url
 * --- format (should be the name of the ol class: GeoJSON, KML, GPX ...;
 *     by default, the file extension will be used)
 * --- attribution
 * --- optionally, style can be given to override defaults
 * - view options:
 * -- initial center {lat: nn, lon: nn} and zoom level
 *    By default maps with vectors zoom to the vector data
 *    and maps with no vectors zoom to the extent of the tile data
 * -- rotation
 * -- if no rasters, projCode can be set, else 4326 used
 */

/** controller module; loaded with and uses System
 * uses System global
 * uses Promise
 * imports olMap, sources/styles/widgets from registry, initialForm/mapDef (as needed)
 * sets paths config for loading proj
 * calls olMap functions to create map
 */

var utils = require('utils');
var mapDef = require('mapDef');

// literal vars
var errors = utils.errors;

System.meta.ol = {
  format: 'global',
  // deps: ['proj4'],
  exports: 'ol'
};
System.meta.proj4 = {
  format: 'global',
  exports: 'proj4'
};
System.paths.proj4 = '//cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.6/proj4.js';
System.paths['components/*'] = 'registry/components/*.js';
System.paths['projections/*'] = 'registry/projections/*.js';
System.paths['sources/*'] = 'registry/sources/*.js';
System.paths['styles/*'] = 'registry/styles/*.js';
System.paths['widgets/*'] = 'registry/widgets/*.js';
System.componentCallback = function(e) {
  var scripts = e.target.import.getElementsByTagName('script');
  for (var i = 0; i < scripts.length; i++) {
    var script = scripts[i];
    if (script.type == 'module') {
      var source = script.innerHTML.substr(1);
      System.module(source)
      .catch(function(err) { setTimeout(function() { throw err; }); });
    }
  }
};

var olMap;

// first create status div
createStatus();

// get options, fetching mapDef if necessary
mapDef.init().then(function(options) {
// and if no options, will display empty map with default 4326 view

    System.depCache = {
      'olMap': ['ol']
    };
    // now we have options, fetch modules specified
    // first import olMap which will create empty map
    System.import('olMap').then(function(olMapIn) {
      // System.import('github:jspm/plugin-css').then(function(css) {
      //   System.fetch({address: '../modular-ol3/dist/olCustom.css', metadata: {plugin: css}});
      // }, errors.noMap);
      olMap = olMapIn;

      // import raster/style modules from registry based on options
      var imports = [], modName;
      if (options.rasters) {
        // assemble array of raster modules to import
        options.rasters.forEach(function(r) {
          // raster option can be object with apikey
          if (typeof r === 'object') {
            modName = Object.keys(r)[0]; // only 1
          // ... or string
          } else {
            modName = r;
          }
          imports.push('sources/' + modName);
        });
        // fetch raster modules
        return Promise.all(
          // map import array to an array of import promises
          imports.map(System.import.bind(System))
        );
      }
    }, errors.noMap).then(function(importeds) {
      // now we have the raster modules, add layers/sources
      if (options.rasters) {
        olMap.addInitialRasters(importeds, options);
      }
      if (options.vectors) {
        // add vector layers/sources
        olMap.addInitialVectors(options);
      }
    }, errors.rasters).then(function() {
      // add controls/widgets
      // these are also in modules but don't have to be loaded in sequence
      // so can simply be imported in a for loop
      if (options.widgets) {
        for (var widget in options.widgets) {
          System.import('widgets/' + widget);//.catch(errorHandler);
        }
      }
      if (options.components) {
        for (var component in options.components) {
          utils.importComponent(component);
        }
      }

      if (!options.vectors || (options.center || options.zoom)) {
        // remove status div if not waiting for zoom to data extent
        var status = document.getElementById('status');
        if (status) {
          status.style.display = 'none';
        }
      }
      if (!options.noKeyboardPan) {
        // set focus to enable keyboard pan/zoom
        olMap.setFocus();
      }

      // if mouseposition not in options, add to toolbar
      if (!(options.widgets && options.widgets.mouseposition)) {
        addMouseButton();
      }
    }, errors.widgets);
});

function createStatus() {
  var statusDiv = document.createElement('div');
  statusDiv.id = 'status';
  statusDiv.style.textAlign = 'center';
  statusDiv.innerHTML = 'Building map, please wait ...'; // FIXME English
  document.body.appendChild(statusDiv);
}

function addMouseButton() {
  var mouseButton = document.createElement('button');
  mouseButton.id = 'mouseButton';
  mouseButton.innerHTML = 'Mouse position'; // FIXME English
  mouseButton.addEventListener('click', function() {
    System.import('widgets/mouseposition');
    this.style.display = 'none';
  });
  document.getElementById('widgets').appendChild(mouseButton);
}<commit_msg>Further map-make tidyup<commit_after>/** Rasters are defined from registry scripts, one for each source/layer.
 * - each raster defines the projection/resolutions/extent for that source
 * - a different view is created for each projection
 * - only 1 raster can currently be displayed at one time
 * Options are set in mapDef, in querystring, or in the html form.
 * valid options are:
 * - map options:
 * -- target (creates one if not present)
 * -- widgets: currently scaleline, latlonmouse (mousePosition in latlons),
 *    projectedmouse (mousePosition in projected coords), tooltip, popup;
 *    a layerswitcher is always included
 * -- noKeyboardPan: true (by default, keyboard pan/zoom are enabled on the
 *    viewport div; use this to override)
 * - layers options:
 * -- rasters: an array listing raster ids
 *    ids are defined in the registry scripts, included with a script tag,
 *    so each raster id should have a corresponding script.
 * -- vectors: an array of objects with the following options:
 * --- url
 * --- format (should be the name of the ol class: GeoJSON, KML, GPX ...;
 *     by default, the file extension will be used)
 * --- attribution
 * --- optionally, style can be given to override defaults
 * - view options:
 * -- initial center {lat: nn, lon: nn} and zoom level
 *    By default maps with vectors zoom to the vector data
 *    and maps with no vectors zoom to the extent of the tile data
 * -- rotation
 * -- if no rasters, projCode can be set, else 4326 used
 */

/** controller module; loaded with and uses System
 * uses System global
 * uses Promise
 * imports olMap, sources/styles/widgets from registry, initialForm/mapDef (as needed)
 * sets paths config for loading proj
 * calls olMap functions to create map
 */

var utils = require('utils');
var mapDef = require('mapDef');

// literal vars
var errors = utils.errors;

System.meta.ol = {
  format: 'global',
  // deps: ['proj4'],
  exports: 'ol'
};
System.meta.proj4 = {
  format: 'global',
  exports: 'proj4'
};
System.paths.proj4 = '//cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.6/proj4.js';
System.paths['components/*'] = 'registry/components/*.js';
System.paths['projections/*'] = 'registry/projections/*.js';
System.paths['sources/*'] = 'registry/sources/*.js';
System.paths['styles/*'] = 'registry/styles/*.js';
System.paths['widgets/*'] = 'registry/widgets/*.js';
System.componentCallback = function(e) {
  var scripts = e.target.import.getElementsByTagName('script');
  for (var i = 0; i < scripts.length; i++) {
    var script = scripts[i];
    if (script.type == 'module') {
      var source = script.innerHTML.substr(1);
      System.module(source)
      .catch(function(err) { setTimeout(function() { throw err; }); });
    }
  }
};

var olMap;

// first create status div
createStatus();

// get options, fetching mapDef if necessary
mapDef.init().then(function(options) {
// and if no options, will display empty map with default 4326 view

    System.depCache = {
      'olMap': ['ol']
    };
    // now we have options, fetch modules specified
    // first import olMap which will create empty map
    System.import('olMap').then(function(olMapIn) {
      olMap = olMapIn;

      // import raster/style modules from registry based on options
      var imports = [], modName;
      if (options.rasters) {
        // assemble array of raster modules to import
        options.rasters.forEach(function(r) {
          // raster option can be object with apikey
          if (typeof r === 'object') {
            modName = Object.keys(r)[0]; // only 1
          // ... or string
          } else {
            modName = r;
          }
          imports.push('sources/' + modName);
        });
        // fetch raster modules
        return Promise.all(
          // map import array to an array of import promises
          imports.map(System.import.bind(System))
        );
      }
    }, errors.noMap).then(function(importeds) {
      // now we have the raster modules, add layers/sources
      if (options.rasters) {
        olMap.addInitialRasters(importeds, options);
      }
      if (options.vectors) {
        // add vector layers/sources
        olMap.addInitialVectors(options);
      }
    }, errors.rasters).then(function() {
      // add controls/widgets
      // these are also in modules but don't have to be loaded in sequence
      // so can simply be imported in a for loop
      if (options.widgets) {
        for (var widget in options.widgets) {
          System.import('widgets/' + widget);
        }
      }
      // ditto components
      if (options.components) {
        for (var component in options.components) {
          utils.importComponent(component);
        }
      }

      if (!options.vectors || (options.center || options.zoom)) {
        // remove status div if not waiting for zoom to data extent
        var status = document.getElementById('status');
        if (status) {
          status.style.display = 'none';
        }
      }
      if (!options.noKeyboardPan) {
        // set focus to enable keyboard pan/zoom
        olMap.setFocus();
      }

      // if mouseposition not in options, add to toolbar
      if (!(options.widgets && options.widgets.mouseposition)) {
        addMouseButton();
      }
    }, errors.widgets);
});

function createStatus() {
  var statusDiv = document.createElement('div');
  statusDiv.id = 'status';
  statusDiv.style.textAlign = 'center';
  statusDiv.innerHTML = 'Building map, please wait ...'; // FIXME English
  document.body.appendChild(statusDiv);
}

function addMouseButton() {
  var mouseButton = document.createElement('button');
  mouseButton.id = 'mouseButton';
  mouseButton.innerHTML = 'Mouse position'; // FIXME English
  mouseButton.addEventListener('click', function() {
    System.import('widgets/mouseposition');
    this.style.display = 'none';
  });
  document.getElementById('widgets').appendChild(mouseButton);
}<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var Mediator = require('mediator-js').Mediator;
var Promise = require('bluebird');

/**
 * A version of {@link once} that returns a Promise
 * @param  {String} channel   Channel identifier to wait on a single message
 * @return {Promise}          A promise that is fulfilled with the next published
 *                             message in the channel
 */
Mediator.prototype.promise = function(channel, options, context) {
  var self = this;
  return new Promise(function(resolve) {
    self.once(channel, resolve, options, context);
  });
};

/**
 * Publishes a message on a topic and wait for a response or error
 *
 * By convention 'return' topics are prefixed with 'done:' and 'error:' to signal
 * the result of the operation, and suffixed with a unique id to map clients in
 * order to supply the results to the correct client.
 *
 * @param  {String} topic      Channel identifier to publish the initial message
 *
 * @param  {Any} parameters    The data to publish to the topic. The unique id used to publish
 *                             the 'return' topic is extracted from this parameter according to
 *                             the following rules:
 *                             - `parameters.id` property, If parameters has this property
 *                             - `parameters[0]` if parameters is an Array
 *                             - `parameters.toString()` otherwise
 *
 * @param  {Object} options    Options object
 * @param  {String} options.uid  Overrides the unique id from the {@link parameters}
 * @param  {String} options.doneTopic  Base topic to subscribe for the result of the request,
 *                               gets prefixed with 'done:'
 * @param  {String} options.errorTopic  Base topic to subscribe for errors on the request,
 *                               gets prefixed with 'error:'
 *
 * @return {Promise}           A Promise that gets fulfilled with the result of the request
 *                               or rejected with the error from the above topics
 */
Mediator.prototype.request = function(topic, parameters, options) {
  var self = this;
  options = options || {};
  var topics = {
    request: topic,
    done: options.doneTopic || 'done:' + topic,
    error: options.errorTopic || 'error:' + topic
  };
  var subs = {};

  var uid = null;
  if (_.has(options, 'uid')) {
    uid = options.uid;
  } else if (typeof parameters !== "undefined" && parameters !== null) {
    if (_.has(parameters, 'id')) {
      uid = parameters.id;
    } else {
      uid = parameters instanceof Array ? parameters[0] : parameters.toString();
    }
  }

  if (uid !== null) {
    topics.done += ':' + uid;
    topics.error += ':' + uid;
  } else {
    console.error('Warning: no status topics defined for ' + topic);
  }

  if (!options.timeout) {
    options.timeout = 60000;
  }

  function unsubscribe() {
    self.remove(topics.done, subs.done.id);
    self.remove(topics.error, subs.error.id);
  }

  var args = [topics.request];
  if (parameters instanceof Array) {
    Array.prototype.push.apply(args, parameters);
  } else {
    args.push(parameters);
  }

  // must setup subscriptions before publish
  var resultPromise = new Promise(function(resolve, reject) {
    subs.done = self.once(topics.done, resolve);
    subs.error = self.once(topics.error, reject);
  });

  self.publish.apply(mediator, args);

  return resultPromise
  .timeout(options.timeout, new Error('Mediator request timeout for topic ' +  topic))
  .finally(function() {
    unsubscribe();
  });
};

var mediator = new Mediator();
mediator.Mediator = Mediator;
module.exports = mediator;
<commit_msg>Expose mediator topics utility to top level<commit_after>'use strict';

var _ = require('lodash');
var Mediator = require('mediator-js').Mediator;
var Promise = require('bluebird');

/**
 * A version of {@link once} that returns a Promise
 * @param  {String} channel   Channel identifier to wait on a single message
 * @return {Promise}          A promise that is fulfilled with the next published
 *                             message in the channel
 */
Mediator.prototype.promise = function(channel, options, context) {
  var self = this;
  return new Promise(function(resolve) {
    self.once(channel, resolve, options, context);
  });
};

/**
 * Publishes a message on a topic and wait for a response or error
 *
 * By convention 'return' topics are prefixed with 'done:' and 'error:' to signal
 * the result of the operation, and suffixed with a unique id to map clients in
 * order to supply the results to the correct client.
 *
 * @param  {String} topic      Channel identifier to publish the initial message
 *
 * @param  {Any} parameters    The data to publish to the topic. The unique id used to publish
 *                             the 'return' topic is extracted from this parameter according to
 *                             the following rules:
 *                             - `parameters.id` property, If parameters has this property
 *                             - `parameters[0]` if parameters is an Array
 *                             - `parameters.toString()` otherwise
 *
 * @param  {Object} options    Options object
 * @param  {String} options.uid  Overrides the unique id from the {@link parameters}
 * @param  {String} options.doneTopic  Base topic to subscribe for the result of the request,
 *                               gets prefixed with 'done:'
 * @param  {String} options.errorTopic  Base topic to subscribe for errors on the request,
 *                               gets prefixed with 'error:'
 *
 * @return {Promise}           A Promise that gets fulfilled with the result of the request
 *                               or rejected with the error from the above topics
 */
Mediator.prototype.request = function(topic, parameters, options) {
  var self = this;
  options = options || {};
  var topics = {
    request: topic,
    done: options.doneTopic || 'done:' + topic,
    error: options.errorTopic || 'error:' + topic
  };
  var subs = {};

  var uid = null;
  if (_.has(options, 'uid')) {
    uid = options.uid;
  } else if (typeof parameters !== "undefined" && parameters !== null) {
    if (_.has(parameters, 'id')) {
      uid = parameters.id;
    } else {
      uid = parameters instanceof Array ? parameters[0] : parameters.toString();
    }
  }

  if (uid !== null) {
    topics.done += ':' + uid;
    topics.error += ':' + uid;
  } else {
    console.error('Warning: no status topics defined for ' + topic);
  }

  if (!options.timeout) {
    options.timeout = 60000;
  }

  function unsubscribe() {
    self.remove(topics.done, subs.done.id);
    self.remove(topics.error, subs.error.id);
  }

  var args = [topics.request];
  if (parameters instanceof Array) {
    Array.prototype.push.apply(args, parameters);
  } else {
    args.push(parameters);
  }

  // must setup subscriptions before publish
  var resultPromise = new Promise(function(resolve, reject) {
    subs.done = self.once(topics.done, resolve);
    subs.error = self.once(topics.error, reject);
  });

  self.publish.apply(mediator, args);

  return resultPromise
  .timeout(options.timeout, new Error('Mediator request timeout for topic ' +  topic))
  .finally(function() {
    unsubscribe();
  });
};

var mediator = new Mediator();
mediator.Mediator = Mediator;
mediator.Topics  = require("./topics");
module.exports = mediator;
<|endoftext|>


<commit_before>var lodash = require('lodash');

var errors = {
    E000: 'not a valid error code',
    E001: 'the `<%= attribute %>` attribute is banned',
    E002: 'attribute names must match the format: <%= format %>',
    E003: 'duplicate attribute: <%= attribute %>',
    E004: 'attribute values must not include unsafe characters',
    E005: 'the `<%= attribute %>` attribute is not <%= format %>',
    E006: 'attribute values cannot be empty',
    E007: '<!DOCTYPE> should be the first element seen',
    E008: 'the doctype must conform to the HTML5 standard',
    E009: 'use only <%= format %> links',
    E010: 'ids and classes may not use the word: <%= word %>',
    E011: '<%= attribute %> value must match the format: <%= format %>',
    E012: 'the id "<%= id %>" is already in use',
    E013: 'the `alt` property must be set for image tags',
    E014: 'a source must be given for each `img` tag',
    E015: 'line ending does not match format: <%= format %>',
    E016: 'the <%= tag %> tag is banned',
    E017: 'tag names must be lowercase',
    E018: 'void element should <%= expect %> close itself',
    E019: 'all labels should have a `for` attribute',
    E020: 'label does not have a `for` attribute or a labeable child',
    E021: 'an element with the id "<%= id %>" does not exist (should match `for` attribute)',
    E022: 'the linked element is not labeable (id: <%= id %>)',
    E023: '<%= part %> contains <%= desc %>: <%= chars %>',
    E024: '<%= type %> not allowed',
    E025: 'html element should specify the language of the page',
    E026: '<%= op %> (all focusable elements on a page must either have a positive tabindex or none at all)',
    E027: 'the <head> tag must contain a title',
    E028: 'the <head> tag can only contain one title; <%= num %> given',
    E029: 'title "<%= title %>" exceeds maximum length of <%= maxlength %>',
    E030: 'tag start and end must match',
    E031: 'table must have a caption for accessibility',
    E032: 'figure must have a figcaption, figcaption must be in a figure (for accessibility)',
    E033: 'input with id: <%= idValue %> (or if type is text, name: <%= nameValue %>) is not associated with a label for accessibility',
    E034: 'radio input must have an associated name',
    E035: 'table must have a header for accessibility',
    E036: 'indenting spaces must be used in groups of <%= width %>',
    E037: 'attributes for one tag on the one line should be limited to <%= limit %>',
    E038: 'lang attribute <%= lang %> is not valid',
    E039: 'lang attribute <%= lang %> in not properly capitalized',
    E040: 'line length should not exceed <%= maxlength %> characters (current: <%= length %>)',
    E041: 'duplicate class: <%= classes %>',
    E042: 'tag is not closed',
    E043: 'attribute <%= attribute %> should come before <%= previous %>',
    E044: 'only <head> and <body> may be children of <html>',
    E045: 'tags in <html> may not be duplicated',
    E046: '<head> tag must come before <body> in <html>',
    E047: 'the only tags allowed in the <head> are base, link, meta, noscript, script, style, template, and title',
    E048: 'invalid value for option <%= option %>: <%= value %>',
    E049: 'tag attributes are malformed',
    E050: 'invalid configuration',
    E051: 'invalid option or preset name: <%= name %>',
    E052: 'not a preset: <%= preset %>',
    E053: 'invalid value for option <%= rule %>: <%= value %>',
    E054: 'option <%= name %> does not exist',
    E055: 'line contains trailing whitespace',
    E056: 'expected from <%= expectedMin %> to <%= expectedMax %> levels of indentation. <%= value %> levels instead',
    E057: 'tag has missing or empty attributes',
    E058: 'rel="noopener" required for links with target="blank"',
    E059: 'WCAG rule 38: links should have at least 4 chars <%= content %> is of length <%= length %>',
    E060: 'WCAG rule 77: Input elements where type=[button|submit|reset] must have a value or title attribute.',
    E061: 'WCAG rule 78: Each button element must contain content.',
    E062: 'WCAG rule 74: The label element should not encapsulate select and textarea elements.',
    E063: 'WCAG rule 73: Each fieldset element should contain a legend element.'
};

module.exports.errors = {};

lodash.forOwn(errors, function (format, code) {
    module.exports.errors[code] = {
        format: format,
        code: code
    };
});

module.exports.renderMsg = function (code, data) {
    var format = errors[code];

    return lodash.template(format)(data);
};

module.exports.renderIssue = function (issue) {
    return this.renderMsg(issue.code, issue.data);
};
<commit_msg>Fix typo<commit_after>var lodash = require('lodash');

var errors = {
    E000: 'not a valid error code',
    E001: 'the `<%= attribute %>` attribute is banned',
    E002: 'attribute names must match the format: <%= format %>',
    E003: 'duplicate attribute: <%= attribute %>',
    E004: 'attribute values must not include unsafe characters',
    E005: 'the `<%= attribute %>` attribute is not <%= format %>',
    E006: 'attribute values cannot be empty',
    E007: '<!DOCTYPE> should be the first element seen',
    E008: 'the doctype must conform to the HTML5 standard',
    E009: 'use only <%= format %> links',
    E010: 'ids and classes may not use the word: <%= word %>',
    E011: '<%= attribute %> value must match the format: <%= format %>',
    E012: 'the id "<%= id %>" is already in use',
    E013: 'the `alt` property must be set for image tags',
    E014: 'a source must be given for each `img` tag',
    E015: 'line ending does not match format: <%= format %>',
    E016: 'the <%= tag %> tag is banned',
    E017: 'tag names must be lowercase',
    E018: 'void element should <%= expect %> close itself',
    E019: 'all labels should have a `for` attribute',
    E020: 'label does not have a `for` attribute or a labeable child',
    E021: 'an element with the id "<%= id %>" does not exist (should match `for` attribute)',
    E022: 'the linked element is not labeable (id: <%= id %>)',
    E023: '<%= part %> contains <%= desc %>: <%= chars %>',
    E024: '<%= type %> not allowed',
    E025: 'html element should specify the language of the page',
    E026: '<%= op %> (all focusable elements on a page must either have a positive tabindex or none at all)',
    E027: 'the <head> tag must contain a title',
    E028: 'the <head> tag can only contain one title; <%= num %> given',
    E029: 'title "<%= title %>" exceeds maximum length of <%= maxlength %>',
    E030: 'tag start and end must match',
    E031: 'table must have a caption for accessibility',
    E032: 'figure must have a figcaption, figcaption must be in a figure (for accessibility)',
    E033: 'input with id: <%= idValue %> (or if type is text, name: <%= nameValue %>) is not associated with a label for accessibility',
    E034: 'radio input must have an associated name',
    E035: 'table must have a header for accessibility',
    E036: 'indenting spaces must be used in groups of <%= width %>',
    E037: 'attributes for one tag on the one line should be limited to <%= limit %>',
    E038: 'lang attribute <%= lang %> is not valid',
    E039: 'lang attribute <%= lang %> in not properly capitalized',
    E040: 'line length should not exceed <%= maxlength %> characters (current: <%= length %>)',
    E041: 'duplicate class: <%= classes %>',
    E042: 'tag is not closed',
    E043: 'attribute <%= attribute %> should come before <%= previous %>',
    E044: 'only <head> and <body> may be children of <html>',
    E045: 'tags in <html> may not be duplicated',
    E046: '<head> tag must come before <body> in <html>',
    E047: 'the only tags allowed in the <head> are base, link, meta, noscript, script, style, template, and title',
    E048: 'invalid value for option <%= option %>: <%= value %>',
    E049: 'tag attributes are malformed',
    E050: 'invalid configuration',
    E051: 'invalid option or preset name: <%= name %>',
    E052: 'not a preset: <%= preset %>',
    E053: 'invalid value for option <%= rule %>: <%= value %>',
    E054: 'option <%= name %> does not exist',
    E055: 'line contains trailing whitespace',
    E056: 'expected from <%= expectedMin %> to <%= expectedMax %> levels of indentation. <%= value %> levels instead',
    E057: 'tag has missing or empty attributes',
    E058: 'rel="noopener" required for links with target="_blank"',
    E059: 'WCAG rule 38: links should have at least 4 chars <%= content %> is of length <%= length %>',
    E060: 'WCAG rule 77: Input elements where type=[button|submit|reset] must have a value or title attribute.',
    E061: 'WCAG rule 78: Each button element must contain content.',
    E062: 'WCAG rule 74: The label element should not encapsulate select and textarea elements.',
    E063: 'WCAG rule 73: Each fieldset element should contain a legend element.'
};

module.exports.errors = {};

lodash.forOwn(errors, function (format, code) {
    module.exports.errors[code] = {
        format: format,
        code: code
    };
});

module.exports.renderMsg = function (code, data) {
    var format = errors[code];

    return lodash.template(format)(data);
};

module.exports.renderIssue = function (issue) {
    return this.renderMsg(issue.code, issue.data);
};
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;

var searchPaths = [
  path.resolve(__dirname, '..', 'build', 'Release'),
  path.resolve(__dirname, '..', 'build', 'Debug'),
  path.resolve(__dirname, '..', 'bin'),
];
function searchCommand(command) {
  if (process.platform == 'win32') {
    command += '.exe'
    var binaryPath = path.join(__dirname, '..', 'deps', 'breakpad', command)
    if (fs.existsSync(binaryPath))
      return b
  } else {
    for (var i in searchPaths) {
      var binaryPath = path.join(searchPaths[i], command);
      if (fs.existsSync(binaryPath))
        return binaryPath;
    }
  }
}

function execute(command, args, callback) {
  var stderr = '';
  var stdout = '';
  var child = spawn(command, args);
  child.stdout.on('data', function(chunk) { stdout += chunk; });
  child.stderr.on('data', function(chunk) { stderr += chunk; });
  child.on('close', function(code) {
    if (code != 0)
      callback(stderr);
    else
      callback(null, stdout);
  });
}

var globalSymbolPaths = [];
module.exports.addSymbolPath = Array.prototype.push.bind(globalSymbolPaths);

module.exports.walkStack = function(minidump, symbolPaths, callback) {
  if (!callback) {
    callback = symbolPaths;
    symbolPaths = [];
  }

  var stackwalk = searchCommand('minidump_stackwalk');
  if (!stackwalk) {
    callback('Unable to find the "minidump_stackwalk"');
    return;
  }

  args = [minidump].concat(symbolPaths, globalSymbolPaths)
  execute(stackwalk, args, callback);
}

module.exports.dumpSymbol = function(binary, symbolPath, callback) {
  var dumpsyms = searchCommand('dump_syms');
  if (!dumpsyms) {
    callback('Unable to find the "dump_syms"');
    return;
  }

  execute(dumpsyms, ['-r', '-c', binary], function(error, output) {
    if (error) return callback(error);

    fs.writeFile(symbolPath, output, callback);
  });
}
<commit_msg>Return the file content to the caller.<commit_after>var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;

var searchPaths = [
  path.resolve(__dirname, '..', 'build', 'Release'),
  path.resolve(__dirname, '..', 'build', 'Debug'),
  path.resolve(__dirname, '..', 'bin'),
];
function searchCommand(command) {
  if (process.platform == 'win32') {
    command += '.exe'
    var binaryPath = path.join(__dirname, '..', 'deps', 'breakpad', command)
    if (fs.existsSync(binaryPath))
      return b
  } else {
    for (var i in searchPaths) {
      var binaryPath = path.join(searchPaths[i], command);
      if (fs.existsSync(binaryPath))
        return binaryPath;
    }
  }
}

function execute(command, args, callback) {
  var stderr = '';
  var stdout = '';
  var child = spawn(command, args);
  child.stdout.on('data', function(chunk) { stdout += chunk; });
  child.stderr.on('data', function(chunk) { stderr += chunk; });
  child.on('close', function(code) {
    if (code != 0)
      callback(stderr);
    else
      callback(null, stdout);
  });
}

var globalSymbolPaths = [];
module.exports.addSymbolPath = Array.prototype.push.bind(globalSymbolPaths);

module.exports.walkStack = function(minidump, symbolPaths, callback) {
  if (!callback) {
    callback = symbolPaths;
    symbolPaths = [];
  }

  var stackwalk = searchCommand('minidump_stackwalk');
  if (!stackwalk) {
    callback('Unable to find the "minidump_stackwalk"');
    return;
  }

  args = [minidump].concat(symbolPaths, globalSymbolPaths)
  execute(stackwalk, args, callback);
}

module.exports.dumpSymbol = function(binary, callback) {
  var dumpsyms = searchCommand('dump_syms');
  if (!dumpsyms) {
    callback('Unable to find the "dump_syms"');
    return;
  }

  execute(dumpsyms, ['-r', '-c', binary], callback)
}
<|endoftext|>


<commit_before>var spawn = require('cross-spawn'),
  path = require('path'),
  fs = require('fs'),
  async = require('async'),
  MobProxy = require('browsermob-proxy-api');

function Proxy(config) {
  this.port = config.port;
  if (config.basicAuth) {
    this.basicAuth = config.basicAuth;
  }
  if (config.headers) {
    this.headers = config.headers;
  }
  if (config.limit) {
    this.limit = config.limit;
  }

}

Proxy.prototype.start = function(cb) {
  // start the browsermob proxy
  this.java = spawn('java', ['-jar', path.join(__dirname, 'bmpwrapper-0.0.1-SNAPSHOT-full.jar'), '-port', '9093']);

  this.java.stdout.on('data', function(data) {
    console.log(data);
  });
  this.java.stderr.on('data', function(data) {
    console.log('e:' + data);
  });

  setTimeout(function() {

    this.proxy = new MobProxy({
      'host': 'localhost',
      'port': '9093'
    });

    var self = this;
    this.proxy.startPort(this.port, function(err, data) {
      async.series([

          function(callback) {
            self.proxy.createHAR(self.port, {
              'initialPageRef': 'foo',
              'captureHeaders': true
            }, callback);
          }
          /*,
          function(callback) {
            if (self.headers) {
              self.proxy.setHeaders(self.port, JSON.stringify(self.headers), callback);
            } else {
              callback();
            }
          },
          function(callback) {
            if (self.basicAuth) {
              self.proxy.setAuthentication(443, domain, json, callback);
            }
          },
          function(callback) {
            if (self.limit) {
              self.proxy.setLimit(self.port, self.limit, callback);
            }
          }*/
        ],
        // optional callback
        function(err, results) {
          cb();
        });
    });
  }, 2000);
};

Proxy.prototype.newPage = function(name) {
  this.proxy.startNewPage(this.port, name, function() {});
};

Proxy.prototype.stop = function(cb) {
  // close the proxy
  var self = this;
  this.proxy.stopPort(this.port, function() {

    try {
      self.java.kill('SIGHUP');
    } catch (error) {
      console.log(error);
    }
    cb();
  });

};

Proxy.prototype.saveHar = function(filename, cb) {
  this.proxy.getHAR(this.port, function(err, har) {
    // TODO for each page insert the timings

    fs.writeFile(filename, har, function(err) {
      if (err) {
        throw err;
      }
      cb();
    });
  });
};

module.exports = Proxy;<commit_msg>fixed references<commit_after>var spawn = require('cross-spawn'),
  path = require('path'),
  fs = require('fs'),
  async = require('async'),
  MobProxy = require('browsermob-proxy-api');

function Proxy(config) {
  this.port = config.port;
  if (config.basicAuth) {
    this.basicAuth = config.basicAuth;
  }
  if (config.headers) {
    this.headers = config.headers;
  }
  if (config.limit) {
    this.limit = config.limit;
  }

}

Proxy.prototype.start = function(cb) {
  // start the browsermob proxy
  this.java = spawn('java', ['-jar', path.join(__dirname, 'bmpwrapper-0.0.1-SNAPSHOT-full.jar'), '-port', '9093']);

  this.java.stdout.on('data', function(data) {
    console.log(data);
  });
  this.java.stderr.on('data', function(data) {
    console.log('e:' + data);
  });

  var self = this;

  setTimeout(function() {

    self.proxy = new MobProxy({
      'host': 'localhost',
      'port': '9093'
    });

    self.proxy.startPort(this.port, function(err, data) {
      async.series([

          function(callback) {
            self.proxy.createHAR(self.port, {
              'initialPageRef': 'foo',
              'captureHeaders': true
            }, callback);
          }
          /*,
          function(callback) {
            if (self.headers) {
              self.proxy.setHeaders(self.port, JSON.stringify(self.headers), callback);
            } else {
              callback();
            }
          },
          function(callback) {
            if (self.basicAuth) {
              self.proxy.setAuthentication(443, domain, json, callback);
            }
          },
          function(callback) {
            if (self.limit) {
              self.proxy.setLimit(self.port, self.limit, callback);
            }
          }*/
        ],
        // optional callback
        function(err, results) {
          cb();
        });
    });
  }, 2000);
};

Proxy.prototype.newPage = function(name) {
  this.proxy.startNewPage(this.port, name, function() {});
};

Proxy.prototype.stop = function(cb) {
  // close the proxy
  var self = this;
  this.proxy.stopPort(this.port, function() {

    try {
      self.java.kill('SIGHUP');
    } catch (error) {
      console.log(error);
    }
    cb();
  });

};

Proxy.prototype.saveHar = function(filename, cb) {
  this.proxy.getHAR(this.port, function(err, har) {
    // TODO for each page insert the timings

    fs.writeFile(filename, har, function(err) {
      if (err) {
        throw err;
      }
      cb();
    });
  });
};

module.exports = Proxy;<|endoftext|>


<commit_before>/* global QUnit, expect, bootlint */
/* jshint browser: true */
/*eslint-env browser */

(function () {
    'use strict';

    function lintCurrentDoc() {
        var lints = [];
        var reporter = function (lint) {
            lints.push(lint.message);
        };
        bootlint.lintCurrentDocument(reporter, []);
        return lints;
    }

    QUnit.test(window.location.pathname, function (assert) {
        expect(1);
        var lints = Array.prototype.slice.call(document.querySelectorAll('#bootlint>li'));
        var expectedLintMsgs = lints.map(function (item) {
            return item.dataset.lint;
        });
        var actualLintMsgs = lintCurrentDoc();
        assert.deepEqual(actualLintMsgs, expectedLintMsgs);
    });
})();
<commit_msg>Remove QUnit UI's checkboxes from the DOM during tests to prevent false positives<commit_after>/* global QUnit, expect, bootlint */
/* jshint browser: true */
/*eslint-env browser */

(function () {
    'use strict';

    function lintCurrentDoc() {
        var lints = [];
        var reporter = function (lint) {
            lints.push(lint.message);
        };
        bootlint.lintCurrentDocument(reporter, []);
        return lints;
    }

    QUnit.test(window.location.pathname, function (assert) {
        // Remove checkboxes QUnit dynamically adds to the DOM as part of its UI
        // because these checkboxes may not comply with some Bootlint checks.
        $('#qunit-filter-pass, #qunit-urlconfig-noglobals, #qunit-urlconfig-notrycatch').remove();

        expect(1);
        var lints = Array.prototype.slice.call(document.querySelectorAll('#bootlint>li'));
        var expectedLintMsgs = lints.map(function (item) {
            return item.dataset.lint;
        });
        var actualLintMsgs = lintCurrentDoc();
        assert.deepEqual(actualLintMsgs, expectedLintMsgs);
    });
})();
<|endoftext|>


<commit_before>var events = require('events');
var util = require('util');

exports.ControlRegister = require('./control_registers');
exports.FIFO = require('./fifo');

exports.MRF24J40 = function(platformOrHW) {
  this.txBuf = new Buffer(146);
  this.rxBuf = new Buffer(146);
  this.panID = 0x0000;
  this.shortAddress = 0x0000;
  this.sequenceNumber = 0;

  if (typeof platformOrHW === 'string') {
    var Hardware = require('./platforms/' + platformOrHW + '/hardware').Hardware;
    this.hw = new Hardware();
  } else {
    this.hw = platformOrHW;
  }

  this.hw.on('interrupt', handleInterrupt.bind(this));
}

util.inherits(exports.MRF24J40, events.EventEmitter);

exports.MRF24J40.prototype.readControlRegister = function(controlRegister) {
  var data = this._transferControlRegister(controlRegister, 0, false);
  return data.readUInt8(data.length - 1);
}

exports.MRF24J40.prototype.writeControlRegister = function(controlRegister, value) {
  this._transferControlRegister(controlRegister, value, true);
}

exports.MRF24J40.prototype._transferControlRegister = function(controlRegister, value, write) {
  var idx = encodeAddress(this.txBuf, controlRegister.address, controlRegister.long, write);
  this.txBuf.writeUInt8(value, idx++);
  this.rxBuf.fill(0, 0, idx);

  return this.hw.transfer(this.txBuf.slice(0, idx), this.rxBuf.slice(0, idx));
}

exports.MRF24J40.prototype.readFIFO = function(fifo, offset, count) {
  var idx = count + 2;
  this.txBuf.fill(0, 2, idx);
  var data = this._transferFIFO(fifo, false, offset, idx);
  return data.slice(2, idx);
}

exports.MRF24J40.prototype.writeFIFO = function(fifo, offset, data) {
  var idx = data.length + 2;
  data.copy(this.txBuf, 2);

  this._transferFIFO(fifo, true, offset, idx);
}

exports.MRF24J40.prototype._transferFIFO = function(fifo, write, offset, idx) {
  encodeAddress(this.txBuf, fifo.address + offset, true, write);

  this.rxBuf.fill(0, 0, idx);
  return this.hw.transfer(this.txBuf.slice(0, idx), this.rxBuf.slice(0, idx));
}

exports.MRF24J40.prototype.reset = function() {
  this.hw.writeReset(0);
  this.hw.writeReset(1);
}

exports.MRF24J40.prototype.initialize = function(channel) {
  if (!channel || channel < 11 || channel > 26) {
    channel = 11; //TODO: maybe throw an exception for out-of-range values?
  }

  channel = ((channel - 11) << 4) & 0xF0;

  // Initialization according datasheet example for non-beacon networks
  this.writeControlRegister(exports.ControlRegister['SOFTRST'], 0x07);
  this.writeControlRegister(exports.ControlRegister['PACON2'], 0x98);
  this.writeControlRegister(exports.ControlRegister['TXSTBL'], 0x95);
  this.writeControlRegister(exports.ControlRegister['RFCON0'], (channel | 0x03));
  this.writeControlRegister(exports.ControlRegister['RFCON1'], 0x01);
  this.writeControlRegister(exports.ControlRegister['RFCON2'], 0x80);
  this.writeControlRegister(exports.ControlRegister['RFCON3'], 0x00);
  this.writeControlRegister(exports.ControlRegister['RFCON6'], 0x90);
  this.writeControlRegister(exports.ControlRegister['RFCON7'], 0x80);
  this.writeControlRegister(exports.ControlRegister['RFCON8'], 0x10);
  this.writeControlRegister(exports.ControlRegister['SLPCON1'], 0x21);

  // Non-beacon
  this.writeControlRegister(exports.ControlRegister['BBREG2'], 0x80);
  this.writeControlRegister(exports.ControlRegister['CCAEDTH'], 0x60);
  this.writeControlRegister(exports.ControlRegister['BBREG6'], 0x40);

  // Enabled: SECIE, RXIE, TXNIE
  this.writeControlRegister(exports.ControlRegister['INTCON'], 0xE6);

  // Flush RX FIFO
  this.writeControlRegister(exports.ControlRegister['RXFLUSH'], 0x01);

  // Reset RF state machine
  this.writeControlRegister(exports.ControlRegister['RFCTL'], 0x04);
  this.writeControlRegister(exports.ControlRegister['RFCTL'], 0x00);
}

exports.MRF24J40.prototype.setPANCoordinator = function() {
  var rxmcr = this.readControlRegister(exports.ControlRegister['RXMCR']);
  this.writeControlRegister(exports.ControlRegister['RXMCR'], (rxmcr | 0x08));
}

exports.MRF24J40.prototype.clearPANCoordinator = function() {
  var rxmcr = this.readControlRegister(exports.ControlRegister['RXMCR']);
  this.writeControlRegister(exports.ControlRegister['RXMCR'], (rxmcr & 0xF7));
}

exports.MRF24J40.prototype.setPANID = function(panID) {
  this.writeControlRegister(exports.ControlRegister['PANIDH'], (panID & 0xFF));
  this.writeControlRegister(exports.ControlRegister['PANIDL'], (panID >> 8));
  this.panID = panID;
}

exports.MRF24J40.prototype.setShortAddress = function(shortAddress) {
  this.writeControlRegister(exports.ControlRegister['SADRH'], (shortAddress & 0xFF));
  this.writeControlRegister(exports.ControlRegister['SADRL'], (shortAddress >> 8));
  this.shortAddress = shortAddress;
}

exports.MRF24J40.prototype.transmitRaw = function(frame, ack, secured) {
  this.writeFIFO(exports.FIFO['TX'], 0, frame);

  var txncon = 0x01;

  if (secured) {
    txncon = txncon | 0x02;
  }

  if (ack) {
    txncon = txncon | 0x04;
  }

  this.writeControlRegister(exports.ControlRegister['TXNCON'], txncon);
}

exports.MRF24J40.prototype.transmitDataPacket = function(destinationAddress, packet, secured) {
  //TODO: check that packet is not too big and handle security

  var idx = 0;
  this.rxBuf.writeUInt8(9, idx++); // Header length
  this.rxBuf.writeUInt8(packet.length + 9, idx++); // Total frame length
  this.rxBuf.writeUInt8(0x61, idx++); // Data packet + ack requested + srcPan == dstPan
  this.rxBuf.writeUInt8(0x88, idx++); // short addressing mode
  this.rxBuf.writeUInt8(this.sequenceNumber++, idx++);
  this.sequenceNumber = this.sequenceNumber & 0xff;
  this.rxBuf.writeUInt16BE(this.panID, idx);
  idx += 2;
  this.rxBuf.writeUInt16BE(destinationAddress, idx);
  idx += 2;
  this.rxBuf.writeUInt16BE(this.shortAddress, idx);
  idx += 2;
  packet.copy(this.rxBuf, idx);
  idx += packet.length;
  this.transmitRaw(this.rxBuf.slice(0, idx), true, secured);
}

function handleInterrupt() {
  var interruptValue = this.readControlRegister(exports.ControlRegister['INTSTAT']);
  if ((interruptValue & 0x80) == 0x80) {
    handleSleepInterrupt.call(this);
  }

  if ((interruptValue & 0x40) == 0x40) {
    handleWakeInterrupt.call(this);
  }

  if ((interruptValue & 0x20) == 0x20) {
    handleHalfSymbolTimerInterrupt.call(this);
  }

  if ((interruptValue & 0x10) == 0x10) {
    handleSecurityKeyRequestInterrupt.call(this);
  }

  if ((interruptValue & 0x08) == 0x08) {
    handleReceiveInterrupt.call(this);
  }

  if ((interruptValue & 0x04) == 0x04) {
    handleTransmitInterrupt.call(this, exports.FIFO['TXGTS2']);
  }

  if ((interruptValue & 0x02) == 0x02) {
    handleTransmitInterrupt.call(this, exports.FIFO['TXGTS1']);
  }

  if ((interruptValue & 0x01) == 0x01) {
    handleTransmitInterrupt.call(this, exports.FIFO['TX']);
  }
}

function handleSleepInterrupt() {

}

function handleWakeInterrupt() {

}

function handleHalfSymbolTimerInterrupt() {

}

function handleSecurityKeyRequestInterrupt() {

}

function handleReceiveInterrupt() {
  this.writeControlRegister(exports.ControlRegister['BBREG1'], 0x04);
  //TODO: measure if it is faster to just dump the entire RX FIFO, or read the length and then only the needed bytes.
  var data = this.readFIFO(exports.FIFO['RX'], 0, exports.FIFO['RX'].size);
  this.writeControlRegister(exports.ControlRegister['RXFLUSH'], 0x01);
  this.writeControlRegister(exports.ControlRegister['BBREG1'], 0x00);
  var end = data.readUInt8(0) - 1; // remove FCS (-2), take in account +1 offset

  var frameControl = data.readUInt16LE(1);

  //only process data frames now
  if ((frameControl & 0x07) == 0x01) {
    //TODO: remove assumption that short addressing + no security is being used.
    var srcAddr = data.readUInt16BE(8);
    var lqi = data.readUInt8(end + 2);
    var rssi = data.readUInt8(end + 3);
    this.emit('data', srcAddr, data.slice(10, end), lqi, rssi);
  }
}

function handleTransmitInterrupt(fifo) {
  var txStatus = this.readControlRegister(exports.ControlRegister['TXSTAT']);

  //TODO: handle GTS FIFOs
  if (fifo == exports.FIFO["TX"]) {
    var txError = (txStatus & 0x01) == 0x01;
    var ccaFail = (txStatus & 0x20) != 0;

    this.emit('transmitted', txError, ccaFail);
  }
}

function encodeAddress(buf, address, longFormat, write) {
  return longFormat ? encodeLongAddress(buf, address, write) : encodeShortAddress(buf, address, write);
}

function encodeLongAddress(buf, address, write) {
  address = (address | 0x400) << 1;

  if (write) {
    address = address | 0x01
  }

  address = address << 4;

  buf.writeUInt16BE(address, 0);

  return 2;
}

function encodeShortAddress(buf, address, write) {
  address = address << 1;

  if (write) {
    address = address | 0x01;
  }

  buf.writeUInt8(address, 0);

  return 1;
}
<commit_msg>removed higher-layer concerns<commit_after>var events = require('events');
var util = require('util');

exports.ControlRegister = require('./control_registers');
exports.FIFO = require('./fifo');

exports.MRF24J40 = function(platformOrHW) {
  this.txBuf = new Buffer(146);
  this.rxBuf = new Buffer(146);

  if (typeof platformOrHW === 'string') {
    var Hardware = require('./platforms/' + platformOrHW + '/hardware').Hardware;
    this.hw = new Hardware();
  } else {
    this.hw = platformOrHW;
  }

  this.hw.on('interrupt', handleInterrupt.bind(this));
}

util.inherits(exports.MRF24J40, events.EventEmitter);

exports.MRF24J40.prototype.readControlRegister = function(controlRegister) {
  var data = this._transferControlRegister(controlRegister, 0, false);
  return data.readUInt8(data.length - 1);
}

exports.MRF24J40.prototype.writeControlRegister = function(controlRegister, value) {
  this._transferControlRegister(controlRegister, value, true);
}

exports.MRF24J40.prototype._transferControlRegister = function(controlRegister, value, write) {
  var idx = encodeAddress(this.txBuf, controlRegister.address, controlRegister.long, write);
  this.txBuf.writeUInt8(value, idx++);
  this.rxBuf.fill(0, 0, idx);

  return this.hw.transfer(this.txBuf.slice(0, idx), this.rxBuf.slice(0, idx));
}

exports.MRF24J40.prototype.readFIFO = function(fifo, offset, count) {
  var idx = count + 2;
  this.txBuf.fill(0, 2, idx);
  var data = this._transferFIFO(fifo, false, offset, idx);
  return data.slice(2, idx);
}

exports.MRF24J40.prototype.writeFIFO = function(fifo, offset, data) {
  var idx = data.length + 2;
  data.copy(this.txBuf, 2);

  this._transferFIFO(fifo, true, offset, idx);
}

exports.MRF24J40.prototype._transferFIFO = function(fifo, write, offset, idx) {
  encodeAddress(this.txBuf, fifo.address + offset, true, write);

  this.rxBuf.fill(0, 0, idx);
  return this.hw.transfer(this.txBuf.slice(0, idx), this.rxBuf.slice(0, idx));
}

exports.MRF24J40.prototype.reset = function() {
  this.hw.writeReset(0);
  this.hw.writeReset(1);
}

exports.MRF24J40.prototype.initialize = function(channel) {
  if (!channel || channel < 11 || channel > 26) {
    channel = 11; //TODO: maybe throw an exception for out-of-range values?
  }

  channel = ((channel - 11) << 4) & 0xF0;

  // Initialization according datasheet example for non-beacon networks
  this.writeControlRegister(exports.ControlRegister['SOFTRST'], 0x07);
  this.writeControlRegister(exports.ControlRegister['PACON2'], 0x98);
  this.writeControlRegister(exports.ControlRegister['TXSTBL'], 0x95);
  this.writeControlRegister(exports.ControlRegister['RFCON0'], (channel | 0x03));
  this.writeControlRegister(exports.ControlRegister['RFCON1'], 0x01);
  this.writeControlRegister(exports.ControlRegister['RFCON2'], 0x80);
  this.writeControlRegister(exports.ControlRegister['RFCON3'], 0x00);
  this.writeControlRegister(exports.ControlRegister['RFCON6'], 0x90);
  this.writeControlRegister(exports.ControlRegister['RFCON7'], 0x80);
  this.writeControlRegister(exports.ControlRegister['RFCON8'], 0x10);
  this.writeControlRegister(exports.ControlRegister['SLPCON1'], 0x21);

  // Non-beacon
  this.writeControlRegister(exports.ControlRegister['BBREG2'], 0x80);
  this.writeControlRegister(exports.ControlRegister['CCAEDTH'], 0x60);
  this.writeControlRegister(exports.ControlRegister['BBREG6'], 0x40);

  // Enabled: SECIE, RXIE, TXNIE
  this.writeControlRegister(exports.ControlRegister['INTCON'], 0xE6);

  // Flush RX FIFO
  this.writeControlRegister(exports.ControlRegister['RXFLUSH'], 0x01);

  // Reset RF state machine
  this.writeControlRegister(exports.ControlRegister['RFCTL'], 0x04);
  this.writeControlRegister(exports.ControlRegister['RFCTL'], 0x00);
}

exports.MRF24J40.prototype.setPANCoordinator = function() {
  var rxmcr = this.readControlRegister(exports.ControlRegister['RXMCR']);
  this.writeControlRegister(exports.ControlRegister['RXMCR'], (rxmcr | 0x08));
}

exports.MRF24J40.prototype.clearPANCoordinator = function() {
  var rxmcr = this.readControlRegister(exports.ControlRegister['RXMCR']);
  this.writeControlRegister(exports.ControlRegister['RXMCR'], (rxmcr & 0xF7));
}

exports.MRF24J40.prototype.setPANID = function(panID) {
  this.writeControlRegister(exports.ControlRegister['PANIDH'], (panID & 0xFF));
  this.writeControlRegister(exports.ControlRegister['PANIDL'], (panID >> 8));
}

exports.MRF24J40.prototype.setShortAddress = function(shortAddress) {
  this.writeControlRegister(exports.ControlRegister['SADRH'], (shortAddress & 0xFF));
  this.writeControlRegister(exports.ControlRegister['SADRL'], (shortAddress >> 8));
}

exports.MRF24J40.prototype.setEUI = function(eui) {
  this.writeControlRegister(exports.ControlRegister['EADR0'], eui[0]);
}

exports.MRF24J40.prototype.transmit = function(frame) {
  this.writeFIFO(exports.FIFO['TX'], 0, frame);

  var txncon = 0x01;

  if ((frame[0] >> 3) & 0x01) {
    txncon = txncon | 0x02;
  }

  if ((frame[0] >> 5) & 0x01) {
    txncon = txncon | 0x04;
  }

  this.writeControlRegister(exports.ControlRegister['TXNCON'], txncon);
}

function handleInterrupt() {
  var interruptValue = this.readControlRegister(exports.ControlRegister['INTSTAT']);
  if ((interruptValue & 0x80) == 0x80) {
    handleSleepInterrupt.call(this);
  }

  if ((interruptValue & 0x40) == 0x40) {
    handleWakeInterrupt.call(this);
  }

  if ((interruptValue & 0x20) == 0x20) {
    handleHalfSymbolTimerInterrupt.call(this);
  }

  if ((interruptValue & 0x10) == 0x10) {
    handleSecurityKeyRequestInterrupt.call(this);
  }

  if ((interruptValue & 0x08) == 0x08) {
    handleReceiveInterrupt.call(this);
  }

  if ((interruptValue & 0x04) == 0x04) {
    handleTransmitInterrupt.call(this, exports.FIFO['TXGTS2']);
  }

  if ((interruptValue & 0x02) == 0x02) {
    handleTransmitInterrupt.call(this, exports.FIFO['TXGTS1']);
  }

  if ((interruptValue & 0x01) == 0x01) {
    handleTransmitInterrupt.call(this, exports.FIFO['TX']);
  }
}

function handleSleepInterrupt() {

}

function handleWakeInterrupt() {

}

function handleHalfSymbolTimerInterrupt() {

}

function handleSecurityKeyRequestInterrupt() {

}

function handleReceiveInterrupt() {
  this.writeControlRegister(exports.ControlRegister['BBREG1'], 0x04);
  //TODO: measure if it is faster to just dump the entire RX FIFO, or read the length and then only the needed bytes.
  var data = this.readFIFO(exports.FIFO['RX'], 0, exports.FIFO['RX'].size);
  this.writeControlRegister(exports.ControlRegister['RXFLUSH'], 0x01);
  this.writeControlRegister(exports.ControlRegister['BBREG1'], 0x00);
  var end = data.readUInt8(0);

  var lqi = data.readUInt8(end);
  var rssi = data.readUInt8(end + 1);
  this.emit('frame', data.slice(0, end), lqi, rssi);
}

function handleTransmitInterrupt(fifo) {
  var txStatus = this.readControlRegister(exports.ControlRegister['TXSTAT']);

  //TODO: handle GTS FIFOs
  if (fifo == exports.FIFO["TX"]) {
    var txError = (txStatus & 0x01) == 0x01;
    var ccaFail = (txStatus & 0x20) != 0;

    this.emit('transmitted', txError, ccaFail);
  }
}

function encodeAddress(buf, address, longFormat, write) {
  return longFormat ? encodeLongAddress(buf, address, write) : encodeShortAddress(buf, address, write);
}

function encodeLongAddress(buf, address, write) {
  address = (address | 0x400) << 1;

  if (write) {
    address = address | 0x01
  }

  address = address << 4;

  buf.writeUInt16BE(address, 0);

  return 2;
}

function encodeShortAddress(buf, address, write) {
  address = address << 1;

  if (write) {
    address = address | 0x01;
  }

  buf.writeUInt8(address, 0);

  return 1;
}
<|endoftext|>


<commit_before>describe('ContextMenu', function() {
  var plugin;
  var $html;
  var templateHtml = '
'
    + '
Item 1
'
    + '
Item 2
'
    + '
Item 3
'
  + '
'
  +'
'
    + '
Copy
'
    + '
Move
'
    + '
Delete
'
  + '
';

  afterEach(function() {
    plugin.destroy();
    $html.remove();
  });

  describe('constructor()', function() {
    it('stores the element and plugin options', function() {
      $html = $(templateHtml).appendTo('body');
      plugin = new Foundation.ContextMenu($html.find('[data-context-menu]'), {});

      plugin.$element.should.be.an('object');
      plugin.options.should.be.an('object');
    });
  });

  describe('_init()', function() {
    it('hides the context menu list', function() {
      $html = $(templateHtml).appendTo('body');
      plugin = new Foundation.ContextMenu($html.find('[data-context-menu]'), {});

      plugin.$element.should.have.attr('data-context-menu');
      plugin.$menu.should.be.hidden;
    });
  });
});
<commit_msg>Enhanced automated test cases.<commit_after>describe('ContextMenu', function() {
  var plugin;
  var $html;
  var templateHtml = '
'
    + '
Item 1
'
    + '
Item 2
'
    + '
Item 3
'
  + '
'
  +'
'
    + '
Copy
'
    + '
Move
'
    + '
Delete
'
  + '
';

  afterEach(function() {
    plugin.destroy();
    $html.remove();
  });

  describe('constructor()', function() {
    it('stores the element and plugin options', function() {
      $html = $(templateHtml).appendTo('body');
      plugin = new Foundation.ContextMenu($html.find('[data-context-menu]'), {});

      plugin.$element.should.be.an('object');
      plugin.options.should.be.an('object');
    });
  });

  describe('_init()', function() {
    it('hides the context menu list', function() {
      $html = $(templateHtml).appendTo('body');
      plugin = new Foundation.ContextMenu($html.find('[data-context-menu]'), {});

      plugin.$element.should.have.attr('data-context-menu');
      plugin.$menu.should.be.hidden;
    });
  });

  describe('show()', function() {
    it('shows the menu', function() {
      $html = $(templateHtml).appendTo('body');
      plugin = new Foundation.ContextMenu($html.find('[data-context-menu]'), {});

      plugin.show();
      plugin.$menu.should.be.visible;
    });
  });

  describe('hide()', function() {
    it('hides the menu', function() {
      $html = $(templateHtml).appendTo('body');
      plugin = new Foundation.ContextMenu($html.find('[data-context-menu]'), {});

      plugin.show();
      plugin.hide();
      plugin.$menu.should.be.hidden;
    });
  });

  describe('events', function() {
    /*it('shows menu on touchstart', function() {
      $html = $(templateHtml).appendTo('body');
      plugin = new Foundation.ContextMenu($html.find('[data-context-menu]'), {});

      plugin.$element.trigger('touchstart');
      plugin.$menu.should.be.visible;
    });*/

    it('hides menu on body click', function() {
      $html = $(templateHtml).appendTo('body');
      plugin = new Foundation.ContextMenu($html.find('[data-context-menu]'), {});

      plugin.show();

      $('body').trigger('click');
      plugin.$menu.should.be.hidden;
    });
  });
});
<|endoftext|>


<commit_before>/*
 * MIT License
 *
 * Copyright (c) 2017-2017 Jan Dockx
 *
 * 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.
 */

/* eslint-env mocha */

const selectInstance = require('../../lib/selectInstance')
const notOneOf = selectInstance.notOneOf
const verifyPostconditions = require('../_util/verifyPostconditions')

const serviceTypePostfix = '._tcp.dns-sd-lookup.toryt.org'
const serviceType1InstanceNoSubtype = '_t1i-no-sub' + serviceTypePostfix
const serviceType1InstanceSubtype = '_subtype._sub._t7i-sub' + serviceTypePostfix
const serviceTypeNInstancesWithWeight = '_t8i-n-inst' + serviceTypePostfix
const manyInstanceCount = 12
const must = require('must')

const batch = 3
/* NOTE: 3 is a magic number for tests on Travis with Node 10. For some reason, the first 3 DNS lookups in a batch take
         in average less than 100ms, but after that the time goes up to 5s, and then 10s and 15s. By keeping the batch
         size 3, the tests below take less than a minute. Note that on a local machine, they take less than a second. */

function testDistribution (timerLabel, deaths, expected) {
  const expectedPattern = Object.keys(expected).join('|')
  const matchExpr = new RegExp(`(${expectedPattern})\\.${serviceTypeNInstancesWithWeight.replace(/\./g, '\\.')}`)

  function totalCount (selections) {
    return Object.keys(selections).reduce(
      (acc, key) => {
        acc += selections[key]
        return acc
      },
      0
    )
  }

  const expectedStr = Object.keys(expected).map(k => `${k} = ${expected[k] * 100}%`).join(', ')

  function report (selections) {
    const total = totalCount(selections)
    console.log(`${total} - expected ${expectedStr}`)
    Object.keys(selections).forEach(k => {
      console.log('  %s: %d/%d (%f%%)', k, selections[k], total, Math.round(selections[k] * 1000 / total) / 10)
    })
    console.log()
  }

  function chain (todo, previous) {
    if (todo <= 0) {
      return previous
    }
    return chain(
      todo - batch,
      previous.then(selections => {
        const next = []
        for (let i = 0; i < Math.min(batch, todo); i++) {
          const tLabel = `${todo - i} - ${timerLabel}`
          console.time(tLabel)
          // noinspection JSUnresolvedFunction
          next.push(
            selectInstance(serviceTypeNInstancesWithWeight, notOneOf(deaths))
              .then(selection => {
                console.timeEnd(tLabel)
                selection.must.be.an.object()
                selection.instance.must.match(matchExpr)
                if (!selections[selection.instance]) {
                  selections[selection.instance] = 0
                }
                selections[selection.instance]++
              })
          )
        }
        return Promise.all(next).then(() => {
          report(selections)
          return selections
        })
      })
    )
  }

  console.time(timerLabel)

  return chain(150, Promise.resolve({}))
    .then(selections => {
      console.timeEnd(timerLabel)
      const total = totalCount(selections)
      Object.keys(expected).forEach(e => {
        Math.abs(expected[e] - (selections[e + '.' + serviceTypeNInstancesWithWeight] / total)).must.be.below(0.15)
        /* NOTE: I would like for the deviation from the expected weight distribution to be less than 2.5% (2 sigma).
                 When testing with 2 instances, in 1024 tries, if often happens that the deviation that the deviation
                 is larger. That is surprising. This would mean that a random choice is not good enough.
                 That would imply that we rather need some sort of memory, which would be bad.
                 After this observation, the limit was lowered to 5%, and the tries are lowered to 256, for
                 test speed reasons. Still there are enough failures to be annoying. The limit was then raised to
                 10%. Since Travis DNS became very slow in 2018 Q III, the tries are lowered to 64, and the limit
                 is raised to 15%. */
      })
    })
}

describe('selectInstance', function () {
  this.timeout(6000) // DNS lookups can take a long time on Travis
  verifyPostconditions(selectInstance)
  verifyPostconditions(selectInstance.selectByWeight)

  it('works in the nominal case with 1 instance, without a subtype', function () {
    // noinspection JSUnresolvedVariable
    return selectInstance(serviceType1InstanceNoSubtype)
      .must.fulfill(selection => {
        selection.must.be.an.object()
        selection.instance.must.equal('instance 1.' + serviceType1InstanceNoSubtype)
        console.log(selection)
      })
  })
  it('works in the nominal case with 1 instance, with a subtype', function () {
    // noinspection JSUnresolvedVariable
    return selectInstance(serviceType1InstanceSubtype)
      .must.fulfill(selection => {
        selection.must.be.an.object()
        selection.instance.must.equal('instance 7._t7i-sub' + serviceTypePostfix)
        console.log(selection)
      })
  })
  it(`works in the nominal case, with ${manyInstanceCount} instances`, function () {
    this.timeout(10000)

    // noinspection JSUnresolvedVariable
    return selectInstance(serviceTypeNInstancesWithWeight)
      .must.fulfill(selection => {
        selection.must.be.an.object()
        selection.instance.must.equal('instance 8a.' + serviceTypeNInstancesWithWeight)
        console.log(selection)
      })
  })
  it('resolves to null with a non-existent service type', function () {
    // noinspection JSUnresolvedVariable
    return selectInstance('_not-exist' + serviceTypePostfix)
      .must.fulfill(selection => {
        must(selection).be.null()
        console.log(selection)
      })
  })
  it('selects according to weight with a filter', function () {
    this.timeout(10000)

    const deaths = [
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8d.${serviceTypeNInstancesWithWeight}`
    ]
    // noinspection JSUnresolvedVariable
    return selectInstance(serviceTypeNInstancesWithWeight, notOneOf(deaths))
      .must.fulfill(selection => {
        selection.must.be.an.object()
        selection.instance.must.match(`instance 8b.${serviceTypeNInstancesWithWeight}`)
        console.log(selection)
      })
  })
  it('selects according to weight with a filter that excludes every instance', function () {
    const deaths = [
      `Instance 8c.${serviceTypeNInstancesWithWeight}`,
      `Instance 8e.${serviceTypeNInstancesWithWeight}`,
      `Instance 8c.${serviceTypeNInstancesWithWeight}`,
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8f.${serviceTypeNInstancesWithWeight}`,
      `Instance 8e.${serviceTypeNInstancesWithWeight}`,
      `Instance 8j.${serviceTypeNInstancesWithWeight}`,
      `Instance 8i.${serviceTypeNInstancesWithWeight}`,
      `Instance 8e.${serviceTypeNInstancesWithWeight}`,
      `Instance 8g.${serviceTypeNInstancesWithWeight}`,
      `Instance 8k.${serviceTypeNInstancesWithWeight}`,
      `Instance 8h.${serviceTypeNInstancesWithWeight}`,
      `Instance 8d.${serviceTypeNInstancesWithWeight}`,
      `Instance 8l.${serviceTypeNInstancesWithWeight}`,
      `Instance 8b.${serviceTypeNInstancesWithWeight}`
    ]
    // noinspection JSUnresolvedVariable
    return selectInstance(serviceTypeNInstancesWithWeight, notOneOf(deaths))
      .must.fulfill(selection => {
        must(selection).be.null()
        console.log(selection)
      })
  })

  const labelA = 'selects according to weight with a filter evenly with 2 instances'
  it(labelA, function () {
    this.timeout(60000)

    const deaths = [
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8b.${serviceTypeNInstancesWithWeight}`
    ]

    const expected = {}
    expected[`instance 8c`] = 0.3
    expected[`instance 8d`] = 0.7

    return testDistribution(labelA, deaths, expected)
  })
  const labelB = 'selects according to weight with a filter evenly with 5 instances'
  it(labelB, function () {
    this.timeout(60000)

    const deaths = [
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8b.${serviceTypeNInstancesWithWeight}`,
      `Instance 8c.${serviceTypeNInstancesWithWeight}`,
      `Instance 8d.${serviceTypeNInstancesWithWeight}`
    ]

    const expected = {}
    expected[`instance 8e`] = 0.1
    expected[`instance 8f`] = 0.2
    expected[`instance 8g`] = 0.3
    expected[`instance 8h`] = 0.2
    expected[`instance 8i`] = 0.2

    return testDistribution(labelB, deaths, expected)
  })
  const labelC = 'selects according to weight with a filter evenly with 3 instances with weight 0'
  it(labelC, function () {
    this.timeout(60000)

    const deaths = [
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8b.${serviceTypeNInstancesWithWeight}`,
      `Instance 8c.${serviceTypeNInstancesWithWeight}`,
      `Instance 8d.${serviceTypeNInstancesWithWeight}`,
      `Instance 8e.${serviceTypeNInstancesWithWeight}`,
      `Instance 8f.${serviceTypeNInstancesWithWeight}`,
      `Instance 8g.${serviceTypeNInstancesWithWeight}`,
      `Instance 8h.${serviceTypeNInstancesWithWeight}`,
      `Instance 8i.${serviceTypeNInstancesWithWeight}`
    ]

    const aThird = 1 / 3
    const expected = {}
    expected[`instance 8j`] = aThird
    expected[`instance 8k`] = aThird
    expected[`instance 8l`] = aThird

    return testDistribution(labelC, deaths, expected)
  })

  let failures = [
    't2i-2-txt',
    't3i-2-srv',
    't4i-2-txt-srv',
    't5i-no-txt',
    't6i-no-srv'
  ]
  failures = failures.map(f => `_${f}${serviceTypePostfix}`)
  failures.forEach(serviceType => {
    it(`fails for instance type ${serviceType}`, function () {
      // noinspection JSUnresolvedVariable
      return selectInstance(serviceType).must.betray(err => {
        console.log(err)
      })
    })
  })
  const aFailure = failures[0]
  it(`fails for instance type ${aFailure} with a filter`, function () {
    const filter = selectInstance.contract.filter.implementation(instance => instance.indexOf(aFailure) >= 0)
    filter.contract.verifyPostconditions = true
    // noinspection JSUnresolvedVariable
    return selectInstance(aFailure, filter).must.betray(err => {
      console.log(err)
      err.instance.must.contain(aFailure)
    })
  })
})
<commit_msg>change final test file from must to should, except fullfil and betray<commit_after>/*
 * MIT License
 *
 * Copyright (c) 2017-2017 Jan Dockx
 *
 * 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.
 */

/* eslint-env mocha */

const selectInstance = require('../../lib/selectInstance')
const notOneOf = selectInstance.notOneOf
const verifyPostconditions = require('../_util/verifyPostconditions')

const serviceTypePostfix = '._tcp.dns-sd-lookup.toryt.org'
const serviceType1InstanceNoSubtype = '_t1i-no-sub' + serviceTypePostfix
const serviceType1InstanceSubtype = '_subtype._sub._t7i-sub' + serviceTypePostfix
const serviceTypeNInstancesWithWeight = '_t8i-n-inst' + serviceTypePostfix
const manyInstanceCount = 12
const should = require('should')

const batch = 3
/* NOTE: 3 is a magic number for tests on Travis with Node 10. For some reason, the first 3 DNS lookups in a batch take
         in average less than 100ms, but after that the time goes up to 5s, and then 10s and 15s. By keeping the batch
         size 3, the tests below take less than a minute. Note that on a local machine, they take less than a second. */

function testDistribution (timerLabel, deaths, expected) {
  const expectedPattern = Object.keys(expected).join('|')
  const matchExpr = new RegExp(`(${expectedPattern})\\.${serviceTypeNInstancesWithWeight.replace(/\./g, '\\.')}`)

  function totalCount (selections) {
    return Object.keys(selections).reduce(
      (acc, key) => {
        acc += selections[key]
        return acc
      },
      0
    )
  }

  const expectedStr = Object.keys(expected).map(k => `${k} = ${expected[k] * 100}%`).join(', ')

  function report (selections) {
    const total = totalCount(selections)
    console.log(`${total} - expected ${expectedStr}`)
    Object.keys(selections).forEach(k => {
      console.log('  %s: %d/%d (%f%%)', k, selections[k], total, Math.round(selections[k] * 1000 / total) / 10)
    })
    console.log()
  }

  function chain (todo, previous) {
    if (todo <= 0) {
      return previous
    }
    return chain(
      todo - batch,
      previous.then(selections => {
        const next = []
        for (let i = 0; i < Math.min(batch, todo); i++) {
          const tLabel = `${todo - i} - ${timerLabel}`
          console.time(tLabel)
          // noinspection JSUnresolvedFunction
          next.push(
            selectInstance(serviceTypeNInstancesWithWeight, notOneOf(deaths))
              .then(selection => {
                console.timeEnd(tLabel)
                selection.should.be.an.Object()
                selection.instance.should.match(matchExpr)
                if (!selections[selection.instance]) {
                  selections[selection.instance] = 0
                }
                selections[selection.instance]++
              })
          )
        }
        return Promise.all(next).then(() => {
          report(selections)
          return selections
        })
      })
    )
  }

  console.time(timerLabel)

  return chain(150, Promise.resolve({}))
    .then(selections => {
      console.timeEnd(timerLabel)
      const total = totalCount(selections)
      Object.keys(expected).forEach(e => {
        Math.abs(expected[e] - (selections[e + '.' + serviceTypeNInstancesWithWeight] / total)).should.be.below(0.15)
        /* NOTE: I would like for the deviation from the expected weight distribution to be less than 2.5% (2 sigma).
                 When testing with 2 instances, in 1024 tries, if often happens that the deviation that the deviation
                 is larger. That is surprising. This would mean that a random choice is not good enough.
                 That would imply that we rather need some sort of memory, which would be bad.
                 After this observation, the limit was lowered to 5%, and the tries are lowered to 256, for
                 test speed reasons. Still there are enough failures to be annoying. The limit was then raised to
                 10%. Since Travis DNS became very slow in 2018 Q III, the tries are lowered to 64, and the limit
                 is raised to 15%. */
      })
    })
}

describe('selectInstance', function () {
  this.timeout(6000) // DNS lookups can take a long time on Travis
  verifyPostconditions(selectInstance)
  verifyPostconditions(selectInstance.selectByWeight)

  it('works in the nominal case with 1 instance, without a subtype', function () {
    // noinspection JSUnresolvedVariable
    return selectInstance(serviceType1InstanceNoSubtype)
      .must.fulfill(selection => {
        selection.should.be.an.Object()
        selection.instance.should.equal('instance 1.' + serviceType1InstanceNoSubtype)
        console.log(selection)
      })
  })
  it('works in the nominal case with 1 instance, with a subtype', function () {
    // noinspection JSUnresolvedVariable
    return selectInstance(serviceType1InstanceSubtype)
      .must.fulfill(selection => {
        selection.should.be.an.Object()
        selection.instance.should.equal('instance 7._t7i-sub' + serviceTypePostfix)
        console.log(selection)
      })
  })
  it(`works in the nominal case, with ${manyInstanceCount} instances`, function () {
    this.timeout(10000)

    // noinspection JSUnresolvedVariable
    return selectInstance(serviceTypeNInstancesWithWeight)
      .must.fulfill(selection => {
        selection.should.be.an.Object()
        selection.instance.should.equal('instance 8a.' + serviceTypeNInstancesWithWeight)
        console.log(selection)
      })
  })
  it('resolves to null with a non-existent service type', function () {
    // noinspection JSUnresolvedVariable
    return selectInstance('_not-exist' + serviceTypePostfix)
      .must.fulfill(selection => {
        should(selection).be.null()
        console.log(selection)
      })
  })
  it('selects according to weight with a filter', function () {
    this.timeout(10000)

    const deaths = [
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8d.${serviceTypeNInstancesWithWeight}`
    ]
    // noinspection JSUnresolvedVariable
    return selectInstance(serviceTypeNInstancesWithWeight, notOneOf(deaths))
      .must.fulfill(selection => {
        selection.should.be.an.Object()
        selection.instance.should.equal(`instance 8b.${serviceTypeNInstancesWithWeight}`)
        console.log(selection)
      })
  })
  it('selects according to weight with a filter that excludes every instance', function () {
    const deaths = [
      `Instance 8c.${serviceTypeNInstancesWithWeight}`,
      `Instance 8e.${serviceTypeNInstancesWithWeight}`,
      `Instance 8c.${serviceTypeNInstancesWithWeight}`,
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8f.${serviceTypeNInstancesWithWeight}`,
      `Instance 8e.${serviceTypeNInstancesWithWeight}`,
      `Instance 8j.${serviceTypeNInstancesWithWeight}`,
      `Instance 8i.${serviceTypeNInstancesWithWeight}`,
      `Instance 8e.${serviceTypeNInstancesWithWeight}`,
      `Instance 8g.${serviceTypeNInstancesWithWeight}`,
      `Instance 8k.${serviceTypeNInstancesWithWeight}`,
      `Instance 8h.${serviceTypeNInstancesWithWeight}`,
      `Instance 8d.${serviceTypeNInstancesWithWeight}`,
      `Instance 8l.${serviceTypeNInstancesWithWeight}`,
      `Instance 8b.${serviceTypeNInstancesWithWeight}`
    ]
    // noinspection JSUnresolvedVariable
    return selectInstance(serviceTypeNInstancesWithWeight, notOneOf(deaths))
      .must.fulfill(selection => {
        should(selection).be.null()
        console.log(selection)
      })
  })

  const labelA = 'selects according to weight with a filter evenly with 2 instances'
  it(labelA, function () {
    this.timeout(60000)

    const deaths = [
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8b.${serviceTypeNInstancesWithWeight}`
    ]

    const expected = {}
    expected[`instance 8c`] = 0.3
    expected[`instance 8d`] = 0.7

    return testDistribution(labelA, deaths, expected)
  })
  const labelB = 'selects according to weight with a filter evenly with 5 instances'
  it(labelB, function () {
    this.timeout(60000)

    const deaths = [
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8b.${serviceTypeNInstancesWithWeight}`,
      `Instance 8c.${serviceTypeNInstancesWithWeight}`,
      `Instance 8d.${serviceTypeNInstancesWithWeight}`
    ]

    const expected = {}
    expected[`instance 8e`] = 0.1
    expected[`instance 8f`] = 0.2
    expected[`instance 8g`] = 0.3
    expected[`instance 8h`] = 0.2
    expected[`instance 8i`] = 0.2

    return testDistribution(labelB, deaths, expected)
  })
  const labelC = 'selects according to weight with a filter evenly with 3 instances with weight 0'
  it(labelC, function () {
    this.timeout(60000)

    const deaths = [
      `Instance 8a.${serviceTypeNInstancesWithWeight}`,
      `Instance 8b.${serviceTypeNInstancesWithWeight}`,
      `Instance 8c.${serviceTypeNInstancesWithWeight}`,
      `Instance 8d.${serviceTypeNInstancesWithWeight}`,
      `Instance 8e.${serviceTypeNInstancesWithWeight}`,
      `Instance 8f.${serviceTypeNInstancesWithWeight}`,
      `Instance 8g.${serviceTypeNInstancesWithWeight}`,
      `Instance 8h.${serviceTypeNInstancesWithWeight}`,
      `Instance 8i.${serviceTypeNInstancesWithWeight}`
    ]

    const aThird = 1 / 3
    const expected = {}
    expected[`instance 8j`] = aThird
    expected[`instance 8k`] = aThird
    expected[`instance 8l`] = aThird

    return testDistribution(labelC, deaths, expected)
  })

  let failures = [
    't2i-2-txt',
    't3i-2-srv',
    't4i-2-txt-srv',
    't5i-no-txt',
    't6i-no-srv'
  ]
  failures = failures.map(f => `_${f}${serviceTypePostfix}`)
  failures.forEach(serviceType => {
    it(`fails for instance type ${serviceType}`, function () {
      // noinspection JSUnresolvedVariable
      return selectInstance(serviceType).must.betray(err => {
        console.log(err)
      })
    })
  })
  const aFailure = failures[0]
  it(`fails for instance type ${aFailure} with a filter`, function () {
    const filter = selectInstance.contract.filter.implementation(instance => instance.indexOf(aFailure) >= 0)
    filter.contract.verifyPostconditions = true
    // noinspection JSUnresolvedVariable
    return selectInstance(aFailure, filter).must.betray(err => {
      console.log(err)
      err.instance.should.containEql(aFailure)
    })
  })
})
<|endoftext|>


<commit_before>define(['../dispatch'], function(dispatch) {

  'use strict';

  function split_contents(formatVersion, segment, entries, offset) {
    switch (formatVersion) {
      default: return Promise.reject('unsupported format version: ' + formatVersion);
      case 6:
        return segment.getBytes(offset + 8, 2).then(function(count) {
          count = count[0] | (count[1] << 8);
          var totalSize = 6 /* signature */
            + 1 /* modifier */
            + 1 /* padding? */
            + 2 /* file count */
            + 13 /* password dooberry */
            + count*13 /* file names */
            + count*4 /* file lengths */
            + count*2 /* flags & ratio */;
          var fileListSegment = segment.getSegment(
            'chunk/ags; which=file-list-v6; base=' + (offset + totalSize),
            offset,
            totalSize);
          entries.add(fileListSegment);
        });
      case 10:
      case 11:
      case 15:
        return segment.getBytes(0, 5).then(function(bytes) {
          if (bytes[0] !== 0) return Promise.reject('not first datafile in chain');
          var containerCount = (bytes[1] | (bytes[2] << 8) | (bytes[3] << 16) | (bytes[4] << 24)) >>> 0;
          return segment.getBytes(1 + 4 + 20*containerCount, 4).then(function(bytes) {
            var fileCount = (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24)) >>> 0;
            var totalSize = 1 + 4 + 20*containerCount + 4 + 25*fileCount + 4*fileCount + 4*fileCount + fileCount;
            entries.add(segment.getSegment('chunk/ags; which=file-list-v' + formatVersion, 0, totalSize));
          });
        });
      case 20:
        return segment.getDataView(0, 5).then(function(dv) {
          if (dv.getUint8(0) !== 0) return Promise.reject('not first datafile in chain');
          var pos = 5;
          function doFileNames(fileCount) {
            if (fileCount === 0) return;
            return segment.getInt16(pos, true)
            .then(function(nameLength) {
              nameLength = (nameLength / 5) | 0;
              pos += 4 + nameLength;
              return doFileNames(fileCount - 1);
            });
          }
          function doFiles(fileCount) {
            return doFileNames(fileCount).then(function() {
              pos += fileCount*4 + fileCount*4 + fileCount;
            });
          }
          function doContainers(containerCount) {
            if (containerCount === 0) {
              return segment.getInt32(pos, true).then(function(fileCount) {
                pos += 4;
                doFiles(fileCount);
              });
            }
            function onByte(b) {
              pos++;
              if (b === 0) return doContainers(containerCount - 1);
              return segment.getUint8(pos).then(onByte);
            }
            return segment.getUint8(pos).then(onByte);
          }
          return doContainers(dv.getUint32(1, true))
          .then(function() {
            entries.add(segment.getSegment('chunk/ags; which=file-list-v' + formatVersion), 0, pos);
          });
        });
    }
  }

  function split_prefix(segment, entries, offset) {
    return segment.getBytes(offset, 6).then(function(prefix) {
      if (String.fromCharCode(prefix[0], prefix[1], prefix[2], prefix[3]) !== 'CLIB') {
        return Promise.reject('resource package signature not found');
      }
      return split_contents(
        prefix[5],
        segment.getSegment(segment.typeName + '; body-version=' + prefix[5]),
        entries,
        offset);
    });
  }

  function split_suffix(segment, entries) {
    return segment.getBytes('suffix', 16).then(function(suffix) {
      var signature = String.fromCharCode.apply(null, suffix.subarray(4, 16));
      if (signature !== 'CLIB\x01\x02\x03\x04SIGE') {
        return Promise.reject('resource package signature not found');
      }
      var offset = (suffix[0] | (suffix[1] << 8) | (suffix[2] << 16) | (suffix[3] << 24)) >>> 0;
      return split_prefix(segment, entries, offset);
    });
  }

  function split(segment, entries) {
    var bodyVersion = segment.getTypeParameter('body-version');
    if (bodyVersion) return split_contents(bodyVersion, segment, entries, 0);

    var mode = segment.getTypeParameter('mode');
    if (mode === 'prefix') return split_prefix(segment, entries, 0);
    if (mode === 'suffix') return split_suffix(segment, entries);

    return split_prefix(segment, entries, 0).then(null, function() {
      return split_suffix(segment, entries);
    });
  }

  function mount(containerSegment, volume) {
    var promiseChain = Promise.resolve();
    return containerSegment.split(function(chunk) {
      if (chunk.typeName === 'chunk/ags' && chunk.getTypeParameter('which') === 'file-list-v6') {
        var base = chunk.getTypeParameter('base');
        if (!base) {
          throw new Error('base must be set');
        }
        base = +base;
        promiseChain = promiseChain.then(
        chunk.getStruct()
        .then(function(fileList) {
          for (var i = 0; i < fileList.fileRecords.length; i++) {
            var file = fileList.fileRecords[i];
            var fileType;
            var ext = file.name.match(/[^\.]*$/)[0].toLowerCase();
            switch (ext) {
              case 'crm':
                fileType = 'application/x-ags-room';
                break;
              case 'mid':
                fileType = 'audio/midi';
                break;
              case 'wav':
                fileType = 'audio/x-wav';
                break;
              case 'dta':
                fileType = 'application/x-ags-game';
                break;
              case 'spr':
                fileType = 'application/x-ags-sprite-pack';
                break;
              case 'wfn':
                fileType = 'application/x-ags-font';
                break;
              case 'dat':
                if (/^aclang.*\.dat$/i.test(file.name)) {
                  fileType = 'application/x-ags-translation';
                  break;
                }
                else {
                  console.log('unknown file: ' + file.name);
                  fileType = 'application/octet-stream';
                }
                break;
              default:
                fileType = dispatch.byExtension[ext] || 'application/octet-stream';
                console.log('unknown file: ' + file.name);
                break;
            }
            volume.addFile(file.name, containerSegment.getSegment(fileType, base + file.byteOffset, file.byteLength));
          }
        }));
      }
      else {
        console.log('unsupported: ' + chunk.type);
      }
    },
    function() {
      return promiseChain;
    });
  }

  return {
    split: split,
    mount: mount,
  };

});
<commit_msg>Update x-ags-container.js<commit_after>define(['../dispatch'], function(dispatch) {

  'use strict';

  function split_contents(formatVersion, segment, entries, offset) {
    switch (formatVersion) {
      default: return Promise.reject('unsupported format version: ' + formatVersion);
      case 6:
        return segment.getBytes(offset + 8, 2).then(function(count) {
          count = count[0] | (count[1] << 8);
          var totalSize = 6 /* signature */
            + 1 /* modifier */
            + 1 /* padding? */
            + 2 /* file count */
            + 13 /* password dooberry */
            + count*13 /* file names */
            + count*4 /* file lengths */
            + count*2 /* flags & ratio */;
          var fileListSegment = segment.getSegment(
            'chunk/ags; which=file-list-v6; base=' + (offset + totalSize),
            offset,
            totalSize);
          entries.add(fileListSegment);
        });
      case 10:
      case 11:
      case 15:
        return segment.getBytes(0, 5).then(function(bytes) {
          if (bytes[0] !== 0) return Promise.reject('not first datafile in chain');
          var containerCount = (bytes[1] | (bytes[2] << 8) | (bytes[3] << 16) | (bytes[4] << 24)) >>> 0;
          return segment.getBytes(1 + 4 + 20*containerCount, 4).then(function(bytes) {
            var fileCount = (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24)) >>> 0;
            var totalSize = 1 + 4 + 20*containerCount + 4 + 25*fileCount + 4*fileCount + 4*fileCount + fileCount;
            entries.add(segment.getSegment('chunk/ags; which=file-list-v' + formatVersion, 0, totalSize));
          });
        });
      case 20:
        return segment.getDataView(0, 5).then(function(dv) {
          if (dv.getUint8(0) !== 0) return Promise.reject('not first datafile in chain');
          var pos = 5;
          function doFileNames(fileCount) {
            if (fileCount === 0) return;
            return segment.getInt16(pos, true)
            .then(function(nameLength) {
              nameLength = (nameLength / 5) | 0;
              pos += 4 + nameLength;
              return doFileNames(fileCount - 1);
            });
          }
          function doFiles(fileCount) {
            return doFileNames(fileCount).then(function() {
              pos += fileCount*4 + fileCount*4 + fileCount;
            });
          }
          function doContainers(containerCount) {
            if (containerCount === 0) {
              return segment.getInt32(pos, true).then(function(fileCount) {
                pos += 4;
                doFiles(fileCount);
              });
            }
            function onByte(b) {
              pos++;
              if (b === 0) return doContainers(containerCount - 1);
              return segment.getUint8(pos).then(onByte);
            }
            return segment.getUint8(pos).then(onByte);
          }
          return doContainers(dv.getUint32(1, true))
          .then(function() {
            entries.add(segment.getSegment('chunk/ags; which=file-list-v' + formatVersion), 0, pos);
          });
        });
    }
  }

  function split_prefix(segment, entries, offset) {
    return segment.getBytes(offset, 6).then(function(prefix) {
      if (String.fromCharCode(prefix[0], prefix[1], prefix[2], prefix[3]) !== 'CLIB') {
        return Promise.reject('resource package signature not found');
      }
      return split_contents(
        prefix[5],
        segment.getSegment(segment.typeName + '; body-version=' + prefix[5]),
        entries,
        offset);
    });
  }

  function split_suffix(segment, entries) {
    return segment.getBytes('suffix', 16).then(function(suffix) {
      var signature = String.fromCharCode.apply(null, suffix.subarray(4, 16));
      if (signature !== 'CLIB\x01\x02\x03\x04SIGE') {
        return Promise.reject('resource package signature not found');
      }
      var offset = (suffix[0] | (suffix[1] << 8) | (suffix[2] << 16) | (suffix[3] << 24)) >>> 0;
      return split_prefix(segment, entries, offset);
    });
  }

  function split(segment, entries) {
    var bodyVersion = segment.format.parameters['body-version'];
    if (bodyVersion) return split_contents(bodyVersion, segment, entries, 0);

    var mode = segment.format.parameters['mode'];
    if (mode === 'prefix') return split_prefix(segment, entries, 0);
    if (mode === 'suffix') return split_suffix(segment, entries);

    return split_prefix(segment, entries, 0).then(null, function() {
      return split_suffix(segment, entries);
    });
  }

  function mount(containerSegment, volume) {
    var promiseChain = Promise.resolve();
    return containerSegment.split(function(chunk) {
      if (chunk.format.name === 'chunk/ags' && chunk.format.parameters['which'] === 'file-list-v6') {
        var base = chunk.format.parameters['base'];
        if (!base) {
          throw new Error('base must be set');
        }
        base = +base;
        promiseChain = promiseChain.then(
        chunk.getStruct()
        .then(function(fileList) {
          for (var i = 0; i < fileList.fileRecords.length; i++) {
            var file = fileList.fileRecords[i];
            var fileType;
            var ext = file.name.match(/[^\.]*$/)[0].toLowerCase();
            switch (ext) {
              case 'crm':
                fileType = 'application/x-ags-room';
                break;
              case 'mid':
                fileType = 'audio/midi';
                break;
              case 'wav':
                fileType = 'audio/x-wav';
                break;
              case 'dta':
                fileType = 'application/x-ags-game';
                break;
              case 'spr':
                fileType = 'application/x-ags-sprite-pack';
                break;
              case 'wfn':
                fileType = 'application/x-ags-font';
                break;
              case 'dat':
                if (/^aclang.*\.dat$/i.test(file.name)) {
                  fileType = 'application/x-ags-translation';
                  break;
                }
                else {
                  console.log('unknown file: ' + file.name);
                  fileType = 'application/octet-stream';
                }
                break;
              default:
                fileType = dispatch.byExtension[ext] || 'application/octet-stream';
                console.log('unknown file: ' + file.name);
                break;
            }
            volume.addFile(file.name, containerSegment.getSegment(fileType, base + file.byteOffset, file.byteLength));
          }
        }));
      }
      else {
        console.log('unsupported: ' + chunk.type);
      }
    },
    function() {
      return promiseChain;
    });
  }

  return {
    split: split,
    mount: mount,
  };

});
<|endoftext|>