<commit_before>/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter;
var small_screen = require('../is-small-screen');
var ocreate = require('../object-create');
var whitelist = require('./whitelist');
var regex = require('../regex');
var utils = require('../utils');
var i18n = require('../i18n');
var bind = require('../bind');
var _ = require('underscore');
var xtend = _.extend;
var okeys = _.keys;
var supportedParams = [
  'state',
  'access_token',
  'scope',
  'protocol',
  'device',
  'request_id',
  'connection_scopes',
  'nonce',
  'offline_mode'
];

/**
 * Expose `OptionsManager`
 */

module.exports = OptionsManager;

/**
 * Create an `OptionsManager` from
 * instanceOptions and displayOptions
 *
 * @param {Auth0Lock} widget
 * @param {Object} displayOptions
 * @constructor
 */

function OptionsManager(widget, options) {
  if (!(this instanceof OptionsManager)) {
    return new OptionsManager(widget, options);
  };

  // Initiate `EventEmitter`
  EventEmitter.call(this);

  // Flag OptionsManager as `unloaded`
  this.state('loading');

  // holds widget as reference
  this.$widget = widget;

  // save widget's $client object reference
  this.$client = widget.$client;

  // save copy of widget's valid strategies
  this.$strategies = widget.$strategies;

  // save widget's `auth0-js` instance object
  this.$auth0 = widget.$auth0;

  // Options moved from constructor and hacked into `auth0-js` instance
  this.$auth0._useJSONP = options.forceJSONP || this.$client._useJSONP;
  this.$auth0._callbackURL = options.callbackURL || this.$client._callbackURL;
  this.$auth0._callbackOnLocationHash = !!options.callbackOnLocationHash || this.$client._callbackOnLocationHash;

  // save widget instance options with `$` prepended
  _.each(okeys(widget.$options), function(key) {
    this["$" + key] = widget.$options[key];
  }, this);

  // copies all provided options to instance
  // filtering by allowed options `whitelist`
  _.each(okeys(options), function(key) {
    // we should whitelist options
    // so no OptionsManager method gets overriden
    // by user configurable object
    // XXX: Broken, fix it!
    // if (!~whitelist.indexOf(key) && !~supportedParams.indexOf(key)) return;
    this[key] = options[key];
  }, this);

  // Set `i18n` dictionary for templates
  this.i18n = i18n.getDict(this.dict);

  // Copies the supportedParams from displayOptions
  // to this.extraParameters
  var extra = utils.extract(options, supportedParams);
  this.extraParameters = _.extend({}, extra, options.extraParameters);

  // enable/disable last login and AD discovery
  this.rememberLastLogin = null != options.rememberLastLogin
    ? options.rememberLastLogin
    : true;
  this.kerberosLogin = null != options.kerberosLogin
    ? options.kerberosLogin
    : true;

  // If popupCallback, enable popup mode
  if ('function' === typeof options.popupCallback) {
    this.popup = true;
    this.popupOptions = _.merge({}, options.popupOptions);
    this.popupCallback = options.popupCallback;
  };

  // force `closable` when provided with `container`
  this.closable = !this.container;

  // Delay options requiring $client configuration
  this.$widget.getClientConfiguration(bind(this._onclientloaded, this));
}

/**
 * Inherit from `EventEmitter`
 */

OptionsManager.prototype = ocreate(EventEmitter.prototype);

/**
 * Get or Set `$_state` for track
 * `OptionsManager`s ready state
 *
 * @param {String} state
 * @param {String} message
 * @return {OptionsManager}
 * @public
 */

OptionsManager.prototype.state = function(state, message) {
  if (0 === arguments.length) {
    return this.$_state;
  }

  // debug('state is now %s', state);
  this.$_state = state;
  this.emit(state, message);
  return this;
};

/**
 * Register `fn` for when `OptionsManager`
 * is ready (or `loaded`)
 *
 * @param {Function} fn
 * @return {OptionsManager}
 * @public
 */

OptionsManager.prototype.ready = function(fn) {
  function done() {
    if ('loaded' === this.state()) {
      return fn();
    }
  }

  if ('loaded' === this.state()) {
    setTimeout(bind(done, this), 0);
  } else {
    this.once('loaded', bind(done, this));
  }

  return this;
}

/**
 * Continue setup once client's configuration
 * is retrieved from assets url or S3
 *
 * @param {Object} client
 * @return {OptionsManager}
 * @private
 */

OptionsManager.prototype._onclientloaded = function(client) {

  // Refresh clients configuration with what's stored
  // on the Auth0's hash
  this.$client = _.extend(this.$client, client);

  // Enrich $client.strategies
  // then, continue setting up the mode
  if (this.connections) {
    this.$client.strategies = _.chain(this.$client.strategies)
      .map(strategiesConnectionsMapper(this.connections))
      .filter(hasConnectionsFilter)
      .value();
  }

  // merge strategies info
  for (var i = 0; i < this.$client.strategies.length; i++) {
    var sname = this.$client.strategies[i].name;
    this.$client.strategies[i] = _.extend({}, this.$client.strategies[i], this.$strategies[sname]);
  }

  // Holds auth0 strategies only
  this.auth0Strategies = _.chain(this.$client.strategies)
    .filter(auth0StrategiesFilter)
    .value();

  // show signup/forgot links
  var auth0Conn = this._getAuth0Connection() || {};

  // if booted on `signup` or `reset`, but not configured
  // on connection => override mode with `signin`
  if (this.mode === 'signup' && !auth0Conn.showSignup) this.mode = 'signin';
  if (this.mode === 'reset' && !auth0Conn.showForgot) this.mode = 'signin';

  // Resolve show action buttons or not
  this.showSignupAction = (this.disableSignupAction !== true) && ((auth0Conn && auth0Conn.showSignup) || this.signupLink);
  this.showResetAction = (this.disableResetAction !== true) && ((auth0Conn && auth0Conn.showForgot) || this.resetLink);

  // usernameStyle
  var auth0ConnStrategy = this._getClientStrategyByConnectionName(auth0Conn.name) || {};

  if (!this.usernameStyle && (auth0ConnStrategy.name === 'ad' || auth0ConnStrategy.name === 'auth0-adldap')) {
    this.usernameStyle = 'username';
  }

  this.state('loaded');

  return this;
}

/**
 * Resolve whether are there or not any
 * social connections within client
 * strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnySocialConnection = function () {
  var client = this.$client;
  var filter = { social: true };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * enterprise or Databse connection within
 * client strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyEnterpriseOrDbConnection = function() {
  var client = this.$client;
  var filter = { social: false };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * database connection within client strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyDBConnection = function() {
  var client = this.$client;
  var filter = { userAndPass: true };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * Active Directory connection within client
 * strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyADConnection = function() {
  return _.some(this.$client.strategies, function (s) {
    return (s.name === 'ad' || s.name === 'auth0-adldap') && s.connections.length > 0;
  });
}

/**
 * Resolves wether `email`'s domain belongs to
 * an enterprise connection or not, and alters
 * `output` object in the way...
 *
 * @param {String} email
 * @param {Object} output
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isEnterpriseConnection = function (email, output) {
  var client = this.$client;
  var parser = regex.email_parser;
  var emailM = parser.exec(email.toLowerCase());
  var email_domain = null != emailM ? emailM.slice(-2)[0] : null;
  var sfilter = { userAndPass: undefined };
  var cfilter = { domain: email_domain };

  if (!email_domain) return false;

  var conn = _.chain(client.strategies)
    .where(sfilter)
    .pluck('connections')
    .flatten()
    .findWhere(cfilter)
    .value();

  if (conn && output) {
    output.domain = conn.domain;
  }

  return !!conn;
};

OptionsManager.prototype._isFreeSubscription = function() {
  return this.$client.subscription && !~['free', 'dev'].indexOf(this.$client.subscription)
}

/**
 * Get resolved Auth0 connection to signin by `userName`
 * XXX: No idea what logic this follows...
 *
 * @param {String} userName
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getAuth0Connection = function(username) {

  // if specified, use it, otherwise return first
  if (null != this.userPwdConnectionName) {
    return _.chain(this.auth0Strategies)
      .pluck('connections')
      .flatten()
      .findWhere({ name: this.userPwdConnectionName })
      .value();
  }

  var domain = username && ~username.indexOf('@') ? username.split('@')[1] : '';

  if (username && domain && this.$client.strategies) {
    //there is still a chance that the connection might be
    //adldap and with domain
    var conn = _.chain(this.$client.strategies)
                .pluck('connections')
                .flatten()
                .findWhere({domain: domain})
                .value();
    if (conn) {
      return conn;
    }
  }

  // By default, if exists, return auth0 connection (db-conn) or first
  var defaultStrategy = _.findWhere(this.auth0Strategies, { name: 'auth0' });
  defaultStrategy = defaultStrategy || (this.auth0Strategies.length > 0 ? this.auth0Strategies[0] : null);

  return defaultStrategy && defaultStrategy.connections.length > 0
    ? defaultStrategy.connections[0]
    : null;
}

/**
 * Get Loggedin auth parameters from `strategy` and `ssoData`
 *
 * @param {String} strategy
 * @param {Object} ssoData
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getLoggedInAuthParams = function (strategy, ssoData) {
  switch (strategy) {
    case 'google-oauth2':
      return { login_hint: ssoData.lastUsedUsername };
    default:
      return {};
  }
};

/**
 * Get client strategy by connection `connName`
 * XXX: Check that there may exist 2 connection with same name
 * but at different strategies... in that case this is wrong,
 * and it should also accept a strategy name as second parameter
 *
 * @param {String} connName
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getClientStrategyByConnectionName = function (connName) {
  return _.chain(this.$client.strategies)
    .filter(function (s) {
      return _.findWhere(s.connections, { name: connName });
    }).value()[0];
};

/**
 * Get configured client strategy by strategy `name`
 *
 * @param {String} name
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getClientStrategyByName = function (name) {
  return _.findWhere(this.$client.strategies, { name: name });
};

/**
 * Resolve whether use or don't use big social buttons
 *
 * @return {Boolean}
 * @public
 */

OptionsManager.prototype._useBigSocialButtons = function() {
  return null != this.socialBigButtons
    ?  this.socialBigButtons
    : !this._isThereAnyEnterpriseOrDbConnection();
}

/**
 * Retrieve `$client` strategies by `social: true`
 *
 * @return {Array}
 * @public
 */

OptionsManager.prototype._getSocialStrategies = function() {
  return _.where(this.$client.strategies, { social: true })
}

/**
 * Resolve disable the focus on `is_small_screen`
 * or at embeded mode in `container`. Or override
 * by user preferences
 *
 * @return {Boolean}
 * @public
 */

OptionsManager.prototype._focusDisabled = function() {
  return null != this.focusInput
    ? !this.focusInput
    : small_screen() || !!this.container;
}

/**
 * Private helpers
 */

function auth0StrategiesFilter(strategy) {
  return strategy.userAndPass && strategy.connections.length > 0;
}

function hasConnectionsFilter(strategy) {
  return strategy.connections.length > 0;
}

function strategiesConnectionsMapper(connections) {
  return function (strategy) {
    // XXX: We need to replace with Lodash
    // no deep clone/extend is a pain for referential objects...
    var strategy = _.extend({}, strategy);
    strategy.connections = _.filter(strategy.connections, function (connection) {
      return _.contains(connections, connection.name);
    });
    return strategy;
  }
}
<commit_msg>Fix popupOptions merge<commit_after>/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter;
var small_screen = require('../is-small-screen');
var ocreate = require('../object-create');
var whitelist = require('./whitelist');
var regex = require('../regex');
var utils = require('../utils');
var i18n = require('../i18n');
var bind = require('../bind');
var _ = require('underscore');
var xtend = _.extend;
var okeys = _.keys;
var supportedParams = [
  'state',
  'access_token',
  'scope',
  'protocol',
  'device',
  'request_id',
  'connection_scopes',
  'nonce',
  'offline_mode'
];

/**
 * Expose `OptionsManager`
 */

module.exports = OptionsManager;

/**
 * Create an `OptionsManager` from
 * instanceOptions and displayOptions
 *
 * @param {Auth0Lock} widget
 * @param {Object} displayOptions
 * @constructor
 */

function OptionsManager(widget, options) {
  if (!(this instanceof OptionsManager)) {
    return new OptionsManager(widget, options);
  };

  // Initiate `EventEmitter`
  EventEmitter.call(this);

  // Flag OptionsManager as `unloaded`
  this.state('loading');

  // holds widget as reference
  this.$widget = widget;

  // save widget's $client object reference
  this.$client = widget.$client;

  // save copy of widget's valid strategies
  this.$strategies = widget.$strategies;

  // save widget's `auth0-js` instance object
  this.$auth0 = widget.$auth0;

  // Options moved from constructor and hacked into `auth0-js` instance
  this.$auth0._useJSONP = options.forceJSONP || this.$client._useJSONP;
  this.$auth0._callbackURL = options.callbackURL || this.$client._callbackURL;
  this.$auth0._callbackOnLocationHash = !!options.callbackOnLocationHash || this.$client._callbackOnLocationHash;

  // save widget instance options with `$` prepended
  _.each(okeys(widget.$options), function(key) {
    this["$" + key] = widget.$options[key];
  }, this);

  // copies all provided options to instance
  // filtering by allowed options `whitelist`
  _.each(okeys(options), function(key) {
    // we should whitelist options
    // so no OptionsManager method gets overriden
    // by user configurable object
    // XXX: Broken, fix it!
    // if (!~whitelist.indexOf(key) && !~supportedParams.indexOf(key)) return;
    this[key] = options[key];
  }, this);

  // Set `i18n` dictionary for templates
  this.i18n = i18n.getDict(this.dict);

  // Copies the supportedParams from displayOptions
  // to this.extraParameters
  var extra = utils.extract(options, supportedParams);
  this.extraParameters = _.extend({}, extra, options.extraParameters);

  // enable/disable last login and AD discovery
  this.rememberLastLogin = null != options.rememberLastLogin
    ? options.rememberLastLogin
    : true;
  this.kerberosLogin = null != options.kerberosLogin
    ? options.kerberosLogin
    : true;

  // If popupCallback, enable popup mode
  if ('function' === typeof options.popupCallback) {
    this.popup = true;
    this.popupOptions = _.extend({}, options.popupOptions);
    this.popupCallback = options.popupCallback;
  };

  // force `closable` when provided with `container`
  this.closable = !this.container;

  // Delay options requiring $client configuration
  this.$widget.getClientConfiguration(bind(this._onclientloaded, this));
}

/**
 * Inherit from `EventEmitter`
 */

OptionsManager.prototype = ocreate(EventEmitter.prototype);

/**
 * Get or Set `$_state` for track
 * `OptionsManager`s ready state
 *
 * @param {String} state
 * @param {String} message
 * @return {OptionsManager}
 * @public
 */

OptionsManager.prototype.state = function(state, message) {
  if (0 === arguments.length) {
    return this.$_state;
  }

  // debug('state is now %s', state);
  this.$_state = state;
  this.emit(state, message);
  return this;
};

/**
 * Register `fn` for when `OptionsManager`
 * is ready (or `loaded`)
 *
 * @param {Function} fn
 * @return {OptionsManager}
 * @public
 */

OptionsManager.prototype.ready = function(fn) {
  function done() {
    if ('loaded' === this.state()) {
      return fn();
    }
  }

  if ('loaded' === this.state()) {
    setTimeout(bind(done, this), 0);
  } else {
    this.once('loaded', bind(done, this));
  }

  return this;
}

/**
 * Continue setup once client's configuration
 * is retrieved from assets url or S3
 *
 * @param {Object} client
 * @return {OptionsManager}
 * @private
 */

OptionsManager.prototype._onclientloaded = function(client) {

  // Refresh clients configuration with what's stored
  // on the Auth0's hash
  this.$client = _.extend(this.$client, client);

  // Enrich $client.strategies
  // then, continue setting up the mode
  if (this.connections) {
    this.$client.strategies = _.chain(this.$client.strategies)
      .map(strategiesConnectionsMapper(this.connections))
      .filter(hasConnectionsFilter)
      .value();
  }

  // merge strategies info
  for (var i = 0; i < this.$client.strategies.length; i++) {
    var sname = this.$client.strategies[i].name;
    this.$client.strategies[i] = _.extend({}, this.$client.strategies[i], this.$strategies[sname]);
  }

  // Holds auth0 strategies only
  this.auth0Strategies = _.chain(this.$client.strategies)
    .filter(auth0StrategiesFilter)
    .value();

  // show signup/forgot links
  var auth0Conn = this._getAuth0Connection() || {};

  // if booted on `signup` or `reset`, but not configured
  // on connection => override mode with `signin`
  if (this.mode === 'signup' && !auth0Conn.showSignup) this.mode = 'signin';
  if (this.mode === 'reset' && !auth0Conn.showForgot) this.mode = 'signin';

  // Resolve show action buttons or not
  this.showSignupAction = (this.disableSignupAction !== true) && ((auth0Conn && auth0Conn.showSignup) || this.signupLink);
  this.showResetAction = (this.disableResetAction !== true) && ((auth0Conn && auth0Conn.showForgot) || this.resetLink);

  // usernameStyle
  var auth0ConnStrategy = this._getClientStrategyByConnectionName(auth0Conn.name) || {};

  if (!this.usernameStyle && (auth0ConnStrategy.name === 'ad' || auth0ConnStrategy.name === 'auth0-adldap')) {
    this.usernameStyle = 'username';
  }

  this.state('loaded');

  return this;
}

/**
 * Resolve whether are there or not any
 * social connections within client
 * strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnySocialConnection = function () {
  var client = this.$client;
  var filter = { social: true };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * enterprise or Databse connection within
 * client strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyEnterpriseOrDbConnection = function() {
  var client = this.$client;
  var filter = { social: false };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * database connection within client strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyDBConnection = function() {
  var client = this.$client;
  var filter = { userAndPass: true };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * Active Directory connection within client
 * strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyADConnection = function() {
  return _.some(this.$client.strategies, function (s) {
    return (s.name === 'ad' || s.name === 'auth0-adldap') && s.connections.length > 0;
  });
}

/**
 * Resolves wether `email`'s domain belongs to
 * an enterprise connection or not, and alters
 * `output` object in the way...
 *
 * @param {String} email
 * @param {Object} output
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isEnterpriseConnection = function (email, output) {
  var client = this.$client;
  var parser = regex.email_parser;
  var emailM = parser.exec(email.toLowerCase());
  var email_domain = null != emailM ? emailM.slice(-2)[0] : null;
  var sfilter = { userAndPass: undefined };
  var cfilter = { domain: email_domain };

  if (!email_domain) return false;

  var conn = _.chain(client.strategies)
    .where(sfilter)
    .pluck('connections')
    .flatten()
    .findWhere(cfilter)
    .value();

  if (conn && output) {
    output.domain = conn.domain;
  }

  return !!conn;
};

OptionsManager.prototype._isFreeSubscription = function() {
  return this.$client.subscription && !~['free', 'dev'].indexOf(this.$client.subscription)
}

/**
 * Get resolved Auth0 connection to signin by `userName`
 * XXX: No idea what logic this follows...
 *
 * @param {String} userName
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getAuth0Connection = function(username) {

  // if specified, use it, otherwise return first
  if (null != this.userPwdConnectionName) {
    return _.chain(this.auth0Strategies)
      .pluck('connections')
      .flatten()
      .findWhere({ name: this.userPwdConnectionName })
      .value();
  }

  var domain = username && ~username.indexOf('@') ? username.split('@')[1] : '';

  if (username && domain && this.$client.strategies) {
    //there is still a chance that the connection might be
    //adldap and with domain
    var conn = _.chain(this.$client.strategies)
                .pluck('connections')
                .flatten()
                .findWhere({domain: domain})
                .value();
    if (conn) {
      return conn;
    }
  }

  // By default, if exists, return auth0 connection (db-conn) or first
  var defaultStrategy = _.findWhere(this.auth0Strategies, { name: 'auth0' });
  defaultStrategy = defaultStrategy || (this.auth0Strategies.length > 0 ? this.auth0Strategies[0] : null);

  return defaultStrategy && defaultStrategy.connections.length > 0
    ? defaultStrategy.connections[0]
    : null;
}

/**
 * Get Loggedin auth parameters from `strategy` and `ssoData`
 *
 * @param {String} strategy
 * @param {Object} ssoData
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getLoggedInAuthParams = function (strategy, ssoData) {
  switch (strategy) {
    case 'google-oauth2':
      return { login_hint: ssoData.lastUsedUsername };
    default:
      return {};
  }
};

/**
 * Get client strategy by connection `connName`
 * XXX: Check that there may exist 2 connection with same name
 * but at different strategies... in that case this is wrong,
 * and it should also accept a strategy name as second parameter
 *
 * @param {String} connName
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getClientStrategyByConnectionName = function (connName) {
  return _.chain(this.$client.strategies)
    .filter(function (s) {
      return _.findWhere(s.connections, { name: connName });
    }).value()[0];
};

/**
 * Get configured client strategy by strategy `name`
 *
 * @param {String} name
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getClientStrategyByName = function (name) {
  return _.findWhere(this.$client.strategies, { name: name });
};

/**
 * Resolve whether use or don't use big social buttons
 *
 * @return {Boolean}
 * @public
 */

OptionsManager.prototype._useBigSocialButtons = function() {
  return null != this.socialBigButtons
    ?  this.socialBigButtons
    : !this._isThereAnyEnterpriseOrDbConnection();
}

/**
 * Retrieve `$client` strategies by `social: true`
 *
 * @return {Array}
 * @public
 */

OptionsManager.prototype._getSocialStrategies = function() {
  return _.where(this.$client.strategies, { social: true })
}

/**
 * Resolve disable the focus on `is_small_screen`
 * or at embeded mode in `container`. Or override
 * by user preferences
 *
 * @return {Boolean}
 * @public
 */

OptionsManager.prototype._focusDisabled = function() {
  return null != this.focusInput
    ? !this.focusInput
    : small_screen() || !!this.container;
}

/**
 * Private helpers
 */

function auth0StrategiesFilter(strategy) {
  return strategy.userAndPass && strategy.connections.length > 0;
}

function hasConnectionsFilter(strategy) {
  return strategy.connections.length > 0;
}

function strategiesConnectionsMapper(connections) {
  return function (strategy) {
    // XXX: We need to replace with Lodash
    // no deep clone/extend is a pain for referential objects...
    var strategy = _.extend({}, strategy);
    strategy.connections = _.filter(strategy.connections, function (connection) {
      return _.contains(connections, connection.name);
    });
    return strategy;
  }
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter;
var small_screen = require('../is-small-screen');
var ocreate = require('../object-create');

var regex = require('../regex');
var i18n = require('../i18n');
var bind = require('../bind');
var _ = require('underscore');
var okeys = _.keys;

/**
 * Expose `OptionsManager`
 */

module.exports = OptionsManager;

/**
 * Create an `OptionsManager` from
 * instanceOptions and displayOptions
 *
 * @param {Auth0Lock} widget
 * @param {Object} displayOptions
 * @constructor
 */

function OptionsManager(widget, options) {
  if (!(this instanceof OptionsManager)) {
    return new OptionsManager(widget, options);
  }

  // Initiate `EventEmitter`
  EventEmitter.call(this);

  // Flag OptionsManager as `unloaded`
  this.state('loading');

  // holds widget as reference
  this.$widget = widget;

  // save widget's $client object reference
  this.$client = widget.$client;

  // save copy of widget's valid strategies
  this.$strategies = widget.$strategies;

  // save widget's `auth0-js` instance object
  this.$auth0 = widget.$auth0;

  // save widget instance options with `$` prepended
  _.each(okeys(widget.$options), function(key) {
    this['$' + key] = widget.$options[key];
  }, this);

  // copies all provided options to instance
  // filtering by allowed options `whitelist`
  _.each(okeys(options), function(key) {
    // we should whitelist options
    // so no OptionsManager method gets overriden
    // by user configurable object
    // XXX: Broken, fix it!
    // if (!~whitelist.indexOf(key) && !~supportedParams.indexOf(key)) return;
    this[key] = options[key];
  }, this);

  // Set `i18n` dictionary for templates
  this.i18n = i18n.getDict(this.dict);

  // default authParams to empty object
  this.authParams = _.extend({}, options.authParams);

  // enable/disable last login and AD discovery
  this.rememberLastLogin = null != options.rememberLastLogin ? !!options.rememberLastLogin : true;
  this.integratedWindowsLogin = null != options.integratedWindowsLogin ? !!options.integratedWindowsLogin : true;

  // If popupCallback, enable popup mode
  if ('function' === typeof options.popupCallback) {
    this.popup = true;
    // XXX: the following to should already come with
    // the options from constructor... right?
    this.popupOptions = _.extend({}, options.popupOptions);
    this.popupCallback = options.popupCallback;
  }

  // save default responseType
  if (options.responseType) {
    this.responseType = options.responseType;
  } else {
    this.responseType = (this.popup || !options.callbackURL) ? 'token' : 'code';
  }

  // Options moved from constructor and hacked into `auth0-js` instance
  this.$auth0._useJSONP = options.forceJSONP || this.$auth0._useJSONP;
  this.$auth0._callbackURL = options.callbackURL || this.$auth0._callbackURL;
  this.$auth0._callbackOnLocationHash = 'token' === this.responseType || !!this.$auth0._callbackOnLocationHash;

  // force `closable` when provided with `container`
  this.closable = null != options.closable ? !!options.closable : !this.container;

  // Delay options requiring $client configuration
  this.$widget.getClientConfiguration(bind(this._onclientloaded, this));
}

/**
 * Inherit from `EventEmitter`
 */

OptionsManager.prototype = ocreate(EventEmitter.prototype);

/**
 * Get or Set `$_state` for track
 * `OptionsManager`s ready state
 *
 * @param {String} state
 * @param {String} message
 * @return {OptionsManager}
 * @public
 */

OptionsManager.prototype.state = function(state, message) {
  if (0 === arguments.length) {
    return this.$_state;
  }

  // debug('state is now %s', state);
  this.$_state = state;
  this.emit(state, message);
  return this;
};

/**
 * Register `fn` for when `OptionsManager`
 * is ready (or `loaded`)
 *
 * @param {Function} fn
 * @return {OptionsManager}
 * @public
 */

OptionsManager.prototype.ready = function(fn) {
  function done() {
    if ('loaded' === this.state()) {
      return fn();
    }
  }

  if ('loaded' === this.state()) {
    setTimeout(bind(done, this), 0);
  } else {
    this.once('loaded', bind(done, this));
  }

  return this;
};

/**
 * Continue setup once client's configuration
 * is retrieved from assets url or S3
 *
 * @param {Object} client
 * @return {OptionsManager}
 * @private
 */

OptionsManager.prototype._onclientloaded = function(client) {

  // Refresh clients configuration with what's stored
  // on the Auth0's hash
  this.$client = _.extend(this.$client, client);

  // Enrich $client.strategies
  // then, continue setting up the mode
  if (this.connections) {
    this.$client.strategies = _.chain(this.$client.strategies)
      .map(strategiesConnectionsMapper(this.connections))
      .filter(hasConnectionsFilter)
      .value();
  }

  // merge strategies info
  for (var i = 0; i < this.$client.strategies.length; i++) {
    var sname = this.$client.strategies[i].name;
    this.$client.strategies[i] = _.extend({}, this.$client.strategies[i], this.$strategies[sname]);
  }

  // Holds auth0 strategies only
  this.auth0Strategies = _.chain(this.$client.strategies)
    .filter(auth0StrategiesFilter)
    .value();

  // show signup/forgot links
  var auth0Conn = this._getAuth0Connection() || {};

  // if booted on `signup` or `reset`, but not configured
  // on connection => override mode with `signin`
  if (this.mode === 'signup' && !auth0Conn.showSignup) { this.mode = 'signin'; }
  if (this.mode === 'reset' && !auth0Conn.showForgot) { this.mode = 'signin'; }

  // Resolve show action buttons or not
  this.showSignupAction = (this.disableSignupAction !== true) && ((auth0Conn && auth0Conn.showSignup) || this.signupLink);
  this.showResetAction = (this.disableResetAction !== true) && ((auth0Conn && auth0Conn.showForgot) || this.resetLink);

  // usernameStyle
  var auth0ConnStrategy = this._getClientStrategyByConnectionName(auth0Conn.name) || {};

  if (!this.usernameStyle && (auth0ConnStrategy.name === 'ad' || auth0ConnStrategy.name === 'auth0-adldap')) {
    this.usernameStyle = 'username';
  }

  this.state('loaded');

  return this;
};

/**
 * Resolve whether are there or not any
 * social connections within client
 * strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnySocialConnection = function () {
  var client = this.$client;
  var filter = { social: true };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * enterprise or Databse connection within
 * client strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyEnterpriseOrDbConnection = function() {
  var client = this.$client;
  var filter = { social: false };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * database connection within client strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyDBConnection = function() {
  var client = this.$client;
  var filter = { userAndPass: true };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * Active Directory connection within client
 * strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyADConnection = function() {
  return _.some(this.$client.strategies, function (s) {
    return (s.name === 'ad' || s.name === 'auth0-adldap') && s.connections.length > 0;
  });
};

/**
 * Given an email extracts it domain part.
 *
 * @param {String} email
 *
 * @return {String} domain of the email
 */
OptionsManager.prototype.extractEmailDomain = function (email) {
  var parser = regex.email_parser;
  var emailM = parser.exec(email.toLowerCase());
  return emailM ? emailM.slice(-2)[0] : null;
};

/**
 * Helper to filter AD connections by domain.
 *
 * @private
 */
OptionsManager.prototype._findConnectionByADDomain = function (domain, strategies) {
  return this._filterConnections(domain, strategies, {userAndPass: true});
};

/**
 * Helper to filter enterprise connections by domain.
 *
 * @private
 */
OptionsManager.prototype._findConnectionByDomain = function (domain, strategies) {
  return this._filterConnections(domain, strategies, {userAndPass: undefined});
};

/**
 * Get from the strategies list (filtered by criteria) connections and
 * filter those connections by a given domain.
 *
 * @param   {String}  domain      Domain to be found.
 * @param   {Array}   strategies  Array with all the existing strategies
 *                                for this client.
 * @param   {Object}  criteria    Criteria to filter strategies.
 *
 * @returns {Object} the connection matching the domain or undefined otherwise.
 *
 * @private
 */
OptionsManager.prototype._filterConnections = function (domain, strategies, criteria) {
  strategies = strategies || this.$client.strategies;
  var conn_obj = _.chain(strategies)
    .where(criteria)
    .pluck('connections')
    .flatten()
    .map(function (e) {
      var l = [];
      if (e.domain) {
        l.push(e.domain);
      }
      l = l.concat(e.domain_aliases || []);
      e.domains = l;
      return e;
    })
    .find(function (e) { return e.domains.indexOf(domain) !== -1; })
    .value();

  return conn_obj;
};

/**
 * Resolves wether `email`'s domain belongs to
 * an enterprise connection or not, and alters
 * `output` object in the way...
 *
 * @param {String} email
 * @param {Object} output
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isEnterpriseConnection = function (email, output) {
  var client = this.$client;
  var sfilter = { userAndPass: undefined };
  var email_domain = this.extractEmailDomain(email);
  var cfilter = { domain: email_domain };

  if (!email_domain) { return false; }

  var conn = _.chain(client.strategies)
    .where(sfilter)
    .pluck('connections')
    .flatten()
    .findWhere(cfilter)
    .value();

  if (conn && output) {
    output.domain = conn.domain;
  }

  return !!conn;
};

OptionsManager.prototype._isFreeSubscription = function() {
  return this.$client.subscription && !~['free', 'dev'].indexOf(this.$client.subscription);
};

/**
 * Get resolved Auth0 connection to signin by `userName`
 * XXX: No idea what logic this follows...
 *
 * @param {String} userName
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getAuth0Connection = function(username) {

  // if specified, use it, otherwise return first
  if (null != this.defaultUserPasswordConnection) {
    return _.chain(this.auth0Strategies)
      .pluck('connections')
      .flatten()
      .findWhere({ name: this.defaultUserPasswordConnection })
      .value();
  }

  var domain = username && ~username.indexOf('@') ? username.split('@')[1] : '';

  if (username && domain && this.$client.strategies) {
    //there is still a chance that the connection might be
    //adldap and with domain
    var conn = _.chain(this.$client.strategies)
                .pluck('connections')
                .flatten()
                .findWhere({domain: domain})
                .value();
    if (conn) {
      return conn;
    }
  }

  // By default, if exists, return auth0 connection (db-conn) or first
  var defaultStrategy = _.findWhere(this.auth0Strategies, { name: 'auth0' });
  defaultStrategy = defaultStrategy || (this.auth0Strategies.length > 0 ? this.auth0Strategies[0] : null);

  return defaultStrategy && defaultStrategy.connections.length > 0 ? defaultStrategy.connections[0] : null;
};

/**
 * Get Loggedin auth parameters from `strategy` and `ssoData`
 *
 * @param {String} strategy
 * @param {Object} ssoData
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getLoggedInAuthParams = function (strategy, ssoData) {
  switch (strategy) {
    case 'google-oauth2':
      return { login_hint: ssoData.lastUsedUsername };
    default:
      return {};
  }
};

/**
 * Get client strategy by connection `connName`
 * XXX: Check that there may exist 2 connection with same name
 * but at different strategies... in that case this is wrong,
 * and it should also accept a strategy name as second parameter
 *
 * @param {String} connName
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getClientStrategyByConnectionName = function (connName) {
  return _.chain(this.$client.strategies)
    .filter(function (s) {
      return _.findWhere(s.connections, { name: connName });
    }).value()[0];
};

/**
 * Get configured client strategy by strategy `name`
 *
 * @param {String} name
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getClientStrategyByName = function (name) {
  return _.findWhere(this.$client.strategies, { name: name });
};

/**
 * Resolve whether use or don't use big social buttons
 *
 * @return {Boolean}
 * @public
 */

OptionsManager.prototype._useBigSocialButtons = function() {
  return null != this.socialBigButtons ? !!this.socialBigButtons : !this._isThereAnyEnterpriseOrDbConnection();
};

/**
 * Retrieve `$client` strategies by `social: true`
 *
 * @return {Array}
 * @public
 */

OptionsManager.prototype._getSocialStrategies = function() {
  return _.where(this.$client.strategies, { social: true });
};

/**
 * Resolve disable the focus on `is_small_screen`
 * or at embeded mode in `container`. Or override
 * by user preferences
 *
 * @return {Boolean}
 * @public
 */

OptionsManager.prototype._focusDisabled = function() {
  return null != this.focusInput ? !this.focusInput : (small_screen() || !!this.container);
};

/**
 * Private helpers
 */

function auth0StrategiesFilter(strategy) {
  return strategy.userAndPass && strategy.connections.length > 0;
}

function hasConnectionsFilter(strategy) {
  return strategy.connections.length > 0;
}

function strategiesConnectionsMapper(connections) {
  return function (strategy) {
    // XXX: We need to replace with Lodash
    // no deep clone/extend is a pain for referential objects...
    var cloned = _.extend({}, strategy);
    cloned.connections = _.filter(cloned.connections, function (connection) {
      return _.contains(connections, connection.name);
    });
    return cloned;
  };
}
<commit_msg>Fixing _isEnterpriseConnection<commit_after>/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter;
var small_screen = require('../is-small-screen');
var ocreate = require('../object-create');

var regex = require('../regex');
var i18n = require('../i18n');
var bind = require('../bind');
var _ = require('underscore');
var okeys = _.keys;

/**
 * Expose `OptionsManager`
 */

module.exports = OptionsManager;

/**
 * Create an `OptionsManager` from
 * instanceOptions and displayOptions
 *
 * @param {Auth0Lock} widget
 * @param {Object} displayOptions
 * @constructor
 */

function OptionsManager(widget, options) {
  if (!(this instanceof OptionsManager)) {
    return new OptionsManager(widget, options);
  }

  // Initiate `EventEmitter`
  EventEmitter.call(this);

  // Flag OptionsManager as `unloaded`
  this.state('loading');

  // holds widget as reference
  this.$widget = widget;

  // save widget's $client object reference
  this.$client = widget.$client;

  // save copy of widget's valid strategies
  this.$strategies = widget.$strategies;

  // save widget's `auth0-js` instance object
  this.$auth0 = widget.$auth0;

  // save widget instance options with `$` prepended
  _.each(okeys(widget.$options), function(key) {
    this['$' + key] = widget.$options[key];
  }, this);

  // copies all provided options to instance
  // filtering by allowed options `whitelist`
  _.each(okeys(options), function(key) {
    // we should whitelist options
    // so no OptionsManager method gets overriden
    // by user configurable object
    // XXX: Broken, fix it!
    // if (!~whitelist.indexOf(key) && !~supportedParams.indexOf(key)) return;
    this[key] = options[key];
  }, this);

  // Set `i18n` dictionary for templates
  this.i18n = i18n.getDict(this.dict);

  // default authParams to empty object
  this.authParams = _.extend({}, options.authParams);

  // enable/disable last login and AD discovery
  this.rememberLastLogin = null != options.rememberLastLogin ? !!options.rememberLastLogin : true;
  this.integratedWindowsLogin = null != options.integratedWindowsLogin ? !!options.integratedWindowsLogin : true;

  // If popupCallback, enable popup mode
  if ('function' === typeof options.popupCallback) {
    this.popup = true;
    // XXX: the following to should already come with
    // the options from constructor... right?
    this.popupOptions = _.extend({}, options.popupOptions);
    this.popupCallback = options.popupCallback;
  }

  // save default responseType
  if (options.responseType) {
    this.responseType = options.responseType;
  } else {
    this.responseType = (this.popup || !options.callbackURL) ? 'token' : 'code';
  }

  // Options moved from constructor and hacked into `auth0-js` instance
  this.$auth0._useJSONP = options.forceJSONP || this.$auth0._useJSONP;
  this.$auth0._callbackURL = options.callbackURL || this.$auth0._callbackURL;
  this.$auth0._callbackOnLocationHash = 'token' === this.responseType || !!this.$auth0._callbackOnLocationHash;

  // force `closable` when provided with `container`
  this.closable = null != options.closable ? !!options.closable : !this.container;

  // Delay options requiring $client configuration
  this.$widget.getClientConfiguration(bind(this._onclientloaded, this));
}

/**
 * Inherit from `EventEmitter`
 */

OptionsManager.prototype = ocreate(EventEmitter.prototype);

/**
 * Get or Set `$_state` for track
 * `OptionsManager`s ready state
 *
 * @param {String} state
 * @param {String} message
 * @return {OptionsManager}
 * @public
 */

OptionsManager.prototype.state = function(state, message) {
  if (0 === arguments.length) {
    return this.$_state;
  }

  // debug('state is now %s', state);
  this.$_state = state;
  this.emit(state, message);
  return this;
};

/**
 * Register `fn` for when `OptionsManager`
 * is ready (or `loaded`)
 *
 * @param {Function} fn
 * @return {OptionsManager}
 * @public
 */

OptionsManager.prototype.ready = function(fn) {
  function done() {
    if ('loaded' === this.state()) {
      return fn();
    }
  }

  if ('loaded' === this.state()) {
    setTimeout(bind(done, this), 0);
  } else {
    this.once('loaded', bind(done, this));
  }

  return this;
};

/**
 * Continue setup once client's configuration
 * is retrieved from assets url or S3
 *
 * @param {Object} client
 * @return {OptionsManager}
 * @private
 */

OptionsManager.prototype._onclientloaded = function(client) {

  // Refresh clients configuration with what's stored
  // on the Auth0's hash
  this.$client = _.extend(this.$client, client);

  // Enrich $client.strategies
  // then, continue setting up the mode
  if (this.connections) {
    this.$client.strategies = _.chain(this.$client.strategies)
      .map(strategiesConnectionsMapper(this.connections))
      .filter(hasConnectionsFilter)
      .value();
  }

  // merge strategies info
  for (var i = 0; i < this.$client.strategies.length; i++) {
    var sname = this.$client.strategies[i].name;
    this.$client.strategies[i] = _.extend({}, this.$client.strategies[i], this.$strategies[sname]);
  }

  // Holds auth0 strategies only
  this.auth0Strategies = _.chain(this.$client.strategies)
    .filter(auth0StrategiesFilter)
    .value();

  // show signup/forgot links
  var auth0Conn = this._getAuth0Connection() || {};

  // if booted on `signup` or `reset`, but not configured
  // on connection => override mode with `signin`
  if (this.mode === 'signup' && !auth0Conn.showSignup) { this.mode = 'signin'; }
  if (this.mode === 'reset' && !auth0Conn.showForgot) { this.mode = 'signin'; }

  // Resolve show action buttons or not
  this.showSignupAction = (this.disableSignupAction !== true) && ((auth0Conn && auth0Conn.showSignup) || this.signupLink);
  this.showResetAction = (this.disableResetAction !== true) && ((auth0Conn && auth0Conn.showForgot) || this.resetLink);

  // usernameStyle
  var auth0ConnStrategy = this._getClientStrategyByConnectionName(auth0Conn.name) || {};

  if (!this.usernameStyle && (auth0ConnStrategy.name === 'ad' || auth0ConnStrategy.name === 'auth0-adldap')) {
    this.usernameStyle = 'username';
  }

  this.state('loaded');

  return this;
};

/**
 * Resolve whether are there or not any
 * social connections within client
 * strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnySocialConnection = function () {
  var client = this.$client;
  var filter = { social: true };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * enterprise or Databse connection within
 * client strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyEnterpriseOrDbConnection = function() {
  var client = this.$client;
  var filter = { social: false };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * database connection within client strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyDBConnection = function() {
  var client = this.$client;
  var filter = { userAndPass: true };
  return !!_.findWhere(client.strategies, filter);
};

/**
 * Resolve whether are there or not any
 * Active Directory connection within client
 * strategies
 *
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isThereAnyADConnection = function() {
  return _.some(this.$client.strategies, function (s) {
    return (s.name === 'ad' || s.name === 'auth0-adldap') && s.connections.length > 0;
  });
};

/**
 * Given an email extracts it domain part.
 *
 * @param {String} email
 *
 * @return {String} domain of the email
 */
OptionsManager.prototype.extractEmailDomain = function (email) {
  var parser = regex.email_parser;
  var emailM = parser.exec(email.toLowerCase());
  return emailM ? emailM.slice(-2)[0] : null;
};

/**
 * Helper to filter AD connections by domain.
 *
 * @private
 */
OptionsManager.prototype._findConnectionByADDomain = function (domain, strategies) {
  return this._filterConnections(domain, strategies, {userAndPass: true});
};

/**
 * Helper to filter enterprise connections by domain.
 *
 * @private
 */
OptionsManager.prototype._findConnectionByDomain = function (domain, strategies) {
  return this._filterConnections(domain, strategies, {userAndPass: undefined});
};

/**
 * Get from the strategies list (filtered by criteria) connections and
 * filter those connections by a given domain.
 *
 * @param   {String}  domain      Domain to be found.
 * @param   {Array}   strategies  Array with all the existing strategies
 *                                for this client.
 * @param   {Object}  criteria    Criteria to filter strategies.
 *
 * @returns {Object} the connection matching the domain or undefined otherwise.
 *
 * @private
 */
OptionsManager.prototype._filterConnections = function (domain, strategies, criteria) {
  strategies = strategies || this.$client.strategies;
  var conn_obj = _.chain(strategies)
    .where(criteria)
    .pluck('connections')
    .flatten()
    .map(function (e) {
      var l = [];
      if (e.domain) {
        l.push(e.domain);
      }
      l = l.concat(e.domain_aliases || []);
      e.domains = l;
      return e;
    })
    .find(function (e) { return e.domains.indexOf(domain) !== -1; })
    .value();

  return conn_obj;
};

/**
 * Resolves wether `email`'s domain belongs to
 * an enterprise connection or not, and alters
 * `output` object in the way...
 *
 * @param {String} email
 * @param {Object} output
 * @return {Boolean}
 * @private
 */

OptionsManager.prototype._isEnterpriseConnection = function (email, output) {
  var client = this.$client;
  var domain = this.extractEmailDomain(email);

  var conn = this._filterConnections(domain, client.strategies, {userAndPass: undefined});

  if (conn && output) {
    output.domain = conn.domain;
  }

  return !!conn;
};

OptionsManager.prototype._isFreeSubscription = function() {
  return this.$client.subscription && !~['free', 'dev'].indexOf(this.$client.subscription);
};

/**
 * Get resolved Auth0 connection to signin by `userName`
 * XXX: No idea what logic this follows...
 *
 * @param {String} userName
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getAuth0Connection = function(username) {

  // if specified, use it, otherwise return first
  if (null != this.defaultUserPasswordConnection) {
    return _.chain(this.auth0Strategies)
      .pluck('connections')
      .flatten()
      .findWhere({ name: this.defaultUserPasswordConnection })
      .value();
  }

  var domain = username && ~username.indexOf('@') ? username.split('@')[1] : '';

  if (username && domain && this.$client.strategies) {
    //there is still a chance that the connection might be
    //adldap and with domain
    var conn = _.chain(this.$client.strategies)
                .pluck('connections')
                .flatten()
                .findWhere({domain: domain})
                .value();
    if (conn) {
      return conn;
    }
  }

  // By default, if exists, return auth0 connection (db-conn) or first
  var defaultStrategy = _.findWhere(this.auth0Strategies, { name: 'auth0' });
  defaultStrategy = defaultStrategy || (this.auth0Strategies.length > 0 ? this.auth0Strategies[0] : null);

  return defaultStrategy && defaultStrategy.connections.length > 0 ? defaultStrategy.connections[0] : null;
};

/**
 * Get Loggedin auth parameters from `strategy` and `ssoData`
 *
 * @param {String} strategy
 * @param {Object} ssoData
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getLoggedInAuthParams = function (strategy, ssoData) {
  switch (strategy) {
    case 'google-oauth2':
      return { login_hint: ssoData.lastUsedUsername };
    default:
      return {};
  }
};

/**
 * Get client strategy by connection `connName`
 * XXX: Check that there may exist 2 connection with same name
 * but at different strategies... in that case this is wrong,
 * and it should also accept a strategy name as second parameter
 *
 * @param {String} connName
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getClientStrategyByConnectionName = function (connName) {
  return _.chain(this.$client.strategies)
    .filter(function (s) {
      return _.findWhere(s.connections, { name: connName });
    }).value()[0];
};

/**
 * Get configured client strategy by strategy `name`
 *
 * @param {String} name
 * @return {Object}
 * @private
 */

OptionsManager.prototype._getClientStrategyByName = function (name) {
  return _.findWhere(this.$client.strategies, { name: name });
};

/**
 * Resolve whether use or don't use big social buttons
 *
 * @return {Boolean}
 * @public
 */

OptionsManager.prototype._useBigSocialButtons = function() {
  return null != this.socialBigButtons ? !!this.socialBigButtons : !this._isThereAnyEnterpriseOrDbConnection();
};

/**
 * Retrieve `$client` strategies by `social: true`
 *
 * @return {Array}
 * @public
 */

OptionsManager.prototype._getSocialStrategies = function() {
  return _.where(this.$client.strategies, { social: true });
};

/**
 * Resolve disable the focus on `is_small_screen`
 * or at embeded mode in `container`. Or override
 * by user preferences
 *
 * @return {Boolean}
 * @public
 */

OptionsManager.prototype._focusDisabled = function() {
  return null != this.focusInput ? !this.focusInput : (small_screen() || !!this.container);
};

/**
 * Private helpers
 */

function auth0StrategiesFilter(strategy) {
  return strategy.userAndPass && strategy.connections.length > 0;
}

function hasConnectionsFilter(strategy) {
  return strategy.connections.length > 0;
}

function strategiesConnectionsMapper(connections) {
  return function (strategy) {
    // XXX: We need to replace with Lodash
    // no deep clone/extend is a pain for referential objects...
    var cloned = _.extend({}, strategy);
    cloned.connections = _.filter(cloned.connections, function (connection) {
      return _.contains(connections, connection.name);
    });
    return cloned;
  };
}
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const UglifyJS = require('uglify-js');

function init({
    processorConfig,
    processorCommonConfig,
    commonConfig,
}) {
  const config = Object.assign({}, commonConfig, processorCommonConfig, processorConfig);

  function read(basePath, { readers }) {
    const scriptPaths = readers.dir(`${basePath}`);
    const scripts = scriptPaths.map((scriptPath) => {
      return {
        id: path.basename(scriptPath),
        name: path.basename(scriptPath),
        isBundle: false,
        contents: readers.fileToString(scriptPath),
      };
    });
    const bundles = config.bundles ? config.bundles.map((bundle) => {
      return {
        id: bundle.name,
        name: bundle.name,
        isBundle: true,
        files: bundle.files,
      };
    }) : [];
    return [...scripts, ...bundles];
  }

// script has name, path
  function preprocess(scripts) {
    return scripts.map((script) => {
      const next = Object.assign({}, script);
      next.writePath = `scripts/${script.name}`;
      return next;
    });
  }

// script has name, path, writePath
  function templateVariables(scripts) {
    return scripts.map((script) => {
      const next = Object.assign({}, script);
      next.url = `/scripts/${script.name}`;
      return next;
    });
  }

// script has name, path, writePath
  function postprocess(scripts) {
    return scripts.map((script) => {
      if (script.isBundle) {
        script.contents = script.files.reduce((s, fileName) => {
          return s + scripts.object[fileName].contents;
        }, '');
      }
      if (config.productionMode) {
        config.minify = config.minify || {};
        config.minify.fromString = true;
        script.contents = UglifyJS.minify(script.contents, config.minify).code;
      }
      return script;
    });
  }

// script has name, path, writePath
  function write(scripts, { writers }) {
    scripts.forEach((script) => {
      writers.file(`${config.outputDir}/${script.writePath}`, script.contents);
    });
  }

  return {
    read,
    preprocess,
    templateVariables,
    postprocess,
    write,
  };
}

const type = 'asset';
const name = 'scripts';
function registration(register) {
  register(type, name, init);
}

module.exports = registration;
<commit_msg>Implement cache busting for scripts processor<commit_after>'use strict';

const path = require('path');
const UglifyJS = require('uglify-js');

function init({
    processorConfig,
    processorCommonConfig,
    commonConfig,
}) {
  const config = Object.assign({}, commonConfig, processorCommonConfig, processorConfig);

  function read(basePath, { readers }) {
    const scriptPaths = readers.dir(`${basePath}`);
    const scripts = scriptPaths.map((scriptPath) => {
      return {
        id: path.basename(scriptPath),
        name: path.basename(scriptPath),
        isBundle: false,
        contents: readers.fileToString(scriptPath),
      };
    });
    const bundles = config.bundles ? config.bundles.map((bundle) => {
      return {
        id: bundle.name,
        name: bundle.name,
        isBundle: true,
        files: bundle.files,
      };
    }) : [];
    return [...scripts, ...bundles];
  }

// script has name, path
  function preprocess(scripts, helpers) {
    return scripts.map((script) => {
      if (script.isBundle) {
        script.contents = script.files.reduce((s, fileName) => {
          return s + scripts.object[fileName].contents;
        }, '');
      }

      const next = Object.assign({}, script);

      if (config.cacheBust) {
        const fingerprintOpts = { env: config.productionMode ? 'production' : 'dev' };
        const filename = helpers.fingerprint(script.name, script.contents, fingerprintOpts).file;
        next.writePath = `scripts/${path.basename(script.name, '.js')}-${filename}`;
      } else {
        next.writePath = `scripts/${script.name}`;
      }
      return next;
    });
  }

// script has name, path, writePath
  function templateVariables(scripts) {
    return scripts.map((script) => {
      const next = Object.assign({}, script);
      next.url = '/' + script.writePath;
      return next;
    });
  }

// script has name, path, writePath
  function postprocess(scripts) {
    return scripts.map((script) => {
      if (config.productionMode) {
        config.minify = config.minify || {};
        config.minify.fromString = true;
        script.contents = UglifyJS.minify(script.contents, config.minify).code;
      }
      return script;
    });
  }

// script has name, path, writePath
  function write(scripts, { writers }) {
    scripts.forEach((script) => {
      writers.file(`${config.outputDir}/${script.writePath}`, script.contents);
    });
  }

  return {
    read,
    preprocess,
    templateVariables,
    postprocess,
    write,
  };
}

const type = 'asset';
const name = 'scripts';
function registration(register) {
  register(type, name, init);
}

module.exports = registration;
<|endoftext|>


<commit_before>'use strict';

const pug = require('pug'),
  fs = require('fs'),
  Promise = require('bluebird'),
  path = require('path');

Promise.promisifyAll(fs);

const basePath = path.resolve(__dirname, 'templates');

const templateCache = {};

function getTemplate(templateName) {
  if (!templateName.endsWith('.pug'))
    templateName = templateName + '.pug';

  const template = templateCache[templateName];

  if (template) {
    return Promise.resolve(template);
  }

  const filename = path.resolve(basePath, templateName);
  return fs.readFileAsync(filename, 'utf-8')
    .then((source) => {
      const renderedTemplate = pug.compile(source, {'filename': filename});
      templateCache[templateName] = renderedTemplate;
      return renderedTemplate;
    });
}

module.exports = {
  renderTemplate(templateName, locals) {
    return getTemplate(templateName)
      .then((template) => template(locals));
  }
};
<commit_msg>Process pug synchronously for better memory management<commit_after>'use strict';

const pug = require('pug'),
  path = require('path');

const basePath = path.resolve(__dirname, 'templates');

const templateCache = {};

function getTemplate(templateName) {
  if (!templateName.endsWith('.pug'))
    templateName = templateName + '.pug';

  const template = templateCache[templateName];
  if (template) {
    return template;
  }

  const filename = path.resolve(basePath, templateName);
  const renderedTemplate = pug.compileFile(filename);

  templateCache[templateName] = renderedTemplate;
  return renderedTemplate;
}

module.exports = {
  renderTemplate(templateName, locals) {
    return getTemplate(templateName)(locals);
  }
};
<|endoftext|>


<commit_before>/*global jQuery *//*!
* FitText.js 1.1
*
* Copyright 2011, Dave Rupert http://daverupert.com
* Released under the WTFPL license
* http://sam.zoy.org/wtfpl/
*
* Date: Thu May 05 14:23:00 2011 -0600
*/(function(e){e.fn.fitText=function(t,n){var r=t||1,i=e.extend({minFontSize:Number.NEGATIVE_INFINITY,maxFontSize:Number.POSITIVE_INFINITY},n);return this.each(function(){var t=e(this),n=function(){t.css("font-size",Math.max(Math.min(t.width()/(r*10),parseFloat(i.maxFontSize)),parseFloat(i.minFontSize)))};n(),e(window).on("resize",n)})}})(jQuery);

/*! jQuery slabtext plugin v2.3.1 MIT/GPL2 @freqdec */
(function($){$.fn.slabText=function(options){var settings={fontRatio:0.78,forceNewCharCount:true,wrapAmpersand:true,headerBreakpoint:null,viewportBreakpoint:null,noResizeEvent:false,resizeThrottleTime:300,maxFontSize:999,postTweak:true,precision:3,minCharsPerLine:0};$("body").addClass("slabtexted");return this.each(function(){if(options){$.extend(settings,options)}var $this=$(this),keepSpans=$("span.slabtext",$this).length,words=keepSpans?[]:String($.trim($this.text())).replace(/\s{2,}/g," ").split(" "),origFontSize=null,idealCharPerLine=null,fontRatio=settings.fontRatio,forceNewCharCount=settings.forceNewCharCount,headerBreakpoint=settings.headerBreakpoint,viewportBreakpoint=settings.viewportBreakpoint,postTweak=settings.postTweak,precision=settings.precision,resizeThrottleTime=settings.resizeThrottleTime,minCharsPerLine=settings.minCharsPerLine,resizeThrottle=null,viewportWidth=$(window).width(),headLink=$this.find("a:first").attr("href")||$this.attr("href"),linkTitle=headLink?$this.find("a:first").attr("title"):"";if(!keepSpans&&minCharsPerLine&&words.join(" ").length<minCharsPerLine){return}var grabPixelFontSize=function(){var dummy=jQuery('

').appendTo($this),emH=dummy.height();dummy.remove();return emH};var resizeSlabs=function resizeSlabs(){var parentWidth=$this.width(),fs;if(parentWidth===0){return}$this.removeClass("slabtextdone slabtextinactive");if(viewportBreakpoint&&viewportBreakpoint>viewportWidth||headerBreakpoint&&headerBreakpoint>parentWidth){$this.addClass("slabtextinactive");return}fs=grabPixelFontSize();if(!keepSpans&&(forceNewCharCount||fs!=origFontSize)){origFontSize=fs;var newCharPerLine=Math.min(60,Math.floor(parentWidth/(origFontSize*fontRatio))),wordIndex=0,lineText=[],counter=0,preText="",postText="",finalText="",slice,preDiff,postDiff;if(newCharPerLine!=0&&newCharPerLine!=idealCharPerLine){idealCharPerLine=newCharPerLine;while(wordIndex<words.length){postText="";while(postText.length<idealCharPerLine){preText=postText;postText+=words[wordIndex]+" ";if(++wordIndex>=words.length){break}}if(minCharsPerLine){slice=words.slice(wordIndex).join(" ");if(slice.length<minCharsPerLine){postText+=slice;preText=postText;wordIndex=words.length+2}}preDiff=idealCharPerLine-preText.length;postDiff=postText.length-idealCharPerLine;if((preDiff<postDiff)&&(preText.length>=(minCharsPerLine||2))){finalText=preText;wordIndex--}else{finalText=postText}finalText=$("
").text(finalText).html();if(settings.wrapAmpersand){finalText=finalText.replace(/&/g,'&')}finalText=$.trim(finalText);lineText.push(''+finalText+"")}$this.html(lineText.join(" "));if(headLink){$this.wrapInner('")}}}else{origFontSize=fs}$("span.slabtext",$this).each(function(){var $span=$(this),innerText=$span.text(),wordSpacing=innerText.split(" ").length>1,diff,ratio,fontSize;if(postTweak){$span.css({"word-spacing":0,"letter-spacing":0})}ratio=parentWidth/$span.width();fontSize=parseFloat(this.style.fontSize)||origFontSize;$span.css("font-size",Math.min((fontSize*ratio).toFixed(precision),settings.maxFontSize)+"px");diff=!!postTweak?parentWidth-$span.width():false;if(diff){$span.css((wordSpacing?"word":"letter")+"-spacing",(diff/(wordSpacing?innerText.split(" ").length-1:innerText.length)).toFixed(precision)+"px")}});$this.addClass("slabtextdone")};resizeSlabs();if(!settings.noResizeEvent){$(window).resize(function(){if($(window).width()==viewportWidth){return}viewportWidth=$(window).width();clearTimeout(resizeThrottle);resizeThrottle=setTimeout(resizeSlabs,resizeThrottleTime)})}})}})(jQuery);

/* BIG Text */
!function(e,t){"use strict";function i(e,t,i,n,s,r,o){var a;if(o="number"==typeof o?o:0,e.css(i,n+r),a=e.width(),a>=t){if(e.css(i,""),a===t)return{match:"exact",size:parseFloat((parseFloat(n)-.1).toFixed(3))};var c=t-o,l=a-t;return{match:"estimate",size:parseFloat((parseFloat(n)-("word-spacing"===i&&o&&c>l?0:s)).toFixed(3))}}return a}function n(e,n,s,r,o){var a=e.clone(!0).addClass("bigtext-cloned").css({fontFamily:e.css("font-family"),textTransform:e.css("text-transform"),wordSpacing:e.css("word-spacing"),letterSpacing:e.css("letter-spacing"),position:"absolute",left:c.DEBUG_MODE?0:-9999,top:c.DEBUG_MODE?0:-9999}).appendTo(document.body),l=[],u=[],p=[],f=[];return n.css("float","left").each(function(){var e,n,a=t(this),u=c.supports.wholeNumberFontSizeOnly?[8,4,1]:[8,4,1,.1];if(a.hasClass(c.EXEMPT_CLASS))return l.push(null),f.push(null),void p.push(!1);var d=32,g=parseFloat(a.css("font-size")),S=(a.width()/g).toFixed(6);n=parseInt(s/S,10)-d;e:for(var h=0,_=u.length;_>h;h++)t:for(var E=1,x=10;x>=E;E++){if(n+E*u[h]>r){n=r;break e}if(e=i(a,s,"font-size",n+E*u[h],u[h],"px",e),"number"!=typeof e){if(n=e.size,"exact"===e.match)break e;break t}}f.push(s/n),n>r?(l.push(r),p.push(!1)):o&&o>n?(l.push(o),p.push(!0)):(l.push(n),p.push(!1))}).each(function(e){var n,r=t(this),o=0,a=1;if(r.hasClass(c.EXEMPT_CLASS))return void u.push(null);r.css("font-size",l[e]+"px");for(var p=1,f=3;f>p;p+=a)if(n=i(r,s,"word-spacing",p,a,"px",n),"number"!=typeof n){o=n.size;break}r.css("font-size",""),u.push(o)}).removeAttr("style"),c.DEBUG_MODE?a.css({"background-color":"rgba(255,255,255,.4)"}):a.remove(),{fontSizes:l,wordSpacings:u,ratios:f,minFontSizes:p}}var s=0,r=t("head"),o=e.BigText,a=t.fn.bigtext,c={DEBUG_MODE:!1,DEFAULT_MIN_FONT_SIZE_PX:null,DEFAULT_MAX_FONT_SIZE_PX:528,GLOBAL_STYLE_ID:"bigtext-style",STYLE_ID:"bigtext-id",LINE_CLASS_PREFIX:"bigtext-line",EXEMPT_CLASS:"bigtext-exempt",noConflict:function(i){return i&&(t.fn.bigtext=a,e.BigText=o),c},supports:{wholeNumberFontSizeOnly:function(){if(!("getComputedStyle"in e))return!0;var i=t("
").css({position:"absolute","font-size":"14.1px"}).insertBefore(t("script").eq(0)),n=e.getComputedStyle(i[0],null),s=n&&"14px"===n.getPropertyValue("font-size");return i.remove(),s}()},init:function(){t("#"+c.GLOBAL_STYLE_ID).length||r.append(c.generateStyleTag(c.GLOBAL_STYLE_ID,[".bigtext * { white-space: nowrap; } .bigtext > * { display: block; }",".bigtext ."+c.EXEMPT_CLASS+", .bigtext ."+c.EXEMPT_CLASS+" * { white-space: normal; }"]))},bindResize:function(i,n){var s;t(e).unbind(i).bind(i,function(){s&&clearTimeout(s),s=setTimeout(n,100)})},getStyleId:function(e){return c.STYLE_ID+"-"+e},generateStyleTag:function(e,i){return t("").attr("id",e)},clearCss:function(e){var i=c.getStyleId(e);t("#"+i).remove()},generateCss:function(e,t,i,n){var s=[];c.clearCss(e);for(var r=0,o=t.length;o>r;r++)s.push("#"+e+" ."+c.LINE_CLASS_PREFIX+r+" {"+(n[r]?" white-space: normal;":"")+(t[r]?" font-size: "+t[r]+"px;":"")+(i[r]?" word-spacing: "+i[r]+"px;":"")+"}");return c.generateStyleTag(c.getStyleId(e),s)},jQueryMethod:function(e){return c.init(),e=t.extend({minfontsize:c.DEFAULT_MIN_FONT_SIZE_PX,maxfontsize:c.DEFAULT_MAX_FONT_SIZE_PX,childSelector:"",resize:!0},e||{}),this.each(function(){var i=t(this).addClass("bigtext"),o=i.width(),a=i.attr("id"),l=e.childSelector?i.find(e.childSelector):i.children();a||(a="bigtext-id"+s++,i.attr("id",a)),e.resize&&c.bindResize("resize.bigtext-event-"+a,function(){c.jQueryMethod.call(t("#"+a),e)}),c.clearCss(a),l.addClass(function(e,t){return[t.replace(new RegExp("\\b"+c.LINE_CLASS_PREFIX+"\\d+\\b"),""),c.LINE_CLASS_PREFIX+e].join(" ")});var u=n(i,l,o,e.maxfontsize,e.minfontsize);r.append(c.generateCss(a,u.fontSizes,u.wordSpacings,u.minFontSizes))}),this.trigger("bigtext:complete")}};t.fn.bigtext=c.jQueryMethod,e.BigText=c}(this,jQuery);

/*
 * Get Viewport Dimensions
 * returns object with viewport dimensions to match css in width and height properties
 * ( source: http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript )
*/
function updateViewportDimensions() {
	var w=window,d=document,e=d.documentElement,g=d.getElementsByTagName('body')[0],x=w.innerWidth||e.clientWidth||g.clientWidth,y=w.innerHeight||e.clientHeight||g.clientHeight;
	return { width:x,height:y };
}
// setting the viewport width
var viewport = updateViewportDimensions();

/*
 * Throttle Resize-triggered Events
 * Wrap your actions in this function to throttle the frequency of firing them off, for better performance, esp. on mobile.
 * ( source: http://stackoverflow.com/questions/2854407/javascript-jquery-window-resize-how-to-fire-after-the-resize-is-completed )
*/
var waitForFinalEvent = (function () {
	var timers = {};
	return function (callback, ms, uniqueId) {
		if (!uniqueId) { uniqueId = "Don't call this twice without a uniqueId"; }
		if (timers[uniqueId]) { clearTimeout (timers[uniqueId]); }
		timers[uniqueId] = setTimeout(callback, ms);
	};
})();

// how long to wait before deciding the resize has stopped, in ms. Around 50-100 should work ok.
var timeToWaitForLast = 100;

// function loadGravatars() {
//   // set the viewport using the function above
//   viewport = updateViewportDimensions();
//   // if the viewport is tablet or larger, we load in the gravatars
//   if (viewport.width >= 768) {
//   jQuery('.comment img[data-gravatar]').each(function(){
//     jQuery(this).attr('src',jQuery(this).attr('data-gravatar'));
//   });
// 	}
// }

/*
 * Put all your regular jQuery in here.
*/
jQuery(document).ready(function($) {

	// Allows us to use a class to create DIV TEXT FILLING GOODNESS.
	$(".bigtext").bigtext();

	// FILTERS
	// ToDo: We will want to make an Ajax call in future to grab all related taxonomy terms to the particular filter.
	// TALKS
	var $btns = $('.btn').click(function() {
	  if (this.id == 'all') {
	    $('#parent > div').fadeIn(450);
	    console.log('all Fade In');
	  } else {
	    var $el = $('.' + this.id).fadeIn(450);
	    $('#parent > div').not($el).hide();
	    console.log('show ' + this.id);
	  }
	  $btns.removeClass('active');
	  $(this).addClass('active');
	});

 	//  var clicked = false;
	// $('.sidebar li a').click(
	// function(){
	// 	//$('#mycontent > div > h2').css('padding-top',0);

	// 	// I want to kill this next line. It adds that padding, but can be done a better way so when you scroll to the top it doesn't look like shit.
	// 	// https://www.dropbox.com/s/6ilpfzro6xej5b4/Screenshot%202015-08-14%2016.41.40.png?dl=0

	// 	$( $( this ).attr('href') ).css('padding-top','50px');
	// 	clicked = true;
	// 	}
	// );

	$('#menu_affix').affix({
		offset: {
			bottom: function () {
			return (this.bottom = $('#footer').outerHeight(true))
		}
	}
})

});<commit_msg>no message<commit_after>/*global jQuery *//*!
* FitText.js 1.1
*
* Copyright 2011, Dave Rupert http://daverupert.com
* Released under the WTFPL license
* http://sam.zoy.org/wtfpl/
*
* Date: Thu May 05 14:23:00 2011 -0600
*/(function(e){e.fn.fitText=function(t,n){var r=t||1,i=e.extend({minFontSize:Number.NEGATIVE_INFINITY,maxFontSize:Number.POSITIVE_INFINITY},n);return this.each(function(){var t=e(this),n=function(){t.css("font-size",Math.max(Math.min(t.width()/(r*10),parseFloat(i.maxFontSize)),parseFloat(i.minFontSize)))};n(),e(window).on("resize",n)})}})(jQuery);

/*! jQuery slabtext plugin v2.3.1 MIT/GPL2 @freqdec */
(function($){$.fn.slabText=function(options){var settings={fontRatio:0.78,forceNewCharCount:true,wrapAmpersand:true,headerBreakpoint:null,viewportBreakpoint:null,noResizeEvent:false,resizeThrottleTime:300,maxFontSize:999,postTweak:true,precision:3,minCharsPerLine:0};$("body").addClass("slabtexted");return this.each(function(){if(options){$.extend(settings,options)}var $this=$(this),keepSpans=$("span.slabtext",$this).length,words=keepSpans?[]:String($.trim($this.text())).replace(/\s{2,}/g," ").split(" "),origFontSize=null,idealCharPerLine=null,fontRatio=settings.fontRatio,forceNewCharCount=settings.forceNewCharCount,headerBreakpoint=settings.headerBreakpoint,viewportBreakpoint=settings.viewportBreakpoint,postTweak=settings.postTweak,precision=settings.precision,resizeThrottleTime=settings.resizeThrottleTime,minCharsPerLine=settings.minCharsPerLine,resizeThrottle=null,viewportWidth=$(window).width(),headLink=$this.find("a:first").attr("href")||$this.attr("href"),linkTitle=headLink?$this.find("a:first").attr("title"):"";if(!keepSpans&&minCharsPerLine&&words.join(" ").length<minCharsPerLine){return}var grabPixelFontSize=function(){var dummy=jQuery('

').appendTo($this),emH=dummy.height();dummy.remove();return emH};var resizeSlabs=function resizeSlabs(){var parentWidth=$this.width(),fs;if(parentWidth===0){return}$this.removeClass("slabtextdone slabtextinactive");if(viewportBreakpoint&&viewportBreakpoint>viewportWidth||headerBreakpoint&&headerBreakpoint>parentWidth){$this.addClass("slabtextinactive");return}fs=grabPixelFontSize();if(!keepSpans&&(forceNewCharCount||fs!=origFontSize)){origFontSize=fs;var newCharPerLine=Math.min(60,Math.floor(parentWidth/(origFontSize*fontRatio))),wordIndex=0,lineText=[],counter=0,preText="",postText="",finalText="",slice,preDiff,postDiff;if(newCharPerLine!=0&&newCharPerLine!=idealCharPerLine){idealCharPerLine=newCharPerLine;while(wordIndex<words.length){postText="";while(postText.length<idealCharPerLine){preText=postText;postText+=words[wordIndex]+" ";if(++wordIndex>=words.length){break}}if(minCharsPerLine){slice=words.slice(wordIndex).join(" ");if(slice.length<minCharsPerLine){postText+=slice;preText=postText;wordIndex=words.length+2}}preDiff=idealCharPerLine-preText.length;postDiff=postText.length-idealCharPerLine;if((preDiff<postDiff)&&(preText.length>=(minCharsPerLine||2))){finalText=preText;wordIndex--}else{finalText=postText}finalText=$("
").text(finalText).html();if(settings.wrapAmpersand){finalText=finalText.replace(/&/g,'&')}finalText=$.trim(finalText);lineText.push(''+finalText+"")}$this.html(lineText.join(" "));if(headLink){$this.wrapInner('")}}}else{origFontSize=fs}$("span.slabtext",$this).each(function(){var $span=$(this),innerText=$span.text(),wordSpacing=innerText.split(" ").length>1,diff,ratio,fontSize;if(postTweak){$span.css({"word-spacing":0,"letter-spacing":0})}ratio=parentWidth/$span.width();fontSize=parseFloat(this.style.fontSize)||origFontSize;$span.css("font-size",Math.min((fontSize*ratio).toFixed(precision),settings.maxFontSize)+"px");diff=!!postTweak?parentWidth-$span.width():false;if(diff){$span.css((wordSpacing?"word":"letter")+"-spacing",(diff/(wordSpacing?innerText.split(" ").length-1:innerText.length)).toFixed(precision)+"px")}});$this.addClass("slabtextdone")};resizeSlabs();if(!settings.noResizeEvent){$(window).resize(function(){if($(window).width()==viewportWidth){return}viewportWidth=$(window).width();clearTimeout(resizeThrottle);resizeThrottle=setTimeout(resizeSlabs,resizeThrottleTime)})}})}})(jQuery);

/* BIG Text */
!function(e,t){"use strict";function i(e,t,i,n,s,r,o){var a;if(o="number"==typeof o?o:0,e.css(i,n+r),a=e.width(),a>=t){if(e.css(i,""),a===t)return{match:"exact",size:parseFloat((parseFloat(n)-.1).toFixed(3))};var c=t-o,l=a-t;return{match:"estimate",size:parseFloat((parseFloat(n)-("word-spacing"===i&&o&&c>l?0:s)).toFixed(3))}}return a}function n(e,n,s,r,o){var a=e.clone(!0).addClass("bigtext-cloned").css({fontFamily:e.css("font-family"),textTransform:e.css("text-transform"),wordSpacing:e.css("word-spacing"),letterSpacing:e.css("letter-spacing"),position:"absolute",left:c.DEBUG_MODE?0:-9999,top:c.DEBUG_MODE?0:-9999}).appendTo(document.body),l=[],u=[],p=[],f=[];return n.css("float","left").each(function(){var e,n,a=t(this),u=c.supports.wholeNumberFontSizeOnly?[8,4,1]:[8,4,1,.1];if(a.hasClass(c.EXEMPT_CLASS))return l.push(null),f.push(null),void p.push(!1);var d=32,g=parseFloat(a.css("font-size")),S=(a.width()/g).toFixed(6);n=parseInt(s/S,10)-d;e:for(var h=0,_=u.length;_>h;h++)t:for(var E=1,x=10;x>=E;E++){if(n+E*u[h]>r){n=r;break e}if(e=i(a,s,"font-size",n+E*u[h],u[h],"px",e),"number"!=typeof e){if(n=e.size,"exact"===e.match)break e;break t}}f.push(s/n),n>r?(l.push(r),p.push(!1)):o&&o>n?(l.push(o),p.push(!0)):(l.push(n),p.push(!1))}).each(function(e){var n,r=t(this),o=0,a=1;if(r.hasClass(c.EXEMPT_CLASS))return void u.push(null);r.css("font-size",l[e]+"px");for(var p=1,f=3;f>p;p+=a)if(n=i(r,s,"word-spacing",p,a,"px",n),"number"!=typeof n){o=n.size;break}r.css("font-size",""),u.push(o)}).removeAttr("style"),c.DEBUG_MODE?a.css({"background-color":"rgba(255,255,255,.4)"}):a.remove(),{fontSizes:l,wordSpacings:u,ratios:f,minFontSizes:p}}var s=0,r=t("head"),o=e.BigText,a=t.fn.bigtext,c={DEBUG_MODE:!1,DEFAULT_MIN_FONT_SIZE_PX:null,DEFAULT_MAX_FONT_SIZE_PX:528,GLOBAL_STYLE_ID:"bigtext-style",STYLE_ID:"bigtext-id",LINE_CLASS_PREFIX:"bigtext-line",EXEMPT_CLASS:"bigtext-exempt",noConflict:function(i){return i&&(t.fn.bigtext=a,e.BigText=o),c},supports:{wholeNumberFontSizeOnly:function(){if(!("getComputedStyle"in e))return!0;var i=t("
").css({position:"absolute","font-size":"14.1px"}).insertBefore(t("script").eq(0)),n=e.getComputedStyle(i[0],null),s=n&&"14px"===n.getPropertyValue("font-size");return i.remove(),s}()},init:function(){t("#"+c.GLOBAL_STYLE_ID).length||r.append(c.generateStyleTag(c.GLOBAL_STYLE_ID,[".bigtext * { white-space: nowrap; } .bigtext > * { display: block; }",".bigtext ."+c.EXEMPT_CLASS+", .bigtext ."+c.EXEMPT_CLASS+" * { white-space: normal; }"]))},bindResize:function(i,n){var s;t(e).unbind(i).bind(i,function(){s&&clearTimeout(s),s=setTimeout(n,100)})},getStyleId:function(e){return c.STYLE_ID+"-"+e},generateStyleTag:function(e,i){return t("").attr("id",e)},clearCss:function(e){var i=c.getStyleId(e);t("#"+i).remove()},generateCss:function(e,t,i,n){var s=[];c.clearCss(e);for(var r=0,o=t.length;o>r;r++)s.push("#"+e+" ."+c.LINE_CLASS_PREFIX+r+" {"+(n[r]?" white-space: normal;":"")+(t[r]?" font-size: "+t[r]+"px;":"")+(i[r]?" word-spacing: "+i[r]+"px;":"")+"}");return c.generateStyleTag(c.getStyleId(e),s)},jQueryMethod:function(e){return c.init(),e=t.extend({minfontsize:c.DEFAULT_MIN_FONT_SIZE_PX,maxfontsize:c.DEFAULT_MAX_FONT_SIZE_PX,childSelector:"",resize:!0},e||{}),this.each(function(){var i=t(this).addClass("bigtext"),o=i.width(),a=i.attr("id"),l=e.childSelector?i.find(e.childSelector):i.children();a||(a="bigtext-id"+s++,i.attr("id",a)),e.resize&&c.bindResize("resize.bigtext-event-"+a,function(){c.jQueryMethod.call(t("#"+a),e)}),c.clearCss(a),l.addClass(function(e,t){return[t.replace(new RegExp("\\b"+c.LINE_CLASS_PREFIX+"\\d+\\b"),""),c.LINE_CLASS_PREFIX+e].join(" ")});var u=n(i,l,o,e.maxfontsize,e.minfontsize);r.append(c.generateCss(a,u.fontSizes,u.wordSpacings,u.minFontSizes))}),this.trigger("bigtext:complete")}};t.fn.bigtext=c.jQueryMethod,e.BigText=c}(this,jQuery);

/*
 * Get Viewport Dimensions
 * returns object with viewport dimensions to match css in width and height properties
 * ( source: http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript )
*/
function updateViewportDimensions() {
	var w=window,d=document,e=d.documentElement,g=d.getElementsByTagName('body')[0],x=w.innerWidth||e.clientWidth||g.clientWidth,y=w.innerHeight||e.clientHeight||g.clientHeight;
	return { width:x,height:y };
}
// setting the viewport width
var viewport = updateViewportDimensions();

/*
 * Throttle Resize-triggered Events
 * Wrap your actions in this function to throttle the frequency of firing them off, for better performance, esp. on mobile.
 * ( source: http://stackoverflow.com/questions/2854407/javascript-jquery-window-resize-how-to-fire-after-the-resize-is-completed )
*/
var waitForFinalEvent = (function () {
	var timers = {};
	return function (callback, ms, uniqueId) {
		if (!uniqueId) { uniqueId = "Don't call this twice without a uniqueId"; }
		if (timers[uniqueId]) { clearTimeout (timers[uniqueId]); }
		timers[uniqueId] = setTimeout(callback, ms);
	};
})();

// how long to wait before deciding the resize has stopped, in ms. Around 50-100 should work ok.
var timeToWaitForLast = 100;

// function loadGravatars() {
//   // set the viewport using the function above
//   viewport = updateViewportDimensions();
//   // if the viewport is tablet or larger, we load in the gravatars
//   if (viewport.width >= 768) {
//   jQuery('.comment img[data-gravatar]').each(function(){
//     jQuery(this).attr('src',jQuery(this).attr('data-gravatar'));
//   });
// 	}
// }

/*
 * Put all your regular jQuery in here.
*/
jQuery(document).ready(function($) {

	// Allows us to use a class to create DIV TEXT FILLING GOODNESS.
	$(".bigtext").bigtext();

	// FILTERS
	// ToDo: We will want to make an Ajax call in future to grab all related taxonomy terms to the particular filter.
	// TALKS
	var $btns = $('.btn').click(function() {
	  if (this.id == 'all') {
	    $('#parent > div').fadeIn(450);
	    console.log('all Fade In');
	  } else {
	    var $el = $('.' + this.id).fadeIn(450);
	    $('#parent > div').not($el).hide();
	    console.log('show ' + this.id);
	  }
	  $btns.removeClass('active');
	  $(this).addClass('active');
	});

 	//  var clicked = false;
	// $('.sidebar li a').click(
	// function(){
	// 	//$('#mycontent > div > h2').css('padding-top',0);

	// 	// I want to kill this next line. It adds that padding, but can be done a better way so when you scroll to the top it doesn't look like shit.
	// 	// https://www.dropbox.com/s/6ilpfzro6xej5b4/Screenshot%202015-08-14%2016.41.40.png?dl=0

	// 	$( $( this ).attr('href') ).css('padding-top','50px');
	// 	clicked = true;
	// 	}
	// );

	$('#menu_affix').affix({
		offset: {
			bottom: function () {
			return (this.bottom = $('#footer').outerHeight(true) + 50)
		}
	}
})

});<|endoftext|>


<commit_before>/*!
 * Copyright (c) 2018 Digital Bazaar, Inc. All rights reserved.
 */
'use strict';

module.exports = class ProofPurpose {
  /**
   * @param term {string} the `proofPurpose` term, as defined in the
   *    SECURITY_CONTEXT_URL `@context` or a URI if not defined in such.
   * @param [date] {string or Date or integer} the expected date for
   *   the creation of the proof.
   * @param [maxTimestampDelta] {integer} a maximum number of seconds that
   *   the date on the signature can deviate from, defaults to `Infinity`.
   */
  constructor({term, date, maxTimestampDelta = Infinity} = {}) {
    if(term === undefined) {
      throw new Error('"term" is required.');
    }
    if(maxTimestampDelta !== undefined &&
      typeof maxTimestampDelta !== 'number') {
      throw new TypeError('"maxTimestampDelta" must be a number.');
    }
    this.term = term;
    if(date !== undefined) {
      this.date = new Date(date);
      if(isNaN(this.date)) {
        throw TypeError(`"date" "${date}" is not a valid date.`);
      }
    }
    this.maxTimestampDelta = maxTimestampDelta;
  }

  /**
   * Called to validate the purpose of a proof. This method is called during
   * proof verification, after the proof value has been checked against the
   * given verification method (e.g. in the case of a digital signature, the
   * signature has been cryptographically verified against the public key).
   *
   * @param proof {object} the proof, in the `constants.SECURITY_CONTEXT_URL`,
   *   with the matching purpose to validate.
   *
   * @return {Promise<object>} resolves to an object with `valid` and `error`.
   */
  async validate(
    proof, {document, suite, verificationMethod,
      documentLoader, expansionMap}) {
    try {
      // check expiration
      if(this.maxTimestampDelta !== Infinity) {
        const expected = Date.parse(this.date || Date.now());
        const delta = this.maxTimestampDelta * 1000;
        const created = Date.parse(proof.created);
        if(created < (expected - delta) || created > (expected + delta)) {
          throw new Error('The proof\'s created timestamp is out of range.');
        }
      }
      return {valid: true};
    } catch(error) {
      return {valid: false, error};
    }
  }

  /**
   * Called to update a proof when it is being created, adding any properties
   * specific to this purpose. This method is called prior to the proof
   * value being generated such that any properties added may be, for example,
   * included in a digital signature value.
   *
   * @param proof {object} the proof, in the `constants.SECURITY_CONTEXT_URL`
   *   to update.
   *
   * @return {Promise<object>} resolves to the proof instance (in the
   *   `constants.SECURITY_CONTEXT_URL`.
   */
  async update(proof, {document, suite, documentLoader, expansionMap}) {
    proof.proofPurpose = this.term;
    return proof;
  }

  /**
   * Determines if the given proof has a purpose that matches this instance,
   * i.e. this ProofPurpose instance should be used to validate the given
   * proof.
   *
   * @param proof {object} the proof to check.
   *
   * @return {Promise<boolean>} `true` if there's a match, `false` if not.
   */
  async match(proof, {document, documentLoader, expansionMap}) {
    return proof.proofPurpose === this.term;
  }
};
<commit_msg>Handle `NaN` case for `proof.created`.<commit_after>/*!
 * Copyright (c) 2018 Digital Bazaar, Inc. All rights reserved.
 */
'use strict';

module.exports = class ProofPurpose {
  /**
   * @param term {string} the `proofPurpose` term, as defined in the
   *    SECURITY_CONTEXT_URL `@context` or a URI if not defined in such.
   * @param [date] {string or Date or integer} the expected date for
   *   the creation of the proof.
   * @param [maxTimestampDelta] {integer} a maximum number of seconds that
   *   the date on the signature can deviate from, defaults to `Infinity`.
   */
  constructor({term, date, maxTimestampDelta = Infinity} = {}) {
    if(term === undefined) {
      throw new Error('"term" is required.');
    }
    if(maxTimestampDelta !== undefined &&
      typeof maxTimestampDelta !== 'number') {
      throw new TypeError('"maxTimestampDelta" must be a number.');
    }
    this.term = term;
    if(date !== undefined) {
      this.date = new Date(date);
      if(isNaN(this.date)) {
        throw TypeError(`"date" "${date}" is not a valid date.`);
      }
    }
    this.maxTimestampDelta = maxTimestampDelta;
  }

  /**
   * Called to validate the purpose of a proof. This method is called during
   * proof verification, after the proof value has been checked against the
   * given verification method (e.g. in the case of a digital signature, the
   * signature has been cryptographically verified against the public key).
   *
   * @param proof {object} the proof, in the `constants.SECURITY_CONTEXT_URL`,
   *   with the matching purpose to validate.
   *
   * @return {Promise<object>} resolves to an object with `valid` and `error`.
   */
  async validate(
    proof, {document, suite, verificationMethod,
      documentLoader, expansionMap}) {
    try {
      // check expiration
      if(this.maxTimestampDelta !== Infinity) {
        const expected = (this.date || new Date()).getTime();
        const delta = this.maxTimestampDelta * 1000;
        const created = new Date(proof.created).getTime();
        // comparing this way handles NaN case where `created` is invalid
        if(!(created >= (expected - delta) && created <= (expected + delta))) {
          throw new Error('The proof\'s created timestamp is out of range.');
        }
      }
      return {valid: true};
    } catch(error) {
      return {valid: false, error};
    }
  }

  /**
   * Called to update a proof when it is being created, adding any properties
   * specific to this purpose. This method is called prior to the proof
   * value being generated such that any properties added may be, for example,
   * included in a digital signature value.
   *
   * @param proof {object} the proof, in the `constants.SECURITY_CONTEXT_URL`
   *   to update.
   *
   * @return {Promise<object>} resolves to the proof instance (in the
   *   `constants.SECURITY_CONTEXT_URL`.
   */
  async update(proof, {document, suite, documentLoader, expansionMap}) {
    proof.proofPurpose = this.term;
    return proof;
  }

  /**
   * Determines if the given proof has a purpose that matches this instance,
   * i.e. this ProofPurpose instance should be used to validate the given
   * proof.
   *
   * @param proof {object} the proof to check.
   *
   * @return {Promise<boolean>} `true` if there's a match, `false` if not.
   */
  async match(proof, {document, documentLoader, expansionMap}) {
    return proof.proofPurpose === this.term;
  }
};
<|endoftext|>