<commit_before>var log = require('debug')('mongoose-hidden');
Object.assign = require('object-assign');

exports = module.exports = function (defaults) {
  var _defaultOptions = {
    autoHide: true,
    autoHideJSON: true,
    autoHideObject: true,
    defaultHidden: { '_id': true, '__v': true },
    virtuals: { },
  };
  var _defaults = defaults || _defaultOptions;
  _defaults = Object.assign(_defaultOptions, _defaults);

  log(_defaults);

  return function (schema, options) {
    options = options || { };

    // Set defaults from options and default
    options = {
      hide: typeof options['hide'] === 'undefined' ? _defaults.autoHide : !!options['hide'],
      hideJSON: typeof options['hideJSON'] === 'undefined' ? _defaults.autoHideJSON : !!options['hideJSON'],
      hideObject: typeof options['hideObject'] === 'undefined' ? _defaults.autoHideObject : !!options['hideObject'],
      defaultHidden: typeof options['defaultHidden'] === 'undefined' ? _defaults.defaultHidden : options['defaultHidden'],
      virtuals: typeof options['virtuals'] === 'undefined' ? _defaults.virtuals : options['virtuals']
    };

    // Set defaults based on `hide`
    options.hideJSON = options.hide === false ? false : options.hideJSON;
    options.hideObject = options.hide === false ? false : options.hideObject;

    log(options);

    var schemaPaths = {};
    var key = null;

    var transformer = function (target, prevTransform) {
      return function (doc, ret, opt) {
        if (typeof doc.ownerDocument === 'function') {
          return ret;
        }
        log(ret);
        var finalTransform = { };
        if (typeof prevTransform === 'function') {
          log(target + ': running existing transform function');
          finalTransform = prevTransform(doc, ret, opt);
          log(finalTransform);
        }
        schema.eachPath(function (pathname, schemaType) {
          var _pathname = pathname;
          if (pathname.indexOf('.') !== -1) {
            _pathname = pathname.substring(0, pathname.indexOf('.'));
          }
          schemaPaths[_pathname] = true;
          if (
            options['hide' + target] === false
            || typeof options.defaultHidden[_pathname] === 'undefined' && typeof options.virtuals[_pathname] === 'undefined'
            && (
              (typeof schemaType.options['hide'] === 'undefined' && typeof schemaType.options['hide' + target] === 'undefined')
              || (
                (schemaType.options['hide'] !== true && schemaType.options['hide' + target] !== true)
                && (typeof schemaType.options['hide'] !== 'function' || schemaType.options['hide'](doc, ret) !== true)
                && (typeof schemaType.options['hide' + target] !== 'function' || schemaType.options['hide' + target](doc, ret) !== true)
              )
            )
          ) {
            log(target + ': copy "' + _pathname + '"');
            if (typeof finalTransform[_pathname] === 'undefined') {
              finalTransform[_pathname] = ret[_pathname];
            }
          } else {
            log(target + ': hiding "' + _pathname + '"');
            delete finalTransform[_pathname];
          }
        });
        // TODO find a better way to include virtuals
        for (key in ret) {
          if (typeof schemaPaths[key] === 'undefined' && (typeof options.virtuals[key] === 'undefined' || (options.virtuals[key] !== 'hide' && options.virtuals[key] !== ('hide' + target)))) {
            log(target + ': copy "' + key + '"');
            finalTransform[key] = ret[key];
          }
        }
        log(finalTransform);
        return finalTransform;
      };
    };

    var toJSONOptions = schema.get('toJSON') || { };

    schema.set('toJSON', {
      getters: toJSONOptions['getters'] || false,
      virtuals: toJSONOptions['virtuals'] || false,
      transform: transformer('JSON', toJSONOptions['transform'] || null)
    });

    var toObjectOptions = schema.get('toObject') || { };

    schema.set('toObject', {
      getters: toObjectOptions['getters'] || false,
      virtuals: toObjectOptions['virtuals'] || false,
      transform: transformer('Object', toObjectOptions['transform'] || null)
    });
  };
};
<commit_msg>Extracted hide logic for readability<commit_after>var log = require('debug')('mongoose-hidden');
Object.assign = require('object-assign');

function shouldHide(schemaType, options, target, doc, ret, _pathname) {
  return ! (
        options['hide' + target] === false
    || !options.defaultHidden[_pathname]
    && !options.virtuals[_pathname]
    && (
          (
              typeof schemaType.options['hide'] === 'undefined'
            && typeof schemaType.options['hide' + target] === 'undefined'
          )
       || (
              (
                   schemaType.options['hide'] !== true
                && schemaType.options['hide' + target] !== true
              )
           && (
                   typeof schemaType.options['hide'] !== 'function'
                || schemaType.options['hide'](doc, ret) !== true
              )
           && (
                   typeof schemaType.options['hide' + target] !== 'function'
                || schemaType.options['hide' + target](doc, ret) !== true
              )
          )
        )
  )
}

exports = module.exports = function (defaults) {
  var _defaultOptions = {
    autoHide: true,
    autoHideJSON: true,
    autoHideObject: true,
    defaultHidden: { '_id': true, '__v': true },
    virtuals: { },
  };
  var _defaults = defaults || _defaultOptions;
  _defaults = Object.assign(_defaultOptions, _defaults);

  log(_defaults);

  return function (schema, options) {
    options = options || { };

    // Set defaults from options and default
    options = {
      hide: typeof options['hide'] === 'undefined' ? _defaults.autoHide : !!options['hide'],
      hideJSON: typeof options['hideJSON'] === 'undefined' ? _defaults.autoHideJSON : !!options['hideJSON'],
      hideObject: typeof options['hideObject'] === 'undefined' ? _defaults.autoHideObject : !!options['hideObject'],
      defaultHidden: typeof options['defaultHidden'] === 'undefined' ? _defaults.defaultHidden : options['defaultHidden'],
      virtuals: typeof options['virtuals'] === 'undefined' ? _defaults.virtuals : options['virtuals']
    };

    // Set defaults based on `hide`
    options.hideJSON = options.hide === false ? false : options.hideJSON;
    options.hideObject = options.hide === false ? false : options.hideObject;

    log(options);

    var schemaPaths = {};
    var key = null;

    var transformer = function (target, prevTransform) {
      return function (doc, ret, opt) {
        if (typeof doc.ownerDocument === 'function') {
          return ret;
        }
        log(ret);
        var finalTransform = { };
        if (typeof prevTransform === 'function') {
          log(target + ': running existing transform function');
          finalTransform = prevTransform(doc, ret, opt);
          log(finalTransform);
        }
        schema.eachPath(function (pathname, schemaType) {
          var _pathname = pathname;
          if (pathname.indexOf('.') !== -1) {
            _pathname = pathname.substring(0, pathname.indexOf('.'));
          }
          schemaPaths[_pathname] = true;
          if ( ! shouldHide(schemaType, options, target, doc, ret, _pathname)) {
            log(target + ': copy "' + _pathname + '"');
            if (typeof finalTransform[_pathname] === 'undefined') {
              finalTransform[_pathname] = ret[_pathname];
            }
          } else {
            log(target + ': hiding "' + _pathname + '"');
            delete finalTransform[_pathname];
          }
        });
        // TODO find a better way to include virtuals
        for (key in ret) {
          if (typeof schemaPaths[key] === 'undefined' && (typeof options.virtuals[key] === 'undefined' || (options.virtuals[key] !== 'hide' && options.virtuals[key] !== ('hide' + target)))) {
            log(target + ': copy "' + key + '"');
            finalTransform[key] = ret[key];
          }
        }
        log(finalTransform);
        return finalTransform;
      };
    };

    var toJSONOptions = schema.get('toJSON') || { };

    schema.set('toJSON', {
      getters: toJSONOptions['getters'] || false,
      virtuals: toJSONOptions['virtuals'] || false,
      transform: transformer('JSON', toJSONOptions['transform'] || null)
    });

    var toObjectOptions = schema.get('toObject') || { };

    schema.set('toObject', {
      getters: toObjectOptions['getters'] || false,
      virtuals: toObjectOptions['virtuals'] || false,
      transform: transformer('Object', toObjectOptions['transform'] || null)
    });
  };
};
<|endoftext|>


<commit_before>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * 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.
 */

import {makeCorrelator} from './correlator';
import {validateData, loadScript} from '../../3p/3p';
import {user} from '../../src/log';

/**
 * @enum {number}
 * @private
 */
const GladeExperiment = {
  NO_EXPERIMENT: 0,
  GLADE_CONTROL: 1,
  GLADE_EXPERIMENT: 2,
  GLADE_OPT_OUT: 3,
};

/**
 * @param {!Window} global
 * @param {!Object} data
 */
export function doubleclick(global, data) {
  const experimentFraction = 0.1;

  // TODO: check mandatory fields
  validateData(data, [], [
    'slot', 'targeting', 'categoryExclusions',
    'tagForChildDirectedTreatment', 'cookieOptions',
    'overrideWidth', 'overrideHeight', 'loadingStrategy',
    'consentNotificationId', 'useSameDomainRenderingUntilDeprecated',
    'experimentId', 'multiSize', 'multiSizeValidation',
  ]);

  if (global.context.clientId) {
    // Read by GPT/Glade for GA/Doubleclick integration.
    global.gaGlobal = {
      vid: global.context.clientId,
      hid: global.context.pageViewId,
    };
  }

  if (data.useSameDomainRenderingUntilDeprecated != undefined ||
      data.multiSize) {
    doubleClickWithGpt(global, data, GladeExperiment.GLADE_OPT_OUT);
  } else {
    const dice = Math.random();
    const href = global.context.location.href;
    if ((href.indexOf('google_glade=0') > 0 || dice < experimentFraction)
        && href.indexOf('google_glade=1') < 0) {
      doubleClickWithGpt(global, data, GladeExperiment.GLADE_CONTROL);
    } else {
      const exp = (dice < 2 * experimentFraction) ?
        GladeExperiment.GLADE_EXPERIMENT : GladeExperiment.NO_EXPERIMENT;
      doubleClickWithGlade(global, data, exp);
    }
  }
}

/**
 * A helper function for determining whether a given width or height violates
 * some condition.
 *
 * Checks the width and height against their corresponding conditions. If
 * either of the conditions fail, the errorBuilder function will be called with
 * the appropriate arguments, its result will be logged to user().error, and
 * validateDimensions will return false. Otherwise, validateDimensions will
 * only return true.
 *
 * @param {(number|string)} width
 * @param {(number|string)} height
 * @param {!function((number|string)): boolean} widthCond
 * @param {!function((number|string)): boolean} heightCond
 * @param {!function(!{badDim: string, badVal: (string|number)}): string}
 *    errorBuilder A function that will produce an informative error message.
 * @return {boolean}
 */
function validateDimensions(width, height, widthCond, heightCond,
    errorBuilder) {
  let badParams = null;
  if (widthCond(width) && heightCond(height)) {
    badParams = {
      badDim: 'width and height',
      badVal: width + 'x' + height,
    };
  }
  else if (widthCond(width)) {
    badParams = {
      badDim: 'width',
      badVal: width,
    };
  }
  else if (heightCond(height)) {
    badParams = {
      badDim: 'height',
      badVal: height,
    };
  }
  if (badParams) {
    user().error('AMP-AD', errorBuilder(badParams));
    return false;
  }
  return true;
}

/**
 * @param {!Window} global
 * @param {!Object} data
 * @param {!GladeExperiment} gladeExperiment
 */
function doubleClickWithGpt(global, data, gladeExperiment) {
  const dimensions = [[
    parseInt(data.overrideWidth || data.width, 10),
    parseInt(data.overrideHeight || data.height, 10),
  ]];

  // Center the ad in the container.
  const container = global.document.querySelector('#c');
  container.style.top = '50%';
  container.style.left = '50%';
  container.style.bottom = '';
  container.style.right = '';
  container.style.transform = 'translate(-50%, -50%)';

  // Get multi-size ad request data, if any, and validate it in the following
  // ways:
  // 1) Ensure that the data string is a comma-separated list of sizes of the
  //    form WxH;
  // 2) that each secondary dimension is strictly less than its primary
  //    dimension counterpart;
  // 3) and, if data-mutli-size-validation is not set to false, that each
  //    secondary dimension is at least 2/3rds of its primary dimension
  //    counterpart.
  const multiSizeDataStr = data.multiSize || null;
  if (multiSizeDataStr) {
    const sizesStr = multiSizeDataStr.split(',');
    sizesStr.forEach(sizeStr => {

      const size = sizeStr.split('x');

      // Make sure that each size is specified in the form val1xval2.
      if (size.length != 2) {
        user().error('AMP-AD', `Invalid multi-size data format '${sizeStr}'.`);
        return;
      }

      const widthStr = size[0];
      const heightStr = size[1];

      // Make sure that both dimensions given are numbers.
      if (!validateDimensions(widthStr, heightStr,
            w => isNaN(Number(w)),
            h => isNaN(Number(h)),
            ({badDim, badVal}) =>
            `Invalid ${badDim} of ${badVal} given for secondary size.`)) {
        return;
      }

      const width = Number(widthStr);
      const height = Number(heightStr);
      const primarySize = dimensions[0];
      const primaryWidth = primarySize[0];
      const primaryHeight = primarySize[1];

      // Check that secondary size is not larger than primary size.
      if (!validateDimensions(width, height,
            w => w > primaryWidth,
            h => h > primaryHeight,
            ({badDim, badVal}) => `Secondary ${badDim} ${badVal} ` +
              `can't be larger than the primary ${badDim}.`)) {
        return;
      }

      // Check that if multi-size-validation is on, that the secondary sizes
      // are at least minRatio of the primary size.
      const validate = data.multiSizeValidation || 'true';
      if (validate != 'false' && validate != false) {

        // The minimum ratio of each secondary dimension to its corresponding
        // primary dimension.
        const minRatio = 2 / 3;
        const minWidth = minRatio * primaryWidth;
        const minHeight = minRatio * primaryHeight;
        if (!validateDimensions(width, height,
              w => w < minWidth,
              h => h < minHeight,
              ({badDim, badVal}) => `Secondary ${badDim} ${badVal} is ` +
              `smaller than 2/3rds of the primary ${badDim}.`)) {
          return;
        }
      }

      // Passed all checks! Push additional size to dimensions.
      dimensions.push([width, height]);
    });
  }

  loadScript(global, 'https://www.googletagservices.com/tag/js/gpt.js', () => {
    global.googletag.cmd.push(() => {
      const googletag = global.googletag;
      const pubads = googletag.pubads();
      const slot = googletag.defineSlot(data.slot, dimensions, 'c')
          .addService(pubads);

      if (gladeExperiment === GladeExperiment.GLADE_CONTROL) {
        pubads.markAsGladeControl();
      } else if (gladeExperiment === GladeExperiment.GLADE_OPT_OUT) {
        pubads.markAsGladeOptOut();
      }

      if (data['experimentId']) {
        const experimentIdList = data['experimentId'].split(',');
        pubads.forceExperiment = pubads.forceExperiment || function() {};
        experimentIdList &&
            experimentIdList.forEach(eid => pubads.forceExperiment(eid));
      }

      pubads.markAsAmp();
      pubads.set('page_url', global.context.canonicalUrl);
      pubads.setCorrelator(Number(getCorrelator(global)));
      googletag.enableServices();

      if (data.categoryExclusions) {
        if (Array.isArray(data.categoryExclusions)) {
          for (let i = 0; i < data.categoryExclusions.length; i++) {
            slot.setCategoryExclusion(data.categoryExclusions[i]);
          }
        } else {
          slot.setCategoryExclusion(data.categoryExclusions);
        }
      }

      if (data.cookieOptions) {
        pubads.setCookieOptions(data.cookieOptions);
      }

      if (data.tagForChildDirectedTreatment != undefined) {
        pubads.setTagForChildDirectedTreatment(
            data.tagForChildDirectedTreatment);
      }

      if (data.targeting) {
        for (const key in data.targeting) {
          slot.setTargeting(key, data.targeting[key]);
        }
      }

      pubads.addEventListener('slotRenderEnded', event => {
        const primaryInvSize = dimensions[0];
        const pWidth = primaryInvSize[0];
        const pHeight = primaryInvSize[1];
        const returnedSize = event.size;
        const rWidth = returnedSize ? returnedSize[0] : null;
        const rHeight = returnedSize ? returnedSize[1] : null;

        let creativeId = event.creativeId || '_backfill_';

        // If the creative is empty, or either dimension of the returned size
        // is larger than its counterpart in the primary size, then we don't
        // want to render the creative.
        if (event.isEmpty ||
            returnedSize && (rWidth > pWidth || rHeight > pHeight)) {
          global.context.noContentAvailable();
          creativeId = '_empty_';
        } else {
          // We only want to call renderStart with a specific size if the
          // returned creative size matches one of the multi-size sizes.
          let newSize;
          for (let i = 1; i < dimensions.length; i++) {
            // dimensions[0] is the primary or overridden size.
            if (dimensions[i][0] == rWidth && dimensions[i][1] == rHeight) {
              newSize = {
                width: rWidth,
                height: rHeight,
              };
              break;
            }
          }
          global.context.renderStart(newSize);
        }
        global.context.reportRenderedEntityIdentifier('dfp-' + creativeId);
      });

      // Exported for testing.
      global.document.getElementById('c')['slot'] = slot;
      googletag.display('c');
    });
  });
}

/**
 * @param {!Window} global
 * @param {!Object} data
 * @param {!GladeExperiment} gladeExperiment
 */
function doubleClickWithGlade(global, data, gladeExperiment) {
  const requestHeight = parseInt(data.overrideHeight || data.height, 10);
  const requestWidth = parseInt(data.overrideWidth || data.width, 10);

  const jsonParameters = {};
  if (data.categoryExclusions) {
    jsonParameters.categoryExclusions = data.categoryExclusions;
  }
  if (data.cookieOptions) {
    jsonParameters.cookieOptOut = data.cookieOptions;
  }
  if (data.tagForChildDirectedTreatment != undefined) {
    jsonParameters.tagForChildDirectedTreatment =
        data.tagForChildDirectedTreatment;
  }
  if (data.targeting) {
    jsonParameters.targeting = data.targeting;
  }
  if (gladeExperiment === GladeExperiment.GLADE_EXPERIMENT) {
    jsonParameters.gladeExp = '1';
    jsonParameters.gladeEids = '108809102';
  }
  const expIds = data['experimentId'];
  if (expIds) {
    jsonParameters.gladeEids = jsonParameters.gladeEids ?
        jsonParameters.gladeEids + ',' + expIds : expIds;
  }

  const slot = global.document.querySelector('#c');
  slot.setAttribute('data-glade', '');
  slot.setAttribute('data-amp-ad', '');
  slot.setAttribute('data-ad-unit-path', data.slot);
  if (Object.keys(jsonParameters).length > 0) {
    slot.setAttribute('data-json', JSON.stringify(jsonParameters));
  }
  slot.setAttribute('data-page-url', global.context.canonicalUrl);

  // Center the ad in the container.
  slot.setAttribute('height', requestHeight);
  slot.setAttribute('width', requestWidth);
  slot.style.top = '50%';
  slot.style.left = '50%';
  slot.style.bottom = '';
  slot.style.right = '';
  slot.style.transform = 'translate(-50%, -50%)';

  slot.addEventListener('gladeAdFetched', event => {
    if (event.detail.empty) {
      global.context.noContentAvailable();
    }
    global.context.renderStart();
  });

  window.glade = {correlator: getCorrelator(global)};
  loadScript(global, 'https://securepubads.g.doubleclick.net/static/glade.js');
}

/**
 * @param {!Window} global
 * @return {number}
 */
function getCorrelator(global) {
  return makeCorrelator(global.context.clientId, global.context.pageViewId);
}
<commit_msg>Remove the no longer used gladeExp param. (#5119)<commit_after>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * 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.
 */

import {makeCorrelator} from './correlator';
import {validateData, loadScript} from '../../3p/3p';
import {user} from '../../src/log';

/**
 * @enum {number}
 * @private
 */
const GladeExperiment = {
  NO_EXPERIMENT: 0,
  GLADE_CONTROL: 1,
  GLADE_EXPERIMENT: 2,
  GLADE_OPT_OUT: 3,
};

/**
 * @param {!Window} global
 * @param {!Object} data
 */
export function doubleclick(global, data) {
  const experimentFraction = 0.1;

  // TODO: check mandatory fields
  validateData(data, [], [
    'slot', 'targeting', 'categoryExclusions',
    'tagForChildDirectedTreatment', 'cookieOptions',
    'overrideWidth', 'overrideHeight', 'loadingStrategy',
    'consentNotificationId', 'useSameDomainRenderingUntilDeprecated',
    'experimentId', 'multiSize', 'multiSizeValidation',
  ]);

  if (global.context.clientId) {
    // Read by GPT/Glade for GA/Doubleclick integration.
    global.gaGlobal = {
      vid: global.context.clientId,
      hid: global.context.pageViewId,
    };
  }

  if (data.useSameDomainRenderingUntilDeprecated != undefined ||
      data.multiSize) {
    doubleClickWithGpt(global, data, GladeExperiment.GLADE_OPT_OUT);
  } else {
    const dice = Math.random();
    const href = global.context.location.href;
    if ((href.indexOf('google_glade=0') > 0 || dice < experimentFraction)
        && href.indexOf('google_glade=1') < 0) {
      doubleClickWithGpt(global, data, GladeExperiment.GLADE_CONTROL);
    } else {
      const exp = (dice < 2 * experimentFraction) ?
        GladeExperiment.GLADE_EXPERIMENT : GladeExperiment.NO_EXPERIMENT;
      doubleClickWithGlade(global, data, exp);
    }
  }
}

/**
 * A helper function for determining whether a given width or height violates
 * some condition.
 *
 * Checks the width and height against their corresponding conditions. If
 * either of the conditions fail, the errorBuilder function will be called with
 * the appropriate arguments, its result will be logged to user().error, and
 * validateDimensions will return false. Otherwise, validateDimensions will
 * only return true.
 *
 * @param {(number|string)} width
 * @param {(number|string)} height
 * @param {!function((number|string)): boolean} widthCond
 * @param {!function((number|string)): boolean} heightCond
 * @param {!function(!{badDim: string, badVal: (string|number)}): string}
 *    errorBuilder A function that will produce an informative error message.
 * @return {boolean}
 */
function validateDimensions(width, height, widthCond, heightCond,
    errorBuilder) {
  let badParams = null;
  if (widthCond(width) && heightCond(height)) {
    badParams = {
      badDim: 'width and height',
      badVal: width + 'x' + height,
    };
  }
  else if (widthCond(width)) {
    badParams = {
      badDim: 'width',
      badVal: width,
    };
  }
  else if (heightCond(height)) {
    badParams = {
      badDim: 'height',
      badVal: height,
    };
  }
  if (badParams) {
    user().error('AMP-AD', errorBuilder(badParams));
    return false;
  }
  return true;
}

/**
 * @param {!Window} global
 * @param {!Object} data
 * @param {!GladeExperiment} gladeExperiment
 */
function doubleClickWithGpt(global, data, gladeExperiment) {
  const dimensions = [[
    parseInt(data.overrideWidth || data.width, 10),
    parseInt(data.overrideHeight || data.height, 10),
  ]];

  // Center the ad in the container.
  const container = global.document.querySelector('#c');
  container.style.top = '50%';
  container.style.left = '50%';
  container.style.bottom = '';
  container.style.right = '';
  container.style.transform = 'translate(-50%, -50%)';

  // Get multi-size ad request data, if any, and validate it in the following
  // ways:
  // 1) Ensure that the data string is a comma-separated list of sizes of the
  //    form WxH;
  // 2) that each secondary dimension is strictly less than its primary
  //    dimension counterpart;
  // 3) and, if data-mutli-size-validation is not set to false, that each
  //    secondary dimension is at least 2/3rds of its primary dimension
  //    counterpart.
  const multiSizeDataStr = data.multiSize || null;
  if (multiSizeDataStr) {
    const sizesStr = multiSizeDataStr.split(',');
    sizesStr.forEach(sizeStr => {

      const size = sizeStr.split('x');

      // Make sure that each size is specified in the form val1xval2.
      if (size.length != 2) {
        user().error('AMP-AD', `Invalid multi-size data format '${sizeStr}'.`);
        return;
      }

      const widthStr = size[0];
      const heightStr = size[1];

      // Make sure that both dimensions given are numbers.
      if (!validateDimensions(widthStr, heightStr,
            w => isNaN(Number(w)),
            h => isNaN(Number(h)),
            ({badDim, badVal}) =>
            `Invalid ${badDim} of ${badVal} given for secondary size.`)) {
        return;
      }

      const width = Number(widthStr);
      const height = Number(heightStr);
      const primarySize = dimensions[0];
      const primaryWidth = primarySize[0];
      const primaryHeight = primarySize[1];

      // Check that secondary size is not larger than primary size.
      if (!validateDimensions(width, height,
            w => w > primaryWidth,
            h => h > primaryHeight,
            ({badDim, badVal}) => `Secondary ${badDim} ${badVal} ` +
              `can't be larger than the primary ${badDim}.`)) {
        return;
      }

      // Check that if multi-size-validation is on, that the secondary sizes
      // are at least minRatio of the primary size.
      const validate = data.multiSizeValidation || 'true';
      if (validate != 'false' && validate != false) {

        // The minimum ratio of each secondary dimension to its corresponding
        // primary dimension.
        const minRatio = 2 / 3;
        const minWidth = minRatio * primaryWidth;
        const minHeight = minRatio * primaryHeight;
        if (!validateDimensions(width, height,
              w => w < minWidth,
              h => h < minHeight,
              ({badDim, badVal}) => `Secondary ${badDim} ${badVal} is ` +
              `smaller than 2/3rds of the primary ${badDim}.`)) {
          return;
        }
      }

      // Passed all checks! Push additional size to dimensions.
      dimensions.push([width, height]);
    });
  }

  loadScript(global, 'https://www.googletagservices.com/tag/js/gpt.js', () => {
    global.googletag.cmd.push(() => {
      const googletag = global.googletag;
      const pubads = googletag.pubads();
      const slot = googletag.defineSlot(data.slot, dimensions, 'c')
          .addService(pubads);

      if (gladeExperiment === GladeExperiment.GLADE_CONTROL) {
        pubads.markAsGladeControl();
      } else if (gladeExperiment === GladeExperiment.GLADE_OPT_OUT) {
        pubads.markAsGladeOptOut();
      }

      if (data['experimentId']) {
        const experimentIdList = data['experimentId'].split(',');
        pubads.forceExperiment = pubads.forceExperiment || function() {};
        experimentIdList &&
            experimentIdList.forEach(eid => pubads.forceExperiment(eid));
      }

      pubads.markAsAmp();
      pubads.set('page_url', global.context.canonicalUrl);
      pubads.setCorrelator(Number(getCorrelator(global)));
      googletag.enableServices();

      if (data.categoryExclusions) {
        if (Array.isArray(data.categoryExclusions)) {
          for (let i = 0; i < data.categoryExclusions.length; i++) {
            slot.setCategoryExclusion(data.categoryExclusions[i]);
          }
        } else {
          slot.setCategoryExclusion(data.categoryExclusions);
        }
      }

      if (data.cookieOptions) {
        pubads.setCookieOptions(data.cookieOptions);
      }

      if (data.tagForChildDirectedTreatment != undefined) {
        pubads.setTagForChildDirectedTreatment(
            data.tagForChildDirectedTreatment);
      }

      if (data.targeting) {
        for (const key in data.targeting) {
          slot.setTargeting(key, data.targeting[key]);
        }
      }

      pubads.addEventListener('slotRenderEnded', event => {
        const primaryInvSize = dimensions[0];
        const pWidth = primaryInvSize[0];
        const pHeight = primaryInvSize[1];
        const returnedSize = event.size;
        const rWidth = returnedSize ? returnedSize[0] : null;
        const rHeight = returnedSize ? returnedSize[1] : null;

        let creativeId = event.creativeId || '_backfill_';

        // If the creative is empty, or either dimension of the returned size
        // is larger than its counterpart in the primary size, then we don't
        // want to render the creative.
        if (event.isEmpty ||
            returnedSize && (rWidth > pWidth || rHeight > pHeight)) {
          global.context.noContentAvailable();
          creativeId = '_empty_';
        } else {
          // We only want to call renderStart with a specific size if the
          // returned creative size matches one of the multi-size sizes.
          let newSize;
          for (let i = 1; i < dimensions.length; i++) {
            // dimensions[0] is the primary or overridden size.
            if (dimensions[i][0] == rWidth && dimensions[i][1] == rHeight) {
              newSize = {
                width: rWidth,
                height: rHeight,
              };
              break;
            }
          }
          global.context.renderStart(newSize);
        }
        global.context.reportRenderedEntityIdentifier('dfp-' + creativeId);
      });

      // Exported for testing.
      global.document.getElementById('c')['slot'] = slot;
      googletag.display('c');
    });
  });
}

/**
 * @param {!Window} global
 * @param {!Object} data
 * @param {!GladeExperiment} gladeExperiment
 */
function doubleClickWithGlade(global, data, gladeExperiment) {
  const requestHeight = parseInt(data.overrideHeight || data.height, 10);
  const requestWidth = parseInt(data.overrideWidth || data.width, 10);

  const jsonParameters = {};
  if (data.categoryExclusions) {
    jsonParameters.categoryExclusions = data.categoryExclusions;
  }
  if (data.cookieOptions) {
    jsonParameters.cookieOptOut = data.cookieOptions;
  }
  if (data.tagForChildDirectedTreatment != undefined) {
    jsonParameters.tagForChildDirectedTreatment =
        data.tagForChildDirectedTreatment;
  }
  if (data.targeting) {
    jsonParameters.targeting = data.targeting;
  }
  if (gladeExperiment === GladeExperiment.GLADE_EXPERIMENT) {
    jsonParameters.gladeEids = '108809102';
  }
  const expIds = data['experimentId'];
  if (expIds) {
    jsonParameters.gladeEids = jsonParameters.gladeEids ?
        jsonParameters.gladeEids + ',' + expIds : expIds;
  }

  const slot = global.document.querySelector('#c');
  slot.setAttribute('data-glade', '');
  slot.setAttribute('data-amp-ad', '');
  slot.setAttribute('data-ad-unit-path', data.slot);
  if (Object.keys(jsonParameters).length > 0) {
    slot.setAttribute('data-json', JSON.stringify(jsonParameters));
  }
  slot.setAttribute('data-page-url', global.context.canonicalUrl);

  // Center the ad in the container.
  slot.setAttribute('height', requestHeight);
  slot.setAttribute('width', requestWidth);
  slot.style.top = '50%';
  slot.style.left = '50%';
  slot.style.bottom = '';
  slot.style.right = '';
  slot.style.transform = 'translate(-50%, -50%)';

  slot.addEventListener('gladeAdFetched', event => {
    if (event.detail.empty) {
      global.context.noContentAvailable();
    }
    global.context.renderStart();
  });

  window.glade = {correlator: getCorrelator(global)};
  loadScript(global, 'https://securepubads.g.doubleclick.net/static/glade.js');
}

/**
 * @param {!Window} global
 * @return {number}
 */
function getCorrelator(global) {
  return makeCorrelator(global.context.clientId, global.context.pageViewId);
}
<|endoftext|>


<commit_before>function Node(key, value) {
  this.key = key
  this.value = value
  this.next = null
}

const LinkedList = class LinkedList {
  constructor() {
    this.length = 0
    this.head = null
  }

  clear() {
    this.length = 0
    this.head = null
  }

  insert(key, value) {
    let node = new Node(key, value)
    let currentNode = this.head

    if (!currentNode) {
      this.head = node
      this.length++
      return node
    }
    while (currentNode.next) {
      currentNode = currentNode.next
    }
    currentNode.next = node
    this.length += 1
    return node
  }

//regular find
  find(key) {
    var currentNode = this.head
    if (!currentNode) {
      return null
    }
    while (currentNode) {
      if (currentNode.key === key){
        return currentNode
      }
    } return false
  }

//find but return the value
  findButReturnValue(key) {
    const node = this.find(key)
    return node ? node.value : null
  }

//see if linked list contains key
  contains(key) {
    return !!this.find(key)
  }

//finds the node whose next value is the target node
  findByNext(key) {
    let currentNode = this.head

    if(!currentNode) {
      return null
    } else if (!currentNode.next && currentNode.key === key){
      return clear()
    } else {
      while(currentNode.next){
        if(currentNode.next.key === key){
          return currentNode
        } else {
          currentNode = currentNode.next
        }
      } return null
    }
  }

//cleaner remove using findByNext
  removeWithFindByNext(key) {
    let currentNode = this.head
    let foundNode = this.findByNext(key)

    if (foundNode === null){
      return null
    } else {
      foundNode.next = foundNode.next.next
      return this.length--
    }
  }

//brute force
  removeIt(key) {
    const previousNode = this.findByNext(key)
    if (previousNode){
      const node = prevNode.next
      previousNode.next = node.next
      return node
    }
    return null
  }

}

export default class HashTable {
  constructor() {
    this.length = 0
    this.table = {}
  }

  //ht.iterate((k, v) => console.log(`${k}: ${v}`))
    // takes a callback function and passes it each key and value in sequence.
  iterate( callback ){
    let table = this.table
    let arrayOfKeys = Object.keys(table)

    for( let i = 0; i < arrayOfKeys.length; i++ ){
      callback( arrayOfKeys[i], table[arrayOfKeys[i]] )
    }
  }

  //ht.hash("name")
    // generates a hash for the key "name"
  hash( str ) {
    const lowerCaseString = str.toLowerCase()
    const alphabit = Array(26).fill().map((_, index) => String.fromCharCode(index + 97))
    const hashed = []

    for( var i = 0; i < lowerCaseString.length; i++ ){
      hashed.push(alphabit.indexOf(lowerCaseString[i]) * (i + 1) * 107)
    }
    return ( hashed.reduce(( a,b ) => a + b, 0 ) % 101 )
  }

  //ht.size()
    // returns the number of key-value pairs in the hash table.
  size() {
    return this.length
  }

  //ht.put("name", "Zanzibar")
    // adds a key-value pair to the hash table, deal with collisions using chaining
  put(key, value) {
    const node = new Node( key, value )
    const linkedList = new LinkedList()
    const table = this.table
    const hashedKey = hash( key )

    if( !(hashedKey in table) ) {
      table.hashedKey = linkedList.insert(key, value)
      return this.length++
    } else {
      table[hashedKey].insert(node)
      return this.length++
    }
  }

  //ht.get("name")
    // returns the key associated with key.
  get(key) {
    const table = this.table
    const hashedKey = hash(key)

    if(!(hashedKey in table)){
      return -1
    } else {
      let linkedListValue = table[hashedKey]
      return linkedListValue.findButReturnValue(key)
    }
  }

  //ht.contains("name")
    // returns true if the hash table contains the key.
  contains(key) {
    const linkedList = this.table[hash(key)]
    return linkedList
      ? linkedList.contains(key)
      : false
  }

  //ht.remove("name")
    // removes a key-value pair by key.
  remove(key){
    const table = this.table
    const hashedKey = hash(key)

    if(!(hashedKey in table)){
      return false
    } else {
      let linkedListValue = table[hashedKey]
      linkedListValue.removeIt(key)
      return this.length--
    }
  }

}
<commit_msg>Address changes to hashTable per review<commit_after>function Node(key, value) {
  this.key = key
  this.value = value
  this.next = null
}

const LinkedList = class LinkedList {
  constructor() {
    this.length = 0
    this.head = null
  }

  clear() {
    this.length = 0
    this.head = null
  }

  insert(key, value) {
    let node = new Node(key, value)
    let currentNode = this.head

    if (!currentNode) {
      this.head = node
      this.length++
      return node
    }
    while (currentNode.next) {
      currentNode = currentNode.next
    }
    currentNode.next = node
    this.length += 1
    return node
  }

//regular find
  find(key) {
    var currentNode = this.head
    if (!currentNode) {
      return null
    }
    while (currentNode) {
      if (currentNode.key === key){
        return currentNode
      }
    } return false
  }

//find but return the value
  findButReturnValue(key) {
    const node = this.find(key)
    return node
      ? node.value
      : null
  }

//see if linked list contains key
  contains(key) {
    return !!this.find(key)
  }

//finds the node whose next value is the target node
  findByNext(key) {
    let currentNode = this.head

    if(!currentNode) {
      return null
    } else if (!currentNode.next && currentNode.key === key){
      return clear()
    } else {
      while(currentNode.next){
        if(currentNode.next.key === key){
          return currentNode
        } else {
          currentNode = currentNode.next
        }
      } return null
    }
  }

//cleaner remove using findByNext
  removeWithFindByNext(key) {
    let currentNode = this.head
    let foundNode = this.findByNext(key)

    if (foundNode === null){
      return null
    } else {
      foundNode.next = foundNode.next.next
      return this.length--
    }
  }

//brute force
  removeIt(key) {
    const previousNode = this.findByNext(key)
    if (previousNode){
      const node = prevNode.next
      previousNode.next = node.next
      return node
    }
    return null
  }

}

export default class HashTable {
  constructor() {
    this.length = 0
    this.table = {}
  }

  //ht.iterate((k, v) => console.log(`${k}: ${v}`))
    // takes a callback function and passes it each key and value in sequence.
  iterate( callback ){
    Object.keys(this.table).forEach( key => { callback(key, this.table[key]) })
  }

  //ht.hash("name")
    // generates a hash for the key "name"
  hash( str ) {
    const lowerCaseString = str.toLowerCase()
    const alphabit = Array(26).fill().map((_, index) => String.fromCharCode(index + 97))
    const hashed = []

    for( var i = 0; i < lowerCaseString.length; i++ ){
      hashed.push(alphabit.indexOf(lowerCaseString[i]) * (i + 1) * 107)
    }
    return ( hashed.reduce(( a,b ) => a + b, 0 ) % 101 )
  }

  //ht.size()
    // returns the number of key-value pairs in the hash table.
  size() {
    return this.length
  }

  //ht.put("name", "Zanzibar")
    // adds a key-value pair to the hash table, deal with collisions using chaining
  put(key, value) {
    const node = new Node( key, value )
    const linkedList = new LinkedList()
    const table = this.table
    const hashedKey = hash( key )

    if( !(hashedKey in table) ) {
      table.hashedKey = linkedList.insert(key, value)
      return this.length++
    } else {
      table[hashedKey].insert(node)
      return this.length++
    }
  }

  //ht.get("name")
    // returns the key associated with key.
  get(key) {
    const table = this.table
    const hashedKey = hash(key)

    if(!(hashedKey in table)){
      return -1
    } else {
      let linkedListValue = table[hashedKey]
      return linkedListValue.findButReturnValue(key)
    }
  }

  //ht.contains("name")
    // returns true if the hash table contains the key.
  contains(key) {
    const linkedList = this.table[hash(key)]
    return linkedList
      ? linkedList.contains(key)
      : false
  }

  //ht.remove("name")
    // removes a key-value pair by key.
  remove(key){
    const table = this.table
    const hashedKey = hash(key)

    if(!(hashedKey in table)){
      return false
    } else {
      let linkedListValue = table[hashedKey]
      linkedListValue.removeIt(key)
      return this.length--
    }
  }

}
<|endoftext|>


<commit_before>var Async = require('async');
var Myriad = require('myriad');
var MyriadServer = require('myriad-server');
var Path = require('path');
var FS = require('fs');
var Debug = require('debug')('myriad-cucumber');
var FeatureFinder = require('./feature_finder');
var Freeport = require('freeport');
var JSYAML = require('js-yaml');
var Colors = require('colors');

var MyriadCucumber = function(options) {
  var self = {
    run: function(callback) {
      var configFile = options.configFile;
      var config = require(Path.resolve(configFile));

      options.localPackage = options.myriadServerUrl ? false : options.localPackage;
      options.features = options.features;
      options.package = config.package;
      options.profiles = config.profiles;

      executeFeatures(
        options,
        function(err) {
          callback(err);
        }
      );
    }
  };

  return self;
}

function executeFeatures(options, callback) {
  var embeddedMyriadServer;

  function done(err) {
    if (embeddedMyriadServer) {
      Debug('Closing embedded myriad-server');
      embeddedMyriadServer.close(function() {
        Debug('Closed embedded myriad-server');
        callback(err);
      })
    }
    else {
      Debug('No embedded myriad-server started so nothing to close');
      callback(err);
    }
  }

  Async.parallel(
    [
      function(callback) {
        prepareMyriadServer(options, function(err, info) {
          embeddedMyriadServer = info.embeddedMyriadServer;
          callback(err, info.myriadServerUrl);
        });
      },
      Async.apply(findFeatures, options),
      Async.apply(preparePackage, options)
    ],
    function(err, results) {
      if (err) {
        done(err);
      }
      else {
        options.myriadServerUrl = results[0];
        options.featurePaths = results[1];
        options.package = results[2];

        executeFeaturesOnMyriadServer(
          options,
          function(err) {
            done(err);
          }
        );
      }
    }
  )
}

function executeFeaturesOnMyriadServer(options, callback) {
  Debug('Spawning cucumber instances');

  var outStream;
  var finished = false;

  function done(err) {
    if (finished) return;
    finished = true;

    if (outStream) {
      outStream.write(']', function() {
        outStream.end();
      });
    }

    callback(err);
  }

  Async.parallel(
    [
      Async.apply(getWorkerTasks, options),
      function(callback) {
        getOutStream(options, function(err, info) {
          if (err) {
            callback(err);
          }
          else if (info.outStream) {
            outStream = info.outStream;

            outStream.on('error', function(err) {
              done(err);
            });

            outStream.write('[', function() {
              callback(null, outStream);
            });
          }
          else {
            callback();
          }
        });
      }
    ],
    function(err, results) {
      var tasks = results[0];

      Debug('Found ' + tasks.length + ' tasks to execute');
      Debug('Execution will be limited to ' + options.workers + ' workers');

      var workerCount = 0;

      var firstReport = true;

      Async.eachLimit(
        tasks,
        options.workers,
        function(task, callback) {
          var workerIndex = workerCount++;

          executeFeatureOnWorker(
            {
              workerIndex: workerIndex,
              myriadServerUrl: options.myriadServerUrl,
              taskOptions: task.options,
              featurePath: task.featurePath,
              dryRun: options.dryRun
            },
            function(report, callback) {
              report = JSON.stringify(report);
              // Remove [ and ] from start and end of the JSON string
              report = report.substr(1, report.length - 2);

              if (report.length == 0) {
                Debug('Empty report');
              }
              else {
                if (firstReport) {
                  Debug('First report');
                  firstReport = false;
                }
                else {
                  Debug('Subsequent report');
                  report = ',' + report;
                }
              }

              if (outStream) {
                Debug('Saving cucumber output file');
                outStream.write(report, function() {
                  callback();
                });
              }
              else {
                Debug('Sending cucumber output to stdout');
                console.log(report);
                callback();
              }
            },
            function(err) {
              callback(err);
            }
          );
        },
        function(err) {
          done(err);
        }
      );
    }
  )
}

function getOutStream(options, callback) {
  if (options.out) {
    Debug('Saving cucumber output to ' + options.out);
    var outStream = FS.createWriteStream(options.out);
    callback(null, { outStream: outStream });
  }
  else {
    Debug('Sending cucumber output to stdout');
    callback(null, {});
  }
}

function getWorkerTasks(options, callback) {
  var tasks = [];

  var extraArgs = [];

  if (options.dryRun) {
    extraArgs.push('--dry-run');
  }

  options.featurePaths.forEach(function(featurePath) {
    Object.keys(options.profiles).forEach(function(profileName) {
      var profile = options.profiles[profileName];

      var task = {
        options: {
          package: options.package,
          localPackage: options.localPackage,
          bin: profile.bin,
          args: profile.args.concat(extraArgs, [featurePath]),
          env: profile.env || {}
        },
        featurePath: featurePath
      };

      tasks.push(task)
    });
  });

  callback(null, tasks);
}

function executeFeatureOnWorker(options, outCallback, callback) {
  var debugPrefix = '#' + options.workerIndex + ' ';
  var myriadConnection;
  var finished = false;

  function done(err, report) {
    if (finished) return;
    finished = true;

    Debug(debugPrefix + 'Closing connection');
    myriadConnection.close(function() {
      Debug(debugPrefix + 'Closed connection');
      callback(err, report);
    });
  }

  if (!options.dryRun) {
    logEvent('featurePath', {
      worker: options.workerIndex,
      status: 'starting',
      uri: options.featurePath
    });
  }

  Debug(debugPrefix + 'Connecting to ' + options.myriadServerUrl);
  myriadConnection = Myriad({ url: options.myriadServerUrl });

  var stdout = [];

  myriadConnection.on('connect', function() {
    Debug(debugPrefix + 'Spawning cucumber instance');
    myriadConnection.spawn(options.taskOptions);
  });

  myriadConnection.on('message', function(event) {
    var data;

    if (event.data) {
      data = new Buffer(event.data, 'base64');
    }

    if (event.event === 'error') {
      console.error(event.error);
      Debug(debugPrefix + 'Error: ' + JSON.stringify(event.error));
      done(event.error);
    }
    else if (event.event === 'child_stdout') {
      Debug(debugPrefix + 'Child stdout: ' + data.toString());
      stdout.push(data);
    }
    else if (event.event === 'child_stderr') {
      console.error(data.toString());
    }
    else if (event.event === 'child_error') {
      console.error(event.error);
      Debug(debugPrefix + 'Child rrror: ' + JSON.stringify(event.error));
      done(event.error);
    }
    else if (event.event === 'child_close') {
      Debug(debugPrefix + 'Child closed.  Exit code ' + event.code);

      if (event.code === 0) {
        stdout = stdout.join('');
        Debug(stdout);
        var report = JSON.parse(stdout);

        logReportProgress({ workerIndex: options.workerIndex, report: report, dryRun: options.dryRun });

        outCallback(report, function() {
          done();
        });
      }
      else {
        done({ message: "Cucumber returned a failure exit code.  Exit code " + event.code, exitCode: event.code });
      }
    }
  });
}

function logReportProgress(options) {
  options.report.forEach(function(item) {
    var featureUri = cleanUri(Path.relative(process.cwd(), item.uri));

    if (item.elements) {
      item.elements.forEach(function(element) {
        var elementStatus = 'stepless';

        if (element.steps) {
          var foundElementStatus = false;

          element.steps.forEach(function(step) {
            if (!foundElementStatus) {
              if (step.result) {
                elementStatus = step.result.status;
                foundElementStatus = elementStatus !== 'passed';
              }
              else {
                elementStatus = 'unknown';
                foundElementStatus = true;
              }
            }
          });
        }

        if (!options.dryRun) {
          logEvent(element.type, {
            worker: options.workerIndex,
            status: elementStatus,
            uri: featureUri + '/' + cleanUri(element.name)
          });
        }
      });
    }

    if (!options.dryRun) {
      logEvent('feature', {
        worker: options.workerIndex,
        status: 'finished',
        uri: featureUri
      });
    }
  });
}

function prepareMyriadServer(options, callback) {
  if (options.myriadServerUrl) {
    callback(null, { myriadServerUrl: options.myriadServerUrl });
  }
  else {
    Debug('Starting embedded myriad server');
    Async.waterfall(
      [
        function(callback) {
          Debug('Finding a free port');
          Freeport(function(err, port) {
            callback(err, port);
          });
        },
        function(port, callback) {
          Debug('Using port ' + port + ' for embedded myriad server');
          embeddedMyriadServer = MyriadServer({ port: port }, function() {
            Debug('Myriad server listening');
            callback(null, port, embeddedMyriadServer);
          });
        }
      ],
      function(err, port, embeddedMyriadServer) {
        if (err) {
          callback(err);
        }
        else {
          Debug('Started myriad server on port ' + port);
          callback(null, { myriadServerUrl: 'ws://localhost:' + port, embeddedMyriadServer: embeddedMyriadServer });
        }
      }
    )
  }
}

function findFeatures(options, callback) {
  if (options.features.length == 0) {
    callback({ message: "No features have been specified" });
  }
  else {
    FeatureFinder.find({ features: options.features, dryRun: options.dryRun }, function(err, featurePaths) {
      if (err) {
        callback(err);
      }
      else {
        callback(null, featurePaths);
      }
    });
  }
}

function preparePackage(options, callback) {
  if (options.localPackage) {
    Debug('Using local package ' + options.package);
    callback(null, options.package);
  }
  else {
    Debug('Packing ' + options.package);
    Myriad.NPM.pack({ package: options.package }, function(err, package) {
      if (err) {
        callback(err);
      }
      else {
        callback(null, package);
      }
    });
  }
}

function logEvent(key, item) {
  var event = {};
  event[key] = item;

  var eventYaml = JSYAML.safeDump(
    event,
    {
      flowLevel: 0
    }
  );

  if (item.status == 'passed') {
    eventYaml = eventYaml.green;
  }
  else if (item.status == 'failed') {
    eventYaml = eventYaml.red;
  }
  else if (item.status == 'stepless') {
    eventYaml = eventYaml.blue;
  }
  else  {
    eventYaml = eventYaml.grey;
  }

  process.stderr.write(eventYaml);
}

function cleanUri(value) {
  return value.replace(/[^a-zA-Z0-9\\/]/g, '_').replace(/\\/g, '/');
}

module.exports = MyriadCucumber;
<commit_msg>Added MYRIAD_CUCUMBER_PROFILE environment variable to spawned cucumber processes<commit_after>var Async = require('async');
var Myriad = require('myriad');
var MyriadServer = require('myriad-server');
var Path = require('path');
var FS = require('fs');
var Debug = require('debug')('myriad-cucumber');
var FeatureFinder = require('./feature_finder');
var Freeport = require('freeport');
var JSYAML = require('js-yaml');
var Colors = require('colors');

var MyriadCucumber = function(options) {
  var self = {
    run: function(callback) {
      var configFile = options.configFile;
      var config = require(Path.resolve(configFile));

      options.localPackage = options.myriadServerUrl ? false : options.localPackage;
      options.features = options.features;
      options.package = config.package;
      options.profiles = config.profiles;

      executeFeatures(
        options,
        function(err) {
          callback(err);
        }
      );
    }
  };

  return self;
}

function executeFeatures(options, callback) {
  var embeddedMyriadServer;

  function done(err) {
    if (embeddedMyriadServer) {
      Debug('Closing embedded myriad-server');
      embeddedMyriadServer.close(function() {
        Debug('Closed embedded myriad-server');
        callback(err);
      })
    }
    else {
      Debug('No embedded myriad-server started so nothing to close');
      callback(err);
    }
  }

  Async.parallel(
    [
      function(callback) {
        prepareMyriadServer(options, function(err, info) {
          embeddedMyriadServer = info.embeddedMyriadServer;
          callback(err, info.myriadServerUrl);
        });
      },
      Async.apply(findFeatures, options),
      Async.apply(preparePackage, options)
    ],
    function(err, results) {
      if (err) {
        done(err);
      }
      else {
        options.myriadServerUrl = results[0];
        options.featurePaths = results[1];
        options.package = results[2];

        executeFeaturesOnMyriadServer(
          options,
          function(err) {
            done(err);
          }
        );
      }
    }
  )
}

function executeFeaturesOnMyriadServer(options, callback) {
  Debug('Spawning cucumber instances');

  var outStream;
  var finished = false;

  function done(err) {
    if (finished) return;
    finished = true;

    if (outStream) {
      outStream.write(']', function() {
        outStream.end();
      });
    }

    callback(err);
  }

  Async.parallel(
    [
      Async.apply(getWorkerTasks, options),
      function(callback) {
        getOutStream(options, function(err, info) {
          if (err) {
            callback(err);
          }
          else if (info.outStream) {
            outStream = info.outStream;

            outStream.on('error', function(err) {
              done(err);
            });

            outStream.write('[', function() {
              callback(null, outStream);
            });
          }
          else {
            callback();
          }
        });
      }
    ],
    function(err, results) {
      var tasks = results[0];

      Debug('Found ' + tasks.length + ' tasks to execute');
      Debug('Execution will be limited to ' + options.workers + ' workers');

      var workerCount = 0;

      var firstReport = true;

      Async.eachLimit(
        tasks,
        options.workers,
        function(task, callback) {
          var workerIndex = workerCount++;

          executeFeatureOnWorker(
            {
              workerIndex: workerIndex,
              myriadServerUrl: options.myriadServerUrl,
              taskOptions: task.options,
              featurePath: task.featurePath,
              dryRun: options.dryRun
            },
            function(report, callback) {
              report = JSON.stringify(report);
              // Remove [ and ] from start and end of the JSON string
              report = report.substr(1, report.length - 2);

              if (report.length == 0) {
                Debug('Empty report');
              }
              else {
                if (firstReport) {
                  Debug('First report');
                  firstReport = false;
                }
                else {
                  Debug('Subsequent report');
                  report = ',' + report;
                }
              }

              if (outStream) {
                Debug('Saving cucumber output file');
                outStream.write(report, function() {
                  callback();
                });
              }
              else {
                Debug('Sending cucumber output to stdout');
                console.log(report);
                callback();
              }
            },
            function(err) {
              callback(err);
            }
          );
        },
        function(err) {
          done(err);
        }
      );
    }
  )
}

function getOutStream(options, callback) {
  if (options.out) {
    Debug('Saving cucumber output to ' + options.out);
    var outStream = FS.createWriteStream(options.out);
    callback(null, { outStream: outStream });
  }
  else {
    Debug('Sending cucumber output to stdout');
    callback(null, {});
  }
}

function getWorkerTasks(options, callback) {
  var tasks = [];

  var extraArgs = [];

  if (options.dryRun) {
    extraArgs.push('--dry-run');
  }

  options.featurePaths.forEach(function(featurePath) {
    Object.keys(options.profiles).forEach(function(profileName) {
      var profile = options.profiles[profileName];

      var env = profile.env || {};
      env.MYRIAD_CUCUMBER_PROFILE = profileName;

      var task = {
        options: {
          package: options.package,
          localPackage: options.localPackage,
          bin: profile.bin,
          args: profile.args.concat(extraArgs, [featurePath]),
          env: env
        },
        featurePath: featurePath
      };

      tasks.push(task)
    });
  });

  callback(null, tasks);
}

function executeFeatureOnWorker(options, outCallback, callback) {
  var debugPrefix = '#' + options.workerIndex + ' ';
  var myriadConnection;
  var finished = false;

  function done(err, report) {
    if (finished) return;
    finished = true;

    Debug(debugPrefix + 'Closing connection');
    myriadConnection.close(function() {
      Debug(debugPrefix + 'Closed connection');
      callback(err, report);
    });
  }

  if (!options.dryRun) {
    logEvent('featurePath', {
      worker: options.workerIndex,
      status: 'starting',
      uri: options.featurePath
    });
  }

  Debug(debugPrefix + 'Connecting to ' + options.myriadServerUrl);
  myriadConnection = Myriad({ url: options.myriadServerUrl });

  var stdout = [];

  myriadConnection.on('connect', function() {
    Debug(debugPrefix + 'Spawning cucumber instance');
    myriadConnection.spawn(options.taskOptions);
  });

  myriadConnection.on('message', function(event) {
    var data;

    if (event.data) {
      data = new Buffer(event.data, 'base64');
    }

    if (event.event === 'error') {
      console.error(event.error);
      Debug(debugPrefix + 'Error: ' + JSON.stringify(event.error));
      done(event.error);
    }
    else if (event.event === 'child_stdout') {
      Debug(debugPrefix + 'Child stdout: ' + data.toString());
      stdout.push(data);
    }
    else if (event.event === 'child_stderr') {
      console.error(data.toString());
    }
    else if (event.event === 'child_error') {
      console.error(event.error);
      Debug(debugPrefix + 'Child rrror: ' + JSON.stringify(event.error));
      done(event.error);
    }
    else if (event.event === 'child_close') {
      Debug(debugPrefix + 'Child closed.  Exit code ' + event.code);

      if (event.code === 0) {
        stdout = stdout.join('');
        Debug(stdout);
        var report = JSON.parse(stdout);

        logReportProgress({ workerIndex: options.workerIndex, report: report, dryRun: options.dryRun });

        outCallback(report, function() {
          done();
        });
      }
      else {
        done({ message: "Cucumber returned a failure exit code.  Exit code " + event.code, exitCode: event.code });
      }
    }
  });
}

function logReportProgress(options) {
  options.report.forEach(function(item) {
    var featureUri = cleanUri(Path.relative(process.cwd(), item.uri));

    if (item.elements) {
      item.elements.forEach(function(element) {
        var elementStatus = 'stepless';

        if (element.steps) {
          var foundElementStatus = false;

          element.steps.forEach(function(step) {
            if (!foundElementStatus) {
              if (step.result) {
                elementStatus = step.result.status;
                foundElementStatus = elementStatus !== 'passed';
              }
              else {
                elementStatus = 'unknown';
                foundElementStatus = true;
              }
            }
          });
        }

        if (!options.dryRun) {
          logEvent(element.type, {
            worker: options.workerIndex,
            status: elementStatus,
            uri: featureUri + '/' + cleanUri(element.name)
          });
        }
      });
    }

    if (!options.dryRun) {
      logEvent('feature', {
        worker: options.workerIndex,
        status: 'finished',
        uri: featureUri
      });
    }
  });
}

function prepareMyriadServer(options, callback) {
  if (options.myriadServerUrl) {
    callback(null, { myriadServerUrl: options.myriadServerUrl });
  }
  else {
    Debug('Starting embedded myriad server');
    Async.waterfall(
      [
        function(callback) {
          Debug('Finding a free port');
          Freeport(function(err, port) {
            callback(err, port);
          });
        },
        function(port, callback) {
          Debug('Using port ' + port + ' for embedded myriad server');
          embeddedMyriadServer = MyriadServer({ port: port }, function() {
            Debug('Myriad server listening');
            callback(null, port, embeddedMyriadServer);
          });
        }
      ],
      function(err, port, embeddedMyriadServer) {
        if (err) {
          callback(err);
        }
        else {
          Debug('Started myriad server on port ' + port);
          callback(null, { myriadServerUrl: 'ws://localhost:' + port, embeddedMyriadServer: embeddedMyriadServer });
        }
      }
    )
  }
}

function findFeatures(options, callback) {
  if (options.features.length == 0) {
    callback({ message: "No features have been specified" });
  }
  else {
    FeatureFinder.find({ features: options.features, dryRun: options.dryRun }, function(err, featurePaths) {
      if (err) {
        callback(err);
      }
      else {
        callback(null, featurePaths);
      }
    });
  }
}

function preparePackage(options, callback) {
  if (options.localPackage) {
    Debug('Using local package ' + options.package);
    callback(null, options.package);
  }
  else {
    Debug('Packing ' + options.package);
    Myriad.NPM.pack({ package: options.package }, function(err, package) {
      if (err) {
        callback(err);
      }
      else {
        callback(null, package);
      }
    });
  }
}

function logEvent(key, item) {
  var event = {};
  event[key] = item;

  var eventYaml = JSYAML.safeDump(
    event,
    {
      flowLevel: 0
    }
  );

  if (item.status == 'passed') {
    eventYaml = eventYaml.green;
  }
  else if (item.status == 'failed') {
    eventYaml = eventYaml.red;
  }
  else if (item.status == 'stepless') {
    eventYaml = eventYaml.blue;
  }
  else  {
    eventYaml = eventYaml.grey;
  }

  process.stderr.write(eventYaml);
}

function cleanUri(value) {
  return value.replace(/[^a-zA-Z0-9\\/]/g, '_').replace(/\\/g, '/');
}

module.exports = MyriadCucumber;
<|endoftext|>


<commit_before>/**
 * @preserve Neuron core:dom v2.0(jQuerified)
 */
;(function(K){

/**
 nodeType:
 ref: https://developer.mozilla.org/en/nodeType

    Node.ELEMENT_NODE == 1
        - document.documentElement
        - document.body
        -

    Node.ATTRIBUTE_NODE == 2
    Node.TEXT_NODE == 3
    Node.CDATA_SECTION_NODE == 4
    Node.ENTITY_REFERENCE_NODE == 5
    Node.ENTITY_NODE == 6
    Node.PROCESSING_INSTRUCTION_NODE == 7
    Node.COMMENT_NODE == 8
    Node.DOCUMENT_NODE == 9
        - document

    Node.DOCUMENT_TYPE_NODE == 10
    Node.DOCUMENT_FRAGMENT_NODE == 11
    Node.NOTATION_NODE == 12
 */

/**
 *
 */
function isDOMSubject(el){
    var type;
    return K.isWindow(el) || el && (type = el.nodeType) && (type === 1 || type === 9);
};

/**
 * filter an array or Array-like object. this method will change the origin subject
 * @param {Array|Object} array
 * @param {function(mixed, number)} filter
 */
function filterArrayObject(array, filter){
    var i = 0;

    // array.length is changing
    for(; i < array.length; i ++){
        if(!filter.call(array, array[i], i)){

            // the member at the iterator has been removed, so we should move the iterator one step to the left
            array.splice(i --, 1);
        }
    }

    return array;
};

// function getContext(el){
    // window         -> window
    // body         -> body
    // [null]        -> [null]
    // $(window)    -> window
//    return !el || isDOMSubject(el) ? el : el._ === atom && el.context;
// };

/**

Select:
$(selector)             =
$(DOMElement)           =
$(NodeList)             =
$(NR.DOM)               =
$(selector, context)    +
$(Array.<DOMElement>)   +

Create:
$(fragment)             +       X
$(fragment, attrs)      +?      X

$.create(tagName, attrs);

*/

/**
 * @private
 * @param {string|DOMElement} selector
 * @param {(DOM|NodeList|DOMElement)=} context
 * @param {boolean=} first only get the first matched element
 */
function DOMInit(selector, context, first){

    // use ECMAScript strict
    var self = this,
        element;

    if(!selector){
        return self;
    }

    if(
        // if `selector` is a DOMElement, ignore `context`
        isDOMSubject(element = selector)

        // optimize finding document.body
        || selector === 'body' && !context && (element = DOC.body)
    ){
        self[0] = element;
        self.length = 1;
        return self;
    }

    if(K.isString(selector)){

        // give id special treatment,
        if(REGEX_IS_ID.test(selector)){
            var id = selector.slice(1),
                el = DOC.getElementById(id);

            // from jQuery:
            // > Check parentNode to catch when Blackberry 4.6 returns
            // > nodes that are no longer in the document [#6963](http://bugs.jquery.com/ticket/6963)
            if(el && el.parentNode){

                // > In IE7 Standards mode and previous modes,
                // > this method performs a case-insensitive match on both the ID and NAME attributes,
                // > which might produce unexpected results
                // via: [msdn](http://msdn.microsoft.com/en-us/library/ie/ms536437(v=vs.85).aspx)
                if(el.id !== id){
                    return ROOT.one(selector);
                }

                self[0] = el;
                self.length = 1;
            }

            return self;
        }

        // normal selectors
        return DOM(context || ROOT)[first ? 'one' : 'find'](selector);
    }

    makeArray(selector, self);
    filterArrayObject(self, isDOMSubject);

    if(first){
        self.splice(1);
    }

    return self;
};

function DOM(element, context){
    return element && element._ === atom ?

        // if already Neuron Element
        element
        : new DOMInit(element, context);
};

/**
 * @param {string} name
 * @param {function()} method
 * @param {string|boolean.<false>} type
 */

// extends is one of the javascript reserved words
function extend(name, method, type){
    var generator,
        host = DOMInit.prototype;

    if(K.isPlainObject(name)){
        generator = IMPLEMENT_GENERATOR[method] || IMPLEMENT_GENERATOR.def;

        for(var n in name){
            generator(host, n, name[n]);
        }
    }else{
        generator = IMPLEMENT_GENERATOR[type] || IMPLEMENT_GENERATOR.def;
        generator(host, name, method);
    }

    return DOM;
};

var

// save the current $ in window, for the future we need to return it back
WIN = K.__HOST,

// store the original value of $
_$ = WIN.$,

SELECTOR = K.S,

DOC = WIN.document,
ROOT = DOM(DOC),

REGEX_IS_ID = /^#[\w\-]+$/,

atom = K._,

AP = Array.prototype,
slice = AP.slice,

makeArray = K.makeArray,

/**
 'mutator'    :   the method modify the current context({Object} this.context, as the same as below),
                and return the modified DOM instance(the old one) itself

 'iterator'    :    the method iterate the current context, may modify the context, maybe not
                and return the modified DOM instance(the old one) itself

 X 'accessor'    :    the method will not modify the context, and generate something based on the current context,
                and return the new value
                (removed!)have been merged with 'def'

 'def'         :   simply implement the method into the prototype of DOM,
                and the returned value determined by the method
 */
IMPLEMENT_GENERATOR = {

// never used currently
/**
    mutator: function(host, name, method){
        host[name] = function(){
            var self = this;
            method.apply(self, arguments);

            return self;
        }
    },
*/

    iterator: function(host, name, method){
        host[name] = function(){
            var self = this,
                i = 0,
                len = self.length;

            for(; i < len; i ++){
                method.apply(self[i], arguments);
            }

            return self;
        }
    },

// never used currently
/**
    accessor: function(host, name, method){
        host[name] = function(){
            return method.apply(this, arguments);
        }
    },
*/
    def: function(host, name, method){
        host[name] = method;
    }
};

DOM.one = function(selector, context){
    return new DOMInit(selector, context, true);
};

extend({

    /**
     * if the prototype of a constructor contains `length` and `splice`, its instance will be printed like an array.

        function A(){};
        A.prototype = {
            length: 0,
            splice: [].slice
        }

        new A; // print []

     */

    // length must not be undefined,
    // otherwise, IE will fail(not throw an exception, but no effect) to push subject to Object use `AP.push.call`.
    // set to `0` as default
    length: 0,

    // @private
    // for internal use
    splice: AP.splice,

    // Neuron DOM methods:
    forEach: AP.forEach,

    // TODO:
    // filter: function(){
    //   var ret = new DOMInit();
    // },

    // identifier to mark Neuron DOM
    _: atom,

    /**
     * @param {string} selector
     * @returns {NR.DOM} Neuron DOM Object contains all matched elements

     * TODO:
     * {NR.DOM|DOMElement}
     */
    find: function(selector){
        return SELECTOR.find(selector, this, DOM());
    },

    /**
     * @returns {NR.DOM} Neuron DOM Object contains only the first matched element
     */
    one: function(selector){
        return SELECTOR.find(selector, this, DOM(), true);
    },

    /**
     * @param {number=} index
     *        if index is a number, returns the element of the specified index
     *        otherwise, returns the shadow array copy of all matched elements
     */
    get: function(index){
        return K.isNumber(index) ? this[index] : slice.call(this, 0);
    },

    count: function(){
        return this.length;
    },

    /**
     * add a subject to the current. using this method will change the origin object
     * @param {string|NR.DOM|DOMElement} subject the subject to be included into the current NR.DOM instance.
         subject could be string of css selectors, instance of NR.DOM, or native DOMElement
     */
    add: function(subject){
        subject && K._pushUnique(this, DOM(subject));
        return this;
    }
});

// traits
// @private
DOM.methods = {};

// @temp
DOM._ = DOMInit;

// @public
// create basic methods and hooks
DOM.__storage = {};

// method for extension
DOM.extend = extend;

// adaptor of selector engine
DOM.SELECTOR = SELECTOR;

// returns the $ object back to its original value
DOM.noConflict = function(){
    WIN.$ = _$;
};

WIN.$ = K.DOM = DOM;

})(NR);

/**
 change log:

 2012-07-27  Kael:
 - refractor

 */<commit_msg>dom/core: add annotations about running Array.prototype.push.call with Object<commit_after>/**
 * @preserve Neuron core:dom v2.0(jQuerified)
 */
;(function(K){

/**
 nodeType:
 ref: https://developer.mozilla.org/en/nodeType

    Node.ELEMENT_NODE == 1
        - document.documentElement
        - document.body
        -

    Node.ATTRIBUTE_NODE == 2
    Node.TEXT_NODE == 3
    Node.CDATA_SECTION_NODE == 4
    Node.ENTITY_REFERENCE_NODE == 5
    Node.ENTITY_NODE == 6
    Node.PROCESSING_INSTRUCTION_NODE == 7
    Node.COMMENT_NODE == 8
    Node.DOCUMENT_NODE == 9
        - document

    Node.DOCUMENT_TYPE_NODE == 10
    Node.DOCUMENT_FRAGMENT_NODE == 11
    Node.NOTATION_NODE == 12
 */

/**
 *
 */
function isDOMSubject(el){
    var type;
    return K.isWindow(el) || el && (type = el.nodeType) && (type === 1 || type === 9);
};

/**
 * filter an array or Array-like object. this method will change the origin subject
 * @param {Array|Object} array
 * @param {function(mixed, number)} filter
 */
function filterArrayObject(array, filter){
    var i = 0;

    // array.length is changing
    for(; i < array.length; i ++){
        if(!filter.call(array, array[i], i)){

            // the member at the iterator has been removed, so we should move the iterator one step to the left
            array.splice(i --, 1);
        }
    }

    return array;
};

// function getContext(el){
    // window         -> window
    // body         -> body
    // [null]        -> [null]
    // $(window)    -> window
//    return !el || isDOMSubject(el) ? el : el._ === atom && el.context;
// };

/**

Select:
$(selector)             =
$(DOMElement)           =
$(NodeList)             =
$(NR.DOM)               =
$(selector, context)    +
$(Array.<DOMElement>)   +

Create:
$(fragment)             +       X
$(fragment, attrs)      +?      X

$.create(tagName, attrs);

*/

/**
 * @private
 * @param {string|DOMElement} selector
 * @param {(DOM|NodeList|DOMElement)=} context
 * @param {boolean=} first only get the first matched element
 */
function DOMInit(selector, context, first){

    // use ECMAScript strict
    var self = this,
        element;

    if(!selector){
        return self;
    }

    if(
        // if `selector` is a DOMElement, ignore `context`
        isDOMSubject(element = selector)

        // optimize finding document.body
        || selector === 'body' && !context && (element = DOC.body)
    ){
        self[0] = element;
        self.length = 1;
        return self;
    }

    if(K.isString(selector)){

        // give id special treatment,
        if(REGEX_IS_ID.test(selector)){
            var id = selector.slice(1),
                el = DOC.getElementById(id);

            // from jQuery:
            // > Check parentNode to catch when Blackberry 4.6 returns
            // > nodes that are no longer in the document [#6963](http://bugs.jquery.com/ticket/6963)
            if(el && el.parentNode){

                // > In IE7 Standards mode and previous modes,
                // > this method performs a case-insensitive match on both the ID and NAME attributes,
                // > which might produce unexpected results
                // via: [msdn](http://msdn.microsoft.com/en-us/library/ie/ms536437(v=vs.85).aspx)
                if(el.id !== id){
                    return ROOT.one(selector);
                }

                self[0] = el;
                self.length = 1;
            }

            return self;
        }

        // normal selectors
        return DOM(context || ROOT)[first ? 'one' : 'find'](selector);
    }

    makeArray(selector, self);
    filterArrayObject(self, isDOMSubject);

    if(first){
        self.splice(1);
    }

    return self;
};

function DOM(element, context){
    return element && element._ === atom ?

        // if already Neuron Element
        element
        : new DOMInit(element, context);
};

/**
 * @param {string} name
 * @param {function()} method
 * @param {string|boolean.<false>} type
 */

// extends is one of the javascript reserved words
function extend(name, method, type){
    var generator,
        host = DOMInit.prototype;

    if(K.isPlainObject(name)){
        generator = IMPLEMENT_GENERATOR[method] || IMPLEMENT_GENERATOR.def;

        for(var n in name){
            generator(host, n, name[n]);
        }
    }else{
        generator = IMPLEMENT_GENERATOR[type] || IMPLEMENT_GENERATOR.def;
        generator(host, name, method);
    }

    return DOM;
};

var

// save the current $ in window, for the future we need to return it back
WIN = K.__HOST,

// store the original value of $
_$ = WIN.$,

SELECTOR = K.S,

DOC = WIN.document,
ROOT = DOM(DOC),

REGEX_IS_ID = /^#[\w\-]+$/,

atom = K._,

AP = Array.prototype,
slice = AP.slice,

makeArray = K.makeArray,

/**
 'mutator'    :   the method modify the current context({Object} this.context, as the same as below),
                and return the modified DOM instance(the old one) itself

 'iterator'    :    the method iterate the current context, may modify the context, maybe not
                and return the modified DOM instance(the old one) itself

 X 'accessor'    :    the method will not modify the context, and generate something based on the current context,
                and return the new value
                (removed!)have been merged with 'def'

 'def'         :   simply implement the method into the prototype of DOM,
                and the returned value determined by the method
 */
IMPLEMENT_GENERATOR = {

// never used currently
/**
    mutator: function(host, name, method){
        host[name] = function(){
            var self = this;
            method.apply(self, arguments);

            return self;
        }
    },
*/

    iterator: function(host, name, method){
        host[name] = function(){
            var self = this,
                i = 0,
                len = self.length;

            for(; i < len; i ++){
                method.apply(self[i], arguments);
            }

            return self;
        }
    },

// never used currently
/**
    accessor: function(host, name, method){
        host[name] = function(){
            return method.apply(this, arguments);
        }
    },
*/
    def: function(host, name, method){
        host[name] = method;
    }
};

DOM.one = function(selector, context){
    return new DOMInit(selector, context, true);
};

extend({

    /**
     * if the prototype of a constructor contains `length` and `splice`,
     * its instance will be printed like an array, which is what firebug and webkit console[native code] do.

        function A(){};
        A.prototype = {
            length: 0,
            splice: [].slice
        }

        new A; // print []

     */

    // length must not be undefined,
    // otherwise, IE will fail(not throw an exception, but no effect) to push subject to Object use `AP.push.call`.
    // set to `0` as default
    length: 0,

    // @private
    // for internal use
    splice: AP.splice,

    // Neuron DOM methods:
    forEach: AP.forEach,

    // TODO:
    // filter: function(){
    //   var ret = new DOMInit();
    // },

    // identifier to mark Neuron DOM
    _: atom,

    /**
     * @param {string} selector
     * @returns {NR.DOM} Neuron DOM Object contains all matched elements

     * TODO:
     * {NR.DOM|DOMElement}
     */
    find: function(selector){
        return SELECTOR.find(selector, this, DOM());
    },

    /**
     * @returns {NR.DOM} Neuron DOM Object contains only the first matched element
     */
    one: function(selector){
        return SELECTOR.find(selector, this, DOM(), true);
    },

    /**
     * @param {number=} index
     *        if index is a number, returns the element of the specified index
     *        otherwise, returns the shadow array copy of all matched elements
     */
    get: function(index){
        return K.isNumber(index) ? this[index] : slice.call(this, 0);
    },

    count: function(){
        return this.length;
    },

    /**
     * add a subject to the current. using this method will change the origin object
     * @param {string|NR.DOM|DOMElement} subject the subject to be included into the current NR.DOM instance.
         subject could be string of css selectors, instance of NR.DOM, or native DOMElement
     */
    add: function(subject){
        subject && K._pushUnique(this, DOM(subject));
        return this;
    }
});

// traits
// @private
DOM.methods = {};

// @temp
DOM._ = DOMInit;

// @public
// create basic methods and hooks
DOM.__storage = {};

// method for extension
DOM.extend = extend;

// adaptor of selector engine
DOM.SELECTOR = SELECTOR;

// returns the $ object back to its original value
DOM.noConflict = function(){
    WIN.$ = _$;
};

WIN.$ = K.DOM = DOM;

})(NR);

/**
 change log:

 2012-07-27  Kael:
 - refractor

 */<|endoftext|>


<commit_before>import React from 'react';

// MATERIAL DESIGN
import Card from 'material-ui/lib/card/card';
import CardActions from '../../node_modules/material-ui/lib/card/card-actions';
import CardHeader from '../../node_modules/material-ui/lib/card/card-header';
import CardMedia from '../../node_modules/material-ui/lib/card/card-media';
import CardTitle from '../../node_modules/material-ui/lib/card/card-title';
import DropDownMenu from '../../node_modules/material-ui/lib/DropDownMenu';
import FlatButton from '../../node_modules/material-ui/lib/flat-button';
import FloatingActionButton from '../../node_modules/material-ui/lib/floating-action-button';
import Favorite from '../../node_modules/material-ui/lib/svg-icons/action/favorite';
import Face from '../../node_modules/material-ui/lib/svg-icons/action/face';
import TrendingUp from '../../node_modules/material-ui/lib/svg-icons/action/trending-up';
import MenuItem from '../../node_modules/material-ui/lib/menus/menu-item';
import Mic from '../../node_modules/material-ui/lib/svg-icons/av/mic';
import MicOff from '../../node_modules/material-ui/lib/svg-icons/av/mic-off';
import CardText from '../../node_modules/material-ui/lib/card/card-text';
import TextField from '../../node_modules/material-ui/lib/text-field';
import RaisedButton from '../../node_modules/material-ui/lib/raised-button';
import Colors from '../../node_modules/material-ui/lib/styles/colors';

import Paper from '../../node_modules/material-ui/lib/paper';

// ROUTER
import { History } from 'react-router';
import reactMixin from 'react-mixin';

// COMPONENTS
import NavBar from './NavBar.js';

// AJAX GET CALL
import $ from '../../public/js/jquery-1.11.1.min';

class BroadcastLive extends React.Component {
  constructor(props) {
    var user = JSON.parse(localStorage.getItem("me"));
    super(props);
    this.state = {
      disabled: false,
      heartCount: 4000,
      listnerLiveCount: 1000,
      name: "Screw It, We're Doing It Live",
      description: "Description of the station",
      artist: user.full_name || "anonymous",
      artistAlias: user.username || "QuantumRadio Broadcaster",
      artistImage: user.avatar_url,
      selectedSource: null,
      audioSources: []
    };
  }

  gotMediaDevices(devices) {
    var audioSources = [];
    console.log('GMD was called');
    devices.forEach(function(device) {
      if (device.kind === 'audio') {
        audioSources.push({
          id: device.id,
          label: device.label || 'Input ' + audioSources.length + 1
        })
      }
    });
    this.setState({
      audioSources: audioSources
    });
  }

  componentWillMount() {
    console.log(this.props.location.state.streamId);
    // var streamId = 44;
    this.bc = new Broadcaster(this.props.location.state.streamId, this.gotMediaDevices.bind(this), function(data) {
        var canvas = document.getElementById("canvas"),
            width = canvas.width,
            height = canvas.height,
            context = canvas.getContext('2d');
        context.clearRect(0, 0, width, height);
        var step = Math.ceil(data.length / width);
        var amp = height / 2;
        for (var i = 0; i < width; i++) {
            var min = 1.0;
            var max = -1.0;
            for (var j = 0; j < step; j++) {
                var datum = data[(i * step) + j];
                if (datum < min)
                    min = datum;
                if (datum > max)
                    max = datum;
            }
            context.fillRect(i, (1 + min) * amp, 1, Math.max(1, (max - min) * amp));
        }
    });
    this.fetchData();
  }

  fetchData() {
    $.ajax({
      url: '/api/stream/'+this.props.location.state.streamId
    })
    .done((responseData) => {
      this.setState({
        stream: responseData
      });
      console.log('State',this.state);
    });
  }

  startBroadcast() {
    if (this.state.selectedSource) {
      this.setState({
        disabled: true
      });
      this.bc.start(this.state.selectedSource);
    }
  }

  stopBroadcast() {
    this.setState({
      disabled: false
    })
    this.bc.stop();
  }

  sourceInput(event, index, value) {
    this.setState({
      selectedSource: value
    })
  }

  goToProfile() {
    this.props.history.push({
      pathname: '/profile/'+ this.state.stream.creator
    })
  }

  render() {
    console.log(this.state.audioSources);
    var dropDown = (
      <DropDownMenu value={this.state.selectedSource} onChange={this.sourceInput.bind(this)}>
        <MenuItem value={null} key={null} primaryText={"Select a source..."} />
        {this.state.audioSources.map(source => <MenuItem value={source.id} key={source.id} primaryText={source.label} />)}
      </DropDownMenu>
    )

    return (

        <NavBar history={this.history}/>

          <Card style={styles.mainBox}>

            <CardHeader
              onClick={this.goToProfile.bind(this)}
              title={this.state.artistAlias}
              // subtitle={this.state.artist}
            />
            <CardMedia style={styles.streamImage}>

            </CardMedia>

            <CardActions>
              <FloatingActionButton onClick={this.startBroadcast.bind(this)} disabled={this.state.disabled}>
                <Mic />
              </FloatingActionButton>
              <FloatingActionButton onClick={this.stopBroadcast.bind(this)} disabled={!this.state.disabled}>
               <MicOff />
              </FloatingActionButton>
              {dropDown}
            </CardActions>

            <CardTitle title={this.state.stream.name}/>
            <CardText>
              {this.state.stream.description}
            </CardText>
          </Card>
          <Card style={styles.box}>
            <CardText>

STATS <TrendingUp />

{this.state.stream.listenerMaxCount.toLocaleString()} Total Listeners <Face />

{this.state.stream.listenerLiveCount.toLocaleString()} Current Listeners <Face />

{this.state.stream.heartCountNum.toLocaleString()} Hearts <Favorite color={Colors.red500}/>

            </CardText>

              <canvas width="600" height="100" id="canvas"></canvas>

          </Card>

    )
  }
}

var styles = {
  container:{
    'display': 'flex',
    'flexDirection' :'column',
  },

  cardContainer:{
    'display': 'flex',
    'flexDirection':'row',
    'flexWrap': 'wrap'
  },

  box: {
    'flex':1
  },

  mainBox: {
    'flexGrow':1,
    'maxWidth': '600px'
  },

  streamImage:{
    'maxWidth': '300px'
  },

  count:{
    fontWeight: 'bold'
  }
}

reactMixin.onClass(BroadcastLive, History);

export default BroadcastLive;
<commit_msg>Refactored: moved visualizer into separate file - Also, now grabbing metadata from server (fixed typos).<commit_after>import React from 'react';

// MATERIAL DESIGN
import Card from 'material-ui/lib/card/card';
import CardActions from '../../node_modules/material-ui/lib/card/card-actions';
import CardHeader from '../../node_modules/material-ui/lib/card/card-header';
import CardMedia from '../../node_modules/material-ui/lib/card/card-media';
import CardTitle from '../../node_modules/material-ui/lib/card/card-title';
import DropDownMenu from '../../node_modules/material-ui/lib/DropDownMenu';
import FlatButton from '../../node_modules/material-ui/lib/flat-button';
import FloatingActionButton from '../../node_modules/material-ui/lib/floating-action-button';
import Favorite from '../../node_modules/material-ui/lib/svg-icons/action/favorite';
import Face from '../../node_modules/material-ui/lib/svg-icons/action/face';
import TrendingUp from '../../node_modules/material-ui/lib/svg-icons/action/trending-up';
import MenuItem from '../../node_modules/material-ui/lib/menus/menu-item';
import Mic from '../../node_modules/material-ui/lib/svg-icons/av/mic';
import MicOff from '../../node_modules/material-ui/lib/svg-icons/av/mic-off';
import CardText from '../../node_modules/material-ui/lib/card/card-text';
import TextField from '../../node_modules/material-ui/lib/text-field';
import RaisedButton from '../../node_modules/material-ui/lib/raised-button';
import Colors from '../../node_modules/material-ui/lib/styles/colors';

import Paper from '../../node_modules/material-ui/lib/paper';

// ROUTER
import { History } from 'react-router';
import reactMixin from 'react-mixin';

// COMPONENTS
import NavBar from './NavBar.js';

// VISUALIZER
import visualizer from './visualizer.js';

// AJAX GET CALL
import $ from '../../public/js/jquery-1.11.1.min';

class BroadcastLive extends React.Component {
  constructor(props) {
    var user = JSON.parse(localStorage.getItem("me"));
    super(props);
    this.state = {
      disabled: false,
      heartCount: 4000,
      listenerLiveCount: 1000,
      listenerMaxCount: 1000000,
      name: "Screw It, We're Doing It Live",
      description: "Description of the station",
      artist: user.full_name || "pseudonymous",
      artistAlias: user.username || "QuantumRadio Broadcaster",
      artistImage: user.avatar_url,
      selectedSource: null,
      audioSources: []
    };
  }

  gotMediaDevices(devices) {
    var audioSources = [];
    console.log('GMD was called');
    devices.forEach(function(device) {
      if (device.kind === 'audio') {
        audioSources.push({
          id: device.id,
          label: device.label || 'Input ' + audioSources.length + 1
        })
      }
    });
    this.setState({
      audioSources: audioSources
    });
  }

  componentWillMount() {
    console.log(this.props.location.state.streamId);
    // var streamId = 44;
    this.bc = new Broadcaster(this.props.location.state.streamId, this.gotMediaDevices.bind(this), visualizer);
    this.fetchData();
  }

  fetchData() {
    $.ajax({
      url: '/api/stream/'+this.props.location.state.streamId
    })
    .done((stream) => {
      this.setState({
        name: stream.name,
        description: stream.description,
        heartCount: stream.heartCountNum,
        listenerLiveCount: stream.listenerLiveCount,
        listenerMaxCount: stream.listenerMaxCount
      });
      console.log('State',this.state);
    });
  }

  startBroadcast() {
    if (this.state.selectedSource) {
      this.setState({
        disabled: true
      });
      this.bc.start(this.state.selectedSource);
    }
  }

  stopBroadcast() {
    this.setState({
      disabled: false
    })
    this.bc.stop();
  }

  sourceInput(event, index, value) {
    this.setState({
      selectedSource: value
    })
  }

  goToProfile() {
    this.props.history.push({
      pathname: '/profile/'+ this.state.stream.creator
    })
  }

  render() {
    console.log(this.state.audioSources);
    var dropDown = (
      <DropDownMenu value={this.state.selectedSource} onChange={this.sourceInput.bind(this)}>
        <MenuItem value={null} key={null} primaryText={"Select a source..."} />
        {this.state.audioSources.map(source => <MenuItem value={source.id} key={source.id} primaryText={source.label} />)}
      </DropDownMenu>
    )

    return (

        <NavBar history={this.history}/>

          <Card style={styles.mainBox}>

            <CardHeader
              onClick={this.goToProfile.bind(this)}
              title={this.state.artistAlias}
              subtitle={this.state.artist}
            />
            <CardMedia style={styles.streamImage}>

            </CardMedia>

            <CardActions>
              <FloatingActionButton onClick={this.startBroadcast.bind(this)} disabled={this.state.disabled}>
                <Mic />
              </FloatingActionButton>
              <FloatingActionButton onClick={this.stopBroadcast.bind(this)} disabled={!this.state.disabled}>
               <MicOff />
              </FloatingActionButton>
              {dropDown}
            </CardActions>

            <CardTitle title={this.state.name}/>
            <CardText>
              {this.state.description}
            </CardText>
          </Card>
          <Card style={styles.box}>
            <CardText>

STATS <TrendingUp />

{this.state.listenerMaxCount.toLocaleString()} Total Listeners <Face />

{this.state.listenerLiveCount.toLocaleString()} Current Listeners <Face />

{this.state.heartCount.toLocaleString()} Hearts <Favorite color={Colors.red500}/>

            </CardText>
            <canvas width="600" height="100" id="visualizer"></canvas>
          </Card>

    )
  }
}

var styles = {
  container:{
    'display': 'flex',
    'flexDirection' :'column',
  },

  cardContainer:{
    'display': 'flex',
    'flexDirection':'row',
    'flexWrap': 'wrap'
  },

  box: {
    'flex':1
  },

  mainBox: {
    'flexGrow':1,
    'maxWidth': '600px'
  },

  streamImage:{
    'maxWidth': '300px'
  },

  count:{
    fontWeight: 'bold'
  }
}

reactMixin.onClass(BroadcastLive, History);

export default BroadcastLive;
<|endoftext|>