<commit_before>'use strict';

const Promise = require('promise');
const cassandraConnector = require('../../clients/cassandra/CassandraConnector');
const withRunTime = require('../shared').withRunTime;

const STREAM_PIPELINE_TWITTER = 'twitter';
const STREAM_CONNECTOR_TWITTER = 'twitter';

const TRUSTED_SOURCES_RANK_DEFAULT = 10;

/**
 * @param {{input: {targetBbox: number[], defaultZoomLevel: number, logo: string, title: string, name: string, defaultLocation: number[], storageConnectionString: string, featuresConnectionString: string, mapzenApiKey: string, fbToken: string, supportedLanguages: string[]}}} args
 * @returns {Promise.<{name: string, properties: {targetBBox: number[], defaultZoomLevel: number, logo: string, title: string, defaultLocation: number[], storageConnectionString: string, featuresConnectionString: string, mapzenApiKey: string, fbToken: string, supportedLanguages: string[]}}>}
 */
function createOrReplaceSite(args, res) { // eslint-disable-line no-unused-vars
}

/**
 * @param {{input: {site: string, pages: Array<{pageUrl: string, RowKey: string}>}}} args
 * @returns {Promise.<{runTime: string, pages: Array<{pageUrl: string, RowKey: string}>}>}
 */
function modifyFacebookPages(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    reject("Modification not yet supported.");
  });
}

/**
 * @param {{input: {site: string, pages: Array<{pageUrl: string, RowKey: string}>}}} args
 * @returns {Promise.<{runTime: string, pages: Array<{pageUrl: string, RowKey: string}>}>}
 */
function removeFacebookPages(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    reject("Removal not yet supported.");
  });
}

function trustedTwitterAccountRowKeyToPrimaryKey(account) {
  const params = account.RowKey.split(/,/);
  if (params.length != 3) {
    throw("Expecting three element comma-delimited RowKey representing (connector, sourceid, sourcetype).");
  }
  return params;
}

function normalizedTrustedTwitterAccount(account) {
  const keyValues = trustedTwitterAccountRowKeyToPrimaryKey(account);
  return {
    RowKey: account.RowKey,
    acctUrl: keyValues[1]
  }
}

/**
 * @param {{input: {site: string, accounts: Array<{acctUrl: string, RowKey: string}>}}} args
 * @returns {Promise.<{runTime: string, accounts: Array<{pageUrl: string, RowKey: string}>}>}
 */
function modifyTrustedTwitterAccounts(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const accounts = args && args.input && args.input.accounts;
    if (!accounts || !accounts.length) return reject('No accounts specified');

    const statement = 'INSERT INTO fortis.trustedsources (connector, sourceid, sourcetype, insertion_time, rank) VALUES (?, ?, ?, dateof(now()), ?)';
    const queries = accounts.map(account => {
      const params = trustedTwitterAccountRowKeyToPrimaryKey(account);
      params.push(TRUSTED_SOURCES_RANK_DEFAULT);

      return {query: statement, params: params};
    });

    cassandraConnector.executeBatchMutations(queries)
    .then(() => { resolve({ accounts: accounts.map(normalizedTrustedTwitterAccount) }) })
    .catch(reject)
    ;
  });
}

/**
 * @param {{input: {site: string, accounts: Array<{acctUrl: string, RowKey: string}>}}} args
 * @returns {Promise.<{runTime: string, accounts: Array<{pageUrl: string, RowKey: string}>}>}
 */
function removeTrustedTwitterAccounts(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const accounts = args && args.input && args.input.accounts;
    if (!accounts || !accounts.length) return reject('No accounts specified');

    const deleteByPrimaryKey = 'DELETE FROM fortis.trustedsources WHERE connector = ? AND sourceid = ? AND sourcetype = ?';
    const queries = accounts.map(account => {
      const params = trustedTwitterAccountRowKeyToPrimaryKey(account);
      return {query: deleteByPrimaryKey, params: params};
    });

    cassandraConnector.executeBatchMutations(queries)
    .then(() => { resolve({ accounts: accounts.map(normalizedTrustedTwitterAccount) }); })
    .catch(reject)
    ;
  });
}

/**
 * @param {{input: {site: string, accounts: Array<{accountName: string, consumerKey: string, consumerSecret: string, token: string, tokenSecret: string}>}}} args
 * @returns {Promise.<{runTime: string, accounts: Array<{accountName: string, consumerKey: string, consumerSecret: string, token: string, tokenSecret: string}>}>}
 */
function modifyTwitterAccounts(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const accounts = args && args.input && args.input.accounts;
    if (!accounts || !accounts.length) return reject('No accounts specified');

    const statement = 'INSERT INTO fortis.streams (pipeline, connector, params) values (?, ?, ?)';
    const queries = accounts.map( account => { return { query: statement, params: [STREAM_PIPELINE_TWITTER, STREAM_CONNECTOR_TWITTER, account]}; } );

    cassandraConnector.executeBatchMutations(queries)
    .then(() => { resolve({ accounts: accounts }); })
    .catch(reject)
    ;

  });
}

/**
 * @param {{input: {site: string, accounts: Array<{accountName: string, consumerKey: string, consumerSecret: string, token: string, tokenSecret: string}>}}} args
 * @returns {Promise.<{runTime: string, accounts: Array<{accountName: string, consumerKey: string, consumerSecret: string, token: string, tokenSecret: string}>}>}
 */
function removeTwitterAccounts(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const accounts = args && args.input && args.input.accounts;
    if (!accounts || !accounts.length) return reject('No accounts specified');

    const statement = 'DELETE FROM fortis.streams WHERE pipeline = ? AND connector = ? AND params = ?';
    const queries = accounts.map( account => { return { query: statement, params: [STREAM_PIPELINE_TWITTER, STREAM_CONNECTOR_TWITTER, account]}; } );

    cassandraConnector.executeBatchMutations(queries)
    .then(() => { resolve({ accounts: accounts }); })
    .catch(reject)
    ;

  });
}

/**
 * @param {{input: {site: string, terms: Array<{RowKey: string, lang: string, filteredTerms: string[]}>}}} args
 * @returns {Promise.<{runTime: string, filters: Array<{filteredTerms: string[], lang: string, RowKey: string}>}>}
 */
function modifyBlacklist(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    reject("Modification not yet supported.");
  });
}

/**
 * @param {{input: {site: string, terms: Array<{RowKey: string, lang: string, filteredTerms: string[]}>}}} args
 * @returns {Promise.<{runTime: string, filters: Array<{filteredTerms: string[], lang: string, RowKey: string}>}>}
 */
function removeBlacklist(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const terms = args && args.input && args.input.terms;
    if (!terms || !terms.length) return reject('No terms specified');

    const params = [terms.map( t => { return t.RowKey; } )];
    const update = 'DELETE FROM fortis.blacklist WHERE id IN ?';
    cassandraConnector.executeQuery(update, params)
    .then(() => { resolve({ filters: terms }); })
    .catch(reject)
    ;
  });
}

module.exports = {
  createOrReplaceSite: createOrReplaceSite,
  modifyFacebookPages: withRunTime(modifyFacebookPages),
  removeFacebookPages: withRunTime(removeFacebookPages),
  modifyTrustedTwitterAccounts: withRunTime(modifyTrustedTwitterAccounts),
  removeTrustedTwitterAccounts: withRunTime(removeTrustedTwitterAccounts),
  modifyTwitterAccounts: withRunTime(modifyTwitterAccounts),
  removeTwitterAccounts: withRunTime(removeTwitterAccounts),
  modifyBlacklist: withRunTime(modifyBlacklist),
  removeBlacklist: withRunTime(removeBlacklist)
};
<commit_msg>eslint corrections.<commit_after>'use strict';

const Promise = require('promise');
const cassandraConnector = require('../../clients/cassandra/CassandraConnector');
const withRunTime = require('../shared').withRunTime;

const STREAM_PIPELINE_TWITTER = 'twitter';
const STREAM_CONNECTOR_TWITTER = 'twitter';

const TRUSTED_SOURCES_RANK_DEFAULT = 10;

/**
 * @param {{input: {targetBbox: number[], defaultZoomLevel: number, logo: string, title: string, name: string, defaultLocation: number[], storageConnectionString: string, featuresConnectionString: string, mapzenApiKey: string, fbToken: string, supportedLanguages: string[]}}} args
 * @returns {Promise.<{name: string, properties: {targetBBox: number[], defaultZoomLevel: number, logo: string, title: string, defaultLocation: number[], storageConnectionString: string, featuresConnectionString: string, mapzenApiKey: string, fbToken: string, supportedLanguages: string[]}}>}
 */
function createOrReplaceSite(args, res) { // eslint-disable-line no-unused-vars
}

/**
 * @param {{input: {site: string, pages: Array<{pageUrl: string, RowKey: string}>}}} args
 * @returns {Promise.<{runTime: string, pages: Array<{pageUrl: string, RowKey: string}>}>}
 */
function modifyFacebookPages(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    reject('Modification not yet supported.');
  });
}

/**
 * @param {{input: {site: string, pages: Array<{pageUrl: string, RowKey: string}>}}} args
 * @returns {Promise.<{runTime: string, pages: Array<{pageUrl: string, RowKey: string}>}>}
 */
function removeFacebookPages(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    reject('Removal not yet supported.');
  });
}

function trustedTwitterAccountRowKeyToPrimaryKey(account) {
  const params = account.RowKey.split(/,/);
  if (params.length != 3) {
    throw('Expecting three element comma-delimited RowKey representing (connector, sourceid, sourcetype).');
  }
  return params;
}

function normalizedTrustedTwitterAccount(account) {
  const keyValues = trustedTwitterAccountRowKeyToPrimaryKey(account);
  return {
    RowKey: account.RowKey,
    acctUrl: keyValues[1]
  };
}

/**
 * @param {{input: {site: string, accounts: Array<{acctUrl: string, RowKey: string}>}}} args
 * @returns {Promise.<{runTime: string, accounts: Array<{pageUrl: string, RowKey: string}>}>}
 */
function modifyTrustedTwitterAccounts(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const accounts = args && args.input && args.input.accounts;
    if (!accounts || !accounts.length) return reject('No accounts specified');

    const statement = 'INSERT INTO fortis.trustedsources (connector, sourceid, sourcetype, insertion_time, rank) VALUES (?, ?, ?, dateof(now()), ?)';
    const queries = accounts.map(account => {
      const params = trustedTwitterAccountRowKeyToPrimaryKey(account);
      params.push(TRUSTED_SOURCES_RANK_DEFAULT);

      return {query: statement, params: params};
    });

    cassandraConnector.executeBatchMutations(queries)
    .then(() => { resolve({ accounts: accounts.map(normalizedTrustedTwitterAccount) }); })
    .catch(reject)
    ;
  });
}

/**
 * @param {{input: {site: string, accounts: Array<{acctUrl: string, RowKey: string}>}}} args
 * @returns {Promise.<{runTime: string, accounts: Array<{pageUrl: string, RowKey: string}>}>}
 */
function removeTrustedTwitterAccounts(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const accounts = args && args.input && args.input.accounts;
    if (!accounts || !accounts.length) return reject('No accounts specified');

    const deleteByPrimaryKey = 'DELETE FROM fortis.trustedsources WHERE connector = ? AND sourceid = ? AND sourcetype = ?';
    const queries = accounts.map(account => {
      const params = trustedTwitterAccountRowKeyToPrimaryKey(account);
      return {query: deleteByPrimaryKey, params: params};
    });

    cassandraConnector.executeBatchMutations(queries)
    .then(() => { resolve({ accounts: accounts.map(normalizedTrustedTwitterAccount) }); })
    .catch(reject)
    ;
  });
}

/**
 * @param {{input: {site: string, accounts: Array<{accountName: string, consumerKey: string, consumerSecret: string, token: string, tokenSecret: string}>}}} args
 * @returns {Promise.<{runTime: string, accounts: Array<{accountName: string, consumerKey: string, consumerSecret: string, token: string, tokenSecret: string}>}>}
 */
function modifyTwitterAccounts(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const accounts = args && args.input && args.input.accounts;
    if (!accounts || !accounts.length) return reject('No accounts specified');

    const statement = 'INSERT INTO fortis.streams (pipeline, connector, params) values (?, ?, ?)';
    const queries = accounts.map( account => { return { query: statement, params: [STREAM_PIPELINE_TWITTER, STREAM_CONNECTOR_TWITTER, account]}; } );

    cassandraConnector.executeBatchMutations(queries)
    .then(() => { resolve({ accounts: accounts }); })
    .catch(reject)
    ;

  });
}

/**
 * @param {{input: {site: string, accounts: Array<{accountName: string, consumerKey: string, consumerSecret: string, token: string, tokenSecret: string}>}}} args
 * @returns {Promise.<{runTime: string, accounts: Array<{accountName: string, consumerKey: string, consumerSecret: string, token: string, tokenSecret: string}>}>}
 */
function removeTwitterAccounts(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const accounts = args && args.input && args.input.accounts;
    if (!accounts || !accounts.length) return reject('No accounts specified');

    const statement = 'DELETE FROM fortis.streams WHERE pipeline = ? AND connector = ? AND params = ?';
    const queries = accounts.map( account => { return { query: statement, params: [STREAM_PIPELINE_TWITTER, STREAM_CONNECTOR_TWITTER, account]}; } );

    cassandraConnector.executeBatchMutations(queries)
    .then(() => { resolve({ accounts: accounts }); })
    .catch(reject)
    ;

  });
}

/**
 * @param {{input: {site: string, terms: Array<{RowKey: string, lang: string, filteredTerms: string[]}>}}} args
 * @returns {Promise.<{runTime: string, filters: Array<{filteredTerms: string[], lang: string, RowKey: string}>}>}
 */
function modifyBlacklist(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    reject('Modification not yet supported.');
  });
}

/**
 * @param {{input: {site: string, terms: Array<{RowKey: string, lang: string, filteredTerms: string[]}>}}} args
 * @returns {Promise.<{runTime: string, filters: Array<{filteredTerms: string[], lang: string, RowKey: string}>}>}
 */
function removeBlacklist(args, res) { // eslint-disable-line no-unused-vars
  return new Promise((resolve, reject) => {
    const terms = args && args.input && args.input.terms;
    if (!terms || !terms.length) return reject('No terms specified');

    const params = [terms.map( t => { return t.RowKey; } )];
    const update = 'DELETE FROM fortis.blacklist WHERE id IN ?';
    cassandraConnector.executeQuery(update, params)
    .then(() => { resolve({ filters: terms }); })
    .catch(reject)
    ;
  });
}

module.exports = {
  createOrReplaceSite: createOrReplaceSite,
  modifyFacebookPages: withRunTime(modifyFacebookPages),
  removeFacebookPages: withRunTime(removeFacebookPages),
  modifyTrustedTwitterAccounts: withRunTime(modifyTrustedTwitterAccounts),
  removeTrustedTwitterAccounts: withRunTime(removeTrustedTwitterAccounts),
  modifyTwitterAccounts: withRunTime(modifyTwitterAccounts),
  removeTwitterAccounts: withRunTime(removeTwitterAccounts),
  modifyBlacklist: withRunTime(modifyBlacklist),
  removeBlacklist: withRunTime(removeBlacklist)
};
<|endoftext|>


<commit_before>export { Base, Params };

/**
 * A base class for extracting and keeping weights. It composes of a Float32Array and a shape. It can
 * be used as CNN (depthwise, pointwise and bias) filter weights.
 *
 * @member {Float32Array} defaultInput
 *   The default input Float32Array. Its byteOffset will be checked against defaultByteOffsetBegin.
 * Its content will be interpret as weights if privilegeInput is null. Otherwise, its content
 * will be ignored if privilegeInput is not null.
 *
 * @member {number} defaultByteOffsetBegin
 *   The weights[] begins at defaultInput's defaultByteOffsetBegin (relative to defaultInput.buffer,
 * not to defaultInput.byteOffset). If this value less than defaultInput.byteOffset, the
 * initialization will fail (i.e. ( isValid() == false ) ).
 *
 * @member {number} defaultByteOffsetEnd
 *   The weights[] ends at defaultInput's defaultByteOffsetEnd (not inclusive) (relative to
 * defaultInput.buffer, not to defaultInput.byteOffset).
 *
 * @member {Float32Array} privilegeInput
 *   The privilege input Float32Array. If not null, its content will be interpret as weights and
 * the content of defaultInput will be ignored.
 *
 * @member {number} privilegeByteOffsetBegin
 *   The weights[] begins at privilegeInput's privilegeByteOffsetBegin (relative to privilegeInput.buffer,
 * not to privilegeInput.byteOffset). If this value less than privilegeInput.byteOffset, the
 * initialization will fail (i.e. ( isValid() == false ) ).
 *
 * @member {number} privilegeByteOffsetEnd
 *   The weights[] ends at privilegeInput's privilegeByteOffsetEnd (not inclusive) (relative
 * to privilegeInput.buffer, not to privilegeInput.byteOffset).
 *
 * @member {number[]} shape
 *   The weights shape (element count for every dimension). The shape.length is dimension. If ( shape.length == 0 ),
 * it is 0-dimension (i.e. scalar). The initialization will fail (i.e. ( isValid() == false ) ) if shape is too large
 * (or NaN) (exceeds the defaultInput (or, privilegeInput if not null) bounding).
 *
 * @member {Float32Array} weights
 *  The values. It is a reference (sub-range) to the underlying defaultInput (or privilegeInput).
 */
class Base {

  /**
   * Create Float32Array weights[] over the defaultInput (or privilegeInput) according to the specific
   * byteOffsetBegin, shape, and weightConverter.
   *
   * The defaultInput and privilegeInput can not both be null. If one of them is null, the non-null is used.
   * If both are non-null, the privilegeInput will be used.
   *
   * @return {boolean} Return false, if initialization failed.
   */
  init( defaultInput, defaultByteOffsetBegin, privilegeInput, privilegeByteOffsetBegin, shape ) {

    this.defaultInput =   defaultInput;
    this.privilegeInput = privilegeInput;
    this.shape =          shape;
    this.weights =        null;   // So that ( isValid() == false ) if re-initialization failed.

    //let weightCount =     ( shape ) ? shape.reduce( ( accumulator, currentValue ) => accumulator * currentValue ) : 0;
    let weightCount =     ( shape ) ? tf.sizeFromShape( shape ) : 0; // tf.sizeFromShape() can handle ( 0 == shape.length ) (i.e. scalar).
    let weightByteCount = weightCount * Float32Array.BYTES_PER_ELEMENT;

    let input, byteOffsetBegin;
    let byteOffsetEnd; // Not inclusive. It will be used as the next filter's beginning.

    if ( privilegeInput ) {       // privilegeInput first.

      if ( privilegeByteOffsetBegin < privilegeInput.byteOffset )
        return false;  // Failed, the privilege beginning position is illegal (less than bounding).

      input = privilegeInput;
      byteOffsetBegin = this.privilegeByteOffsetBegin = privilegeByteOffsetBegin;
      byteOffsetEnd =   this.privilegeByteOffsetEnd =   privilegeByteOffsetBegin + weightByteCount;
      this.defaultByteOffsetBegin = this.defaultByteOffsetEnd = defaultByteOffsetBegin; // Stay at beginning for not used.

    } else if ( defaultInput ) {  // defaultInput second.

      if ( defaultByteOffsetBegin < defaultInput.byteOffset )
        return false;  // Failed, the default beginning position is illegal (less than bounding).

      input = defaultInput;
      byteOffsetBegin = this.defaultByteOffsetBegin = defaultByteOffsetBegin;
      byteOffsetEnd =   this.defaultByteOffsetEnd =   defaultByteOffsetBegin + weightByteCount;
      this.privilegeByteOffsetBegin = this.privilegeByteOffsetEnd = privilegeByteOffsetBegin; // Stay at beginning for not used.

    } else {
      return false;  // Failed, both privilege and default input are null.
    }

    // Bounded by the input.byteLength.
    let legalByteOffsetEnd = input.byteOffset + input.byteLength;
    if ( byteOffsetEnd > legalByteOffsetEnd )
      return false;  // Failed, if shape is too large (or NaN).

    this.weights = new Float32Array( input.buffer, byteOffsetBegin, weightCount );  // Share the underlying array buffer.
    return true;     // Success.
  }

  /** @return {number} Return the absolute value of the trucated value. */
  static toPositiveInteger( v ) {
    return Math.abs( Math.trunc( v ) );
  }

  /** @return Return true, if initialization is success (i.e. ( this.weights != null )). */
  isValid()                      { return ( this.weights ) ? true : false; }

  get weightByteCount()          { return this.weights.byteLength; }
  get weightCount()              { return this.weights.length; }
}

/**
 * The parameters for the weights of a neural network layer.
 *
 * @member {Map} parameterMap
 *   All parameters provided by this object. Its keys are the same as init()'s parameterMap. Its values are
 * combined from init()'s parameterMap and inputFloat32Array (or fixedWeights).
 *
 * @member {number} parameterCountExtracted
 *   How many parameters are extracted from inputFloat32Array or fixedWeights in fact. Only existed if init()
 * successfully. The same as this.weightCount (i.e. length of this.weights[] and this.weightsModified[]).
 *
 * @member {number} parameterCount
 *   Always ( parameterMap.size ). This is the total parameter count provided by this object
 * if init() successfully.
 *
 * @member {number} inChannels
 *   The input channel count of this neural network layer.
 *
 * @member {number} channelMultiplier
 *   Every input channel will be expanded into how many channels.
 *
 * @member {number} outChannels
 *   The output channel count of this neural network layer.
 *
 * @member {Float32Array} weightsModified
 *  The copied extracted values. They are copied from inputFloat32Array or fixedWeights, and then converted
 * to positive integer. Its length will be the same as parameterCountExtracted.
 */
class Params extends Base {

  /**
   *
   * The parameterMap should have at least:
   *
   * - Params.Keys.inChannels
   *   {number} There will be how many input channels. (Input channel count) (By specifying)
   *
   *
   * The parameterMap could also have (and any other custom key-value pairs):
   *
   * - Params.Keys.channelMultiplier
   *   {number} Every input channel will be expanded into how many channels. (No matter how it is determined, it will
   *   always be forcibly adjusted to positive integer.)
   *     - If not null, it will be used instead of extracting from inputFloat32Array or fixedWeights. (By specifying)
   *     - If null, extracted from inputFloat32Array or fixedWeights. (By evolution)
   *
   * - Params.Keys.outChannels
   *   {number} There will be how many output channels. (Output channel count)
   *     - If Number.isFinite(), it will be used instead of extracting from inputFloat32Array or fixedWeights. (By specifying)
   *     - If Infinity (Number.POSITIVE_INFINITY), it will be ( inChannels * channelMultiplier ). (By channelMultiplier)
   *     - If null, extracted from inputFloat32Array or fixedWeights. (By evolution)
   *
   *
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights. It should have ( parameterCountExtractedAtLeast ) or
   * ( parameterCountExtractedAtLeast + 1 ) or ( parameterCountExtractedAtLeast + 2 ) elements according to the
   * value of channelMultiplier and outChannels.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {Map} parameterMap
   *   Describe what parameters to be used or extracted. The keys of parameterMap will be viewed as parameter names.
   * The values of parameterMap will be viewed as parameter values. If the value of a [ key, value ] entry is null,
   * the parameter's value will be extracted from inputFloat32Array or fixedWeights (i.e. by evolution). Otherwise,
   * the parameter's value will be the value of the [ key, value ] entry (i.e. by specifying).
   *
   * @param {Float32Array|Array} fixedWeights
   *   If null, extract parameters from inputFloat32Array. If not null, extract parameters from it instead of
   * inputFloat32Array. When not null, it should have parameterCountExtracted elements (i.e. the count of non-null values
   * of parameterMap).
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, parameterMap, fixedWeights = null ) {

    this.weightsModified = this.parameterMap = null; // So that distinguishable if re-initialization failed.

    if ( !parameterMap )
      return false;  // Do not know what parameters to be used or extracted.

    let inChannels = parameterMap.get( Params.Keys.inChannels );
    if ( !inChannels )
      return false;  // At least, there should be a (required) parameter (i.e. input channel count).

    this.parameterMap = new Map( parameterMap );  // Copy so that the original map will not be modified.

    // Collect what parameters should be extracted from input array (rather than use values in the parameterMap).
    // At the same time, its array index will also be recorded for extracting its value from array.
    let arrayIndexMap = new Map();
    {
      let i = 0;
      for ( let [ key, value ] of parameterMap ) {
        // A null (or undefined) value means it should be extracted from inputFloat32Array or fixedWeights (i.e. by evolution).
        //
        // Note: This is different from ( !value ). If value is 0, ( !value ) is true but ( null == value ) is false.
        if ( null == value ) {
          arrayIndexMap.set( key, i ); // Record the index to this.weightsModified[].
          ++i;
        }
      }
    }

    let parameterCountExtracted = arrayIndexMap.size; // Determine how many parameters should be extracted from array.

    // If has fixedWeights, use it as priviledge input.
    let privilegeInput;
    if ( fixedWeights ) {
      if ( fixedWeights instanceof Float32Array )
        privilegeInput = fixedWeights;
      else
        privilegeInput = new Float32Array( fixedWeights );  // Convert to Float32Array.
    }

    // Extract a block of input array.
    let bInitOk = super.init( inputFloat32Array, byteOffsetBegin, privilegeInput, 0, [ parameterCountExtracted ] );

    if ( !bInitOk )
      return false;

    // Copy and convert to integer.
    //
    // Do not modify the original array data. When backtracking (to try another neural network layer
    // configuration), the original data is necessary.
    this.weightsModified = new Float32Array( this.weights.length );
    for ( let i = 0; i < this.weightsModified.length; ++i ) {
      this.weightsModified[ i ] = Base.toPositiveInteger( this.weights[ i ] );
    }

    // Extract (by evolution) values from array into map.
    for ( let [ key, arrayIndex ] of arrayIndexMap ) {
      this.parameterMap.set( key, this.weightsModified[ arrayIndex ] );
    }

    // Restrict channelMultiplier to positive integer.
    //
    // If it is zero or negative, the outChannels (= inChannels * channelMultiplier ) will be strange value.
    // Strange outChannels value will affect the parameters extraction of the next neural network layer.
    let channelMultiplier = this.channelMultiplier;   // May be specified or extracted.
    if ( channelMultiplier < 1 ) {
      channelMultiplier = 1;
      this.parameterMap.set( Params.Keys.channelMultiplier, channelMultiplier );
    }

    // If original parameterMap has output channel count and its value is infinity, its value will depend
    // on channelMultiplier (i.e. by channelMultiplier).
    //
    // Usually, the embedding layer uses this behavior.
    let outChannelsOriginal = parameterMap.get( Params.Keys.outChannels );
    if ( outChannelsOriginal ) {
      if ( !Number.isFinite( outChannelsOriginal ) ) {
        let outChannels = inChannels * channelMultiplier;
        this.parameterMap.set( Params.Keys.outChannels, outChannels );
      } else {
        // Use the original value in parameterMap as output channel count (i.e. by specifying).
      }
    } else {
      // Either use the extracted value as output channel count (i.e. by evolution),
      // or there is no output channel count in fact.
    }

    return bInitOk;
  }

  get parameterCountExtracted() { return this.weightCount; }
  get parameterCount()          { return this.parameterMap.size; }

  // Most kinds of layers have these parameters.

  get inChannels()        { return this.parameterMap.get( Params.Keys.inChannels ); }
  get channelMultiplier() { return this.parameterMap.get( Params.Keys.channelMultiplier ); }
  get outChannels()       { return this.parameterMap.get( Params.Keys.outChannels ); }
}

/**
 * Define parameter keys.
 *
 * They are (static) symbol objects used as keys of Params.init()'s parameterMap. They can be seen inside Map when
 * debugging, and are faster than string (or String object) when Map's key comparing.
 */
Params.Keys = class {
//   // Although they are functions, they should not be called. They are (static function) objects used as keys of
//   // Params.init()'s parameterMap. Using function object (rather than general object) so that the (function) name
//   // can be seen inside Map when debugging. Using function object (rather than string or String object) so that
//   // Map's key comparing is faster.
//   static inChannels()        {}
//   static channelMultiplier() {}
//   static outChannels()       {}
}

Params.Keys.inChannels =        Symbol("inChannels");
Params.Keys.channelMultiplier = Symbol("channelMultiplier");
Params.Keys.outChannels =       Symbol("outChannels");

Params.Keys.dilationHeight =    Symbol("dilationHeight");
Params.Keys.dilationWidth =     Symbol("dilationWidth");
Params.Keys.filterHeight =      Symbol("filterHeight");
Params.Keys.filterWidth =       Symbol("filterWidth");
<commit_msg>Update Weights.js<commit_after>export { Base, Params };

/**
 * A base class for extracting and keeping weights. It composes of a Float32Array and a shape. It can
 * be used as CNN (depthwise, pointwise and bias) filter weights.
 *
 * @member {Float32Array} defaultInput
 *   The default input Float32Array. Its byteOffset will be checked against defaultByteOffsetBegin.
 * Its content will be interpret as weights if privilegeInput is null. Otherwise, its content
 * will be ignored if privilegeInput is not null.
 *
 * @member {number} defaultByteOffsetBegin
 *   The weights[] begins at defaultInput's defaultByteOffsetBegin (relative to defaultInput.buffer,
 * not to defaultInput.byteOffset). If this value less than defaultInput.byteOffset, the
 * initialization will fail (i.e. ( isValid() == false ) ).
 *
 * @member {number} defaultByteOffsetEnd
 *   The weights[] ends at defaultInput's defaultByteOffsetEnd (not inclusive) (relative to
 * defaultInput.buffer, not to defaultInput.byteOffset).
 *
 * @member {Float32Array} privilegeInput
 *   The privilege input Float32Array. If not null, its content will be interpret as weights and
 * the content of defaultInput will be ignored.
 *
 * @member {number} privilegeByteOffsetBegin
 *   The weights[] begins at privilegeInput's privilegeByteOffsetBegin (relative to privilegeInput.buffer,
 * not to privilegeInput.byteOffset). If this value less than privilegeInput.byteOffset, the
 * initialization will fail (i.e. ( isValid() == false ) ).
 *
 * @member {number} privilegeByteOffsetEnd
 *   The weights[] ends at privilegeInput's privilegeByteOffsetEnd (not inclusive) (relative
 * to privilegeInput.buffer, not to privilegeInput.byteOffset).
 *
 * @member {number[]} shape
 *   The weights shape (element count for every dimension). The shape.length is dimension. If ( shape.length == 0 ),
 * it is 0-dimension (i.e. scalar). The initialization will fail (i.e. ( isValid() == false ) ) if shape is too large
 * (or NaN) (exceeds the defaultInput (or, privilegeInput if not null) bounding).
 *
 * @member {Float32Array} weights
 *  The values. It is a reference (sub-range) to the underlying defaultInput (or privilegeInput).
 */
class Base {

  /**
   * Create Float32Array weights[] over the defaultInput (or privilegeInput) according to the specific
   * byteOffsetBegin, shape, and weightConverter.
   *
   * The defaultInput and privilegeInput can not both be null. If one of them is null, the non-null is used.
   * If both are non-null, the privilegeInput will be used.
   *
   * @return {boolean} Return false, if initialization failed.
   */
  init( defaultInput, defaultByteOffsetBegin, privilegeInput, privilegeByteOffsetBegin, shape ) {

    this.defaultInput =   defaultInput;
    this.privilegeInput = privilegeInput;
    this.shape =          shape;
    this.weights =        null;   // So that ( isValid() == false ) if re-initialization failed.

    //let weightCount =     ( shape ) ? shape.reduce( ( accumulator, currentValue ) => accumulator * currentValue ) : 0;
    let weightCount =     ( shape ) ? tf.util.sizeFromShape( shape ) : 0; // It can handle ( 0 == shape.length ) (i.e. scalar).
    let weightByteCount = weightCount * Float32Array.BYTES_PER_ELEMENT;

    let input, byteOffsetBegin;
    let byteOffsetEnd; // Not inclusive. It will be used as the next filter's beginning.

    if ( privilegeInput ) {       // privilegeInput first.

      if ( privilegeByteOffsetBegin < privilegeInput.byteOffset )
        return false;  // Failed, the privilege beginning position is illegal (less than bounding).

      input = privilegeInput;
      byteOffsetBegin = this.privilegeByteOffsetBegin = privilegeByteOffsetBegin;
      byteOffsetEnd =   this.privilegeByteOffsetEnd =   privilegeByteOffsetBegin + weightByteCount;
      this.defaultByteOffsetBegin = this.defaultByteOffsetEnd = defaultByteOffsetBegin; // Stay at beginning for not used.

    } else if ( defaultInput ) {  // defaultInput second.

      if ( defaultByteOffsetBegin < defaultInput.byteOffset )
        return false;  // Failed, the default beginning position is illegal (less than bounding).

      input = defaultInput;
      byteOffsetBegin = this.defaultByteOffsetBegin = defaultByteOffsetBegin;
      byteOffsetEnd =   this.defaultByteOffsetEnd =   defaultByteOffsetBegin + weightByteCount;
      this.privilegeByteOffsetBegin = this.privilegeByteOffsetEnd = privilegeByteOffsetBegin; // Stay at beginning for not used.

    } else {
      return false;  // Failed, both privilege and default input are null.
    }

    // Bounded by the input.byteLength.
    let legalByteOffsetEnd = input.byteOffset + input.byteLength;
    if ( byteOffsetEnd > legalByteOffsetEnd )
      return false;  // Failed, if shape is too large (or NaN).

    this.weights = new Float32Array( input.buffer, byteOffsetBegin, weightCount );  // Share the underlying array buffer.
    return true;     // Success.
  }

  /** @return {number} Return the absolute value of the trucated value. */
  static toPositiveInteger( v ) {
    return Math.abs( Math.trunc( v ) );
  }

  /** @return Return true, if initialization is success (i.e. ( this.weights != null )). */
  isValid()                      { return ( this.weights ) ? true : false; }

  get weightByteCount()          { return this.weights.byteLength; }
  get weightCount()              { return this.weights.length; }
}

/**
 * The parameters for the weights of a neural network layer.
 *
 * @member {Map} parameterMap
 *   All parameters provided by this object. Its keys are the same as init()'s parameterMap. Its values are
 * combined from init()'s parameterMap and inputFloat32Array (or fixedWeights).
 *
 * @member {number} parameterCountExtracted
 *   How many parameters are extracted from inputFloat32Array or fixedWeights in fact. Only existed if init()
 * successfully. The same as this.weightCount (i.e. length of this.weights[] and this.weightsModified[]).
 *
 * @member {number} parameterCount
 *   Always ( parameterMap.size ). This is the total parameter count provided by this object
 * if init() successfully.
 *
 * @member {number} inChannels
 *   The input channel count of this neural network layer.
 *
 * @member {number} channelMultiplier
 *   Every input channel will be expanded into how many channels.
 *
 * @member {number} outChannels
 *   The output channel count of this neural network layer.
 *
 * @member {Float32Array} weightsModified
 *  The copied extracted values. They are copied from inputFloat32Array or fixedWeights, and then converted
 * to positive integer. Its length will be the same as parameterCountExtracted.
 */
class Params extends Base {

  /**
   *
   * The parameterMap should have at least:
   *
   * - Params.Keys.inChannels
   *   {number} There will be how many input channels. (Input channel count) (By specifying)
   *
   *
   * The parameterMap could also have (and any other custom key-value pairs):
   *
   * - Params.Keys.channelMultiplier
   *   {number} Every input channel will be expanded into how many channels. (No matter how it is determined, it will
   *   always be forcibly adjusted to positive integer.)
   *     - If not null, it will be used instead of extracting from inputFloat32Array or fixedWeights. (By specifying)
   *     - If null, extracted from inputFloat32Array or fixedWeights. (By evolution)
   *
   * - Params.Keys.outChannels
   *   {number} There will be how many output channels. (Output channel count)
   *     - If Number.isFinite(), it will be used instead of extracting from inputFloat32Array or fixedWeights. (By specifying)
   *     - If Infinity (Number.POSITIVE_INFINITY), it will be ( inChannels * channelMultiplier ). (By channelMultiplier)
   *     - If null, extracted from inputFloat32Array or fixedWeights. (By evolution)
   *
   *
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights. It should have ( parameterCountExtractedAtLeast ) or
   * ( parameterCountExtractedAtLeast + 1 ) or ( parameterCountExtractedAtLeast + 2 ) elements according to the
   * value of channelMultiplier and outChannels.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {Map} parameterMap
   *   Describe what parameters to be used or extracted. The keys of parameterMap will be viewed as parameter names.
   * The values of parameterMap will be viewed as parameter values. If the value of a [ key, value ] entry is null,
   * the parameter's value will be extracted from inputFloat32Array or fixedWeights (i.e. by evolution). Otherwise,
   * the parameter's value will be the value of the [ key, value ] entry (i.e. by specifying).
   *
   * @param {Float32Array|Array} fixedWeights
   *   If null, extract parameters from inputFloat32Array. If not null, extract parameters from it instead of
   * inputFloat32Array. When not null, it should have parameterCountExtracted elements (i.e. the count of non-null values
   * of parameterMap).
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, parameterMap, fixedWeights = null ) {

    this.weightsModified = this.parameterMap = null; // So that distinguishable if re-initialization failed.

    if ( !parameterMap )
      return false;  // Do not know what parameters to be used or extracted.

    let inChannels = parameterMap.get( Params.Keys.inChannels );
    if ( !inChannels )
      return false;  // At least, there should be a (required) parameter (i.e. input channel count).

    this.parameterMap = new Map( parameterMap );  // Copy so that the original map will not be modified.

    // Collect what parameters should be extracted from input array (rather than use values in the parameterMap).
    // At the same time, its array index will also be recorded for extracting its value from array.
    let arrayIndexMap = new Map();
    {
      let i = 0;
      for ( let [ key, value ] of parameterMap ) {
        // A null (or undefined) value means it should be extracted from inputFloat32Array or fixedWeights (i.e. by evolution).
        //
        // Note: This is different from ( !value ). If value is 0, ( !value ) is true but ( null == value ) is false.
        if ( null == value ) {
          arrayIndexMap.set( key, i ); // Record the index to this.weightsModified[].
          ++i;
        }
      }
    }

    let parameterCountExtracted = arrayIndexMap.size; // Determine how many parameters should be extracted from array.

    // If has fixedWeights, use it as priviledge input.
    let privilegeInput;
    if ( fixedWeights ) {
      if ( fixedWeights instanceof Float32Array )
        privilegeInput = fixedWeights;
      else
        privilegeInput = new Float32Array( fixedWeights );  // Convert to Float32Array.
    }

    // Extract a block of input array.
    let bInitOk = super.init( inputFloat32Array, byteOffsetBegin, privilegeInput, 0, [ parameterCountExtracted ] );

    if ( !bInitOk )
      return false;

    // Copy and convert to integer.
    //
    // Do not modify the original array data. When backtracking (to try another neural network layer
    // configuration), the original data is necessary.
    this.weightsModified = new Float32Array( this.weights.length );
    for ( let i = 0; i < this.weightsModified.length; ++i ) {
      this.weightsModified[ i ] = Base.toPositiveInteger( this.weights[ i ] );
    }

    // Extract (by evolution) values from array into map.
    for ( let [ key, arrayIndex ] of arrayIndexMap ) {
      this.parameterMap.set( key, this.weightsModified[ arrayIndex ] );
    }

    // Restrict channelMultiplier to positive integer.
    //
    // If it is zero or negative, the outChannels (= inChannels * channelMultiplier ) will be strange value.
    // Strange outChannels value will affect the parameters extraction of the next neural network layer.
    let channelMultiplier = this.channelMultiplier;   // May be specified or extracted.
    if ( channelMultiplier < 1 ) {
      channelMultiplier = 1;
      this.parameterMap.set( Params.Keys.channelMultiplier, channelMultiplier );
    }

    // If original parameterMap has output channel count and its value is infinity, its value will depend
    // on channelMultiplier (i.e. by channelMultiplier).
    //
    // Usually, the embedding layer uses this behavior.
    let outChannelsOriginal = parameterMap.get( Params.Keys.outChannels );
    if ( outChannelsOriginal ) {
      if ( !Number.isFinite( outChannelsOriginal ) ) {
        let outChannels = inChannels * channelMultiplier;
        this.parameterMap.set( Params.Keys.outChannels, outChannels );
      } else {
        // Use the original value in parameterMap as output channel count (i.e. by specifying).
      }
    } else {
      // Either use the extracted value as output channel count (i.e. by evolution),
      // or there is no output channel count in fact.
    }

    return bInitOk;
  }

  get parameterCountExtracted() { return this.weightCount; }
  get parameterCount()          { return this.parameterMap.size; }

  // Most kinds of layers have these parameters.

  get inChannels()        { return this.parameterMap.get( Params.Keys.inChannels ); }
  get channelMultiplier() { return this.parameterMap.get( Params.Keys.channelMultiplier ); }
  get outChannels()       { return this.parameterMap.get( Params.Keys.outChannels ); }
}

/**
 * Define parameter keys.
 *
 * They are (static) symbol objects used as keys of Params.init()'s parameterMap. They can be seen inside Map when
 * debugging, and are faster than string (or String object) when Map's key comparing.
 */
Params.Keys = class {
//   // Although they are functions, they should not be called. They are (static function) objects used as keys of
//   // Params.init()'s parameterMap. Using function object (rather than general object) so that the (function) name
//   // can be seen inside Map when debugging. Using function object (rather than string or String object) so that
//   // Map's key comparing is faster.
//   static inChannels()        {}
//   static channelMultiplier() {}
//   static outChannels()       {}
}

Params.Keys.inChannels =        Symbol("inChannels");
Params.Keys.channelMultiplier = Symbol("channelMultiplier");
Params.Keys.outChannels =       Symbol("outChannels");

Params.Keys.dilationHeight =    Symbol("dilationHeight");
Params.Keys.dilationWidth =     Symbol("dilationWidth");
Params.Keys.filterHeight =      Symbol("filterHeight");
Params.Keys.filterWidth =       Symbol("filterWidth");
<|endoftext|>


<commit_before>'use strict';

var forEach             = require('es5-ext/object/for-each')
  , normalizeOptions    = require('es5-ext/object/normalize-options')
  , d                   = require('d')
  , generateId          = require('time-uuid')
  , resolvePropertyPath = require('dbjs/_setup/utils/resolve-property-path')

  , db = require('mano').db
  , ns = require('mano').domjs.ns;

/*
return object of form:
{
controls: ids || undefined,
legacy: lagacyScript || undefined
}
*/
module.exports = Object.defineProperty(db.FormSectionBase.prototype, 'getLegacy',
	d(function (formId/*, options */) {
		var result, legacyData, options = Object(arguments[1]), self, master;
		master = options.master || this.master;
		result = {};
		result.controls = {};
		self = this;
		this.constructor.propertyNames.forEach(function (item, propName) {
			var val, id, resolved, formFieldPath, propOptions;
			resolved = resolvePropertyPath(master, propName);
			formFieldPath = resolved.object.__id__ + '/' + resolved.key;
			if (self.inputOptions.has(propName)) {
				propOptions = normalizeOptions(self.inputOptions.get(propName));
				forEach(propOptions, function (value, name) {
					if (typeof value !== 'function') return;
					if (!value.isOptionResolver) return;
					propOptions[name] = value();
				});
				result.controls[formFieldPath] = propOptions;
			}
			val = resolved.object.getDescriptor(db.Object.getApplicablePropName(resolved.key)
				)._value_;
			if (typeof val !== 'function') {
				return;
			}
			if (!legacyData) {
				legacyData = [];
			}
			id = 'input-' + generateId();
			legacyData.push({ id: id, constraint: val, sKeyPath: propName });
			result.controls[formFieldPath] = normalizeOptions(result.controls[formFieldPath], { id: id });
		}, this);
		if (legacyData) {
			result.legacy = ns.legacy('formSectionStateHelper', formId, master.__id__,
				legacyData, options.legacyEntityProto);
		}

		return result;
	}));
<commit_msg>Resolve option in context of section<commit_after>'use strict';

var forEach             = require('es5-ext/object/for-each')
  , normalizeOptions    = require('es5-ext/object/normalize-options')
  , d                   = require('d')
  , generateId          = require('time-uuid')
  , resolvePropertyPath = require('dbjs/_setup/utils/resolve-property-path')

  , db = require('mano').db
  , ns = require('mano').domjs.ns;

/*
return object of form:
{
controls: ids || undefined,
legacy: lagacyScript || undefined
}
*/
module.exports = Object.defineProperty(db.FormSectionBase.prototype, 'getLegacy',
	d(function (formId/*, options */) {
		var result, legacyData, options = Object(arguments[1]), self, master;
		master = options.master || this.master;
		result = {};
		result.controls = {};
		self = this;
		this.constructor.propertyNames.forEach(function (item, propName) {
			var val, id, resolved, formFieldPath, propOptions;
			resolved = resolvePropertyPath(master, propName);
			formFieldPath = resolved.object.__id__ + '/' + resolved.key;
			if (self.inputOptions.has(propName)) {
				propOptions = normalizeOptions(self.inputOptions.get(propName));
				forEach(propOptions, function (value, name) {
					if (typeof value !== 'function') return;
					if (!value.isOptionResolver) return;
					propOptions[name] = value.call(this);
				});
				result.controls[formFieldPath] = propOptions;
			}
			val = resolved.object.getDescriptor(db.Object.getApplicablePropName(resolved.key)
				)._value_;
			if (typeof val !== 'function') {
				return;
			}
			if (!legacyData) {
				legacyData = [];
			}
			id = 'input-' + generateId();
			legacyData.push({ id: id, constraint: val, sKeyPath: propName });
			result.controls[formFieldPath] = normalizeOptions(result.controls[formFieldPath], { id: id });
		}, this);
		if (legacyData) {
			result.legacy = ns.legacy('formSectionStateHelper', formId, master.__id__,
				legacyData, options.legacyEntityProto);
		}

		return result;
	}));
<|endoftext|>


<commit_before>'use strict';
var d  = require('d')
  , generateId = require('time-uuid')
  , resolvePropertyPath = require('dbjs/_setup/utils/resolve-property-path')
  , normalizeOptions = require('es5-ext/object/normalize-options')
  , db = require('mano').db
  , ns = require('mano').domjs.ns;

/*
return object of form:
{
controls: ids || undefined,
legacy: lagacyScript || undefined
}
*/
module.exports = Object.defineProperty(db.FormSectionBase.prototype, 'getLegacy',
	d(function (formId/*, options */) {
		var result, legacyData, options = Object(arguments[1]), self, master;
		master = options.master || this.master;
		result = {};
		result.controls = {};
		self = this;
		this.constructor.propertyNames.forEach(function (item, propName) {
			var val, id, resolved, formFieldPath;
			resolved = resolvePropertyPath(this, propName);
			formFieldPath = resolved.object.__id__ + '/' + resolved.key;
			if (self.inputOptions.has(propName)) {
				result.controls[formFieldPath] = self.inputOptions.get(propName);
			}
			val = resolved.object.getDescriptor(db.Object.getApplicablePropName(resolved.key)
				)._value_;
			if (typeof val !== 'function') {
				return;
			}
			if (!legacyData) {
				legacyData = [];
			}
			id = 'input-' + generateId();
			legacyData.push({ id: id, constraint: val, sKeyPath: propName });
			result.controls[formFieldPath] = normalizeOptions(result.controls[formFieldPath], { id: id });
		}, master);
		if (legacyData) {
			result.legacy = ns.legacy('formSectionStateHelper', formId, master.__id__,
				legacyData, options.legacyEntityProto);
		}

		return result;
	}));
<commit_msg>Support values generated on demand<commit_after>'use strict';
var d  = require('d')
  , generateId = require('time-uuid')
  , resolvePropertyPath = require('dbjs/_setup/utils/resolve-property-path')
  , normalizeOptions = require('es5-ext/object/normalize-options')
  , db = require('mano').db
  , ns = require('mano').domjs.ns;

/*
return object of form:
{
controls: ids || undefined,
legacy: lagacyScript || undefined
}
*/
module.exports = Object.defineProperty(db.FormSectionBase.prototype, 'getLegacy',
	d(function (formId/*, options */) {
		var result, legacyData, options = Object(arguments[1]), self, master;
		master = options.master || this.master;
		result = {};
		result.controls = {};
		self = this;
		this.constructor.propertyNames.forEach(function (item, propName) {
			var val, id, resolved, formFieldPath, controlOption;
			resolved = resolvePropertyPath(this, propName);
			formFieldPath = resolved.object.__id__ + '/' + resolved.key;
			if (propName in self.inputOptions) {
				controlOption = self.inputOptions[propName];
				result.controls[formFieldPath] =
					(typeof controlOption === 'function') ? controlOption() : controlOption;
			}
			val = resolved.object.getDescriptor(db.Object.getApplicablePropName(resolved.key)
				)._value_;
			if (typeof val !== 'function') {
				return;
			}
			if (!legacyData) {
				legacyData = [];
			}
			id = 'input-' + generateId();
			legacyData.push({ id: id, constraint: val, sKeyPath: propName });
			result.controls[formFieldPath] = normalizeOptions(result.controls[formFieldPath], { id: id });
		}, master);
		if (legacyData) {
			result.legacy = ns.legacy('formSectionStateHelper', formId, master.__id__,
				legacyData, options.legacyEntityProto);
		}

		return result;
	}));
<|endoftext|>


<commit_before>/*
 * Copyright © 2015 Cask Data, Inc.
 *
 * 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.
 */

angular.module(PKG.name + '.services')
  .factory('myAppUploader', function(myFileUploader, $state, $alert, myAlert) {
    function upload(files, namespace) {

      for (var i = 0; i < files.length; i++) {
        myFileUploader.upload({
          path: '/namespaces/' + ($state.params.namespace || namespace) + '/apps',
          file: files[i]

        }, 'application/octet-stream')
          .then(success,error);
      }

      function success() {
        $alert({
          type: 'success',
          title: 'Upload success',
          content: 'The application has been uploaded successfully'
        });
        $state.reload();
      }

      // Independent xhr request. Failure case will not be handled by $rootScope.
      function error(err) {
        myAlert({
          type: 'danger',
          title: 'Upload failed',
          content: err || ''
        });
      }
    }

    return {
      upload: upload
    };
  });
<commit_msg>adds new error styling to custom app uploader<commit_after>/*
 * Copyright © 2015 Cask Data, Inc.
 *
 * 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.
 */

angular.module(PKG.name + '.services')
  .factory('myAppUploader', function(myFileUploader, $state, myAlertOnValium) {
    function upload(files, namespace) {

      for (var i = 0; i < files.length; i++) {
        myFileUploader.upload({
          path: '/namespaces/' + ($state.params.namespace || namespace) + '/apps',
          file: files[i]

        }, 'application/octet-stream')
          .then(success,error);
      }

      function success() {
        myAlertOnValium.show({
          type: 'success',
          title: 'Upload success',
          content: 'The application has been uploaded successfully'
        });
        $state.reload();
      }

      // Independent xhr request. Failure case will not be handled by $rootScope.
      function error(err) {
        myAlertOnValium.show({
          type: 'danger',
          title: 'Upload failed',
          content: err || '',
          duration: false
        });
      }
    }

    return {
      upload: upload
    };
  });
<|endoftext|>


<commit_before>importModule('core.string');
importModule('helma.logging', 'logging');
var log = logging.getLogger('helma.skin');

/*global getResource importModule parseSkin */

/**
 * Parse a skin from a resource and render it using the given context.
 * @param skinOrResource
 * @param context
 */
function renderSkin(skinOrPath, context, scope) {
    scope = scope || this;
    var skin = (skinOrPath instanceof Skin) ?
           skinOrPath : createSkin(scope.getResource(skinOrPath), scope);
    skin.render(context);
}

/**
 * Parse a skin from a resource.
 * @param resource
 */
function createSkin(resourceOrString, scope) {
    var mainSkin = [];
    var subSkins = {};
    var currentSkin = mainSkin;
    var parentSkin = null;
    parseSkin(resourceOrString, function(part) {
        if (part.name === 'extends') {
            var skinPath = part.getParameter(0);
            var skinResource;
            if (resourceOrString.repository) {
                skinResource = resourceOrString.repository.getResource(skinPath);
            }
            if (!skinResource || !skinResource.exists()) {
                skinResource = scope.getResource(skinPath);
            }
            parentSkin = createSkin(skinResource);
        } else if (part.name === 'subskin')  {
            var skinName = part.getParameter('name', 0);
            currentSkin = [];
            subSkins[skinName] = currentSkin;
        } else {
            currentSkin[currentSkin.length] = part;
        }
    });
    // normalization: cut trailing whitespace so it's
    // easier to tell if main skin shoule be inherited
    var lastPart = mainSkin[mainSkin.length - 1];
    if (typeof(lastPart) === 'string' && lastPart.trim() === '') {
        mainSkin.pop();
    }
    return new Skin(mainSkin, subSkins, parentSkin);
}

/**
 * The Skin object. This takes an array of skin parts (literal strings and MacroTags)
 * and a dictionary of subskins.
 * @param mainSkin an array of skin parts: string literals and macro tags
 * @param subSkins a dictionary of named skin components
 */
function Skin(mainSkin, subSkins, parentSkin) {

    var self = this;

    this.render = function(context) {
        if (mainSkin.length === 0 && parentSkin) {
            renderInternal(parentSkin.getSkinParts(), context);
        } else {
            renderInternal(mainSkin, context);
        }
    };

    this.renderSubskin = function(skinName, context) {
        if (!subSkins[skinName] && parentSkin) {
            renderInternal(parentSkin.getSkinParts(skinName), context);
        } else {
            renderInternal(subSkins[skinName], context);
        }
    };

    this.getSubskin = function(skinName) {
        if (subSkins[skinName]) {
            return new Skin(subSkins[skinName], subSkins, parentSkin);
        } else {
            return null;
        }
    };

    this.getSkinParts = function(skinName) {
        var parts = skinName ? subSkins[skinName] : mainSkin;
        if (!parts || (!skinName && parts.length === 0)) {
            return parentSkin ? parentSkin.getSkinParts(skinName) : null;
        }
        return parts;
    };

    var renderInternal = function(parts, context) {
        for (var i in parts) {
            var part = parts[i];
            if (part instanceof MacroTag) {
                if (part.name) {
                    evaluateMacro(part, context);
                }
            } else {
                res.write(part);
            }
        }
    };

    var evaluateMacro = function(macro, context, paramIndex) {
        paramIndex = paramIndex || 0;
        var length = res.buffer.length;
        if (macro.name in builtins) {
            builtins[macro.name](macro, context, paramIndex);
        } else {
            var value = evaluateExpression(macro, context, '_macro');
            var visibleValue = isVisible(value);
            var wroteSomething = res.buffer.length > length;

            // check if macro has a filter, if so extra work ahead
            if (macro.filter) {
                // make sure value is not undefined,
                // otherwise evaluateExpression() might become confused
                if (!visibleValue) {
                    value = "";
                }
                if (wroteSomething) {
                    var written = res.buffer.cutOff(length);
                    if (visibleValue) {
                        value = written + value;
                    } else {
                        value = written;
                    }
                }
                value = evaluateExpression(macro.filter, context, '_filter', value);
                visibleValue = isVisible(value);
                wroteSomething = res.buffer.length > length;
            }
            if (visibleValue || wroteSomething) {
                res.buffer.insert(length, macro.getParameter('prefix') || '');
                if (visibleValue) {
                    res.write(value);
                }
                res.write(macro.getParameter('suffix') || '');
            } else {
                res.write(macro.getParameter('default') || '');
            }
        }
    };

    var evaluateExpression = function(macro, context, suffix, value) {
        var path = macro.name.split('.');
        var elem = context;
        var length = path.length;
        var last = path[length-1];
        for (var i = 0; i < length - 1; i++) {
            elem = elem[path[i]];
            if (!isDefined(elem)) {
                break;
            }
        }
        if (isDefined(elem)) {
            if (elem[last + suffix] instanceof Function) {
                return value === undefined ?
                       elem[last + suffix].call(elem, macro, self, context) :
                       elem[last + suffix].call(elem, value, macro, self, context);
            } else if (value === undefined && isDefined(elem[last])) {
                elem = elem[path[length-1]];
                if (elem instanceof Function) {
                    return elem(macro, self, context);
                } else {
                    return elem;
                }
            }
        }
        return undefined;
    };

    var isDefined = function(elem) {
        return elem !== undefined && elem !== null;
    }

    var isVisible = function(elem) {
        return elem !== undefined && elem !== null && elem !== '';
    }

    // builtin macro handlers
    var builtins = {
        render: function(macro, context, paramIndex) {
            var skinName = macro.getParameter(paramIndex);
            self.renderSubskin(skinName, context);
        },
        // experimental if tag
        "if": function(macro, context, paramIndex) {
            var condition = macro.getParameter(paramIndex);
            if (evaluateExpression(condition, context, '_macro')) {
                evaluateMacro(macro, context, paramIndex + 1);
            }
        }
    };

    this.toString = function() {
        return "[Skin Object]";
    };

}
<commit_msg>* Return unmodified value if macro filter is not defined.<commit_after>importModule('core.string');
importModule('helma.logging', 'logging');
var log = logging.getLogger('helma.skin');

/*global getResource importModule parseSkin */

/**
 * Parse a skin from a resource and render it using the given context.
 * @param skinOrResource
 * @param context
 */
function renderSkin(skinOrPath, context, scope) {
    scope = scope || this;
    var skin = (skinOrPath instanceof Skin) ?
           skinOrPath : createSkin(scope.getResource(skinOrPath), scope);
    skin.render(context);
}

/**
 * Parse a skin from a resource.
 * @param resource
 */
function createSkin(resourceOrString, scope) {
    var mainSkin = [];
    var subSkins = {};
    var currentSkin = mainSkin;
    var parentSkin = null;
    parseSkin(resourceOrString, function(part) {
        if (part.name === 'extends') {
            var skinPath = part.getParameter(0);
            var skinResource;
            if (resourceOrString.repository) {
                skinResource = resourceOrString.repository.getResource(skinPath);
            }
            if (!skinResource || !skinResource.exists()) {
                skinResource = scope.getResource(skinPath);
            }
            parentSkin = createSkin(skinResource);
        } else if (part.name === 'subskin')  {
            var skinName = part.getParameter('name', 0);
            currentSkin = [];
            subSkins[skinName] = currentSkin;
        } else {
            currentSkin[currentSkin.length] = part;
        }
    });
    // normalization: cut trailing whitespace so it's
    // easier to tell if main skin shoule be inherited
    var lastPart = mainSkin[mainSkin.length - 1];
    if (typeof(lastPart) === 'string' && lastPart.trim() === '') {
        mainSkin.pop();
    }
    return new Skin(mainSkin, subSkins, parentSkin);
}

/**
 * The Skin object. This takes an array of skin parts (literal strings and MacroTags)
 * and a dictionary of subskins.
 * @param mainSkin an array of skin parts: string literals and macro tags
 * @param subSkins a dictionary of named skin components
 */
function Skin(mainSkin, subSkins, parentSkin) {

    var self = this;

    this.render = function(context) {
        if (mainSkin.length === 0 && parentSkin) {
            renderInternal(parentSkin.getSkinParts(), context);
        } else {
            renderInternal(mainSkin, context);
        }
    };

    this.renderSubskin = function(skinName, context) {
        if (!subSkins[skinName] && parentSkin) {
            renderInternal(parentSkin.getSkinParts(skinName), context);
        } else {
            renderInternal(subSkins[skinName], context);
        }
    };

    this.getSubskin = function(skinName) {
        if (subSkins[skinName]) {
            return new Skin(subSkins[skinName], subSkins, parentSkin);
        } else {
            return null;
        }
    };

    this.getSkinParts = function(skinName) {
        var parts = skinName ? subSkins[skinName] : mainSkin;
        if (!parts || (!skinName && parts.length === 0)) {
            return parentSkin ? parentSkin.getSkinParts(skinName) : null;
        }
        return parts;
    };

    var renderInternal = function(parts, context) {
        for (var i in parts) {
            var part = parts[i];
            if (part instanceof MacroTag) {
                if (part.name) {
                    evaluateMacro(part, context);
                }
            } else {
                res.write(part);
            }
        }
    };

    var evaluateMacro = function(macro, context, paramIndex) {
        paramIndex = paramIndex || 0;
        var length = res.buffer.length;
        if (macro.name in builtins) {
            builtins[macro.name](macro, context, paramIndex);
        } else {
            var value = evaluateExpression(macro, context, '_macro');
            var visibleValue = isVisible(value);
            var wroteSomething = res.buffer.length > length;

            // check if macro has a filter, if so extra work ahead
            if (macro.filter) {
                // make sure value is not undefined,
                // otherwise evaluateExpression() might become confused
                if (!visibleValue) {
                    value = "";
                }
                if (wroteSomething) {
                    var written = res.buffer.cutOff(length);
                    if (visibleValue) {
                        value = written + value;
                    } else {
                        value = written;
                    }
                }
                value = evaluateExpression(macro.filter, context, '_filter', value);
                visibleValue = isVisible(value);
                wroteSomething = res.buffer.length > length;
            }
            if (visibleValue || wroteSomething) {
                res.buffer.insert(length, macro.getParameter('prefix') || '');
                if (visibleValue) {
                    res.write(value);
                }
                res.write(macro.getParameter('suffix') || '');
            } else {
                res.write(macro.getParameter('default') || '');
            }
        }
    };

    var evaluateExpression = function(macro, context, suffix, value) {
        var path = macro.name.split('.');
        var elem = context;
        var length = path.length;
        var last = path[length-1];
        for (var i = 0; i < length - 1; i++) {
            elem = elem[path[i]];
            if (!isDefined(elem)) {
                break;
            }
        }
        if (isDefined(elem)) {
            if (elem[last + suffix] instanceof Function) {
                return value === undefined ?
                       elem[last + suffix].call(elem, macro, self, context) :
                       elem[last + suffix].call(elem, value, macro, self, context);
            } else if (value === undefined && isDefined(elem[last])) {
                elem = elem[path[length-1]];
                if (elem instanceof Function) {
                    return elem(macro, self, context);
                } else {
                    return elem;
                }
            }
        }
        // TODO: if filter is not found just return value as is
        return value;
    };

    var isDefined = function(elem) {
        return elem !== undefined && elem !== null;
    }

    var isVisible = function(elem) {
        return elem !== undefined && elem !== null && elem !== '';
    }

    // builtin macro handlers
    var builtins = {
        render: function(macro, context, paramIndex) {
            var skinName = macro.getParameter(paramIndex);
            self.renderSubskin(skinName, context);
        },
        // experimental if tag
        "if": function(macro, context, paramIndex) {
            var condition = macro.getParameter(paramIndex);
            if (evaluateExpression(condition, context, '_macro')) {
                evaluateMacro(macro, context, paramIndex + 1);
            }
        }
    };

    this.toString = function() {
        return "[Skin Object]";
    };

}
<|endoftext|>


<commit_before>/*eslint-env mocha*/
describe('useHistoryRestoreScroll', () => {
  describe('intial history entry', () => {
    it('restores window scroll when going back to it')
    it('restores window scroll when going back from a different domain')
    it('restores different positions for different entries in the history of the same url')
  })

  describe('second history entry', () => {
    // we don't control location.state on the first entry but we do for the
    // rest of them, so we have to have some code to workaround that, thus, two
    // sets of tests for the same stuff since
    it('restores window scroll when going back')
    it('restores window scroll when going back from a different domain')
    it('restores different positions for different entries in the history of the same url')
  })

})

describe('RestoreScroll', () => {
  it('restores scroll positions of scrollable elements')
})

<commit_msg>exit tests correctly<commit_after>/*eslint-env mocha*/
import expect from 'expect'

describe('useHistoryRestoreScroll', () => {
  it('exits correctly', () => {
    expect(true).toBe(true)
  })
  describe('intial history entry', () => {
    it('restores window scroll when going back to it')
    it('restores window scroll when going back from a different domain')
    it('restores different positions for different entries in the history of the same url')
  })

  describe('second history entry', () => {
    // we don't control location.state on the first entry but we do for the
    // rest of them, so we have to have some code to workaround that, thus, two
    // sets of tests for the same stuff since
    it('restores window scroll when going back')
    it('restores window scroll when going back from a different domain')
    it('restores different positions for different entries in the history of the same url')
  })

})

describe('RestoreScroll', () => {
  it('restores scroll positions of scrollable elements')
})

<|endoftext|>


<commit_before>var request = require('request');
var twitchClientID = require('../config/botlogin.js').twitchClientID;

function getHitBoxStatus(user, callback){
	request('https://api.hitbox.tv/user/' + user, function(error, response, body){
		if(error || response.statusCode !== 200){
			return callback(error);
		}
		body = JSON.parse(body);
		if(body.is_live == 1){
			return callback(null, true, 'http://edge.sf.hitbox.tv/' + body.user_logo);
		}
		else{
			return callback(null, false);
		}
	});
}

module.exports.getTwitchStream = function(user, callback){
    request('https://api.twitch.tv/kraken/streams/' + user + "?client_id=" + twitchClientID, function(error, response, body){
		if(error || response.statusCode !== 200){
			if(!error) var error = response.statusCode;
			return callback(error);
		}
		body = JSON.parse(body);
		if(body.stream != null){
			return callback(null, true, body.stream.game, body.stream.channel.url, body.stream.channel.logo);
		} else{
			return callback(null, false);
		}
	});
}

module.exports.getHitboxStream = function(user, callback){
	getHitBoxStatus(user, function(error, status, user_logo){
		if(error) return callback(error);
    	if(status){
    		request('https://api.hitbox.tv/media/live/' + user, function(error, response, body){
				if(error || response.statusCode !== 200){
					return callback(error);
				}
				body = JSON.parse(body);
				return callback(null, true, body.livestream[0].category_name, "http://www.hitbox.tv/" + user, user_logo);
			});
    	} else {
    		return callback(null, false);
    	}
    });
}<commit_msg>Removed HitBox request; No longer HitBox<commit_after>var request = require('request');
var twitchClientID = require('../config/botlogin.js').twitchClientID;

module.exports.getTwitchStream = function(user, callback){
    request('https://api.twitch.tv/kraken/streams/' + user + "?client_id=" + twitchClientID, function(error, response, body){
		if(error || response.statusCode !== 200){
			if(!error) var error = response.statusCode;
			return callback(error);
		}
		body = JSON.parse(body);
		if(body.stream != null){
			return callback(null, true, body.stream.game, body.stream.channel.url, body.stream.channel.logo);
		} else{
			return callback(null, false);
		}
	});
}<|endoftext|>


<commit_before>kindling.module(function () {
	'use strict';

	function publishNotification(e, options, username, message) {
		if (!options || !username || !message || options.notifications !== 'true') {
			return;
		}

		var $message = $(message);
		if (message.id.indexOf('message_') !== -1 && message.id.indexOf('message_pending') === -1 && !($message.is('.enter_message,.leave_message,.kick_message,.timestamp_message,.you'))) {
			var $body, $author = $message.find('.author:first');
			if ($message.is('.topic_change_message')) {
				$body = $message.find('.body:first');
			} else {
				$body = $message.find('code:first');
				if ($body.length === 0) {
					$body = $message.find('div:.body:first');
				}
			}

			if (options.filterNotifications === 'true') {
				var regex = kindling.getUsernameRegex(username);
				if (!regex.test($body.html())) {
					return;
				}
			}

			chrome.extension.sendRequest({
				type: 'notification',
				value: {
					username: username,
					room: $('#room_name').html(),
					author: $author.text(),
					avatar: $author.attr('data-avatar'),
					message: $body.html()
				}
			});
		}
	}

	return {
		init: function () {
			$.subscribe('newMessage', publishNotification);
		}
	};
}());
<commit_msg>Added support for room locking notifications.<commit_after>kindling.module(function () {
	'use strict';

	function publishNotification(e, options, username, message) {
		if (!options || !username || !message || options.notifications !== 'true') {
			return;
		}

		var $message = $(message);
		if (message.id.indexOf('message_') !== -1 && message.id.indexOf('message_pending') === -1 && !($message.is('.enter_message,.leave_message,.kick_message,.timestamp_message,.you'))) {
			var $body, $author = $message.find('.author:first');
			if ($message.hasClass('topic_change_message') || $message.hasClass('lock_message') || $message.hasClass('unlock_message')) {
				$body = $message.find('.body:first');
			} else {
				$body = $message.find('code:first');
				if ($body.length === 0) {
					$body = $message.find('div:.body:first');
				}
			}

			if (options.filterNotifications === 'true') {
				var regex = kindling.getUsernameRegex(username);
				if (!regex.test($body.html())) {
					return;
				}
			}

			chrome.extension.sendRequest({
				type: 'notification',
				value: {
					username: username,
					room: $('#room_name').html(),
					author: $author.text(),
					avatar: $author.attr('data-avatar'),
					message: $body.html()
				}
			});
		}
	}

	return {
		init: function () {
			$.subscribe('newMessage', publishNotification);
		}
	};
}());
<|endoftext|>


<commit_before>
var {Headers, getMimeParameter} = require('ringo/webapp/util');
var {Stream} = require('io');
var {Binary, ByteString} = require('binary');
var system = require('system')

export('handleRequest');
var log = require('ringo/logging').getLogger(module.id);

/**
 * Handle a JSGI request.
 * @param moduleId the module id. Ignored if functionObj is already a function.
 * @param functionObj the function, either as function object or function name to be
 *             imported from the module moduleId.
 * @param req the JSGI request object
 * @returns the JSGI response object
 */
function handleRequest(moduleId, functionObj, request) {
    initRequest(request);
    var app;
    if (typeof(functionObj) == 'function') {
        app = functionObj;
    } else {
        var module = require(moduleId);
        app = module[functionObj];
        var middleware = module.middleware || [];
        request.env.ringo_config = moduleId;
        app = middleware.reduceRight(middlewareWrapper, resolve(app));
    }
    if (!(typeof(app) == 'function')) {
        throw new Error('No valid JSGI app: ' + app);
    }
    var result = app(request);
    if (!result) {
        throw new Error('No valid JSGI response: ' + result);
    }
    commitResponse(request, result);
}

/**
 * Set up the I/O related properties of a jsgi environment object.
 * @param env a jsgi request object
 */
function initRequest(request) {
    var input, errors;
    if (request.hasOwnProperty('input')) {
        // already set up, probably because the original request threw a retry
        return;
    }
    Object.defineProperty(request, "input", {
        get: function() {
            if (!input)
                input = new Stream(request.env.servletRequest.getInputStream());
            return input;
        }
    });
    Object.defineProperty(request.jsgi, "errors", {
        value: system.stderr
    });
}

/**
 * Apply the return value of a JSGI application to a servlet response.
 * This is used internally by the org.ringojs.jsgi.JsgiServlet class, so
 * you won't need this unless you're implementing your own servlet
 * based JSGI connector.
 *
 * @param req the JSGI request argument
 * @param result the object returned by a JSGI application
 */
function commitResponse(req, result) {
    var request = req.env.servletRequest;
    var response = req.env.servletResponse;
    if (response.isCommitted()) {
        // Allow application/middleware to handle request via Servlet API
        return;
    }
    var {status, headers, body} = result;
    if (!status || !headers || !body) {
        // As a convenient shorthand, we also handle async responses returning the
        // not only the promise but the full deferred (as obtained by
        // ringo.promise.defer()).
        if (result.promise && typeof result.promise.then === "function") {
            result = result.promise;
        }
        // If the response has a "then" function, we asume it is a promise and
        // switch to async reponse handling.
        if (typeof result.then === "function") {
            handleAsyncResponse(request, result);
            return;
        }
        throw new Error('No valid JSGI response: ' + result);
    }
    response.setStatus(status);
    for (var key in headers) {
        var values = headers[key];
        if (typeof values === "string") {
            values = values.split("\n");
        } else if (!Array.isArray(values)) {
            continue;
        }
        values.forEach(function(value) {
            response.addHeader(key, value);
        });
    }
    var charset = getMimeParameter(Headers(headers).get("Content-Type"), "charset");
    writeBody(response, body, charset);
}

function writeBody(response, body, charset) {
    if (body && typeof body.forEach == "function") {
        var output = response.getOutputStream();
        var writer = function(part) {
            if (!(part instanceof Binary)) {
                part = part.toByteString(charset);
            }
            output.write(part);
        };
        body.forEach(writer);
        if (typeof body.close == "function") {
            body.close(writer);
        }
        output.close();
    } else {
        throw new Error("Response body doesn't implement forEach: " + body);
    }
}

function writeAsync(servletResponse, jsgiResponse) {
    if (!jsgiResponse.status || !jsgiResponse.headers || !jsgiResponse.body) {
        throw new Error('No valid JSGI response: ' + jsgiResponse);
    }
    var {status, headers} = jsgiResponse;
    servletResponse.status = status;
    for (var name in headers) {
        servletResponse.setHeader(name, headers[name]);
    }
    var charset = getMimeParameter(Headers(headers).get("Content-Type"), "charset");
    writeBody(servletResponse, jsgiResponse.body, charset);
}

function handleAsyncResponse(request, result) {
    // experimental support for asynchronous JSGI based on Jetty continuations
    var ContinuationSupport = org.eclipse.jetty.continuation.ContinuationSupport;
    var continuation = ContinuationSupport.getContinuation(request);
    var handled = false;

    var onFinish = sync(function(value) {
        if (handled) return;
        log.debug("JSGI async response finished", value);
        handled = true;
        if (value && typeof value.close === 'function') {
            value = value.close();
        }
        writeAsync(continuation.getServletResponse(), value);
        continuation.complete();
    }, request);

    var onError = sync(function(error) {
        if (handled) return;
        log.error("JSGI async error", error);
        var jsgiResponse = {
            status: 500,
            headers: {"Content-Type": "text/html"},
            body: ["<!DOCTYPE html>
Error

", String(error), "
"]
        };
        handled = true;
        writeAsync(continuation.getServletResponse(), jsgiResponse);
        continuation.complete();
    }, request);

    continuation.addContinuationListener(new org.eclipse.jetty.continuation.ContinuationListener({
        onTimeout: sync(function() {
            if (handled) return;
            log.error("JSGI async timeout");
            var jsgiResponse = {
                status: 500,
                headers: {"Content-Type": "text/html"},
                body: ["<!DOCTYPE html>
Error

Request timed out
"]
            };
            handled = true;
            writeAsync(continuation.getServletResponse(), jsgiResponse);
            continuation.complete();
        }, request)
    }));

    log.debug('handling JSGI async response, calling then');
    sync(function() {
        result.then(onFinish, onError);
        // default async request timeout is 30 seconds
        continuation.setTimeout(30000);
        continuation.suspend();
    }, request)();
}

/**
 * Convenience function that resolves a module id or object to a
 * JSGI middleware or application function. This assumes the function is
 * exported as "middleware" or "handleRequest".
 * @param module a function, module object, or moudule id
 */
function resolve(module) {
    if (typeof module == 'string') {
        module = require(module);
        return module.middleware || module.handleRequest;
    }
    return module;
}

/**
 * Helper function for wrapping middleware stacks
 * @param inner an app or middleware module or function wrapped by outer
 * @param outer a middleware module or function wrapping inner
 */
function middlewareWrapper(inner, outer) {
    return resolve(outer)(inner);
}
<commit_msg>Add experimental X-JSGI-Skip-Response header<commit_after>
var {Headers, getMimeParameter} = require('ringo/webapp/util');
var {Stream} = require('io');
var {Binary, ByteString} = require('binary');
var system = require('system')

export('handleRequest');
var log = require('ringo/logging').getLogger(module.id);

/**
 * Handle a JSGI request.
 * @param moduleId the module id. Ignored if functionObj is already a function.
 * @param functionObj the function, either as function object or function name to be
 *             imported from the module moduleId.
 * @param req the JSGI request object
 * @returns the JSGI response object
 */
function handleRequest(moduleId, functionObj, request) {
    initRequest(request);
    var app;
    if (typeof(functionObj) == 'function') {
        app = functionObj;
    } else {
        var module = require(moduleId);
        app = module[functionObj];
        var middleware = module.middleware || [];
        request.env.ringo_config = moduleId;
        app = middleware.reduceRight(middlewareWrapper, resolve(app));
    }
    if (!(typeof(app) == 'function')) {
        throw new Error('No valid JSGI app: ' + app);
    }
    var result = app(request);
    if (!result) {
        throw new Error('No valid JSGI response: ' + result);
    }
    commitResponse(request, result);
}

/**
 * Set up the I/O related properties of a jsgi environment object.
 * @param env a jsgi request object
 */
function initRequest(request) {
    var input, errors;
    if (request.hasOwnProperty('input')) {
        // already set up, probably because the original request threw a retry
        return;
    }
    Object.defineProperty(request, "input", {
        get: function() {
            if (!input)
                input = new Stream(request.env.servletRequest.getInputStream());
            return input;
        }
    });
    Object.defineProperty(request.jsgi, "errors", {
        value: system.stderr
    });
}

/**
 * Apply the return value of a JSGI application to a servlet response.
 * This is used internally by the org.ringojs.jsgi.JsgiServlet class, so
 * you won't need this unless you're implementing your own servlet
 * based JSGI connector.
 *
 * @param req the JSGI request argument
 * @param result the object returned by a JSGI application
 */
function commitResponse(req, result) {
    var request = req.env.servletRequest;
    var response = req.env.servletResponse;
    if (response.isCommitted()) {
        // Allow application/middleware to handle request via Servlet API
        return;
    }
    var {status, headers, body} = result;
    if (!status || !headers || !body) {
        // As a convenient shorthand, we also handle async responses returning the
        // not only the promise but the full deferred (as obtained by
        // ringo.promise.defer()).
        if (result.promise && typeof result.promise.then === "function") {
            result = result.promise;
        }
        // If the response has a "then" function, we asume it is a promise and
        // switch to async reponse handling.
        if (typeof result.then === "function") {
            handleAsyncResponse(request, result);
            return;
        }
        throw new Error('No valid JSGI response: ' + result);
    }
    // special convention to skip writing the response
    if (Headers(headers).get("X-JSGI-Skip-Response")) {
        return;
    }
    response.setStatus(status);
    for (var key in headers) {
        var values = headers[key];
        if (typeof values === "string") {
            values = values.split("\n");
        } else if (!Array.isArray(values)) {
            continue;
        }
        values.forEach(function(value) {
            response.addHeader(key, value);
        });
    }
    var charset = getMimeParameter(headers.get("Content-Type"), "charset");
    writeBody(response, body, charset);
}

function writeBody(response, body, charset) {
    if (body && typeof body.forEach == "function") {
        var output = response.getOutputStream();
        var writer = function(part) {
            if (!(part instanceof Binary)) {
                part = part.toByteString(charset);
            }
            output.write(part);
        };
        body.forEach(writer);
        if (typeof body.close == "function") {
            body.close(writer);
        }
        output.close();
    } else {
        throw new Error("Response body doesn't implement forEach: " + body);
    }
}

function writeAsync(servletResponse, jsgiResponse) {
    if (!jsgiResponse.status || !jsgiResponse.headers || !jsgiResponse.body) {
        throw new Error('No valid JSGI response: ' + jsgiResponse);
    }
    var {status, headers} = jsgiResponse;
    servletResponse.status = status;
    for (var name in headers) {
        servletResponse.setHeader(name, headers[name]);
    }
    var charset = getMimeParameter(Headers(headers).get("Content-Type"), "charset");
    writeBody(servletResponse, jsgiResponse.body, charset);
}

function handleAsyncResponse(request, result) {
    // experimental support for asynchronous JSGI based on Jetty continuations
    var ContinuationSupport = org.eclipse.jetty.continuation.ContinuationSupport;
    var continuation = ContinuationSupport.getContinuation(request);
    var handled = false;

    var onFinish = sync(function(value) {
        if (handled) return;
        log.debug("JSGI async response finished", value);
        handled = true;
        if (value && typeof value.close === 'function') {
            value = value.close();
        }
        writeAsync(continuation.getServletResponse(), value);
        continuation.complete();
    }, request);

    var onError = sync(function(error) {
        if (handled) return;
        log.error("JSGI async error", error);
        var jsgiResponse = {
            status: 500,
            headers: {"Content-Type": "text/html"},
            body: ["<!DOCTYPE html>
Error

", String(error), "
"]
        };
        handled = true;
        writeAsync(continuation.getServletResponse(), jsgiResponse);
        continuation.complete();
    }, request);

    continuation.addContinuationListener(new org.eclipse.jetty.continuation.ContinuationListener({
        onTimeout: sync(function() {
            if (handled) return;
            log.error("JSGI async timeout");
            var jsgiResponse = {
                status: 500,
                headers: {"Content-Type": "text/html"},
                body: ["<!DOCTYPE html>
Error

Request timed out
"]
            };
            handled = true;
            writeAsync(continuation.getServletResponse(), jsgiResponse);
            continuation.complete();
        }, request)
    }));

    log.debug('handling JSGI async response, calling then');
    sync(function() {
        result.then(onFinish, onError);
        // default async request timeout is 30 seconds
        continuation.setTimeout(30000);
        continuation.suspend();
    }, request)();
}

/**
 * Convenience function that resolves a module id or object to a
 * JSGI middleware or application function. This assumes the function is
 * exported as "middleware" or "handleRequest".
 * @param module a function, module object, or moudule id
 */
function resolve(module) {
    if (typeof module == 'string') {
        module = require(module);
        return module.middleware || module.handleRequest;
    }
    return module;
}

/**
 * Helper function for wrapping middleware stacks
 * @param inner an app or middleware module or function wrapped by outer
 * @param outer a middleware module or function wrapping inner
 */
function middlewareWrapper(inner, outer) {
    return resolve(outer)(inner);
}
<|endoftext|>