<commit_before>/**
 * Angular Selectize2
 * https://github.com/machineboy2045/angular-selectize
 **/

angular.module('selectize', []).value('selectizeConfig', {}).directive("selectize", ['selectizeConfig', function(selectizeConfig) {
  return {
    restrict: 'E',
    require: '^ngModel',
    scope: {ngModel: '=', config: '=?', options: '=?', ngDisabled: '='},
    link: function(scope, element, attrs, modelCtrl) {

      Selectize.defaults.maxItems = null; //default to tag editor

      var selectize,
          config = angular.extend({}, Selectize.defaults, selectizeConfig, scope.config);

      modelCtrl.$isEmpty = function(val){
        return (val === undefined || val === null || !val.length); //override to support checking empty arrays
      }

      function createItem(input) {
        var data = {};
        data[config.labelField] = input;
        data[config.valueField] = input;
        return data;
      }

      function toggle(disabled){
        disabled ? selectize.disable() : selectize.enable();
      }

      var validate = function() {
        var isInvalid = config.required && modelCtrl.$isEmpty(scope.ngModel);
        modelCtrl.$setValidity('required', !isInvalid)
      };

      function generateOptions(data){
        if(!data)
          return [];

        data = angular.isArray(data) ? data : [data]

        return $.map(data, function(opt){
          return typeof opt === 'string' ? createItem(opt) : opt;
        });
      }

      function updateSelectize(){
        validate();

        selectize.$control.toggleClass('ng-valid', modelCtrl.$valid)
        selectize.$control.toggleClass('ng-invalid', modelCtrl.$invalid)
        selectize.$control.toggleClass('ng-dirty', modelCtrl.$dirty)
        selectize.$control.toggleClass('ng-pristine', modelCtrl.$pristine)

        if( !angular.equals(selectize.items, scope.ngModel) ){
          selectize.addOption(generateOptions(scope.ngModel))
          selectize.setValue(scope.ngModel)
        }
      }

      config.onChange = function(){
        if( !angular.equals(selectize.items, scope.ngModel) )
          scope.$evalAsync(function(){
            var value = angular.copy(selectize.items);
            if (config.maxItems == 1) {
              value = value[0]
            }
            modelCtrl.$setViewValue( value );
          });
      }

      config.onOptionAdd = function(value, data) {
        if( scope.options.indexOf(data) === -1 )
          scope.options.push(data);
      }

      // ngModel (ie selected items) is included in this because if no options are specified, we
      // need to create the corresponding options for the items to be visible
      scope.options = generateOptions( angular.copy(scope.options || config.options || scope.ngModel) );

      var angularCallback = config.onInitialize;

      config.onInitialize = function(){
        selectize = element[0].selectize;
        selectize.addOption(scope.options)
        selectize.setValue(scope.ngModel)

        //provides a way to access the selectize element from an
        //angular controller
        if(angularCallback){
          angularCallback(selectize);
        }

        scope.$watchCollection('options', selectize.addOption.bind(selectize));
        scope.$watch('ngModel', updateSelectize);
        scope.$watch('ngDisabled', toggle);
      }

      element.selectize(config);

      element.on('$destroy', function() {
        if (selectize) {
          selectize.destroy();
          element = null;
        }
      });

    }
  };
}]);
<commit_msg>support ng-required or plain required attributes<commit_after>/**
 * Angular Selectize2
 * https://github.com/machineboy2045/angular-selectize
 **/

angular.module('selectize', []).value('selectizeConfig', {}).directive("selectize", ['selectizeConfig', function(selectizeConfig) {
  return {
    restrict: 'E',
    require: '^ngModel',
    scope: {ngModel: '=', config: '=?', options: '=?', ngDisabled: '=', ngRequired: '&'},
    link: function(scope, element, attrs, modelCtrl) {

      Selectize.defaults.maxItems = null; //default to tag editor

      var selectize,
          config = angular.extend({}, Selectize.defaults, selectizeConfig, scope.config);

      modelCtrl.$isEmpty = function(val){
        return (val === undefined || val === null || !val.length); //override to support checking empty arrays
      }

      function createItem(input) {
        var data = {};
        data[config.labelField] = input;
        data[config.valueField] = input;
        return data;
      }

      function toggle(disabled){
        disabled ? selectize.disable() : selectize.enable();
      }

      var validate = function() {
        var isInvalid = (scope.ngRequired() || attrs.required || config.required) && modelCtrl.$isEmpty(scope.ngModel);
        modelCtrl.$setValidity('required', !isInvalid)
      };

      function generateOptions(data){
        if(!data)
          return [];

        data = angular.isArray(data) ? data : [data]

        return $.map(data, function(opt){
          return typeof opt === 'string' ? createItem(opt) : opt;
        });
      }

      function updateSelectize(){
        validate();

        selectize.$control.toggleClass('ng-valid', modelCtrl.$valid)
        selectize.$control.toggleClass('ng-invalid', modelCtrl.$invalid)
        selectize.$control.toggleClass('ng-dirty', modelCtrl.$dirty)
        selectize.$control.toggleClass('ng-pristine', modelCtrl.$pristine)

        if( !angular.equals(selectize.items, scope.ngModel) ){
          selectize.addOption(generateOptions(scope.ngModel))
          selectize.setValue(scope.ngModel)
        }
      }

      config.onChange = function(){
        if( !angular.equals(selectize.items, scope.ngModel) )
          scope.$evalAsync(function(){
            var value = angular.copy(selectize.items);
            if (config.maxItems == 1) {
              value = value[0]
            }
            modelCtrl.$setViewValue( value );
          });
      }

      config.onOptionAdd = function(value, data) {
        if( scope.options.indexOf(data) === -1 )
          scope.options.push(data);
      }

      // ngModel (ie selected items) is included in this because if no options are specified, we
      // need to create the corresponding options for the items to be visible
      scope.options = generateOptions( angular.copy(scope.options || config.options || scope.ngModel) );

      var angularCallback = config.onInitialize;

      config.onInitialize = function(){
        selectize = element[0].selectize;
        selectize.addOption(scope.options)
        selectize.setValue(scope.ngModel)

        //provides a way to access the selectize element from an
        //angular controller
        if(angularCallback){
          angularCallback(selectize);
        }

        scope.$watchCollection('options', selectize.addOption.bind(selectize));
        scope.$watch('ngModel', updateSelectize);
        scope.$watch('ngDisabled', toggle);
      }

      element.selectize(config);

      element.on('$destroy', function() {
        if (selectize) {
          selectize.destroy();
          element = null;
        }
      });

    }
  };
}]);
<|endoftext|>


<commit_before>var socket = io();

window.addEventListener('DOMContentLoaded', function() {
  var app = document.querySelector('#app');
  app.page = 'library';
});

window.addEventListener('WebComponentsReady', function() {
  // Print pretty info into the console
  console.log('%coudjo -- main-window', 'font-size: x-large; background: ' +
    '-webkit-linear-gradient(top, #bcff05 0%,#70cd19 100%);' +
    '-webkit-background-clip: text;' +
    '-webkit-text-fill-color: transparent;');

  // Handle window resizing
  window.onresize = onWindowResize;

  var app = document.querySelector('#app');

  var $oudjoBar = document.querySelector('oudjo-bar');
  var $drawerMenu = document.querySelector('#drawer-menu');
  var $drawerPanel = document.querySelector('paper-drawer-panel');

  // Close the drawer, when item is selected
  $drawerMenu.addEventListener('iron-select', function() {
    $drawerPanel.closeDrawer();

    // Hide the oudjo-bar when user is on 'Now playing' tab
    if (app.page === 'now-playing') {
      $oudjoBar.hide();
    } else {
      $oudjoBar.show();
    }
  });

  // Change page to 'Now playing' when user taps on oudjo-bar
  $oudjoBar.addEventListener('activate', function() {
    app.page = 'now-playing';
  });

  socket.on('library', function(details) {
    if (details.action === 'scanning-progress') {
      // Show toast
      document.querySelector('#toast-database-scanning').show();

      // Update progress bar on the toast
      document.querySelector('#progress-database-scanning').value =
        details.scanningProgress.progress;
    }
  });

  onWindowResize();
});

// Adjusts the size of panel that's under toolbar and above oudjo-bar
// (#content-panel)
function onWindowResize() {
  // 64px because toolbar is 64px high
  var contentPanelHeight = window.innerHeight - 64;

  document.querySelector('#content-panel')
    .style.height = contentPanelHeight + 'px';
}
<commit_msg>Fixed bad resizing on orientation change<commit_after>var socket = io();

var orientationHorizontal = true;

window.addEventListener('DOMContentLoaded', function() {
  var app = document.querySelector('#app');
  app.page = 'library';
});

window.addEventListener('WebComponentsReady', function() {
  // Print pretty info into the console
  console.log('%coudjo -- main-window', 'font-size: x-large; background: ' +
    '-webkit-linear-gradient(top, #bcff05 0%,#70cd19 100%);' +
    '-webkit-background-clip: text;' +
    '-webkit-text-fill-color: transparent;');

  // Handle window resizing
  window.onresize = onWindowResize;

  var app = document.querySelector('#app');

  var $oudjoBar = document.querySelector('oudjo-bar');
  var $drawerMenu = document.querySelector('#drawer-menu');
  var $drawerPanel = document.querySelector('paper-drawer-panel');

  // Close the drawer, when item is selected
  $drawerMenu.addEventListener('iron-select', function() {
    $drawerPanel.closeDrawer();

    // Hide the oudjo-bar when user is on 'Now playing' tab
    if (app.page === 'now-playing') {
      $oudjoBar.hide();
    } else {
      $oudjoBar.show();
    }
  });

  // Change page to 'Now playing' when user taps on oudjo-bar
  $oudjoBar.addEventListener('activate', function() {
    app.page = 'now-playing';
  });

  socket.on('library', function(details) {
    if (details.action === 'scanning-progress') {
      // Show toast
      document.querySelector('#toast-database-scanning').show();

      // Update progress bar on the toast
      document.querySelector('#progress-database-scanning').value =
        details.scanningProgress.progress;
    }
  });

  onWindowResize();
});

function onWindowResize() {

  // Adjusts the size of panel that's under toolbar and above oudjo-bar
  // (#content-panel)

  // 64px because toolbar is 64px high
  var contentPanelHeight = window.innerHeight - 64;

  document.querySelector('#content-panel')
    .style.height = contentPanelHeight + 'px';

  // Calculate screen orientation
  var lastOrientation = orientationHorizontal;

  if (window.innerHeight < window.innerWidth) {
    orientationHorizontal = true;
  } else {
    orientationHorizontal = false;
  }

  if (lastOrientation !== orientationHorizontal) {
    document.querySelector('oudjo-library')._onResize();
    document.querySelector('oudjo-now-playing')._onResize();
    document.querySelector('oudjo-settings')._onResize();
  }
}
<|endoftext|>


<commit_before>//Variaveis da progress bar têm de ser globais por causa do scope
var progressbar = $("#progressbar");
var progressLabel = $(".progress-label");

var ROWS;
var COLS;

//Global variables for Run time
var ts;
var tt;

/*
    O heatMap e o maxHeatMap sao variaveis globais
    lidas pelo ficheiro SurfacePlot.js
*/
var heatMap;
var maxHeatMap = 0;
var plotTime;

//Visualization array. vector with ignition time results
//computed from Monte Carlo samples
var visArray;

function launch() {

  var i, n;

  var numVar = 3; //Moisture, windSpeed, windDir

  ROWS = parseInt(document.getElementById('resolution').value);
  COLS = parseInt(document.getElementById('resolution').value);

  var ignPointYY = parseInt(document.getElementById('ignPointYY').value);
  var ignPointXX = parseInt(document.getElementById('ignPointXX').value);

  var moisture = parseFloat(document.getElementById('moistureInput').value);
  var fuelLoad = parseFloat(document.getElementById('fuelLoad').value);
  var windDir = parseFloat(document.getElementById('windDir').value);

  var mcSamples = parseInt(document.getElementById('mcSamples').value);
  var windVelAvg = parseFloat(document.getElementById('windVelAvg').value);
  var windVelDev = parseFloat(document.getElementById('windVelDev').value);

  //Create data, results and visualization Array
  var dataArray = new Array(mcSamples);
  var resultsIdx = 0;
  var resultsArray = new Array(mcSamples);
  visArray = new Array(ROWS * COLS);

  heatMap = new Array(ROWS * COLS);

  for (i = 0; i < mcSamples; i++) {

    dataArray[i] = new Array(numVar);
    resultsArray[i] = new Array(COLS * ROWS);

  }

  //Populate: |moiture|windSpeed|windDir|
  for (i = 0; i < mcSamples; i++) {
    dataArray[i][0] = moisture;
    dataArray[i][1] = gauss(windVelAvg, windVelDev/100*windVelAvg);
    dataArray[i][1] = (dataArray[i][1] >= 0 ? dataArray[i][1] : 0);
    console.log('mcSample,U:',i,dataArray[i][1]);
    dataArray[i][2] = windDir;
  }

  //Prepare Run Function
  RunString = Run.toString();

  //storage for slope (entry 0) and aspect (entry 1)
  var terrainArray = [0, 0];

  //Read aspect and slope files and create arrays
  //ACHTUNG Esta porra e assincrona!
  var runnerCounter = 3;
  arrayFromGrassFile('/InputMaps/malcataSlope_' + ROWS.toString() + '.grass', slopeCB);
  arrayFromGrassFile('/InputMaps/malcataAspect_' + ROWS.toString() + '.grass', aspectCB);

  replaceRunPlaceHolders();

  function slopeCB(fileArray) {

    terrainArray[0] = fileArray;
    RunString = RunString.replace(/SLOPEMAP_PC/, JSON.stringify(terrainArray[0]));
    console.log('Slope Map is loaded in string "Run"');
    launchRunner();
  }

  function aspectCB(fileArray) {

    terrainArray[1] = fileArray;
    RunString = RunString.replace(/ASPECTMAP_PC/, JSON.stringify(terrainArray[1]));
    console.log('Aspect Map is loaded in string "Run"');
    launchRunner();
  }

  function launchRunner() {

    --runnerCounter;

    if (runnerCounter > 0)
      return;

    CrowdProcessConnect(function (jobs) {

      document.querySelector('#progress h2').innerHTML = 'Monte Carlo Sample Simulations: ' + mcSamples;

      hidesEl('input');
      showsEl('progress');

      jobs.run(RunString, 1, dataArray, onProgress, onResult, onFinished);

      function onProgress(progress) {

        progressbar.progressbar("value", progress);

      }

      function onResult(err, result) {

        if (resultsIdx === 0) ts = Date.now();

        if (err) console.log(err);

        resultsArray[resultsIdx] = JSON.parse(result);

        console.log('Arrived result #', resultsIdx);
        ++resultsIdx;

        if (resultsIdx < mcSamples)
          return;

        tt = (Date.now() - ts)/1000;

        document.getElementById('timer').innerHTML = 'Total run time:'+tt.toString();

        document.getElementById('ppProg').innerHTML = 'Post Processing...';

        for (var i = 0; i < ROWS * COLS; i++)
          visArray[i] = 0;

        for (var n = 0; n < mcSamples; n++) {
          for (var i = 0; i < ROWS * COLS; i++) {
            visArray[i] += resultsArray[n][i]/ mcSamples;

          }
        }

        document.getElementById('ppProg').innerHTML = 'Post Processing...Done';

        showsEl('resultsButton');

      }

      function onFinished() {}

    });

    //Progress Bar function
    $(function () {

      progressbar.progressbar({
        value: false,
        change: function () {
          progressLabel.text(progressbar.progressbar("value") + "%");
        },
        complete: function () {
          // progressLabel.text( "Complete!" );
        }
      });
    });

  }

  function replaceRunPlaceHolders() {

    RunString = RunString.replace(/ROWS_PC/, ROWS);
    RunString = RunString.replace(/COLS_PC/, COLS);
    launchRunner();
  }

  function arrayFromGrassFile(fileName, cb) {

    var array = new Array(ROWS * COLS);

    var req = new XMLHttpRequest();

    req.onreadystatechange = onreadystatechange;

    req.open('GET', fileName);

    req.send();

    function onreadystatechange() {

      if (req.readyState !== 4)
        return;

      //Reads Grass file and stores data in array
      array = readGrassFile(req.responseText);

      cb(array);
    }
  }

  function readGrassFile(data) {

    /*
      funcao que recebe ficheiros grass em string e faz parse para float array
    */

    //variavel com os valores numericos do mapa
    var dataMap = [];

    //variavel com a string do map sem cabeçalho
    var dataString = data.replace(/(.+?\n){6}/, '').match(/[\d.]+/g);

    //Apaga a primeira fileira de zeros do mapa de alturas

    //pasa os elementos de string pa float
    //(exclui a primeira e a segunda fileira de zeros)
    for (var cell = 0; cell < COLS * (ROWS); cell++)
      dataMap[cell] = parseFloat(dataString[cell]);

    return dataMap;
  }

  function gauss(avg, sDev) {

    //returns random number with gaussian distribution
    //uses box Muller algorithm to compute normal 0-1 distribution

    var gaussNumber = avg + sDev * BoxMuller()[0];

    return gaussNumber;
  }

  function BoxMuller() {

    //Normal distribution mean 0, deviation 1
    //allways returns an array with 2 random numbers

    var x = 0,
      y = 0,
      rds, c;

    // Get two random numbers from -1 to 1.
    // If the radius is zero or greater than 1, throw them out and pick two new ones
    // Rejection sampling throws away about 20% of the pairs.
    do {
      x = Math.random() * 2 - 1;
      y = Math.random() * 2 - 1;
      rds = x * x + y * y;
    }
    while (rds === 0 || rds > 1);

    // This magic is the Box-Muller Transform
    c = Math.sqrt(-2 * Math.log(rds) / rds);

    // It always creates a pair of numbers. I'll return them in an array.
    // This function is quite efficient so don't be afraid to throw one away if you don't need both.
    return [x * c, y * c];
  }

} //Launch

function  updateBox(){

  var slider = document.getElementById('slider1');
  var rangeValue = document.getElementById('rangeValue1');
  plotTime = slider1.value;

  rangeValue.setAttribute('value',plotTime);

}

function visualize() {

  var surfacePlot;

  var slider = document.getElementById('slider1');
  var rangeValue = document.getElementById('rangeValue1');

  var plotTime = slider.value;

  var heightMap = new Array(ROWS * COLS);

  showsEl('visualization');

  setUp();

  function setUp() {

    //Cria request para leitura dos results e alturas
    //req -> request, objecto que vai conter os dados do ficheiro
    var req = new XMLHttpRequest();

    //Lanca funcao de leitura
    req.onreadystatechange = onreadystatechange;

    //requeste do mapa de altimetria
    req.open('GET', '/InputMaps/malcataHeight_' + ROWS.toString() + '.grass', true);
    req.send();

    function onreadystatechange() {

      if (req.readyState !== 4)
        return;

      //le ficheiro de altimetria e cria mapa de altimetria
      //heightMap tem formato vector
      heightMap = readGrassFile(req.responseText)

      //Reinitialize heatMap (erases zeroes)
      for (var cell = 0; cell < COLS * ROWS; cell++)
        heatMap[cell] = visArray[cell];

      //calculo do valor maximo do mapa para ficar adimensionalizado
      maxHeatMap = 0;
      for (var cell = 0; cell < COLS * ROWS; cell++)
        maxHeatMap = (heatMap[cell] > maxHeatMap) ? heatMap[cell] : maxHeatMap;

      console.log('Max Ignition Time', maxHeatMap);

      //Initializes slide and rabgeValue text box
      slider.setAttribute('min',0);
      slider.setAttribute('max',maxHeatMap);
      slider.setAttribute('step',maxHeatMap/100);

      console.log('Slider plot time', plotTime);

      //Burned area limit
      for (var cell = 0; cell < COLS * ROWS; cell++) {
        if (heatMap[cell] > plotTime)
          heatMap[cell] = 0;
      }

      //printar o plot no ecran depois de carregar a Array
      plotZ();

    }

  }

  function plotZ() {

    var tooltipStrings = new Array();
    var values = new Array(ROWS);
    var data = {
      nRows: ROWS,
      nCols: COLS,
      formattedValues: values
    };

    //var d = 360 / ROWS;
    var idx = 0;

    for (var i = 0; i < ROWS; i++) {

      values[i] = new Array(COLS);

      for (var j = 0; j < COLS; j++) {
        values[i][j] = heightMap[j + i * COLS];
        tooltipStrings[idx] = "x:" + i + ", y:" + j + " = " + heatMap[j + i * COLS];
        idx++;
      }
    }

    var oldPlot1 = document.getElementById("surfacePlot1");

    if(oldPlot1) oldPlot1.remove();

    surfacePlot = new SurfacePlot(document.getElementById("surfacePlotDiv"));

    // Don't fill polygons in IE. It's too slow.
    var fillPly = true;

    // Define a colour gradient.
    var black = {
      red: 0,
      green: 0,
      blue: 0
    };
    var white = {
      red: 255,
      green: 255,
      blue: 255
    };

    var lessWhite = {
      red: 255,
      green: 100,
      blue: 100
    };

    var lesserWhite = {
      red: 255,
      green: 20,
      blue: 20
    };

    var greenIsh = {
      red: 193,
      green: 265,
      blue: 167
    };
    var colour1 = {
      red: 0,
      green: 0,
      blue: 255
    };
    var colour2 = {
      red: 0,
      green: 255,
      blue: 255
    };
    var colour3 = {
      red: 0,
      green: 255,
      blue: 0
    };
    var colour4 = {
      red: 255,
      green: 255,
      blue: 0
    };
    var colour5 = {
      red: 255,
      green: 0,
      blue: 0
    };
    //var colours = [white, colour1, colour2, colour3, colour4, colour5];
    var colours = [greenIsh, colour5];
    //var colours = [ white, colour4, colour5];
    //var colours = [ white,  colour5 ];

    // Axis labels.
    var xAxisHeader = "X-axis";
    var yAxisHeader = "Y-axis";
    var zAxisHeader = "Z-axis";

    var renderDataPoints = false;
    var background = '#ffffff';
    var axisForeColour = '#ff0000';
    var hideFloorPolygons = true;
    var chartOrigin1 = {
      x: 350,
      y: 350
    };

    // Options for the basic canvas pliot.
    var basicPlotOptions = {
      fillPolygons: fillPly,
      tooltips: tooltipStrings,
      renderPoints: renderDataPoints
    };

    // Options for the webGL plot.
    var xLabels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    var yLabels = [0, 1, 2, 3, 4, 5];
    var zLabels = [0, 1, 2, 3, 4, 5, 6]; // These labels are used when autoCalcZScale is false;
    var glOptions = {
      xLabels: xLabels,
      yLabels: yLabels,
      zLabels: zLabels,
      chkControlId: "allowWebGL",
      autoCalcZScale: true
    };

    // Options plot 1

    var options = {
      left: 0,
      right: 0,
      width: 700,
      height: 700,
      colourGradient: colours,
      xTitle: xAxisHeader,
      yTitle: yAxisHeader,
      zTitle: zAxisHeader,
      backColour: background,
      axisTextColour: axisForeColour,
      hideFlatMinPolygons: hideFloorPolygons,
      origin: chartOrigin1
    };

    surfacePlot.draw(data, options, basicPlotOptions, glOptions);

    // Link the two charts for rotation.
    //var plot1 = surfacePlot.getChart();
    //plot1.otherPlots = [plot2];
  }

  function toggleChart(chkbox) {
    surfacePlot.redraw();
  }

  function readGrassFile(data) {

    /*
        funcao para leitura de ficheiro e criacao de matrix para ser plotado
      */

    //variavel com os valores numericos do mapa
    var dataMap = [];

    //variavel com a string do map sem cabeçalho
    var dataString = data.replace(/(.+?\n){6}/, '').match(/[\d.]+/g);

    //Apaga a primeira fileira de zeros do mapa de alturas
    //dataString.slice(ROWS);

    //pasa os elementos de string pa float
    //(exclui a primeira e a segunda fileira de zeros)
    for (var cell = 0; cell < COLS * (ROWS); cell++)
      dataMap[cell] = parseFloat(dataString[cell]);

    for (var cell = 0; cell < COLS; cell++)
      dataMap[cell] = dataMap[COLS+cell];

    for (var cell = 0; cell < COLS; cell++)
      dataMap[cell+COLS*(ROWS-1)] = dataMap[cell+COLS*(ROWS-2)];

    if (ROWS > 100){
      for (var row = 0; row < ROWS; row++){
        //na fronteira oeste, vai buscar o valor a direira
        dataMap[row*COLS] = dataMap[row*COLS + 1];
        //na fronteira este, vai buscar o valor a esquerda
        dataMap[(row+1)*COLS-1] = dataMap[(row+1)*COLS-2];

      }

    }

    return dataMap;
  }

}//Visualize

function showsEl(boxid) {
  document.getElementById(boxid).style.display = "initial";
}

function hidesEl(boxid) {
  document.getElementById(boxid).style.display = "none";
}<commit_msg>onError<commit_after>//Variaveis da progress bar têm de ser globais por causa do scope
var progressbar = $("#progressbar");
var progressLabel = $(".progress-label");

var ROWS;
var COLS;

//Global variables for Run time
var ts;
var tt;

/*
    O heatMap e o maxHeatMap sao variaveis globais
    lidas pelo ficheiro SurfacePlot.js
*/
var heatMap;
var maxHeatMap = 0;
var plotTime;

//Visualization array. vector with ignition time results
//computed from Monte Carlo samples
var visArray;

function launch() {

  var i, n;

  var numVar = 3; //Moisture, windSpeed, windDir

  ROWS = parseInt(document.getElementById('resolution').value);
  COLS = parseInt(document.getElementById('resolution').value);

  var ignPointYY = parseInt(document.getElementById('ignPointYY').value);
  var ignPointXX = parseInt(document.getElementById('ignPointXX').value);

  var moisture = parseFloat(document.getElementById('moistureInput').value);
  var fuelLoad = parseFloat(document.getElementById('fuelLoad').value);
  var windDir = parseFloat(document.getElementById('windDir').value);

  var mcSamples = parseInt(document.getElementById('mcSamples').value);
  var windVelAvg = parseFloat(document.getElementById('windVelAvg').value);
  var windVelDev = parseFloat(document.getElementById('windVelDev').value);

  //Create data, results and visualization Array
  var dataArray = new Array(mcSamples);
  var resultsIdx = 0;
  var resultsArray = new Array(mcSamples);
  visArray = new Array(ROWS * COLS);

  heatMap = new Array(ROWS * COLS);

  for (i = 0; i < mcSamples; i++) {

    dataArray[i] = new Array(numVar);
    resultsArray[i] = new Array(COLS * ROWS);

  }

  //Populate: |moiture|windSpeed|windDir|
  for (i = 0; i < mcSamples; i++) {
    dataArray[i][0] = moisture;
    dataArray[i][1] = gauss(windVelAvg, windVelDev/100*windVelAvg);
    dataArray[i][1] = (dataArray[i][1] >= 0 ? dataArray[i][1] : 0);
    console.log('mcSample,U:',i,dataArray[i][1]);
    dataArray[i][2] = windDir;
  }

  //Prepare Run Function
  RunString = Run.toString();

  //storage for slope (entry 0) and aspect (entry 1)
  var terrainArray = [0, 0];

  //Read aspect and slope files and create arrays
  //ACHTUNG Esta porra e assincrona!
  var runnerCounter = 3;
  arrayFromGrassFile('/InputMaps/malcataSlope_' + ROWS.toString() + '.grass', slopeCB);
  arrayFromGrassFile('/InputMaps/malcataAspect_' + ROWS.toString() + '.grass', aspectCB);

  replaceRunPlaceHolders();

  function slopeCB(fileArray) {

    terrainArray[0] = fileArray;
    RunString = RunString.replace(/SLOPEMAP_PC/, JSON.stringify(terrainArray[0]));
    console.log('Slope Map is loaded in string "Run"');
    launchRunner();
  }

  function aspectCB(fileArray) {

    terrainArray[1] = fileArray;
    RunString = RunString.replace(/ASPECTMAP_PC/, JSON.stringify(terrainArray[1]));
    console.log('Aspect Map is loaded in string "Run"');
    launchRunner();
  }

  function launchRunner() {

    --runnerCounter;

    if (runnerCounter > 0)
      return;

    CrowdProcessConnect(function (jobs) {

      document.querySelector('#progress h2').innerHTML = 'Monte Carlo Sample Simulations: ' + mcSamples;

      hidesEl('input');
      showsEl('progress');

      jobs.run(RunString, 1, dataArray, onError, onProgress, onResult, onFinished);

      function onError(error) {
        console.error(error);
      }

      function onProgress(progress) {

        progressbar.progressbar("value", progress);

      }

      function onResult(err, result) {

        if (resultsIdx === 0) ts = Date.now();

        if (err) console.log(err);

        resultsArray[resultsIdx] = JSON.parse(result);

        console.log('Arrived result #', resultsIdx);
        ++resultsIdx;

        if (resultsIdx < mcSamples)
          return;

        tt = (Date.now() - ts)/1000;

        document.getElementById('timer').innerHTML = 'Total run time:'+tt.toString();

        document.getElementById('ppProg').innerHTML = 'Post Processing...';

        for (var i = 0; i < ROWS * COLS; i++)
          visArray[i] = 0;

        for (var n = 0; n < mcSamples; n++) {
          for (var i = 0; i < ROWS * COLS; i++) {
            visArray[i] += resultsArray[n][i]/ mcSamples;

          }
        }

        document.getElementById('ppProg').innerHTML = 'Post Processing...Done';

        showsEl('resultsButton');

      }

      function onFinished(realTime, jobTime) {
        console.log('the job finished in', realTime, 'it happened in', jobTime);
      }

    });

    //Progress Bar function
    $(function () {

      progressbar.progressbar({
        value: false,
        change: function () {
          progressLabel.text(progressbar.progressbar("value") + "%");
        },
        complete: function () {
          // progressLabel.text( "Complete!" );
        }
      });
    });

  }

  function replaceRunPlaceHolders() {

    RunString = RunString.replace(/ROWS_PC/, ROWS);
    RunString = RunString.replace(/COLS_PC/, COLS);
    launchRunner();
  }

  function arrayFromGrassFile(fileName, cb) {

    var array = new Array(ROWS * COLS);

    var req = new XMLHttpRequest();

    req.onreadystatechange = onreadystatechange;

    req.open('GET', fileName);

    req.send();

    function onreadystatechange() {

      if (req.readyState !== 4)
        return;

      //Reads Grass file and stores data in array
      array = readGrassFile(req.responseText);

      cb(array);
    }
  }

  function readGrassFile(data) {

    /*
      funcao que recebe ficheiros grass em string e faz parse para float array
    */

    //variavel com os valores numericos do mapa
    var dataMap = [];

    //variavel com a string do map sem cabeçalho
    var dataString = data.replace(/(.+?\n){6}/, '').match(/[\d.]+/g);

    //Apaga a primeira fileira de zeros do mapa de alturas

    //pasa os elementos de string pa float
    //(exclui a primeira e a segunda fileira de zeros)
    for (var cell = 0; cell < COLS * (ROWS); cell++)
      dataMap[cell] = parseFloat(dataString[cell]);

    return dataMap;
  }

  function gauss(avg, sDev) {

    //returns random number with gaussian distribution
    //uses box Muller algorithm to compute normal 0-1 distribution

    var gaussNumber = avg + sDev * BoxMuller()[0];

    return gaussNumber;
  }

  function BoxMuller() {

    //Normal distribution mean 0, deviation 1
    //allways returns an array with 2 random numbers

    var x = 0,
      y = 0,
      rds, c;

    // Get two random numbers from -1 to 1.
    // If the radius is zero or greater than 1, throw them out and pick two new ones
    // Rejection sampling throws away about 20% of the pairs.
    do {
      x = Math.random() * 2 - 1;
      y = Math.random() * 2 - 1;
      rds = x * x + y * y;
    }
    while (rds === 0 || rds > 1);

    // This magic is the Box-Muller Transform
    c = Math.sqrt(-2 * Math.log(rds) / rds);

    // It always creates a pair of numbers. I'll return them in an array.
    // This function is quite efficient so don't be afraid to throw one away if you don't need both.
    return [x * c, y * c];
  }

} //Launch

function  updateBox(){

  var slider = document.getElementById('slider1');
  var rangeValue = document.getElementById('rangeValue1');
  plotTime = slider1.value;

  rangeValue.setAttribute('value',plotTime);

}

function visualize() {

  var surfacePlot;

  var slider = document.getElementById('slider1');
  var rangeValue = document.getElementById('rangeValue1');

  var plotTime = slider.value;

  var heightMap = new Array(ROWS * COLS);

  showsEl('visualization');

  setUp();

  function setUp() {

    //Cria request para leitura dos results e alturas
    //req -> request, objecto que vai conter os dados do ficheiro
    var req = new XMLHttpRequest();

    //Lanca funcao de leitura
    req.onreadystatechange = onreadystatechange;

    //requeste do mapa de altimetria
    req.open('GET', '/InputMaps/malcataHeight_' + ROWS.toString() + '.grass', true);
    req.send();

    function onreadystatechange() {

      if (req.readyState !== 4)
        return;

      //le ficheiro de altimetria e cria mapa de altimetria
      //heightMap tem formato vector
      heightMap = readGrassFile(req.responseText)

      //Reinitialize heatMap (erases zeroes)
      for (var cell = 0; cell < COLS * ROWS; cell++)
        heatMap[cell] = visArray[cell];

      //calculo do valor maximo do mapa para ficar adimensionalizado
      maxHeatMap = 0;
      for (var cell = 0; cell < COLS * ROWS; cell++)
        maxHeatMap = (heatMap[cell] > maxHeatMap) ? heatMap[cell] : maxHeatMap;

      console.log('Max Ignition Time', maxHeatMap);

      //Initializes slide and rabgeValue text box
      slider.setAttribute('min',0);
      slider.setAttribute('max',maxHeatMap);
      slider.setAttribute('step',maxHeatMap/100);

      console.log('Slider plot time', plotTime);

      //Burned area limit
      for (var cell = 0; cell < COLS * ROWS; cell++) {
        if (heatMap[cell] > plotTime)
          heatMap[cell] = 0;
      }

      //printar o plot no ecran depois de carregar a Array
      plotZ();

    }

  }

  function plotZ() {

    var tooltipStrings = new Array();
    var values = new Array(ROWS);
    var data = {
      nRows: ROWS,
      nCols: COLS,
      formattedValues: values
    };

    //var d = 360 / ROWS;
    var idx = 0;

    for (var i = 0; i < ROWS; i++) {

      values[i] = new Array(COLS);

      for (var j = 0; j < COLS; j++) {
        values[i][j] = heightMap[j + i * COLS];
        tooltipStrings[idx] = "x:" + i + ", y:" + j + " = " + heatMap[j + i * COLS];
        idx++;
      }
    }

    var oldPlot1 = document.getElementById("surfacePlot1");

    if(oldPlot1) oldPlot1.remove();

    surfacePlot = new SurfacePlot(document.getElementById("surfacePlotDiv"));

    // Don't fill polygons in IE. It's too slow.
    var fillPly = true;

    // Define a colour gradient.
    var black = {
      red: 0,
      green: 0,
      blue: 0
    };
    var white = {
      red: 255,
      green: 255,
      blue: 255
    };

    var lessWhite = {
      red: 255,
      green: 100,
      blue: 100
    };

    var lesserWhite = {
      red: 255,
      green: 20,
      blue: 20
    };

    var greenIsh = {
      red: 193,
      green: 265,
      blue: 167
    };
    var colour1 = {
      red: 0,
      green: 0,
      blue: 255
    };
    var colour2 = {
      red: 0,
      green: 255,
      blue: 255
    };
    var colour3 = {
      red: 0,
      green: 255,
      blue: 0
    };
    var colour4 = {
      red: 255,
      green: 255,
      blue: 0
    };
    var colour5 = {
      red: 255,
      green: 0,
      blue: 0
    };
    //var colours = [white, colour1, colour2, colour3, colour4, colour5];
    var colours = [greenIsh, colour5];
    //var colours = [ white, colour4, colour5];
    //var colours = [ white,  colour5 ];

    // Axis labels.
    var xAxisHeader = "X-axis";
    var yAxisHeader = "Y-axis";
    var zAxisHeader = "Z-axis";

    var renderDataPoints = false;
    var background = '#ffffff';
    var axisForeColour = '#ff0000';
    var hideFloorPolygons = true;
    var chartOrigin1 = {
      x: 350,
      y: 350
    };

    // Options for the basic canvas pliot.
    var basicPlotOptions = {
      fillPolygons: fillPly,
      tooltips: tooltipStrings,
      renderPoints: renderDataPoints
    };

    // Options for the webGL plot.
    var xLabels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    var yLabels = [0, 1, 2, 3, 4, 5];
    var zLabels = [0, 1, 2, 3, 4, 5, 6]; // These labels are used when autoCalcZScale is false;
    var glOptions = {
      xLabels: xLabels,
      yLabels: yLabels,
      zLabels: zLabels,
      chkControlId: "allowWebGL",
      autoCalcZScale: true
    };

    // Options plot 1

    var options = {
      left: 0,
      right: 0,
      width: 700,
      height: 700,
      colourGradient: colours,
      xTitle: xAxisHeader,
      yTitle: yAxisHeader,
      zTitle: zAxisHeader,
      backColour: background,
      axisTextColour: axisForeColour,
      hideFlatMinPolygons: hideFloorPolygons,
      origin: chartOrigin1
    };

    surfacePlot.draw(data, options, basicPlotOptions, glOptions);

    // Link the two charts for rotation.
    //var plot1 = surfacePlot.getChart();
    //plot1.otherPlots = [plot2];
  }

  function toggleChart(chkbox) {
    surfacePlot.redraw();
  }

  function readGrassFile(data) {

    /*
        funcao para leitura de ficheiro e criacao de matrix para ser plotado
      */

    //variavel com os valores numericos do mapa
    var dataMap = [];

    //variavel com a string do map sem cabeçalho
    var dataString = data.replace(/(.+?\n){6}/, '').match(/[\d.]+/g);

    //Apaga a primeira fileira de zeros do mapa de alturas
    //dataString.slice(ROWS);

    //pasa os elementos de string pa float
    //(exclui a primeira e a segunda fileira de zeros)
    for (var cell = 0; cell < COLS * (ROWS); cell++)
      dataMap[cell] = parseFloat(dataString[cell]);

    for (var cell = 0; cell < COLS; cell++)
      dataMap[cell] = dataMap[COLS+cell];

    for (var cell = 0; cell < COLS; cell++)
      dataMap[cell+COLS*(ROWS-1)] = dataMap[cell+COLS*(ROWS-2)];

    if (ROWS > 100){
      for (var row = 0; row < ROWS; row++){
        //na fronteira oeste, vai buscar o valor a direira
        dataMap[row*COLS] = dataMap[row*COLS + 1];
        //na fronteira este, vai buscar o valor a esquerda
        dataMap[(row+1)*COLS-1] = dataMap[(row+1)*COLS-2];

      }

    }

    return dataMap;
  }

}//Visualize

function showsEl(boxid) {
  document.getElementById(boxid).style.display = "initial";
}

function hidesEl(boxid) {
  document.getElementById(boxid).style.display = "none";
}<|endoftext|>


<commit_before>module.exports = [
  {
    path: 'package/swiper-bundle.js',
    name: 'bundle',
    limit: '40 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'ESM bundle',
    import: 'SwiperCore, { Navigation, Pagination, Scrollbar, A11y, Virtual, Zoom }',
    limit: '30 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Core',
    import: 'SwiperCore',
    limit: '30 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Navigation',
    import: '{ Navigation }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Pagination',
    import: '{ Pagination }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Virtual',
    import: '{ Virtual }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Keyboard',
    import: '{ Keyboard }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Mousewheel',
    import: '{ Mousewheel }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Scrollbar',
    import: '{ Scrollbar }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Parallax',
    import: '{ Parallax }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Zoom',
    import: '{ Zoom }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Lazy',
    import: '{ Lazy }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Controller',
    import: '{ Controller }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'A11y',
    import: '{ A11y }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'History',
    import: '{ History }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'HashNavigation',
    import: '{ HashNavigation }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Autoplay',
    import: '{ Autoplay }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'EffectFade',
    import: '{ EffectFade }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'EffectCube',
    import: '{ EffectCube }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'EffectFlip',
    import: '{ EffectFlip }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'EffectCoverflow',
    import: '{ EffectCoverflow }',
    limit: '25 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Thumbs',
    import: '{ Thumbs }',
    limit: '25 KB',
  },
];
<commit_msg>chore: size limit sizes<commit_after>module.exports = [
  {
    path: 'package/swiper-bundle.js',
    name: 'bundle',
    limit: '40 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'ESM bundle',
    import:
      'SwiperCore, { Navigation, Pagination, Virtual, Keyboard, Scrollbar, A11y, Virtual, Zoom, Mousewheel, Lazy, History, HashNavigation, EffectFade, EffectCube, EffectFlip, EffectCoverflow, Thumbs, Autoplay, Controller, Parallax }',
    limit: '40 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Core',
    import: 'SwiperCore',
    limit: '20 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Navigation',
    import: '{ Navigation }',
    limit: '5 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Pagination',
    import: '{ Pagination }',
    limit: '7 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Virtual',
    import: '{ Virtual }',
    limit: '6 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Keyboard',
    import: '{ Keyboard }',
    limit: '5 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Mousewheel',
    import: '{ Mousewheel }',
    limit: '7 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Scrollbar',
    import: '{ Scrollbar }',
    limit: '7 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Parallax',
    import: '{ Parallax }',
    limit: '8 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Zoom',
    import: '{ Zoom }',
    limit: '7 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Lazy',
    import: '{ Lazy }',
    limit: '7 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Controller',
    import: '{ Controller }',
    limit: '2 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'A11y',
    import: '{ A11y }',
    limit: '7 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'History',
    import: '{ History }',
    limit: '3 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'HashNavigation',
    import: '{ HashNavigation }',
    limit: '6 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Autoplay',
    import: '{ Autoplay }',
    limit: '3 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'EffectFade',
    import: '{ EffectFade }',
    limit: '2 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'EffectCube',
    import: '{ EffectCube }',
    limit: '7 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'EffectFlip',
    import: '{ EffectFlip }',
    limit: '7 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'EffectCoverflow',
    import: '{ EffectCoverflow }',
    limit: '7 KB',
  },
  {
    path: 'package/swiper.esm.js',
    name: 'Thumbs',
    import: '{ Thumbs }',
    limit: '7 KB',
  },
];
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
var fs = require('fs')
  , path = require('path')
  , util = require('util')
  , Strategy = require('./strategy')
  , SessionStrategy = require('./strategies/session')
  , initialize = require('./middleware/initialize')
  , authenticate = require('./middleware/authenticate');

/**
 * `Passport` constructor.
 *
 * @api public
 */
function Passport() {
  this._key = 'passport';
  this._strategies = {};
  this._serializers = [];
  this._deserializers = [];
  this._infoTransformers = [];
  this._plugin = null;

  this._userProperty = 'user';

  this.use(new SessionStrategy());
};

/**
 * Utilize the given `strategy` with optional `name`, overridding the strategy's
 * default name.
 *
 * Examples:
 *
 *     passport.use(new TwitterStrategy(...));
 *
 *     passport.use('api', new http.BasicStrategy(...));
 *
 * @param {String|Strategy} name
 * @param {Strategy} strategy
 * @return {Passport} for chaining
 * @api public
 */
Passport.prototype.use = function(name, strategy) {
  if (!strategy) {
    strategy = name;
    name = strategy.name;
  }
  if (!name) throw new Error('authentication strategies must have a name');

  this._strategies[name] = strategy;
  return this;
};

/**
 * Un-utilize the `strategy` with given `name`.
 *
 * In typical applications, the necessary authentication strategies are static,
 * configured once and always available.  As such, there is often no need to
 * invoke this function.
 *
 * However, in certain situations, applications may need dynamically configure
 * and de-configure authentication strategies.  The `use()`/`unuse()`
 * combination satisfies these scenarios.
 *
 * Examples:
 *
 *     passport.unuse('legacy-api');
 *
 * @param {String} name
 * @return {Passport} for chaining
 * @api public
 */
Passport.prototype.unuse = function(name) {
  delete this._strategies[name];
  return this;
}

/**
 * Passport's primary initialization middleware.
 *
 * This middleware must be in use by the Connect/Express application for
 * Passport to operate.
 *
 * Options:
 *   - `userProperty`  Property to set on `req` upon login, defaults to _user_
 *
 * Examples:
 *
 *     app.configure(function() {
 *       app.use(passport.initialize());
 *     });
 *
 *     app.configure(function() {
 *       app.use(passport.initialize({ userProperty: 'currentUser' }));
 *     });
 *
 * @param {Object} options
 * @return {Function} middleware
 * @api public
 */
Passport.prototype.initialize = function(options) {
  options = options || {};
  this._userProperty = options.userProperty || 'user';

  if (this._plugin && this._plugin.initialize) {
    return this._plugin.initialize().bind(this);
  }

  return initialize().bind(this);
}

/**
 * Middleware that will restore login state from a session.
 *
 * Web applications typically use sessions to maintain login state between
 * requests.  For example, a user will authenticate by entering credentials into
 * a form which is submitted to the server.  If the credentials are valid, a
 * login session is established by setting a cookie containing a session
 * identifier in the user's web browser.  The web browser will send this cookie
 * in subsequent requests to the server, allowing a session to be maintained.
 *
 * If sessions are being utilized, and a login session has been established,
 * this middleware will populate `req.user` with the current user.
 *
 * Note that sessions are not strictly required for Passport to operate.
 * However, as a general rule, most web applications will make use of sessions.
 * An exception to this rule would be an API server, which expects each HTTP
 * request to provide credentials in an Authorization header.
 *
 * Examples:
 *
 *     app.configure(function() {
 *       app.use(connect.cookieParser());
 *       app.use(connect.session({ secret: 'keyboard cat' }));
 *       app.use(passport.initialize());
 *       app.use(passport.session());
 *     });
 *
 * @return {Function} middleware
 * @api public
 */
Passport.prototype.session = function() {
  return this.authenticate('session');
}

/**
 * Middleware that will authenticate a request using the given `strategy` name,
 * with optional `options` and `callback`.
 *
 * Examples:
 *
 *     passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })(req, res);
 *
 *     passport.authenticate('local', function(err, user) {
 *       if (!user) { return res.redirect('/login'); }
 *       res.end('Authenticated!');
 *     })(req, res);
 *
 *     passport.authenticate('basic', { session: false })(req, res);
 *
 *     app.get('/auth/twitter', passport.authenticate('twitter'), function(req, res) {
 *       // request will be redirected to Twitter
 *     });
 *     app.get('/auth/twitter/callback', passport.authenticate('twitter'), function(req, res) {
 *       res.json(req.user);
 *     });
 *
 * @param {String} strategy
 * @param {Object} options
 * @param {Function} callback
 * @return {Function} middleware
 * @api public
 */
Passport.prototype.authenticate = function(strategy, options, callback) {
  if (this._plugin && this._plugin.authenticate) {
    return this._plugin.authenticate(strategy, options, callback).bind(this);
  }

  return authenticate(strategy, options, callback).bind(this);
}

/**
 * Middleware that will authorize a third-party account using the given
 * `strategy` name, with optional `options`.
 *
 * If authorization is successful, the result provided by the strategy's verify
 * callback will be assigned to `req.account`.  The existing login session and
 * `req.user` will be unaffected.
 *
 * This function is particularly useful when connecting third-party accounts
 * to the local account of a user that is currently authenticated.
 *
 * Examples:
 *
 *    passport.authorize('twitter-authz', { failureRedirect: '/account' });
 *
 * @param {String} strategy
 * @param {Object} options
 * @return {Function} middleware
 * @api public
 */
Passport.prototype.authorize = function(strategy, options, callback) {
  options = options || {};
  options.assignProperty = 'account';

  return authenticate(strategy, options, callback).bind(this);
}

/**
 * Registers a function used to serialize user objects into the session.
 *
 * Examples:
 *
 *     passport.serializeUser(function(user, done) {
 *       done(null, user.id);
 *     });
 *
 * @api public
 */
Passport.prototype.serializeUser = function(fn, done) {
  if (typeof fn === 'function') {
    return this._serializers.push(fn);
  }

  // private implementation that traverses the chain of serializers, attempting
  // to serialize a user
  var user = fn;

  var stack = this._serializers;
  (function pass(i, err, obj) {
    // serializers use 'pass' as an error to skip processing
    if ('pass' === err) {
      err = undefined;
    }
    // an error or serialized object was obtained, done
    if (err || obj || obj === 0) { return done(err, obj); }

    var layer = stack[i];
    if (!layer) {
      return done(new Error('failed to serialize user into session'));
    }

    try {
      layer(user, function(e, o) { pass(i + 1, e, o); } )
    } catch(e) {
      return done(e);
    }
  })(0);
}

/**
 * Registers a function used to deserialize user objects out of the session.
 *
 * Examples:
 *
 *     passport.deserializeUser(function(id, done) {
 *       User.findById(id, function (err, user) {
 *         done(err, user);
 *       });
 *     });
 *
 * @api public
 */
Passport.prototype.deserializeUser = function(fn, done) {
  if (typeof fn === 'function') {
    return this._deserializers.push(fn);
  }

  // private implementation that traverses the chain of deserializers,
  // attempting to deserialize a user
  var obj = fn;

  var stack = this._deserializers;
  (function pass(i, err, user) {
    // deserializers use 'pass' as an error to skip processing
    if ('pass' === err) {
      err = undefined;
    }
    // an error or deserialized user was obtained, done
    if (err || user) { return done(err, user); }
    // a valid user existed when establishing the session, but that user has
    // since been removed
    if (user === null || user === false) { return done(null, false); }

    var layer = stack[i];
    if (!layer) {
      return done(new Error('failed to deserialize user out of session'));
    }

    try {
      layer(obj, function(e, u) { pass(i + 1, e, u); } )
    } catch(e) {
      return done(e);
    }
  })(0);
}

/**
 * Registers a function used to transform auth info.
 *
 * In some circumstances authorization details are contained in authentication
 * credentials or loaded as part of verification.
 *
 * For example, when using bearer tokens for API authentication, the tokens may
 * encode (either directly or indirectly in a database), details such as scope
 * of access or the client to which the token was issued.
 *
 * Such authorization details should be enforced separately from authentication.
 * Because Passport deals only with the latter, this is the responsiblity of
 * middleware or routes further along the chain.  However, it is not optimal to
 * decode the same data or execute the same database query later.  To avoid
 * this, Passport accepts optional `info` along with the authenticated `user`
 * in a strategy's `success()` action.  This info is set at `req.authInfo`,
 * where said later middlware or routes can access it.
 *
 * Optionally, applications can register transforms to proccess this info,
 * which take effect prior to `req.authInfo` being set.  This is useful, for
 * example, when the info contains a client ID.  The transform can load the
 * client from the database and include the instance in the transformed info,
 * allowing the full set of client properties to be convieniently accessed.
 *
 * If no transforms are registered, `info` supplied by the strategy will be left
 * unmodified.
 *
 * Examples:
 *
 *     passport.transformAuthInfo(function(info, done) {
 *       Client.findById(info.clientID, function (err, client) {
 *         info.client = client;
 *         done(err, info);
 *       });
 *     });
 *
 * @api public
 */
Passport.prototype.transformAuthInfo = function(fn, done) {
  if (typeof fn === 'function') {
    return this._infoTransformers.push(fn);
  }

  // private implementation that traverses the chain of transformers,
  // attempting to transform auth info
  var info = fn;

  var stack = this._infoTransformers;
  (function pass(i, err, tinfo) {
    // transformers use 'pass' as an error to skip processing
    if ('pass' === err) {
      err = undefined;
    }
    // an error or transformed info was obtained, done
    if (err || tinfo) { return done(err, tinfo); }

    var layer = stack[i];
    if (!layer) {
      // if no transformers are registered (or they all pass), the default
      // behavior is to use the un-transformed info as-is
      return done(null, info);
    }

    try {
      var arity = layer.length;
      if (arity == 1) {
        // sync
        var t = layer(info);
        pass(i + 1, null, t);
      } else {
        // async
        layer(info, function(e, t) { pass(i + 1, e, t); } )
      }
    } catch(e) {
      return done(e);
    }
  })(0);
}

/**
 * Return strategy with given `name`.
 *
 * @param {String} name
 * @return {Strategy}
 * @api private
 */
Passport.prototype._strategy = function(name) {
  return this._strategies[name];
}

/**
 * Export default singleton.
 *
 * @api public
 */
exports = module.exports = new Passport();

/**
 * Framework version.
 */
require('pkginfo')(module, 'version');

/**
 * Expose constructors.
 */
exports.Passport = Passport;
exports.Strategy = Strategy;

/**
 * Expose strategies.
 */
exports.strategies = {};
exports.strategies.SessionStrategy = SessionStrategy;

/**
 * HTTP extensions.
 */
require('./http/request');<commit_msg>Slight tweaks for framework setup.<commit_after>/**
 * Module dependencies.
 */
var fs = require('fs')
  , path = require('path')
  , util = require('util')
  , Strategy = require('./strategy')
  , SessionStrategy = require('./strategies/session')
  , initialize = require('./middleware/initialize')
  , authenticate = require('./middleware/authenticate');

/**
 * `Passport` constructor.
 *
 * @api public
 */
function Passport() {
  this._key = 'passport';
  this._strategies = {};
  this._serializers = [];
  this._deserializers = [];
  this._infoTransformers = [];
  this._framework = null;

  this._userProperty = 'user';

  this.use(new SessionStrategy());
};

/**
 * Utilize the given `strategy` with optional `name`, overridding the strategy's
 * default name.
 *
 * Examples:
 *
 *     passport.use(new TwitterStrategy(...));
 *
 *     passport.use('api', new http.BasicStrategy(...));
 *
 * @param {String|Strategy} name
 * @param {Strategy} strategy
 * @return {Passport} for chaining
 * @api public
 */
Passport.prototype.use = function(name, strategy) {
  if (!strategy) {
    strategy = name;
    name = strategy.name;
  }
  if (!name) throw new Error('authentication strategies must have a name');

  this._strategies[name] = strategy;
  return this;
};

/**
 * Un-utilize the `strategy` with given `name`.
 *
 * In typical applications, the necessary authentication strategies are static,
 * configured once and always available.  As such, there is often no need to
 * invoke this function.
 *
 * However, in certain situations, applications may need dynamically configure
 * and de-configure authentication strategies.  The `use()`/`unuse()`
 * combination satisfies these scenarios.
 *
 * Examples:
 *
 *     passport.unuse('legacy-api');
 *
 * @param {String} name
 * @return {Passport} for chaining
 * @api public
 */
Passport.prototype.unuse = function(name) {
  delete this._strategies[name];
  return this;
}

/**
 * Setup Passport to be used under framework.
 *
 * By default, Passport exposes middleware that operate using Connect-style
 * middleware using a `fn(req, res, next)` signature.  Other popular frameworks
 * have different expectations, and this function allows Passport to be adapted
 * to operate within such environments.
 *
 * If you are using a Connect-compatible framework, including Express, there is
 * no need to invoke this function.
 *
 * Examples:
 *
 *     passport.framework(require('hapi-passport')());
 *
 * @param {Object} name
 * @return {Passport} for chaining
 * @api public
 */
Passport.prototype.framework = function(fw) {
  this._framework = fw;
  return this;
}

/**
 * Passport's primary initialization middleware.
 *
 * This middleware must be in use by the Connect/Express application for
 * Passport to operate.
 *
 * Options:
 *   - `userProperty`  Property to set on `req` upon login, defaults to _user_
 *
 * Examples:
 *
 *     app.configure(function() {
 *       app.use(passport.initialize());
 *     });
 *
 *     app.configure(function() {
 *       app.use(passport.initialize({ userProperty: 'currentUser' }));
 *     });
 *
 * @param {Object} options
 * @return {Function} middleware
 * @api public
 */
Passport.prototype.initialize = function(options) {
  options = options || {};
  this._userProperty = options.userProperty || 'user';

  if (this._framework && this._framework.initialize) {
    return this._framework.initialize().bind(this);
  }

  return initialize().bind(this);
}

/**
 * Middleware that will restore login state from a session.
 *
 * Web applications typically use sessions to maintain login state between
 * requests.  For example, a user will authenticate by entering credentials into
 * a form which is submitted to the server.  If the credentials are valid, a
 * login session is established by setting a cookie containing a session
 * identifier in the user's web browser.  The web browser will send this cookie
 * in subsequent requests to the server, allowing a session to be maintained.
 *
 * If sessions are being utilized, and a login session has been established,
 * this middleware will populate `req.user` with the current user.
 *
 * Note that sessions are not strictly required for Passport to operate.
 * However, as a general rule, most web applications will make use of sessions.
 * An exception to this rule would be an API server, which expects each HTTP
 * request to provide credentials in an Authorization header.
 *
 * Examples:
 *
 *     app.configure(function() {
 *       app.use(connect.cookieParser());
 *       app.use(connect.session({ secret: 'keyboard cat' }));
 *       app.use(passport.initialize());
 *       app.use(passport.session());
 *     });
 *
 * @return {Function} middleware
 * @api public
 */
Passport.prototype.session = function() {
  return this.authenticate('session');
}

/**
 * Middleware that will authenticate a request using the given `strategy` name,
 * with optional `options` and `callback`.
 *
 * Examples:
 *
 *     passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })(req, res);
 *
 *     passport.authenticate('local', function(err, user) {
 *       if (!user) { return res.redirect('/login'); }
 *       res.end('Authenticated!');
 *     })(req, res);
 *
 *     passport.authenticate('basic', { session: false })(req, res);
 *
 *     app.get('/auth/twitter', passport.authenticate('twitter'), function(req, res) {
 *       // request will be redirected to Twitter
 *     });
 *     app.get('/auth/twitter/callback', passport.authenticate('twitter'), function(req, res) {
 *       res.json(req.user);
 *     });
 *
 * @param {String} strategy
 * @param {Object} options
 * @param {Function} callback
 * @return {Function} middleware
 * @api public
 */
Passport.prototype.authenticate = function(strategy, options, callback) {
  if (this._framework && this._framework.authenticate) {
    return this._framework.authenticate(strategy, options, callback).bind(this);
  }

  return authenticate(strategy, options, callback).bind(this);
}

/**
 * Middleware that will authorize a third-party account using the given
 * `strategy` name, with optional `options`.
 *
 * If authorization is successful, the result provided by the strategy's verify
 * callback will be assigned to `req.account`.  The existing login session and
 * `req.user` will be unaffected.
 *
 * This function is particularly useful when connecting third-party accounts
 * to the local account of a user that is currently authenticated.
 *
 * Examples:
 *
 *    passport.authorize('twitter-authz', { failureRedirect: '/account' });
 *
 * @param {String} strategy
 * @param {Object} options
 * @return {Function} middleware
 * @api public
 */
Passport.prototype.authorize = function(strategy, options, callback) {
  options = options || {};
  options.assignProperty = 'account';

  return authenticate(strategy, options, callback).bind(this);
}

/**
 * Registers a function used to serialize user objects into the session.
 *
 * Examples:
 *
 *     passport.serializeUser(function(user, done) {
 *       done(null, user.id);
 *     });
 *
 * @api public
 */
Passport.prototype.serializeUser = function(fn, done) {
  if (typeof fn === 'function') {
    return this._serializers.push(fn);
  }

  // private implementation that traverses the chain of serializers, attempting
  // to serialize a user
  var user = fn;

  var stack = this._serializers;
  (function pass(i, err, obj) {
    // serializers use 'pass' as an error to skip processing
    if ('pass' === err) {
      err = undefined;
    }
    // an error or serialized object was obtained, done
    if (err || obj || obj === 0) { return done(err, obj); }

    var layer = stack[i];
    if (!layer) {
      return done(new Error('failed to serialize user into session'));
    }

    try {
      layer(user, function(e, o) { pass(i + 1, e, o); } )
    } catch(e) {
      return done(e);
    }
  })(0);
}

/**
 * Registers a function used to deserialize user objects out of the session.
 *
 * Examples:
 *
 *     passport.deserializeUser(function(id, done) {
 *       User.findById(id, function (err, user) {
 *         done(err, user);
 *       });
 *     });
 *
 * @api public
 */
Passport.prototype.deserializeUser = function(fn, done) {
  if (typeof fn === 'function') {
    return this._deserializers.push(fn);
  }

  // private implementation that traverses the chain of deserializers,
  // attempting to deserialize a user
  var obj = fn;

  var stack = this._deserializers;
  (function pass(i, err, user) {
    // deserializers use 'pass' as an error to skip processing
    if ('pass' === err) {
      err = undefined;
    }
    // an error or deserialized user was obtained, done
    if (err || user) { return done(err, user); }
    // a valid user existed when establishing the session, but that user has
    // since been removed
    if (user === null || user === false) { return done(null, false); }

    var layer = stack[i];
    if (!layer) {
      return done(new Error('failed to deserialize user out of session'));
    }

    try {
      layer(obj, function(e, u) { pass(i + 1, e, u); } )
    } catch(e) {
      return done(e);
    }
  })(0);
}

/**
 * Registers a function used to transform auth info.
 *
 * In some circumstances authorization details are contained in authentication
 * credentials or loaded as part of verification.
 *
 * For example, when using bearer tokens for API authentication, the tokens may
 * encode (either directly or indirectly in a database), details such as scope
 * of access or the client to which the token was issued.
 *
 * Such authorization details should be enforced separately from authentication.
 * Because Passport deals only with the latter, this is the responsiblity of
 * middleware or routes further along the chain.  However, it is not optimal to
 * decode the same data or execute the same database query later.  To avoid
 * this, Passport accepts optional `info` along with the authenticated `user`
 * in a strategy's `success()` action.  This info is set at `req.authInfo`,
 * where said later middlware or routes can access it.
 *
 * Optionally, applications can register transforms to proccess this info,
 * which take effect prior to `req.authInfo` being set.  This is useful, for
 * example, when the info contains a client ID.  The transform can load the
 * client from the database and include the instance in the transformed info,
 * allowing the full set of client properties to be convieniently accessed.
 *
 * If no transforms are registered, `info` supplied by the strategy will be left
 * unmodified.
 *
 * Examples:
 *
 *     passport.transformAuthInfo(function(info, done) {
 *       Client.findById(info.clientID, function (err, client) {
 *         info.client = client;
 *         done(err, info);
 *       });
 *     });
 *
 * @api public
 */
Passport.prototype.transformAuthInfo = function(fn, done) {
  if (typeof fn === 'function') {
    return this._infoTransformers.push(fn);
  }

  // private implementation that traverses the chain of transformers,
  // attempting to transform auth info
  var info = fn;

  var stack = this._infoTransformers;
  (function pass(i, err, tinfo) {
    // transformers use 'pass' as an error to skip processing
    if ('pass' === err) {
      err = undefined;
    }
    // an error or transformed info was obtained, done
    if (err || tinfo) { return done(err, tinfo); }

    var layer = stack[i];
    if (!layer) {
      // if no transformers are registered (or they all pass), the default
      // behavior is to use the un-transformed info as-is
      return done(null, info);
    }

    try {
      var arity = layer.length;
      if (arity == 1) {
        // sync
        var t = layer(info);
        pass(i + 1, null, t);
      } else {
        // async
        layer(info, function(e, t) { pass(i + 1, e, t); } )
      }
    } catch(e) {
      return done(e);
    }
  })(0);
}

/**
 * Return strategy with given `name`.
 *
 * @param {String} name
 * @return {Strategy}
 * @api private
 */
Passport.prototype._strategy = function(name) {
  return this._strategies[name];
}

/**
 * Export default singleton.
 *
 * @api public
 */
exports = module.exports = new Passport();

/**
 * Framework version.
 */
require('pkginfo')(module, 'version');

/**
 * Expose constructors.
 */
exports.Passport = Passport;
exports.Strategy = Strategy;

/**
 * Expose strategies.
 */
exports.strategies = {};
exports.strategies.SessionStrategy = SessionStrategy;

/**
 * HTTP extensions.
 */
require('./http/request');<|endoftext|>


<commit_before>/* global Log, Config */
'use strict';

const EventEmitter = require('events').EventEmitter;
const async = require('async');
const StringTemplate = require('./string-template');
const Metadata = require('./source/metadata');
const S3 = require('./source/s3');

// const Consul = require('./source/consul');
// const File = require('./source/file');

const DEFAULT_CALLBACK_DELAY = 2000;

/**
 * Recursively iterate through an object applying the callback to each k/v pair
 * @param {Object} obj
 * @param {Function} callback
 * @returns {Object}
 */
function iter(obj, callback) {
  const collected = {};
  let value;

  for (const prop in obj) {
    if (obj.hasOwnProperty(prop)) {
      if (typeof obj[prop] === 'object') {
        value = iter(obj[prop], callback);
      } else {
        value = callback(prop, obj[prop]);
      }
      collected[prop] = value;
    }
  }
  return collected;
}

class PluginManager extends EventEmitter {
  /**
   * Constructor
   * @param {Storage} storage
   */
  constructor(storage) {
    super();
    this.storage = storage;
    this.index = new S3({
      interval: Config.get('index:interval'),
      bucket: Config.get('index:bucket'),
      path: Config.get('index:path')
    });
    this.metadata = new Metadata();
    this._running = false;
    this._ok = true;
    this.updateDelay = DEFAULT_CALLBACK_DELAY;
    this._boundInit = this._init.bind(this);
  }

  /**
   * Initialize the PluginManager's index and metadata source
   */
  initialize() {
    Log.info('Initializing index and metadata');
    this._running = true;
    this._init();
  }

  /**
   * Shutdown the plugin manager's sources and then the plugin manager
   * @param {Boolean} del
   */
  shutdown(del) {
    Log.info('Shutting down sources');
    this.storage.sources.forEach((source) => {
      source.shutdown();
    });

    if (del === true) {
      this.storage.sources = [];
    }

    this._running = false;
  }

  /**
   * Get the plugin manager's status
   * @returns {{running: boolean, ok: boolean, sources: Array}}
   */
  status() {
    return {
      running: this._running,
      ok: this._ok,
      sources: this._sourcesStatus()
    };
  }

  /**
   * Gets an array of source statuses
   * @returns {Array}
   * @private
   */
  _sourcesStatus() {
    return this.storage.sources.map((source) => {
      return {
        name: source.name,
        type: source.type,
        status: source.status()
      };
    });
  }

  /**
   * Coordinates synchronizing the index and metadata sources and then mapping them together
   * @private
   */
  _init() {
    const boundIndex = this._index.bind(this);
    const boundMapper = this._mapper.bind(this);

    async.series([boundIndex, boundMapper], (err, response) => {
      // There's no possible error condition because both this._mapper() and this._index() handle
      // their own errors in order to retry their respective operations.
      this._ok = true;

      const sources = response[1];

      this.emit('sources-generated', sources);
      this._registerSources(sources);
      this.emit('sources-registered', this.storage.sources);
    });
  }

  /**
   * Coordinate fetching the PluginManager's index and metadata source
   * @param {Function} callback
   * @returns {null}
   * @private
   */
  _index(callback) {
    return async.parallel([
      this._createParallelTask(this.index),
      this._createParallelTask(this.metadata)
    ], (err) => {
      if (err) {
        // If any of the parallel tasks fail it means either the Metadata plugin or the index plugin has failed
        // so we need to determine which plugin threw the error then set a listener on it for its next update to
        // try to assemble the whole thing again.
        this._error(err);

        // ECONNREFUSED seems to be the only code that indicates an EC2MetadataService failure
        if (err.code === 'ECONNREFUSED') {
          this.metadata.shutdown();
          this.metadata.once('update', this._boundInit);
          this.metadata.initialize();
        } else {
          this.index.shutdown();
          this.index.once('update', this._boundInit);
          this.index.initialize();
        }
        return;
      }
      callback(null);
    });
  }

  /**
   * Maps instance metadata into the index document
   * @param {Function} callback
   * @private
   */
  _mapper(callback) {
    let sources = [];

    try {
      sources = this.index.properties.sources.map((el) => {
        return iter(el, (k, v) => {
          // TODO: No handling for arrays. Are we expecting arrays anywhere in the index?
          return StringTemplate.coerce(v, this.metadata.properties);
        });
      });
      callback(null, sources);
    } catch (mappingErr) {
      this._error(mappingErr);
      setTimeout(this._boundInit, this.updateDelay);
    }
  }

  /**
   * Create function that feeds async.parallel (or any async marshalling function)
   * @param {Metadata|S3} source
   * @returns {Function}
   * @private
   */
  _createParallelTask(source) {
    return (c) => {
      const callBackErr = (err) => {
        source.removeListener('update', callBackSuccess); // eslint-disable-line no-use-before-define
        c(err);
      };
      const callBackSuccess = () => {
        source.removeListener('error', callBackErr);
        c(null);
      };

      source.shutdown();
      source.once('update', callBackSuccess);
      source.once('error', callBackErr);
      source.initialize();
    };
  }

  /**
   * Register the sources with the storage layer and their event handlers
   * @param {Array} sources
   * @private
   */
  _registerSources(sources) {
    sources.forEach((source) => {
      let instance;

      switch (source.type.toLowerCase()) {
        case 's3':
          instance = new S3(Object.assign(source.parameters, {bucket: this.index.bucket}));
          break;

        // case 'file':
        //  //
        //  break;
        // case 'consul':
        //  //
        //  break;
        default:
          this._error(new Error(`Source type ${source.type} not implemented`));
      }
      this.emit('source-instantiated', instance);

      this.storage.register(instance);
      this._registerSourceEvents(instance);
      this.emit('source-registered', instance);

      instance.initialize();
      this.emit('source-initialized', instance);
    });

    this.index.once('update', () => {
      // TODO: Implement timeout so that someone spamming updates to the GH repo won't cause a constant rebuild
      setTimeout(() => {
        Log.info('There was an update to the index document.');
        this._boundInit();
      }, this.updateDelay);
    });
  }

  /**
   * Bind event handlers for each source.
   * @param {Metadata|S3} source
   * @private
   */
  _registerSourceEvents(source) {
    source.on('startup', () => {
      Log.info(`${source.name} started up.`);
    });

    source.on('shutdown', () => {
      Log.info(`${source.name} shut down.`);
    });

    source.on('update', () => {
      Log.info(`${source.name}'s data was updated from its underlying source data.`);
      setInterval(() => {
        this.storage.update();
      }, this.updateDelay);
    });

    source.on('no-update', () => {
      Log.info(`${source.name} has no update to its underlying source data.`);
    });

    source.on('error', (err) => {
      Log.info(`${source.name} encountered the following error: ${err}`);
      this._error(err);
    });
  }

  /**
   * General handler for all errors
   * @param {Error} err
   * @returns {PluginManager}
   * @private
   */
  _error(err) {
    Log.error(err, {
      source: this.name,
      type: this.type
    });

    this._ok = false;

    // Only emit an error event if there are listeners.
    if (this.listeners('error').length > 0) {
      this.emit('error', err);
    }

    return this;
  }
}

module.exports = PluginManager;
<commit_msg>Added a TODO about implementing source types, removed an old one that doesn't apply anymore, and changed Log.info to Log.error when adding a listener for a source's 'error' event.<commit_after>/* global Log, Config */
'use strict';

const EventEmitter = require('events').EventEmitter;
const async = require('async');
const StringTemplate = require('./string-template');
const Metadata = require('./source/metadata');
const S3 = require('./source/s3');

// TODO: Implement both of these source types
// const Consul = require('./source/consul');
// const File = require('./source/file');

const DEFAULT_CALLBACK_DELAY = 2000;

/**
 * Recursively iterate through an object applying the callback to each k/v pair
 * @param {Object} obj
 * @param {Function} callback
 * @returns {Object}
 */
function iter(obj, callback) {
  const collected = {};
  let value;

  for (const prop in obj) {
    if (obj.hasOwnProperty(prop)) {
      if (typeof obj[prop] === 'object') {
        value = iter(obj[prop], callback);
      } else {
        value = callback(prop, obj[prop]);
      }
      collected[prop] = value;
    }
  }
  return collected;
}

class PluginManager extends EventEmitter {
  /**
   * Constructor
   * @param {Storage} storage
   */
  constructor(storage) {
    super();
    this.storage = storage;
    this.index = new S3({
      interval: Config.get('index:interval'),
      bucket: Config.get('index:bucket'),
      path: Config.get('index:path')
    });
    this.metadata = new Metadata();
    this._running = false;
    this._ok = true;
    this.updateDelay = DEFAULT_CALLBACK_DELAY;
    this._boundInit = this._init.bind(this);
  }

  /**
   * Initialize the PluginManager's index and metadata source
   */
  initialize() {
    Log.info('Initializing index and metadata');
    this._running = true;
    this._init();
  }

  /**
   * Shutdown the plugin manager's sources and then the plugin manager
   * @param {Boolean} del
   */
  shutdown(del) {
    Log.info('Shutting down sources');
    this.storage.sources.forEach((source) => {
      source.shutdown();
    });

    if (del === true) {
      this.storage.sources = [];
    }

    this._running = false;
  }

  /**
   * Get the plugin manager's status
   * @returns {{running: boolean, ok: boolean, sources: Array}}
   */
  status() {
    return {
      running: this._running,
      ok: this._ok,
      sources: this._sourcesStatus()
    };
  }

  /**
   * Gets an array of source statuses
   * @returns {Array}
   * @private
   */
  _sourcesStatus() {
    return this.storage.sources.map((source) => {
      return {
        name: source.name,
        type: source.type,
        status: source.status()
      };
    });
  }

  /**
   * Coordinates synchronizing the index and metadata sources and then mapping them together
   * @private
   */
  _init() {
    const boundIndex = this._index.bind(this);
    const boundMapper = this._mapper.bind(this);

    async.series([boundIndex, boundMapper], (err, response) => {
      // There's no possible error condition because both this._mapper() and this._index() handle
      // their own errors in order to retry their respective operations.
      this._ok = true;

      const sources = response[1];

      this.emit('sources-generated', sources);
      this._registerSources(sources);
      this.emit('sources-registered', this.storage.sources);
    });
  }

  /**
   * Coordinate fetching the PluginManager's index and metadata source
   * @param {Function} callback
   * @returns {null}
   * @private
   */
  _index(callback) {
    return async.parallel([
      this._createParallelTask(this.index),
      this._createParallelTask(this.metadata)
    ], (err) => {
      if (err) {
        // If any of the parallel tasks fail it means either the Metadata plugin or the index plugin has failed
        // so we need to determine which plugin threw the error then set a listener on it for its next update to
        // try to assemble the whole thing again.
        this._error(err);

        // ECONNREFUSED seems to be the only code that indicates an EC2MetadataService failure
        if (err.code === 'ECONNREFUSED') {
          this.metadata.shutdown();
          this.metadata.once('update', this._boundInit);
          this.metadata.initialize();
        } else {
          this.index.shutdown();
          this.index.once('update', this._boundInit);
          this.index.initialize();
        }
        return;
      }
      callback(null);
    });
  }

  /**
   * Maps instance metadata into the index document
   * @param {Function} callback
   * @private
   */
  _mapper(callback) {
    let sources = [];

    try {
      sources = this.index.properties.sources.map((el) => {
        return iter(el, (k, v) => {
          // TODO: No handling for arrays. Are we expecting arrays anywhere in the index?
          return StringTemplate.coerce(v, this.metadata.properties);
        });
      });
      callback(null, sources);
    } catch (mappingErr) {
      this._error(mappingErr);
      setTimeout(this._boundInit, this.updateDelay);
    }
  }

  /**
   * Create function that feeds async.parallel (or any async marshalling function)
   * @param {Metadata|S3} source
   * @returns {Function}
   * @private
   */
  _createParallelTask(source) {
    return (c) => {
      const callBackErr = (err) => {
        source.removeListener('update', callBackSuccess); // eslint-disable-line no-use-before-define
        c(err);
      };
      const callBackSuccess = () => {
        source.removeListener('error', callBackErr);
        c(null);
      };

      source.shutdown();
      source.once('update', callBackSuccess);
      source.once('error', callBackErr);
      source.initialize();
    };
  }

  /**
   * Register the sources with the storage layer and their event handlers
   * @param {Array} sources
   * @private
   */
  _registerSources(sources) {
    sources.forEach((source) => {
      let instance;

      switch (source.type.toLowerCase()) {
        case 's3':
          instance = new S3(Object.assign(source.parameters, {bucket: this.index.bucket}));
          break;

        // case 'file':
        //  //
        //  break;
        // case 'consul':
        //  //
        //  break;
        default:
          this._error(new Error(`Source type ${source.type} not implemented`));
      }
      this.emit('source-instantiated', instance);

      this.storage.register(instance);
      this._registerSourceEvents(instance);
      this.emit('source-registered', instance);

      instance.initialize();
      this.emit('source-initialized', instance);
    });

    this.index.once('update', () => {
      setTimeout(() => {
        Log.info('There was an update to the index document.');
        this._boundInit();
      }, this.updateDelay);
    });
  }

  /**
   * Bind event handlers for each source.
   * @param {Metadata|S3} source
   * @private
   */
  _registerSourceEvents(source) {
    source.on('startup', () => {
      Log.info(`${source.name} started up.`);
    });

    source.on('shutdown', () => {
      Log.info(`${source.name} shut down.`);
    });

    source.on('update', () => {
      Log.info(`${source.name}'s data was updated from its underlying source data.`);
      setInterval(() => {
        this.storage.update();
      }, this.updateDelay);
    });

    source.on('no-update', () => {
      Log.info(`${source.name} has no update to its underlying source data.`);
    });

    source.on('error', (err) => {
      Log.error(`${source.name} encountered the following error: ${err}`);
      this._error(err);
    });
  }

  /**
   * General handler for all errors
   * @param {Error} err
   * @returns {PluginManager}
   * @private
   */
  _error(err) {
    Log.error(err, {
      source: this.name,
      type: this.type
    });

    this._ok = false;

    // Only emit an error event if there are listeners.
    if (this.listeners('error').length > 0) {
      this.emit('error', err);
    }

    return this;
  }
}

module.exports = PluginManager;
<|endoftext|>


<commit_before>// iphone.js: iPhone friendliness. Free of charge.
// The iPhone touch code is heavily borrowed from libraries around the web.
// If you recognize anything as yours and you'd like credit for it, get in touch.
var iPhone = !!navigator.userAgent.match(/iPhone/), iPad = !!navigator.userAgent.match(/iPad/);

if (iPhone || iPad) {
  Shining.config.help = false;
  Shining.config.transitions = 'none';
  if (iPhone) {
    clearInterval(Shining.pluginProcesses['resize']);
    $(window).unbind('resize');
  }

  var scale = window.innerWidth / document.documentElement.clientWidth;

  Shining.slides.playSlide = function(name) {
    var index = Shining.slides._slides.indexOf(name),
      offset = index * window.innerWidth,
      slide = Shining.slides._loaded[name];
    $('body').transformTransition({translate: {x: -offset }});
    Shining.slides.current(name);
    if (iPad) setTimeout(function() { Shining.centerStage('animate'); }, 500);
    var aside = $('#stage .slide:eq(' + index + ') aside');
    if (aside.length) setTimeout(
      function() {
        Shining.note(aside.html(), 5000);
        $('#note').css({right: 10 - offset, maxWidth: '60%'});
      },
      500
    );
  };

  Shining.centerStage = function(animate) {
    var top = (window.innerHeight - currentSlide().outerHeight()) / 3;
    if (top < 0) top = 0;
    $('.slide')[!!animate ? 'animate' : 'css']({ top: top }, 100);
  };

  Shining.slides.nextSlide = function() {
    Shining.slides.playSlide(Shining.slides.next() || Shining.slides.current());
  };

  Shining.slides.previousSlide = function() {
    Shining.slides.playSlide(Shining.slides.previous() || Shining.slides.current());
  };

  function fitToView() {
    $('div.slide').each(function(i) {
      $(this).css({left: i * window.innerWidth, height: window.innerHeight });
    });
  }

  function currentSlide() {
    return $('#stage .slide:eq(' + Shining.slides._slides.indexOf(Shining.slides.current()) + ') aside');
  }

  $(window).bind('orientationchange', function() {
    fitToView();
    var offset = Shining.slides._slides.indexOf(Shining.slides.current()) * window.innerWidth;
    $('body').transformTransition({translate: {x: -offset }});
    $('#note').css({right: 10 - offset});
  });

  function flick(direction) {
    return Shining.slides[direction == 'right' ? 'nextSlide' : 'previousSlide']();
	}

  Shining.when('slidesloaded', function(i) {
    $('#stage').empty();
    $(Shining.config.slides).each(function(i) {
      var slide = Shining.slides._loaded[this];
      $('
' + slide.markup + '
')
        .css({left: i * window.innerWidth })
        .appendTo($('#stage'));
    });
    if (iPad) Shining.centerStage();
    $('body').bind('touchstart', function() {
      $(this).data('pan', {
        startX: event.targetTouches[0].screenX,
        lastX: event.targetTouches[0].screenX,
        startTime: new Date().getTime(),
        startOffset: $(this).transform().translate.x,
        distance: function() { return Math.round(scale * (this.startX - this.lastX)); },
        delta: function() {
          var x = event.targetTouches[0].screenX;
          this.dir = this.lastX > x ? 'right' : 'left';
          var delta = Math.round(scale * (this.lastX - x));
          this.lastX = x;
          return delta;
        },
        duration: function() { return new Date().getTime() - this.startTime; }
      });
      return false;
    })
    .bind('touchmove', function() {
      var pan = $(this).data('pan');
      $(this).transform({translateBy: {x: -pan.delta()}});
      return false;
    })
    .bind('touchend', function() {
      var pan = $(this).data('pan');
      if (pan.dir) flick(pan.dir);
      return false;
    });
  });
}<commit_msg>better ipad slide centering<commit_after>// iphone.js: iPhone friendliness. Free of charge.
// The iPhone touch code is heavily borrowed from libraries around the web.
// If you recognize anything as yours and you'd like credit for it, get in touch.
var iPhone = !!navigator.userAgent.match(/iPhone/), iPad = !!navigator.userAgent.match(/iPad/);

if (iPhone || iPad) {
  Shining.config.help = false;
  Shining.config.transitions = 'none';
  if (iPhone) {
    clearInterval(Shining.pluginProcesses['resize']);
    $(window).unbind('resize');
  }

  var scale = window.innerWidth / document.documentElement.clientWidth;

  Shining.slides.playSlide = function(name) {
    var index = Shining.slides._slides.indexOf(name),
      offset = index * window.innerWidth,
      slide = Shining.slides._loaded[name];
    $('body').transformTransition({translate: {x: -offset }});
    Shining.slides.current(name);
    var aside = $('#stage .slide:eq(' + index + ') aside');
    if (aside.length) setTimeout(
      function() {
        Shining.note(aside.html(), 5000);
        $('#note').css({right: 10 - offset, maxWidth: '60%'});
      },
      500
    );
  };

  Shining.centerStage = function(animate) {
    $('.slide').each(function() {
      centerSlide($(this));
    });
  };

  Shining.slides.nextSlide = function() {
    Shining.slides.playSlide(Shining.slides.next() || Shining.slides.current());
  };

  Shining.slides.previousSlide = function() {
    Shining.slides.playSlide(Shining.slides.previous() || Shining.slides.current());
  };

  function fitToView() {
    $('div.slide').each(function(i) {
      $(this).css({left: i * window.innerWidth, height: window.innerHeight });
    });
  }

  function centerSlide(slide) {
    var top = (window.innerHeight - slide.outerHeight()) / 3;
    if (top < 0) top = 0;
    slide.css({ top: top });
  }

  function currentSlide() {
    return $('#stage .slide:eq(' + Shining.slides._slides.indexOf(Shining.slides.current()) + ')');
  }

  $(window).bind('orientationchange', function() {
    fitToView();
    var offset = Shining.slides._slides.indexOf(Shining.slides.current()) * window.innerWidth;
    $('body').transformTransition({translate: {x: -offset }});
    $('#note').css({right: 10 - offset});
  });

  function flick(direction) {
    return Shining.slides[direction == 'right' ? 'nextSlide' : 'previousSlide']();
	}

  Shining.when('slidesloaded', function(i) {
    $('#stage').empty();
    $(Shining.config.slides).each(function(i) {
      var slide = Shining.slides._loaded[this];
      $('
' + slide.markup + '
')
        .css({left: i * window.innerWidth })
        .appendTo($('#stage'));
    });
    if (iPad) Shining.centerStage();
    $('body').bind('touchstart', function() {
      $(this).data('pan', {
        startX: event.targetTouches[0].screenX,
        lastX: event.targetTouches[0].screenX,
        startTime: new Date().getTime(),
        startOffset: $(this).transform().translate.x,
        distance: function() { return Math.round(scale * (this.startX - this.lastX)); },
        delta: function() {
          var x = event.targetTouches[0].screenX;
          this.dir = this.lastX > x ? 'right' : 'left';
          var delta = Math.round(scale * (this.lastX - x));
          this.lastX = x;
          return delta;
        },
        duration: function() { return new Date().getTime() - this.startTime; }
      });
      return false;
    })
    .bind('touchmove', function() {
      var pan = $(this).data('pan');
      $(this).transform({translateBy: {x: -pan.delta()}});
      return false;
    })
    .bind('touchend', function() {
      var pan = $(this).data('pan');
      if (pan.dir) flick(pan.dir);
      return false;
    });
  });
}<|endoftext|>


<commit_before>'use strict';

class Plugin {

    constructor( name ) {

        this.name = name;
    }

    execute( event, callback ) {

        callback( new Error( 'not implemented' ) );
    }

    configure( config ) {

    }
}

module.exports = Plugin;
<commit_msg>changed execute() to call callback()<commit_after>'use strict';

class Plugin {

    constructor( name ) {

        this.name = name;
    }

    execute( event, callback ) {

        // short circuit
        callback();
    }

    configure( config ) {

    }
}

module.exports = Plugin;
<|endoftext|>