<commit_before>/**
 * Copyright 2013-present NightWorld.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var auth = require('basic-auth'),
    error = require('node-restify-errors'),
    runner = require('./runner'),
    token = require('./token');

module.exports = Grant;

/**
 * This is the function order used by the runner
 *
 * @type {Array}
 */
var fns = [
  extractCredentials,
  checkClient,
  checkGrantTypeAllowed,
  checkGrantType,
  generateAccessToken,
  saveAccessToken,
  generateRefreshToken,
  saveRefreshToken,
  sendResponse
];

/**
 * Grant
 *
 * @param {Object}   config Instance of OAuth object
 * @param {Object}   req
 * @param {Object}   res
 * @param {Function} next
 */
function Grant (config, req, res, next) {
  this.config = config;
  this.model = config.model;
  this.now = new Date();
  this.req = req;
  this.res = res;

  runner(fns, this, next);
}

/**
 * Basic request validation and extraction of grant_type and client creds
 *
 * @param  {Function} next
 * @this   OAuth
 */
function extractCredentials (next) {
  // Only POST via application/x-www-form-urlencoded is acceptable
  if (this.req.method !== 'POST' ||
      !this.req.is('application/x-www-form-urlencoded')) {
    return next(new error.BadMethodError('Method must be POST with application/x-www-form-urlencoded encoding'));
  }

  // Grant type
  this.grantType = this.req.body && this.req.body.grant_type;
  if (!this.grantType || !this.grantType.match(this.config.regex.grantType)) {
    return next(new error.BadMethodError('Invalid or missing grant_type parameter'));
  }

  // Extract credentials
  // http://tools.ietf.org/html/rfc6749#section-3.2.1
  this.client = credsFromBasic(this.req) || credsFromBody(this.req);

  if (!this.client.clientId ||
      !this.client.clientId.match(this.config.regex.clientId)) {
    return next(new error.InvalidCredentialsError('Invalid or missing client_id parameter'));
  } else if (!this.client.clientSecret) {
    return next(new error.InvalidCredentialsError('Missing client_secret parameter'));
  }

  next();
}

/**
 * Client Object (internal use only)
 *
 * @param {String} id     client_id
 * @param {String} secret client_secret
 */
function Client (id, secret) {
  this.clientId = id;
  this.clientSecret = secret;
}

/**
 * Extract client creds from Basic auth
 *
 * @return {Object} Client
 */
function credsFromBasic (req) {
  var user = auth(req);

  if (!user) return false;

  return new Client(user.name, user.pass);
}

/**
 * Extract client creds from body
 *
 * @return {Object} Client
 */
function credsFromBody (req) {
  return new Client(req.body.client_id, req.body.client_secret);
}

/**
 * Check extracted client against model
 *
 * @param  {Function} next
 * @this   OAuth
 */
function checkClient (next) {
  this.model.getClient(this.client.clientId, this.client.clientSecret,
      function (err, client) {
        if (err) return next(new error.InternalError(err));

        if (!client) {
          return next(new error.InvalidCredentialsError('Client credentials are invalid'));
        }

        next();
      });
}

/**
 * Delegate to the relvant grant function based on grant_type
 *
 * @param  {Function} next
 * @this   OAuth
 */
function checkGrantType (next) {
  if (this.grantType.match(/^http(s|):\/\//) && this.model.extendedGrant) {
    return useExtendedGrant.call(this, next);
  }

  switch (this.grantType) {
    case 'authorization_code':
      return useAuthCodeGrant.call(this, next);
    case 'password':
      return usePasswordGrant.call(this, next);
    case 'refresh_token':
      return useRefreshTokenGrant.call(this, next);
    case 'client_credentials':
      return useClientCredentialsGrant.call(this, next);
    default:
      next(new error.MissingParameterError('Invalid grant_type parameter or parameter missing'));
  }
}

/**
 * Grant for authorization_code grant type
 *
 * @param  {Function} next
 */
function useAuthCodeGrant (next) {
  var code = this.req.body.code;

  if (!code) {
    return next(new error.MissingParameterError('No "code" parameter'));
  }

  var self = this;
  this.model.getAuthCode(code, function (err, authCode) {
    if (err) return next(new error('server_error', false, err));

    if (!authCode || authCode.clientId !== self.client.clientId) {
      return next(new error.InvalidContentError('Invalid code'));
    } else if (authCode.expires < self.now) {
      return next(new error.InvalidContentError('Code has expired'));
    }

    self.user = authCode.user || { id: authCode.userId };
    if (!self.user.id) {
      return next(new error.InternalError('No user/userId parameter returned from getauthCode'));
    }

    next();
  });
}

/**
 * Grant for password grant type
 *
 * @param  {Function} next
 */
function usePasswordGrant (next) {
  // User credentials
  var uname = this.req.body.username,
      pword = this.req.body.password;
  if (!uname || !pword) {
    return next(new error.InvalidCredentialsError('Missing parameters. "username" and "password" are required'));
  }

  var self = this;
  return this.model.getUser(uname, pword, function (err, user) {
    if (err) return next(new error.InternalError(err));
    if (!user) {
      return next(new error.InvalidCredentialsError('User credentials are invalid'));
    }

    self.user = user;
    next();
  });
}

/**
 * Grant for refresh_token grant type
 *
 * @param  {Function} next
 */
function useRefreshTokenGrant (next) {
  var token = this.req.body.refresh_token;

  if (!token) {
    return next(new error.BadMethodError('No "refresh_token" parameter'));
  }

  var self = this;
  this.model.getRefreshToken(token, function (err, refreshToken) {
    if (err) return next(new error.InternalError(err));

    if (!refreshToken || refreshToken.clientId !== self.client.clientId) {
      return next(new error.BadMethodError('Invalid refresh token'));
    } else if (refreshToken.expires !== null &&
        refreshToken.expires < self.now) {
      return next(new error.BadMethodError('Refresh token has expired'));
    }

    if (!refreshToken.user && !refreshToken.userId) {
      return next(new error.InternalError('No user/userId parameter returned from getRefreshToken'));
    }

    self.user = refreshToken.user || { id: refreshToken.userId };

    if (self.model.revokeRefreshToken) {
      return self.model.revokeRefreshToken(token, function (err) {
        if (err) return next(new error.InternalError(err));
        next();
      });
    }

    next();
  });
}

/**
 * Grant for client_credentials grant type
 *
 * @param  {Function} next
 */
function useClientCredentialsGrant (next) {
  // Client credentials
  var clientId = this.client.clientId,
      clientSecret = this.client.clientSecret;

  if (!clientId || !clientSecret) {
    return next(new error.InvalidCredentialsError('Missing parameters. "client_id" and "client_secret" are required'));
  }

  var self = this;
  return this.model.getUserFromClient(clientId, clientSecret,
      function (err, user) {
        if (err) return next(new error.InternalError(err));
        if (!user) {
          return next(new error.InvalidCredentialsError('Client credentials are invalid'));
        }

        self.user = user;
        next();
      });
}

/**
 * Grant for extended (http://*) grant type
 *
 * @param  {Function} next
 */
function useExtendedGrant (next) {
  var self = this;
  this.model.extendedGrant(this.grantType, this.req,
      function (err, supported, user) {
        if (err) {
          return next(new error(err.error || 'server_error',
              err.description || err.message, err));
        }

        if (!supported) {
          return next(new error.BadMethodError('Invalid grant_type parameter or parameter missing'));
        } else if (!user || user.id === undefined) {
          return next(new error.BadMethodError('Invalid request.'));
        }

        self.user = user;
        next();
      });
}

/**
 * Check the grant type is allowed for this client
 *
 * @param  {Function} next
 * @this   OAuth
 */
function checkGrantTypeAllowed (next) {
  this.model.grantTypeAllowed(this.client.clientId, this.grantType,
      function (err, allowed) {
        if (err) return next(new error.InternalError(err));

        if (!allowed) {
          return next(new error.InvalidCredentialsError('The grant type is unauthorised for this client_id'));
        }

        next();
      });
}

/**
 * Generate an access token
 *
 * @param  {Function} next
 * @this   OAuth
 */
function generateAccessToken (next) {
  var self = this;
  token(this, 'accessToken', function (err, token) {
    self.accessToken = token;
    next(err);
  });
}

/**
 * Save access token with model
 *
 * @param  {Function} next
 * @this   OAuth
 */
function saveAccessToken (next) {
  var accessToken = this.accessToken;

  // Object indicates a reissue
  if (typeof accessToken === 'object' && accessToken.accessToken) {
    this.accessToken = accessToken.accessToken;
    return next();
  }

  var expires = null;
  if (this.config.accessTokenLifetime !== null) {
    expires = new Date(this.now);
    expires.setSeconds(expires.getSeconds() + this.config.accessTokenLifetime);
  }

  this.model.saveAccessToken(accessToken, this.client.clientId, expires,
      this.user, function (err, token) {
        if (err) return next(new error.InternalError(err));
        next();
      });
}

/**
 * Generate a refresh token
 *
 * @param  {Function} next
 * @this   OAuth
 */
function generateRefreshToken (next) {
  if (this.config.grants.indexOf('refresh_token') === -1) return next();

  var self = this;
  token(this, 'refreshToken', function (err, token) {
    self.refreshToken = token;
    next(err);
  });
}

/**
 * Save refresh token with model
 *
 * @param  {Function} next
 * @this   OAuth
 */
function saveRefreshToken (next) {
  var refreshToken = this.refreshToken;

  if (!refreshToken) return next();

  // Object indicates a reissue
  if (typeof refreshToken === 'object' && refreshToken.refreshToken) {
    this.refreshToken = refreshToken.refreshToken;
    return next();
  }

  var expires = null;
  if (this.config.refreshTokenLifetime !== null) {
    expires = new Date(this.now);
    expires.setSeconds(expires.getSeconds() + this.config.refreshTokenLifetime);
  }

  this.model.saveRefreshToken(refreshToken, this.client.clientId, expires,
      this.user, function (err, user) {
        if (err) return next(new error.InternalError(err));
        next();
      });
}

/**
 * Create an access token and save it with the model
 *
 * @param  {Function} next
 * @this   OAuth
 */
function sendResponse (next) {
  var response = {
    token_type: 'bearer',
    access_token: this.accessToken
  };

  if (this.config.accessTokenLifetime !== null) {
    response.expires_in = this.config.accessTokenLifetime;
  }

  if (this.refreshToken) response.refresh_token = this.refreshToken;

  this.res.send(response);

  if (this.config.continueAfterResponse)
    next();
}
<commit_msg>Update grant.js<commit_after>/**
 * Copyright 2013-present NightWorld.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var auth = require('basic-auth'),
    error = require('node-restify-errors'),
    runner = require('./runner'),
    token = require('./token');

module.exports = Grant;

/**
 * This is the function order used by the runner
 *
 * @type {Array}
 */
var fns = [
  extractCredentials,
  checkClient,
  checkGrantTypeAllowed,
  checkGrantType,
  generateAccessToken,
  saveAccessToken,
  generateRefreshToken,
  saveRefreshToken,
  sendResponse
];

/**
 * Grant
 *
 * @param {Object}   config Instance of OAuth object
 * @param {Object}   req
 * @param {Object}   res
 * @param {Function} next
 */
function Grant (config, req, res, next) {
  this.config = config;
  this.model = config.model;
  this.now = new Date();
  this.req = req;
  this.res = res;

  runner(fns, this, next);
}

/**
 * Basic request validation and extraction of grant_type and client creds
 *
 * @param  {Function} next
 * @this   OAuth
 */
function extractCredentials (next) {
  // Only POST via application/x-www-form-urlencoded is acceptable
  if (this.req.method !== 'POST' ||
      !this.req.is('application/x-www-form-urlencoded')) {
    return next(new error.BadMethodError('Method must be POST with application/x-www-form-urlencoded encoding'));
  }

  // Grant type
  this.grantType = this.req.body && this.req.body.grant_type;
  if (!this.grantType || !this.grantType.match(this.config.regex.grantType)) {
    return next(new error.BadMethodError('Invalid or missing grant_type parameter'));
  }

  // Extract credentials
  // http://tools.ietf.org/html/rfc6749#section-3.2.1
  this.client = credsFromBasic(this.req) || credsFromBody(this.req);

  if (!this.client.clientId ||
      !this.client.clientId.match(this.config.regex.clientId)) {
    return next(new error.InvalidCredentialsError('Invalid or missing client_id parameter'));
  } else if (!this.client.clientSecret) {
    return next(new error.InvalidCredentialsError('Missing client_secret parameter'));
  }

  next();
}

/**
 * Client Object (internal use only)
 *
 * @param {String} id     client_id
 * @param {String} secret client_secret
 */
function Client (id, secret) {
  this.clientId = id;
  this.clientSecret = secret;
}

/**
 * Extract client creds from Basic auth
 *
 * @return {Object} Client
 */
function credsFromBasic (req) {
  var user = auth(req);

  if (!user) return false;

  return new Client(user.name, user.pass);
}

/**
 * Extract client creds from body
 *
 * @return {Object} Client
 */
function credsFromBody (req) {
  return new Client(req.body.client_id, req.body.client_secret);
}

/**
 * Check extracted client against model
 *
 * @param  {Function} next
 * @this   OAuth
 */
function checkClient (next) {
  this.model.getClient(this.client.clientId, this.client.clientSecret,
      function (err, client) {
        if (err) return next(new error.InternalError(err));

        if (!client) {
          return next(new error.InvalidCredentialsError('Client credentials are invalid'));
        }

        next();
      });
}

/**
 * Delegate to the relvant grant function based on grant_type
 *
 * @param  {Function} next
 * @this   OAuth
 */
function checkGrantType (next) {
  if (this.grantType.match(/^http(s|):\/\//) && this.model.extendedGrant) {
    return useExtendedGrant.call(this, next);
  }

  switch (this.grantType) {
    case 'authorization_code':
      return useAuthCodeGrant.call(this, next);
    case 'password':
      return usePasswordGrant.call(this, next);
    case 'refresh_token':
      return useRefreshTokenGrant.call(this, next);
    case 'client_credentials':
      return useClientCredentialsGrant.call(this, next);
    default:
      next(new error.MissingParameterError('Invalid grant_type parameter or parameter missing'));
  }
}

/**
 * Grant for authorization_code grant type
 *
 * @param  {Function} next
 */
function useAuthCodeGrant (next) {
  var code = this.req.body.code;

  if (!code) {
    return next(new error.MissingParameterError('No "code" parameter'));
  }

  var self = this;
  this.model.getAuthCode(code, function (err, authCode) {
    if (err) return next(new error('server_error', false, err));

    if (!authCode || authCode.clientId !== self.client.clientId) {
      return next(new error.InvalidContentError('Invalid code'));
    } else if (authCode.expires < self.now) {
      return next(new error.InvalidContentError('Code has expired'));
    }

    self.user = authCode.user || { id: authCode.userId };
    if (!self.user.id) {
      return next(new error.InternalError('No user/userId parameter returned from getauthCode'));
    }

    next();
  });
}

/**
 * Grant for password grant type
 *
 * @param  {Function} next
 */
function usePasswordGrant (next) {
  // User credentials
  var uname = this.req.body.username,
      pword = this.req.body.password;
  if (!uname || !pword) {
    return next(new error.InvalidCredentialsError('Missing parameters. "username" and "password" are required'));
  }

  var self = this;
  return this.model.getUser(uname, pword, function (err, user) {
    if (err) return next(new error.InternalError(err));
    if (!user) {
      return next(new error.InvalidCredentialsError('User credentials are invalid'));
    }

    self.user = user;
    next();
  });
}

/**
 * Grant for refresh_token grant type
 *
 * @param  {Function} next
 */
function useRefreshTokenGrant (next) {
  var token = this.req.body.refresh_token;

  if (!token) {
    return next(new error.BadMethodError('No "refresh_token" parameter'));
  }

  var self = this;
  this.model.getRefreshToken(token, function (err, refreshToken) {
    if (err) return next(new error.InternalError(err));

    if (!refreshToken || refreshToken.clientId !== self.client.clientId) {
      return next(new error.BadMethodError('Invalid refresh token'));
    } else if (refreshToken.expires !== null &&
        refreshToken.expires < self.now) {
      return next(new error.BadMethodError('Refresh token has expired'));
    }

    if (!refreshToken.user && !refreshToken.userId) {
      return next(new error.InternalError('No user/userId parameter returned from getRefreshToken'));
    }

    self.user = refreshToken.user || { id: refreshToken.userId };

    if (self.model.revokeRefreshToken) {
      return self.model.revokeRefreshToken(token, function (err) {
        if (err) return next(new error.InternalError(err));
        next();
      });
    }

    next();
  });
}

/**
 * Grant for client_credentials grant type
 *
 * @param  {Function} next
 */
function useClientCredentialsGrant (next) {
  // Client credentials
  var clientId = this.client.clientId,
      clientSecret = this.client.clientSecret;

  if (!clientId || !clientSecret) {
    return next(new error.InvalidCredentialsError('Missing parameters. "client_id" and "client_secret" are required'));
  }

  var self = this;
  return this.model.getUserFromClient(clientId, clientSecret,
      function (err, user) {
        if (err) return next(new error.InternalError(err));
        if (!user) {
          return next(new error.InvalidCredentialsError('Client credentials are invalid'));
        }

        self.user = user;
        next();
      });
}

/**
 * Grant for extended (http://*) grant type
 *
 * @param  {Function} next
 */
function useExtendedGrant (next) {
  var self = this;
  this.model.extendedGrant(this.grantType, this.req,
      function (err, supported, user) {
        if (err) {
          return next(new error(err.error || 'server_error',
              err.description || err.message, err));
        }

        if (!supported) {
          return next(new error.BadMethodError('Invalid grant_type parameter or parameter missing'));
        } else if (!user || user.id === undefined) {
          return next(new error.BadMethodError('Invalid request.'));
        }

        self.user = user;
        next();
      });
}

/**
 * Check the grant type is allowed for this client
 *
 * @param  {Function} next
 * @this   OAuth
 */
function checkGrantTypeAllowed (next) {
  this.model.grantTypeAllowed(this.client.clientId, this.grantType,
      function (err, allowed) {
        if (err) return next(new error.InternalError(err));

        if (!allowed) {
          return next(new error.InvalidCredentialsError('The grant type is unauthorised for this client_id'));
        }

        next();
      });
}

/**
 * Generate an access token
 *
 * @param  {Function} next
 * @this   OAuth
 */
function generateAccessToken (next) {
  var self = this;
  token(this, 'accessToken', function (err, token) {
    self.accessToken = token;
    next(err);
  });
}

/**
 * Save access token with model
 *
 * @param  {Function} next
 * @this   OAuth
 */
function saveAccessToken (next) {
  var accessToken = this.accessToken;

  // Object indicates a reissue
  if (typeof accessToken === 'object' && accessToken.accessToken) {
    this.accessToken = accessToken.accessToken;
    return next();
  }

  var expires = null;
  if (this.config.accessTokenLifetime !== null) {
    expires = new Date(this.now);
    expires.setSeconds(expires.getSeconds() + this.config.accessTokenLifetime);
  }

  this.model.saveAccessToken(accessToken, this.client.clientId, expires,
      this.user, function (err, token) {
        if (err) return next(new error.InternalError(err));
        next();
      });
}

/**
 * Generate a refresh token
 *
 * @param  {Function} next
 * @this   OAuth
 */
function generateRefreshToken (next) {
  if (this.config.grants.indexOf('refresh_token') === -1) return next();

  var self = this;
  token(this, 'refreshToken', function (err, token) {
    self.refreshToken = token;
    next(err);
  });
}

/**
 * Save refresh token with model
 *
 * @param  {Function} next
 * @this   OAuth
 */
function saveRefreshToken (next) {
  var refreshToken = this.refreshToken;

  if (!refreshToken) return next();

  // Object indicates a reissue
  if (typeof refreshToken === 'object' && refreshToken.refreshToken) {
    this.refreshToken = refreshToken.refreshToken;
    return next();
  }

  var expires = null;
  if (this.config.refreshTokenLifetime !== null) {
    expires = new Date(this.now);
    expires.setSeconds(expires.getSeconds() + this.config.refreshTokenLifetime);
  }

  this.model.saveRefreshToken(refreshToken, this.client.clientId, expires,
      this.user, function (err, user) {
        if (err) return next(new error.InternalError(err));
        next();
      });
}

/**
 * Create an access token and save it with the model
 *
 * @param  {Function} next
 * @this   OAuth
 */
function sendResponse (next) {
  var response = {
    token_type: 'bearer',
    access_token: this.accessToken
  };

  if (this.config.accessTokenLifetime !== null) {
    response.expires_in = this.config.accessTokenLifetime;
  }

  if (this.refreshToken) response.refresh_token = this.refreshToken;

  this.res
      .set('Cache-Control', 'no-store')
      .set('Pragma', 'no-cache')
      .send(response);

  if (this.config.continueAfterResponse)
    next();
}
<|endoftext|>


<commit_before>import * as managers from './proxies';

export function array(item, options) {
  return function _array(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.ArrayManager(question, chain, basePath, name, item, options);
    return chain[name];
  };
}

export function shape(args) {
  return function _shape(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.ShapeManager(question, chain, basePath, name, args);
    return chain[name];
  };
}

export function string(options={}) {
  if (options.isRequired === undefined) {
    options.isRequired = false;
  }
  return function _string(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.StringManager(question, basePath, options);
    return chain[name];
  };
}

export function bool(options={}) {
  if (options.isRequired === undefined) {
    options.isRequired = false;
  }
  return function _string(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.BoolManager(question, basePath, options);
    return chain[name];
  };
}

export function integer(options={}) {
  if (options.isRequired === undefined) {
    options.isRequired = false;
  }
  return function _string(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.IntegerManager(question, basePath, options);
    return chain[name];
  };
}

export * from './Question';
<commit_msg>Fix schema array factory<commit_after>import * as managers from './proxies';

export function array(item, options={}) {
  return function _array(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.ArrayManager(question, chain, basePath, name, item, options);
    return chain[name];
  };
}

export function shape(args) {
  return function _shape(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.ShapeManager(question, chain, basePath, name, args);
    return chain[name];
  };
}

export function string(options={}) {
  if (options.isRequired === undefined) {
    options.isRequired = false;
  }
  return function _string(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.StringManager(question, basePath, options);
    return chain[name];
  };
}

export function bool(options={}) {
  if (options.isRequired === undefined) {
    options.isRequired = false;
  }
  return function _string(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.BoolManager(question, basePath, options);
    return chain[name];
  };
}

export function integer(options={}) {
  if (options.isRequired === undefined) {
    options.isRequired = false;
  }
  return function _string(question, chain, basePath) {
    let name = basePath[basePath.length - 1];
    chain[name] = new managers.IntegerManager(question, basePath, options);
    return chain[name];
  };
}

export * from './Question';
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import Relay from 'react-relay/classic';
import PropTypes from 'prop-types';
import Box from '@material-ui/core/Box';
import CardContent from '@material-ui/core/CardContent';
import CardHeader from '@material-ui/core/CardHeader';
import CardActions from '@material-ui/core/CardActions';
import Typography from '@material-ui/core/Typography';
import { withStyles } from '@material-ui/core/styles';
import MediaRoute from '../../relay/MediaRoute';
import MediaExpandedActions from './MediaExpandedActions';
import MediaExpandedArchives from './MediaExpandedArchives';
import MediaExpandedMetadata from './MediaExpandedMetadata';
import MediaExpandedSecondRow from './MediaExpandedSecondRow';
import MediaExpandedUrl from './MediaExpandedUrl';
import MediaLanguageChip from './MediaLanguageChip';
import MoreLess from '../layout/MoreLess';
import ParsedText from '../ParsedText';
import QuoteMediaCard from './QuoteMediaCard';
import WebPageMediaCard from './WebPageMediaCard';
import ImageMediaCard from './ImageMediaCard';
import MediaPlayerCard from './MediaPlayerCard';
import PenderCard from '../PenderCard';
import BlankMediaButton from './BlankMediaButton';
import UserUtil from '../user/UserUtil';
import { truncateLength } from '../../helpers';
import CheckContext from '../../CheckContext';
import { withPusher, pusherShape } from '../../pusher';
import { units, black54 } from '../../styles/js/shared';

const TypographyBlack54 = withStyles({
  root: {
    color: black54,
  },
})(Typography);

const useStyles = () => ({
  title: {
    fontSize: 14,
    lineHeight: '1.5em',
    color: 'black',
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    textDecoration: 'none',
    '&:hover': {
      color: 'black',
    },
    '&:visited': {
      color: 'black',
    },
  },
});

class MediaExpandedComponent extends Component {
  constructor(props) {
    super(props);

    this.state = {
      mediaVersion: false,
      playbackRate: 1,
    };
  }

  componentDidMount() {
    this.setContext();
    this.subscribe();
  }

  componentDidUpdate() {
    this.setContext();
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  getContext() {
    return new CheckContext(this).getContextStore();
  }

  setContext() {
    const context = new CheckContext(this);
    const { team, project } = this.props.media;
    context.setContextStore({ team, project });
  }

  subscribe() {
    const { pusher, clientSessionId, media } = this.props;
    if (pusher) {
      pusher.subscribe(media.pusher_channel).bind('media_updated', 'MediaComponent', (data, run) => {
        const annotation = JSON.parse(data.message);
        if (annotation.annotated_id === media.dbid && clientSessionId !== data.actor_session_id) {
          if (run) {
            this.props.relay.forceFetch();
            return true;
          }
          return {
            id: `media-${media.dbid}`,
            callback: this.props.relay.forceFetch,
          };
        }
        return false;
      });
    }
  }

  unsubscribe() {
    const { pusher, media } = this.props;
    if (pusher) {
      pusher.unsubscribe(media.pusher_channel);
    }
  }

  render() {
    const { classes } = this.props;
    const {
      media, playing, start, end, gaps, seekTo, scrubTo, setPlayerState, onPlayerReady, isTrends,
    } = this.props;
    const { playbackRate } = this.state;

    const {
      onTimelineCommentOpen,
      onVideoAnnoToggle,
      showVideoAnnotation,
    } = this.props;

    const currentUserRole = UserUtil.myRole(
      this.getContext().currentUser,
      this.getContext().team.slug,
    );

    const data = typeof media.media.metadata === 'string' ? JSON.parse(media.media.metadata) : media.media.metadata;
    const isImage = media.media.type === 'UploadedImage';
    const isMedia = ['UploadedVideo', 'UploadedAudio'].indexOf(media.media.type) > -1;
    const isYoutube = media.media.url && media.domain === 'youtube.com';
    let filePath = media.media.file_path;
    if (isYoutube) {
      filePath = media.media.url;
    }
    const isQuote = media.media.type === 'Claim';
    const isBlank = media.media.type === 'Blank';
    const isWebPage = media.media.url && data.provider === 'page';
    const isPender = media.media.url && data.provider !== 'page';
    const randomNumber = Math.floor(Math.random() * 1000000);
    const { mediaUrl, mediaQuery, linkTitle } = this.props;
    const coverImage = media.media.thumbnail_path || '/images/player_cover.svg';

    let warningType = null;
    if (media.dynamic_annotation_flag) {
      // Sort by flag category likelihood and display most likely
      let sortable = [];
      // Put custom flag at beginning of array
      if (media.dynamic_annotation_flag.data.custom) {
        sortable = sortable.concat([...Object.entries(media.dynamic_annotation_flag.data.custom)]);
      }
      const filteredFlags = {};
      ['adult', 'medical', 'violence'].forEach((key) => { filteredFlags[key] = media.dynamic_annotation_flag.data.flags[key]; });
      sortable = sortable.concat([...Object.entries(filteredFlags)]);
      sortable.sort((a, b) => b[1] - a[1]);
      const type = sortable[0];
      [warningType] = type;
    }

    const embedCard = (() => {
      if (isImage) {
        return (
          <ImageMediaCard
            key={media.dynamic_annotation_flag}
            contentWarning={media.show_warning_cover}
            warningCreator={media.dynamic_annotation_flag?.annotator?.name}
            warningCategory={warningType}
            imagePath={media.media.embed_path}
          />
        );
      } else if (isMedia || isYoutube) {
        return (

            <MediaPlayerCard
              key={media.dynamic_annotation_flag}
              filePath={filePath}
              coverImage={coverImage}
              contentWarning={media.show_warning_cover}
              warningCreator={media.dynamic_annotation_flag?.annotator?.name}
              warningCategory={warningType}
              {...{
                playing, start, end, gaps, scrubTo, seekTo, onPlayerReady, setPlayerState, playbackRate,
              }}
            />

        );
      } else if (isQuote) {
        return (
          <QuoteMediaCard
            quote={media.media.quote}
            languageCode={media.language_code}
          />
        );
      } else if (isWebPage || !data.html) {
        return (
          <WebPageMediaCard
            key={media.dynamic_annotation_flag}
            contentWarning={media.show_warning_cover}
            warningCreator={media.dynamic_annotation_flag?.annotator?.name}
            warningCategory={warningType}
            media={media}
            mediaUrl={mediaUrl}
            mediaQuery={mediaQuery}
            data={data}
          />
        );
      } else if (isPender) {
        return (
          <PenderCard
            url={media.media.url}
            fallback={null}
            domId={`pender-card-${randomNumber}`}
            mediaVersion={this.state.mediaVersion || data.refreshes_count}
          />
        );
      }

      return null;
    })();

    if (isBlank) {
      return (
        <CardContent>
          <Box
            display="flex"
            justifyContent="center"
            alignItems="center"
            style={{ minHeight: 400 }}
          >
            <BlankMediaButton
              projectMediaId={media.id}
              team={media.team}
            />
          </Box>
        </CardContent>
      );
    }

    const fileTitle = media.media.file_path ? media.media.file_path.split('/').pop().replace(/\..*$/, '') : null;
    const title = media.media.metadata.title || media.media.quote || fileTitle || media.title;
    let description = media.extracted_text ? media.extracted_text.data.text : media.media.metadata.description;
    description = media.transcription && media.transcription.data.text ? media.transcription.data.text : description;

    return (
      <React.Fragment>
        <CardHeader
          className="media-expanded__title"
          title={
            linkTitle ?

                {truncateLength(title, 110)}
               : truncateLength(title, 110)
          }
        />
        <CardContent style={{ padding: `0 ${units(2)}` }}>
          <MediaExpandedSecondRow projectMedia={media} isTrends={isTrends} />
          { isImage ?
            <Box mb={2}>
              <TypographyBlack54 variant="body2" color={black54}>
                { media.extracted_text ?
                  <FormattedMessage id="mediaExpanded.extractedText" defaultMessage="Text extracted from image:" description="Label for text extracted from the image below" /> :
                  <FormattedMessage id="mediaExpanded.noExtractedText" defaultMessage="No text extracted from this image" description="Label when text extracted from an image is not available" />
                }
              </TypographyBlack54>
            </Box> : null
          }
          { isMedia ?
            <Box mb={2}>
              <TypographyBlack54 variant="body2" color={black54}>
                { media.transcription && media.transcription.data.last_response.job_status === 'COMPLETED' ?
                  <FormattedMessage id="mediaExpanded.transcriptionCompleted" defaultMessage="Audio transcribed from media:" description="Label for transcription from audio or video" /> : null }
                { media.transcription && media.transcription.data.last_response.job_status === 'IN_PROGRESS' ?
                  <FormattedMessage id="mediaExpanded.transcriptionInProgress" defaultMessage="Audio transcription in progress…" description="Label when transcription is in progress" /> : null }
                { !media.transcription ?
                  <FormattedMessage id="mediaExpanded.noTranscription" defaultMessage="No audio transcribed from this media" description="Label when transcription is not (yet) available" /> : null }
              </TypographyBlack54>
            </Box> : null
          }
          <Box mb={2}>
            <MoreLess>
              <Typography variant="body2">
                <ParsedText text={description} />
              </Typography>
            </MoreLess>
          </Box>
          <Box mb={2}><MediaLanguageChip projectMedia={media} /></Box>
          <MediaExpandedUrl url={media.media.url} />
          <MediaExpandedArchives projectMedia={media} />
          <MediaExpandedMetadata projectMedia={media} />
          {embedCard}
        </CardContent>
        {
          isTrends ? null : (
            <CardActions>
              <MediaExpandedActions
                currentUserRole={currentUserRole}
                onTimelineCommentOpen={onTimelineCommentOpen}
                onVideoAnnoToggle={onVideoAnnoToggle}
                showVideoAnnotation={showVideoAnnotation}
                projectMedia={media}
                playbackRate={playbackRate}
                onPlaybackRateChange={r => this.setState({ playbackRate: r })}
              />
            </CardActions>
          )
        }
      </React.Fragment>
    );
  }
}

MediaExpandedComponent.contextTypes = {
  store: PropTypes.object,
};

MediaExpandedComponent.propTypes = {
  pusher: pusherShape.isRequired,
  playerRef: PropTypes.shape({ current: PropTypes.instanceOf(HTMLElement) }).isRequired,
  relay: PropTypes.object.isRequired,
  /*
    FIXME refactor MediaExpanded component and children.
    Make children fragmentContainer and simplify parent query.
    Define actual propTypes shape of media for MediaExpanded.
  */
  media: PropTypes.object.isRequired,
};

const MediaExpandedContainer = Relay.createContainer(withPusher(MediaExpandedComponent), {
  initialVariables: {
    contextId: null,
  },
  fragments: {
    media: () => Relay.QL`
      fragment on ProjectMedia {
        id
        dbid
        permissions
        domain
        created_at
        last_seen
        share_count
        requests_count
        picture
        title
        description
        language_code
        language
        extracted_text: annotation(annotation_type: "extracted_text") {
          data
        }
        transcription: annotation(annotation_type: "transcription") {
          data
        }
        project_id
        pusher_channel
        full_url
        dynamic_annotation_language {
          id
        }
        show_warning_cover
        dynamic_annotation_flag {
          id
          dbid
          content
          data
          annotator {
            name
          }
        }
        ${MediaExpandedActions.getFragment('projectMedia')}
        ${MediaExpandedArchives.getFragment('projectMedia')}
        ${MediaExpandedMetadata.getFragment('projectMedia')}
        media {
          url
          type
          quote
          thumbnail_path
          file_path
          embed_path
          metadata
        }
        team {
          id
          dbid
          slug
          search_id
          verification_statuses
          get_languages
          permissions
          smooch_bot: team_bot_installation(bot_identifier: "smooch") {
            id
          }
        }
      }
    `,
  },
});

const MediaExpanded = (props) => {
  if (props.isTrends) {
    return <MediaExpandedComponent {...props} />;
  }

  const projectId = props.media.project_id;
  let ids = `${props.media.dbid},${projectId}`;
  if (props.media.team && props.media.team.dbid) {
    ids = `${props.media.dbid},${projectId},${props.media.team.dbid}`;
  }
  const route = new MediaRoute({ ids });

  return (
    <Relay.RootContainer
      Component={MediaExpandedContainer}
      renderFetched={data => <MediaExpandedContainer {...props} {...data} />}
      route={route}
    />
  );
};

export default withStyles(useStyles)(MediaExpanded);
<commit_msg>Ticket CHECK-1457: Fixing crash when cluster item is opened in a new tab<commit_after>import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import Relay from 'react-relay/classic';
import PropTypes from 'prop-types';
import Box from '@material-ui/core/Box';
import CardContent from '@material-ui/core/CardContent';
import CardHeader from '@material-ui/core/CardHeader';
import CardActions from '@material-ui/core/CardActions';
import Typography from '@material-ui/core/Typography';
import { withStyles } from '@material-ui/core/styles';
import MediaRoute from '../../relay/MediaRoute';
import MediaExpandedActions from './MediaExpandedActions';
import MediaExpandedArchives from './MediaExpandedArchives';
import MediaExpandedMetadata from './MediaExpandedMetadata';
import MediaExpandedSecondRow from './MediaExpandedSecondRow';
import MediaExpandedUrl from './MediaExpandedUrl';
import MediaLanguageChip from './MediaLanguageChip';
import MoreLess from '../layout/MoreLess';
import ParsedText from '../ParsedText';
import QuoteMediaCard from './QuoteMediaCard';
import WebPageMediaCard from './WebPageMediaCard';
import ImageMediaCard from './ImageMediaCard';
import MediaPlayerCard from './MediaPlayerCard';
import PenderCard from '../PenderCard';
import BlankMediaButton from './BlankMediaButton';
import UserUtil from '../user/UserUtil';
import { truncateLength } from '../../helpers';
import CheckContext from '../../CheckContext';
import { withPusher, pusherShape } from '../../pusher';
import { units, black54 } from '../../styles/js/shared';

const TypographyBlack54 = withStyles({
  root: {
    color: black54,
  },
})(Typography);

const useStyles = () => ({
  title: {
    fontSize: 14,
    lineHeight: '1.5em',
    color: 'black',
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    textDecoration: 'none',
    '&:hover': {
      color: 'black',
    },
    '&:visited': {
      color: 'black',
    },
  },
});

class MediaExpandedComponent extends Component {
  constructor(props) {
    super(props);

    this.state = {
      mediaVersion: false,
      playbackRate: 1,
    };
  }

  componentDidMount() {
    this.setContext();
    this.subscribe();
  }

  componentDidUpdate() {
    this.setContext();
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  getContext() {
    return new CheckContext(this).getContextStore();
  }

  setContext() {
    const context = new CheckContext(this);
    const { team, project } = this.props.media;
    context.setContextStore({ team, project });
  }

  subscribe() {
    const { pusher, clientSessionId, media } = this.props;
    if (pusher) {
      pusher.subscribe(media.pusher_channel).bind('media_updated', 'MediaComponent', (data, run) => {
        const annotation = JSON.parse(data.message);
        if (annotation.annotated_id === media.dbid && clientSessionId !== data.actor_session_id) {
          if (run) {
            this.props.relay.forceFetch();
            return true;
          }
          return {
            id: `media-${media.dbid}`,
            callback: this.props.relay.forceFetch,
          };
        }
        return false;
      });
    }
  }

  unsubscribe() {
    const { pusher, media } = this.props;
    if (pusher) {
      pusher.unsubscribe(media.pusher_channel);
    }
  }

  render() {
    const { classes } = this.props;
    const {
      media, playing, start, end, gaps, seekTo, scrubTo, setPlayerState, onPlayerReady, isTrends,
    } = this.props;
    const { playbackRate } = this.state;

    const {
      onTimelineCommentOpen,
      onVideoAnnoToggle,
      showVideoAnnotation,
    } = this.props;

    const currentTeam = this.getContext().team || this.getContext().currentUser.current_team;

    const currentUserRole = UserUtil.myRole(
      this.getContext().currentUser,
      currentTeam.slug,
    );

    const data = typeof media.media.metadata === 'string' ? JSON.parse(media.media.metadata) : media.media.metadata;
    const isImage = media.media.type === 'UploadedImage';
    const isMedia = ['UploadedVideo', 'UploadedAudio'].indexOf(media.media.type) > -1;
    const isYoutube = media.media.url && media.domain === 'youtube.com';
    let filePath = media.media.file_path;
    if (isYoutube) {
      filePath = media.media.url;
    }
    const isQuote = media.media.type === 'Claim';
    const isBlank = media.media.type === 'Blank';
    const isWebPage = media.media.url && data.provider === 'page';
    const isPender = media.media.url && data.provider !== 'page';
    const randomNumber = Math.floor(Math.random() * 1000000);
    const { mediaUrl, mediaQuery, linkTitle } = this.props;
    const coverImage = media.media.thumbnail_path || '/images/player_cover.svg';

    let warningType = null;
    if (media.dynamic_annotation_flag) {
      // Sort by flag category likelihood and display most likely
      let sortable = [];
      // Put custom flag at beginning of array
      if (media.dynamic_annotation_flag.data.custom) {
        sortable = sortable.concat([...Object.entries(media.dynamic_annotation_flag.data.custom)]);
      }
      const filteredFlags = {};
      ['adult', 'medical', 'violence'].forEach((key) => { filteredFlags[key] = media.dynamic_annotation_flag.data.flags[key]; });
      sortable = sortable.concat([...Object.entries(filteredFlags)]);
      sortable.sort((a, b) => b[1] - a[1]);
      const type = sortable[0];
      [warningType] = type;
    }

    const embedCard = (() => {
      if (isImage) {
        return (
          <ImageMediaCard
            key={media.dynamic_annotation_flag}
            contentWarning={media.show_warning_cover}
            warningCreator={media.dynamic_annotation_flag?.annotator?.name}
            warningCategory={warningType}
            imagePath={media.media.embed_path}
          />
        );
      } else if (isMedia || isYoutube) {
        return (

            <MediaPlayerCard
              key={media.dynamic_annotation_flag}
              filePath={filePath}
              coverImage={coverImage}
              contentWarning={media.show_warning_cover}
              warningCreator={media.dynamic_annotation_flag?.annotator?.name}
              warningCategory={warningType}
              {...{
                playing, start, end, gaps, scrubTo, seekTo, onPlayerReady, setPlayerState, playbackRate,
              }}
            />

        );
      } else if (isQuote) {
        return (
          <QuoteMediaCard
            quote={media.media.quote}
            languageCode={media.language_code}
          />
        );
      } else if (isWebPage || !data.html) {
        return (
          <WebPageMediaCard
            key={media.dynamic_annotation_flag}
            contentWarning={media.show_warning_cover}
            warningCreator={media.dynamic_annotation_flag?.annotator?.name}
            warningCategory={warningType}
            media={media}
            mediaUrl={mediaUrl}
            mediaQuery={mediaQuery}
            data={data}
          />
        );
      } else if (isPender) {
        return (
          <PenderCard
            url={media.media.url}
            fallback={null}
            domId={`pender-card-${randomNumber}`}
            mediaVersion={this.state.mediaVersion || data.refreshes_count}
          />
        );
      }

      return null;
    })();

    if (isBlank) {
      return (
        <CardContent>
          <Box
            display="flex"
            justifyContent="center"
            alignItems="center"
            style={{ minHeight: 400 }}
          >
            <BlankMediaButton
              projectMediaId={media.id}
              team={media.team}
            />
          </Box>
        </CardContent>
      );
    }

    const fileTitle = media.media.file_path ? media.media.file_path.split('/').pop().replace(/\..*$/, '') : null;
    const title = media.media.metadata.title || media.media.quote || fileTitle || media.title;
    let description = media.extracted_text ? media.extracted_text.data.text : media.media.metadata.description;
    description = media.transcription && media.transcription.data.text ? media.transcription.data.text : description;

    return (
      <React.Fragment>
        <CardHeader
          className="media-expanded__title"
          title={
            linkTitle ?

                {truncateLength(title, 110)}
               : truncateLength(title, 110)
          }
        />
        <CardContent style={{ padding: `0 ${units(2)}` }}>
          <MediaExpandedSecondRow projectMedia={media} isTrends={isTrends} />
          { isImage ?
            <Box mb={2}>
              <TypographyBlack54 variant="body2" color={black54}>
                { media.extracted_text ?
                  <FormattedMessage id="mediaExpanded.extractedText" defaultMessage="Text extracted from image:" description="Label for text extracted from the image below" /> :
                  <FormattedMessage id="mediaExpanded.noExtractedText" defaultMessage="No text extracted from this image" description="Label when text extracted from an image is not available" />
                }
              </TypographyBlack54>
            </Box> : null
          }
          { isMedia ?
            <Box mb={2}>
              <TypographyBlack54 variant="body2" color={black54}>
                { media.transcription && media.transcription.data.last_response.job_status === 'COMPLETED' ?
                  <FormattedMessage id="mediaExpanded.transcriptionCompleted" defaultMessage="Audio transcribed from media:" description="Label for transcription from audio or video" /> : null }
                { media.transcription && media.transcription.data.last_response.job_status === 'IN_PROGRESS' ?
                  <FormattedMessage id="mediaExpanded.transcriptionInProgress" defaultMessage="Audio transcription in progress…" description="Label when transcription is in progress" /> : null }
                { !media.transcription ?
                  <FormattedMessage id="mediaExpanded.noTranscription" defaultMessage="No audio transcribed from this media" description="Label when transcription is not (yet) available" /> : null }
              </TypographyBlack54>
            </Box> : null
          }
          <Box mb={2}>
            <MoreLess>
              <Typography variant="body2">
                <ParsedText text={description} />
              </Typography>
            </MoreLess>
          </Box>
          <Box mb={2}><MediaLanguageChip projectMedia={media} /></Box>
          <MediaExpandedUrl url={media.media.url} />
          <MediaExpandedArchives projectMedia={media} />
          <MediaExpandedMetadata projectMedia={media} />
          {embedCard}
        </CardContent>
        {
          isTrends ? null : (
            <CardActions>
              <MediaExpandedActions
                currentUserRole={currentUserRole}
                onTimelineCommentOpen={onTimelineCommentOpen}
                onVideoAnnoToggle={onVideoAnnoToggle}
                showVideoAnnotation={showVideoAnnotation}
                projectMedia={media}
                playbackRate={playbackRate}
                onPlaybackRateChange={r => this.setState({ playbackRate: r })}
              />
            </CardActions>
          )
        }
      </React.Fragment>
    );
  }
}

MediaExpandedComponent.contextTypes = {
  store: PropTypes.object,
};

MediaExpandedComponent.propTypes = {
  pusher: pusherShape.isRequired,
  playerRef: PropTypes.shape({ current: PropTypes.instanceOf(HTMLElement) }).isRequired,
  relay: PropTypes.object.isRequired,
  /*
    FIXME refactor MediaExpanded component and children.
    Make children fragmentContainer and simplify parent query.
    Define actual propTypes shape of media for MediaExpanded.
  */
  media: PropTypes.object.isRequired,
};

const MediaExpandedContainer = Relay.createContainer(withPusher(MediaExpandedComponent), {
  initialVariables: {
    contextId: null,
  },
  fragments: {
    media: () => Relay.QL`
      fragment on ProjectMedia {
        id
        dbid
        permissions
        domain
        created_at
        last_seen
        share_count
        requests_count
        picture
        title
        description
        language_code
        language
        extracted_text: annotation(annotation_type: "extracted_text") {
          data
        }
        transcription: annotation(annotation_type: "transcription") {
          data
        }
        project_id
        pusher_channel
        full_url
        dynamic_annotation_language {
          id
        }
        show_warning_cover
        dynamic_annotation_flag {
          id
          dbid
          content
          data
          annotator {
            name
          }
        }
        ${MediaExpandedActions.getFragment('projectMedia')}
        ${MediaExpandedArchives.getFragment('projectMedia')}
        ${MediaExpandedMetadata.getFragment('projectMedia')}
        media {
          url
          type
          quote
          thumbnail_path
          file_path
          embed_path
          metadata
        }
        team {
          id
          dbid
          slug
          search_id
          verification_statuses
          get_languages
          permissions
          smooch_bot: team_bot_installation(bot_identifier: "smooch") {
            id
          }
        }
      }
    `,
  },
});

const MediaExpanded = (props) => {
  if (props.isTrends) {
    return <MediaExpandedComponent {...props} />;
  }

  const projectId = props.media.project_id;
  let ids = `${props.media.dbid},${projectId}`;
  if (props.media.team && props.media.team.dbid) {
    ids = `${props.media.dbid},${projectId},${props.media.team.dbid}`;
  }
  const route = new MediaRoute({ ids });

  return (
    <Relay.RootContainer
      Component={MediaExpandedContainer}
      renderFetched={data => <MediaExpandedContainer {...props} {...data} />}
      route={route}
    />
  );
};

export default withStyles(useStyles)(MediaExpanded);
<|endoftext|>


<commit_before>/*
* µmarkdown.js
* markdown in under 5kb
*
* Copyright 2014, Simon Waldherr - http://simon.waldherr.eu/
* Released under the MIT Licence
* http://simon.waldherr.eu/license/mit/
*
* Github:  https://github.com/simonwaldherr/micromarkdown.js/
* Version: 0.1.6
*/

/*jslint browser: true, plusplus: true, indent: 2, regexp: true, ass: true */
/*global ActiveXObject */

var micromarkdown = {
  useajax: false,
  regexobject: {
    headline:   /^(\#{1,6})([^\#\n]+)$/m,
    code:       /\s\`\`\`\n?([^`]+)\`\`\`/g,
    hr:         /\n(?:([\*\-_] ?)+)\1\1$/gm,
    lists:      /^(( *(\*|\d\.) [^\n]+)\n)+/gm,
    bolditalic: /(?:([\*_~]{1,3}))([^\*_~\n]+[^\*_~\s])\1/g,
    links:      /!?\[([^\]<>]+)\]\(([^ \)<>]+)( "[^\(\)\"]+")?\)/g,
    reflinks:   /\[([^\]]+)\]\[([^\]]+)\]/g,
    mail:       /<(([a-z0-9_\-\.])+\@([a-z0-9_\-\.])+\.([a-z]{2,7}))>/gmi,
    tables:     /\n(([^|\n]+ *\| *)+([^|\n]+\n))((:?\-+:?\|)+(:?\-+:?)*\n)((([^|\n]+ *\| *)+([^|\n]+)\n)+)/g,
    include:    /[\[<]include (\S+) from (https?:\/\/[a-z0-9\.\-]+\.[a-z]{2,9}[a-z0-9\.\-\?\&\/]+)[\]>]/gi,
    url:        /<([a-zA-Z0-9@:%_\+.~#?&\/\/=]{2,256}\.[a-z]{2,4}\b(\/[\-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)?)>/g
  },
  parse: function (str) {
    'use strict';
    var line, nstatus, status, cel, calign, helper, helper1, helper2, count, repstr, stra, trashgc = [], casca = 0, i = 0, j = 0;
    str = '\n' + str + '\n';

    /* code */
    while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
      str = str.replace(stra[0], '\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '
').replace(/\ /gm, ' ') + '\n');
    }

    /* headlines */
    while ((stra = micromarkdown.regexobject.headline.exec(str)) !== null) {
      count = stra[1].length;
      str = str.replace(stra[0], '<h' + count + '>' + stra[2] + '</h' + count + '>' + '\n');
    }

    /* horizontal line */
    while ((stra = micromarkdown.regexobject.hr.exec(str)) !== null) {
      str = str.replace(stra[0], '\n
\n');
    }

    /* lists */
    while ((stra = micromarkdown.regexobject.lists.exec(str)) !== null) {
      casca = 0;
      if (stra[0].trim().substr(0, 1) === '*') {
        repstr = '
';
      } else {
        repstr = '
';
      }
      helper = stra[0].split('\n');
      status = 0;
      for (i = 0; i < helper.length; i++) {
        if ((line = /^(( )*(\*|\d\.) ([^\n]+))/.exec(helper[i])) !== null) {
          if (line[2] === undefined) {
            nstatus = 0;
          } else {
            nstatus = line[2].length;
          }
          if (status > nstatus) {
            repstr += '
';
            status = nstatus;
            casca--;
          }
          if (status < nstatus) {
            repstr += '
';
            status = nstatus;
            casca++;
          }
          repstr += '
' + line[4] + '
' + '\n';
        }
      }
      while (casca > 0) {
        repstr += '
';
        casca--;
      }
      if (stra[0].trim().substr(0, 1) === '*') {
        repstr += '
';
      } else {
        repstr += '
';
      }
      str = str.replace(stra[0], repstr + '\n');
    }

    /* tables */
    while ((stra = micromarkdown.regexobject.tables.exec(str)) !== null) {
      repstr = '

';
      helper = stra[1].split('|');
      calign = stra[4].split('|');
      for (i = 0; i < helper.length; i++) {
        if (calign.length <= i) {
          calign.push(0);
        } else if (calign[i].trimRight().slice(-1) === ':') {
          if (calign[i][0] === ':') {
            calign[i] = 3;
          } else {
            calign[i] = 2;
          }
        } else {
          if (calign[i][0] === ':') {
            calign[i] = 1;
          } else {
            calign[i] = 0;
          }
        }
      }
      cel = ['', '', '', ''];
      for (i = 0; i < helper.length; i++) {
        repstr += cel[calign[i]] + helper[i].trim() + '	';
      }
      repstr += '
';
      cel = ['', '', '', ''];
      helper1 = stra[7].split('\n');
      for (i = 0; i < helper1.length; i++) {
        helper2 = helper1[i].split('|');
        if (helper2[0].length !== 0) {
          while (calign.length < helper2.length) {
            calign.push(0);
          }
          repstr += '
';
          for (j = 0; j < helper2.length; j++) {
            repstr += cel[calign[j]] + helper2[j].trim() + '	';
          }
          repstr += '
' + '\n';
        }
      }
      repstr += '
';
      str = str.replace(stra[0], repstr);
    }

    /* links */
    while ((stra = micromarkdown.regexobject.links.exec(str)) !== null) {
      if (stra[0].substr(0, 1) === '!') {
        str = str.replace(stra[0], '\n');
      } else {
        str = str.replace(stra[0], '' + stra[1] + '\n');
      }
    }
    while ((stra = micromarkdown.regexobject.mail.exec(str)) !== null) {
      str = str.replace(stra[0], '' + stra[1] + '');
    }
    while ((stra = micromarkdown.regexobject.url.exec(str)) !== null) {
      repstr = stra[1];
      if (repstr.indexOf('://') === -1) {
        repstr = 'http://' + repstr;
      }
      str = str.replace(stra[0], '' + repstr.replace(/(https:\/\/|http:\/\/|mailto:|ftp:\/\/)/gmi, '') + '');
    }
    while ((stra = micromarkdown.regexobject.reflinks.exec(str)) !== null) {
      helper1 = new RegExp('\\[' + stra[2] + '\\]: ?([^ \n]+)', "gi");
      if ((helper = helper1.exec(str)) !== null) {
        str = str.replace(stra[0], '' + stra[1] + '');
        trashgc.push(helper[0]);
      }
    }
    for (i = 0; i < trashgc.length; i++) {
      str = str.replace(trashgc[i], '');
    }

    /* bold and italic */
    for (i = 0; i < 3; i++) {
      while ((stra = micromarkdown.regexobject.bolditalic.exec(str)) !== null) {
        repstr = [];
        if (stra[1] === '~~') {
          str = str.replace(stra[0], '' + stra[2] + '');
        } else {
          switch (stra[1].length) {
          case 1:
            repstr = ['', ''];
            break;
          case 2:
            repstr = ['', ''];
            break;
          case 3:
            repstr = ['', ''];
            break;
          }
          str = str.replace(stra[0], repstr[0] + stra[2] + repstr[1]);
        }
      }
    }

    /* include */
    if (micromarkdown.useajax !== false) {
      while ((stra = micromarkdown.regexobject.include.exec(str)) !== null) {
        helper = stra[2].replace(/[\.\:\/]+/gm, '');
        helper1 = '';
        if (document.getElementById(helper)) {
          helper1 = document.getElementById(helper).innerHTML.trim();
        } else {
          micromarkdown.ajax(stra[2]);
        }
        if ((stra[1] === 'csv') && (helper1 !== '')) {
          helper2 = {};
          helper2[';'] = [];
          helper2[','] = [];
          helper2[0] = [';', ','];
          helper1 = helper1.split('\n');
          for (j = 0; j < helper2[0].length; j++) {
            for (i = 0; i < helper1.length; i++) {
              helper2[helper2[0][j]][i] = helper1[i].split(helper2[0][j]).length;
              if (i > 0) {
                if (helper2[helper2[0][j]] !== false) {
                  if ((helper2[helper2[0][j]][i] !== helper2[helper2[0][j]][i - 1]) || (helper2[helper2[0][j]][i] === 1)) {
                    helper2[helper2[0][j]] = false;
                  }
                }
              }
            }
          }
          if ((helper2[','] !== false) || (helper2[';'] !== false)) {
            if (helper2[';'] !== false) {
              helper2 = ';';
            } else {
              helper2 = ',';
            }
            repstr = '
';
            for (i = 0; i < helper1.length; i++) {
              helper = helper1[i].split(helper2);
              repstr += '
';
              for (j = 0; j < helper.length; j++) {
                repstr += '' + micromarkdown.htmlEncode(helper[j]) + '	';
              }
              repstr += '
';
            }
            repstr += '
';
            str = str.replace(stra[0], repstr);
          } else {
            str = str.replace(stra[0], '' + helper1.join('\n') + '');
          }
        } else {
          str = str.replace(stra[0], '');
        }
      }
    }

    str = str.replace(/ {2,}[\n]{1,}/gmi, '

');
    return str;
  },
  ajax: function (str) {
    'use strict';
    var xhr;
    if (document.getElementById(str.replace(/[\.\:\/]+/gm, ''))) {
      return false;
    }
    if (window.ActiveXObject) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e) {
        xhr = null;
        return e;
      }
    } else {
      xhr = new XMLHttpRequest();
    }
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        var ele = document.createElement('code');
        ele.innerHTML = xhr.responseText;
        ele.id = str.replace(/[\.\:\/]+/gm, '');
        ele.style.display = 'none';
        document.getElementsByTagName('body')[0].appendChild(ele);
        micromarkdown.useajax();
      }
    };
    xhr.open('GET', str, true);
    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhr.send();
  },
  countingChars: function (string, split) {
    'use strict';
    string = string.split(split);
    if (typeof string === 'object') {
      return string.length - 1;
    }
    return 0;
  },
  htmlEncode: function (str) {
    'use strict';
    return str.replace(/[^a-zA-Z0-9\.:\n]/g, function (c) {
      return '&' + (micromarkdown.entityTable[c.charCodeAt(0)] || '#' + c.charCodeAt(0)) + ';';
    });
  },
  entityTable: {32: 'nbsp', 34: 'quot', 38: 'amp', 39: 'apos', 60: 'lt', 62: 'gt', 161: 'iexcl', 162: 'cent', 163: 'pound', 164: 'curren', 165: 'yen', 166: 'brvbar', 167: 'sect', 168: 'uml', 169: 'copy', 170: 'ordf', 171: 'laquo', 172: 'not', 173: 'shy', 174: 'reg', 175: 'macr', 176: 'deg', 177: 'plusmn', 178: 'sup2', 179: 'sup3', 180: 'acute', 181: 'micro', 196: 'Auml', 203: 'Euml', 207: 'Iuml', 214: 'Ouml', 220: 'Uuml', 223: 'szlig', 228: 'auml', 235: 'euml', 239: 'iuml', 246: 'ouml', 252: 'uuml', 255: 'yuml', 913: 'Alpha', 914: 'Beta', 915: 'Gamma', 916: 'Delta', 917: 'Epsilon', 918: 'Zeta', 919: 'Eta', 920: 'Theta', 921: 'Iota', 922: 'Kappa', 923: 'Lambda', 924: 'Mu', 925: 'Nu', 926: 'Xi', 927: 'Omicron', 928: 'Pi', 929: 'Rho', 931: 'Sigma', 932: 'Tau', 933: 'Upsilon', 934: 'Phi', 935: 'Chi', 936: 'Psi', 937: 'Omega', 945: 'alpha', 946: 'beta', 947: 'gamma', 948: 'delta', 949: 'epsilon', 950: 'zeta', 951: 'eta', 952: 'theta', 953: 'iota', 954: 'kappa', 955: 'lambda', 956: 'mu', 957: 'nu', 958: 'xi', 959: 'omicron', 960: 'pi', 961: 'rho', 962: 'sigmaf', 963: 'sigma', 964: 'tau', 965: 'upsilon', 966: 'phi', 967: 'chi', 968: 'psi', 969: 'omega', 8364: 'euro'}
};
<commit_msg>fixes #3 nested lists<commit_after>/*
* µmarkdown.js
* markdown in under 5kb
*
* Copyright 2014, Simon Waldherr - http://simon.waldherr.eu/
* Released under the MIT Licence
* http://simon.waldherr.eu/license/mit/
*
* Github:  https://github.com/simonwaldherr/micromarkdown.js/
* Version: 0.1.6
*/

/*jslint browser: true, plusplus: true, indent: 2, regexp: true, ass: true */
/*global ActiveXObject */

var micromarkdown = {
  useajax: false,
  regexobject: {
    headline:   /^(\#{1,6})([^\#\n]+)$/m,
    code:       /\s\`\`\`\n?([^`]+)\`\`\`/g,
    hr:         /^(?:([\*\-_] ?)+)\1\1$/gm,
    lists:      /^((\s*(\*|\d\.) [^\n]+)\n)+/gm,
    bolditalic: /(?:([\*_~]{1,3}))([^\*_~\n]+[^\*_~\s])\1/g,
    links:      /!?\[([^\]<>]+)\]\(([^ \)<>]+)( "[^\(\)\"]+")?\)/g,
    reflinks:   /\[([^\]]+)\]\[([^\]]+)\]/g,
    mail:       /<(([a-z0-9_\-\.])+\@([a-z0-9_\-\.])+\.([a-z]{2,7}))>/gmi,
    tables:     /\n(([^|\n]+ *\| *)+([^|\n]+\n))((:?\-+:?\|)+(:?\-+:?)*\n)((([^|\n]+ *\| *)+([^|\n]+)\n)+)/g,
    include:    /[\[<]include (\S+) from (https?:\/\/[a-z0-9\.\-]+\.[a-z]{2,9}[a-z0-9\.\-\?\&\/]+)[\]>]/gi,
    url:        /<([a-zA-Z0-9@:%_\+.~#?&\/\/=]{2,256}\.[a-z]{2,4}\b(\/[\-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)?)>/g
  },
  parse: function (str) {
    'use strict';
    var line, nstatus = 0, status, cel, calign, indent, helper, helper1, helper2, count, repstr, stra, trashgc = [], casca = 0, i = 0, j = 0;
    str = '\n' + str + '\n';

    /* code */
    while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
      str = str.replace(stra[0], '\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '
').replace(/\ /gm, ' ') + '\n');
    }

    /* headlines */
    while ((stra = micromarkdown.regexobject.headline.exec(str)) !== null) {
      count = stra[1].length;
      str = str.replace(stra[0], '<h' + count + '>' + stra[2] + '</h' + count + '>' + '\n');
    }

    /* lists */
    while ((stra = micromarkdown.regexobject.lists.exec(str)) !== null) {
      casca = 0;
      if (stra[0].trim().substr(0, 1) === '*') {
        repstr = '
';
      } else {
        repstr = '
';
      }
      helper = stra[0].split('\n');
      helper1 = [];
      status = 0;
      indent = false;
      for (i = 0; i < helper.length; i++) {
        if ((line = /^((\s*)(\*|\d\.) ([^\n]+))/.exec(helper[i])) !== null) {
          if ((line[2] === undefined)||(line[2].length === 0)) {
            nstatus = 0;
          } else {
            if (indent === false) {
              indent = line[2].replace(/\t/, '    ').length;
            }
            nstatus = Math.round(line[2].replace(/\t/, '    ').length / indent);
          }
          while (status > nstatus) {
            repstr += helper1.pop();
            status--;
            casca--;
          }
          while (status < nstatus) {
            if (line[0].trim().substr(0, 1) === '*') {
              repstr += '
';
              helper1.push('
');
            } else {
              repstr += '
';
              helper1.push('
');
            }
            status++;
            casca++;
          }
          repstr += '
' + line[4] + '
' + '\n';
        }
      }
      while (casca > 0) {
        repstr += '
';
        casca--;
      }
      if (stra[0].trim().substr(0, 1) === '*') {
        repstr += '
';
      } else {
        repstr += '
';
      }
      str = str.replace(stra[0], repstr + '\n');
    }

    /* tables */
    while ((stra = micromarkdown.regexobject.tables.exec(str)) !== null) {
      repstr = '

';
      helper = stra[1].split('|');
      calign = stra[4].split('|');
      for (i = 0; i < helper.length; i++) {
        if (calign.length <= i) {
          calign.push(0);
        } else if (calign[i].trimRight().slice(-1) === ':') {
          if (calign[i][0] === ':') {
            calign[i] = 3;
          } else {
            calign[i] = 2;
          }
        } else {
          if (calign[i][0] === ':') {
            calign[i] = 1;
          } else {
            calign[i] = 0;
          }
        }
      }
      cel = ['', '', '', ''];
      for (i = 0; i < helper.length; i++) {
        repstr += cel[calign[i]] + helper[i].trim() + '	';
      }
      repstr += '
';
      cel = ['', '', '', ''];
      helper1 = stra[7].split('\n');
      for (i = 0; i < helper1.length; i++) {
        helper2 = helper1[i].split('|');
        if (helper2[0].length !== 0) {
          while (calign.length < helper2.length) {
            calign.push(0);
          }
          repstr += '
';
          for (j = 0; j < helper2.length; j++) {
            repstr += cel[calign[j]] + helper2[j].trim() + '	';
          }
          repstr += '
' + '\n';
        }
      }
      repstr += '
';
      str = str.replace(stra[0], repstr);
    }

    /* links */
    while ((stra = micromarkdown.regexobject.links.exec(str)) !== null) {
      if (stra[0].substr(0, 1) === '!') {
        str = str.replace(stra[0], '\n');
      } else {
        str = str.replace(stra[0], '' + stra[1] + '\n');
      }
    }
    while ((stra = micromarkdown.regexobject.mail.exec(str)) !== null) {
      str = str.replace(stra[0], '' + stra[1] + '');
    }
    while ((stra = micromarkdown.regexobject.url.exec(str)) !== null) {
      repstr = stra[1];
      if (repstr.indexOf('://') === -1) {
        repstr = 'http://' + repstr;
      }
      str = str.replace(stra[0], '' + repstr.replace(/(https:\/\/|http:\/\/|mailto:|ftp:\/\/)/gmi, '') + '');
    }
    while ((stra = micromarkdown.regexobject.reflinks.exec(str)) !== null) {
      helper1 = new RegExp('\\[' + stra[2] + '\\]: ?([^ \n]+)', "gi");
      if ((helper = helper1.exec(str)) !== null) {
        str = str.replace(stra[0], '' + stra[1] + '');
        trashgc.push(helper[0]);
      }
    }
    for (i = 0; i < trashgc.length; i++) {
      str = str.replace(trashgc[i], '');
    }

    /* bold and italic */
    for (i = 0; i < 3; i++) {
      while ((stra = micromarkdown.regexobject.bolditalic.exec(str)) !== null) {
        repstr = [];
        if (stra[1] === '~~') {
          str = str.replace(stra[0], '' + stra[2] + '');
        } else {
          switch (stra[1].length) {
          case 1:
            repstr = ['', ''];
            break;
          case 2:
            repstr = ['', ''];
            break;
          case 3:
            repstr = ['', ''];
            break;
          }
          str = str.replace(stra[0], repstr[0] + stra[2] + repstr[1]);
        }
      }
    }

    /* horizontal line */
    while ((stra = micromarkdown.regexobject.hr.exec(str)) !== null) {
      str = str.replace(stra[0], '\n
\n');
    }

    /* include */
    if (micromarkdown.useajax !== false) {
      while ((stra = micromarkdown.regexobject.include.exec(str)) !== null) {
        helper = stra[2].replace(/[\.\:\/]+/gm, '');
        helper1 = '';
        if (document.getElementById(helper)) {
          helper1 = document.getElementById(helper).innerHTML.trim();
        } else {
          micromarkdown.ajax(stra[2]);
        }
        if ((stra[1] === 'csv') && (helper1 !== '')) {
          helper2 = {};
          helper2[';'] = [];
          helper2[','] = [];
          helper2[0] = [';', ','];
          helper1 = helper1.split('\n');
          for (j = 0; j < helper2[0].length; j++) {
            for (i = 0; i < helper1.length; i++) {
              if (i > 0) {
                if (helper2[helper2[0][j]] !== false) {
                  if ((helper2[helper2[0][j]][i] !== helper2[helper2[0][j]][i - 1]) || (helper2[helper2[0][j]][i] === 1)) {
                    helper2[helper2[0][j]] = false;
                  }
                }
              }
            }
          }
          if ((helper2[','] !== false) || (helper2[';'] !== false)) {
            if (helper2[';'] !== false) {
              helper2 = ';';
            } else {
              helper2 = ',';
            }
            repstr = '
';
            for (i = 0; i < helper1.length; i++) {
              helper = helper1[i].split(helper2);
              repstr += '
';
              for (j = 0; j < helper.length; j++) {
                repstr += '' + micromarkdown.htmlEncode(helper[j]) + '	';
              }
              repstr += '
';
            }
            repstr += '
';
            str = str.replace(stra[0], repstr);
          } else {
            str = str.replace(stra[0], '' + helper1.join('\n') + '');
          }
        } else {
          str = str.replace(stra[0], '');
        }
      }
    }

    str = str.replace(/ {2,}[\n]{1,}/gmi, '

');
    return str;
  },
  ajax: function (str) {
    'use strict';
    var xhr;
    if (document.getElementById(str.replace(/[\.\:\/]+/gm, ''))) {
      return false;
    }
    if (window.ActiveXObject) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e) {
        xhr = null;
        return e;
      }
    } else {
      xhr = new XMLHttpRequest();
    }
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        var ele = document.createElement('code');
        ele.innerHTML = xhr.responseText;
        ele.id = str.replace(/[\.\:\/]+/gm, '');
        ele.style.display = 'none';
        document.getElementsByTagName('body')[0].appendChild(ele);
        micromarkdown.useajax();
      }
    };
    xhr.open('GET', str, true);
    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhr.send();
  },
  countingChars: function (string, split) {
    'use strict';
    string = string.split(split);
    if (typeof string === 'object') {
      return string.length - 1;
    }
    return 0;
  },
  htmlEncode: function (str) {
    'use strict';
    return str.replace(/[^a-zA-Z0-9\.:\n]/g, function (c) {
      return '&' + (micromarkdown.entityTable[c.charCodeAt(0)] || '#' + c.charCodeAt(0)) + ';';
    });
  },
  entityTable: {32: 'nbsp', 34: 'quot', 38: 'amp', 39: 'apos', 60: 'lt', 62: 'gt', 161: 'iexcl', 162: 'cent', 163: 'pound', 164: 'curren', 165: 'yen', 166: 'brvbar', 167: 'sect', 168: 'uml', 169: 'copy', 170: 'ordf', 171: 'laquo', 172: 'not', 173: 'shy', 174: 'reg', 175: 'macr', 176: 'deg', 177: 'plusmn', 178: 'sup2', 179: 'sup3', 180: 'acute', 181: 'micro', 196: 'Auml', 203: 'Euml', 207: 'Iuml', 214: 'Ouml', 220: 'Uuml', 223: 'szlig', 228: 'auml', 235: 'euml', 239: 'iuml', 246: 'ouml', 252: 'uuml', 255: 'yuml', 913: 'Alpha', 914: 'Beta', 915: 'Gamma', 916: 'Delta', 917: 'Epsilon', 918: 'Zeta', 919: 'Eta', 920: 'Theta', 921: 'Iota', 922: 'Kappa', 923: 'Lambda', 924: 'Mu', 925: 'Nu', 926: 'Xi', 927: 'Omicron', 928: 'Pi', 929: 'Rho', 931: 'Sigma', 932: 'Tau', 933: 'Upsilon', 934: 'Phi', 935: 'Chi', 936: 'Psi', 937: 'Omega', 945: 'alpha', 946: 'beta', 947: 'gamma', 948: 'delta', 949: 'epsilon', 950: 'zeta', 951: 'eta', 952: 'theta', 953: 'iota', 954: 'kappa', 955: 'lambda', 956: 'mu', 957: 'nu', 958: 'xi', 959: 'omicron', 960: 'pi', 961: 'rho', 962: 'sigmaf', 963: 'sigma', 964: 'tau', 965: 'upsilon', 966: 'phi', 967: 'chi', 968: 'psi', 969: 'omega', 8364: 'euro'}
};
<|endoftext|>


<commit_before>/*!
 * Samsaara Local Group Constructor
 * Copyright(c) 2014 Arjun Mehta <arjun@newlief.com>
 * MIT Licensed
 */

var debug = require('debugit').add('samsaara:groups:Group');

var samsaara;
var execute;

function initialize(core, coreExecute) {
    samsaara = core;
    execute = coreExecute;

    return Group;
}

function Group(groupName, members) {

    if (Array.isArray(members)) {
        members = convertToObj(members);
    }

    this.members = members || {};
    this.id = groupName;
    this.count = Object.keys(this.members).length;
}

Group.prototype.add = function(connection) {

    var connectionID = connection.id;
    var groupName = this.id;

    if (this.members[connectionID] === undefined) {

        this.count++;
        this.members[connectionID] = true;
        connection.groups[groupName] = true;

        connection.nameSpace('samsaaraGroups').execute('addedToGroup')(this.id);

        debug('Member', connectionID, 'added to:', groupName);

        return true;
    }

    return false;
};

Group.prototype.remove = function(connection) {

    var connectionID = connection.id;

    if (this.members[connectionID] !== undefined) {
        delete connection.groups[this.id];
        delete this.members[connectionID];
        this.count--;
    }
};

// creates a namespace object that holds an execute method with the namespace as a closure..

Group.prototype.nameSpace = function(namespaceName) {

    var memberList = Object.keys(this.members);

    return {
        execute: function(funcName) {
            execute(createSendClosure(memberList), 'GRP', memberList, namespaceName, funcName, arguments);
        }
    };
};

Group.prototype.execute = function(funcName) {
    var memberList = Object.keys(this.members);
    execute(createSendClosure(memberList), 'GRP', memberList, 'core', funcName, arguments);
};

Group.prototype.except = function(exceptionArray) {

    var exceptions = convertToObj(exceptionArray);
    var membersList = Object.keys(this.members);
    var filteredMembers = {};
    var i;

    for (i = 0; i < membersList.length; i++) {
        if (exceptions[membersList[i]] !== true) {
            filteredMembers[membersList[i]] = true;
        }
    }

    return new Group('filtered_' + this.id, filteredMembers);
};

function createSendClosure(memberList) {

    return {
        send: function(packet) {
            var i;

            for (i = 0; i < memberList.length; i++) {
                if (samsaara.connection(memberList[i]) !== undefined) {
                    samsaara.connection(memberList[i]).socket.send(packet);
                }
            }
        }
    };
}

function convertToObj(array) {

    var obj = {};
    var i;

    for (i = 0; i < array.length; i++) {
        obj[array[i]] = true;
    }

    return obj;
}

module.exports = exports = {
    initialize: initialize,
    Constructor: Group
};
<commit_msg>Leveraging var name.<commit_after>/*!
 * Samsaara Local Group Constructor
 * Copyright(c) 2014 Arjun Mehta <arjun@newlief.com>
 * MIT Licensed
 */

var debug = require('debugit').add('samsaara:groups:Group');

var samsaara;
var execute;

function initialize(core, coreExecute) {
    samsaara = core;
    execute = coreExecute;

    return Group;
}

function Group(groupName, members) {

    if (Array.isArray(members)) {
        members = convertToObj(members);
    }

    this.members = members || {};
    this.id = groupName;
    this.count = Object.keys(this.members).length;
}

Group.prototype.add = function(connection) {

    var connectionID = connection.id;
    var groupName = this.id;

    if (this.members[connectionID] === undefined) {

        this.count++;
        this.members[connectionID] = true;
        connection.groups[groupName] = true;

        connection.nameSpace('samsaaraGroups').execute('addedToGroup')(groupName);

        debug('Member', connectionID, 'added to:', groupName);

        return true;
    }

    return false;
};

Group.prototype.remove = function(connection) {

    var connectionID = connection.id;

    if (this.members[connectionID] !== undefined) {
        delete connection.groups[this.id];
        delete this.members[connectionID];
        this.count--;
    }
};

// creates a namespace object that holds an execute method with the namespace as a closure..

Group.prototype.nameSpace = function(namespaceName) {

    var memberList = Object.keys(this.members);

    return {
        execute: function(funcName) {
            execute(createSendClosure(memberList), 'GRP', memberList, namespaceName, funcName, arguments);
        }
    };
};

Group.prototype.execute = function(funcName) {
    var memberList = Object.keys(this.members);
    execute(createSendClosure(memberList), 'GRP', memberList, 'core', funcName, arguments);
};

Group.prototype.except = function(exceptionArray) {

    var exceptions = convertToObj(exceptionArray);
    var membersList = Object.keys(this.members);
    var filteredMembers = {};
    var i;

    for (i = 0; i < membersList.length; i++) {
        if (exceptions[membersList[i]] !== true) {
            filteredMembers[membersList[i]] = true;
        }
    }

    return new Group('filtered_' + this.id, filteredMembers);
};

function createSendClosure(memberList) {

    return {
        send: function(packet) {
            var i;

            for (i = 0; i < memberList.length; i++) {
                if (samsaara.connection(memberList[i]) !== undefined) {
                    samsaara.connection(memberList[i]).socket.send(packet);
                }
            }
        }
    };
}

function convertToObj(array) {

    var obj = {};
    var i;

    for (i = 0; i < array.length; i++) {
        obj[array[i]] = true;
    }

    return obj;
}

module.exports = exports = {
    initialize: initialize,
    Constructor: Group
};
<|endoftext|>


<commit_before>const { assign, filter, flatten } = require('lodash')

const metadataRepo = require('../../../lib/metadata')
const { saveCompany } = require('../repos')
const { transformObjectToOption } = require('../../transformers')
const { transformCompanyToExportDetailsView } = require('../transformers')
const { exportDetailsLabels } = require('../labels')

function renderExports (req, res) {
  const exportDetails = transformCompanyToExportDetailsView(res.locals.company)

  res
    .breadcrumb('Exports')
    .render('companies/views/exports-view', {
      exportDetails,
    })
}

function populateExportForm (req, res, next) {
  const {
    export_to_countries,
    future_interest_countries,
    export_experience_category,
  } = res.locals.company

  res.locals.formData = assign({}, {
    export_experience_category,
    export_to_countries: export_to_countries.map(country => country.id),
    future_interest_countries: future_interest_countries.map(country => country.id),
  }, req.body)

  next()
}

function renderExportEdit (req, res) {
  const { id, name } = res.locals.company

  res
    .breadcrumb(name, `/companies/${id}`)
    .breadcrumb('Exports', `/companies/${id}/exports`)
    .breadcrumb('Edit')
    .render('companies/views/exports-edit', {
      exportDetailsLabels,
      exportExperienceCategories: metadataRepo.exportExperienceCategory.map(transformObjectToOption),
      countryOptions: metadataRepo.countryOptions.map(transformObjectToOption),
    })
}

async function handleEditFormPost (req, res, next) {
  const exportToCountries = flatten([req.body.export_to_countries])
  const futureInterestCountries = flatten([req.body.future_interest_countries])

  const data = assign({}, res.locals.company, {
    export_experience_category: req.body.export_experience_category,
    export_to_countries: filter(exportToCountries),
    future_interest_countries: filter(futureInterestCountries),
  })

  try {
    const save = await saveCompany(req.session.token, data)

    res.redirect(`/companies/${save.id}/exports`)
  } catch (error) {
    next(error)
  }
}

module.exports = {
  populateExportForm,
  renderExports,
  renderExportEdit,
  handleEditFormPost,
}
<commit_msg>Add missing item in company exports<commit_after>const { assign, filter, flatten } = require('lodash')

const metadataRepo = require('../../../lib/metadata')
const { saveCompany } = require('../repos')
const { transformObjectToOption } = require('../../transformers')
const { transformCompanyToExportDetailsView } = require('../transformers')
const { exportDetailsLabels } = require('../labels')

function renderExports (req, res) {
  const { id, name } = res.locals.company
  const exportDetails = transformCompanyToExportDetailsView(res.locals.company)

  res
    .breadcrumb(name, `/companies/${id}`)
    .breadcrumb('Exports')
    .render('companies/views/exports-view', {
      exportDetails,
    })
}

function populateExportForm (req, res, next) {
  const {
    export_to_countries,
    future_interest_countries,
    export_experience_category,
  } = res.locals.company

  res.locals.formData = assign({}, {
    export_experience_category,
    export_to_countries: export_to_countries.map(country => country.id),
    future_interest_countries: future_interest_countries.map(country => country.id),
  }, req.body)

  next()
}

function renderExportEdit (req, res) {
  const { id, name } = res.locals.company

  res
    .breadcrumb(name, `/companies/${id}`)
    .breadcrumb('Exports', `/companies/${id}/exports`)
    .breadcrumb('Edit')
    .render('companies/views/exports-edit', {
      exportDetailsLabels,
      exportExperienceCategories: metadataRepo.exportExperienceCategory.map(transformObjectToOption),
      countryOptions: metadataRepo.countryOptions.map(transformObjectToOption),
    })
}

async function handleEditFormPost (req, res, next) {
  const exportToCountries = flatten([req.body.export_to_countries])
  const futureInterestCountries = flatten([req.body.future_interest_countries])

  const data = assign({}, res.locals.company, {
    export_experience_category: req.body.export_experience_category,
    export_to_countries: filter(exportToCountries),
    future_interest_countries: filter(futureInterestCountries),
  })

  try {
    const save = await saveCompany(req.session.token, data)

    res.redirect(`/companies/${save.id}/exports`)
  } catch (error) {
    next(error)
  }
}

module.exports = {
  populateExportForm,
  renderExports,
  renderExportEdit,
  handleEditFormPost,
}
<|endoftext|>


<commit_before>/*
 * haibu.js: Top level include for the haibu module
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    path = require('path'),
    flatiron = require('flatiron'),
    semver = require('semver');

var haibu = module.exports = new flatiron.App({
  delimiter: ':',
  root: path.join(__dirname, '..'),
  directories: {
    apps: '#ROOT/local',
    autostart: '#ROOT/autostart',
    config: '#ROOT/config',
    packages: '#ROOT/packages',
    tmp: '#ROOT/tmp'
  }
});

//
// Expose version through `pkginfo`.
//
require('pkginfo')(module, 'version');

//
// Set the allowed executables
//
haibu.config.set('allowedExecutables', ['node', 'coffee']);

haibu.common     = haibu.utils = require('./haibu/common');
haibu.Spawner    = require('./haibu/core/spawner').Spawner;
haibu.repository = require('./haibu/repositories');
haibu.drone      = require('./haibu/drone');
haibu.running    = {};

haibu.sendResponse = function sendResponse(res, status, body) {
  return res.json(status, body);
}

//
// Expose the `chroot` plugin as a lazy-loaded getter
//
haibu.__defineGetter__('chroot', function () {
  return require('./haibu/plugins/chroot');
});
haibu.__defineGetter__('coffee', function () {
  return require('./haibu/plugins/coffee');
});
haibu.__defineGetter__('advanced-replies', function () {
  return require('./haibu/plugins/advanced-replies');
});
<commit_msg>[api fix] Use `flatiron.plugins.exceptions`<commit_after>/*
 * haibu.js: Top level include for the haibu module
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    path = require('path'),
    flatiron = require('flatiron'),
    semver = require('semver');

var haibu = module.exports = new flatiron.App({
  delimiter: ':',
  root: path.join(__dirname, '..'),
  directories: {
    apps: '#ROOT/local',
    autostart: '#ROOT/autostart',
    config: '#ROOT/config',
    packages: '#ROOT/packages',
    tmp: '#ROOT/tmp'
  }
});

haibu.use(flatiron.plugins.exceptions);

//
// Expose version through `pkginfo`.
//
require('pkginfo')(module, 'version');

//
// Set the allowed executables
//
haibu.config.set('allowedExecutables', ['node', 'coffee']);

haibu.common     = haibu.utils = require('./haibu/common');
haibu.Spawner    = require('./haibu/core/spawner').Spawner;
haibu.repository = require('./haibu/repositories');
haibu.drone      = require('./haibu/drone');
haibu.running    = {};

haibu.sendResponse = function sendResponse(res, status, body) {
  return res.json(status, body);
}

//
// Expose the `chroot` plugin as a lazy-loaded getter
//
haibu.__defineGetter__('chroot', function () {
  return require('./haibu/plugins/chroot');
});
haibu.__defineGetter__('coffee', function () {
  return require('./haibu/plugins/coffee');
});
haibu.__defineGetter__('advanced-replies', function () {
  return require('./haibu/plugins/advanced-replies');
});
<|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');

function helloRoute() {
  var hello = new express.Router();
  hello.use(cors());
  hello.use(bodyParser());

  // GET REST endpoint - query params may or may not be populated
  hello.get('/', function(req, res) {
    var world = req.query && req.query.hello ? req.query.hello : 'World';

    // see http://expressjs.com/4x/api.html#res.json
    res.json({msg: 'Hello ' + world});
  });

  // POST REST endpoint - note we use 'body-parser' middleware above to parse the request body in this route.
  // This can also be added in application.js
  // See: https://github.com/senchalabs/connect#middleware for a list of Express 4 middleware
  hello.post('/', function(req, res) {
    var world = req.body && req.body.hello ? req.body.hello : 'World';

    // see http://expressjs.com/4x/api.html#res.json
    res.json({msg: 'Hello ' + world});
  });

  return hello;
}

module.exports = helloRoute;<commit_msg>Added extra logging in routes<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');

function helloRoute() {
  var hello = new express.Router();
  hello.use(cors());
  hello.use(bodyParser());

  // GET REST endpoint - query params may or may not be populated
  hello.get('/', function(req, res) {
    console.log(new Date(), 'In hello route GET / req.query=', req.query);
    var world = req.query && req.query.hello ? req.query.hello : 'World';

    // see http://expressjs.com/4x/api.html#res.json
    res.json({msg: 'Hello ' + world});
  });

  // POST REST endpoint - note we use 'body-parser' middleware above to parse the request body in this route.
  // This can also be added in application.js
  // See: https://github.com/senchalabs/connect#middleware for a list of Express 4 middleware
  hello.post('/', function(req, res) {
    console.log(new Date(), 'In hello route POST / req.body=', req.body);
    var world = req.body && req.body.hello ? req.body.hello : 'World';

    // see http://expressjs.com/4x/api.html#res.json
    res.json({msg: 'Hello ' + world});
  });

  return hello;
}

module.exports = helloRoute;
<|endoftext|>


<commit_before>import d3 from 'glo/node_modules/d3';
import GLO from 'glo';

import VisComponent from '../../VisComponent';

const colorNodes = (glo, field, type) => {
  glo.glo.node_attr({
    [field]: type
  });

  glo.glo.color_nodes_by(field);
};

export default class Glo extends VisComponent {
  constructor (el, {nodes, edges, width = 960, height = 540}) {
    super(el);

    // Empty the top-level div.
    d3.select(this.el)
      .selectAll('*')
      .remove();

    // Construct and append an SVG element to the top-level div.
    this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    this.svg.setAttribute('width', width);
    this.svg.setAttribute('height', height);
    this.el.appendChild(this.svg);

    // Construct a GLO object.
    this.glo = new GLO.GLO(d3.select(this.svg))
      .nodes(nodes)
      .edges(edges);
  }

  render () {
    this.glo.draw();
  }

  colorNodesDiscrete (field) {
    colorNodes(this, field, 'discrete');
  }

  colorNodesContinuous (field) {
    colorNodes(this, field, 'continuous');
  }

  colorNodesDefault () {
    this.glo.color_nodes_by_constant();
  }

  sizeNodes (field) {
    this.glo.node_attr({
      [field]: 'continuous'
    });

    this.glo.size_nodes_by(field);
  }

  sizeNodesDefault () {
    this.glo.size_nodes_by_constant();
  }

  distributeNodes (axis, attr = null) {
    if (attr === null) {
      this.glo.evenly_distribute_nodes_on(axis);
    } else {
      this.glo.evenly_distribute_nodes_on(axis, {
        by: attr
      });
    }
  }

  positionNodes (axis, value) {
    this.glo.node_attr({
      [value]: 'continuous'
    });

    this.glo.position_nodes_on(axis, value);
  }

  forceDirected () {
    this.glo.apply_force_directed_algorithm_to_nodes();
  }

  showEdges () {
    this.glo.show_all_edges();
  }

  hideEdges () {
    this.glo.hide_edges();
  }

  fadeEdges () {
    this.glo.show_edges_as_faded();
  }

  solidEdges () {
    this.hideEdges();
    this.showEdges();
  }

  incidentEdges () {
    this.glo.show_incident_edges();
  }

  curvedEdges () {
    this.glo.display_edges_as_curved_lines();
  }

  straightEdges () {
    this.glo.display_edges_as_straight_lines();
  }
}
<commit_msg>refactor(Glo): disallow multiple calls to glo.draw()<commit_after>import d3 from 'glo/node_modules/d3';
import GLO from 'glo';

import VisComponent from '../../VisComponent';

const colorNodes = (glo, field, type) => {
  glo.glo.node_attr({
    [field]: type
  });

  glo.glo.color_nodes_by(field);
};

export default class Glo extends VisComponent {
  constructor (el, {nodes, edges, width = 960, height = 540}) {
    super(el);

    // Empty the top-level div.
    d3.select(this.el)
      .selectAll('*')
      .remove();

    // Construct and append an SVG element to the top-level div.
    this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    this.svg.setAttribute('width', width);
    this.svg.setAttribute('height', height);
    this.el.appendChild(this.svg);

    // Construct a GLO object.
    this.glo = new GLO.GLO(d3.select(this.svg))
      .nodes(nodes)
      .edges(edges);
  }

  render () {
    if (!this.drawn) {
      this.glo.draw();
      this.drawn = true;
    }
  }

  colorNodesDiscrete (field) {
    colorNodes(this, field, 'discrete');
  }

  colorNodesContinuous (field) {
    colorNodes(this, field, 'continuous');
  }

  colorNodesDefault () {
    this.glo.color_nodes_by_constant();
  }

  sizeNodes (field) {
    this.glo.node_attr({
      [field]: 'continuous'
    });

    this.glo.size_nodes_by(field);
  }

  sizeNodesDefault () {
    this.glo.size_nodes_by_constant();
  }

  distributeNodes (axis, attr = null) {
    if (attr === null) {
      this.glo.evenly_distribute_nodes_on(axis);
    } else {
      this.glo.evenly_distribute_nodes_on(axis, {
        by: attr
      });
    }
  }

  positionNodes (axis, value) {
    this.glo.node_attr({
      [value]: 'continuous'
    });

    this.glo.position_nodes_on(axis, value);
  }

  forceDirected () {
    this.glo.apply_force_directed_algorithm_to_nodes();
  }

  showEdges () {
    this.glo.show_all_edges();
  }

  hideEdges () {
    this.glo.hide_edges();
  }

  fadeEdges () {
    this.glo.show_edges_as_faded();
  }

  solidEdges () {
    this.hideEdges();
    this.showEdges();
  }

  incidentEdges () {
    this.glo.show_incident_edges();
  }

  curvedEdges () {
    this.glo.display_edges_as_curved_lines();
  }

  straightEdges () {
    this.glo.display_edges_as_straight_lines();
  }
}
<|endoftext|>


<commit_before>/*
 * imgur-upload
 * https://github.com/adamcoulombe/node-imgur-api
 *
 * Copyright (c) 2013 adamcoulombe
 * Licensed under the MIT license.
 */

 'use strict';

var https = require('https'),
  fs = require('fs'),
  request = require('request');
var request = request.defaults({
  json: true
});

var imgur = {
  _clientID : null,
  setClientID : function(clientID){
    this._clientID = clientID;
  },
  upload : function(_file,_cb) {
    if(this._clientID && _file){
      var options = {
        url: 'https://api.imgur.com/3/upload',
        headers: {
          'Authorization': 'Client-ID ' + this._clientID
        }
      };
      var post = request.post(options, function(err, req, body){
        _cb(err, body);
      });

      var upload = post.form();
      if (_file.match(/^https?:\/\//i)) {
        upload.append('type','url');
        upload.append('image',_file);
      } else {
        upload.append('type', 'file');
        upload.append('image', fs.createReadStream(_file));
      }
    }
  }
};

exports.upload = imgur.upload;
exports.setClientID = imgur.setClientID;
<commit_msg>Changed copyright info<commit_after>/*
 * imgur-upload
 * https://github.com/jamiees2/imgur-node-api/
 *
 * Copyright (c) 2013 jamiees2
 * Licensed under the MIT license.
 */

 'use strict';

var https = require('https'),
  fs = require('fs'),
  request = require('request');
var request = request.defaults({
  json: true
});

var imgur = {
  _clientID : null,
  setClientID : function(clientID){
    this._clientID = clientID;
  },
  upload : function(_file,_cb) {
    if(this._clientID && _file){
      var options = {
        url: 'https://api.imgur.com/3/upload',
        headers: {
          'Authorization': 'Client-ID ' + this._clientID
        }
      };
      var post = request.post(options, function(err, req, body){
        _cb(err, body);
      });

      var upload = post.form();
      if (_file.match(/^https?:\/\//i)) {
        upload.append('type','url');
        upload.append('image',_file);
      } else {
        upload.append('type', 'file');
        upload.append('image', fs.createReadStream(_file));
      }
    }
  }
};

exports.upload = imgur.upload;
exports.setClientID = imgur.setClientID;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var ngTartan = require('../../module');

ngTartan.directive('tartanFormatted', [
  function() {
    return {
      restrict: 'E',
      require: '^^tartan',
      template: '

',
      replace: true,
      scope: {},
      link: function($scope, element, attr, controller) {
        var target = element.find('pre');

        function tartanChanged(state) {
          target.text(state.formatted);
        }

        controller.on('tartan.changed', tartanChanged);

        controller.requestUpdate(tartanChanged);

        $scope.$on('$destroy', function() {
          controller.off('tartan.changed', tartanChanged);
        });
      }
    };
  }
]);
<commit_msg>Formatted text was not displayed<commit_after>'use strict';

var _ = require('lodash');
var ngTartan = require('../../module');

ngTartan.directive('tartanFormatted', [
  function() {
    return {
      restrict: 'E',
      require: '^^tartan',
      template: '

',
      replace: true,
      scope: {},
      link: function($scope, element, attr, controller) {

        function tartanChanged(state) {
          element.text(state.formatted);
        }

        controller.on('tartan.changed', tartanChanged);

        controller.requestUpdate(tartanChanged);

        $scope.$on('$destroy', function() {
          controller.off('tartan.changed', tartanChanged);
        });
      }
    };
  }
]);
<|endoftext|>