<commit_before>/*
 * Set variables for managment
 *
 * ACTIVE = controls run loop
 * DEBUG = Report on values at runtime
 *
 * ENERGY_* values set upper and lower usage values
 *
 * LIMIT_* values max creep spawn per room
 *
 */

var Constant = {};

// User
Constant.USERNAME                       = 'nightred';

// debug = 0 off, 1 info, 2 debug, 3 verbose
Constant.DEBUG                          = 2;
Constant.SIM                            = false;
Constant.VISUALS                        = true;

Constant.ENERGY_ROOM_WITHDRAW_MIN       = 250;
Constant.ENERGY_CREEP_SPAWN_MIN         = 200;
Constant.ENERGY_TOWER_REPAIR_MIN        = 301;
Constant.ENERGY_STORAGE_MIN_FILL_TOWER  = 0.05;
Constant.ENERGY_CONTAINER_MAX_PERCENT   = 0.9;
Constant.ENERGY_CONTAINER_MIN_PERCENT   = 0.1 ;
Constant.ENERGY_CONTAINER_MIN_WITHDRAW  = 100;
Constant.ENERGY_STORAGE_MIN_WITHDRAW    = 100;
Constant.ENERGY_LINK_IN_MIN             = 0.1;
Constant.ENERGY_LINK_OUT_MAX            = 0.8;
Constant.LINK_STORAGE_TRANSFER_MIN      = 0.34;
Constant.ENERGY_LINK_STORAGE_MIN        = 0.38;
Constant.ENERGY_LINK_STORAGE_MAX        = 0.8;
Constant.ENERGY_STORAGE_MAX             = 0.6;
Constant.ENERGY_CONTAINER_MAX           = 0.9;
Constant.ENERGY_STORAGE_SECONDARY_MIN   = 0.14;

Constant.TERMINAL_ENERGY_MAX            = 0.5;

Constant.CONTROLLER_WITHDRAW_LEVEL      = 2;
Constant.CONTROLLER_RESERVE_MAX         = 4000;
Constant.CONTROLLER_RESERVE_MIN         = 1000;

Constant.FIND_WAIT_TICKS                = 8;
Constant.MANAGE_WAIT_TICKS              = 10;
Constant.MANAGE_MEMORY_TICKS            = 20;
Constant.TOWER_REPAIR_TICKS             = 5;
Constant.REPORT_TICKS                   = 500;
Constant.DEFENSE_COOLDOWN               = 80;
Constant.DEFENSE_LIMIT_INCREASE_DELAY   = 100;

Constant.SPAWN_COST_DECAY               = 200;
Constant.SPAWN_QUEUE_DELAY              = 2;
Constant.SPAWN_ENERGY_MAX               = 0.8;

Constant.REPAIR_HIT_WORK_MIN            = 0.80;
Constant.REPAIR_HIT_WORK_MAX            = 0.98;
Constant.RAMPART_HIT_MAX                = 100000;
Constant.WALL_HIT_MAX                   = 100000;

Constant.REFILL_TOWER_MAX               = 0.92;
Constant.REFILL_TOWER_MIN               = 0.40;

Constant.CREEP_DESPAWN_TICKS            = 1;
Constant.CREEP_IDLE_TIME                = 4;
Constant.CREEP_STUCK_TICK               = 4;
Constant.CREEP_FILL_TICKS               = 6;
Constant.CREEP_TRAVEL_RANGE             = 4;

Constant.QUEUE_WORK                     = 'work';
Constant.QUEUE_SPAWN                    = 'spawn';
Constant.QUEUE_MIL                      = 'mil';

Constant.MINER                          = 'miner';
Constant.HARVESTER                      = 'harvester';
Constant.UPGRADER                       = 'upgrader';
Constant.TECH                           = 'tech';
Constant.CRASHTECH                      = 'crashtech';
Constant.HAULER                         = 'hauler';
Constant.RESUPPLY                       = 'resupply';
Constant.LINKER                         = 'linker';
Constant.SCOUT                          = 'scout';
Constant.CONTROLLER                     = 'controller';
Constant.COMBAT_BRAWLER                 = 'combat.brawler';
Constant.COMBAT_SWARMER                 = 'combat.swarmer';
Constant.COMBAT_MEDIC                   = 'combat.medic';

Constant.ROLE_TYPES = [
    Constant.MINER,
    Constant.UPGRADER,
    Constant.TECH,
    Constant.CRASHTECH,
    Constant.HAULER,
    Constant.RESUPPLY,
    Constant.LINKER,
    Constant.SCOUT,
    Constant.CONTROLLER,
    Constant.COMBAT_BRAWLER,
    Constant.COMBAT_SWARMER,
    Constant.COMBAT_MEDIC,
    ];

Constant.DIRECTOR_ROOM                  = 'director.room';
Constant.DIRECTOR_MINE                  = 'director.mine';
Constant.DIRECTOR_TECH                  = 'director.tech';
Constant.DIRECTOR_CRASHTECH             = 'director.crashtech';
Constant.DIRECTOR_HAUL                  = 'director.haul';
Constant.DIRECTOR_LONGHAUL              = 'director.longhaul';
Constant.DIRECTOR_RESUPPLY              = 'director.resupply';
Constant.DIRECTOR_LINKER                = 'director.linker';
Constant.TOWER_REFILL                   = 'tower.fill';
Constant.TERMINAL_EMPTY                 = 'terminal.empty';
Constant.MINE                           = 'mine';
Constant.HARVEST                        = 'harvest';
Constant.UPGRADE                        = 'upgrade';
Constant.RESERVE                        = 'reserve';
Constant.REPAIR                         = 'repair';
Constant.CONSTRUCTION                   = 'construction';
Constant.SIGNCONTROLLER                 = 'signcontroller';
Constant.SCOUTING                       = 'scouting';
Constant.DEFENSE                        = 'defense';
Constant.ATTACK                         = 'attack';
Constant.CLAIM                          = 'claim';

Constant.WORK_TASKS = [
    Constant.DIRECTOR_ROOM,
    Constant.DIRECTOR_MINE,
    Constant.DIRECTOR_TECH,
    Constant.DIRECTOR_CRASHTECH,
    Constant.DIRECTOR_HAUL,
    Constant.DIRECTOR_LONGHAUL,
    Constant.DIRECTOR_LINKER,
    Constant.DIRECTOR_RESUPPLY,
    Constant.TOWER_REFILL,
    Constant.TERMINAL_EMPTY,
    Constant.MINE,
    Constant.UPGRADE,
    Constant.RESERVE,
    Constant.REPAIR,
    Constant.CONSTRUCTION,
    Constant.SIGNCONTROLLER,
    Constant.SCOUTING,
    Constant.DEFENSE,
    Constant.ATTACK,
    Constant.CLAIM,
    ];

Constant.DIRECTIONS = {
    1: [0, -1],
    2: [1, -1],
    3: [1, 0],
    4: [1, 1],
    5: [0, 1],
    6: [-1, 1],
    7: [-1, 0],
    8: [-1, -1],
};

module.exports = Constant;
<commit_msg>terminal max energy<commit_after>/*
 * Set variables for managment
 *
 * ACTIVE = controls run loop
 * DEBUG = Report on values at runtime
 *
 * ENERGY_* values set upper and lower usage values
 *
 * LIMIT_* values max creep spawn per room
 *
 */

var Constant = {};

// User
Constant.USERNAME                       = 'nightred';

// debug = 0 off, 1 info, 2 debug, 3 verbose
Constant.DEBUG                          = 2;
Constant.SIM                            = false;
Constant.VISUALS                        = true;

Constant.ENERGY_ROOM_WITHDRAW_MIN       = 250;
Constant.ENERGY_CREEP_SPAWN_MIN         = 200;
Constant.ENERGY_TOWER_REPAIR_MIN        = 301;
Constant.ENERGY_STORAGE_MIN_FILL_TOWER  = 0.05;
Constant.ENERGY_CONTAINER_MAX_PERCENT   = 0.9;
Constant.ENERGY_CONTAINER_MIN_PERCENT   = 0.1 ;
Constant.ENERGY_CONTAINER_MIN_WITHDRAW  = 100;
Constant.ENERGY_STORAGE_MIN_WITHDRAW    = 100;
Constant.ENERGY_LINK_IN_MIN             = 0.1;
Constant.ENERGY_LINK_OUT_MAX            = 0.8;
Constant.LINK_STORAGE_TRANSFER_MIN      = 0.34;
Constant.ENERGY_LINK_STORAGE_MIN        = 0.38;
Constant.ENERGY_LINK_STORAGE_MAX        = 0.8;
Constant.ENERGY_STORAGE_MAX             = 0.6;
Constant.ENERGY_CONTAINER_MAX           = 0.9;
Constant.ENERGY_STORAGE_SECONDARY_MIN   = 0.14;

Constant.TERMINAL_ENERGY_MAX            = 0.2;

Constant.CONTROLLER_WITHDRAW_LEVEL      = 2;
Constant.CONTROLLER_RESERVE_MAX         = 4000;
Constant.CONTROLLER_RESERVE_MIN         = 1000;

Constant.FIND_WAIT_TICKS                = 8;
Constant.MANAGE_WAIT_TICKS              = 10;
Constant.MANAGE_MEMORY_TICKS            = 20;
Constant.TOWER_REPAIR_TICKS             = 5;
Constant.REPORT_TICKS                   = 500;
Constant.DEFENSE_COOLDOWN               = 80;
Constant.DEFENSE_LIMIT_INCREASE_DELAY   = 100;

Constant.SPAWN_COST_DECAY               = 200;
Constant.SPAWN_QUEUE_DELAY              = 2;
Constant.SPAWN_ENERGY_MAX               = 0.8;

Constant.REPAIR_HIT_WORK_MIN            = 0.80;
Constant.REPAIR_HIT_WORK_MAX            = 0.98;
Constant.RAMPART_HIT_MAX                = 100000;
Constant.WALL_HIT_MAX                   = 100000;

Constant.REFILL_TOWER_MAX               = 0.92;
Constant.REFILL_TOWER_MIN               = 0.40;

Constant.CREEP_DESPAWN_TICKS            = 1;
Constant.CREEP_IDLE_TIME                = 4;
Constant.CREEP_STUCK_TICK               = 4;
Constant.CREEP_FILL_TICKS               = 6;
Constant.CREEP_TRAVEL_RANGE             = 4;

Constant.QUEUE_WORK                     = 'work';
Constant.QUEUE_SPAWN                    = 'spawn';
Constant.QUEUE_MIL                      = 'mil';

Constant.MINER                          = 'miner';
Constant.HARVESTER                      = 'harvester';
Constant.UPGRADER                       = 'upgrader';
Constant.TECH                           = 'tech';
Constant.CRASHTECH                      = 'crashtech';
Constant.HAULER                         = 'hauler';
Constant.RESUPPLY                       = 'resupply';
Constant.LINKER                         = 'linker';
Constant.SCOUT                          = 'scout';
Constant.CONTROLLER                     = 'controller';
Constant.COMBAT_BRAWLER                 = 'combat.brawler';
Constant.COMBAT_SWARMER                 = 'combat.swarmer';
Constant.COMBAT_MEDIC                   = 'combat.medic';

Constant.ROLE_TYPES = [
    Constant.MINER,
    Constant.UPGRADER,
    Constant.TECH,
    Constant.CRASHTECH,
    Constant.HAULER,
    Constant.RESUPPLY,
    Constant.LINKER,
    Constant.SCOUT,
    Constant.CONTROLLER,
    Constant.COMBAT_BRAWLER,
    Constant.COMBAT_SWARMER,
    Constant.COMBAT_MEDIC,
    ];

Constant.DIRECTOR_ROOM                  = 'director.room';
Constant.DIRECTOR_MINE                  = 'director.mine';
Constant.DIRECTOR_TECH                  = 'director.tech';
Constant.DIRECTOR_CRASHTECH             = 'director.crashtech';
Constant.DIRECTOR_HAUL                  = 'director.haul';
Constant.DIRECTOR_LONGHAUL              = 'director.longhaul';
Constant.DIRECTOR_RESUPPLY              = 'director.resupply';
Constant.DIRECTOR_LINKER                = 'director.linker';
Constant.TOWER_REFILL                   = 'tower.fill';
Constant.TERMINAL_EMPTY                 = 'terminal.empty';
Constant.MINE                           = 'mine';
Constant.HARVEST                        = 'harvest';
Constant.UPGRADE                        = 'upgrade';
Constant.RESERVE                        = 'reserve';
Constant.REPAIR                         = 'repair';
Constant.CONSTRUCTION                   = 'construction';
Constant.SIGNCONTROLLER                 = 'signcontroller';
Constant.SCOUTING                       = 'scouting';
Constant.DEFENSE                        = 'defense';
Constant.ATTACK                         = 'attack';
Constant.CLAIM                          = 'claim';

Constant.WORK_TASKS = [
    Constant.DIRECTOR_ROOM,
    Constant.DIRECTOR_MINE,
    Constant.DIRECTOR_TECH,
    Constant.DIRECTOR_CRASHTECH,
    Constant.DIRECTOR_HAUL,
    Constant.DIRECTOR_LONGHAUL,
    Constant.DIRECTOR_LINKER,
    Constant.DIRECTOR_RESUPPLY,
    Constant.TOWER_REFILL,
    Constant.TERMINAL_EMPTY,
    Constant.MINE,
    Constant.UPGRADE,
    Constant.RESERVE,
    Constant.REPAIR,
    Constant.CONSTRUCTION,
    Constant.SIGNCONTROLLER,
    Constant.SCOUTING,
    Constant.DEFENSE,
    Constant.ATTACK,
    Constant.CLAIM,
    ];

Constant.DIRECTIONS = {
    1: [0, -1],
    2: [1, -1],
    3: [1, 0],
    4: [1, 1],
    5: [0, 1],
    6: [-1, 1],
    7: [-1, 0],
    8: [-1, -1],
};

module.exports = Constant;
<|endoftext|>


<commit_before>import dayjsCommon from 'dayjs';

dayjsCommon.extend(require('dayjs/plugin/advancedFormat'))
dayjsCommon.extend(require('dayjs/plugin/calendar'))
dayjsCommon.extend(require('dayjs/plugin/relativeTime'))
dayjsCommon.extend(require('dayjs/plugin/timezone'))
dayjsCommon.extend(require('dayjs/plugin/utc'))
dayjsCommon.extend(require('dayjs/plugin/customParseFormat'))

export const dayjs = dayjsCommon;

export const getNow = (rootState = null) => {
  if (rootState && rootState.cardDisplayDates &&
      rootState.cardDisplayDates.comparison_date) {
    return dayjs(rootState.cardDisplayDates.comparison_date);
  }
  // dayjs.tz.setDefault("America/Los_Angeles");
  // default value using client device local timezone
  return dayjs();
};

export const strToDate = (dateStr) => {
  return dayjs.tz(dateStr, "America/Los_Angeles");
};
<commit_msg>Support event timezone aware datetime<commit_after>import dayjsCommon from 'dayjs';

dayjsCommon.extend(require('dayjs/plugin/advancedFormat'))
dayjsCommon.extend(require('dayjs/plugin/calendar'))
dayjsCommon.extend(require('dayjs/plugin/relativeTime'))
dayjsCommon.extend(require('dayjs/plugin/timezone'))
dayjsCommon.extend(require('dayjs/plugin/utc'))
dayjsCommon.extend(require('dayjs/plugin/customParseFormat'))

export const dayjs = dayjsCommon;

export const getNow = (rootState = null) => {
  if (rootState && rootState.cardDisplayDates &&
      rootState.cardDisplayDates.comparison_date) {
    return dayjs(rootState.cardDisplayDates.comparison_date);
  }
  // dayjs.tz.setDefault("America/Los_Angeles");
  // default value using client device local timezone
  return dayjs();
};

export const strToDate = (dateStr) => {
  if (dateStr.includes("T")) {
    // timezone aware UTC format
    return dayjs.utc(dateStr);
  }
  return dayjs.tz(dateStr, "America/Los_Angeles");
};
<|endoftext|>


<commit_before>"use latest";

const _ = require('underscore');
const hbs = require('handlebars');
const async = require('async');
const request = require('request');
const cheerio = require('cheerio');

module.exports = (ctx, req, res) => {
  if (ctx.data && ctx.data.okta) {
    // The Okta tutorials are just raw HTML files in GitHub, no point in redirecting to it since it's unreadable.
    // Instead, we'll use this webtask as a proxy to serve the HTML within the page (in a modal dialog).
    return request.get('https://raw.githubusercontent.com' + ctx.data.okta.replace('/blob', ''), function(err, r, html) {
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end(html
        .replace(/\"resources/g, '"https://raw.githubusercontent.com/okta/UserDocs/master/SAML_Docs/resources')
        .replace(/\"images/g, '"https://raw.githubusercontent.com/okta/UserDocs/master/SAML_Docs/images'));
    });
  }

  const View = `

        <title>Third Party SAML Configuration</title>
        <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootswatch/3.3.5/cosmo/bootstrap.min.css">
        <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">

SAML Configuration Tutorials

This is a list of tutorials that show the SAML configuration of third party applications. Because these tutorials simply show how to configure the applications using SAML standard you can easily reuse them when integrating these apps with your own internal IdP or "SSO vendor".

            <input id="filter" type="text" class="form-control" placeholder="Filter in {{total}} tutorials from Auth0, Azure AD, Okta, OneLogin...">

          {{#if err}}

{{message}}

          {{else}}

                Source
                Application

              </thread>

              {{#each links}}

                  {{type}}
                  {{text}}

              {{/each}}

          {{/if}}

                <button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>

Thid Party Application

                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>

    `;

  getLinks((err, links) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });

    const template = hbs.compile(View);
    res.end(template({
      err: err,
      links: links,
      total: links.length
    }));
  });
};

//
const getLinks = (done) => {
  const links = [];
  async.waterfall([
    (callback) => {
      request(`https://auth0.com/docs/saml-apps`, (err, res, html) => {
        if (err) {
          return callback(err);
        }

        if (res.statusCode !== 200) {
          return callback(new Error('Unexpected status code: ' + res.statusCode));
        }

        var $ = cheerio.load(html);
        $('section.docs-content h2').each((i, element) => {
          var header = $(element);
          links.push({
            text: header.text(),
            type: 'Auth0',
            url: 'https://auth0.com/docs/saml-apps' + header.find('span').attr('href')
          });
        });

        callback();
      });
    },
    (callback) => {
      const thirdParty = [{"id":"salesforce","name":"Salesforce","img_class":"salesforce","data_logo":"Salesforce","configRoute":"/add-ons/salesforce","callback":"https://login.salesforce.com"},{"id":"dropbox","name":"Dropbox","img_class":"dropbox","data_logo":"Dropbox","configRoute":"/add-ons/dropbox","callback":"https://www.dropbox.com/saml_login"},{"id":"sharepoint","name":"SharePoint","img_class":"microsoft-sharepoint-logo","data_logo":"Sharepoint","configRoute":"/add-ons/sharepoint"},{"id":"rms","name":"AD RMS","img_class":"windows-logo","data_logo":"Windows Server Active Directory RMS","configRoute":"/add-ons/rms"},{"id":"zendesk","name":"Zendesk","img_class":"zendesk","data_logo":"Zendesk","configRoute":"/add-ons/zendesk"},{"id":"box","name":"Box","img_class":"box","data_logo":"Box","configRoute":"/add-ons/box","callback":"https://sso.services.box.net/sp/ACS.saml2"},{"id":"echosign","name":"EchoSign","img_class":"echosign","data_logo":"AdobeEchoSign","configRoute":"/add-ons/echosign"},{"id":"springcm","name":"SpringCM","img_class":"springcm","data_logo":"SpringCM","configRoute":"/add-ons/springcm"},{"id":"newrelic","name":"New Relic","img_class":"newrelic","data_logo":"NewRelic","configRoute":"/add-ons/newrelic"},{"id":"egnyte","name":"Egnyte","img_class":"egnyte","data_logo":"Egnyte","configRoute":"/add-ons/egnyte"},{"id":"cloudbees","name":"CloudBees","img_class":"cloudbees","data_logo":"CloudBees","configRoute":"/add-ons/cloudbees","callback":"https://grandcentral.cloudbees.com/authenticate/saml/consume"},{"id":"mscrm","name":"Dynamics CRM","img_class":"mscrm","data_logo":"MicrosoftDynamicsCRM","configRoute":"/add-ons/mscrm"},{"id":"office365","name":"Office 365 (beta)","img_class":"office365","data_logo":"Office365","configRoute":"/add-ons/office365"},{"id":"zoom","name":"Zoom","img_class":"zoom","data_logo":"Zoom","configRoute":"/add-ons/zoom"},{"id":"concur","name":"Concur (beta)","img_class":"concur","data_logo":"Concur","configRoute":"/add-ons/concur","callback":"https://www.concursolutions.com/SAMLRedirector/ClientSAMLLogin.aspx"},{"id":"slack","name":"Slack","img_class":"slack","data_logo":"Slack","configRoute":"/add-ons/slack"}];
      thirdParty.forEach(a => {
        links.push({
          text: a.name,
          type: 'Auth0 (Third Party App)',
          url: 'https://manage.auth0.com/#/externalapps'
        });
      });

      callback();
    },
    (callback) => {
      request(`https://azure.microsoft.com/en-us/documentation/articles/active-directory-saas-tutorial-list/`, (err, res, html) => {
        if (err) {
          return callback(err);
        }

        if (res.statusCode !== 200) {
          return callback(new Error('Unexpected status code: ' + res.statusCode));
        }

        var $ = cheerio.load(html);
        $('div.wa-spacer-docContent table tbody tr a').each((i, element) => {
          var link = $(element);
          links.push({
            text: link.text(),
            type: 'Azure AD',
            url: link.attr('href')
          });
        });

        callback();
      });
    },
    (callback) => {
      request(`https://github.com/okta/UserDocs/tree/master/SAML_Docs`, (err, res, html) => {
        if (err) {
          return callback(err);
        }

        if (res.statusCode !== 200) {
          return callback(new Error('Unexpected status code: ' + res.statusCode));
        }

        var $ = cheerio.load(html);
        $('div.repository-content tr.js-navigation-item a').each((i, element) => {
          var link = $(element);
          if (link.attr('href').indexOf('.html') >= 0
            && (link.attr('href').indexOf('How-to-') >= 0 || link.attr('href').indexOf('Configuring-SAML-') >= 0)) {
            links.push({
              text: link.text()
                .replace('How-to-Enable-SAML-2.0-in-', '')
                .replace('How-to-Configure-SAML-for-', '')
                .replace('How-To-Configure-SAML-1.1-for-', '')
                .replace('How-To-Configure-SAML-2.0-for-', '')
                .replace('How-to-Configure-SAML-1.1-for-', '')
                .replace('How-to-Configure-SAML-2.0-for-', '')
                .replace('How-to-configure-SAML-2.0-for-', '')
                .replace('Configure-SAML-1.1-in-', '')
                .replace('Configure-SAML-2.0-in-', '')
                .replace('Configuring-SAML-2.0-in-', '')
                .replace('Configuring SAML SSO for', '')
                .replace('Configure-CustomSSO-for-', '')
                .replace('Configuring SAML SSO for', '')
                .replace('How-to-', '')
                .replace('-', ' ')
                .replace('.html', ''),
              type: 'Okta',
              url: '?okta=' + link.attr('href')
            });
          }
        });

        callback();
      });
    },
    (callback) => {
      async.each([1, 2, 3, 4], (index, cb) => {
        request(`https://onelogin.zendesk.com/hc/en-us/sections/200245384-Application-Integration-SAML-?page=${index}#articles`, (err, res, html) => {
          if (err) {
            return cb(err);
          }

          if (res.statusCode !== 200) {
            return cb(new Error('Unexpected status code: ' + res.statusCode));
          }

          var $ = cheerio.load(html);
          $('ul.article-list li a').each((i, element) => {
            var link = $(element);
            links.push({
              text: link.text().replace('Configuring SAML for ', ''),
              type: 'OneLogin',
              url: 'https://onelogin.zendesk.com' + link.attr('href')
            });
          });

          cb();
        });
      }, (err) => {
        return callback(err);
      });
  }], (err, context) => {
    done(err, _.sortBy(links, 'text'));
  });
};
<commit_msg>Updated UI<commit_after>"use latest";

const _ = require('underscore');
const hbs = require('handlebars');
const async = require('async');
const request = require('request');
const cheerio = require('cheerio');

module.exports = (ctx, req, res) => {
  if (ctx.data && ctx.data.okta) {
    // The Okta tutorials are just raw HTML files in GitHub, no point in redirecting to it since it's unreadable.
    // Instead, we'll use this webtask as a proxy to serve the HTML within the page (in a modal dialog).
    return request.get('https://raw.githubusercontent.com' + ctx.data.okta.replace('/blob', ''), function(err, r, html) {
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end(html
        .replace(/\"resources/g, '"https://raw.githubusercontent.com/okta/UserDocs/master/SAML_Docs/resources')
        .replace(/\"images/g, '"https://raw.githubusercontent.com/okta/UserDocs/master/SAML_Docs/images'));
    });
  }

  const View = `

        <title>Third Party SAML Configuration</title>
        <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootswatch/3.3.5/cosmo/bootstrap.min.css">
        <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">

SAML Configuration Tutorials

This is a list of tutorials that show the SAML configuration of third party applications. Because these tutorials simply show how to configure the applications using SAML standard you can easily reuse them when integrating these apps with your own internal IdP or "SSO vendor".

            <input id="filter" type="text" class="form-control" placeholder="Filter in {{total}} tutorials from Auth0, Azure AD, Okta, OneLogin...">

          {{#if err}}

{{message}}

          {{else}}

                Source
                Application

              </thread>

              {{#each links}}

                  {{type}}
                  {{text}}

              {{/each}}

          {{/if}}

                <button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>

Thid Party Application

                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>

    `;

  getLinks((err, links) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });

    const template = hbs.compile(View);
    res.end(template({
      err: err,
      links: links,
      total: links.length
    }));
  });
};

//
const getLinks = (done) => {
  const links = [];
  async.waterfall([
    (callback) => {
      request(`https://auth0.com/docs/saml-apps`, (err, res, html) => {
        if (err) {
          return callback(err);
        }

        if (res.statusCode !== 200) {
          return callback(new Error('Unexpected status code: ' + res.statusCode));
        }

        var $ = cheerio.load(html);
        $('section.docs-content h2').each((i, element) => {
          var header = $(element);
          links.push({
            text: header.text(),
            type: 'Auth0',
            url: 'https://auth0.com/docs/saml-apps' + header.find('span').attr('href')
          });
        });

        callback();
      });
    },
    (callback) => {
      const thirdParty = [{"id":"salesforce","name":"Salesforce","img_class":"salesforce","data_logo":"Salesforce","configRoute":"/add-ons/salesforce","callback":"https://login.salesforce.com"},{"id":"dropbox","name":"Dropbox","img_class":"dropbox","data_logo":"Dropbox","configRoute":"/add-ons/dropbox","callback":"https://www.dropbox.com/saml_login"},{"id":"sharepoint","name":"SharePoint","img_class":"microsoft-sharepoint-logo","data_logo":"Sharepoint","configRoute":"/add-ons/sharepoint"},{"id":"rms","name":"AD RMS","img_class":"windows-logo","data_logo":"Windows Server Active Directory RMS","configRoute":"/add-ons/rms"},{"id":"zendesk","name":"Zendesk","img_class":"zendesk","data_logo":"Zendesk","configRoute":"/add-ons/zendesk"},{"id":"box","name":"Box","img_class":"box","data_logo":"Box","configRoute":"/add-ons/box","callback":"https://sso.services.box.net/sp/ACS.saml2"},{"id":"echosign","name":"EchoSign","img_class":"echosign","data_logo":"AdobeEchoSign","configRoute":"/add-ons/echosign"},{"id":"springcm","name":"SpringCM","img_class":"springcm","data_logo":"SpringCM","configRoute":"/add-ons/springcm"},{"id":"newrelic","name":"New Relic","img_class":"newrelic","data_logo":"NewRelic","configRoute":"/add-ons/newrelic"},{"id":"egnyte","name":"Egnyte","img_class":"egnyte","data_logo":"Egnyte","configRoute":"/add-ons/egnyte"},{"id":"cloudbees","name":"CloudBees","img_class":"cloudbees","data_logo":"CloudBees","configRoute":"/add-ons/cloudbees","callback":"https://grandcentral.cloudbees.com/authenticate/saml/consume"},{"id":"mscrm","name":"Dynamics CRM","img_class":"mscrm","data_logo":"MicrosoftDynamicsCRM","configRoute":"/add-ons/mscrm"},{"id":"office365","name":"Office 365 (beta)","img_class":"office365","data_logo":"Office365","configRoute":"/add-ons/office365"},{"id":"zoom","name":"Zoom","img_class":"zoom","data_logo":"Zoom","configRoute":"/add-ons/zoom"},{"id":"concur","name":"Concur (beta)","img_class":"concur","data_logo":"Concur","configRoute":"/add-ons/concur","callback":"https://www.concursolutions.com/SAMLRedirector/ClientSAMLLogin.aspx"},{"id":"slack","name":"Slack","img_class":"slack","data_logo":"Slack","configRoute":"/add-ons/slack"}];
      thirdParty.forEach(a => {
        links.push({
          text: a.name,
          type: 'Auth0 (Third Party App)',
          url: 'https://manage.auth0.com/#/externalapps'
        });
      });

      callback();
    },
    (callback) => {
      request(`https://azure.microsoft.com/en-us/documentation/articles/active-directory-saas-tutorial-list/`, (err, res, html) => {
        if (err) {
          return callback(err);
        }

        if (res.statusCode !== 200) {
          return callback(new Error('Unexpected status code: ' + res.statusCode));
        }

        var $ = cheerio.load(html);
        $('div.wa-spacer-docContent table tbody tr a').each((i, element) => {
          var link = $(element);
          links.push({
            text: link.text(),
            type: 'Azure AD',
            url: link.attr('href')
          });
        });

        callback();
      });
    },
    (callback) => {
      request(`https://github.com/okta/UserDocs/tree/master/SAML_Docs`, (err, res, html) => {
        if (err) {
          return callback(err);
        }

        if (res.statusCode !== 200) {
          return callback(new Error('Unexpected status code: ' + res.statusCode));
        }

        var $ = cheerio.load(html);
        $('div.repository-content tr.js-navigation-item a').each((i, element) => {
          var link = $(element);
          if (link.attr('href').indexOf('.html') >= 0
            && (link.attr('href').indexOf('How-to-') >= 0 || link.attr('href').indexOf('Configuring-SAML-') >= 0)) {
            links.push({
              text: link.text()
                .replace('How-to-Enable-SAML-2.0-in-', '')
                .replace('How-to-Configure-SAML-for-', '')
                .replace('How-To-Configure-SAML-1.1-for-', '')
                .replace('How-To-Configure-SAML-2.0-for-', '')
                .replace('How-to-Configure-SAML-1.1-for-', '')
                .replace('How-to-Configure-SAML-2.0-for-', '')
                .replace('How-to-configure-SAML-2.0-for-', '')
                .replace('Configure-SAML-1.1-in-', '')
                .replace('Configure-SAML-2.0-in-', '')
                .replace('Configuring-SAML-2.0-in-', '')
                .replace('Configuring SAML SSO for', '')
                .replace('Configure-CustomSSO-for-', '')
                .replace('Configuring SAML SSO for', '')
                .replace('How-to-', '')
                .replace('-', ' ')
                .replace('.html', ''),
              type: 'Okta',
              url: '?okta=' + link.attr('href')
            });
          }
        });

        callback();
      });
    },
    (callback) => {
      async.each([1, 2, 3, 4], (index, cb) => {
        request(`https://onelogin.zendesk.com/hc/en-us/sections/200245384-Application-Integration-SAML-?page=${index}#articles`, (err, res, html) => {
          if (err) {
            return cb(err);
          }

          if (res.statusCode !== 200) {
            return cb(new Error('Unexpected status code: ' + res.statusCode));
          }

          var $ = cheerio.load(html);
          $('ul.article-list li a').each((i, element) => {
            var link = $(element);
            links.push({
              text: link.text().replace('Configuring SAML for ', ''),
              type: 'OneLogin',
              url: 'https://onelogin.zendesk.com' + link.attr('href')
            });
          });

          cb();
        });
      }, (err) => {
        return callback(err);
      });
  }], (err, context) => {
    done(err, _.sortBy(links, 'text'));
  });
};
<|endoftext|>


<commit_before>const HELP_MESSAGE = 'I paste snippet code from Beanstalk links. \n\n' +
    '_How to get started_\n' +
    '1. Browse to a specific file in one of your Beanstalk repositories \n' +
    '2. In Beanstalk, click on the line of code you want to share \n' +
    '3. Copy the URL from your browser and paste it into slack \n' +
    ':champagne: :champagne:'

const EMPTY_REQUEST = "I'll need a valid URL to the file on Beanstalk"

const ERROR_MESSAGE = 'We had an issue getting the snippet from Beanstalk. Please make sure that you entered the correct username and authorization token.'

const MISSING_AUTH = 'We could not find your Team\'s Beanstalk Authorization info. Please go fill it out.'

const UNRECOGNIZED_REQUEST = 'I didn\'t understand that. Try asking for `help` or paste a Beanstalk file URL.'

const BS_URL_MATCH = '.beanstalkapp.com/'

module.exports = {
    HELP_MESSAGE,
    EMPTY_REQUEST,
    ERROR_MESSAGE,
    MISSING_AUTH,
    UNRECOGNIZED_REQUEST,
    BS_URL_MATCH
}
<commit_msg>Ignore max-len in constants<commit_after>/* eslint max-len: 0 */

const HELP_MESSAGE = 'I paste snippet code from Beanstalk links. \n\n' +
    '_How to get started_\n' +
    '1. Browse to a specific file in one of your Beanstalk repositories \n' +
    '2. In Beanstalk, click on the line of code you want to share \n' +
    '3. Copy the URL from your browser and paste it into slack \n' +
    ':champagne: :champagne:'

const EMPTY_REQUEST = "I'll need a valid URL to the file on Beanstalk"

const ERROR_MESSAGE = 'We had an issue getting the snippet from Beanstalk. Please make sure that you entered the correct username and authorization token.'

const MISSING_AUTH = 'We could not find your Team\'s Beanstalk Authorization info. Please go fill it out.'

const UNRECOGNIZED_REQUEST = 'I didn\'t understand that. Try asking for `help` or paste a Beanstalk file URL.'

const BS_URL_MATCH = '.beanstalkapp.com/'

module.exports = {
    HELP_MESSAGE,
    EMPTY_REQUEST,
    ERROR_MESSAGE,
    MISSING_AUTH,
    UNRECOGNIZED_REQUEST,
    BS_URL_MATCH
}
<|endoftext|>


<commit_before>import * as core from 'core-js';
import {Metadata} from 'aurelia-metadata';
import {AggregateError} from 'aurelia-logging';
import {ClassActivator} from './activators';
import {Resolver, StrategyResolver} from './resolvers';

// Fix Function#name on browsers that do not support it (IE):
function test() {}
if (!test.name) {
  Object.defineProperty(Function.prototype, 'name', {
    get: function() {
      let name = this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1];
      // For better performance only parse once, and then cache the
      // result through a new accessor for repeated access.
      Object.defineProperty(this, 'name', { value: name });
      return name;
    }
  });
}

const badKeyError = 'key/value cannot be null or undefined. Are you trying to inject/register something that doesn\'t exist with DI?';
export const emptyParameters = Object.freeze([]);

Metadata.registration = 'aurelia:registration';
Metadata.instanceActivator = 'aurelia:instance-activator';

class ConstructionInfo {
  constructor(activator, keys) {
    this.activator = activator;
    this.keys = keys;
  }
}

/**
* A lightweight, extensible dependency injection container.
*/
export class Container {
  static instance: Container;

  constructor(constructionInfo?: Map<Function, Object>, parent?: Container, root?: Container) {
    this.resolvers = new Map();
    this.resolvers.set(Container, new StrategyResolver(0, this));

    this.constructionInfo = constructionInfo === undefined ? new Map() : constructionInfo;
    this.root = root === undefined ? this : root;
    this.parent = parent === undefined ? null : parent;
  }

  /**
  * Makes this container instance globally reachable through Container.instance.
  */
  makeGlobal(): Container {
    Container.instance = this;
    return this;
  }

  /**
  * Registers an existing object instance with the container.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param instance The instance that will be resolved when the key is matched.
  */
  registerInstance(key: any, instance?: any): Resolver {
    return this._registerStrategyResolver(key, 0, instance === undefined ? key : instance);
  }

  /**
  * Registers a type (constructor function) such that the container always returns the same instance for each request.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param [fn] The constructor function to use when the dependency needs to be instantiated.
  */
  registerSingleton(key: any, fn?: Function): Resolver {
    return this._registerStrategyResolver(key, 1, fn === undefined ? key : fn);
  }

  /**
  * Registers a type (constructor function) such that the container returns a new instance for each request.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param [fn] The constructor function to use when the dependency needs to be instantiated.
  */
  registerTransient(key: any, fn?: Function): Resolver {
    return this._registerStrategyResolver(key, 2, fn === undefined ? key : fn);
  }

  /**
  * Registers a custom resolution function such that the container calls this function for each request to obtain the instance.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param handler The resolution function to use when the dependency is needed. It will be passed one arguement, the container instance that is invoking it.
  */
  registerHandler(key: any, handler: (container?: Container, key?: any, resolver?: Resolver) => any): Resolver {
    return this._registerStrategyResolver(key, 3, handler);
  }

  /**
  * Registers a type (constructor function) by inspecting its registration annotations. If none are found, then the default singleton registration is used.
  * @param fn The constructor function to use when the dependency needs to be instantiated.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  */
  autoRegister(fn: any, key?: any): Resolver {
    if (typeof fn === 'function') {
      let registration = Metadata.get(Metadata.registration, fn);

      if (registration === undefined) {
        return this._registerStrategyResolver(key === undefined ? fn : key, 1, fn);
      }

      let resolver = registration.createResolver(this, key === undefined ? fn : key, fn);
      this.resolvers.set(key === undefined ? fn : key, resolver);
      return resolver;
    }

    return this._registerStrategyResolver(key === undefined ? fn : key, 0, fn);
  }

  /**
  * Registers an array of types (constructor functions) by inspecting their registration annotations. If none are found, then the default singleton registration is used.
  * @param {Function[]} fns The constructor function to use when the dependency needs to be instantiated.
  */
  autoRegisterAll(fns: any[]): void {
    let i = fns.length;
    while (i--) {
      this.autoRegister(fns[i]);
    }
  }

  /**
  * Unregisters based on key.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  */
  unregister(key: any) : void {
    this.resolvers.delete(key);
  }

  /**
  * Inspects the container to determine if a particular key has been registred.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param checkParent Indicates whether or not to check the parent container hierarchy.
  * @return Returns true if the key has been registred; false otherwise.
  */
  hasResolver(key: any, checkParent?: boolean = false): boolean {
    if (key === null || key === undefined) {
      throw new Error(badKeyError);
    }

    return this.resolvers.has(key) || (checkParent && this.parent !== null && this.parent.hasResolver(key, checkParent));
  }

  /**
  * Creates a new dependency injection container whose parent is the current container.
  * @return Returns a new container instance parented to this.
  */
  createChild(): Container {
    return new Container(this.constructionInfo, this, this.root);
  }

  /**
  * Invokes a function, recursively resolving its dependencies.
  * @param fn The function to invoke with the auto-resolved dependencies.
  * @param deps Additional function dependencies to use during invocation.
  * @return Returns the instance resulting from calling the function.
  */
  invoke(fn: Function, deps?: any[]): any {
    let info;
    let i;
    let ii;
    let keys;
    let args;

    try {
      info = this._getOrCreateConstructionInfo(fn);
      keys = info.keys;
      args = new Array(keys.length);

      for (i = 0, ii = keys.length; i < ii; ++i) {
        args[i] = this.get(keys[i]);
      }

      if (deps !== undefined) {
        args = args.concat(deps);
      }

      return info.activator.invoke(fn, args);
    } catch(e) {
      let activatingText = info && info.activator instanceof ClassActivator ? 'instantiating' : 'invoking';
      let message = `Error ${activatingText} ${fn.name}.`;

      if (i < ii) {
        message += ` The argument at index ${i} (key:${keys[i]}) could not be satisfied.`;
      }

      message += ' Check the inner error for details.';

      throw new AggregateError(message, e, true);
    }
  }

  /**
  * Resolves a single instance based on the provided key.
  * @param key The key that identifies the object to resolve.
  * @return Returns the resolved instance.
  */
  get(key: any): any {
    if (key === null || key === undefined) {
      throw new Error(badKeyError);
    }

    if (key instanceof Resolver) {
      return key.get(this);
    }

    return this._get(key);
  }

  _get(key) {
    let resolver = this.resolvers.get(key);

    if (resolver === undefined) {
      if (this.parent === null) {
        resolver = this.autoRegister(key);
      } else {
        return this.parent._get(key);
      }
    }

    return resolver.get(this, key);
  }

  /**
  * Resolves all instance registered under the provided key.
  * @param key The key that identifies the objects to resolve.
  * @return Returns an array of the resolved instances.
  */
  getAll(key: any): any[] {
    if (key === null || key === undefined) {
      throw new Error(badKeyError);
    }

    let resolver = this.resolvers.get(key);

    if (resolver === undefined) {
      if (this.parent === null) {
        return emptyParameters;
      }

      return this.parent.getAll(key);
    }

    if (resolver.strategy === 4) {
      let state = resolver.state;
      let i = state.length;
      let results = new Array(i);

      while (i--) {
        results[i] = state[i].get(this, key);
      }

      return results;
    }

    return resolver.get(this, key);
  }

  _registerStrategyResolver(key, strategy, state) {
    if (key === null || key === undefined) {
      throw new Error(badKeyError);
    }

    let resolver = this.resolvers.get(key);

    if (resolver === undefined) {
      this.resolvers.set(key, resolver = new StrategyResolver(strategy, state));
    } else if (resolver.strategy === 4) {
      resolver.state.push(new StrategyResolver(strategy, state));
    } else {
      let states = [resolver, new StrategyResolver(strategy, state)];
      this.resolvers.set(key, resolver = new StrategyResolver(4, states));
    }

    return resolver;
  }

  _getOrCreateConstructionInfo(fn) {
    let info = this.constructionInfo.get(fn);

    if (info === undefined) {
      info = this._createConstructionInfo(fn);
      this.constructionInfo.set(fn, info);
    }

    return info;
  }

  _createConstructionInfo(fn) {
    let activator = Metadata.getOwn(Metadata.instanceActivator, fn) || ClassActivator.instance;

    if (typeof fn.inject === 'function') {
      return new ConstructionInfo(activator, fn.inject());
    } else if (fn.inject !== undefined) {
      return new ConstructionInfo(activator, fn.inject);
    }

    return new ConstructionInfo(activator, Metadata.getOwn(Metadata.paramTypes, fn) || emptyParameters);
  }
}
<commit_msg>fix(container): prevent a breaking api change<commit_after>import * as core from 'core-js';
import {Metadata} from 'aurelia-metadata';
import {AggregateError} from 'aurelia-logging';
import {ClassActivator} from './activators';
import {Resolver, StrategyResolver} from './resolvers';

// Fix Function#name on browsers that do not support it (IE):
function test() {}
if (!test.name) {
  Object.defineProperty(Function.prototype, 'name', {
    get: function() {
      let name = this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1];
      // For better performance only parse once, and then cache the
      // result through a new accessor for repeated access.
      Object.defineProperty(this, 'name', { value: name });
      return name;
    }
  });
}

const badKeyError = 'key/value cannot be null or undefined. Are you trying to inject/register something that doesn\'t exist with DI?';
export const emptyParameters = Object.freeze([]);

Metadata.registration = 'aurelia:registration';
Metadata.instanceActivator = 'aurelia:instance-activator';

class ConstructionInfo {
  constructor(activator, keys) {
    this.activator = activator;
    this.keys = keys;
  }
}

/**
* A lightweight, extensible dependency injection container.
*/
export class Container {
  static instance: Container;

  constructor(constructionInfo?: Map<Function, Object>, parent?: Container, root?: Container) {
    this.resolvers = new Map();
    this.resolvers.set(Container, new StrategyResolver(0, this));

    this.constructionInfo = constructionInfo === undefined ? new Map() : constructionInfo;
    this.root = root === undefined ? this : root;
    this.parent = parent === undefined ? null : parent;
  }

  /**
  * Makes this container instance globally reachable through Container.instance.
  */
  makeGlobal(): Container {
    Container.instance = this;
    return this;
  }

  /**
  * Registers an existing object instance with the container.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param instance The instance that will be resolved when the key is matched.
  */
  registerInstance(key: any, instance?: any): Resolver {
    return this._registerStrategyResolver(key, 0, instance === undefined ? key : instance);
  }

  /**
  * Registers a type (constructor function) such that the container always returns the same instance for each request.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param [fn] The constructor function to use when the dependency needs to be instantiated.
  */
  registerSingleton(key: any, fn?: Function): Resolver {
    return this._registerStrategyResolver(key, 1, fn === undefined ? key : fn);
  }

  /**
  * Registers a type (constructor function) such that the container returns a new instance for each request.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param [fn] The constructor function to use when the dependency needs to be instantiated.
  */
  registerTransient(key: any, fn?: Function): Resolver {
    return this._registerStrategyResolver(key, 2, fn === undefined ? key : fn);
  }

  /**
  * Registers a custom resolution function such that the container calls this function for each request to obtain the instance.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param handler The resolution function to use when the dependency is needed. It will be passed one arguement, the container instance that is invoking it.
  */
  registerHandler(key: any, handler: (container?: Container, key?: any, resolver?: Resolver) => any): Resolver {
    return this._registerStrategyResolver(key, 3, handler);
  }

  /**
  * Registers a type (constructor function) by inspecting its registration annotations. If none are found, then the default singleton registration is used.
  * @param fn The constructor function to use when the dependency needs to be instantiated.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  */
  autoRegister(fn: any, key?: any): Resolver {
    if (typeof fn === 'function') {
      let registration = Metadata.get(Metadata.registration, fn);

      if (registration === undefined) {
        return this._registerStrategyResolver(key === undefined ? fn : key, 1, fn);
      }

      let resolver = registration.createResolver(this, key === undefined ? fn : key, fn);
      this.resolvers.set(key === undefined ? fn : key, resolver);
      return resolver;
    }

    return this._registerStrategyResolver(key === undefined ? fn : key, 0, fn);
  }

  /**
  * Registers an array of types (constructor functions) by inspecting their registration annotations. If none are found, then the default singleton registration is used.
  * @param {Function[]} fns The constructor function to use when the dependency needs to be instantiated.
  */
  autoRegisterAll(fns: any[]): void {
    let i = fns.length;
    while (i--) {
      this.autoRegister(fns[i]);
    }
  }

  /**
  * Unregisters based on key.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  */
  unregister(key: any) : void {
    this.resolvers.delete(key);
  }

  /**
  * Inspects the container to determine if a particular key has been registred.
  * @param key The key that identifies the dependency at resolution time; usually a constructor function.
  * @param checkParent Indicates whether or not to check the parent container hierarchy.
  * @return Returns true if the key has been registred; false otherwise.
  */
  hasResolver(key: any, checkParent?: boolean = false): boolean {
    if (key === null || key === undefined) {
      throw new Error(badKeyError);
    }

    return this.resolvers.has(key) || (checkParent && this.parent !== null && this.parent.hasResolver(key, checkParent));
  }

  /**
  * Deprecated. Use hasResolver instead.
  */
  hasHandler(key, checkParent) {
    return this.hasResolver(key, checkParent);
  }

  /**
  * Creates a new dependency injection container whose parent is the current container.
  * @return Returns a new container instance parented to this.
  */
  createChild(): Container {
    return new Container(this.constructionInfo, this, this.root);
  }

  /**
  * Invokes a function, recursively resolving its dependencies.
  * @param fn The function to invoke with the auto-resolved dependencies.
  * @param deps Additional function dependencies to use during invocation.
  * @return Returns the instance resulting from calling the function.
  */
  invoke(fn: Function, deps?: any[]): any {
    let info;
    let i;
    let ii;
    let keys;
    let args;

    try {
      info = this._getOrCreateConstructionInfo(fn);
      keys = info.keys;
      args = new Array(keys.length);

      for (i = 0, ii = keys.length; i < ii; ++i) {
        args[i] = this.get(keys[i]);
      }

      if (deps !== undefined) {
        args = args.concat(deps);
      }

      return info.activator.invoke(fn, args);
    } catch(e) {
      let activatingText = info && info.activator instanceof ClassActivator ? 'instantiating' : 'invoking';
      let message = `Error ${activatingText} ${fn.name}.`;

      if (i < ii) {
        message += ` The argument at index ${i} (key:${keys[i]}) could not be satisfied.`;
      }

      message += ' Check the inner error for details.';

      throw new AggregateError(message, e, true);
    }
  }

  /**
  * Resolves a single instance based on the provided key.
  * @param key The key that identifies the object to resolve.
  * @return Returns the resolved instance.
  */
  get(key: any): any {
    if (key === null || key === undefined) {
      throw new Error(badKeyError);
    }

    if (key instanceof Resolver) {
      return key.get(this);
    }

    return this._get(key);
  }

  _get(key) {
    let resolver = this.resolvers.get(key);

    if (resolver === undefined) {
      if (this.parent === null) {
        resolver = this.autoRegister(key);
      } else {
        return this.parent._get(key);
      }
    }

    return resolver.get(this, key);
  }

  /**
  * Resolves all instance registered under the provided key.
  * @param key The key that identifies the objects to resolve.
  * @return Returns an array of the resolved instances.
  */
  getAll(key: any): any[] {
    if (key === null || key === undefined) {
      throw new Error(badKeyError);
    }

    let resolver = this.resolvers.get(key);

    if (resolver === undefined) {
      if (this.parent === null) {
        return emptyParameters;
      }

      return this.parent.getAll(key);
    }

    if (resolver.strategy === 4) {
      let state = resolver.state;
      let i = state.length;
      let results = new Array(i);

      while (i--) {
        results[i] = state[i].get(this, key);
      }

      return results;
    }

    return resolver.get(this, key);
  }

  _registerStrategyResolver(key, strategy, state) {
    if (key === null || key === undefined) {
      throw new Error(badKeyError);
    }

    let resolver = this.resolvers.get(key);

    if (resolver === undefined) {
      this.resolvers.set(key, resolver = new StrategyResolver(strategy, state));
    } else if (resolver.strategy === 4) {
      resolver.state.push(new StrategyResolver(strategy, state));
    } else {
      let states = [resolver, new StrategyResolver(strategy, state)];
      this.resolvers.set(key, resolver = new StrategyResolver(4, states));
    }

    return resolver;
  }

  _getOrCreateConstructionInfo(fn) {
    let info = this.constructionInfo.get(fn);

    if (info === undefined) {
      info = this._createConstructionInfo(fn);
      this.constructionInfo.set(fn, info);
    }

    return info;
  }

  _createConstructionInfo(fn) {
    let activator = Metadata.getOwn(Metadata.instanceActivator, fn) || ClassActivator.instance;

    if (typeof fn.inject === 'function') {
      return new ConstructionInfo(activator, fn.inject());
    } else if (fn.inject !== undefined) {
      return new ConstructionInfo(activator, fn.inject);
    }

    return new ConstructionInfo(activator, Metadata.getOwn(Metadata.paramTypes, fn) || emptyParameters);
  }
}
<|endoftext|>


<commit_before>!(function () {
    function extend () {
        var target = arguments[0] || {}, length = arguments.length, i = 1, options, name, src, copy;
        for (; i < length; i++) {
            // Only deal with non-null/undefined values
            if ((options = arguments[i]) != null) {
                // Extend the base object
                for (name in options) {
                    src = target[name];
                    copy = options[name];

                    // Prevent never-ending loop
                    if (target === copy) {
                        continue;
                    }

                    if (copy !== undefined) {
                        target[name] = copy;
                    }
                }
            }
        }
        return target;
    }

    /**
     * 客户端观察者，主要处理事件的添加、删除、执行等
     * @class BI.OB
     * @abstract
     */
    var OB = function (config) {
        this._constructor(config);
    };
    _.extend(OB.prototype, {
        props: {},
        init: null,
        destroyed: null,

        _constructor: function (config) {
            this._initProps(config);
            this._init();
            this._initRef();
        },

        _defaultConfig: function (config) {
            return {};
        },

        _initProps: function (config) {
            var props = this.props;
            if (BI.isFunction(this.props)) {
                props = this.props(config);
            }
            var defaultProps = extend(this._defaultConfig(config), props);
            var modifiedDefaultProps = (config && config.type && BI.OB.configFunctions[config.type + ".props"]) ? BI.reduce(BI.OB.configFunctions[config.type + ".props"], function (value, conf, index) {
                return extend({}, conf, value.fn(defaultProps, config, value.opt));
            }, {}) : null;
            this.options = extend(defaultProps, modifiedDefaultProps, config);
        },

        _init: function () {
            this._initListeners();
            this.init && this.init();
        },

        _initListeners: function () {
            var self = this;
            if (this.options.listeners != null) {
                _.each(this.options.listeners, function (lis, eventName) {
                    if (_.isFunction(lis)) {
                        self.on(eventName, lis);
                        return;
                    }
                    (lis.target ? lis.target : self)[lis.once ? "once" : "on"](lis.eventName, _.bind(lis.action, self));
                });
                delete this.options.listeners;
            }
        },

        // 获得一个当前对象的引用
        _initRef: function () {
            if (this.options.__ref) {
                this.options.__ref.call(this, this);
            }
            if (this.options.ref) {
                this.options.ref.call(this, this);
            }
        },

        //释放当前对象
        _purgeRef: function () {
            if (this.options.__ref) {
                this.options.__ref.call(null);
                this.options.__ref = null;
            }
            if (this.options.ref) {
                this.options.ref.call(null);
                this.options.ref = null;
            }
        },

        _getEvents: function () {
            if (!_.isObject(this.events)) {
                this.events = {};
            }
            return this.events;
        },

        /**
         * 给观察者绑定一个事件
         * @param {String} eventName 事件的名字
         * @param {Function} fn 事件对应的执行函数
         */
        on: function (eventName, fn) {
            var self = this;
            eventName = eventName.toLowerCase();
            var fns = this._getEvents()[eventName];
            if (!_.isArray(fns)) {
                fns = [];
                this._getEvents()[eventName] = fns;
            }
            fns.push(fn);

            return function () {
                self.un(eventName, fn);
            };
        },

        /**
         * 给观察者绑定一个只执行一次的事件
         * @param {String} eventName 事件的名字
         * @param {Function} fn 事件对应的执行函数
         */
        once: function (eventName, fn) {
            var proxy = function () {
                fn.apply(this, arguments);
                this.un(eventName, proxy);
            };
            this.on(eventName, proxy);
        },
        /**
         * 解除观察者绑定的指定事件
         * @param {String} eventName 要解除绑定事件的名字
         * @param {Function} fn 事件对应的执行函数，该参数是可选的，没有该参数时，将解除绑定所有同名字的事件
         */
        un: function (eventName, fn) {
            eventName = eventName.toLowerCase();

            /* alex:如果fn是null,就是把eventName上面所有方法都un掉*/
            if (fn == null) {
                delete this._getEvents()[eventName];
            } else {
                var fns = this._getEvents()[eventName];
                if (_.isArray(fns)) {
                    var newFns = [];
                    _.each(fns, function (ifn) {
                        if (ifn != fn) {
                            newFns.push(ifn);
                        }
                    });
                    this._getEvents()[eventName] = newFns;
                }
            }
        },
        /**
         * 清除观察者的所有事件绑定
         */
        purgeListeners: function () {
            /* alex:清空events*/
            this.events = {};
        },
        /**
         * 触发绑定过的事件
         *
         * @param {String} eventName 要触发的事件的名字
         * @returns {Boolean} 如果事件函数返回false，则返回false并中断其他同名事件的执行，否则执行所有的同名事件并返回true
         */
        fireEvent: function () {
            var eventName = arguments[0].toLowerCase();
            var fns = this._getEvents()[eventName];
            if (BI.isArray(fns)) {
                if (BI.isArguments(arguments[1])) {
                    for (var i = 0; i < fns.length; i++) {
                        if (fns[i].apply(this, arguments[1]) === false) {
                            return false;
                        }
                    }
                } else {
                    var args = Array.prototype.slice.call(arguments, 1);
                    for (var i = 0; i < fns.length; i++) {
                        if (fns[i].apply(this, args) === false) {
                            return false;
                        }
                    }
                }
            }
            return true;
        },

        destroy: function () {
            this.destroyed && this.destroyed();
            this._purgeRef();
            this.purgeListeners();
        }
    });
    BI.OB = BI.OB || OB;
})();
<commit_msg>Pull request #2566: 无JIRA任务 bugfix<commit_after>!(function () {
    function extend () {
        var target = arguments[0] || {}, length = arguments.length, i = 1, options, name, src, copy;
        for (; i < length; i++) {
            // Only deal with non-null/undefined values
            if ((options = arguments[i]) != null) {
                // Extend the base object
                for (name in options) {
                    src = target[name];
                    copy = options[name];

                    // Prevent never-ending loop
                    if (target === copy) {
                        continue;
                    }

                    if (copy !== undefined) {
                        target[name] = copy;
                    }
                }
            }
        }
        return target;
    }

    /**
     * 客户端观察者，主要处理事件的添加、删除、执行等
     * @class BI.OB
     * @abstract
     */
    var OB = function (config) {
        this._constructor(config);
    };
    _.extend(OB.prototype, {
        props: {},
        init: null,
        destroyed: null,

        _constructor: function (config) {
            this._initProps(config);
            this._init();
            this._initRef();
        },

        _defaultConfig: function (config) {
            return {};
        },

        _initProps: function (config) {
            var props = this.props;
            if (BI.isFunction(this.props)) {
                props = this.props(config);
            }
            var defaultProps = extend(this._defaultConfig(config), props);
            var modifiedDefaultProps = (config && config.type && BI.OB.configFunctions[config.type + ".props"]) ? BI.reduce(BI.OB.configFunctions[config.type + ".props"], function (value, conf, index) {
                return extend(conf, value.fn(defaultProps, config, value.opt));
            }, {}) : null;
            this.options = extend(defaultProps, modifiedDefaultProps, config);
        },

        _init: function () {
            this._initListeners();
            this.init && this.init();
        },

        _initListeners: function () {
            var self = this;
            if (this.options.listeners != null) {
                _.each(this.options.listeners, function (lis, eventName) {
                    if (_.isFunction(lis)) {
                        self.on(eventName, lis);
                        return;
                    }
                    (lis.target ? lis.target : self)[lis.once ? "once" : "on"](lis.eventName, _.bind(lis.action, self));
                });
                delete this.options.listeners;
            }
        },

        // 获得一个当前对象的引用
        _initRef: function () {
            if (this.options.__ref) {
                this.options.__ref.call(this, this);
            }
            if (this.options.ref) {
                this.options.ref.call(this, this);
            }
        },

        //释放当前对象
        _purgeRef: function () {
            if (this.options.__ref) {
                this.options.__ref.call(null);
                this.options.__ref = null;
            }
            if (this.options.ref) {
                this.options.ref.call(null);
                this.options.ref = null;
            }
        },

        _getEvents: function () {
            if (!_.isObject(this.events)) {
                this.events = {};
            }
            return this.events;
        },

        /**
         * 给观察者绑定一个事件
         * @param {String} eventName 事件的名字
         * @param {Function} fn 事件对应的执行函数
         */
        on: function (eventName, fn) {
            var self = this;
            eventName = eventName.toLowerCase();
            var fns = this._getEvents()[eventName];
            if (!_.isArray(fns)) {
                fns = [];
                this._getEvents()[eventName] = fns;
            }
            fns.push(fn);

            return function () {
                self.un(eventName, fn);
            };
        },

        /**
         * 给观察者绑定一个只执行一次的事件
         * @param {String} eventName 事件的名字
         * @param {Function} fn 事件对应的执行函数
         */
        once: function (eventName, fn) {
            var proxy = function () {
                fn.apply(this, arguments);
                this.un(eventName, proxy);
            };
            this.on(eventName, proxy);
        },
        /**
         * 解除观察者绑定的指定事件
         * @param {String} eventName 要解除绑定事件的名字
         * @param {Function} fn 事件对应的执行函数，该参数是可选的，没有该参数时，将解除绑定所有同名字的事件
         */
        un: function (eventName, fn) {
            eventName = eventName.toLowerCase();

            /* alex:如果fn是null,就是把eventName上面所有方法都un掉*/
            if (fn == null) {
                delete this._getEvents()[eventName];
            } else {
                var fns = this._getEvents()[eventName];
                if (_.isArray(fns)) {
                    var newFns = [];
                    _.each(fns, function (ifn) {
                        if (ifn != fn) {
                            newFns.push(ifn);
                        }
                    });
                    this._getEvents()[eventName] = newFns;
                }
            }
        },
        /**
         * 清除观察者的所有事件绑定
         */
        purgeListeners: function () {
            /* alex:清空events*/
            this.events = {};
        },
        /**
         * 触发绑定过的事件
         *
         * @param {String} eventName 要触发的事件的名字
         * @returns {Boolean} 如果事件函数返回false，则返回false并中断其他同名事件的执行，否则执行所有的同名事件并返回true
         */
        fireEvent: function () {
            var eventName = arguments[0].toLowerCase();
            var fns = this._getEvents()[eventName];
            if (BI.isArray(fns)) {
                if (BI.isArguments(arguments[1])) {
                    for (var i = 0; i < fns.length; i++) {
                        if (fns[i].apply(this, arguments[1]) === false) {
                            return false;
                        }
                    }
                } else {
                    var args = Array.prototype.slice.call(arguments, 1);
                    for (var i = 0; i < fns.length; i++) {
                        if (fns[i].apply(this, args) === false) {
                            return false;
                        }
                    }
                }
            }
            return true;
        },

        destroy: function () {
            this.destroyed && this.destroyed();
            this._purgeRef();
            this.purgeListeners();
        }
    });
    BI.OB = BI.OB || OB;
})();
<|endoftext|>


<commit_before>import Utils from '../core/Utils'
import Translator from '../core/Translator'
import ee from 'namespace-emitter'
// import deepFreeze from 'deep-freeze-strict'
import UppySocket from './UppySocket'
import en_US from '../locales/en_US'

/**
 * Main Uppy core
 *
 * @param {object} opts general options, like locales, to show modal or not to show
 */
export default class Core {
  constructor (opts) {
    // set default options
    const defaultOptions = {
      // load English as the default locales
      locales: en_US,
      autoProceed: true,
      debug: false
    }

    // Merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)

    // Dictates in what order different plugin types are ran:
    this.types = [ 'presetter', 'orchestrator', 'progressindicator',
                    'acquirer', 'modifier', 'uploader', 'presenter', 'debugger']

    this.type = 'core'

    // Container for different types of plugins
    this.plugins = {}

    this.translator = new Translator({locales: this.opts.locales})
    this.i18n = this.translator.translate.bind(this.translator)
    this.getState = this.getState.bind(this)
    this.updateMeta = this.updateMeta.bind(this)
    this.initSocket = this.initSocket.bind(this)
    this.log = this.log.bind(this)
    this.addFile = this.addFile.bind(this)

    this.bus = this.emitter = ee()
    this.on = this.bus.on.bind(this.bus)
    this.emit = this.bus.emit.bind(this.bus)

    this.state = {
      files: {},
      capabilities: {}
    }

    if (this.opts.debug) {
      // for debugging and testing
      global.UppyState = this.state
      global.uppyLog = ''
      global.UppyAddFile = this.addFile.bind(this)
      global._Uppy = this
    }
  }

  /**
   * Iterate on all plugins and run `update` on them. Called each time when state changes
   *
   */
  updateAll (state) {
    Object.keys(this.plugins).forEach((pluginType) => {
      this.plugins[pluginType].forEach((plugin) => {
        plugin.update(state)
      })
    })
  }

  /**
   * Updates state
   *
   * @param {newState} object
   */
  setState (stateUpdate) {
    const newState = Object.assign({}, this.state, stateUpdate)
    this.bus.emit('core:state-update', this.state, newState, stateUpdate)

    this.state = newState
    this.updateAll(this.state)

    // this.log('Updating state with: ')
    // this.log(newState)
  }

  /**
   * Gets current state, making sure to make a copy of the state object and pass that,
   * instead of an actual reference to `this.state`
   *
   */
  getState () {
    // return deepFreeze(this.state)
    return this.state
  }

  updateMeta (data, fileID) {
    const updatedFiles = Object.assign({}, this.getState().files)
    const newMeta = Object.assign({}, updatedFiles[fileID].meta, data)
    updatedFiles[fileID] = Object.assign({}, updatedFiles[fileID], {
      meta: newMeta
    })
    this.setState({files: updatedFiles})
  }

  addFile (file) {
    const updatedFiles = Object.assign({}, this.state.files)

    const fileName = file.name || 'noname'
    const fileType = Utils.getFileType(file) ? Utils.getFileType(file).split('/') : ['', '']
    const fileTypeGeneral = fileType[0]
    const fileTypeSpecific = fileType[1]
    const fileExtension = Utils.getFileNameAndExtension(fileName)[1]
    const isRemote = file.isRemote || false

    const fileID = Utils.generateFileID(fileName)

    const newFile = {
      source: file.source || '',
      id: fileID,
      name: fileName,
      extension: fileExtension || '',
      meta: {
        name: fileName
      },
      type: {
        general: fileTypeGeneral,
        specific: fileTypeSpecific
      },
      data: file.data,
      progress: {
        percentage: 0,
        uploadComplete: false,
        uploadStarted: false
      },
      size: file.data.size || 0,
      isRemote: isRemote,
      remote: file.remote || ''
    }

    updatedFiles[fileID] = newFile
    this.setState({files: updatedFiles})

    this.bus.emit('file-added', fileID)
    this.log(`Added file: ${fileName}, ${fileID}`)

    if (fileTypeGeneral === 'image' && !isRemote) {
      this.addThumbnail(newFile.id)
    }

    if (this.opts.autoProceed) {
      this.bus.emit('core:upload')
    }
  }

  removeFile (fileID) {
    const updatedFiles = Object.assign({}, this.getState().files)
    delete updatedFiles[fileID]
    this.setState({files: updatedFiles})
  }

  addThumbnail (fileID) {
    const file = this.getState().files[fileID]

    Utils.readFile(file.data)
      .then((imgDataURI) => Utils.createImageThumbnail(imgDataURI, 200))
      .then((thumbnail) => {
        const updatedFiles = Object.assign({}, this.getState().files)
        const updatedFile = Object.assign({}, updatedFiles[fileID], {
          preview: thumbnail
        })
        updatedFiles[fileID] = updatedFile
        this.setState({files: updatedFiles})
      })
  }

  /**
   * Registers listeners for all global actions, like:
   * `file-add`, `file-remove`, `upload-progress`, `reset`
   *
   */
  actions () {
    // this.bus.on('*', (payload) => {
    //   console.log('emitted: ', this.event)
    //   console.log('with payload: ', payload)
    // })

    // const bus = this.bus

    this.on('core:file-add', (data) => {
      this.addFile(data)
    })

    // `remove-file` removes a file from `state.files`, for example when
    // a user decides not to upload particular file and clicks a button to remove it
    this.on('core:file-remove', (fileID) => {
      this.removeFile(fileID)
    })

    this.on('core:upload-started', (fileID, upload) => {
      const updatedFiles = Object.assign({}, this.getState().files)
      const updatedFile = Object.assign({}, updatedFiles[fileID],
        Object.assign({}, {
          progress: Object.assign({}, updatedFiles[fileID].progress, {
            uploadStarted: Date.now()
          })
        }
      ))
      updatedFiles[fileID] = updatedFile

      this.setState({files: updatedFiles})
    })

    this.on('core:upload-progress', (data) => {
      const fileID = data.id
      const updatedFiles = Object.assign({}, this.getState().files)
      if (!updatedFiles[fileID]) {
        console.error('Trying to set progress for a file that’s not with us anymore: ', fileID)
        return
      }

      const updatedFile = Object.assign({}, updatedFiles[fileID],
        Object.assign({}, {
          progress: Object.assign({}, updatedFiles[fileID].progress, {
            bytesUploaded: data.bytesUploaded,
            bytesTotal: data.bytesTotal,
            percentage: Math.round((data.bytesUploaded / data.bytesTotal * 100).toFixed(2))
          })
        }
      ))
      updatedFiles[data.id] = updatedFile

      // calculate total progress, using the number of files currently uploading,
      // multiplied by 100 and the summ of individual progress of each file
      const inProgress = Object.keys(updatedFiles).filter((file) => {
        return updatedFiles[file].progress.uploadStarted
      })
      const progressMax = inProgress.length * 100
      let progressAll = 0
      inProgress.forEach((file) => {
        progressAll = progressAll + updatedFiles[file].progress.percentage
      })

      const totalProgress = Math.round((progressAll * 100 / progressMax).toFixed(2))

      if (totalProgress === 100) {
        const completeFiles = Object.keys(updatedFiles).filter((file) => {
          // this should be `uploadComplete`
          return updatedFiles[file].progress.percentage === 100
        })
        this.emit('core:success', completeFiles.length)
      }

      this.setState({
        totalProgress: totalProgress,
        files: updatedFiles
      })
    })

    this.on('core:upload-success', (fileID, uploadURL) => {
      const updatedFiles = Object.assign({}, this.getState().files)
      const updatedFile = Object.assign({}, updatedFiles[fileID], {
        progress: Object.assign({}, updatedFiles[fileID].progress, {
          uploadComplete: true
        }),
        uploadURL: uploadURL
      })
      updatedFiles[fileID] = updatedFile

      this.setState({
        files: updatedFiles
      })
    })

    this.on('core:update-meta', (data, fileID) => {
      this.updateMeta(data, fileID)
    })

    // show informer if offline
    if (typeof window !== 'undefined') {
      window.addEventListener('online', () => this.isOnline(true))
      window.addEventListener('offline', () => this.isOnline(false))
      setTimeout(() => this.isOnline(), 3000)
    }
  }

  isOnline (status) {
    const online = status || window.navigator.onLine
    if (!online) {
      this.emit('is-offline')
      this.emit('informer', 'No internet connection', 'error', 0)
      this.wasOffline = true
    } else {
      this.emit('is-online')
      if (this.wasOffline) {
        this.emit('informer', 'Connected!', 'success', 3000)
        this.wasOffline = false
      }
    }
  }

/**
 * Registers a plugin with Core
 *
 * @param {Class} Plugin object
 * @param {Object} options object that will be passed to Plugin later
 * @return {Object} self for chaining
 */
  use (Plugin, opts) {
    // Prepare props to pass to plugins
    const props = {
      getState: this.getState.bind(this),
      setState: this.setState.bind(this),
      updateMeta: this.updateMeta.bind(this),
      addFile: this.addFile.bind(this),
      i18n: this.i18n.bind(this),
      bus: this.ee,
      log: this.log.bind(this)
    }
    // Instantiate
    const plugin = new Plugin(this, opts, props)
    const pluginName = plugin.id
    this.plugins[plugin.type] = this.plugins[plugin.type] || []

    if (!pluginName) {
      throw new Error('Your plugin must have a name')
    }

    if (!plugin.type) {
      throw new Error('Your plugin must have a type')
    }

    let existsPluginAlready = this.getPlugin(pluginName)
    if (existsPluginAlready) {
      let msg = `Already found a plugin named '${existsPluginAlready.name}'.
        Tried to use: '${pluginName}'.
        Uppy is currently limited to running one of every plugin.
        Share your use case with us over at
        https://github.com/transloadit/uppy/issues/
        if you want us to reconsider.`
      throw new Error(msg)
    }

    this.plugins[plugin.type].push(plugin)

    return this
  }

/**
 * Find one Plugin by name
 *
 * @param string name description
 */
  getPlugin (name) {
    let foundPlugin = false
    this.iteratePlugins((plugin) => {
      const pluginName = plugin.id
      if (pluginName === name) {
        foundPlugin = plugin
        return false
      }
    })
    return foundPlugin
  }

/**
 * Iterate through all `use`d plugins
 *
 * @param function method description
 */
  iteratePlugins (method) {
    Object.keys(this.plugins).forEach((pluginType) => {
      this.plugins[pluginType].forEach(method)
    })
  }

/**
 * Logs stuff to console, only if `debug` is set to true. Silent in production.
 *
 * @return {String|Object} to log
 */
  log (msg) {
    if (!this.opts.debug) {
      return
    }
    if (msg === `${msg}`) {
      console.log(`LOG: ${msg}`)
    } else {
      console.dir(msg)
    }
    global.uppyLog = global.uppyLog + '\n' + 'DEBUG LOG: ' + msg
  }

  initSocket (opts) {
    if (!this.socket) {
      this.socket = new UppySocket(opts)
    }

    return this.socket
  }

  installAll () {
    Object.keys(this.plugins).forEach((pluginType) => {
      this.plugins[pluginType].forEach((plugin) => {
        plugin.install()
      })
    })
  }

/**
 * Initializes actions, installs all plugins (by iterating on them and calling `install`), sets options
 *
 * (In the past was used to run a waterfall of runType plugin packs, like so:
 * All preseters(data) --> All acquirers(data) --> All uploaders(data) --> done)
 */
  run () {
    this.log('Core is run, initializing actions, installing plugins...')

    // setInterval(() => {
    //   this.updateAll(this.state)
    // }, 1000)

    this.actions()

    // Forse set `autoProceed` option to false if there are multiple selector Plugins active
    if (this.plugins.acquirer && this.plugins.acquirer.length > 1) {
      this.opts.autoProceed = false
    }

    // Install all plugins
    this.installAll()

    return
  }
}
<commit_msg>add totalProgress: 0 to initial state<commit_after>import Utils from '../core/Utils'
import Translator from '../core/Translator'
import ee from 'namespace-emitter'
// import deepFreeze from 'deep-freeze-strict'
import UppySocket from './UppySocket'
import en_US from '../locales/en_US'

/**
 * Main Uppy core
 *
 * @param {object} opts general options, like locales, to show modal or not to show
 */
export default class Core {
  constructor (opts) {
    // set default options
    const defaultOptions = {
      // load English as the default locales
      locales: en_US,
      autoProceed: true,
      debug: false
    }

    // Merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)

    // Dictates in what order different plugin types are ran:
    this.types = [ 'presetter', 'orchestrator', 'progressindicator',
                    'acquirer', 'modifier', 'uploader', 'presenter', 'debugger']

    this.type = 'core'

    // Container for different types of plugins
    this.plugins = {}

    this.translator = new Translator({locales: this.opts.locales})
    this.i18n = this.translator.translate.bind(this.translator)
    this.getState = this.getState.bind(this)
    this.updateMeta = this.updateMeta.bind(this)
    this.initSocket = this.initSocket.bind(this)
    this.log = this.log.bind(this)
    this.addFile = this.addFile.bind(this)

    this.bus = this.emitter = ee()
    this.on = this.bus.on.bind(this.bus)
    this.emit = this.bus.emit.bind(this.bus)

    this.state = {
      files: {},
      capabilities: {},
      totalProgress: 0
    }

    if (this.opts.debug) {
      // for debugging and testing
      global.UppyState = this.state
      global.uppyLog = ''
      global.UppyAddFile = this.addFile.bind(this)
      global._Uppy = this
    }
  }

  /**
   * Iterate on all plugins and run `update` on them. Called each time when state changes
   *
   */
  updateAll (state) {
    Object.keys(this.plugins).forEach((pluginType) => {
      this.plugins[pluginType].forEach((plugin) => {
        plugin.update(state)
      })
    })
  }

  /**
   * Updates state
   *
   * @param {newState} object
   */
  setState (stateUpdate) {
    const newState = Object.assign({}, this.state, stateUpdate)
    this.bus.emit('core:state-update', this.state, newState, stateUpdate)

    this.state = newState
    this.updateAll(this.state)

    // this.log('Updating state with: ')
    // this.log(newState)
  }

  /**
   * Gets current state, making sure to make a copy of the state object and pass that,
   * instead of an actual reference to `this.state`
   *
   */
  getState () {
    // return deepFreeze(this.state)
    return this.state
  }

  updateMeta (data, fileID) {
    const updatedFiles = Object.assign({}, this.getState().files)
    const newMeta = Object.assign({}, updatedFiles[fileID].meta, data)
    updatedFiles[fileID] = Object.assign({}, updatedFiles[fileID], {
      meta: newMeta
    })
    this.setState({files: updatedFiles})
  }

  addFile (file) {
    const updatedFiles = Object.assign({}, this.state.files)

    const fileName = file.name || 'noname'
    const fileType = Utils.getFileType(file) ? Utils.getFileType(file).split('/') : ['', '']
    const fileTypeGeneral = fileType[0]
    const fileTypeSpecific = fileType[1]
    const fileExtension = Utils.getFileNameAndExtension(fileName)[1]
    const isRemote = file.isRemote || false

    const fileID = Utils.generateFileID(fileName)

    const newFile = {
      source: file.source || '',
      id: fileID,
      name: fileName,
      extension: fileExtension || '',
      meta: {
        name: fileName
      },
      type: {
        general: fileTypeGeneral,
        specific: fileTypeSpecific
      },
      data: file.data,
      progress: {
        percentage: 0,
        uploadComplete: false,
        uploadStarted: false
      },
      size: file.data.size || 0,
      isRemote: isRemote,
      remote: file.remote || ''
    }

    updatedFiles[fileID] = newFile
    this.setState({files: updatedFiles})

    this.bus.emit('file-added', fileID)
    this.log(`Added file: ${fileName}, ${fileID}`)

    if (fileTypeGeneral === 'image' && !isRemote) {
      this.addThumbnail(newFile.id)
    }

    if (this.opts.autoProceed) {
      this.bus.emit('core:upload')
    }
  }

  removeFile (fileID) {
    const updatedFiles = Object.assign({}, this.getState().files)
    delete updatedFiles[fileID]
    this.setState({files: updatedFiles})
  }

  addThumbnail (fileID) {
    const file = this.getState().files[fileID]

    Utils.readFile(file.data)
      .then((imgDataURI) => Utils.createImageThumbnail(imgDataURI, 200))
      .then((thumbnail) => {
        const updatedFiles = Object.assign({}, this.getState().files)
        const updatedFile = Object.assign({}, updatedFiles[fileID], {
          preview: thumbnail
        })
        updatedFiles[fileID] = updatedFile
        this.setState({files: updatedFiles})
      })
  }

  /**
   * Registers listeners for all global actions, like:
   * `file-add`, `file-remove`, `upload-progress`, `reset`
   *
   */
  actions () {
    // this.bus.on('*', (payload) => {
    //   console.log('emitted: ', this.event)
    //   console.log('with payload: ', payload)
    // })

    // const bus = this.bus

    this.on('core:file-add', (data) => {
      this.addFile(data)
    })

    // `remove-file` removes a file from `state.files`, for example when
    // a user decides not to upload particular file and clicks a button to remove it
    this.on('core:file-remove', (fileID) => {
      this.removeFile(fileID)
    })

    this.on('core:upload-started', (fileID, upload) => {
      const updatedFiles = Object.assign({}, this.getState().files)
      const updatedFile = Object.assign({}, updatedFiles[fileID],
        Object.assign({}, {
          progress: Object.assign({}, updatedFiles[fileID].progress, {
            uploadStarted: Date.now()
          })
        }
      ))
      updatedFiles[fileID] = updatedFile

      this.setState({files: updatedFiles})
    })

    this.on('core:upload-progress', (data) => {
      const fileID = data.id
      const updatedFiles = Object.assign({}, this.getState().files)
      if (!updatedFiles[fileID]) {
        console.error('Trying to set progress for a file that’s not with us anymore: ', fileID)
        return
      }

      const updatedFile = Object.assign({}, updatedFiles[fileID],
        Object.assign({}, {
          progress: Object.assign({}, updatedFiles[fileID].progress, {
            bytesUploaded: data.bytesUploaded,
            bytesTotal: data.bytesTotal,
            percentage: Math.round((data.bytesUploaded / data.bytesTotal * 100).toFixed(2))
          })
        }
      ))
      updatedFiles[data.id] = updatedFile

      // calculate total progress, using the number of files currently uploading,
      // multiplied by 100 and the summ of individual progress of each file
      const inProgress = Object.keys(updatedFiles).filter((file) => {
        return updatedFiles[file].progress.uploadStarted
      })
      const progressMax = inProgress.length * 100
      let progressAll = 0
      inProgress.forEach((file) => {
        progressAll = progressAll + updatedFiles[file].progress.percentage
      })

      const totalProgress = Math.round((progressAll * 100 / progressMax).toFixed(2))

      if (totalProgress === 100) {
        const completeFiles = Object.keys(updatedFiles).filter((file) => {
          // this should be `uploadComplete`
          return updatedFiles[file].progress.percentage === 100
        })
        this.emit('core:success', completeFiles.length)
      }

      this.setState({
        totalProgress: totalProgress,
        files: updatedFiles
      })
    })

    this.on('core:upload-success', (fileID, uploadURL) => {
      const updatedFiles = Object.assign({}, this.getState().files)
      const updatedFile = Object.assign({}, updatedFiles[fileID], {
        progress: Object.assign({}, updatedFiles[fileID].progress, {
          uploadComplete: true
        }),
        uploadURL: uploadURL
      })
      updatedFiles[fileID] = updatedFile

      this.setState({
        files: updatedFiles
      })
    })

    this.on('core:update-meta', (data, fileID) => {
      this.updateMeta(data, fileID)
    })

    // show informer if offline
    if (typeof window !== 'undefined') {
      window.addEventListener('online', () => this.isOnline(true))
      window.addEventListener('offline', () => this.isOnline(false))
      setTimeout(() => this.isOnline(), 3000)
    }
  }

  isOnline (status) {
    const online = status || window.navigator.onLine
    if (!online) {
      this.emit('is-offline')
      this.emit('informer', 'No internet connection', 'error', 0)
      this.wasOffline = true
    } else {
      this.emit('is-online')
      if (this.wasOffline) {
        this.emit('informer', 'Connected!', 'success', 3000)
        this.wasOffline = false
      }
    }
  }

/**
 * Registers a plugin with Core
 *
 * @param {Class} Plugin object
 * @param {Object} options object that will be passed to Plugin later
 * @return {Object} self for chaining
 */
  use (Plugin, opts) {
    // Prepare props to pass to plugins
    const props = {
      getState: this.getState.bind(this),
      setState: this.setState.bind(this),
      updateMeta: this.updateMeta.bind(this),
      addFile: this.addFile.bind(this),
      i18n: this.i18n.bind(this),
      bus: this.ee,
      log: this.log.bind(this)
    }
    // Instantiate
    const plugin = new Plugin(this, opts, props)
    const pluginName = plugin.id
    this.plugins[plugin.type] = this.plugins[plugin.type] || []

    if (!pluginName) {
      throw new Error('Your plugin must have a name')
    }

    if (!plugin.type) {
      throw new Error('Your plugin must have a type')
    }

    let existsPluginAlready = this.getPlugin(pluginName)
    if (existsPluginAlready) {
      let msg = `Already found a plugin named '${existsPluginAlready.name}'.
        Tried to use: '${pluginName}'.
        Uppy is currently limited to running one of every plugin.
        Share your use case with us over at
        https://github.com/transloadit/uppy/issues/
        if you want us to reconsider.`
      throw new Error(msg)
    }

    this.plugins[plugin.type].push(plugin)

    return this
  }

/**
 * Find one Plugin by name
 *
 * @param string name description
 */
  getPlugin (name) {
    let foundPlugin = false
    this.iteratePlugins((plugin) => {
      const pluginName = plugin.id
      if (pluginName === name) {
        foundPlugin = plugin
        return false
      }
    })
    return foundPlugin
  }

/**
 * Iterate through all `use`d plugins
 *
 * @param function method description
 */
  iteratePlugins (method) {
    Object.keys(this.plugins).forEach((pluginType) => {
      this.plugins[pluginType].forEach(method)
    })
  }

/**
 * Logs stuff to console, only if `debug` is set to true. Silent in production.
 *
 * @return {String|Object} to log
 */
  log (msg) {
    if (!this.opts.debug) {
      return
    }
    if (msg === `${msg}`) {
      console.log(`LOG: ${msg}`)
    } else {
      console.dir(msg)
    }
    global.uppyLog = global.uppyLog + '\n' + 'DEBUG LOG: ' + msg
  }

  initSocket (opts) {
    if (!this.socket) {
      this.socket = new UppySocket(opts)
    }

    return this.socket
  }

  installAll () {
    Object.keys(this.plugins).forEach((pluginType) => {
      this.plugins[pluginType].forEach((plugin) => {
        plugin.install()
      })
    })
  }

/**
 * Initializes actions, installs all plugins (by iterating on them and calling `install`), sets options
 *
 * (In the past was used to run a waterfall of runType plugin packs, like so:
 * All preseters(data) --> All acquirers(data) --> All uploaders(data) --> done)
 */
  run () {
    this.log('Core is run, initializing actions, installing plugins...')

    // setInterval(() => {
    //   this.updateAll(this.state)
    // }, 1000)

    this.actions()

    // Forse set `autoProceed` option to false if there are multiple selector Plugins active
    if (this.plugins.acquirer && this.plugins.acquirer.length > 1) {
      this.opts.autoProceed = false
    }

    // Install all plugins
    this.installAll()

    return
  }
}
<|endoftext|>