<commit_before>/*
NodeBench : An apache bench like web bench marking tool using nodejs.
*/

var cluster = require('cluster');
var http = require('http');
var url = require('url');

var benchmrk_opts = {
	num_requests : 1,
	num_concur : 1
};

var url_options = {
	host : 'www.google.co.in',
	port : 80,
	path : '/',
	method : 'GET',
	agent : false
};

var program = require('commander');

	//Parse the command line arguments
program
  .version('0.0.1')
  .usage('[options] <url>')
  .option('-n, --numreqs <n>', 'Number of requests', parseInt)
  .option('-c, --concurrent <n>', 'Number of concurrent request', parseInt)
  .option('-U, --url <url>', 'URL to run bench mark')
  .parse(process.argv);

if(program.numreqs) benchmrk_opts.num_requests=program.numreqs;
if(program.concurrent) benchmrk_opts.num_concur = program.concurrent;
if(program.url) {

	console.log(program.url + "\n");
	var parsedurl = url.parse(program.url);

	url_options.host = parsedurl.hostname;
	url_options.port = parsedurl.port;
	url_options.path = parsedurl.path;
}else{
	console.log('Missing arguments');
	process.exit(1);
}

/// Master Node
if (cluster.isMaster) {
	var spawned_reqs=0;
	var num_forked = 0;
	var num_died = 0;
	var current_req=0;
	var results = [];

	//console.log('n: ' + benchmrk_opts.num_requests + ' c: ' + benchmrk_opts.num_concur + ' ' + JSON.stringify(url_options));
	//process.exit(0);

	var timerid = setInterval(spawnWorker, 500);

    function onMessage(msg) {
        if (msg.cmd && msg.cmd == 'onworkdone') {
            if (benchmrk_opts.num_requests == spawned_reqs) {
                console.log('Clearing Timer');
                clearInterval(timerid);
                console.log('Results : \n ' + JSON.stringify(results));
            }
            console.log('STATUS     : ' + msg.status);
            //console.log('\nHEADERS    : ' + JSON.stringify(msg.headers));
            console.log('DataLength : ' + msg.datalength);
            console.log('Time Taken : ' + msg.time + '\n');
            results[current_req] = msg;
            console.log('Current Req ' + current_req);
            current_req = current_req + 1;
        }
    }

	function spawnWorker() {
		console.log('\n++++++++setInterval++++++\n');
		if (num_forked == num_died && spawned_reqs <benchmrk_opts.num_requests) {
			num_forked = 0;
			num_died = 0;
			var numtofork = ((benchmrk_opts.num_requests-spawned_reqs)>=benchmrk_opts.num_concur)?benchmrk_opts.num_concur:(benchmrk_opts.num_requests-spawned_reqs);
			console.log("num to fork: " + numtofork + "\n");
			for (var i = 0; i < numtofork; i++) {

				var worker = cluster.fork();
				num_forked++;
				spawned_reqs=spawned_reqs+1;
				console.log('Worker PID : ' + worker.pid);
				worker.on('message', onMessage );
			}
		}

	}

	cluster.on('death', function (worker) {
		num_died = num_died + 1;
		//console.log('worker ' + worker.pid + ' died.');
		console.log('Number of died : ' + num_died);
	});

} else {

	/// Worker Process.
	var timetook;
	var datalen = 0;
	//console.time('http-request-time-' + process.pid);
	var startTime = (new Date()).getTime();
	var req = http.request(url_options, function (res) {
			// var req = http.get(url_options, function(res) {
			res.setEncoding('utf8');

			res.on('close', function (err) {
				process.send({
					cmd : 'onworkdone',
					status : 'closed',
					headers : res.headers,
					datalength : datalen,
					time : timetook
				});
			});

			res.on('end', function () {
				//console.timeEnd('http-request-time-' + process.pid);
				timetook = (new Date()).getTime() - startTime;
				process.send({
					cmd : 'onworkdone',
					status : res.statusCode,
					headers : res.headers,
					datalength : datalen,
					time : timetook
				});
				process.exit(0);
			});

			res.on('data', function (chunk) {
				//console.log('BODY: ' + chunk);
				//console.log('Data Length:' + chunk.length);
				datalen = datalen + chunk.length;
			});
		});

	req.on('error', function (e) {
		console.log('problem with request: ' + e.message);

	});

	req.end();
}
<commit_msg>Modifying the nodebench for proxy support<commit_after>/*
NodeBench : An apache bench like web bench marking tool using nodejs.
*/

var cluster = require('cluster');
var http = require('http');
var url = require('url');
var program = require('commander');

var benchmrk_opts = {
	num_requests : 1,
	num_concur : 1
};

var url_options = {
	host : 'www.google.co.in',
	port : 80,
	path : '/',
	method : 'GET',
	agent : false
};

//Parse the command line arguments
program
  .version('0.0.1')
  .usage('[options] <url>')
  .option('-n, --numreqs <n>', 'Number of requests', parseInt)
  .option('-c, --concurrent <n>', 'Number of concurrent request', parseInt)
  .option('-U, --url <url>', 'URL to run bench mark')
  .option('-A, --authentication <credentials>', 'Basic Authentication credentials colon separated')
  .option('-P, --proxycred <credentials>', 'Proxy authentication credentials colon separated')
  .option('-X, --proxy <proxy:port>', 'Proxy server and port <proxy:port>')
  .parse(process.argv);

if(program.numreqs) benchmrk_opts.num_requests=program.numreqs;
if(program.concurrent) benchmrk_opts.num_concur = program.concurrent;
if(program.url) {

	console.log(program.url + "\n");
	var parsedurl = url.parse(program.url);

	url_options.host = parsedurl.hostname;
	url_options.port = parsedurl.port;
	url_options.path = parsedurl.path;
}else{
	console.log('Missing arguments');
	process.exit(1);
}

/// Master Node
if (cluster.isMaster) {
	var spawned_reqs=0;
	var num_forked = 0;
	var num_died = 0;
	var current_req=0;
	var results = [];

	//console.log('n: ' + benchmrk_opts.num_requests + ' c: ' + benchmrk_opts.num_concur + ' ' + JSON.stringify(url_options));
	//process.exit(0);

	var timerid = setInterval(spawnWorker, 500);

    function onMessage(msg) {
        if (msg.cmd && msg.cmd == 'onworkdone') {
            if (benchmrk_opts.num_requests == spawned_reqs) {
                console.log('Clearing Timer');
                clearInterval(timerid);
                console.log('Results : \n ' + JSON.stringify(results));
            }
            console.log('STATUS     : ' + msg.status);
            //console.log('\nHEADERS    : ' + JSON.stringify(msg.headers));
            console.log('DataLength : ' + msg.datalength);
            console.log('Time Taken : ' + msg.time + '\n');
            results[current_req] = msg;
            console.log('Current Req ' + current_req);
            current_req = current_req + 1;
        }
    }

	function spawnWorker() {
		console.log('\n++++++++setInterval++++++\n');
		if (num_forked == num_died && spawned_reqs <benchmrk_opts.num_requests) {
			num_forked = 0;
			num_died = 0;
			var numtofork = ((benchmrk_opts.num_requests-spawned_reqs)>=benchmrk_opts.num_concur)?benchmrk_opts.num_concur:(benchmrk_opts.num_requests-spawned_reqs);
			console.log("num to fork: " + numtofork + "\n");
			for (var i = 0; i < numtofork; i++) {

				var worker = cluster.fork();
				num_forked++;
				spawned_reqs=spawned_reqs+1;
				console.log('Worker PID : ' + worker.pid);
				worker.on('message', onMessage );
			}
		}

	}

	cluster.on('death', function (worker) {
		num_died = num_died + 1;
		//console.log('worker ' + worker.pid + ' died.');
		console.log('Number of died : ' + num_died);
	});

} else {

	/// Worker Process.
	var timetook;
	var datalen = 0;
	//console.time('http-request-time-' + process.pid);
	var startTime = (new Date()).getTime();
	var req = http.request(url_options, function (res) {
			// var req = http.get(url_options, function(res) {
			res.setEncoding('utf8');

			res.on('close', function (err) {
				process.send({
					cmd : 'onworkdone',
					status : 'closed',
					headers : res.headers,
					datalength : datalen,
					time : timetook
				});
			});

			res.on('end', function () {
				//console.timeEnd('http-request-time-' + process.pid);
				timetook = (new Date()).getTime() - startTime;
				process.send({
					cmd : 'onworkdone',
					status : res.statusCode,
					headers : res.headers,
					datalength : datalen,
					time : timetook
				});
				process.exit(0);
			});

			res.on('data', function (chunk) {
				//console.log('BODY: ' + chunk);
				//console.log('Data Length:' + chunk.length);
				datalen = datalen + chunk.length;
			});
		});

	req.on('error', function (e) {
		console.log('problem with request: ' + e.message);

	});

	req.end();
}
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import warning from 'warning';

import {
  ListRow,
  Button,
  Icon,
  Text,
  TextLabel,
} from '@ichef/gypcrete';

import Popover from '@ichef/gypcrete/lib/Popover';
import prefixClass from '@ichef/gypcrete/lib/utils/prefixClass';
import icBEM from '@ichef/gypcrete/lib/utils/icBEM';

import SelectList from './SelectList';

import parseSelectOptions from './utils/parseSelectOptions';
import formRow, { rowPropTypes } from './mixins/formRow';
import './styles/SelectRow.scss';

export const COMPONENT_NAME = prefixClass('form-select');
const ROOT_BEM = icBEM(COMPONENT_NAME);
export const BEM = {
  root: ROOT_BEM,
  popover: ROOT_BEM.element('popover'),
  placeholder: ROOT_BEM.element('placeholder'),
};

const CLOSABLE_CONFIG = {
  onEscape: true,
  onClickOutside: true,
  onClickInside: false,
};

/**
 * Generate a value-label map from all `<SelectOption>`s.
 *
 * @param {array} fromOptions
 * @return {Map}
 */
function getValueToLabelAvatarMap(fromChildren = []) {
  const resultMap = new Map();
  const options = parseSelectOptions(fromChildren);

  options.forEach(
    (option) => {
      const { label, avatar } = option;
      resultMap.set(option.value, {
        label,
        avatar,
      });
    }
  );
  return resultMap;
}

class SelectRow extends PureComponent {
    static propTypes = {
      label: PropTypes.node.isRequired,
      asideAllLabel: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.bool, // can pass false to disable 'All' label
      ]),
      asideNoneLabel: PropTypes.string,
      asideSeparator: PropTypes.string,
      disabled: PropTypes.bool,
      // <SelectList> props
      multiple: SelectList.propTypes.multiple,
      value: SelectList.propTypes.value,
      defaultValue: SelectList.propTypes.defaultValue,
      onChange: PropTypes.func,
      // from formRow()
      ineditable: PropTypes.bool,
      rowProps: rowPropTypes,
    };

    static defaultProps = {
      asideAllLabel: 'All',
      asideNoneLabel: '(Unset)',
      asideSeparator: ', ',
      disabled: false,
      // <SelectList> props
      multiple: SelectList.defaultProps.multiple,
      value: SelectList.defaultProps.value,
      defaultValue: SelectList.defaultProps.defaultValue,
      onChange: () => {},
      // from formRow()
      ineditable: false,
      rowProps: {},
    };

    state = {
      isPopoverOpen: false,
      valueLabelMap: getValueToLabelAvatarMap(this.props.children),
      cachedValue: this.getInitialValue(),
    };

    // eslint-disable-next-line react/no-deprecated
    componentWillReceiveProps(nextProps) {
      this.setState({
        valueLabelMap: getValueToLabelAvatarMap(nextProps.children),
      });

      warning(
        this.getIsControlled(this.props) === this.getIsControlled(nextProps),
        '<SelectRow> should not switch from controlled to uncontrolled (or vice versa).'
      );

      if (this.getIsControlled(nextProps)) {
        this.setState({ cachedValue: nextProps.value });
      } else if (this.props.multiple !== nextProps.multiple) {
        warning(false, '<SelectRow>: you should not change `multiple` prop while it is uncontrolled. Its value will be reset now.');
        this.setState({ cachedValue: (nextProps.multiple) ? [] : null });
      }
    }

    getInitialValue() {
      const { value, defaultValue, multiple } = this.props;

      if (value !== undefined) {
        return value;
      }

      if (multiple && defaultValue === undefined) {
        return [];
      }

      return defaultValue;
    }

    getIsControlled(fromProps = this.props) {
      return fromProps.value !== undefined;
    }

    getCacheValueArray = () => {
      const { multiple } = this.props;
      const { cachedValue } = this.state;
      return (multiple) ? cachedValue : [cachedValue].filter(val => val !== undefined);
    }

    handleButtonClick = () => {
      this.setState({ isPopoverOpen: true });
    }

    handlePopoverClose = () => {
      this.setState({ isPopoverOpen: false });
    }

    handleSelectChange = (nextValue) => {
      if (!this.getIsControlled()) {
        this.setState({ cachedValue: nextValue });
      }
      this.props.onChange(nextValue);

      if (!this.props.multiple) {
        this.handlePopoverClose();
      }
    }

    renderPopover(selectListProps) {
      return (
        <Popover
          anchor={this.anchorNode}
          className={BEM.popover.toString()}
          closable={CLOSABLE_CONFIG}
          onClose={this.handlePopoverClose}
        >
          <SelectList
            value={this.state.cachedValue}
            onChange={this.handleSelectChange}
            {...selectListProps}
          />
        </Popover>
      );
    }

    renderRowValueLabel() {
      const { multiple, asideAllLabel, asideNoneLabel, asideSeparator } = this.props;
      const { cachedValue, valueLabelMap } = this.state;

      const isSingleEmptyValue = cachedValue === undefined || cachedValue === '';
      const isMultipleEmptyValue = multiple && cachedValue.length === 0;

      if (isSingleEmptyValue || isMultipleEmptyValue) {
        return {asideNoneLabel};
      }

      if (multiple) {
        // Can turn off 'All' display by passing `false`.
        if (asideAllLabel && cachedValue.length === valueLabelMap.size) {
          return asideAllLabel;
        }
      }

      return this.getCacheValueArray()
        .map((value) => {
          const valueMap = valueLabelMap.get(value) || {};
          return valueMap.label;
        })
        .filter(label => Boolean(label))
        .join(asideSeparator);
    }

    renderAvatar() {
      const { valueLabelMap } = this.state;
      return this.getCacheValueArray()
        .map((value) => {
          const valueMap = valueLabelMap.get(value) || {};
          return (
            <React.Fragment key={value}>
              {valueMap.avatar}
            </React.Fragment>
          );
        });
    }

    render() {
      const {
        label,
        asideAllLabel,
        asideNoneLabel,
        asideSeparator,
        disabled,
        // <ListRow> props (intercepted from it)
        // multiple,
        value,
        defaultValue,
        onChange,
        // from formRow()
        ineditable,
        rowProps,
        // React props
        className,
        ...selectListProps
      } = this.props;
      const { isPopoverOpen } = this.state;

      const wrapperClassName = classNames(
        COMPONENT_NAME,
        className,
      );

      const Content = ineditable ? TextLabel : Button;
      const contentProps = ineditable ? {} : {
        onClick: this.handleButtonClick,
      };

      return (
        <ListRow className={wrapperClassName} {...rowProps}>
          {this.renderAvatar()}
          <Content minified={false} disabled={disabled} {...contentProps}>
            <Text
              verticalOrder="reverse"
              bold={!ineditable}
              basic={this.renderRowValueLabel()}
              aside={label}
            />

             { this.anchorNode = ref; }}>
              <Icon type="dropdown" />

            {isPopoverOpen && this.renderPopover(selectListProps)}
          </Content>
        </ListRow>
      );
    }
}

export { SelectRow as PureSelectRow };
export default formRow()(SelectRow);
<commit_msg>feat: add renderRowValueLabel for SelectRow<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import warning from 'warning';

import {
  ListRow,
  Button,
  Icon,
  Text,
  TextLabel,
} from '@ichef/gypcrete';

import Popover from '@ichef/gypcrete/lib/Popover';
import prefixClass from '@ichef/gypcrete/lib/utils/prefixClass';
import icBEM from '@ichef/gypcrete/lib/utils/icBEM';

import SelectList from './SelectList';

import parseSelectOptions from './utils/parseSelectOptions';
import formRow, { rowPropTypes } from './mixins/formRow';
import './styles/SelectRow.scss';

export const COMPONENT_NAME = prefixClass('form-select');
const ROOT_BEM = icBEM(COMPONENT_NAME);
export const BEM = {
  root: ROOT_BEM,
  popover: ROOT_BEM.element('popover'),
  placeholder: ROOT_BEM.element('placeholder'),
};

const CLOSABLE_CONFIG = {
  onEscape: true,
  onClickOutside: true,
  onClickInside: false,
};

/**
 * Generate a value-label map from all `<SelectOption>`s.
 *
 * @param {array} fromOptions
 * @return {Map}
 */
function getValueToLabelAvatarMap(fromChildren = []) {
  const resultMap = new Map();
  const options = parseSelectOptions(fromChildren);

  options.forEach(
    (option) => {
      const { label, avatar } = option;
      resultMap.set(option.value, {
        label,
        avatar,
      });
    }
  );
  return resultMap;
}

class SelectRow extends PureComponent {
    static propTypes = {
      label: PropTypes.node.isRequired,
      asideAllLabel: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.bool, // can pass false to disable 'All' label
      ]),
      asideNoneLabel: PropTypes.string,
      asideSeparator: PropTypes.string,
      disabled: PropTypes.bool,
      renderRowValueLabel: PropTypes.func,
      // <SelectList> props
      multiple: SelectList.propTypes.multiple,
      value: SelectList.propTypes.value,
      defaultValue: SelectList.propTypes.defaultValue,
      onChange: PropTypes.func,
      // from formRow()
      ineditable: PropTypes.bool,
      rowProps: rowPropTypes,
    };

    static defaultProps = {
      asideAllLabel: 'All',
      asideNoneLabel: '(Unset)',
      asideSeparator: ', ',
      disabled: false,
      renderRowValueLabel: undefined,
      // <SelectList> props
      multiple: SelectList.defaultProps.multiple,
      value: SelectList.defaultProps.value,
      defaultValue: SelectList.defaultProps.defaultValue,
      onChange: () => {},
      // from formRow()
      ineditable: false,
      rowProps: {},
    };

    state = {
      isPopoverOpen: false,
      valueLabelMap: getValueToLabelAvatarMap(this.props.children),
      cachedValue: this.getInitialValue(),
    };

    // eslint-disable-next-line react/no-deprecated
    componentWillReceiveProps(nextProps) {
      this.setState({
        valueLabelMap: getValueToLabelAvatarMap(nextProps.children),
      });

      warning(
        this.getIsControlled(this.props) === this.getIsControlled(nextProps),
        '<SelectRow> should not switch from controlled to uncontrolled (or vice versa).'
      );

      if (this.getIsControlled(nextProps)) {
        this.setState({ cachedValue: nextProps.value });
      } else if (this.props.multiple !== nextProps.multiple) {
        warning(false, '<SelectRow>: you should not change `multiple` prop while it is uncontrolled. Its value will be reset now.');
        this.setState({ cachedValue: (nextProps.multiple) ? [] : null });
      }
    }

    getInitialValue() {
      const { value, defaultValue, multiple } = this.props;

      if (value !== undefined) {
        return value;
      }

      if (multiple && defaultValue === undefined) {
        return [];
      }

      return defaultValue;
    }

    getIsControlled(fromProps = this.props) {
      return fromProps.value !== undefined;
    }

    getCacheValueArray = () => {
      const { multiple } = this.props;
      const { cachedValue } = this.state;
      return (multiple) ? cachedValue : [cachedValue].filter(val => val !== undefined);
    }

    handleButtonClick = () => {
      this.setState({ isPopoverOpen: true });
    }

    handlePopoverClose = () => {
      this.setState({ isPopoverOpen: false });
    }

    handleSelectChange = (nextValue) => {
      if (!this.getIsControlled()) {
        this.setState({ cachedValue: nextValue });
      }
      this.props.onChange(nextValue);

      if (!this.props.multiple) {
        this.handlePopoverClose();
      }
    }

    renderPopover(selectListProps) {
      return (
        <Popover
          anchor={this.anchorNode}
          className={BEM.popover.toString()}
          closable={CLOSABLE_CONFIG}
          onClose={this.handlePopoverClose}
        >
          <SelectList
            value={this.state.cachedValue}
            onChange={this.handleSelectChange}
            {...selectListProps}
          />
        </Popover>
      );
    }

    renderRowValueLabel() {
      const {
        multiple,
        asideAllLabel,
        asideNoneLabel,
        asideSeparator,
        renderRowValueLabel,
      } = this.props;
      const { cachedValue, valueLabelMap } = this.state;

      if (typeof renderRowValueLabel === 'function') {
        return renderRowValueLabel({ values: cachedValue, valueLabelMap });
      }

      const isSingleEmptyValue = cachedValue === undefined || cachedValue === '';
      const isMultipleEmptyValue = multiple && cachedValue.length === 0;

      if (isSingleEmptyValue || isMultipleEmptyValue) {
        return {asideNoneLabel};
      }

      if (multiple) {
        // Can turn off 'All' display by passing `false`.
        if (asideAllLabel && cachedValue.length === valueLabelMap.size) {
          return asideAllLabel;
        }
      }

      return this.getCacheValueArray()
        .map((value) => {
          const valueMap = valueLabelMap.get(value) || {};
          return valueMap.label;
        })
        .filter(label => Boolean(label))
        .join(asideSeparator);
    }

    renderAvatar() {
      const { valueLabelMap } = this.state;
      return this.getCacheValueArray()
        .map((value) => {
          const valueMap = valueLabelMap.get(value) || {};
          return (
            <React.Fragment key={value}>
              {valueMap.avatar}
            </React.Fragment>
          );
        });
    }

    render() {
      const {
        label,
        asideAllLabel,
        asideNoneLabel,
        asideSeparator,
        disabled,
        // <ListRow> props (intercepted from it)
        // multiple,
        value,
        defaultValue,
        onChange,
        // from formRow()
        ineditable,
        rowProps,
        // React props
        className,
        ...selectListProps
      } = this.props;
      const { isPopoverOpen } = this.state;

      const wrapperClassName = classNames(
        COMPONENT_NAME,
        className,
      );

      const Content = ineditable ? TextLabel : Button;
      const contentProps = ineditable ? {} : {
        onClick: this.handleButtonClick,
      };

      return (
        <ListRow className={wrapperClassName} {...rowProps}>
          {this.renderAvatar()}
          <Content minified={false} disabled={disabled} {...contentProps}>
            <Text
              verticalOrder="reverse"
              bold={!ineditable}
              basic={this.renderRowValueLabel()}
              aside={label}
            />

             { this.anchorNode = ref; }}>
              <Icon type="dropdown" />

            {isPopoverOpen && this.renderPopover(selectListProps)}
          </Content>
        </ListRow>
      );
    }
}

export { SelectRow as PureSelectRow };
export default formRow()(SelectRow);
<|endoftext|>


<commit_before>// Vogue - Client
// Copyright (c) 2011 Andrew Davey (andrew@equin.co.uk)
(function () {
  var script,
      hop = Object.prototype.hasOwnProperty,
      head = document.getElementsByTagName("head")[0];

  function vogue() {
    var stylesheets,
        socket = io.connect(script.rootUrl);

    /**
     * Watch for all available stylesheets.
     */
    function watchAllStylesheets() {
      var href;
      for (href in stylesheets) {
        if (hop.call(stylesheets, href)) {
          socket.emit("watch", {
            href: href
          });
        }
      }
    }

    /**
     * Reload a stylesheet.
     *
     * @param {String} href The URL of the stylesheet to be reloaded.
     */
    function reloadStylesheet(href) {
      var newHref = stylesheets[href].href +
            (href.indexOf("?") >= 0 ? "&" : "?") +
            "_vogue_nocache=" + (new Date).getTime(),
          stylesheet;
      // Check if the appropriate DOM Node is there.
      if (!stylesheets[href].setAttribute) {
        // Create the link.
        stylesheet = document.createElement("link");
        stylesheet.setAttribute("rel", "stylesheet");
        stylesheet.setAttribute("href", newHref);
        head.appendChild(stylesheet);

        // Update the reference to the newly created link.
        stylesheets[href] = stylesheet;
      } else {
        // Update the href to the new URL.
        stylesheets[href].href = newHref;
      }
    }

    /**
     * Handle messages from socket.io, and load the appropriate stylesheet.
     *
     * @param message Socket.io message object.
     * @param message.href The url of the stylesheet to be loaded.
     */
    function handleMessage(message) {
      reloadStylesheet(message.href);
    }

    /**
     * Fetch all the local stylesheets from the page.
     *
     * @returns {Object} The list of local stylesheets keyed by their base URL.
     */
    function getLocalStylesheets() {

      /**
       * Checks if the stylesheet is local.
       *
       * @param {Object} link The link to check for.
       * @returns {Boolean}
       */
      function isLocalStylesheet(link) {
        var href, i, isExternal = true;
        if (link.getAttribute("rel") !== "stylesheet") {
          return false;
        }
        href = link.href;

        for (i = 0; i < script.bases.length; i += 1) {
          if (href.indexOf(script.bases[i]) > -1) {
            isExternal = false;
            break;
          }
        }

        return !(isExternal && href.match(/^https?:/));
      }

      /**
       * Checks if the stylesheet's media attribute is 'print'
       *
       * @param (Object) link The stylesheet element to check.
       * @returns (Boolean)
       */
      function isPrintStylesheet(link) {
        return link.getAttribute("media") === "print";
      }

      /**
       * Get the link's base URL.
       *
       * @param {String} href The URL to check.
       * @returns {String|Boolean} The base URL, or false if no matches found.
       */
      function getBase(href) {
        var base, j;
        for (j = 0; j < script.bases.length; j += 1) {
          base = script.bases[j];
          if (href.indexOf(base) > -1) {
            return href.substr(base.length);
          }
        }
        return false;
      }

      function getProperty(property) {
        return this[property];
      }

      var stylesheets = {},
          reImport = /@import\s+url\(["']?([^"'\)]+)["']?\)/g,
          links = document.getElementsByTagName("link"),
          link, href, matches, content, i, m;

      // Go through all the links in the page, looking for stylesheets.
      for (i = 0, m = links.length; i < m; i += 1) {
        link = links[i];
        if (isPrintStylesheet(link)) continue;
        if (!isLocalStylesheet(link)) continue;
        // Link is local, get the base URL.
        href = getBase(link.href);
        if (href !== false) {
          stylesheets[href] = link;
        }
      }

      // Go through all the style tags, looking for @import tags.
      links = document.getElementsByTagName("style");
      for (i = 0, m = links.length; i < m; i += 1) {
        if (isPrintStylesheet(links[i])) continue;
        content = links[i].text || links[i].textContent;
        while ((matches = reImport.exec(content))) {
          link = {
            rel: "stylesheet",
            href: matches[1],
            getAttribute: getProperty
          };
          if (isLocalStylesheet(link)) {
            // Link is local, get the base URL.
            href = getBase(link.href);
            if (href !== false) {
              stylesheets[href] = link;
            }
          }
        }
      }
      return stylesheets;
    }

    stylesheets = getLocalStylesheets();
    socket.on("connect", watchAllStylesheets);
    socket.on("update", handleMessage);
  }

  /**
   * Load a script into the page, and call a callback when it is loaded.
   *
   * @param {String} src The URL of the script to be loaded.
   * @param {Function} loadedCallback The function to be called when the script is loaded.
   */
  function loadScript(src, loadedCallback) {
    var script = document.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("src", src);

    // Call the callback when the script is loaded.
    script.onload = loadedCallback;
    script.onreadystatechange = function () {
      if (this.readyState === "complete" || this.readyState === "loaded") {
        loadedCallback();
      }
    };

    head.appendChild(script);
  }

  /**
   * Load scripts into the page, and call a callback when they are loaded.
   *
   * @param {Array} scripts The scripts to be loaded.
   * @param {Function} loadedCallback The function to be called when all the scripts have loaded.
   */
  function loadScripts(scripts, loadedCallback) {
    var srcs = [], property, count, i, src,
        countDown = function () {
          count -= 1;
          if (!count) {
            loadedCallback();
          }
        };

    for (property in scripts) {
      if (!(property in window)) {
        srcs.push(scripts[property]);
      }
    }

    count = srcs.length;
    if (!count) {
      loadedCallback();
    }

    for (i = 0; i < srcs.length; i += 1) {
      src = srcs[i];
      loadScript(src, countDown);
    }
  }

  /**
   * Fetches the info for the vogue client.
   */
  function getScriptInfo() {
    var bases = [ document.location.protocol + "//" + document.location.host ],
        scripts, src, rootUrl, baseMatch;
    if (typeof window.__vogue__ === "undefined") {
      scripts = document.getElementsByTagName("script");
      for (var i=0; i < scripts.length; i++) {
        src = scripts[i].getAttribute("src");
        if (src.slice(-15) == 'vogue-client.js') break;
      }
      rootUrl = src.match(/^https?\:\/\/(.*?)\//)[0];
      // There is an optional base argument, that can be used.
      baseMatch = src.match(/\bbase=(.*)(&|$)/);

      if (baseMatch) {
        bases = bases.concat(baseMatch[1].split(","));
      }
      return {
        rootUrl: rootUrl,
        bases: bases
      };
    } else {
      window.__vogue__.bases = bases;
      return window.__vogue__;
    }
  }

  /**
   * Fetches the port from the URL.
   *
   * @param {String} url URL to get the port from
   * @returns {Number} The port number, or 80 if no port number found or is invalid.
   */
  function getPort(url) {
    // URL may contain the port number after the second colon.
    // http://domain:1234/
    var index = url.indexOf(":", 6); // skipping 6 characters to ignore first colon
    return index < 0 ? 80 : parseInt(url.substr(index + 1), 10);
  }

  script = getScriptInfo();
  loadScripts({
    io: script.rootUrl + "socket.io/socket.io.js"
  }, vogue);
}());
<commit_msg>Triple equals OCD.<commit_after>// Vogue - Client
// Copyright (c) 2011 Andrew Davey (andrew@equin.co.uk)
(function () {
  var script,
      hop = Object.prototype.hasOwnProperty,
      head = document.getElementsByTagName("head")[0];

  function vogue() {
    var stylesheets,
        socket = io.connect(script.rootUrl);

    /**
     * Watch for all available stylesheets.
     */
    function watchAllStylesheets() {
      var href;
      for (href in stylesheets) {
        if (hop.call(stylesheets, href)) {
          socket.emit("watch", {
            href: href
          });
        }
      }
    }

    /**
     * Reload a stylesheet.
     *
     * @param {String} href The URL of the stylesheet to be reloaded.
     */
    function reloadStylesheet(href) {
      var newHref = stylesheets[href].href +
            (href.indexOf("?") >= 0 ? "&" : "?") +
            "_vogue_nocache=" + (new Date).getTime(),
          stylesheet;
      // Check if the appropriate DOM Node is there.
      if (!stylesheets[href].setAttribute) {
        // Create the link.
        stylesheet = document.createElement("link");
        stylesheet.setAttribute("rel", "stylesheet");
        stylesheet.setAttribute("href", newHref);
        head.appendChild(stylesheet);

        // Update the reference to the newly created link.
        stylesheets[href] = stylesheet;
      } else {
        // Update the href to the new URL.
        stylesheets[href].href = newHref;
      }
    }

    /**
     * Handle messages from socket.io, and load the appropriate stylesheet.
     *
     * @param message Socket.io message object.
     * @param message.href The url of the stylesheet to be loaded.
     */
    function handleMessage(message) {
      reloadStylesheet(message.href);
    }

    /**
     * Fetch all the local stylesheets from the page.
     *
     * @returns {Object} The list of local stylesheets keyed by their base URL.
     */
    function getLocalStylesheets() {

      /**
       * Checks if the stylesheet is local.
       *
       * @param {Object} link The link to check for.
       * @returns {Boolean}
       */
      function isLocalStylesheet(link) {
        var href, i, isExternal = true;
        if (link.getAttribute("rel") !== "stylesheet") {
          return false;
        }
        href = link.href;

        for (i = 0; i < script.bases.length; i += 1) {
          if (href.indexOf(script.bases[i]) > -1) {
            isExternal = false;
            break;
          }
        }

        return !(isExternal && href.match(/^https?:/));
      }

      /**
       * Checks if the stylesheet's media attribute is 'print'
       *
       * @param (Object) link The stylesheet element to check.
       * @returns (Boolean)
       */
      function isPrintStylesheet(link) {
        return link.getAttribute("media") === "print";
      }

      /**
       * Get the link's base URL.
       *
       * @param {String} href The URL to check.
       * @returns {String|Boolean} The base URL, or false if no matches found.
       */
      function getBase(href) {
        var base, j;
        for (j = 0; j < script.bases.length; j += 1) {
          base = script.bases[j];
          if (href.indexOf(base) > -1) {
            return href.substr(base.length);
          }
        }
        return false;
      }

      function getProperty(property) {
        return this[property];
      }

      var stylesheets = {},
          reImport = /@import\s+url\(["']?([^"'\)]+)["']?\)/g,
          links = document.getElementsByTagName("link"),
          link, href, matches, content, i, m;

      // Go through all the links in the page, looking for stylesheets.
      for (i = 0, m = links.length; i < m; i += 1) {
        link = links[i];
        if (isPrintStylesheet(link)) continue;
        if (!isLocalStylesheet(link)) continue;
        // Link is local, get the base URL.
        href = getBase(link.href);
        if (href !== false) {
          stylesheets[href] = link;
        }
      }

      // Go through all the style tags, looking for @import tags.
      links = document.getElementsByTagName("style");
      for (i = 0, m = links.length; i < m; i += 1) {
        if (isPrintStylesheet(links[i])) continue;
        content = links[i].text || links[i].textContent;
        while ((matches = reImport.exec(content))) {
          link = {
            rel: "stylesheet",
            href: matches[1],
            getAttribute: getProperty
          };
          if (isLocalStylesheet(link)) {
            // Link is local, get the base URL.
            href = getBase(link.href);
            if (href !== false) {
              stylesheets[href] = link;
            }
          }
        }
      }
      return stylesheets;
    }

    stylesheets = getLocalStylesheets();
    socket.on("connect", watchAllStylesheets);
    socket.on("update", handleMessage);
  }

  /**
   * Load a script into the page, and call a callback when it is loaded.
   *
   * @param {String} src The URL of the script to be loaded.
   * @param {Function} loadedCallback The function to be called when the script is loaded.
   */
  function loadScript(src, loadedCallback) {
    var script = document.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("src", src);

    // Call the callback when the script is loaded.
    script.onload = loadedCallback;
    script.onreadystatechange = function () {
      if (this.readyState === "complete" || this.readyState === "loaded") {
        loadedCallback();
      }
    };

    head.appendChild(script);
  }

  /**
   * Load scripts into the page, and call a callback when they are loaded.
   *
   * @param {Array} scripts The scripts to be loaded.
   * @param {Function} loadedCallback The function to be called when all the scripts have loaded.
   */
  function loadScripts(scripts, loadedCallback) {
    var srcs = [], property, count, i, src,
        countDown = function () {
          count -= 1;
          if (!count) {
            loadedCallback();
          }
        };

    for (property in scripts) {
      if (!(property in window)) {
        srcs.push(scripts[property]);
      }
    }

    count = srcs.length;
    if (!count) {
      loadedCallback();
    }

    for (i = 0; i < srcs.length; i += 1) {
      src = srcs[i];
      loadScript(src, countDown);
    }
  }

  /**
   * Fetches the info for the vogue client.
   */
  function getScriptInfo() {
    var bases = [ document.location.protocol + "//" + document.location.host ],
        scripts, src, rootUrl, baseMatch;
    if (typeof window.__vogue__ === "undefined") {
      scripts = document.getElementsByTagName("script");
      for (var i=0; i < scripts.length; i++) {
        src = scripts[i].getAttribute("src");
        if (src.slice(-15) === 'vogue-client.js') break;
      }
      rootUrl = src.match(/^https?\:\/\/(.*?)\//)[0];
      // There is an optional base argument, that can be used.
      baseMatch = src.match(/\bbase=(.*)(&|$)/);

      if (baseMatch) {
        bases = bases.concat(baseMatch[1].split(","));
      }
      return {
        rootUrl: rootUrl,
        bases: bases
      };
    } else {
      window.__vogue__.bases = bases;
      return window.__vogue__;
    }
  }

  /**
   * Fetches the port from the URL.
   *
   * @param {String} url URL to get the port from
   * @returns {Number} The port number, or 80 if no port number found or is invalid.
   */
  function getPort(url) {
    // URL may contain the port number after the second colon.
    // http://domain:1234/
    var index = url.indexOf(":", 6); // skipping 6 characters to ignore first colon
    return index < 0 ? 80 : parseInt(url.substr(index + 1), 10);
  }

  script = getScriptInfo();
  loadScripts({
    io: script.rootUrl + "socket.io/socket.io.js"
  }, vogue);
}());
<|endoftext|>


<commit_before>var app = require("application");
var context = app.android.context;
var TextToSpeech = android.speech.tts.TextToSpeech;
var initialised = false;
var tts;

var text_to_speech = {
	speak : function(text, queue, pitch, speakRate, volume){
		if(!isString(text)) {
			console.log("no text was provided");
			return;
		}

		if(text.length > 4000) {
			console.log("text cannot be greater than 4000 characters");
			return;
		}

		if(!tts || !initialised) {
			tts = new TextToSpeech(context, new TextToSpeech.OnInitListener({
				onInit : function(status) {
					if(status === TextToSpeech.SUCCESS) {
						initialised = true;
						speakText(text, queue, pitch, speakRate, volume);
					}
				}
			}));
		} else {
			speakText(text, queue, pitch, speakRate, volume);
		}
	}
};

var isString = function (elem) {
	return Object.prototype.toString.call(elem).slice(8, -1) === 'String';
};

var isBoolean = function (elem) {
	return Object.prototype.toString.call(elem).slice(8, -1) === 'Boolean';
};

var isNumber = function (elem) {
	return Object.prototype.toString.call(elem).slice(8, -1) === 'Number';
};

var speakText = function(text, queue, pitch, speakRate, volume) {
	if(!isBoolean(queue)) {
		queue = false;
	}

	if(!queue && tts.isSpeaking()) {
		tts.stop();
	}

	// no range of valid values for Android so just cover default value if none provided
	if(!isNumber(pitch)) {
		pitch = 1.0;
	}

	// no range of valid values for Android so just cover default value if none provided
	if(!isNumber(speakRate)) {
		speakRate = 1.0;
	}

	// valid values are 0.0 to 1.0
	if(!isNumber(volume) || volume > 1.0) {
		volume = 1.0;
	} else if(volume < 0.0) {
		volume = 0.0;
	}

	var hashMap = new java.util.HashMap();
	hashMap.put(TextToSpeech.Engine.KEY_PARAM_VOLUME, volume.toString());

	tts.setPitch(pitch);
	tts.setSpeechRate(speakRate);

	var queueMode = queue ? TextToSpeech.QUEUE_ADD : TextToSpeech.QUEUE_FLUSH;

	tts.speak(text, queueMode, hashMap);
};

module.exports = text_to_speech;<commit_msg>Syntax fix to get Android running<commit_after>var app = require("application");
var context = app.android.context;
var TextToSpeech = android.speech.tts.TextToSpeech;
var initialised = false;
var tts;

var text_to_speech = {
	speak : function(text, queue, pitch, speakRate, volume){
		if(!isString(text)) {
			console.log("no text was provided");
			return;
		}

		if(text.length > 4000) {
			console.log("text cannot be greater than 4000 characters");
			return;
		}

		if(!tts || !initialised) {
			tts = new TextToSpeech(context, new TextToSpeech.OnInitListener({
				onInit : function(status) {
					if(status === TextToSpeech.SUCCESS) {
						initialised = true;
						speakText(text, queue, pitch, speakRate, volume);
					}
				}
			}));
		} else {
			speakText(text, queue, pitch, speakRate, volume);
		}
	}
};

var isString = function (elem) {
	return Object.prototype.toString.call(elem).slice(8, -1) === 'String';
};

var isBoolean = function (elem) {
	return Object.prototype.toString.call(elem).slice(8, -1) === 'Boolean';
};

var isNumber = function (elem) {
	return Object.prototype.toString.call(elem).slice(8, -1) === 'Number';
};

var speakText = function(text, queue, pitch, speakRate, volume) {
	if(!isBoolean(queue)) {
		queue = false;
	}

	if(!queue && tts.isSpeaking()) {
		tts.stop();
	}

	// no range of valid values for Android so just cover default value if none provided
	if(!isNumber(pitch)) {
		pitch = 1.0;
	}

	// no range of valid values for Android so just cover default value if none provided
	if(!isNumber(speakRate)) {
		speakRate = 1.0;
	}

	// valid values are 0.0 to 1.0
	if(!isNumber(volume) || volume > 1.0) {
		volume = 1.0;
	} else if(volume < 0.0) {
		volume = 0.0;
	}

	var hashMap = new java.util.HashMap();
	hashMap.put(tts.Engine.KEY_PARAM_VOLUME, volume.toString());

	tts.setPitch(pitch);
	tts.setSpeechRate(speakRate);

	var queueMode = queue ? TextToSpeech.QUEUE_ADD : TextToSpeech.QUEUE_FLUSH;

	tts.speak(text, queueMode, hashMap);
};

module.exports = text_to_speech;<|endoftext|>


<commit_before>$(function () {
  $('.markdown').each(function (index) {
    var input = $(this);
    var simplemde = new SimpleMDE({
      element: input[0],
      autosave: {
        enabled: true,
        uniqueId: window.location.href
      },
      renderingConfig: {
        codeSyntaxHighlighting: true
      },
      spellChecker: false
    });
    input.data('editor', simplemde);

    inlineAttachment.editors.codemirror4.attach(simplemde.codemirror, {
      uploadUrl: '/api/attachedfiles/',
      extraHeaders: {
        Accept: 'application/json'
      }
    });
  });

  $('.markdown-text').each(function (index) {
    var elt = $(this);

    md = elt.text();
    elt.html(marked(md));
    elt.removeClass('hide');
  });
});
<commit_msg>fixes #329<commit_after>$(function () {
  $('.markdown').each(function (index) {
    var input = $(this);
    var simplemde = new SimpleMDE({
      element: input[0],
      autosave: {
        enabled: true,
        uniqueId: window.location.href
      },
      renderingConfig: {
        codeSyntaxHighlighting: true
      },
      spellChecker: false
    });
    input.data('editor', simplemde);

    inlineAttachment.editors.codemirror4.attach(simplemde.codemirror, {
      uploadUrl: '/api/attachedfiles/',
      extraHeaders: {
        Accept: 'application/json'
      }
    });
  });

  $('.markdown-text').each(function (index) {
    var elt = $(this);

    md = elt.text();
    elt.html(marked(md, {
      sanitize: true
    }));
    elt.removeClass('hide');
  });
});
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow
import * as React from 'react';
import { Provider } from 'react-redux';
import { render, fireEvent } from 'react-testing-library';
// This module is mocked.
import copy from 'copy-to-clipboard';

import ProfileCallTreeView from '../../components/calltree/ProfileCallTreeView';
import CallNodeContextMenu from '../../components/shared/CallNodeContextMenu';
import { processProfile } from '../../profile-logic/process-profile';
import { ensureExists } from '../../utils/flow';

import mockCanvasContext from '../fixtures/mocks/canvas-context';
import { storeWithProfile } from '../fixtures/stores';
import { getBoundingBox } from '../fixtures/utils';
import { getProfileFromTextSamples } from '../fixtures/profiles/processed-profile';
import { createGeckoProfile } from '../fixtures/profiles/gecko-profile';

import {
  getEmptyThread,
  getEmptyProfile,
} from '../../profile-logic/data-structures';
import {
  changeCallTreeSearchString,
  changeImplementationFilter,
  changeInvertCallstack,
  commitRange,
  addTransformToStack,
} from '../../actions/profile-view';

import type { Profile } from '../../types/profile';

beforeEach(() => {
  // Mock out the 2d canvas for the loupe view.
  jest
    .spyOn(HTMLCanvasElement.prototype, 'getContext')
    .mockImplementation(() => mockCanvasContext());
  // This makes the bounding box large enough so that we don't trigger
  // VirtualList's virtualization. We assert this above.
  jest
    .spyOn(HTMLElement.prototype, 'getBoundingClientRect')
    .mockImplementation(() => getBoundingBox(1000, 2000));
});

describe('calltree/ProfileCallTreeView', function() {
  function setup(profile?: Profile) {
    if (!profile) {
      profile = getProfileFromTextSamples(`
        A  A  A
        B  B  B
        C  C  H
        D  F  I
        E  E
      `).profile;
    }

    const store = storeWithProfile(profile);
    const renderResult = render(
      <Provider store={store}>
        <>
          <CallNodeContextMenu />
          <ProfileCallTreeView hideThreadActivityGraph={true} />
        </>
      </Provider>
    );
    const { container, getByText } = renderResult;

    const getRowElement = functionName =>
      ensureExists(
        getByText(functionName).closest('.treeViewRow'),
        `Couldn't find the row for node ${functionName}.`
      );
    const getContextMenu = () =>
      ensureExists(
        container.querySelector('.react-contextmenu'),
        `Couldn't find the context menu.`
      );

    // Because different components listen to different events, we trigger all
    // the right events as part of click and rightClick actions.
    const click = (element: HTMLElement) => {
      fireEvent.mouseDown(element);
      fireEvent.mouseUp(element);
      fireEvent.click(element);
    };

    const rightClick = (element: HTMLElement) => {
      fireEvent.mouseDown(element, { button: 2, buttons: 2 });
      fireEvent.mouseUp(element, { button: 2, buttons: 2 });
      fireEvent.contextMenu(element);
    };

    return {
      ...store,
      ...renderResult,
      getRowElement,
      getContextMenu,
      click,
      rightClick,
    };
  }

  it('renders an unfiltered call tree', () => {
    const { container } = setup();
    expect(container.firstChild).toMatchSnapshot();
  });

  it('renders an inverted call tree', () => {
    const profileForInvertedTree = getProfileFromTextSamples(`
      A  A  A
      B  B  B
      C  X  C
      D  Y  X
      E  Z  Y
            Z
    `).profile;

    // Note: we're not using the setup function because we want to change the
    // invertCallstack flag before rendering, so that we get the initial
    // expanded selection in this case too.
    const store = storeWithProfile(profileForInvertedTree);
    store.dispatch(changeInvertCallstack(true));

    const { container } = render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    );

    expect(container.firstChild).toMatchSnapshot();
  });

  it('renders call tree with some search strings', () => {
    const { container, dispatch } = setup();

    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString('C'));
    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString('C,'));
    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString('C, F'));
    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString('C, F,E'));
    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString(' C , E   '));
    expect(container.firstChild).toMatchSnapshot();
  });

  it('computes a width for a call tree of a really deep stack', () => {
    const { profile } = getProfileFromTextSamples(
      Array(113)
        .fill('name')
        .join('\n')
    );

    const { container } = setup(profile);
    const treeBody = ensureExists(
      container.querySelector('.treeViewBodyInner1')
    );
    const treeBodyWidth = parseInt(treeBody.style.width);
    expect(treeBodyWidth).toBeGreaterThan(3000);
  });

  it('selects a node when left clicking', () => {
    const { getByText, getRowElement, click } = setup();

    click(getByText('A'));
    expect(getRowElement('A')).toHaveClass('isSelected');

    click(getByText('B'));
    expect(getRowElement('A')).not.toHaveClass('isSelected');
    expect(getRowElement('B')).toHaveClass('isSelected');
  });

  it('displays a context menu when right clicking', () => {
    // Fake timers are needed when dealing with the context menu.
    jest.useFakeTimers();

    const { getContextMenu, getByText, getRowElement, rightClick } = setup();

    function checkMenuIsDisplayedForNode(str) {
      expect(getContextMenu()).toHaveClass('react-contextmenu--visible');

      // Note that selecting a menu item will close the menu.
      fireEvent.click(getByText('Copy function name'));
      expect(copy).toHaveBeenLastCalledWith(str);
    }

    rightClick(getByText('A'));
    expect(getRowElement('A')).toHaveClass('isRightClicked');
    checkMenuIsDisplayedForNode('A');

    // Wait that all timers are done before trying again.
    jest.runAllTimers();

    // Now try it again by right clicking 2 nodes in sequence.
    rightClick(getByText('A'));
    rightClick(getByText('C'));
    expect(getRowElement('C')).toHaveClass('isRightClicked');
    checkMenuIsDisplayedForNode('C');

    // Wait that all timers are done before trying again.
    jest.runAllTimers();

    // And now let's do it again, but this time waiting for timers before
    // clicking, because the timer can impact the menu being displayed.
    rightClick(getByText('A'));
    rightClick(getByText('C'));
    jest.runAllTimers();
    expect(getRowElement('C')).toHaveClass('isRightClicked');
    checkMenuIsDisplayedForNode('C');
  });

  it('hides the context menu by left clicking somewhere else', () => {
    // Fake timers are needed when dealing with the context menu.
    jest.useFakeTimers();

    const { getContextMenu, getByText, click, rightClick, container } = setup();
    rightClick(getByText('A'));
    expect(getContextMenu()).toHaveClass('react-contextmenu--visible');

    click(getByText('C'));
    expect(getContextMenu()).not.toHaveClass('react-contextmenu--visible');

    jest.runAllTimers();
    expect(container.querySelector('.react-contextmenu')).toBeFalsy();
  });

  it('highlights the row properly when rightclicking a selected row', () => {
    // Fake timers are needed when dealing with the context menu.
    jest.useFakeTimers();

    const { getByText, getRowElement, click, rightClick } = setup();

    click(getByText('A'));
    expect(getRowElement('A')).toHaveClass('isSelected');
    expect(getRowElement('A')).not.toHaveClass('isRightClicked');

    rightClick(getByText('A'));
    // Both classes will be set, but our CSS styles `rightClicked` only when
    // `selected` is not present either.
    expect(getRowElement('A')).toHaveClass('isSelected');
    expect(getRowElement('A')).toHaveClass('isRightClicked');
  });

  it('highlights the row properly when selecting a rightclicked row', () => {
    // Fake timers are needed when dealing with the context menu.
    jest.useFakeTimers();

    const { getByText, getRowElement, click, rightClick } = setup();

    rightClick(getByText('A'));
    expect(getRowElement('A')).not.toHaveClass('isSelected');
    expect(getRowElement('A')).toHaveClass('isRightClicked');

    // When the node is highlighted from a right click, left clicking it will
    // chnage its highlight style.
    click(getByText('A'));
    expect(getRowElement('A')).toHaveClass('isSelected');
    expect(getRowElement('A')).toHaveClass('isRightClicked');

    // After a timeout, the menu publicizes that it's hidden and the right click
    // information is removed.
    jest.runAllTimers();
    expect(getRowElement('A')).toHaveClass('isSelected');
    expect(getRowElement('A')).not.toHaveClass('isRightClicked');
  });

  it('selects the heaviest stack if it is not idle', () => {
    const { profile } = getProfileFromTextSamples(`
      A  A  A  A  A
      B  C  C  C  D
      E           E
    `);
    const { getRowElement } = setup(profile);
    expect(getRowElement('C')).toHaveClass('isSelected');
  });

  it('does not select the heaviest stack if it is idle', () => {
    const { profile } = getProfileFromTextSamples(`
      A  A            A            A            A
      B  C[cat:Idle]  C[cat:Idle]  C[cat:Idle]  D
      E                                         E
    `);
    const { container } = setup(profile);
    expect(container.querySelector('.treeViewRow.isSelected')).toBeFalsy();
  });
});

describe('calltree/ProfileCallTreeView EmptyReasons', function() {
  const { profile } = getProfileFromTextSamples(`
    A  A  A
    B  B  B
    C  C  H
    D  F  I
    E  E
  `);
  profile.threads[0].name = 'Thread with samples';

  function renderWithStore(store) {
    return render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    ).container.firstChild;
  }

  it('shows a reason for a call tree with no samples', function() {
    const profile = getEmptyProfile();
    const thread = getEmptyThread();
    thread.name = 'Empty Thread';
    profile.threads.push(thread);

    const store = storeWithProfile(profile);
    expect(renderWithStore(store)).toMatchSnapshot();
  });

  it('shows reasons for being out of range of a threads samples', function() {
    const store = storeWithProfile(profile);
    store.dispatch(commitRange(5, 10));
    expect(renderWithStore(store)).toMatchSnapshot();
  });

  it('shows reasons for when samples are completely filtered out', function() {
    const store = storeWithProfile(profile);
    store.dispatch(changeImplementationFilter('js'));
    expect(renderWithStore(store)).toMatchSnapshot();
  });
});

describe('calltree/ProfileCallTreeView navigation keys', () => {
  function setup(profileString: string, expectedRowsLength: number) {
    const { profile } = getProfileFromTextSamples(profileString);
    const store = storeWithProfile(profile);
    const { container } = render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    );

    // Assert that we used a large enough bounding box to include all children.
    const renderedRows = container.querySelectorAll(
      '.treeViewRow.treeViewRowScrolledColumns'
    );
    expect(renderedRows.length).toBe(expectedRowsLength);

    return {
      // take either a key as a string, or a full event if we need more
      // information like modifier keys.
      simulateKey: (param: string | { key: string }) => {
        const treeViewBody = ensureExists(
          container.querySelector('div.treeViewBody'),
          `Couldn't find the tree view body with selector div.treeViewBody`
        );
        fireEvent.keyDown(
          treeViewBody,
          // There's a shortcoming in either Flow or the flow type for the
          // `keyDown` method. $FlowExpectError
          param.key ? param : { key: param }
        );
      },
      selectedText: () =>
        ensureExists(
          container.querySelector('.treeViewRowScrolledColumns.isSelected'),
          `Couldn't find the selected column with selector .treeViewRowScrolledColumns.isSelected`
        ).textContent,
    };
  }

  it('reacts properly to up/down navigation keys', () => {
    // This generates a profile where function "name<i + 1>" is present
    // <length - i> times, which means it will have a self time of <length - i>
    // ms. This is a good way to control the order we'll get in the call tree
    // view: function "name1" will be first, etc.
    const profileString = Array.from({ length: 100 }).reduce(
      (result, func, i, array) => {
        const funcName = `name${i + 1}  `;
        result += funcName.repeat(array.length - i);
        return result;
      },
      ''
    );

    const { simulateKey, selectedText } = setup(profileString, 100);

    expect(selectedText()).toBe('name1');
    simulateKey('ArrowDown');
    expect(selectedText()).toBe('name2');
    simulateKey('PageDown');
    expect(selectedText()).toBe('name17'); // 15 rows below
    simulateKey('End');
    expect(selectedText()).toBe('name100');
    simulateKey('ArrowUp');
    expect(selectedText()).toBe('name99');
    simulateKey('PageUp');
    expect(selectedText()).toBe('name84'); // 15 rows above
    simulateKey('Home');
    expect(selectedText()).toBe('name1');

    // These are MacOS shortcuts.
    simulateKey({ key: 'ArrowDown', metaKey: true });
    expect(selectedText()).toBe('name100');
    simulateKey({ key: 'ArrowUp', metaKey: true });
    expect(selectedText()).toBe('name1');
  });
});

describe('calltree/ProfileCallTreeView TransformNavigator', () => {
  it('renders with multiple transforms applied', () => {
    const {
      profile,
      funcNamesPerThread: [funcNames],
    } = getProfileFromTextSamples(`
      A  A  A
      B  B  B
      C  C  H
      D  F  I
      E  E
    `);
    profile.threads[0].name = 'Thread with samples';
    const store = storeWithProfile(profile);

    // Applying some transforms
    const A = funcNames.indexOf('A');
    const B = funcNames.indexOf('B');
    const C = funcNames.indexOf('C');
    store.dispatch(
      addTransformToStack(0, {
        type: 'focus-subtree',
        callNodePath: [A],
        implementation: 'combined',
        inverted: false,
      })
    );
    store.dispatch(
      addTransformToStack(0, {
        type: 'focus-subtree',
        callNodePath: [B],
        implementation: 'combined',
        inverted: false,
      })
    );
    store.dispatch(
      addTransformToStack(0, {
        type: 'focus-subtree',
        callNodePath: [C],
        implementation: 'combined',
        inverted: false,
      })
    );

    const { container } = render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    );
    expect(
      container.querySelector('.calltreeTransformNavigator')
    ).toMatchSnapshot();
  });
});

describe('ProfileCallTreeView/end-to-end', () => {
  it('can display a gecko profile without crashing', () => {
    const geckoProfile = createGeckoProfile();
    const processedProfile = processProfile(geckoProfile);
    const store = storeWithProfile(processedProfile);
    render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    );
  });
});
<commit_msg>Suppress the UnhandledPromiseRejectionWarning because of the indexedDB.open execution in ProfileCallTreeView tests (PR #2149)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow
import * as React from 'react';
import { Provider } from 'react-redux';
import { render, fireEvent } from 'react-testing-library';
// This module is mocked.
import copy from 'copy-to-clipboard';
import fakeIndexedDB from 'fake-indexeddb';

import ProfileCallTreeView from '../../components/calltree/ProfileCallTreeView';
import CallNodeContextMenu from '../../components/shared/CallNodeContextMenu';
import { processProfile } from '../../profile-logic/process-profile';
import { ensureExists } from '../../utils/flow';

import mockCanvasContext from '../fixtures/mocks/canvas-context';
import { storeWithProfile } from '../fixtures/stores';
import { getBoundingBox } from '../fixtures/utils';
import { getProfileFromTextSamples } from '../fixtures/profiles/processed-profile';
import { createGeckoProfile } from '../fixtures/profiles/gecko-profile';

import {
  getEmptyThread,
  getEmptyProfile,
} from '../../profile-logic/data-structures';
import {
  changeCallTreeSearchString,
  changeImplementationFilter,
  changeInvertCallstack,
  commitRange,
  addTransformToStack,
} from '../../actions/profile-view';

import type { Profile } from '../../types/profile';

beforeEach(() => {
  // Mock out the 2d canvas for the loupe view.
  jest
    .spyOn(HTMLCanvasElement.prototype, 'getContext')
    .mockImplementation(() => mockCanvasContext());
  // This makes the bounding box large enough so that we don't trigger
  // VirtualList's virtualization. We assert this above.
  jest
    .spyOn(HTMLElement.prototype, 'getBoundingClientRect')
    .mockImplementation(() => getBoundingBox(1000, 2000));

  window.indexedDB = fakeIndexedDB;
});

afterEach(() => {
  delete window.indexedDB;
});

describe('calltree/ProfileCallTreeView', function() {
  function setup(profile?: Profile) {
    if (!profile) {
      profile = getProfileFromTextSamples(`
        A  A  A
        B  B  B
        C  C  H
        D  F  I
        E  E
      `).profile;
    }

    const store = storeWithProfile(profile);
    const renderResult = render(
      <Provider store={store}>
        <>
          <CallNodeContextMenu />
          <ProfileCallTreeView hideThreadActivityGraph={true} />
        </>
      </Provider>
    );
    const { container, getByText } = renderResult;

    const getRowElement = functionName =>
      ensureExists(
        getByText(functionName).closest('.treeViewRow'),
        `Couldn't find the row for node ${functionName}.`
      );
    const getContextMenu = () =>
      ensureExists(
        container.querySelector('.react-contextmenu'),
        `Couldn't find the context menu.`
      );

    // Because different components listen to different events, we trigger all
    // the right events as part of click and rightClick actions.
    const click = (element: HTMLElement) => {
      fireEvent.mouseDown(element);
      fireEvent.mouseUp(element);
      fireEvent.click(element);
    };

    const rightClick = (element: HTMLElement) => {
      fireEvent.mouseDown(element, { button: 2, buttons: 2 });
      fireEvent.mouseUp(element, { button: 2, buttons: 2 });
      fireEvent.contextMenu(element);
    };

    return {
      ...store,
      ...renderResult,
      getRowElement,
      getContextMenu,
      click,
      rightClick,
    };
  }

  it('renders an unfiltered call tree', () => {
    const { container } = setup();
    expect(container.firstChild).toMatchSnapshot();
  });

  it('renders an inverted call tree', () => {
    const profileForInvertedTree = getProfileFromTextSamples(`
      A  A  A
      B  B  B
      C  X  C
      D  Y  X
      E  Z  Y
            Z
    `).profile;

    // Note: we're not using the setup function because we want to change the
    // invertCallstack flag before rendering, so that we get the initial
    // expanded selection in this case too.
    const store = storeWithProfile(profileForInvertedTree);
    store.dispatch(changeInvertCallstack(true));

    const { container } = render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    );

    expect(container.firstChild).toMatchSnapshot();
  });

  it('renders call tree with some search strings', () => {
    const { container, dispatch } = setup();

    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString('C'));
    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString('C,'));
    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString('C, F'));
    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString('C, F,E'));
    expect(container.firstChild).toMatchSnapshot();

    dispatch(changeCallTreeSearchString(' C , E   '));
    expect(container.firstChild).toMatchSnapshot();
  });

  it('computes a width for a call tree of a really deep stack', () => {
    const { profile } = getProfileFromTextSamples(
      Array(113)
        .fill('name')
        .join('\n')
    );

    const { container } = setup(profile);
    const treeBody = ensureExists(
      container.querySelector('.treeViewBodyInner1')
    );
    const treeBodyWidth = parseInt(treeBody.style.width);
    expect(treeBodyWidth).toBeGreaterThan(3000);
  });

  it('selects a node when left clicking', () => {
    const { getByText, getRowElement, click } = setup();

    click(getByText('A'));
    expect(getRowElement('A')).toHaveClass('isSelected');

    click(getByText('B'));
    expect(getRowElement('A')).not.toHaveClass('isSelected');
    expect(getRowElement('B')).toHaveClass('isSelected');
  });

  it('displays a context menu when right clicking', () => {
    // Fake timers are needed when dealing with the context menu.
    jest.useFakeTimers();

    const { getContextMenu, getByText, getRowElement, rightClick } = setup();

    function checkMenuIsDisplayedForNode(str) {
      expect(getContextMenu()).toHaveClass('react-contextmenu--visible');

      // Note that selecting a menu item will close the menu.
      fireEvent.click(getByText('Copy function name'));
      expect(copy).toHaveBeenLastCalledWith(str);
    }

    rightClick(getByText('A'));
    expect(getRowElement('A')).toHaveClass('isRightClicked');
    checkMenuIsDisplayedForNode('A');

    // Wait that all timers are done before trying again.
    jest.runAllTimers();

    // Now try it again by right clicking 2 nodes in sequence.
    rightClick(getByText('A'));
    rightClick(getByText('C'));
    expect(getRowElement('C')).toHaveClass('isRightClicked');
    checkMenuIsDisplayedForNode('C');

    // Wait that all timers are done before trying again.
    jest.runAllTimers();

    // And now let's do it again, but this time waiting for timers before
    // clicking, because the timer can impact the menu being displayed.
    rightClick(getByText('A'));
    rightClick(getByText('C'));
    jest.runAllTimers();
    expect(getRowElement('C')).toHaveClass('isRightClicked');
    checkMenuIsDisplayedForNode('C');
  });

  it('hides the context menu by left clicking somewhere else', () => {
    // Fake timers are needed when dealing with the context menu.
    jest.useFakeTimers();

    const { getContextMenu, getByText, click, rightClick, container } = setup();
    rightClick(getByText('A'));
    expect(getContextMenu()).toHaveClass('react-contextmenu--visible');

    click(getByText('C'));
    expect(getContextMenu()).not.toHaveClass('react-contextmenu--visible');

    jest.runAllTimers();
    expect(container.querySelector('.react-contextmenu')).toBeFalsy();
  });

  it('highlights the row properly when rightclicking a selected row', () => {
    // Fake timers are needed when dealing with the context menu.
    jest.useFakeTimers();

    const { getByText, getRowElement, click, rightClick } = setup();

    click(getByText('A'));
    expect(getRowElement('A')).toHaveClass('isSelected');
    expect(getRowElement('A')).not.toHaveClass('isRightClicked');

    rightClick(getByText('A'));
    // Both classes will be set, but our CSS styles `rightClicked` only when
    // `selected` is not present either.
    expect(getRowElement('A')).toHaveClass('isSelected');
    expect(getRowElement('A')).toHaveClass('isRightClicked');
  });

  it('highlights the row properly when selecting a rightclicked row', () => {
    // Fake timers are needed when dealing with the context menu.
    jest.useFakeTimers();

    const { getByText, getRowElement, click, rightClick } = setup();

    rightClick(getByText('A'));
    expect(getRowElement('A')).not.toHaveClass('isSelected');
    expect(getRowElement('A')).toHaveClass('isRightClicked');

    // When the node is highlighted from a right click, left clicking it will
    // chnage its highlight style.
    click(getByText('A'));
    expect(getRowElement('A')).toHaveClass('isSelected');
    expect(getRowElement('A')).toHaveClass('isRightClicked');

    // After a timeout, the menu publicizes that it's hidden and the right click
    // information is removed.
    jest.runAllTimers();
    expect(getRowElement('A')).toHaveClass('isSelected');
    expect(getRowElement('A')).not.toHaveClass('isRightClicked');
  });

  it('selects the heaviest stack if it is not idle', () => {
    const { profile } = getProfileFromTextSamples(`
      A  A  A  A  A
      B  C  C  C  D
      E           E
    `);
    const { getRowElement } = setup(profile);
    expect(getRowElement('C')).toHaveClass('isSelected');
  });

  it('does not select the heaviest stack if it is idle', () => {
    const { profile } = getProfileFromTextSamples(`
      A  A            A            A            A
      B  C[cat:Idle]  C[cat:Idle]  C[cat:Idle]  D
      E                                         E
    `);
    const { container } = setup(profile);
    expect(container.querySelector('.treeViewRow.isSelected')).toBeFalsy();
  });
});

describe('calltree/ProfileCallTreeView EmptyReasons', function() {
  const { profile } = getProfileFromTextSamples(`
    A  A  A
    B  B  B
    C  C  H
    D  F  I
    E  E
  `);
  profile.threads[0].name = 'Thread with samples';

  function renderWithStore(store) {
    return render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    ).container.firstChild;
  }

  it('shows a reason for a call tree with no samples', function() {
    const profile = getEmptyProfile();
    const thread = getEmptyThread();
    thread.name = 'Empty Thread';
    profile.threads.push(thread);

    const store = storeWithProfile(profile);
    expect(renderWithStore(store)).toMatchSnapshot();
  });

  it('shows reasons for being out of range of a threads samples', function() {
    const store = storeWithProfile(profile);
    store.dispatch(commitRange(5, 10));
    expect(renderWithStore(store)).toMatchSnapshot();
  });

  it('shows reasons for when samples are completely filtered out', function() {
    const store = storeWithProfile(profile);
    store.dispatch(changeImplementationFilter('js'));
    expect(renderWithStore(store)).toMatchSnapshot();
  });
});

describe('calltree/ProfileCallTreeView navigation keys', () => {
  function setup(profileString: string, expectedRowsLength: number) {
    const { profile } = getProfileFromTextSamples(profileString);
    const store = storeWithProfile(profile);
    const { container } = render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    );

    // Assert that we used a large enough bounding box to include all children.
    const renderedRows = container.querySelectorAll(
      '.treeViewRow.treeViewRowScrolledColumns'
    );
    expect(renderedRows.length).toBe(expectedRowsLength);

    return {
      // take either a key as a string, or a full event if we need more
      // information like modifier keys.
      simulateKey: (param: string | { key: string }) => {
        const treeViewBody = ensureExists(
          container.querySelector('div.treeViewBody'),
          `Couldn't find the tree view body with selector div.treeViewBody`
        );
        fireEvent.keyDown(
          treeViewBody,
          // There's a shortcoming in either Flow or the flow type for the
          // `keyDown` method. $FlowExpectError
          param.key ? param : { key: param }
        );
      },
      selectedText: () =>
        ensureExists(
          container.querySelector('.treeViewRowScrolledColumns.isSelected'),
          `Couldn't find the selected column with selector .treeViewRowScrolledColumns.isSelected`
        ).textContent,
    };
  }

  it('reacts properly to up/down navigation keys', () => {
    // This generates a profile where function "name<i + 1>" is present
    // <length - i> times, which means it will have a self time of <length - i>
    // ms. This is a good way to control the order we'll get in the call tree
    // view: function "name1" will be first, etc.
    const profileString = Array.from({ length: 100 }).reduce(
      (result, func, i, array) => {
        const funcName = `name${i + 1}  `;
        result += funcName.repeat(array.length - i);
        return result;
      },
      ''
    );

    const { simulateKey, selectedText } = setup(profileString, 100);

    expect(selectedText()).toBe('name1');
    simulateKey('ArrowDown');
    expect(selectedText()).toBe('name2');
    simulateKey('PageDown');
    expect(selectedText()).toBe('name17'); // 15 rows below
    simulateKey('End');
    expect(selectedText()).toBe('name100');
    simulateKey('ArrowUp');
    expect(selectedText()).toBe('name99');
    simulateKey('PageUp');
    expect(selectedText()).toBe('name84'); // 15 rows above
    simulateKey('Home');
    expect(selectedText()).toBe('name1');

    // These are MacOS shortcuts.
    simulateKey({ key: 'ArrowDown', metaKey: true });
    expect(selectedText()).toBe('name100');
    simulateKey({ key: 'ArrowUp', metaKey: true });
    expect(selectedText()).toBe('name1');
  });
});

describe('calltree/ProfileCallTreeView TransformNavigator', () => {
  it('renders with multiple transforms applied', () => {
    const {
      profile,
      funcNamesPerThread: [funcNames],
    } = getProfileFromTextSamples(`
      A  A  A
      B  B  B
      C  C  H
      D  F  I
      E  E
    `);
    profile.threads[0].name = 'Thread with samples';
    const store = storeWithProfile(profile);

    // Applying some transforms
    const A = funcNames.indexOf('A');
    const B = funcNames.indexOf('B');
    const C = funcNames.indexOf('C');
    store.dispatch(
      addTransformToStack(0, {
        type: 'focus-subtree',
        callNodePath: [A],
        implementation: 'combined',
        inverted: false,
      })
    );
    store.dispatch(
      addTransformToStack(0, {
        type: 'focus-subtree',
        callNodePath: [B],
        implementation: 'combined',
        inverted: false,
      })
    );
    store.dispatch(
      addTransformToStack(0, {
        type: 'focus-subtree',
        callNodePath: [C],
        implementation: 'combined',
        inverted: false,
      })
    );

    const { container } = render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    );
    expect(
      container.querySelector('.calltreeTransformNavigator')
    ).toMatchSnapshot();
  });
});

describe('ProfileCallTreeView/end-to-end', () => {
  it('can display a gecko profile without crashing', () => {
    const geckoProfile = createGeckoProfile();
    const processedProfile = processProfile(geckoProfile);
    const store = storeWithProfile(processedProfile);
    render(
      <Provider store={store}>
        <ProfileCallTreeView hideThreadActivityGraph={true} />
      </Provider>
    );
  });
});
<|endoftext|>


<commit_before>// NB: Any page importing this script MUST define a function
// addPlate that takes 1 parameter (the plate id) and does the work
// of actually collecting the necessary info from the plate the
// user has chosen to add.

// plateTypeList: a list of the types of plates to include in the add plate
// modal; an acceptable value for plateTypeList would be, e.g.,
// ['gDNA', 'compressed gDNA'].
// getOnlyQuantified: a boolean that is true if the only plates included in
// the add plate modal should be those that have already been quantified,
// false if all plates of the relevant types should be included.
function setUpAddPlateModal(plateTypeList, getOnlyQuantified) {
    // populate the table of potential plates to add
    var table = $('#searchPlateTable').DataTable({
            'ajax': {'url': '/plate_list',
                'data': {
                    'plate_type': JSON.stringify(plateTypeList),
                    'only_quantified': getOnlyQuantified
                }
            },
            'columnDefs': [
                {
                'targets': -1, // last column
                'data': null,
                'render': function (data, type, row, meta) {
                    var plateId = data[0];
                    return "<button id='addBtnPlate" + plateId + "' class='btn btn-success btn-circle-small'></button>";
                    }
                },
                {
                'targets': -2, // second to last column
                'data': null,
                'render': function (data, type, row, meta) {
                    // index 3 in row is the list of studies
                    return data[3].join('
');
                    }
                }
            ],
            // per https://datatables.net/reference/option/destroy:
            // Initialise a new DataTable as usual, but if there is an existing
            // DataTable which matches the selector, it will be destroyed and
            // replaced with the new table.
            'destroy': true,
            'order': [[0, "desc"]] // order rows in desc order by plate id
        }
    );

    // Add function called by clicking on one of the buttons that adds a plate
    $('#searchPlateTable tbody').on('click', 'button', function () {
        var plateId = table.row($(this).parents('tr')).data()[0];
        try {
            addPlate(plateId);

            // Disable the button to add the plate to the list
            $('#addBtnPlate' + plateId).prop('disabled', true);
        } finally {
            // No matter what happens, hide the modal to add plates
            $('#addPlateModal').modal('hide');
        }
    });
}<commit_msg>Fixes #474<commit_after>// NB: Any page importing this script MUST define a function
// addPlate that takes 1 parameter (the plate id) and does the work
// of actually collecting the necessary info from the plate the
// user has chosen to add.

// plateTypeList: a list of the types of plates to include in the add plate
// modal; an acceptable value for plateTypeList would be, e.g.,
// ['gDNA', 'compressed gDNA'].
// getOnlyQuantified: a boolean that is true if the only plates included in
// the add plate modal should be those that have already been quantified,
// false if all plates of the relevant types should be included.
function setUpAddPlateModal(plateTypeList, getOnlyQuantified) {
    // populate the table of potential plates to add
    var table = $('#searchPlateTable').DataTable({
            'ajax': {'url': '/plate_list',
                'data': {
                    'plate_type': JSON.stringify(plateTypeList),
                    'only_quantified': getOnlyQuantified
                }
            },
            'columnDefs': [
                {
                'targets': -1, // last column
                'data': null,
                'render': function (data, type, row, meta) {
                    var plateId = data[0];
                    return "<button id='addBtnPlate" + plateId + "' class='btn btn-success btn-circle-small'></button>";
                    }
                },
                {
                'targets': -2, // second to last column
                'data': null,
                'render': function (data, type, row, meta) {
                    // index 3 in row is the list of studies
                    return data[3].join('
');
                    }
                }
            ],
            // per https://datatables.net/reference/option/destroy:
            // Initialise a new DataTable as usual, but if there is an existing
            // DataTable which matches the selector, it will be destroyed and
            // replaced with the new table.
            'destroy': true,
            'order': [[0, "desc"]] // order rows in desc order by plate id
        }
    );

    // Remove any existing event handler already attached to plate add buttons
    $('#searchPlateTable tbody').off('click', 'button');

    // Add function called by clicking on one of the buttons that adds a plate
    $('#searchPlateTable tbody').on('click', 'button', function () {
        var plateId = table.row($(this).parents('tr')).data()[0];
        try {
            addPlate(plateId);

            // Disable the button to add the plate to the list
            $('#addBtnPlate' + plateId).prop('disabled', true);
        } finally {
            // No matter what happens, hide the modal to add plates
            $('#addPlateModal').modal('hide');
        }
    });
}<|endoftext|>


<commit_before>var limiter = require('limiter');
var request = require('request');
var util = require('util');

var _ = require('underscore');

var settings = require('./settings');

var QUERY_STRING_EXIST = /\?/;

var facebookQueue = [];

function facebookRequest(url_orig, limit, cb, payload) {
    var url = url_orig;
    if (url.substring(0, 4) !== 'http') {
        url = 'https://graph.facebook.com/' + url;
    }

    if (QUERY_STRING_EXIST.test(url)) {
        url += '&access_token=' + settings.FACEBOOK_ACCESS_TOKEN;
    }
    else {
        url += '?access_token=' + settings.FACEBOOK_ACCESS_TOKEN;
    }

    if (_.isFinite(limit)) {
        // If limit === 0 we want to fetch multiple pages, everything, so we go for 5000 per page
        url += '&limit=' + (limit || 5000);
    }

    function page(url, cb) {
        request({
            'url': url,
            'method': _.isObject(payload) ? 'POST' : 'GET',
            'form': payload
        }, function (error, res, body) {
            if (error || !res || res.statusCode !== 200) {
                cb("Facebook request (" + url_orig + ") error, error: " + error + ", status: " + (res && res.statusCode) + ", body: " + util.inspect(body));
                return;
            }

            try {
                body = JSON.parse(body);
            }
            catch (e) {
                cb("Facebook request (" + url_orig + ") parse error: " + e);
                return;
            }

            // If limit === 0 we want to fetch multiple pages, everything, so we go for next page, if available
            if (limit === 0 && body.data && body.data.length !== 0 && body.paging && body.paging.next) {
                page(body.paging.next, function (err, next_body) {
                    if (err) {
                        cb(err);
                        return;
                    }

                    // We take only body.data from next pages
                    body.data.push.apply(body.data, next_body.data);

                    cb(null, body);
                });
            }
            else {
                cb(null, body);
            }
        });
    }

    page(url, cb);
}

var facebookLimiter = new limiter.RateLimiter(settings.FACEBOOK_THROTTLE.requests, settings.FACEBOOK_THROTTLE.interval);

var queueWarning = _.throttle(function () {
    console.warn("Queue has grown to %s elements", facebookQueue.length);
}, 10 * 1000); // Warn only once per 10 s

var limiterWarning = _.throttle(function (remainingRequests) {
    console.warn("Limiter has only %s requests left, %s in the queue", remainingRequests, facebookQueue.length);
}, 10 * 1000); // Warn only once per 10 s

function processQueue() {
    var f = facebookQueue.pop();
    if (!f) {
        return;
    }

    if (facebookQueue.length > 1000) {
        // Ups, queue is really long
        queueWarning();
    }

    facebookLimiter.removeTokens(1, function(err, remainingRequests) {
        f();

        if (remainingRequests < 10) {
            limiterWarning(remainingRequests);
        }
    });
}

exports.request = function (url_orig, limit, cb, payload) {
    facebookQueue.unshift(function () {
        facebookRequest(url_orig, limit, function () {
            processQueue();
            cb.apply(this, arguments);
        }, payload);
    });
    processQueue();
};

exports.request.post = function (url, cb, payload) {
    payload = payload || {};
    exports.request(url, null, cb, payload);
};
<commit_msg>Detect when we exceed Facebook rate limit.<commit_after>var limiter = require('limiter');
var request = require('request');
var util = require('util');

var _ = require('underscore');

var settings = require('./settings');

var QUERY_STRING_EXIST = /\?/;

var facebookQueue = [];

function facebookRequest(url_orig, limit, cb, payload) {
    var url = url_orig;
    if (url.substring(0, 4) !== 'http') {
        url = 'https://graph.facebook.com/' + url;
    }

    if (QUERY_STRING_EXIST.test(url)) {
        url += '&access_token=' + settings.FACEBOOK_ACCESS_TOKEN;
    }
    else {
        url += '?access_token=' + settings.FACEBOOK_ACCESS_TOKEN;
    }

    if (_.isFinite(limit)) {
        // If limit === 0 we want to fetch multiple pages, everything, so we go for 5000 per page
        url += '&limit=' + (limit || 5000);
    }

    function page(url, cb) {
        request({
            'url': url,
            'method': _.isObject(payload) ? 'POST' : 'GET',
            'form': payload
        }, function (error, res, body) {
            if (error || !res || res.statusCode !== 200) {
                try {
                    body = JSON.parse(body);
                }
                catch (e) {
                }

                var err = "Facebook request (" + url_orig + ") error, error: " + error + ", status: " + (res && res.statusCode) + ", body: " + util.inspect(body);
                err.body = body;

                cb(err);
                return;
            }

            try {
                body = JSON.parse(body);
            }
            catch (e) {
                cb("Facebook request (" + url_orig + ") parse error: " + e);
                return;
            }

            // If limit === 0 we want to fetch multiple pages, everything, so we go for next page, if available
            if (limit === 0 && body.data && body.data.length !== 0 && body.paging && body.paging.next) {
                page(body.paging.next, function (err, next_body) {
                    if (err) {
                        cb(err);
                        return;
                    }

                    // We take only body.data from next pages
                    body.data.push.apply(body.data, next_body.data);

                    cb(null, body);
                });
            }
            else {
                cb(null, body);
            }
        });
    }

    page(url, cb);
}

var facebookLimiter = new limiter.RateLimiter(settings.FACEBOOK_THROTTLE.requests, settings.FACEBOOK_THROTTLE.interval);

var queueWarning = _.throttle(function () {
    console.warn("Queue has grown to %s elements", facebookQueue.length);
}, 10 * 1000); // Warn only once per 10 s

var limiterWarning = _.throttle(function (remainingRequests) {
    console.warn("Limiter has only %s requests left, %s in the queue", remainingRequests, facebookQueue.length);
}, 10 * 1000); // Warn only once per 10 s

function processQueue(purge_tokens) {
    var f = facebookQueue.pop();
    if (!f) {
        return;
    }

    if (facebookQueue.length > 1000) {
        // Ups, queue is really long
        queueWarning();
    }

    facebookLimiter.removeTokens(purge_tokens ? (parseInt(facebookLimiter.tokenBucket.content) || 1) : 1, function(err, remainingRequests) {
        f();

        if (remainingRequests < 10) {
            limiterWarning(remainingRequests);
        }
    });
}

exports.request = function (url_orig, limit, cb, payload) {
    facebookQueue.unshift(function () {
        facebookRequest(url_orig, limit, function (err) {
            processQueue(err && err.body && err.body.error && err.body.error.code === 613);
            cb.apply(this, arguments);
        }, payload);
    });
    processQueue();
};

exports.request.post = function (url, cb, payload) {
    payload = payload || {};
    exports.request(url, null, cb, payload);
};
<|endoftext|>


<commit_before>"use strict";

const { src, dest, series, parallel } = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const minifyCSS = require("gulp-csso");
const svgmin = require("gulp-svgmin");

const sassOptions = {
  errLogToConsole: true,
  outputStyle: "expanded",
  includePaths: ["./node_modules/foundation-sites/scss"]
};

function icons() {
  return src("*.svg", { cwd: "./icons" })
    .pipe(
      svgmin({
      plugins: [
        { removeMetadata: false },
        { removeTitle: false },
        { removeDimensions: true },
          { addClassesToSVGElement: { className: "icon" } },
          {
            addAttributesToSVGElement: {
              attributes: [{ "aria-hidden": "true", focusable: "false" }]
            }
          }
      ]
      })
    )
    .pipe(dest("./html/images/icons/dist"));
}

function css() {
  return src("./scss/**/*.scss")
    .pipe(sourcemaps.init())
    .pipe(sass(sassOptions).on("error", sass.logError))
    .pipe(minifyCSS())
    .pipe(sourcemaps.write("."))
    .pipe(dest("./html/css/dist"));
}

function copyJs() {
  return src(
    [
      "./node_modules/foundation-sites/js/vendor/*.js",
      "./node_modules/foundation-sites/js/foundation.min.js",
      "./node_modules/jquery-ui-dist/jquery-ui.min.js",
      "./node_modules/papaparse/papaparse.min.js",
      "./node_modules/osmtogeojson/osmtogeojson.js",
      "./node_modules/leaflet/dist/**/*.*",
      "./node_modules/leaflet.markercluster/dist/**/*.*",
      "./node_modules/blueimp-tmpl/js/*.js",
      "./node_modules/blueimp-load-image/js/load-image.all.min.js",
      "./node_modules/blueimp-canvas-to-blob/js/*.js",
      "./node_modules/blueimp-file-upload/js/*.js",
      "./node_modules/@yaireo/tagify/dist/tagify.min.js"
    ],
    { sourcemaps: true }
  ).pipe(dest("./html/js/dist", { sourcemaps: true }));
}

function buildJs() {
  return src([
    './html/js/display*.js',
    './html/js/product-multilingual.js',
    './html/js/search.js'
  ], { sourcemaps: false })
  .pipe(dest('./html/js/dist', { sourcemaps: false }))
}

function jQueryUiThemes() {
  return src("./node_modules/jquery-ui-dist/**/*.css", {
    sourcemaps: true
  }).pipe(dest("./html/css/dist/jqueryui/themes/base", { sourcemaps: true }));
}

function copyCss() {
  return src(["./node_modules/@yaireo/tagify/dist/tagify.css"]).pipe(
    dest("./html/css/dist")
  );

}

exports.copyJs = copyJs;
exports.css = css;
exports.icons = icons;
exports.default = parallel(copyJs, copyCss, jQueryUiThemes, series(icons, css));
<commit_msg>fix: Run buildJs task in gulp<commit_after>"use strict";

const { src, dest, series, parallel } = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const minifyCSS = require("gulp-csso");
const svgmin = require("gulp-svgmin");

const sassOptions = {
  errLogToConsole: true,
  outputStyle: "expanded",
  includePaths: ["./node_modules/foundation-sites/scss"]
};

function icons() {
  return src("*.svg", { cwd: "./icons" })
    .pipe(
      svgmin({
      plugins: [
        { removeMetadata: false },
        { removeTitle: false },
        { removeDimensions: true },
          { addClassesToSVGElement: { className: "icon" } },
          {
            addAttributesToSVGElement: {
              attributes: [{ "aria-hidden": "true", focusable: "false" }]
            }
          }
      ]
      })
    )
    .pipe(dest("./html/images/icons/dist"));
}

function css() {
  return src("./scss/**/*.scss")
    .pipe(sourcemaps.init())
    .pipe(sass(sassOptions).on("error", sass.logError))
    .pipe(minifyCSS())
    .pipe(sourcemaps.write("."))
    .pipe(dest("./html/css/dist"));
}

function copyJs() {
  return src(
    [
      "./node_modules/foundation-sites/js/vendor/*.js",
      "./node_modules/foundation-sites/js/foundation.min.js",
      "./node_modules/jquery-ui-dist/jquery-ui.min.js",
      "./node_modules/papaparse/papaparse.min.js",
      "./node_modules/osmtogeojson/osmtogeojson.js",
      "./node_modules/leaflet/dist/**/*.*",
      "./node_modules/leaflet.markercluster/dist/**/*.*",
      "./node_modules/blueimp-tmpl/js/*.js",
      "./node_modules/blueimp-load-image/js/load-image.all.min.js",
      "./node_modules/blueimp-canvas-to-blob/js/*.js",
      "./node_modules/blueimp-file-upload/js/*.js",
      "./node_modules/@yaireo/tagify/dist/tagify.min.js"
    ],
    { sourcemaps: true }
  ).pipe(dest("./html/js/dist", { sourcemaps: true }));
}

function buildJs() {
  return src([
    './html/js/display*.js',
    './html/js/product-multilingual.js',
    './html/js/search.js'
  ], { sourcemaps: false })
  .pipe(dest('./html/js/dist', { sourcemaps: false }))
}

function jQueryUiThemes() {
  return src("./node_modules/jquery-ui-dist/**/*.css", {
    sourcemaps: true
  }).pipe(dest("./html/css/dist/jqueryui/themes/base", { sourcemaps: true }));
}

function copyCss() {
  return src(["./node_modules/@yaireo/tagify/dist/tagify.css"]).pipe(
    dest("./html/css/dist")
  );

}

exports.copyJs = copyJs;
exports.buildJs = buildJs;
exports.css = css;
exports.icons = icons;
exports.default = parallel(copyJs, buildJs, copyCss, jQueryUiThemes, series(icons, css));
<|endoftext|>