<commit_before>$(window).on('load', function() {
  var documentSettings = {};

  // Some constants, such as default settings
  const CHAPTER_ZOOM = 15;

  // This watches for the scrollable container
  var scrollPosition = 0;
  $('div#contents').scroll(function() {
    scrollPosition = $(this).scrollTop();
  });

  // First, try reading Options.csv
  $.get('csv/Options.csv', function(options) {

    $.get('csv/Chapters.csv', function(chapters) {
      initMap(
        $.csv.toObjects(options),
        $.csv.toObjects(chapters)
      )
    }).fail(function(e) { alert('Found Options.csv, but could not read Chapters.csv') });

  // If not available, try from the Google Sheet
  }).fail(function(e) {

    var parse = function(res) {
      return Papa.parse(Papa.unparse(res[0].values), {header: true} ).data;
    }

    // First, try reading data from the Google Sheet
    if (typeof googleDocURL !== 'undefined' && googleDocURL) {

      if (typeof googleApiKey !== 'undefined' && googleApiKey) {

        var apiUrl = 'https://sheets.googleapis.com/v4/spreadsheets/'
        var spreadsheetId = googleDocURL.split('/d/')[1].split('/')[0];

        $.when(
          $.getJSON(apiUrl + spreadsheetId + '/values/Options?key=' + googleApiKey),
          $.getJSON(apiUrl + spreadsheetId + '/values/Chapters?key=' + googleApiKey),
        ).then(function(options, chapters) {
          initMap(parse(options), parse(chapters))
        })

      } else {
        alert('You load data from a Google Sheet, you need to add a free Google API key')
      }

    } else {
      alert('You need to specify a valid Google Sheet (googleDocURL)')
    }

  })

  /**
  * Reformulates documentSettings as a dictionary, e.g.
  * {"webpageTitle": "Leaflet Boilerplate", "infoPopupText": "Stuff"}
  */
  function createDocumentSettings(settings) {
    for (var i in settings) {
      var setting = settings[i];
      documentSettings[setting.Setting] = setting.Customize;
    }
  }

  /**
   * Returns the value of a setting s
   * getSetting(s) is equivalent to documentSettings[constants.s]
   */
  function getSetting(s) {
    return documentSettings[constants[s]];
  }

  /**
   * Returns the value of setting named s from constants.js
   * or def if setting is either not set or does not exist
   * Both arguments are strings
   * e.g. trySetting('_authorName', 'No Author')
   */
  function trySetting(s, def) {
    s = getSetting(s);
    if (!s || s.trim() === '') { return def; }
    return s;
  }

  /**
   * Loads the basemap and adds it to the map
   */
  function addBaseMap() {
    var basemap = trySetting('_tileProvider', 'Stamen.TonerLite');
    L.tileLayer.provider(basemap, {
      maxZoom: 18
    }).addTo(map);
  }

  function initMap(options, chapters) {
    createDocumentSettings(options);

    var chapterContainerMargin = 70;

    document.title = getSetting('_mapTitle');
    $('#header').append('
' + getSetting('_mapTitle') + '
');
    $('#header').append('
' + getSetting('_mapSubtitle') + '
');

    // Add logo
    if (getSetting('_mapLogo')) {
      $('#logo').append('');
      $('#top').css('height', '60px');
    } else {
      $('#logo').css('display', 'none');
      $('#header').css('padding-top', '25px');
    }

    // Load tiles
    addBaseMap();

    // Add zoom controls if needed
    if (getSetting('_zoomControls') !== 'off') {
      L.control.zoom({
        position: getSetting('_zoomControls')
      }).addTo(map);
    }

    var markers = [];

    var markActiveColor = function(k) {
      /* Removes marker-active class from all markers */
      for (var i = 0; i < markers.length; i++) {
        if (markers[i] && markers[i]._icon) {
          markers[i]._icon.className = markers[i]._icon.className.replace(' marker-active', '');

          if (i == k) {
            /* Adds marker-active class, which is orange, to marker k */
            markers[k]._icon.className += ' marker-active';
          }
        }
      }
    }

    var pixelsAbove = [];
    var chapterCount = 0;

    var currentlyInFocus; // integer to specify each chapter is currently in focus
    var overlay;  // URL of the overlay for in-focus chapter
    var geoJsonOverlay;

    for (i in chapters) {
      var c = chapters[i];

      if ( !isNaN(parseFloat(c['Latitude'])) && !isNaN(parseFloat(c['Longitude']))) {
        var lat = parseFloat(c['Latitude']);
        var lon = parseFloat(c['Longitude']);

        chapterCount += 1;

        markers.push(
          L.marker([lat, lon], {
            icon: L.ExtraMarkers.icon({
              icon: 'fa-number',
              number: c['Marker'] === 'Plain' ? '' : chapterCount,
              markerColor: c['Marker Color'] || 'blue'
            }),
            opacity: c['Marker'] === 'Hidden' ? 0 : 0.9,
            interactive: c['Marker'] === 'Hidden' ? false : true,
          }
        ));

      } else {
        markers.push(null);
      }

      // Add chapter container
      var container = $('

', {
        id: 'container' + i,
        class: 'chapter-container'
      });

      // Add media and credits: YouTube, audio, or image
      var media = null;
      var mediaContainer = null;

      // Add media source
      var source = '';
      if (c['Media Credit Link']) {
        source = $('', {
          text: c['Media Credit'],
          href: c['Media Credit Link'],
          target: "_blank",
          class: 'source'
        });
      } else {
        source = $('', {
          text: c['Media Credit'],
          class: 'source'
        });
      }

      // YouTube
      if (c['Media Link'] && c['Media Link'].indexOf('youtube.com/') > -1) {
        media = $('', {
          src: c['Media Link'],
          width: '100%',
          height: '100%',
          frameborder: '0',
          allow: 'autoplay; encrypted-media',
          allowfullscreen: 'allowfullscreen',
        });

        mediaContainer = $('
</div', {
          class: 'img-container'
        }).append(media).after(source);
      }

      // If not YouTube: either audio or image
      var mediaTypes = {
        'jpg': 'img',
        'jpeg': 'img',
        'png': 'img',
        'mp3': 'audio',
        'ogg': 'audio',
        'wav': 'audio',
      }

      var mediaExt = c['Media Link'].split('.').pop().toLowerCase();
      var mediaType = mediaTypes[mediaExt];

      if (mediaType) {
        media = $('<' + mediaType + '>', {
          src: c['Media Link'],
          controls: mediaType == 'audio' ? 'controls' : '',
        });

        mediaContainer = $('
 0) {
      $('.img-container').css({
        'height': imgContainerHeight + 'px',
        'max-height': imgContainerHeight + 'px',
      });
    }

    // For each block (chapter), calculate how many pixels above it
    pixelsAbove[0] = -100;
    for (i = 1; i < chapters.length; i++) {
      pixelsAbove[i] = pixelsAbove[i-1] + $('div#container' + (i-1)).height() + chapterContainerMargin;
    }
    pixelsAbove.push(Number.MAX_VALUE);

    $('div#contents').scroll(function() {
      var currentPosition = $(this).scrollTop();

      // Make title disappear on scroll
      if (currentPosition < 200) {
        $('#title').css('opacity', 1 - Math.min(1, currentPosition / 100));
      }

      for (var i = 0; i < pixelsAbove.length - 1; i++) {

        if ( currentPosition >= pixelsAbove[i]
          && currentPosition < (pixelsAbove[i+1] - 2 * chapterContainerMargin)
          && currentlyInFocus != i
        ) {
          // Remove styling for the old in-focus chapter and
          // add it to the new active chapter
          $('.chapter-container').removeClass("in-focus").addClass("out-focus");
          $('div#container' + i).addClass("in-focus").removeClass("out-focus");

          currentlyInFocus = i;
          markActiveColor(currentlyInFocus);

          // Remove overlay tile layer if needed
          if (map.hasLayer(overlay)) {
            map.removeLayer(overlay);
          }

          // Remove GeoJson Overlay tile layer if needed
          if (map.hasLayer(geoJsonOverlay)) {
            map.removeLayer(geoJsonOverlay);
          }

          var c = chapters[i];

          // Add chapter's overlay tiles if specified in options
          if (c['Overlay']) {
            var opacity = (c['Overlay Transparency'] !== '') ? parseFloat(c['Overlay Transparency']) : 1;
            var url = c['Overlay'];

            if (url.split('.').pop() == 'geojson') {
              $.getJSON(url, function(geojson) {
                overlay = L.geoJson(geojson, {
                  style: function(feature) {
                    return {
                      fillColor: feature.properties.COLOR,
                      weight: 1,
                      opacity: 0.5,
                      color: feature.properties.COLOR,
                      fillOpacity: 0.5,
                    }
                  }
                }).addTo(map);
              });
            } else {
              overlay = L.tileLayer(c['Overlay'], {opacity: opacity}).addTo(map);
            }

          }

          if (c['GeoJSON Overlay']) {
            $.getJSON(c['GeoJSON Overlay'], function(geojson) {

              // Parse properties string into a JS object
              var props = {};

              if (c['GeoJSON Feature Properties']) {
                var propsArray = c['GeoJSON Feature Properties'].split(';');
                var props = {};
                for (var p in propsArray) {
                  if (propsArray[p].split(':').length === 2) {
                    props[ propsArray[p].split(':')[0].trim() ] = propsArray[p].split(':')[1].trim();
                  }
                }
              }

              geoJsonOverlay = L.geoJson(geojson, {
                style: function(feature) {
                  return {
                    fillColor: feature.properties.COLOR || props.fillColor || 'white',
                    weight: props.weight || 1,
                    opacity: props.opacity || 0.5,
                    color: feature.properties.COLOR || props.color || 'silver',
                    fillOpacity: props.fillOpacity || 0.5,
                  }
                }
              }).addTo(map);
            });
          }

          // Fly to the new marker destination if latitude and longitude exist
          if (c['Latitude'] && c['Longitude']) {
            var zoom = c['Zoom'] ? c['Zoom'] : CHAPTER_ZOOM;
            map.flyTo([c['Latitude'], c['Longitude']], zoom);
          }

          // No need to iterate through the following chapters
          break;
        }
      }
    });

    $('#contents').append(" \

 \
          \

 \
          Top  \
         \

 \
    ");

    /* Generate a CSS sheet with cosmetic changes */
    $("


<commit_before>// Copyright (c) E5R Development Team. All rights reserved.
// Licensed under the MIT License. See LICENSE file for license information.

(function(){ 'use strict'
  var _shell = new ActiveXObject("WScript.Shell"),
      _network = new ActiveXObject("WScript.Network"),
      _drivers = _network.EnumNetworkDrives(),
      _printers = _network.EnumPrinterConnections(),
      _consts = {
        ENVTYPE_PROCESS: 'PROCESS',
        ENVTYPE_USER: 'USER',
        ENVTYPE_SYSTEM: 'SYSTEM'
      },
      _host = {
        name: WScript.Name,
        version: WScript.Version,
        build: WScript.BuildVersion,
        execPath: WScript.FullName,
        execDirectory: WScript.Path
      },
      _script = {
        name: WScript.ScriptName,
        file: WScript.ScriptFullName,
        directory: _shell.CurrentDirectory
      },
      _net = {
        domain: _network.UserDomain,
        user: _network.UserName,
        computer: _network.ComputerName,
        drives: (function(){
          var _result = [];
          for(i = 0; i < _drivers.length; i += 2) {
            _result.push({drive: _drivers.Item(i), path: _drivers.Item(i+1)});
          }
          return _result;
        })(),
        printers: (function(){
          var _result = [];
          for(i = 0; i < _printers.length; i += 2) {
            _result.push({id: _printers.Item(i), name: _printers.Item(i+1)});
          }
          return _result;
        })()
      };

  /**
   * Get a maker for environment variables of process data
   *
   * @return function
   */
  function _getterProcessEnv(){
    return _shell.Environment(_consts.ENVTYPE_PROCESS);
  }

  /**
   * Get a maker for environment variables of user data
   *
   * @return function
   */
  function _getterUserEnvironment(){
    return _shell.Environment(_consts.ENVTYPE_USER);
  }

  /**
   * Get a maker for environment variables of system data
   *
   * @return function
   */
  function _getterSystemEnvironment(){
    return _shell.Environment(_consts.ENVTYPE_SYSTEM);
  }

  /**
   * Awaits X miliseconds
   *
   * @param {int} miliseconds Number of miliseconds to wait
   */
  function _sleep(miliseconds){
    WScript.Sleep(miliseconds);
  }

  /**
   * Build a string, expandding a environment variable names.
   *
   * @param {string} envString  String to expand
   *
   * @return New string with environment variable expandded.
   */
  function _buildEnvirementString(envString){
    return _shell.ExpandEnvironmentStrings(envString);
  }

  /**
   * Get a environment variable value.
   *
   * @param {string}  varName   Name of environment variable
   * @param {ENVTYPE} envType   Type of environment
   *
   * @return Value os variable or empty string if not found.
   */
  function _getEnvironment(varName, envType){
    var _getEnv,
        envType = envType || _consts.ENVTYPE_PROCESS;
    if(envType == _consts.ENVTYPE_SYSTEM){
      _getEnv = _getterSystemEnvironment();
    }else if(envType == _consts.ENVTYPE_USER){
      _getEnv = _getterUserEnvironment();
    }else{
      _getEnv = _getterProcessEnv();
    }
    return _getEnv(varName);
  }

  /**
   * Set a environment variable value.
   *
   * @param {string}  varName   Name of environment variable
   * @param {string}  varValue  Value os environment variable
   * @param {ENVTYPE} envType   Type of environment
   *
   * @return Value os variable or empty string if not found.
   */
  function _setEnvironment(varName, varValue, envType){
    var _getEnv,
        envType = envType || _consts.ENVTYPE_PROCESS;
    if(envType == _consts.ENVTYPE_SYSTEM){
      _getEnv = _getterSystemEnvironment();
    }else if(envType == _consts.ENVTYPE_USER){
      _getEnv = _getterUserEnvironment();
    }else{
      _getEnv = _getterProcessEnv();
    }
    _getEnv(varName) = varValue;
    return _getEnv(varName);
  }

  module.exports = {
    CONST: _consts,
    stdErr: WScript.StdErr,
    stdIn: WScript.StdIn,
    stdOut: WScript.StdOut,

    host: _host,
    script: _script,
    net: _net,

    sleep: _sleep,
    buildEnvString: _buildEnvirementString,
    getEnvironment: _getEnvironment,
    setEnvironment: _setEnvironment
  }
})();
<commit_msg>Added cmdActions for sysutils.js<commit_after>// Copyright (c) E5R Development Team. All rights reserved.
// Licensed under the MIT License. See LICENSE file for license information.

(function(){ 'use strict'
  var _shell = new ActiveXObject("WScript.Shell"),
      _network = new ActiveXObject("WScript.Network"),
      _drivers = _network.EnumNetworkDrives(),
      _printers = _network.EnumPrinterConnections(),
      _consts = {
        ENVTYPE_PROCESS: 'PROCESS',
        ENVTYPE_USER: 'USER',
        ENVTYPE_SYSTEM: 'SYSTEM'
      },
      _host = {
        name: WScript.Name,
        version: WScript.Version,
        build: WScript.BuildVersion,
        execPath: WScript.FullName,
        execDirectory: WScript.Path
      },
      _script = {
        name: WScript.ScriptName,
        file: WScript.ScriptFullName,
        directory: _shell.CurrentDirectory
      },
      _net = {
        domain: _network.UserDomain,
        user: _network.UserName,
        computer: _network.ComputerName,
        drives: (function(){
          var _result = [];
          for(i = 0; i < _drivers.length; i += 2) {
            _result.push({drive: _drivers.Item(i), path: _drivers.Item(i+1)});
          }
          return _result;
        })(),
        printers: (function(){
          var _result = [];
          for(i = 0; i < _printers.length; i += 2) {
            _result.push({id: _printers.Item(i), name: _printers.Item(i+1)});
          }
          return _result;
        })()
      };

  /**
   * Get a maker for environment variables of process data
   *
   * @return function
   */
  function _getterProcessEnv(){
    return _shell.Environment(_consts.ENVTYPE_PROCESS);
  }

  /**
   * Get a maker for environment variables of user data
   *
   * @return function
   */
  function _getterUserEnvironment(){
    return _shell.Environment(_consts.ENVTYPE_USER);
  }

  /**
   * Get a maker for environment variables of system data
   *
   * @return function
   */
  function _getterSystemEnvironment(){
    return _shell.Environment(_consts.ENVTYPE_SYSTEM);
  }

  /**
   * Awaits X miliseconds
   *
   * @param {int} miliseconds Number of miliseconds to wait
   */
  function _sleep(miliseconds){
    WScript.Sleep(miliseconds);
  }

  /**
   * Build a string, expandding a environment variable names.
   *
   * @param {string} envString  String to expand
   *
   * @return New string with environment variable expandded.
   */
  function _buildEnvirementString(envString){
    return _shell.ExpandEnvironmentStrings(envString);
  }

  /**
   * Get a environment variable value.
   *
   * @param {string}  varName   Name of environment variable
   * @param {ENVTYPE} envType   Type of environment
   *
   * @return Value os variable or empty string if not found.
   */
  function _getEnvironment(varName, envType){
    var _getEnv,
        envType = envType || _consts.ENVTYPE_PROCESS;
    if(envType == _consts.ENVTYPE_SYSTEM){
      _getEnv = _getterSystemEnvironment();
    }else if(envType == _consts.ENVTYPE_USER){
      _getEnv = _getterUserEnvironment();
    }else{
      _getEnv = _getterProcessEnv();
    }
    return _getEnv(varName);
  }

  /**
   * Set a environment variable value.
   *
   * @param {string}  varName   Name of environment variable
   * @param {string}  varValue  Value os environment variable
   * @param {ENVTYPE} envType   Type of environment
   *
   * @return Value os variable or empty string if not found.
   */
  function _setEnvironment(varName, varValue, envType){
    var _getEnv,
        envType = envType || _consts.ENVTYPE_PROCESS;
    if(envType == _consts.ENVTYPE_SYSTEM){
      _getEnv = _getterSystemEnvironment();
    }else if(envType == _consts.ENVTYPE_USER){
      _getEnv = _getterUserEnvironment();
    }else{
      _getEnv = _getterProcessEnv();
    }
    _getEnv(varName) = varValue;
    return _getEnv(varName);
  }

  /**
   * Checks if @v is a valid Array object
   */
  function _isArray(v){
    return (v.length && typeof v != 'string');
  }

  /**
   * Checks if @a contains @v
   */
  function _inArray(a,v){
    for(var i in a){
      if(a[i] == v) return true;
    }
    return false;
  }

  /**
   * Create a command actions runner descriptor.
   *
   * @param {array} options   List of options arguments
   *
   * Ex1:    ['value|version|v','value|path|p']
   *
   * Usage: e5r <cmd> -v 1.0       | e5r <cmd> --version 1.0
   *        e5r <cmd> -p "path/to" | e5r <cmd> --path "path/to"
   *
   *        {version:'1.0', path:null}
   *        {version:null, path:'path/to'}
   *
   * Ex2:   ['switch|test|t,T']
   *
   * Usage: e5r <cmd> --test --other "value param"
   *        e5r <cmd> --other "value param"
   *        e5r <cmd> --T
   *
   *        {test: true, other:'value param'}
   *        {test: false, other:'value param'}
   *        {test: true, other:null}
   *
   * @param {array} actions   List of actions to execute
   *
   * @return Command Action Descriptor object
   */
  function _createCmdActionsDescriptor(options, actions){
    if(!_isArray(options)){
      throw new Error('<sysutils._createCmdActionsDescriptor> #ArgumentException: @options must be an Array.');
    }
    if(!_isArray(actions)){
      throw new Error('<sysutils._createCmdActionsDescriptor> #ArgumentException: @actions must be an Array.');
    }
    for(var a in actions){
      if(!_isArray(actions[a])){
        throw new Error('<sysutils._createCmdActionsDescriptor> #ArgumentException: @actions['+a+'] must be an Array.');
      }
    }

    return {
      __options: options,
      __actions: actions,

      __getAction: function(act){
        for(var _act in this.__actions){
          var _actObj = this.__actions[_act],
              _actName = _actObj.length > 1 ? (_actObj[0]||null) : '',
              _actCbck = _actObj.length > 1 ? (_actObj[1]||null) : (_actObj[0]||null);
          if(typeof _actName != 'string' || typeof _actCbck != 'function')
            throw new Error('<sysutils._createCmdActionsDescriptor.__getAction>: Invalid action signature.');
          if(_actName === act)
            return _actCbck;
        }
        return null;
      },

      __describe: function(n){
        for(var o in this.__options){
          var oArray = this.__options[o].split('|'),
              oType = (oArray[0]||null),
              oName = (oArray[1]||null),
              oAliases = (oArray[2]||'').split(','),
              _n = '',
              _isNamed = false;
          if(oType == null || oName == null){
            throw new Error('<sysutils._createCmdActionsDescriptor.__describe>: Invalid option syntax.');
          }
          for(var c = 0; c < n.length; c++){
            if(c < 2 && n.charAt(c)=='-') continue;
            _n += n.charAt(c);
          }
          _isNamed = (n.length == 2 && n.charAt(0) == '-' && n.charAt(1) != '-') ||
                     (n.length > 2 && n.charAt(0) == '-' && n.charAt(1) == '-');
          if(!_isNamed) continue;
          if(oName == _n || _inArray(oAliases,_n)){
            return {
              type: oType.toLowerCase(),
              name: oName
            }
          }
        }
        return null;
      },

      run:function(args){
        var _opts = {},
            _args = [],
            _pIdx = 0;
        // Options informed in params
        while(_pIdx < args.length){
          var param = args[_pIdx],
              nextParam = (args[_pIdx+1]||null),
              descriptor = this.__describe(param);
          if(!descriptor){
            _args.push(param);
            _pIdx++;
            continue;
          }
          if(descriptor.type == 'switch'){
            _opts[descriptor.name] = true;
          }
          if(descriptor.type == 'value'){
            if(nextParam != null && nextParam.charAt(0) != '-'){
              _opts[descriptor.name] = nextParam;
              _pIdx++;
            }else{
              _opts[descriptor.name] = null;
            }
          }
          _pIdx++;
        }
        // Not informed options
        for(var _iO in this.__options){
          var _optObj = this.__options[_iO].split('|'),
              _optType = _optObj[0]||null,
              _optName = _optObj[1]||null;
          if(!(_optName in _opts))
            _opts[_optName] = _optType == 'switch' ? false : null;
        }
        // Detect action name and others parameters
        var _actionName = _args[0]||'',
            _actionCallback = this.__getAction(_actionName),
            _newArgs = [];
        if(_actionName) for(var a = 1; a < _args.length;a++)
          _newArgs.push(_args[a]);

        if(typeof _actionCallback == 'function')
          _actionCallback(_opts, _newArgs);
      }
    }
  }

  module.exports = {
    CONST: _consts,
    stdErr: WScript.StdErr,
    stdIn: WScript.StdIn,
    stdOut: WScript.StdOut,

    host: _host,
    script: _script,
    net: _net,

    sleep: _sleep,
    buildEnvString: _buildEnvirementString,
    getEnvironment: _getEnvironment,
    setEnvironment: _setEnvironment,
    cmdActions: _createCmdActionsDescriptor
  }
})();
<|endoftext|>


<commit_before>#!/usr/bin/env node
"use strict";
var promiseutil = require("../lib/promiseutil");
var nopt = require("nopt");
var request = require("request");
var joinUrl = require("url").resolve;

var opts = nopt({
    "room-alias": String,
    "appservice-token": String,
    "msg": String,
    "url": String,
    "help": Boolean
}, {
    "h": "--help"
});

if (!opts["room-alias"] || !opts["appservice-token"] || !opts.url) {
    opts.help = true;
}

if (opts.help) {
    console.log("Unbridge a dynamically created room with an alias");
    console.log();
    console.log("This will DELETE the alias->room_id mapping and make the room's");
    console.log("join_rules: invite. You can optionally send a message in this");
    console.log("room to tell users that they should re-join via the alias.");
    console.log("This script does NOT create a new room. It relies on the AS to");
    console.log("do this via onAliasQuery pokes.");
    console.log();
    console.log("Usage:");
    console.log("unbridge.js --url BASEURL --room-alias ALIAS --appservice-token TOKEN [--admin-token TOKEN] [--msg MESSAGE]");
    console.log();
    console.log("--url               The home server base URL e.g. https://matrix.org");
    console.log("--room-alias        The alias to unbridge.");
    console.log("--appservice-token  The AS token. Used to set join_rules in the room.");
    console.log("--msg               Optional. The message to send to the room.");
    process.exit(0);
    return;
}

// admin token is always the AS token since it's "exclusively" claimed.
main(
    opts.url, opts["room-alias"], opts["appservice-token"], opts["appservice-token"],
    opts.msg
);

function main(url, alias, astoken, admintoken, msg) {
    console.log("Unbridging %s", alias);
    var roomId = null;
    getIdForAlias(url, alias).then(function(id) {
        roomId = id;
        if (!id) {
            panic("No room ID!");
        }
        console.log("Resolved %s to %s", alias, id);
        return deleteAlias(url, alias, admintoken);
    }).then(function() {
        console.log("Deleted alias %s", alias);
        return setInviteJoinRules(url, roomId, astoken);
    }).then(function() {
        console.log("Made room %s join_rules: invite", roomId);
        return sendMessage(url, roomId, astoken, msg);
    }).done(function() {
        console.log("Sent optional message.");
        console.log("Success.");
    }, panic);
}

function getIdForAlias(url, alias) {
    var d = promiseutil.defer();
    var encAlias = encodeURIComponent(alias);
    request.get(
        joinUrl(url, "/_matrix/client/api/v1/directory/room/" + encAlias),
        function(err, res, body) {
            if (isRejected(d, err, res, "Failed to get ID for alias")) {
                return;
            }
            try {
                d.resolve(JSON.parse(body).room_id);
            }
            catch (e) {
                d.reject(e);
            }
        }
    );
    return d.promise;
}

function deleteAlias(url, alias, token) {
    var d = promiseutil.defer();
    var encAlias = encodeURIComponent(alias);
    request({
        url: joinUrl(
            url, "/_matrix/client/api/v1/directory/room/" + encAlias
        ) + "?access_token=" + token,
        method: "DELETE"
    }, function(err, res, body) {
        if (isRejected(d, err, res, "Failed to delete room alias")) {
            return;
        }
        d.resolve();
    });
    return d.promise;
}

function setInviteJoinRules(url, roomId, token) {
    var d = promiseutil.defer();
    var encRoomId = encodeURIComponent(roomId);
    request({
        url: joinUrl(
            url, "/_matrix/client/api/v1/rooms/" + encRoomId + "/state/m.room.join_rules"
        ) + "?access_token=" + token,
        body: {
            join_rule: "invite"
        },
        json: true,
        method: "PUT"
    }, function(err, res, body) {
        if (isRejected(d, err, res, "Failed to set invite join_rules")) {
            return;
        }
        d.resolve();
    });
    return d.promise;
}

function sendMessage(url, roomId, token, msg) {
    if (!msg) {
        console.log("No message provided. Skipping.");
        return q();
    }
    var d = promiseutil.defer();
    var encRoomId = encodeURIComponent(roomId);
    request({
        url: joinUrl(
            url, "/_matrix/client/api/v1/rooms/" + encRoomId + "/send/m.room.message"
        ) + "?access_token=" + token,
        body: {
            msgtype: "m.notice",
            body: "" + msg // typecast
        },
        json: true,
        method: "POST"
    }, function(err, res, body) {
        if (isRejected(d, err, res, "Failed to send message")) {
            return;
        }
        d.resolve();
    });
    return d.promise;
}

function isRejected(defer, err, res, msg) {
    if (err) {
        defer.reject(err);
        return true;
    }
    else if (res.statusCode >= 300) {
        console.error(res.body);
        defer.reject(new Error("HTTP Status: " + res.statusCode + " : " + msg));
        return true;
    }
    return false;
}

function panic(err) {
    console.error(err.stack);
    console.log("FAILED");
    console.error(err);
    process.exit(1);
}
<commit_msg>Fix invalid call to q() when not sending a message<commit_after>#!/usr/bin/env node
"use strict";
var promiseutil = require("../lib/promiseutil");
var nopt = require("nopt");
var request = require("request");
var joinUrl = require("url").resolve;

var opts = nopt({
    "room-alias": String,
    "appservice-token": String,
    "msg": String,
    "url": String,
    "help": Boolean
}, {
    "h": "--help"
});

if (!opts["room-alias"] || !opts["appservice-token"] || !opts.url) {
    opts.help = true;
}

if (opts.help) {
    console.log("Unbridge a dynamically created room with an alias");
    console.log();
    console.log("This will DELETE the alias->room_id mapping and make the room's");
    console.log("join_rules: invite. You can optionally send a message in this");
    console.log("room to tell users that they should re-join via the alias.");
    console.log("This script does NOT create a new room. It relies on the AS to");
    console.log("do this via onAliasQuery pokes.");
    console.log();
    console.log("Usage:");
    console.log("unbridge.js --url BASEURL --room-alias ALIAS --appservice-token TOKEN [--admin-token TOKEN] [--msg MESSAGE]");
    console.log();
    console.log("--url               The home server base URL e.g. https://matrix.org");
    console.log("--room-alias        The alias to unbridge.");
    console.log("--appservice-token  The AS token. Used to set join_rules in the room.");
    console.log("--msg               Optional. The message to send to the room.");
    process.exit(0);
    return;
}

// admin token is always the AS token since it's "exclusively" claimed.
main(
    opts.url, opts["room-alias"], opts["appservice-token"], opts["appservice-token"],
    opts.msg
);

function main(url, alias, astoken, admintoken, msg) {
    console.log("Unbridging %s", alias);
    var roomId = null;
    getIdForAlias(url, alias).then(function(id) {
        roomId = id;
        if (!id) {
            panic("No room ID!");
        }
        console.log("Resolved %s to %s", alias, id);
        return deleteAlias(url, alias, admintoken);
    }).then(function() {
        console.log("Deleted alias %s", alias);
        return setInviteJoinRules(url, roomId, astoken);
    }).then(function() {
        console.log("Made room %s join_rules: invite", roomId);
        return sendMessage(url, roomId, astoken, msg);
    }).done(function() {
        console.log("Sent optional message.");
        console.log("Success.");
    }, panic);
}

function getIdForAlias(url, alias) {
    var d = promiseutil.defer();
    var encAlias = encodeURIComponent(alias);
    request.get(
        joinUrl(url, "/_matrix/client/api/v1/directory/room/" + encAlias),
        function(err, res, body) {
            if (isRejected(d, err, res, "Failed to get ID for alias")) {
                return;
            }
            try {
                d.resolve(JSON.parse(body).room_id);
            }
            catch (e) {
                d.reject(e);
            }
        }
    );
    return d.promise;
}

function deleteAlias(url, alias, token) {
    var d = promiseutil.defer();
    var encAlias = encodeURIComponent(alias);
    request({
        url: joinUrl(
            url, "/_matrix/client/api/v1/directory/room/" + encAlias
        ) + "?access_token=" + token,
        method: "DELETE"
    }, function(err, res, body) {
        if (isRejected(d, err, res, "Failed to delete room alias")) {
            return;
        }
        d.resolve();
    });
    return d.promise;
}

function setInviteJoinRules(url, roomId, token) {
    var d = promiseutil.defer();
    var encRoomId = encodeURIComponent(roomId);
    request({
        url: joinUrl(
            url, "/_matrix/client/api/v1/rooms/" + encRoomId + "/state/m.room.join_rules"
        ) + "?access_token=" + token,
        body: {
            join_rule: "invite"
        },
        json: true,
        method: "PUT"
    }, function(err, res, body) {
        if (isRejected(d, err, res, "Failed to set invite join_rules")) {
            return;
        }
        d.resolve();
    });
    return d.promise;
}

function sendMessage(url, roomId, token, msg) {
    var d = promiseutil.defer();
    if (!msg) {
        console.log("No message provided. Skipping.");
        d.resolve();
        return d.promise
    }
    var encRoomId = encodeURIComponent(roomId);
    request({
        url: joinUrl(
            url, "/_matrix/client/api/v1/rooms/" + encRoomId + "/send/m.room.message"
        ) + "?access_token=" + token,
        body: {
            msgtype: "m.notice",
            body: "" + msg // typecast
        },
        json: true,
        method: "POST"
    }, function(err, res, body) {
        if (isRejected(d, err, res, "Failed to send message")) {
            return;
        }
        d.resolve();
    });
    return d.promise;
}

function isRejected(defer, err, res, msg) {
    if (err) {
        defer.reject(err);
        return true;
    }
    else if (res.statusCode >= 300) {
        console.error(res.body);
        defer.reject(new Error("HTTP Status: " + res.statusCode + " : " + msg));
        return true;
    }
    return false;
}

function panic(err) {
    console.error(err.stack);
    console.log("FAILED");
    console.error(err);
    process.exit(1);
}
<|endoftext|>


<commit_before>var dict_descriptions=new Array();var image_path="//static.pardus.at/img/std/";function getElement(a){return document.getElementById(a)}function writeContents(a,b){a.innerHTML=b}function getOffset(b,c){var a=b["offset"+c];if(c=="Top"){a+=b.offsetHeight}b=b.offsetParent;while(b!=null){a+=b["offset"+c];b=b.offsetParent}return a}function setPosition(a,c,b){xOffset=15;yOffset=-13;if(b=="l"){xOffset+=-220}else{if(b=="er"){xOffset+=128}else{if(b=="lf"){xOffset+=-160;yOffset+=-310}}}a.style.top=getOffset(c,"Top")+yOffset+"px";a.style.left=getOffset(c,"Left")+xOffset+"px"}function setVisibility(b,a){if(a){b.style.visibility="visible"}else{b.style.visibility="hidden"}}function getDescriptionFor(a){if(typeof(dict_descriptions[a])=="undefined"){return"Sorry, no description ("+a+") available."}return dict_descriptions[a]}function getContent(c,a,d){var b;var e;if(d=="portal"){e="Pardus Information Box"}else{e="GNN Library"}b="

		"+a+"

   	"+getDescriptionFor(c)+"

   	<spacer type='block' width='1' height='1'>

   	"+e+"

";return b}function displayContents(b,f,a,e,g){var c=getElement("tipBox");var d=getContent(f,a,g);writeContents(c,d);setPosition(c,b,e);setVisibility(c,true)}function tip(b,d,a,c,e){d=unescape(d);if(typeof(dict_descriptions)=="undefined"){return}displayContents(b,d,a,c,e)}function nukeTip(){setVisibility(getElement("tipBox"),false)};<commit_msg>some unminimization<commit_after>var dict_descriptions=new Array();
var image_path="//static.pardus.at/img/std/";

function getElement(a){
	return document.getElementById(a)
	}

function writeContents(a,b){
	a.innerHTML=b
}
function getOffset(b,c){
	var a=b["offset"+c];
	if(c=="Top"){
		a+=b.offsetHeight
		}
	b=b.offsetParent;
	while(b!=null){
		a+=b["offset"+c];
		b=b.offsetParent
		}
	return a
}
function setPosition(a,c,b){
	xOffset=15;
	yOffset=-13;
	if(b=="l"){
		xOffset+=-220
	} else {
		if (b=="er"){
			xOffset+=128
		} else {
			if(b=="lf"){
				xOffset+=-160;
				yOffset+=-310
			}
		}
	}
	a.style.top=getOffset(c,"Top")+yOffset+"px";
	a.style.left=getOffset(c,"Left")+xOffset+"px"
}

function setVisibility(b,a){
	if(a){
		b.style.visibility="visible"
	}else{
		b.style.visibility="hidden"
	}
}
function getDescriptionFor(a){
	if(typeof(dict_descriptions[a])=="undefined"){
		return"Sorry, no description ("+a+") available."
	}
	return dict_descriptions[a]
}
function getContent(c,a,d){
	var b;var e;
	if(d=="portal"){
		e="Pardus Information Box"
	}else{
		e="GNN Library"
	}
	b="

		"+a+"

   	"+getDescriptionFor(c)+"

   	<spacer type='block' width='1' height='1'>

   	"+e+"

";
	return b
}
function displayContents(b,f,a,e,g){
	var c=getElement("tipBox");
	var d=getContent(f,a,g);
	writeContents(c,d);
	setPosition(c,b,e);
	setVisibility(c,true)
}
function tip(b,d,a,c,e){
	d=unescape(d);
	if(typeof(dict_descriptions)=="undefined"){
		return
	}
	displayContents(b,d,a,c,e)
}
function nukeTip(){
	setVisibility(getElement("tipBox"),false)
};<|endoftext|>


<commit_before>
var NodeCrypto = require('crypto');
var subtle = require("subtle")

var Subtle_Options = {
  HMAC: {
    name: "HMAC"
    , hash: {name: "SHA-256"}
    , usage: ["sign","verify"]
    , extractable : false
  }

                }
exports.id = '1a';

// env-specific crypto methods
exports.crypt = function(ecc,aes)
{
  NodeCrypto.ecc = ecc;
  NodeCrypto.aes = aes;
}

exports.generate = function(cb)
{

  try {
    var k = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1);
  }catch(E){
    return cb(E);
  }
  cb(null, {key:k.PublicKey, secret:k.PrivateKey});
}

exports._Local = function(pair){
  var local = new exports.Local(pair)

  this.token = local.token
  this.decrypt = function(body){
    return Promise.resolve(local.decrypt(body))
  }

  return Promise.resolve(this)
}

exports._Remote = function(key){
  var remote = new exports.Remote(key)
  this.token = remote.token
  this.encrypt = function(a1, a2){
    return Promise.resolve(remote.encrypt(a1, a2))
  }

  this.verify  = remote._verify;

  return Promise.resolve(this)
}

exports._Ephemeral = function(remote, body){
  var ephemeral = new exports.Ephemeral(remote, body)
  this.token = ephemeral.token
  this.encrypt = function(body){
    return Promise.resolve(ephemeral.encrypt(body))
  }

  this.decrypt = function(body){
    return Promise.resolve(ephemeral.decrypt(body))
  }

  return Promise.resolve(this)
}

exports.Local = function(pair)
{
  var self = this;
  try{
    self.key = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, pair.key, true);
    self.secret = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, pair.secret);
    if(self.key.PublicKey.toString() != pair.key.toString()) throw new Error('invalid public key data');
    if(self.secret.PrivateKey.toString() != pair.secret.toString()) throw new Error('invalid secret key data');
  }catch(E){
    self.err = E;
  }

  // decrypt message body and return the inner
  self.decrypt = function(body){
    if(!Buffer.isBuffer(body)) return false;
    if(body.length < 21+4+4) return false;

    var keybuf = body.slice(0,21);
    var iv = body.slice(21,21+4);
    var innerc = body.slice(21+4,body.length-4);
    // mac is handled during verify stage

    try{
      var ephemeral = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, keybuf, true);
      var secret = self.secret.deriveSharedSecret(ephemeral);
    }catch(E){
      return false;
    }

    var key = fold(1,NodeCrypto.createHash("sha256").update(secret).digest());
    var ivz = new Buffer(12);
    ivz.fill(0);

    // aes-128 decipher the inner
    try{
      var inner = NodeCrypto.aes(false, key, Buffer.concat([iv,ivz]), innerc);
    }catch(E){
      return false;
    }

    return inner;
  };
}

exports.Remote = function(key)
{
  var self = this;
  try{
    self.endpoint = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, key, true);
    self.ephemeral = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1);
    self.token = NodeCrypto.createHash('sha256').update(self.ephemeral.PublicKey.slice(0,16)).digest().slice(0,16);
    self.seq = NodeCrypto.randomBytes(4).readUInt32LE(0); // start from random place
  }catch(E){
    self.err = E;
  }

  self._verify = function(local, body){

      if(!Buffer.isBuffer(body))
        return Promise.reject(new Error("CS1a verify: 2nd argument not a Buffer"));

      // derive shared secret from both identity keys
      var secret = local.secret.deriveSharedSecret(self.endpoint);

      // hmac key is the secret and seq bytes combined to make it unique each time
      var iv = body.slice(21,21+4);
    return subtle.importKey("raw",Buffer.concat([secret,iv]),Subtle_Options.HMAC,  true, Subtle_Options.HMAC.usage)
          .then(function(key){
            console.log("HMAC KEY", key)
            return subtle.sign({name:"HMAC"}, key, body.slice(0,body.length-4))
          })
          .then(function(sig){
            var mac = fold(3,new Buffer(new Uint8Array(sig)));
            var passed = (mac.toString('hex') === body.slice(body.length-4).toString('hex'));
            return (passed) ? true : Promise.reject();
          })
  }

  // verifies the hmac on an incoming message body
  self.verify = function(local, body){
    if(!Buffer.isBuffer(body)) return false;

    // derive shared secret from both identity keys
    var secret = local.secret.deriveSharedSecret(self.endpoint);

    // hmac key is the secret and seq bytes combined to make it unique each time
    var iv = body.slice(21,21+4);
    var dig = NodeCrypto.createHmac("sha256", Buffer.concat([secret,iv])).update(body.slice(0,body.length-4)).digest();
    var mac = fold(3,dig)

    if(mac.toString('hex') != body.slice(body.length-4).toString('hex')) return false;

    return true;
  };

  self.encrypt = function(local, inner){
    if(!Buffer.isBuffer(inner)) return false;

    // get the shared secret to create the iv+key for the open aes
    try{
      var secret = self.ephemeral.deriveSharedSecret(self.endpoint);
    }catch(E){
      return false;
    }
    var key = fold(1,NodeCrypto.createHash("sha256").update(secret).digest());
    var iv = new Buffer(4);
    iv.writeUInt32LE(self.seq++,0);
    var ivz = new Buffer(12);
    ivz.fill(0);

    // encrypt the inner
    try{
      var innerc = NodeCrypto.aes(true, key, Buffer.concat([iv,ivz]), inner);
      var macsecret = local.secret.deriveSharedSecret(self.endpoint);
    }catch(E){
      return false;
    }

    // prepend the key and hmac it
    var macd = Buffer.concat([self.ephemeral.PublicKey,iv,innerc]);
    // key is the secret and seq bytes combined
    var hmac = fold(3,NodeCrypto.createHmac("sha256", Buffer.concat([macsecret,iv])).update(macd).digest());

    // create final message body
    return Buffer.concat([macd,hmac]);
  };

}

exports.Ephemeral = function(remote, body)
{
  var self = this;

  self.seq = NodeCrypto.randomBytes(4).readUInt32LE(0); // start from random place

  try{
    // sender token
    self.token = NodeCrypto.createHash('sha256').update(body.slice(0,16)).digest().slice(0,16);

    // extract received ephemeral key
    var key = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, body.slice(0,21), true);

    // get shared secret to make channel keys
    var secret = remote.ephemeral.deriveSharedSecret(key);
    self.encKey = fold(1,NodeCrypto.createHash("sha256")
      .update(secret)
      .update(remote.ephemeral.PublicKey)
      .update(key.PublicKey)
      .digest());
    self.decKey = fold(1,NodeCrypto.createHash("sha256")
      .update(secret)
      .update(key.PublicKey)
      .update(remote.ephemeral.PublicKey)
      .digest());
  }catch(E){
    self.err = E;
  }

  self.decrypt = function(outer){
    // extract the three buffers
    var seq = outer.slice(0,4);
    var cbody = outer.slice(4,outer.length-4);
    var mac1 = outer.slice(outer.length-4);

    // validate the hmac
    var key = Buffer.concat([self.decKey,seq]);
    var mac2 = fold(3,NodeCrypto.createHmac("sha256", key).update(cbody).digest());
    if(mac1.toString('hex') != mac2.toString('hex')) return false;

    // decrypt body
    var ivz = new Buffer(12);
    ivz.fill(0);
    try{
      var body = NodeCrypto.aes(false,self.decKey,Buffer.concat([seq,ivz]),cbody);
    }catch(E){
      return false;
    }
    return body;
  };

  self.encrypt = function(inner){
    // now encrypt the packet
    var iv = new Buffer(16);
    iv.fill(0);
    iv.writeUInt32LE(self.seq++,0);

    var cbody = NodeCrypto.aes(true, self.encKey, iv, inner);

    // create the hmac
    var key = Buffer.concat([self.encKey,iv.slice(0,4)]);
    var mac = fold(3,NodeCrypto.createHmac("sha256", key).update(cbody).digest());

    // return final body
    return Buffer.concat([iv.slice(0,4),cbody,mac]);
  };
}

// simple xor buffer folder
function fold(count, buf)
{
  if(!count || buf.length % 2) return buf;
  var ret = buf.slice(0,buf.length/2);
  for(var i = 0; i < ret.length; i++) ret[i] = ret[i] ^ buf[i+ret.length];
  return fold(count-1,ret);
}
<commit_msg>cs1a async wrappers<commit_after>
var NodeCrypto = require('crypto');
var subtle = require("subtle")

var Subtle_Options = {
  HMAC: {
    name: "HMAC"
    , hash: {name: "SHA-256"}
    , usage: ["sign","verify"]
    , extractable : false
  }

                }
exports.id = '1a';

// env-specific crypto methods
exports.crypt = function(ecc,aes)
{
  NodeCrypto.ecc = ecc;
  NodeCrypto.aes = aes;
}

exports.generate = function(cb)
{

  try {
    var k = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1);
  }catch(E){
    return cb(E);
  }
  cb(null, {key:k.PublicKey, secret:k.PrivateKey});
}

exports._Local = function(pair){
  var local = new exports.Local(pair)

  this.token = local.token
  this.decrypt = function(body){
    return Promise.resolve(local.decrypt(body))
  }

  return this;
}

exports._Remote = function(key){
  var remote = new exports.Remote(key)
  this.token = remote.token
  this.encrypt = function(a1, a2){
    return Promise.resolve(remote.encrypt(a1, a2))
  }

  this.verify  = remote._verify;

  return this;
}

exports._Ephemeral = function(remote, body){
  var ephemeral = new exports.Ephemeral(remote, body)
  this.token = ephemeral.token
  this.encrypt = function(body){
    return Promise.resolve(ephemeral.encrypt(body))
  }

  this.decrypt = function(body){
    return Promise.resolve(ephemeral.decrypt(body))
  }

  return this;
}

exports.Local = function(pair)
{
  var self = this;
  try{
    self.key = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, pair.key, true);
    self.secret = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, pair.secret);
    if(self.key.PublicKey.toString() != pair.key.toString()) throw new Error('invalid public key data');
    if(self.secret.PrivateKey.toString() != pair.secret.toString()) throw new Error('invalid secret key data');
  }catch(E){
    self.err = E;
  }

  // decrypt message body and return the inner
  self.decrypt = function(body){
    if(!Buffer.isBuffer(body)) return false;
    if(body.length < 21+4+4) return false;

    var keybuf = body.slice(0,21);
    var iv = body.slice(21,21+4);
    var innerc = body.slice(21+4,body.length-4);
    // mac is handled during verify stage

    try{
      var ephemeral = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, keybuf, true);
      var secret = self.secret.deriveSharedSecret(ephemeral);
    }catch(E){
      return false;
    }

    var key = fold(1,NodeCrypto.createHash("sha256").update(secret).digest());
    var ivz = new Buffer(12);
    ivz.fill(0);

    // aes-128 decipher the inner
    try{
      var inner = NodeCrypto.aes(false, key, Buffer.concat([iv,ivz]), innerc);
    }catch(E){
      return false;
    }

    return inner;
  };
}

exports.Remote = function(key)
{
  var self = this;
  try{
    self.endpoint = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, key, true);
    self.ephemeral = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1);
    self.token = NodeCrypto.createHash('sha256').update(self.ephemeral.PublicKey.slice(0,16)).digest().slice(0,16);
    self.seq = NodeCrypto.randomBytes(4).readUInt32LE(0); // start from random place
  }catch(E){
    self.err = E;
  }

  self._verify = function(local, body){

      if(!Buffer.isBuffer(body))
        return Promise.reject(new Error("CS1a verify: 2nd argument not a Buffer"));

      // derive shared secret from both identity keys
      var secret = local.secret.deriveSharedSecret(self.endpoint);

      // hmac key is the secret and seq bytes combined to make it unique each time
      var iv = body.slice(21,21+4);
    return subtle.importKey("raw",Buffer.concat([secret,iv]),Subtle_Options.HMAC,  true, Subtle_Options.HMAC.usage)
          .then(function(key){
            console.log("HMAC KEY", key)
            return subtle.sign({name:"HMAC"}, key, body.slice(0,body.length-4))
          })
          .then(function(sig){
            var mac = fold(3,new Buffer(new Uint8Array(sig)));
            var passed = (mac.toString('hex') === body.slice(body.length-4).toString('hex'));
            return (passed) ? true : Promise.reject();
          })
  }

  // verifies the hmac on an incoming message body
  self.verify = function(local, body){
    if(!Buffer.isBuffer(body)) return false;

    // derive shared secret from both identity keys
    var secret = local.secret.deriveSharedSecret(self.endpoint);

    // hmac key is the secret and seq bytes combined to make it unique each time
    var iv = body.slice(21,21+4);
    var dig = NodeCrypto.createHmac("sha256", Buffer.concat([secret,iv])).update(body.slice(0,body.length-4)).digest();
    var mac = fold(3,dig)

    if(mac.toString('hex') != body.slice(body.length-4).toString('hex')) return false;

    return true;
  };

  self.encrypt = function(local, inner){
    if(!Buffer.isBuffer(inner)) return false;

    // get the shared secret to create the iv+key for the open aes
    try{
      var secret = self.ephemeral.deriveSharedSecret(self.endpoint);
    }catch(E){
      return false;
    }
    var key = fold(1,NodeCrypto.createHash("sha256").update(secret).digest());
    var iv = new Buffer(4);
    iv.writeUInt32LE(self.seq++,0);
    var ivz = new Buffer(12);
    ivz.fill(0);

    // encrypt the inner
    try{
      var innerc = NodeCrypto.aes(true, key, Buffer.concat([iv,ivz]), inner);
      var macsecret = local.secret.deriveSharedSecret(self.endpoint);
    }catch(E){
      return false;
    }

    // prepend the key and hmac it
    var macd = Buffer.concat([self.ephemeral.PublicKey,iv,innerc]);
    // key is the secret and seq bytes combined
    var hmac = fold(3,NodeCrypto.createHmac("sha256", Buffer.concat([macsecret,iv])).update(macd).digest());

    // create final message body
    return Buffer.concat([macd,hmac]);
  };

}

exports.Ephemeral = function(remote, body)
{
  var self = this;

  self.seq = NodeCrypto.randomBytes(4).readUInt32LE(0); // start from random place

  try{
    // sender token
    self.token = NodeCrypto.createHash('sha256').update(body.slice(0,16)).digest().slice(0,16);

    // extract received ephemeral key
    var key = new NodeCrypto.ecc.ECKey(NodeCrypto.ecc.ECCurves.secp160r1, body.slice(0,21), true);

    // get shared secret to make channel keys
    var secret = remote.ephemeral.deriveSharedSecret(key);
    self.encKey = fold(1,NodeCrypto.createHash("sha256")
      .update(secret)
      .update(remote.ephemeral.PublicKey)
      .update(key.PublicKey)
      .digest());
    self.decKey = fold(1,NodeCrypto.createHash("sha256")
      .update(secret)
      .update(key.PublicKey)
      .update(remote.ephemeral.PublicKey)
      .digest());
  }catch(E){
    self.err = E;
  }

  self.decrypt = function(outer){
    // extract the three buffers
    var seq = outer.slice(0,4);
    var cbody = outer.slice(4,outer.length-4);
    var mac1 = outer.slice(outer.length-4);

    // validate the hmac
    var key = Buffer.concat([self.decKey,seq]);
    var mac2 = fold(3,NodeCrypto.createHmac("sha256", key).update(cbody).digest());
    if(mac1.toString('hex') != mac2.toString('hex')) return false;

    // decrypt body
    var ivz = new Buffer(12);
    ivz.fill(0);
    try{
      var body = NodeCrypto.aes(false,self.decKey,Buffer.concat([seq,ivz]),cbody);
    }catch(E){
      return false;
    }
    return body;
  };

  self.encrypt = function(inner){
    // now encrypt the packet
    var iv = new Buffer(16);
    iv.fill(0);
    iv.writeUInt32LE(self.seq++,0);

    var cbody = NodeCrypto.aes(true, self.encKey, iv, inner);

    // create the hmac
    var key = Buffer.concat([self.encKey,iv.slice(0,4)]);
    var mac = fold(3,NodeCrypto.createHmac("sha256", key).update(cbody).digest());

    // return final body
    return Buffer.concat([iv.slice(0,4),cbody,mac]);
  };
}

// simple xor buffer folder
function fold(count, buf)
{
  if(!count || buf.length % 2) return buf;
  var ret = buf.slice(0,buf.length/2);
  for(var i = 0; i < ret.length; i++) ret[i] = ret[i] ^ buf[i+ret.length];
  return fold(count-1,ret);
}
<|endoftext|>


<commit_before>var disease_ID = 0;

function Disease(name, diseaseInfo) {
	var ratio = diseaseInfo[0] / 2.5;
	var _id = disease_ID++;
	this.name = name;
	this.currentInfected = 0;
	this.totalInfected = 0;
	this.totalKilled = 0;

	this.fatalityRateUnVacc = diseaseInfo[1] / 100;
	if (diseaseInfo[2]) {
		this.fatalityRateVacc = diseaseInfo[2] / 100;
	} else {
		this.fatalityRateVacc = this.fatalityRateUnVacc;
	}

	this.rateToAdd = (fatalityRateUnVacc * 100) / (1 / fatalityRateUnVacc);

	//TODO figure out where we're going to get the data for these parameters

	/* I ASSUME THIS IS WHAT HE MEANT IN THE EMAIL */

	//If we figure out we do not need any of these we can take them out
	this.transmissibility = 1.0 * ratio / 100;
	this.prob_of_symptoms = 0.67 * ratio;
	this.symp_infectivity = 1.0 * ratio;
	this.asymp_infectivity = 0.5 * ratio;

	//Have to figure out if we want to deal with this as 3 days being
	//the same or make it vary
	this.days_latent = 3 * ratio;
	this.days_infectious = 7 * ratio;
	this.days_incubating = 3 * ratio;
	this.days_symptomatic = 7 * ratio;

	console.log(this.name, this.fatalityRateUnVacc);

	/*
	this->natural_history = null;
	this->min_symptoms_for_seek_healthcare = -1.0;
	this->hospitalization_prob = null;
	this->outpatient_healthcare_prob = null;
	this->seasonality_Ka = -1.0;
	this->seasonality_Kb = -1.0;
	this->seasonality_min = -1.0;
	this->seasonality_max = -1.0;
	strcpy(this->natural_history_model,"markov");
	strcpy(this->transmission_mode,"respiratory");
	*/
	this.getId = function() {
		return _id;
	}
}

Disease.prototype = {
	printTotalInfected: function() {
		console.log("Disease " + this.getId() + ": " + this.name + " has infected " + this.totalInfected + " people.");
	},
	printTotalKilled: function() {
		console.log("Disease " + this.getId() + ": " + this.name + " has killed " + this.totalKilled + " people.");
	}
}

module.exports = Disease;<commit_msg>fixed calling fatalityRateUnVacc incorrectly<commit_after>var disease_ID = 0;

function Disease(name, diseaseInfo) {
	var ratio = diseaseInfo[0] / 2.5;
	var _id = disease_ID++;
	this.name = name;
	this.currentInfected = 0;
	this.totalInfected = 0;
	this.totalKilled = 0;

	this.fatalityRateUnVacc = diseaseInfo[1] / 100;
	if (diseaseInfo[2]) {
		this.fatalityRateVacc = diseaseInfo[2] / 100;
	} else {
		this.fatalityRateVacc = this.fatalityRateUnVacc;
	}

	this.rateToAdd = (this.fatalityRateUnVacc * 100) / (1 / this.fatalityRateUnVacc);

	//TODO figure out where we're going to get the data for these parameters

	/* I ASSUME THIS IS WHAT HE MEANT IN THE EMAIL */

	//If we figure out we do not need any of these we can take them out
	this.transmissibility = 1.0 * ratio / 100;
	this.prob_of_symptoms = 0.67 * ratio;
	this.symp_infectivity = 1.0 * ratio;
	this.asymp_infectivity = 0.5 * ratio;

	//Have to figure out if we want to deal with this as 3 days being
	//the same or make it vary
	this.days_latent = 3 * ratio;
	this.days_infectious = 7 * ratio;
	this.days_incubating = 3 * ratio;
	this.days_symptomatic = 7 * ratio;

	console.log(this.name, this.fatalityRateUnVacc);

	/*
	this->natural_history = null;
	this->min_symptoms_for_seek_healthcare = -1.0;
	this->hospitalization_prob = null;
	this->outpatient_healthcare_prob = null;
	this->seasonality_Ka = -1.0;
	this->seasonality_Kb = -1.0;
	this->seasonality_min = -1.0;
	this->seasonality_max = -1.0;
	strcpy(this->natural_history_model,"markov");
	strcpy(this->transmission_mode,"respiratory");
	*/
	this.getId = function() {
		return _id;
	}
}

Disease.prototype = {
	printTotalInfected: function() {
		console.log("Disease " + this.getId() + ": " + this.name + " has infected " + this.totalInfected + " people.");
	},
	printTotalKilled: function() {
		console.log("Disease " + this.getId() + ": " + this.name + " has killed " + this.totalKilled + " people.");
	}
}

module.exports = Disease;<|endoftext|>


<commit_before>import {Content, SectionLayout} from 'widget/sectionLayout'
import {RecipeActions, defaultModel} from './opticalMosaicRecipe'
import {compose} from 'compose'
import {msg} from 'translate'
import {recipe} from 'app/home/body/process/recipeContext'
import {selectFrom} from 'stateUtils'
import {setAoiLayer} from 'app/home/map/aoiLayer'
import AutoSelectScenes from './autoSelectScenes'
import BandSelection from './bandSelection'
import MapScale from 'app/home/map/mapScale'
import MapToolbar from 'app/home/map/mapToolbar'
import MosaicToolbar from './panels/opticalMosaicToolbar'
import OpticalMosaicPreview from './opticalMosaicPreview'
import React from 'react'
import SceneAreas from './sceneAreas'
import SceneDeselection from './sceneDeselection'
import SceneSelection from './sceneSelection'
import styles from './opticalMosaic.module.css'

const mapRecipeToProps = recipe => ({
    initialized: selectFrom(recipe, 'ui.initialized'),
    aoi: selectFrom(recipe, 'model.aoi')
})

class _Mosaic extends React.Component {
    constructor(props) {
        super(props)
        const {recipeId} = props
        const recipeActions = RecipeActions(recipeId)
        recipeActions.setSceneAreasShown(true).dispatch()
        recipeActions.setBands('red, green, blue').dispatch()
        recipeActions.setAutoSelectSceneCount({min: 1, max: 99}).dispatch()
    }

    render() {
        const {recipeContext: {statePath}, initialized} = this.props
        return (
            <SectionLayout>
                <Content>

                        <MapToolbar statePath={[statePath, 'ui']} labelLayerIndex={3}/>
                        <MapScale/>
                        <MosaicToolbar/>
                        {initialized
                            ? <React.Fragment>
                                <OpticalMosaicPreview/>
                                <SceneAreas/>
                                <AutoSelectScenes/>
                                <SceneSelection/>
                                <SceneDeselection/>
                                <BandSelection/>
                            </React.Fragment>
                            : null}

                </Content>
            </SectionLayout>
        )
    }

    componentDidMount() {
        const {mapContext, aoi} = this.props
        setAoiLayer({
            mapContext,
            aoi,
            // destroy$: componentWillUnmount$, [TODO] check
            onInitialized: () => mapContext.sepalMap.fitLayer('aoi')
        })
    }
}

const Mosaic = compose(
    _Mosaic,
    recipe({defaultModel, mapRecipeToProps})
)

export default () => ({
    id: 'MOSAIC',
    labels: {
        name: msg('process.mosaic.create'),
        creationDescription: msg('process.mosaic.description'),
        tabPlaceholder: msg('process.mosaic.tabPlaceholder'),
    },
    components: {
        recipe: Mosaic
    }
})
<commit_msg>Refactoring.<commit_after>import {Content, SectionLayout} from 'widget/sectionLayout'
import {RecipeActions, defaultModel} from './opticalMosaicRecipe'
import {compose} from 'compose'
import {msg} from 'translate'
import {recipe} from 'app/home/body/process/recipeContext'
import {selectFrom} from 'stateUtils'
import {setAoiLayer} from 'app/home/map/aoiLayer'
import AutoSelectScenes from './autoSelectScenes'
import BandSelection from './bandSelection'
import MapScale from 'app/home/map/mapScale'
import MapToolbar from 'app/home/map/mapToolbar'
import MosaicToolbar from './panels/opticalMosaicToolbar'
import OpticalMosaicPreview from './opticalMosaicPreview'
import React from 'react'
import SceneAreas from './sceneAreas'
import SceneDeselection from './sceneDeselection'
import SceneSelection from './sceneSelection'
import styles from './opticalMosaic.module.css'

const mapRecipeToProps = recipe => ({
    initialized: selectFrom(recipe, 'ui.initialized'),
    aoi: selectFrom(recipe, 'model.aoi')
})

class _OpticalMosaic extends React.Component {
    constructor(props) {
        super(props)
        const {recipeId} = props
        const recipeActions = RecipeActions(recipeId)
        recipeActions.setSceneAreasShown(true).dispatch()
        recipeActions.setBands('red, green, blue').dispatch()
        recipeActions.setAutoSelectSceneCount({min: 1, max: 99}).dispatch()
    }

    render() {
        const {recipeContext: {statePath}, initialized} = this.props
        return (
            <SectionLayout>
                <Content>

                        <MapToolbar statePath={[statePath, 'ui']} labelLayerIndex={3}/>
                        <MapScale/>
                        <MosaicToolbar/>
                        {initialized
                            ? <React.Fragment>
                                <OpticalMosaicPreview/>
                                <SceneAreas/>
                                <AutoSelectScenes/>
                                <SceneSelection/>
                                <SceneDeselection/>
                                <BandSelection/>
                            </React.Fragment>
                            : null}

                </Content>
            </SectionLayout>
        )
    }

    componentDidMount() {
        const {mapContext, aoi} = this.props
        setAoiLayer({
            mapContext,
            aoi,
            // destroy$: componentWillUnmount$, [TODO] check
            onInitialized: () => mapContext.sepalMap.fitLayer('aoi')
        })
    }
}

const OpticalMosaic = compose(
    _OpticalMosaic,
    recipe({defaultModel, mapRecipeToProps})
)

export default () => ({
    id: 'MOSAIC',
    labels: {
        name: msg('process.mosaic.create'),
        creationDescription: msg('process.mosaic.description'),
        tabPlaceholder: msg('process.mosaic.tabPlaceholder'),
    },
    components: {
        recipe: OpticalMosaic
    }
})
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var async = require('async');
var GitStatistics = require('../classes/gitStatistics.js');

function Updater(pathToReposDir) {
  self = this;
  this.pathToReposDir = pathToReposDir;

  this.statisticsSections = function(gitStatistics) {
    return [
    function(callback) {
      gitStatistics.updateRepo(function() {
        callback(null, null);
      });
    },
    function(callback) {
      gitStatistics.get10LatestCommits(function(latestCommits) {
        callback(null, { latestCommits: latestCommits });
      });
    },
    function(callback) {
      gitStatistics.getTop10Committers(function(top10Committers) {
        callback(null, { top10Committers: top10Committers });
      });
    }
  ]};

  this.updateData = function(done) {
    var repos = this.getAllRepos();
    var gitStatisticsArray = new Array();
    repos.forEach(function(repoName) {
      gitStatisticsArray.push(new GitStatistics(path.join(self.pathToReposDir, repoName)));
    });

    async.map(gitStatisticsArray, function(gitStatistics, repoCallback) {
      async.series(
        self.statisticsSections(gitStatistics),
        function(err, results) {
          var repoData = {};
          repoData[path.basename(gitStatistics.repoPath)] = {};
          self.formatResults(repoData[path.basename(gitStatistics.repoPath)], results);

          repoCallback(null, repoData);
        }
      );
    }, function(err, results) {
      var data = {};
      data.repos = repos;
      self.formatResults(data, results);

      self.calculateCombinedStatistics(data, function(combinedStatistics) {
        self.formatResults(data, [combinedStatistics]);
        data.repos.push('all');
        done(data);
      });
    });
  }

  this.getAllRepos = function() {
    var repos = new Array();
    var files = fs.readdirSync(self.pathToReposDir);
    files.forEach(function(file) {
      var repoPath = path.join(self.pathToReposDir, file);
      if(fs.lstatSync(repoPath).isDirectory()) {
        repos.push(file);
      }
    });

    return repos;
  }

  this.formatResults = function(data, results) {
    for (var i in results) {
      for (var c in results[i]) {
        data[c] = results[i][c];
      }
    }
  }

  this.calculateCombinedStatistics = function(data, callback) {
    var combinedStatistics = {};

    // latestCommits
    var allCommits = self.getCombinedStatisticsSection(data, 'latestCommits');
    allCommits.sort(function(a,b) { return new Date(a.date) - new Date(b.data) } );
    combinedStatistics.latestCommits = allCommits.slice(0, 10);

    // top10Committers
    var allCommitters = self.getCombinedStatisticsSection(data, 'top10Committers');
    allCommitters.sort(function(a,b) { return parseInt(a.numberOfCommits) - parseInt(b.numberOfCommits) } );
    combinedStatistics.top10Committers = allCommits.slice(0, 10);

    callback({all: combinedStatistics});
  }

  this.getCombinedStatisticsSection = function(data, section) {
    var items = [];
    for(var i in data) {
      if(data[i] == 'repos') {
        continue;
      }

      for(var c in data[i][section]) {
        items.push(data[i][section][c]);
      }
    }
    return items;
  }
}

module.exports = Updater;<commit_msg>Fixed list of top 10 committers<commit_after>var fs = require('fs');
var path = require('path');
var async = require('async');
var GitStatistics = require('../classes/gitStatistics.js');

function Updater(pathToReposDir) {
  self = this;
  this.pathToReposDir = pathToReposDir;

  this.statisticsSections = function(gitStatistics) {
    return [
    function(callback) {
      gitStatistics.updateRepo(function() {
        callback(null, null);
      });
    },
    function(callback) {
      gitStatistics.get10LatestCommits(function(latestCommits) {
        callback(null, { latestCommits: latestCommits });
      });
    },
    function(callback) {
      gitStatistics.getTop10Committers(function(top10Committers) {
        callback(null, { top10Committers: top10Committers });
      });
    }
  ]};

  this.updateData = function(done) {
    var repos = this.getAllRepos();
    var gitStatisticsArray = new Array();
    repos.forEach(function(repoName) {
      gitStatisticsArray.push(new GitStatistics(path.join(self.pathToReposDir, repoName)));
    });

    async.map(gitStatisticsArray, function(gitStatistics, repoCallback) {
      async.series(
        self.statisticsSections(gitStatistics),
        function(err, results) {
          var repoData = {};
          repoData[path.basename(gitStatistics.repoPath)] = {};
          self.formatResults(repoData[path.basename(gitStatistics.repoPath)], results);

          repoCallback(null, repoData);
        }
      );
    }, function(err, results) {
      var data = {};
      data.repos = repos;
      self.formatResults(data, results);

      self.calculateCombinedStatistics(data, function(combinedStatistics) {
        self.formatResults(data, [combinedStatistics]);
        data.repos.push('all');
        done(data);
      });
    });
  }

  this.getAllRepos = function() {
    var repos = new Array();
    var files = fs.readdirSync(self.pathToReposDir);
    files.forEach(function(file) {
      var repoPath = path.join(self.pathToReposDir, file);
      if(fs.lstatSync(repoPath).isDirectory()) {
        repos.push(file);
      }
    });

    return repos;
  }

  this.formatResults = function(data, results) {
    for (var i in results) {
      for (var c in results[i]) {
        data[c] = results[i][c];
      }
    }
  }

  this.calculateCombinedStatistics = function(data, callback) {
    var combinedStatistics = {};

    // latestCommits
    var allCommits = self.getCombinedStatisticsSection(data, 'latestCommits');
    allCommits.sort(function(a,b) { return new Date(a.date) - new Date(b.data) } );
    combinedStatistics.latestCommits = allCommits.slice(0, 10);

    // top10Committers
    var allCommitters = self.getCombinedStatisticsSection(data, 'top10Committers');
    allCommitters.sort(function(a,b) { return parseInt(a.numberOfCommits) - parseInt(b.numberOfCommits) } );
    combinedStatistics.top10Committers = allCommitters.slice(0, 10);

    callback({all: combinedStatistics});
  }

  this.getCombinedStatisticsSection = function(data, section) {
    var items = [];
    for(var i in data) {
      if(data[i] == 'repos') {
        continue;
      }

      for(var c in data[i][section]) {
        items.push(data[i][section][c]);
      }
    }
    return items;
  }
}

module.exports = Updater;<|endoftext|>


<commit_before>describe('The atlas-layer-selection component', function () {
    var $compile,
        $rootScope,
        store,
        ACTIONS;

    beforeEach(function () {
        angular.mock.module(
            'atlasLayerSelection',
            {
                store: {
                    dispatch: function () {}
                }
            },
            function ($provide) {
                $provide.constant('BASE_LAYERS', [
                    {
                        slug: 'base_layer_a',
                        label: 'Base layer A'
                    }, {
                        slug: 'base_layer_b',
                        label: 'Base layer B'
                    }
                ]);

                $provide.constant('OVERLAYS', {
                    SOURCES: {
                        overlay_1_a: {
                            label: 'Overlay 1a',
                            minZoom: 8,
                            maxZoom: 16
                        },
                        overlay_1_b: {
                            label: 'Overlay 1b',
                            minZoom: 8,
                            maxZoom: 16
                        },
                        overlay_2_a: {
                            label: 'Overlay 2a',
                            minZoom: 8,
                            maxZoom: 10
                        },
                        overlay_2_b: {
                            label: 'Overlay 2b',
                            minZoom: 11,
                            maxZoom: 14
                        },
                        overlay_2_c: {
                            label: 'Overlay 2c',
                            minZoom: 15,
                            maxZoom: 16
                        }
                    },
                    HIERARCHY: [
                        {
                            heading: 'Category 1',
                            overlays: ['overlay_1_a', 'overlay_1_b']
                        }, {
                            heading: 'Category 2',
                            overlays: ['overlay_2_a', 'overlay_2_b', 'overlay_2_c']
                        }
                    ]
                });
            }
        );

        angular.mock.inject(function (_$compile_, _$rootScope_, _store_, _ACTIONS_) {
            $compile = _$compile_;
            $rootScope = _$rootScope_;
            store = _store_;
            ACTIONS = _ACTIONS_;
        });

        spyOn(store, 'dispatch');
    });

    function getComponent (baseLayer, overlays, zoom) {
        var component,
            element,
            scope;

        element = document.createElement('atlas-layer-selection');
        element.setAttribute('base-layer', baseLayer);
        element.setAttribute('overlays', 'overlays');
        element.setAttribute('zoom', 'zoom');

        scope = $rootScope.$new();
        scope.overlays = overlays;
        scope.zoom = zoom;

        component = $compile(element)(scope);
        scope.$apply();

        return component;
    }

    describe('base layer', function () {
        it('lists all base layers as radio buttons w/ labels', function () {
            var component = getComponent('base_layer_a', {}, 8);

            expect(component.find('ul').eq(0).find('li').length).toBe(2);
            expect(component.find('ul').eq(0).find('li').eq(0).find('label').text().trim()).toBe('Base layer A');
            expect(component.find('ul').eq(0).find('li').eq(0).find('input').attr('type')).toBe('radio');
            expect(component.find('ul').eq(0).find('li').eq(0).find('input').val()).toBe('base_layer_a');

            expect(component.find('ul').eq(0).find('li').eq(1).find('label').text().trim()).toBe('Base layer B');
            expect(component.find('ul').eq(0).find('li').eq(1).find('input').attr('type')).toBe('radio');
            expect(component.find('ul').eq(0).find('li').eq(1).find('input').val()).toBe('base_layer_b');
        });

        it('can set the base layer', function () {
            var component = getComponent('base_layer_a', [], 8),
                scope = component.isolateScope();

            //Choose another base layer
            scope.vm.setBaseLayer('base_layer_b');

            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.MAP_SET_BASELAYER,
                payload: 'base_layer_b'
            });

            //Put the default base layer back
            scope.vm.setBaseLayer('base_layer_a');

            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.MAP_SET_BASELAYER,
                payload: 'base_layer_a'
            });
        });
    });

    describe('overlays', function () {
        it('lists all overlays as checkboxes w/ labels', function () {
            var component = getComponent('base_layer_a', [], 8);

            expect(component.find('div').eq(2).find('h2').text()).toBe('Category 1');
            expect(component.find('div').eq(2).find('li').eq(0).find('label').text().trim()).toBe('Overlay 1a');
            expect(component.find('div').eq(2).find('li').eq(0).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(2).find('li').eq(0).find('input').val()).toBe('overlay_1_a');

            expect(component.find('div').eq(2).find('li').eq(1).find('label').text().trim()).toBe('Overlay 1b');
            expect(component.find('div').eq(2).find('li').eq(1).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(2).find('li').eq(1).find('input').val()).toBe('overlay_1_b');

            expect(component.find('div').eq(3).find('h2').text()).toBe('Category 2');
            expect(component.find('div').eq(3).find('li').eq(0).find('label').text().trim()).toBe('Overlay 2a');
            expect(component.find('div').eq(3).find('li').eq(0).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(3).find('li').eq(0).find('input').val()).toBe('overlay_2_a');

            expect(component.find('div').eq(3).find('li').eq(1).find('label').text().trim()).toBe('Overlay 2b');
            expect(component.find('div').eq(3).find('li').eq(1).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(3).find('li').eq(1).find('input').val()).toBe('overlay_2_b');

            expect(component.find('div').eq(3).find('li').eq(2).find('label').text().trim()).toBe('Overlay 2c');
            expect(component.find('div').eq(3).find('li').eq(2).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(3).find('li').eq(2).find('input').val()).toBe('overlay_2_c');
        });

        it('marks the checkboxes for active overlays', function () {
            var component;

            //Nothing is checked if there are no overlays
            component = getComponent('base_layer_a', [], 8);

            expect(component.find('div').eq(2).find('li').eq(0).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(2).find('li').eq(1).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(0).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(1).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(2).find('input').attr('checked')).toBeUndefined();

            //With active overlays
            component = getComponent('base_layer_a', ['overlay_1_a', 'overlay_2_b'], 8);

            expect(component.find('div').eq(2).find('li').eq(0).find('input').attr('checked')).toBe('checked');
            expect(component.find('div').eq(2).find('li').eq(1).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(0).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(1).find('input').attr('checked')).toBe('checked');
            expect(component.find('div').eq(3).find('li').eq(2).find('input').attr('checked')).toBeUndefined();
        });

        it('can toggle overlays', function () {
            var component = getComponent('base_layer_a', ['overlay_1_a'], 8),
                scope = component.isolateScope();

            //Add one
            scope.vm.toggleOverlay('overlay_1_b');

            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.MAP_ADD_OVERLAY,
                payload: 'overlay_1_b'
            });

            //Remove one
            scope.vm.toggleOverlay('overlay_1_a');

            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.MAP_REMOVE_OVERLAY,
                payload: 'overlay_1_a'
            });
        });

        it('has a indicator for overlays not visible on this zoom level', function () {
            var component,
                allOverlays,
                zoom,
                expectedZoomIndicatorText;

            allOverlays = {
                'overlay_1_a': true,
                'overlay_1_b': true,
                'overlay_2_a': true,
                'overlay_2_b': true,
                'overlay_2_c': true
            };

            expectedZoomIndicatorText = '(deze kaartlaag is niet zichtbaar op dit zoomniveau)';

            for (zoom = 8; zoom <= 10; zoom++) {
                component = getComponent('base_layer_a', allOverlays, zoom);

                expect(component.find('div').eq(2).find('li').eq(0).find('span').length).toBe(0);
                expect(component.find('div').eq(2).find('li').eq(1).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
            }

            for (zoom = 11; zoom <= 14; zoom++) {
                component = getComponent('base_layer_a', allOverlays, zoom);

                expect(component.find('div').eq(2).find('li').eq(0).find('span').length).toBe(0);
                expect(component.find('div').eq(2).find('li').eq(1).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
            }

            for (zoom = 15; zoom <= 16; zoom++) {
                component = getComponent('base_layer_a', allOverlays, zoom);

                expect(component.find('div').eq(2).find('li').eq(0).find('span').length).toBe(0);
                expect(component.find('div').eq(2).find('li').eq(1).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(0);
            }
        });

        it('only shows the i-am-not-visible indicator for active overlays', function () {
            var component;

            //When the overlays are active
            component = getComponent('base_layer_a', ['overlay_2_b', 'overlay_2_c'], 8);
            expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(1);
            expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(1);

            //When the overlays are not active
            component = getComponent('base_layer_a', [], 8);
            expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(0);
            expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(0);
        });
    });

    it('has a button to close the layer selection sidebar', function () {
        var component = getComponent('base_layer_a', [], 8);

        component.find('button')[0].click();

        expect(store.dispatch).toHaveBeenCalledWith({
            type: ACTIONS.HIDE_LAYER_SELECTION
        });
    });
});<commit_msg>Hierarchy should use array to note overlays<commit_after>describe('The atlas-layer-selection component', function () {
    var $compile,
        $rootScope,
        store,
        ACTIONS;

    beforeEach(function () {
        angular.mock.module(
            'atlasLayerSelection',
            {
                store: {
                    dispatch: function () {}
                }
            },
            function ($provide) {
                $provide.constant('BASE_LAYERS', [
                    {
                        slug: 'base_layer_a',
                        label: 'Base layer A'
                    }, {
                        slug: 'base_layer_b',
                        label: 'Base layer B'
                    }
                ]);

                $provide.constant('OVERLAYS', {
                    SOURCES: {
                        overlay_1_a: {
                            label: 'Overlay 1a',
                            minZoom: 8,
                            maxZoom: 16
                        },
                        overlay_1_b: {
                            label: 'Overlay 1b',
                            minZoom: 8,
                            maxZoom: 16
                        },
                        overlay_2_a: {
                            label: 'Overlay 2a',
                            minZoom: 8,
                            maxZoom: 10
                        },
                        overlay_2_b: {
                            label: 'Overlay 2b',
                            minZoom: 11,
                            maxZoom: 14
                        },
                        overlay_2_c: {
                            label: 'Overlay 2c',
                            minZoom: 15,
                            maxZoom: 16
                        }
                    },
                    HIERARCHY: [
                        {
                            heading: 'Category 1',
                            overlays: ['overlay_1_a', 'overlay_1_b']
                        }, {
                            heading: 'Category 2',
                            overlays: ['overlay_2_a', 'overlay_2_b', 'overlay_2_c']
                        }
                    ]
                });
            }
        );

        angular.mock.inject(function (_$compile_, _$rootScope_, _store_, _ACTIONS_) {
            $compile = _$compile_;
            $rootScope = _$rootScope_;
            store = _store_;
            ACTIONS = _ACTIONS_;
        });

        spyOn(store, 'dispatch');
    });

    function getComponent (baseLayer, overlays, zoom) {
        var component,
            element,
            scope;

        element = document.createElement('atlas-layer-selection');
        element.setAttribute('base-layer', baseLayer);
        element.setAttribute('overlays', 'overlays');
        element.setAttribute('zoom', 'zoom');

        scope = $rootScope.$new();
        scope.overlays = overlays;
        scope.zoom = zoom;

        component = $compile(element)(scope);
        scope.$apply();

        return component;
    }

    describe('base layer', function () {
        it('lists all base layers as radio buttons w/ labels', function () {
            var component = getComponent('base_layer_a', {}, 8);

            expect(component.find('ul').eq(0).find('li').length).toBe(2);
            expect(component.find('ul').eq(0).find('li').eq(0).find('label').text().trim()).toBe('Base layer A');
            expect(component.find('ul').eq(0).find('li').eq(0).find('input').attr('type')).toBe('radio');
            expect(component.find('ul').eq(0).find('li').eq(0).find('input').val()).toBe('base_layer_a');

            expect(component.find('ul').eq(0).find('li').eq(1).find('label').text().trim()).toBe('Base layer B');
            expect(component.find('ul').eq(0).find('li').eq(1).find('input').attr('type')).toBe('radio');
            expect(component.find('ul').eq(0).find('li').eq(1).find('input').val()).toBe('base_layer_b');
        });

        it('can set the base layer', function () {
            var component = getComponent('base_layer_a', [], 8),
                scope = component.isolateScope();

            //Choose another base layer
            scope.vm.setBaseLayer('base_layer_b');

            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.MAP_SET_BASELAYER,
                payload: 'base_layer_b'
            });

            //Put the default base layer back
            scope.vm.setBaseLayer('base_layer_a');

            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.MAP_SET_BASELAYER,
                payload: 'base_layer_a'
            });
        });
    });

    describe('overlays', function () {
        it('lists all overlays as checkboxes w/ labels', function () {
            var component = getComponent('base_layer_a', [], 8);

            expect(component.find('div').eq(2).find('h2').text()).toBe('Category 1');
            expect(component.find('div').eq(2).find('li').eq(0).find('label').text().trim()).toBe('Overlay 1a');
            expect(component.find('div').eq(2).find('li').eq(0).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(2).find('li').eq(0).find('input').val()).toBe('overlay_1_a');

            expect(component.find('div').eq(2).find('li').eq(1).find('label').text().trim()).toBe('Overlay 1b');
            expect(component.find('div').eq(2).find('li').eq(1).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(2).find('li').eq(1).find('input').val()).toBe('overlay_1_b');

            expect(component.find('div').eq(3).find('h2').text()).toBe('Category 2');
            expect(component.find('div').eq(3).find('li').eq(0).find('label').text().trim()).toBe('Overlay 2a');
            expect(component.find('div').eq(3).find('li').eq(0).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(3).find('li').eq(0).find('input').val()).toBe('overlay_2_a');

            expect(component.find('div').eq(3).find('li').eq(1).find('label').text().trim()).toBe('Overlay 2b');
            expect(component.find('div').eq(3).find('li').eq(1).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(3).find('li').eq(1).find('input').val()).toBe('overlay_2_b');

            expect(component.find('div').eq(3).find('li').eq(2).find('label').text().trim()).toBe('Overlay 2c');
            expect(component.find('div').eq(3).find('li').eq(2).find('input').attr('type')).toBe('checkbox');
            expect(component.find('div').eq(3).find('li').eq(2).find('input').val()).toBe('overlay_2_c');
        });

        it('marks the checkboxes for active overlays', function () {
            var component;

            //Nothing is checked if there are no overlays
            component = getComponent('base_layer_a', {}, 8);

            expect(component.find('div').eq(2).find('li').eq(0).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(2).find('li').eq(1).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(0).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(1).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(2).find('input').attr('checked')).toBeUndefined();

            //With active overlays
            component = getComponent('base_layer_a', {'overlay_1_a': true, 'overlay_2_b':true }, 8);

            expect(component.find('div').eq(2).find('li').eq(0).find('input').attr('checked')).toBe('checked');
            expect(component.find('div').eq(2).find('li').eq(1).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(0).find('input').attr('checked')).toBeUndefined();
            expect(component.find('div').eq(3).find('li').eq(1).find('input').attr('checked')).toBe('checked');
            expect(component.find('div').eq(3).find('li').eq(2).find('input').attr('checked')).toBeUndefined();
        });

        it('can toggle overlays', function () {
            var component = getComponent('base_layer_a', {'overlay_1_a': true}, 8),
                scope = component.isolateScope();

            //Add one
            scope.vm.toggleOverlay('overlay_1_b');

            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.MAP_ADD_OVERLAY,
                payload: 'overlay_1_b'
            });

            //Remove one
            scope.vm.toggleOverlay('overlay_1_a');

            expect(store.dispatch).toHaveBeenCalledWith({
                type: ACTIONS.MAP_REMOVE_OVERLAY,
                payload: 'overlay_1_a'
            });
        });

        it('has a indicator for overlays not visible on this zoom level', function () {
            var component,
                allOverlays,
                zoom,
                expectedZoomIndicatorText;

            allOverlays = {
                'overlay_1_a': true,
                'overlay_1_b': true,
                'overlay_2_a': true,
                'overlay_2_b': true,
                'overlay_2_c': true
            };

            expectedZoomIndicatorText = '(deze kaartlaag is niet zichtbaar op dit zoomniveau)';

            for (zoom = 8; zoom <= 10; zoom++) {
                component = getComponent('base_layer_a', allOverlays, zoom);

                expect(component.find('div').eq(2).find('li').eq(0).find('span').length).toBe(0);
                expect(component.find('div').eq(2).find('li').eq(1).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
            }

            for (zoom = 11; zoom <= 14; zoom++) {
                component = getComponent('base_layer_a', allOverlays, zoom);

                expect(component.find('div').eq(2).find('li').eq(0).find('span').length).toBe(0);
                expect(component.find('div').eq(2).find('li').eq(1).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
            }

            for (zoom = 15; zoom <= 16; zoom++) {
                component = getComponent('base_layer_a', allOverlays, zoom);

                expect(component.find('div').eq(2).find('li').eq(0).find('span').length).toBe(0);
                expect(component.find('div').eq(2).find('li').eq(1).find('span').length).toBe(0);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(0).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(1);
                expect(component.find('div').eq(3).find('li').eq(1).find('span').text().trim())
                    .toBe(expectedZoomIndicatorText);
                expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(0);
            }
        });

        it('only shows the i-am-not-visible indicator for active overlays', function () {
            var component;

            //When the overlays are active
            component = getComponent('base_layer_a', {'overlay_2_b': true, 'overlay_2_c': true}, 8);
            expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(1);
            expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(1);

            //When the overlays are not active
            component = getComponent('base_layer_a', [], 8);
            expect(component.find('div').eq(3).find('li').eq(1).find('span').length).toBe(0);
            expect(component.find('div').eq(3).find('li').eq(2).find('span').length).toBe(0);
        });
    });

    it('has a button to close the layer selection sidebar', function () {
        var component = getComponent('base_layer_a', {}, 8);

        component.find('button')[0].click();

        expect(store.dispatch).toHaveBeenCalledWith({
            type: ACTIONS.HIDE_LAYER_SELECTION
        });
    });
});<|endoftext|>