<commit_before>var MobProxy = require('./mobproxy'),
    winston = require('winston'),
    url = require('url');

var nullProxy = {
    launchProcess: function(cb) { cb(); },
    stopProcess: function(cb) { cb(); },
    openProxy: function(cb) { cb(); },
    closeProxy: function(cb) { cb(); },
    clearDNS: function(cb) { cb(); },
    newPage: function(name, cb) { cb(); },
    saveHar: function(filename, data, cb) { cb(); },
    getProxyUrl: function() { return undefined; }
};

function setupMobProxyDefaults(options) {
    options.proxySleepBeforeStart = options.proxySleepBeforeStart || 15000;

    options.mobProxyPort = options.mobProxyPort ||  9091;
    options.proxyPort = options.proxyPort ||  9092;
    options.proxy = 'localhost:' + options.mobProxyPort;

    if (options.connection) {
        if (options.connection === 'mobile3g') {
            options.connectionRaw = {
                downstreamKbps: 1600,
                upstreamKbps: 768,
                latency: 300
            };
        } else if (options.connection === 'mobile3gfast') {
            options.connectionRaw = {
                downstreamKbps: 1600,
                upstreamKbps: 768,
                latency: 150
            };
        } else if (options.connection === 'cable') {
            options.connectionRaw = {
                downstreamKbps: 5000,
                upstreamKbps: 1000,
                latency: 28
            };
        }
    }
}

var createProxy = function(options) {
    var useProxy = options.useProxy;
    setupLogs(options);

    if (useProxy && typeof useProxy === 'string' && useProxy !== 'true') {
        return nullProxy;
    }

    setupMobProxyDefaults(options);

    return new MobProxy({
        port: options.mobProxyPort,
        proxyPort: options.proxyPort,
        domain: url.parse(options.url).hostname,
        headers: options.headers,
        basicAuth: options.basicAuth,
        limit: options.connectionRaw,
        proxySleepBeforeStart: options.proxySleepBeforeStart,
        logDir: options.logDir,
        silent: options.silent
    });
};

function setupLogs(options) {
  winston.loggers.add('browsermobproxy', {
    file: {
      level: 'info',
      json: 'false',
      label: 'browsermobproxy',
      filename: options.logDir?path.join(options.logDir,'browsermobproxy.log'):'browsermobproxy.log'
    },
    console: {
      silent: true
    }
  });

  winston.loggers.add('browsertime', {
    file: {
      level: 'info',
      json: 'false',
      label: 'browsertime',
      filename: options.logDir?path.join(options.logDir,'browsertime.log'):'browsertime.log'
    },
    console: {
      silent: (options.silent === 'true')? true : false
    }
  });

};

module.exports.createProxy = createProxy;
<commit_msg>jshint errors<commit_after>var MobProxy = require('./mobproxy'),
    winston = require('winston'),
    path = require('path'),
    url = require('url');

var nullProxy = {
    launchProcess: function(cb) { cb(); },
    stopProcess: function(cb) { cb(); },
    openProxy: function(cb) { cb(); },
    closeProxy: function(cb) { cb(); },
    clearDNS: function(cb) { cb(); },
    newPage: function(name, cb) { cb(); },
    saveHar: function(filename, data, cb) { cb(); },
    getProxyUrl: function() { return undefined; }
};

function setupMobProxyDefaults(options) {
    options.proxySleepBeforeStart = options.proxySleepBeforeStart || 15000;

    options.mobProxyPort = options.mobProxyPort ||  9091;
    options.proxyPort = options.proxyPort ||  9092;
    options.proxy = 'localhost:' + options.mobProxyPort;

    if (options.connection) {
        if (options.connection === 'mobile3g') {
            options.connectionRaw = {
                downstreamKbps: 1600,
                upstreamKbps: 768,
                latency: 300
            };
        } else if (options.connection === 'mobile3gfast') {
            options.connectionRaw = {
                downstreamKbps: 1600,
                upstreamKbps: 768,
                latency: 150
            };
        } else if (options.connection === 'cable') {
            options.connectionRaw = {
                downstreamKbps: 5000,
                upstreamKbps: 1000,
                latency: 28
            };
        }
    }
}

var createProxy = function(options) {
    var useProxy = options.useProxy;
    setupLogs(options);

    if (useProxy && typeof useProxy === 'string' && useProxy !== 'true') {
        return nullProxy;
    }

    setupMobProxyDefaults(options);

    return new MobProxy({
        port: options.mobProxyPort,
        proxyPort: options.proxyPort,
        domain: url.parse(options.url).hostname,
        headers: options.headers,
        basicAuth: options.basicAuth,
        limit: options.connectionRaw,
        proxySleepBeforeStart: options.proxySleepBeforeStart,
        logDir: options.logDir,
        silent: options.silent
    });
};

function setupLogs(options) {
  winston.loggers.add('browsermobproxy', {
    file: {
      level: 'info',
      json: 'false',
      label: 'browsermobproxy',
      filename: options.logDir?path.join(options.logDir,'browsermobproxy.log'):'browsermobproxy.log'
    },
    console: {
      silent: true
    }
  });

  winston.loggers.add('browsertime', {
    file: {
      level: 'info',
      json: 'false',
      label: 'browsertime',
      filename: options.logDir?path.join(options.logDir,'browsertime.log'):'browsertime.log'
    },
    console: {
      silent: (options.silent === 'true')? true : false
    }
  });

}

module.exports.createProxy = createProxy;
<|endoftext|>


<commit_before>var net = require('net');
var tls = require('tls');
var url = require('url');
var parseReq = require('./parse-req');
var connect = require('./connect');
var util = require('../../util');

module.exports = function init(app) {
	var config = app.config;
	var server = net.createServer(function(socket) {
		var proxyReq;
		function destroy() {
			socket.destroy();
			proxyReq && proxyReq.destroy();
		}
		abortIfUnavailable(socket);

		function abortIfUnavailable(socket) {
			socket.on('error', destroy).on('close', destroy);
		}

		parseReq(socket, function(err, req) {
			if (err) {
				return destroy();
			}

			var options = req.headers[config.httpsProxyHost];
			options = options && url.parse('http://' + options);
			if (!options || !net.isIP(options.hostname)) {
				return destroy();
			}
			options.host = options.hostname;
			req.options = options;

			connect(req, function(err, proxySocket) {
				if (err) {
					return destroy();
				}

				proxyReq = tls.connect({
			        rejectUnauthorized: false,
			        socket: proxySocket
			    }, function () {
			    	req.pipe(proxyReq);
			    	proxyReq.pipe(socket);
			    });
				abortIfUnavailable(proxyReq);
			});
		});
	})
	.listen(config.httpsproxyport, 'localhost')
	.on('error', util.noop);
};<commit_msg>client error<commit_after>var net = require('net');
var tls = require('tls');
var url = require('url');
var parseReq = require('./parse-req');
var connect = require('./connect');
var util = require('../../util');

module.exports = function init(app) {
	var config = app.config;
	var server = net.createServer(function(socket) {
		var proxyReq;
		function destroy() {
			socket.destroy();
			proxyReq && proxyReq.destroy();
		}
		abortIfUnavailable(socket);

		function abortIfUnavailable(socket) {
			socket.on('error', destroy).on('close', destroy);
		}

		parseReq(socket, function(err, req) {
			if (err) {
				return destroy();
			}

			var options = req.headers[config.httpsProxyHost];
			options = options && url.parse('http://' + options);
			if (!options || !net.isIP(options.hostname)) {
				return destroy();
			}
			options.host = options.hostname;
			req.options = options;

			connect(req, function(err, proxySocket) {
				if (err) {
					return destroy();
				}

				proxyReq = tls.connect({
			        rejectUnauthorized: false,
			        socket: proxySocket
			    }, function () {
			    	req.pipe(proxyReq);
			    	proxyReq.pipe(socket);
			    });
				abortIfUnavailable(proxyReq);
			});
		});
	})
	.listen(config.httpsproxyport, 'localhost')
	.on('error', util.noop)
	.on('clientError', util.noop);
};<|endoftext|>


<commit_before>
/*!
 * querystring
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Library version.
 */

exports.version = '0.1.0';

/**
 * Parse the given query `str`, returning an object.
 *
 * @param {String} str
 * @return {Object}
 * @api public
 */

exports.parse = function(str){
  if (str == undefined || str == '') return {};

  return String(str)
    .split('&')
    .reduce(function(ret, pair){
      var pair = decodeURIComponent(pair.replace(/\+/g, ' '))
        , eql = pair.indexOf('=')
        , brace = lastBraceInKey(pair)
        , key = pair.substr(0, brace || eql)
        , val = pair.substr(brace || eql, pair.length)
        , val = val.substr(val.indexOf('=') + 1, val.length)
        , obj = ret;

      // ?foo
      if ('' == key) key = pair, val = '';

      // nested
      if (~key.indexOf(']')) {
        var parts = key.split('[')
          , len = parts.length
          , last = len - 1;

        function parse(obj, parts, parent, key) {
          var part = parts.shift();

          // end
          if (!part) {
            if (Array.isArray(parent[key])) {
              parent[key].push(val);
            } else if ('object' == typeof parent[key]) {
              parent[key] = val;
            } else {
              parent[key] = [parent[key], val];
            }
          // array
          } else if (']' == part) {
            obj = parent[key] = Array.isArray(parent[key])
              ? parent[key]
              : [];
            if ('' != val) obj.push(val);
          // prop
          } else if (~part.indexOf(']')) {
            part = part.substr(0, part.length - 1);
            parse(obj[part] = obj[part] || {}, parts, obj, part);
          // key
          } else {
            parse(obj[part] = obj[part] || {}, parts, obj, part);
          }
        }

        parse(obj, parts);
      // optimize
      } else {
        set(obj, key, val);
      }

      return ret;
    }, {});
};

/**
 * Turn the given `obj` into a query string
 *
 * @param {Object} obj
 * @return {String}
 * @api public
 */

var stringify = exports.stringify = function(obj, prefix) {
  var ret = [];
  if (Array.isArray(obj)) {
    if (!prefix) throw new TypeError('stringify expects an object');
    for (var i = 0; i < obj.length; i++) {
      ret.push( stringify(obj[i], prefix + '[]') );
    }
    return ret.join('&');
  } else if ('object' == typeof obj) {
    var k,v;
    for (var i in obj) {
      if ( obj.hasOwnProperty(i) ) {
        k = i;
        v = obj[i];
        ret.push( stringify(v, prefix ? prefix + '[' + encodeURIComponent(k) + ']' : encodeURIComponent(k) ) );
      }
    }
    return ret.join("&");
  } else if ('string' == typeof obj) {
    if (!prefix) throw new TypeError('stringify expects an object');
    return prefix + '=' + encodeURIComponent(obj);
  } else {
    return prefix;
  }
}

/**
 * Set `obj`'s `key` to `val` respecting
 * the weird and wonderful syntax of a qs,
 * where "foo=bar&foo=baz" becomes an array.
 *
 * @param {Object} obj
 * @param {String} key
 * @param {String} val
 * @api private
 */

function set(obj, key, val) {
  var v = obj[key];
  if (undefined === v) {
    obj[key] = val;
  } else if (Array.isArray(v)) {
    v.push(val);
  } else {
    obj[key] = [v, val];
  }
}

/**
 * Locate last brace in `str` within the key.
 *
 * @param {String} str
 * @return {Number}
 * @api private
 */

function lastBraceInKey(str) {
  var len = str.length
    , brace
    , c;
  for (var i = 0; i < len; ++i) {
    c = str[i];
    if (']' == c) brace = false;
    if ('[' == c) brace = true;
    if ('=' == c && !brace) return i;
  }
}
<commit_msg>fixed some styling<commit_after>
/*!
 * querystring
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Library version.
 */

exports.version = '0.1.0';

/**
 * Object#toString() ref for stringify().
 */

var toString = Object.prototype.toString;

/**
 * Parse the given query `str`, returning an object.
 *
 * @param {String} str
 * @return {Object}
 * @api public
 */

exports.parse = function(str){
  if (str == undefined || str == '') return {};

  return String(str)
    .split('&')
    .reduce(function(ret, pair){
      var pair = decodeURIComponent(pair.replace(/\+/g, ' '))
        , eql = pair.indexOf('=')
        , brace = lastBraceInKey(pair)
        , key = pair.substr(0, brace || eql)
        , val = pair.substr(brace || eql, pair.length)
        , val = val.substr(val.indexOf('=') + 1, val.length)
        , obj = ret;

      // ?foo
      if ('' == key) key = pair, val = '';

      // nested
      if (~key.indexOf(']')) {
        var parts = key.split('[')
          , len = parts.length
          , last = len - 1;

        function parse(obj, parts, parent, key) {
          var part = parts.shift();

          // end
          if (!part) {
            if (Array.isArray(parent[key])) {
              parent[key].push(val);
            } else if ('object' == typeof parent[key]) {
              parent[key] = val;
            } else {
              parent[key] = [parent[key], val];
            }
          // array
          } else if (']' == part) {
            obj = parent[key] = Array.isArray(parent[key])
              ? parent[key]
              : [];
            if ('' != val) obj.push(val);
          // prop
          } else if (~part.indexOf(']')) {
            part = part.substr(0, part.length - 1);
            parse(obj[part] = obj[part] || {}, parts, obj, part);
          // key
          } else {
            parse(obj[part] = obj[part] || {}, parts, obj, part);
          }
        }

        parse(obj, parts);
      // optimize
      } else {
        set(obj, key, val);
      }

      return ret;
    }, {});
};

/**
 * Turn the given `obj` into a query string
 *
 * @param {Object} obj
 * @return {String}
 * @api public
 */

var stringify = exports.stringify = function(obj, prefix) {
  var ret = [];
  if (Array.isArray(obj)) {
    if (!prefix) throw new TypeError('stringify expects an object');
    for (var i = 0; i < obj.length; i++) {
      ret.push(stringify(obj[i], prefix + '[]'));
    }
    return ret.join('&');
  } else if ('[object Object]' == toString.call(obj)) {
    var keys = Object.keys(obj)
      , key;

    for (var i = 0, len = keys.length; i < len; ++i) {
      key = keys[i];
      ret.push(stringify(obj[key], prefix
        ? prefix + '[' + encodeURIComponent(key) + ']'
        : encodeURIComponent(key)));
    }

    return ret.join('&');
  } else if ('string' == typeof obj) {
    if (!prefix) throw new TypeError('stringify expects an object');
    return prefix + '=' + encodeURIComponent(obj);
  } else {
    return prefix;
  }
}

/**
 * Set `obj`'s `key` to `val` respecting
 * the weird and wonderful syntax of a qs,
 * where "foo=bar&foo=baz" becomes an array.
 *
 * @param {Object} obj
 * @param {String} key
 * @param {String} val
 * @api private
 */

function set(obj, key, val) {
  var v = obj[key];
  if (undefined === v) {
    obj[key] = val;
  } else if (Array.isArray(v)) {
    v.push(val);
  } else {
    obj[key] = [v, val];
  }
}

/**
 * Locate last brace in `str` within the key.
 *
 * @param {String} str
 * @return {Number}
 * @api private
 */

function lastBraceInKey(str) {
  var len = str.length
    , brace
    , c;
  for (var i = 0; i < len; ++i) {
    c = str[i];
    if (']' == c) brace = false;
    if ('[' == c) brace = true;
    if ('=' == c && !brace) return i;
  }
}
<|endoftext|>


<commit_before>'use babel';

import { CompositeDisposable } from 'atom';

import { createTransform } from 'transformime';

const transform = createTransform();

export default class ResultView {

  constructor(marker) {
    this.marker = marker;
    this.element = document.createElement('div');
    this.element.classList.add('hydrogen', 'output-bubble', 'empty');

    const outputContainer = document.createElement('div');
    outputContainer.classList.add('bubble-output-container');
    outputContainer.onmousewheel = e => e.stopPropagation();
    this.element.appendChild(outputContainer);

    this.resultContainer = document.createElement('div');
    this.resultContainer.classList.add('bubble-result-container');
    outputContainer.appendChild(this.resultContainer);

    this.errorContainer = document.createElement('div');
    this.errorContainer.classList.add('bubble-error-container');
    outputContainer.appendChild(this.errorContainer);

    this.statusContainer = document.createElement('div');
    this.statusContainer.classList.add('bubble-status-container');
    this.spinner = this.buildSpinner();
    this.statusContainer.appendChild(this.spinner);
    outputContainer.appendChild(this.statusContainer);

    const richCloseButton = document.createElement('div');
    richCloseButton.classList.add('rich-close-button', 'icon', 'icon-x');
    richCloseButton.onclick = () => this.destroy();
    this.element.appendChild(richCloseButton);

    const actionPanel = document.createElement('div');
    actionPanel.classList.add('bubble-action-panel');
    this.element.appendChild(actionPanel);

    const closeButton = document.createElement('div');
    closeButton.classList.add('action-button',
      'close-button', 'icon', 'icon-x');
    closeButton.onclick = () => this.destroy();
    actionPanel.appendChild(closeButton);

    const padding = document.createElement('div');
    padding.classList.add('padding');
    actionPanel.appendChild(padding);

    const copyButton = document.createElement('div');
    copyButton.classList.add('action-button',
      'copy-button', 'icon', 'icon-clippy');
    copyButton.onclick = () => {
      atom.clipboard.write(this.getAllText());
      atom.notifications.addSuccess('Copied to clipboard');
    };
    actionPanel.appendChild(copyButton);

    const openButton = document.createElement('div');
    openButton.classList.add('action-button',
      'open-button', 'icon', 'icon-file-symlink-file');
    openButton.onclick = () => {
      const bubbleText = this.getAllText();
      atom.workspace.open().then(editor => editor.insertText(bubbleText));
    };
    actionPanel.appendChild(openButton);

    this.setMultiline(false);

    this.tooltips = new CompositeDisposable();
    this.addCopyTooltip(copyButton);
    this.tooltips.add(atom.tooltips.add(openButton, { title: 'Open in new editor' }));

    this._hasResult = false;
    this._executionCount = null;

    return this;
  }

  addCopyTooltip(element) {
    this.tooltips.add(atom.tooltips.add(element, {
      title: () => {
        if (!this._executionCount) {
          return 'Copy to clipboard';
        }
        return `Copy to clipboard (Out[${this._executionCount}])`;
      },
    }));
  }

  addResult(result) {
    let container;
    console.log('ResultView: Add result', result);

    if (result.stream === 'execution_count') {
      this._executionCount = result.data;
      return;
    }

    this.element.classList.remove('empty');

    if (result.stream === 'status') {
      if (!this._hasResult && result.data === 'ok') {
        console.log('ResultView: Show status container');
        this.statusContainer.classList.add('icon', 'icon-check');
        this.statusContainer.style.display = 'inline-block';
      }
      return;
    }

    if (result.stream === 'stderr') {
      container = this.errorContainer;
    } else if (result.stream === 'stdout') {
      container = this.resultContainer;
    } else if (result.stream === 'error') {
      container = this.errorContainer;
    } else {
      container = this.resultContainer;
    }

    const onSuccess = ({ mimetype, el }) => {
      console.log('ResultView: Hide status container');
      this._hasResult = true;
      this.statusContainer.style.display = 'none';

      const mimeType = mimetype;
      let htmlElement = el;

      if (mimeType === 'text/plain') {
        this.element.classList.remove('rich');

        const previousText = this.getAllText();
        const text = result.data['text/plain'];
        if (previousText === '' && text.length < 50 &&
          text.indexOf('\n') === -1) {
          this.setMultiline(false);

          this.addCopyTooltip(container);

          container.onclick = () => {
            atom.clipboard.write(this.getAllText());
            atom.notifications.addSuccess('Copied to clipboard');
          };
        } else {
          this.setMultiline(true);
        }
      } else {
        this.element.classList.add('rich');
        this.setMultiline(true);
      }

      if (mimeType === 'application/pdf') {
        const webview = document.createElement('webview');
        webview.src = htmlElement.href;
        htmlElement = webview;
      }

      console.log('ResultView: Rendering as MIME ', mimeType);
      console.log('ResultView: Rendering as ', htmlElement);
      // this.getAllText must be called after appending the htmlElement
      // in order to obtain innerText
      container.appendChild(htmlElement);

      if (mimeType === 'text/html') {
        if (this.getAllText() !== '') {
          this.element.classList.remove('rich');
        }
      }

      if (mimeType === 'image/svg+xml') {
        container.classList.add('svg');
      }

      if (mimeType === 'text/markdown') {
        this.element.classList.add('markdown');
        this.element.classList.remove('rich');
      }

      if (mimeType === 'text/latex') {
        this.element.classList.add('latex');
      }

      if (this.errorContainer.getElementsByTagName('span').length === 0) {
        this.errorContainer.classList.add('plain-error');
      } else {
        this.errorContainer.classList.remove('plain-error');
      }
    };

    const onError = error => console.error('ResultView: Rendering error:', error);

    transform(result.data).then(onSuccess, onError);
  }

  getAllText() {
    let text = '';

    const resultText = this.resultContainer.innerText.trim();
    if (resultText.length > 0) {
      text += resultText;
    }

    const errorText = this.errorContainer.innerText.trim();
    if (errorText.length > 0) {
      text += `\n${errorText}`;
    }

    return text;
  }

  setMultiline(multiline) {
    if (multiline) {
      this.element.classList.add('multiline');
    } else {
      this.element.classList.remove('multiline');
    }
  }

  buildSpinner() {
    const container = document.createElement('div');
    container.classList.add('spinner');

    const rect1 = document.createElement('div');
    rect1.classList.add('rect1');
    const rect2 = document.createElement('div');
    rect2.classList.add('rect2');
    const rect3 = document.createElement('div');
    rect3.classList.add('rect3');
    const rect4 = document.createElement('div');
    rect4.classList.add('rect4');
    const rect5 = document.createElement('div');
    rect5.classList.add('rect5');

    container.appendChild(rect1);
    container.appendChild(rect2);
    container.appendChild(rect3);
    container.appendChild(rect4);
    container.appendChild(rect5);

    return container;
  }

  spin(shouldSpin) {
    if (shouldSpin) {
      this.element.classList.remove('empty');
      this.spinner.style.display = 'block';
    } else {
      this.spinner.style.display = 'none';
    }
  }

  destroy() {
    this.tooltips.dispose();
    if (this.marker) {
      this.marker.destroy();
    }
    this.element.innerHTML = '';
  }
}
<commit_msg>Don't block editor scrolling in result views<commit_after>'use babel';

import { CompositeDisposable } from 'atom';

import { createTransform } from 'transformime';

const transform = createTransform();

export default class ResultView {

  constructor(marker) {
    this.marker = marker;
    this.element = document.createElement('div');
    this.element.classList.add('hydrogen', 'output-bubble', 'empty');

    const outputContainer = document.createElement('div');
    outputContainer.classList.add('bubble-output-container');
    outputContainer.onmousewheel = (event) => {
      const clientHeight = outputContainer.clientHeight;
      const scrollHeight = outputContainer.scrollHeight;
      const scrollTop = outputContainer.scrollTop;
      const atTop = (scrollTop !== 0 && event.deltaY < 0);
      const atBottom = (scrollTop !== scrollHeight - clientHeight && event.deltaY > 0);

      if (clientHeight < scrollHeight && (atTop || atBottom)) {
        event.stopPropagation();
      }
    };
    this.element.appendChild(outputContainer);

    this.resultContainer = document.createElement('div');
    this.resultContainer.classList.add('bubble-result-container');
    outputContainer.appendChild(this.resultContainer);

    this.errorContainer = document.createElement('div');
    this.errorContainer.classList.add('bubble-error-container');
    outputContainer.appendChild(this.errorContainer);

    this.statusContainer = document.createElement('div');
    this.statusContainer.classList.add('bubble-status-container');
    this.spinner = this.buildSpinner();
    this.statusContainer.appendChild(this.spinner);
    outputContainer.appendChild(this.statusContainer);

    const richCloseButton = document.createElement('div');
    richCloseButton.classList.add('rich-close-button', 'icon', 'icon-x');
    richCloseButton.onclick = () => this.destroy();
    this.element.appendChild(richCloseButton);

    const actionPanel = document.createElement('div');
    actionPanel.classList.add('bubble-action-panel');
    this.element.appendChild(actionPanel);

    const closeButton = document.createElement('div');
    closeButton.classList.add('action-button',
      'close-button', 'icon', 'icon-x');
    closeButton.onclick = () => this.destroy();
    actionPanel.appendChild(closeButton);

    const padding = document.createElement('div');
    padding.classList.add('padding');
    actionPanel.appendChild(padding);

    const copyButton = document.createElement('div');
    copyButton.classList.add('action-button',
      'copy-button', 'icon', 'icon-clippy');
    copyButton.onclick = () => {
      atom.clipboard.write(this.getAllText());
      atom.notifications.addSuccess('Copied to clipboard');
    };
    actionPanel.appendChild(copyButton);

    const openButton = document.createElement('div');
    openButton.classList.add('action-button',
      'open-button', 'icon', 'icon-file-symlink-file');
    openButton.onclick = () => {
      const bubbleText = this.getAllText();
      atom.workspace.open().then(editor => editor.insertText(bubbleText));
    };
    actionPanel.appendChild(openButton);

    this.setMultiline(false);

    this.tooltips = new CompositeDisposable();
    this.addCopyTooltip(copyButton);
    this.tooltips.add(atom.tooltips.add(openButton, { title: 'Open in new editor' }));

    this._hasResult = false;
    this._executionCount = null;

    return this;
  }

  addCopyTooltip(element) {
    this.tooltips.add(atom.tooltips.add(element, {
      title: () => {
        if (!this._executionCount) {
          return 'Copy to clipboard';
        }
        return `Copy to clipboard (Out[${this._executionCount}])`;
      },
    }));
  }

  addResult(result) {
    let container;
    console.log('ResultView: Add result', result);

    if (result.stream === 'execution_count') {
      this._executionCount = result.data;
      return;
    }

    this.element.classList.remove('empty');

    if (result.stream === 'status') {
      if (!this._hasResult && result.data === 'ok') {
        console.log('ResultView: Show status container');
        this.statusContainer.classList.add('icon', 'icon-check');
        this.statusContainer.style.display = 'inline-block';
      }
      return;
    }

    if (result.stream === 'stderr') {
      container = this.errorContainer;
    } else if (result.stream === 'stdout') {
      container = this.resultContainer;
    } else if (result.stream === 'error') {
      container = this.errorContainer;
    } else {
      container = this.resultContainer;
    }

    const onSuccess = ({ mimetype, el }) => {
      console.log('ResultView: Hide status container');
      this._hasResult = true;
      this.statusContainer.style.display = 'none';

      const mimeType = mimetype;
      let htmlElement = el;

      if (mimeType === 'text/plain') {
        this.element.classList.remove('rich');

        const previousText = this.getAllText();
        const text = result.data['text/plain'];
        if (previousText === '' && text.length < 50 &&
          text.indexOf('\n') === -1) {
          this.setMultiline(false);

          this.addCopyTooltip(container);

          container.onclick = () => {
            atom.clipboard.write(this.getAllText());
            atom.notifications.addSuccess('Copied to clipboard');
          };
        } else {
          this.setMultiline(true);
        }
      } else {
        this.element.classList.add('rich');
        this.setMultiline(true);
      }

      if (mimeType === 'application/pdf') {
        const webview = document.createElement('webview');
        webview.src = htmlElement.href;
        htmlElement = webview;
      }

      console.log('ResultView: Rendering as MIME ', mimeType);
      console.log('ResultView: Rendering as ', htmlElement);
      // this.getAllText must be called after appending the htmlElement
      // in order to obtain innerText
      container.appendChild(htmlElement);

      if (mimeType === 'text/html') {
        if (this.getAllText() !== '') {
          this.element.classList.remove('rich');
        }
      }

      if (mimeType === 'image/svg+xml') {
        container.classList.add('svg');
      }

      if (mimeType === 'text/markdown') {
        this.element.classList.add('markdown');
        this.element.classList.remove('rich');
      }

      if (mimeType === 'text/latex') {
        this.element.classList.add('latex');
      }

      if (this.errorContainer.getElementsByTagName('span').length === 0) {
        this.errorContainer.classList.add('plain-error');
      } else {
        this.errorContainer.classList.remove('plain-error');
      }
    };

    const onError = error => console.error('ResultView: Rendering error:', error);

    transform(result.data).then(onSuccess, onError);
  }

  getAllText() {
    let text = '';

    const resultText = this.resultContainer.innerText.trim();
    if (resultText.length > 0) {
      text += resultText;
    }

    const errorText = this.errorContainer.innerText.trim();
    if (errorText.length > 0) {
      text += `\n${errorText}`;
    }

    return text;
  }

  setMultiline(multiline) {
    if (multiline) {
      this.element.classList.add('multiline');
    } else {
      this.element.classList.remove('multiline');
    }
  }

  buildSpinner() {
    const container = document.createElement('div');
    container.classList.add('spinner');

    const rect1 = document.createElement('div');
    rect1.classList.add('rect1');
    const rect2 = document.createElement('div');
    rect2.classList.add('rect2');
    const rect3 = document.createElement('div');
    rect3.classList.add('rect3');
    const rect4 = document.createElement('div');
    rect4.classList.add('rect4');
    const rect5 = document.createElement('div');
    rect5.classList.add('rect5');

    container.appendChild(rect1);
    container.appendChild(rect2);
    container.appendChild(rect3);
    container.appendChild(rect4);
    container.appendChild(rect5);

    return container;
  }

  spin(shouldSpin) {
    if (shouldSpin) {
      this.element.classList.remove('empty');
      this.spinner.style.display = 'block';
    } else {
      this.spinner.style.display = 'none';
    }
  }

  destroy() {
    this.tooltips.dispose();
    if (this.marker) {
      this.marker.destroy();
    }
    this.element.innerHTML = '';
  }
}
<|endoftext|>


<commit_before>var util = require('../util');
var values = require('./util').values;
var url = require('url');
var net = require('net');
var lookup = require('./dns');
var extend = require('util')._extend;
var allowDnsCache = true;
var protocols = ['host', 'rule', 'head', 'req', 'reqHeaders', 'reqBody', 'prependReq', 'appendReq',
                 'res', 'resHeaders', 'resBody', 'prependRes', 'appendRes', 'weinre', 'filter'];
var rules = {};
protocols.forEach(function(protocol) {
	rules[protocol] = [];
});

function parse(text) {
	protocols.forEach(function(protocol) {
		rules[protocol] = [];
	});
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^(?:http|ws)s?:\/\//.test(pattern))) {
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else if (!util.isRegExp(pattern) && util.isRegExp(line[1])) {
		parseRule(line[1], pattern, raw);
	} else {
		parseRule(pattern, line[1], raw);
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var list;
	if (net.isIP(matcher)) {
		list = rules.host;
	} else {
		var protocol = matcher.match(/^([\w\-]+):\/\//);
		list = rules[protocol && protocol[1]] || rules.rule;
	}
	list.push({
		isRegExp: isRegExp,
		protocol: isRegExp ? null : util.getProtocol(pattern),
		pattern: isRegExp ? pattern : formatUrl(pattern),
		matcher: matcher,
		raw: raw,
		rawPattern: pattern
	});
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!options.hostname || (!util.isWebsocketProtocol(options.protocol)
			&& !util.isWebProtocol(options.protocol))) {

		return callback(null, null);
	}

	var host = !resolveFilter(_url).host && getRule(_url, rules.host);
	host ? callback(null, host.matcher)
			: lookup(options.hostname, callback, allowDnsCache);
}

function formatUrl(pattern) {
	var queryString = '';
	var queryIndex = pattern.indexOf('?');
	if (queryIndex != -1) {
		queryString = pattern.substring(queryIndex);
		pattern = pattern.substring(0, queryIndex);
	}
	return (pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern) + queryString;
}

exports.resolveHost = resolveHost;

function getRule(url, list) {
	var rule = _getRule(url, list);
	//支持域名匹配
	if (!rule && (rule = _getRule(url.replace(/^((?:http|ws)s?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), list))
			&& util.removeProtocol(rule.rawPattern, true).indexOf('/') != -1) {
		rule = null;
	}
	if (rule) {
		url = rule.url;
		var index = url.indexOf('://') + 3;
		var protocol = url.substring(0, index);
		url = url.substring(index);
		var key = getKey(url);
		if (key) {
			rule.key = key;
		}
		var value = key && values.get(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(url)) !== false) {
			rule.value = protocol + value;
		} else if ((value = getPath(url)) !== false) {
			rule.path = protocol + value;
			rule.files = getFiles(rule.path);
		}
	}

	return rule;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, list) {
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}

	var _url = url.replace(/[?#].*$/, '');
	for (var i = 0, rule; rule = list[i]; i++) {
		var pattern = rule.pattern;
		var matcher = setProtocol(rule.matcher, url);
		var files = getFiles(matcher);
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				function replaceSubMatcher(url) {
					return url.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					});
				}

				return extend({
					url: replaceSubMatcher(matcher),
					files: files && files.map(function(file) {
						return replaceSubMatcher(file);
					})
				}, rule);;
			}
		} else if (url.indexOf(pattern = setProtocol(pattern, url)) === 0) {
			var len = pattern.length;
			if (pattern.indexOf('?') != -1) {
				var filePath = url.substring(len);
				return extend({
					url: matcher + (matcher.indexOf('?') == -1 && filePath.indexOf('?') == -1 ? '?' : '') + filePath,
					files: files && files.map(function(file) {
						return file + (file.indexOf('?') == -1 && filePath.indexOf('?') == -1 ? '?' : '') + filePath;
					})
				}, rule);
			}

			if (pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
				var filePath = url.substring(len);
				return extend({
					files: files && files.map(function(file) {
						return join(file, filePath);
					}),
					url: join(matcher, filePath)
				}, rule);
			}
		}
	}
}

function getFiles(path) {

	return /^x?(file|tpl|jsonp|dust):/.test(path) ? util.removeProtocol(path, true).split('|') : null;
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\' || ch == '?';
}

function join(first, last) {
	if (!first || !last) {
		return first + last;
	}

	if (isPathSeparator(first[first.length - 1])) {
		return isPathSeparator(last[0]) ? first + last.substring(1) : first + last;
	}

	return isPathSeparator(last[0]) ? first + last : first + '/' + last;
}

function resolveFilter(url) {
	var filter = getRule(url, rules.filter);
	var result = {};
	if (filter) {
		util.removeProtocol(util.rule.getMatcher(filter), true)
				.split('|').forEach(function(action) {
			result[action] = true;
		});
	}

	return result;
}

exports.resolveFilter = resolveFilter;

exports.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rule;
	var _rules = {};
	var filter = resolveFilter(url);
	protocols.forEach(function(protocol) {
		if (protocol != 'filter' && !filter[protocol] && (rule = getRule(url, rules[protocol]))) {
			_rules[protocol] = rule;
		}
	});

	return _rules;
};

exports.disableDnsCache = function() {
	allowDnsCache = false;
};
<commit_msg>add file rule<commit_after>var util = require('../util');
var values = require('./util').values;
var url = require('url');
var net = require('net');
var lookup = require('./dns');
var extend = require('util')._extend;
var allowDnsCache = true;
var protocols = ['host', 'rule', 'head', 'req', 'reqHeaders', 'reqBody', 'prependReq', 'appendReq',
                 'res', 'resHeaders', 'resBody', 'prependRes', 'appendRes', 'weinre', 'filter'];
var rules = {};
protocols.forEach(function(protocol) {
	rules[protocol] = [];
});

function parse(text) {
	protocols.forEach(function(protocol) {
		rules[protocol] = [];
	});
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^(?:http|ws)s?:\/\//.test(pattern))) {
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else if (!util.isRegExp(pattern) && util.isRegExp(line[1])) {
		parseRule(line[1], pattern, raw);
	} else {
		parseRule(pattern, line[1], raw);
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var list;
	if (net.isIP(matcher)) {
		list = rules.host;
	} else {
		var protocol = matcher.match(/^([\w\-]+):\/\//);
		list = rules[protocol && protocol[1]] || rules.rule;
	}
	list.push({
		isRegExp: isRegExp,
		protocol: isRegExp ? null : util.getProtocol(pattern),
		pattern: isRegExp ? pattern : formatUrl(pattern),
		matcher: matcher,
		raw: raw,
		rawPattern: pattern
	});
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!options.hostname || (!util.isWebsocketProtocol(options.protocol)
			&& !util.isWebProtocol(options.protocol))) {

		return callback(null, null);
	}

	var host = !resolveFilter(_url).host && getRule(_url, rules.host);
	host ? callback(null, host.matcher)
			: lookup(options.hostname, callback, allowDnsCache);
}

function formatUrl(pattern) {
	var queryString = '';
	var queryIndex = pattern.indexOf('?');
	if (queryIndex != -1) {
		queryString = pattern.substring(queryIndex);
		pattern = pattern.substring(0, queryIndex);
	}
	return (pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern) + queryString;
}

exports.resolveHost = resolveHost;

function getRule(url, list) {
	var rule = _getRule(url, list);
	//支持域名匹配
	if (!rule && (rule = _getRule(url.replace(/^((?:http|ws)s?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), list))
			&& util.removeProtocol(rule.rawPattern, true).indexOf('/') != -1) {
		rule = null;
	}
	if (rule) {
		url = rule.url;
		var index = url.indexOf('://') + 3;
		var protocol = url.substring(0, index);
		url = url.substring(index);
		var key = getKey(url);
		if (key) {
			rule.key = key;
		}
		var value = key && values.get(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(url)) !== false) {
			rule.value = protocol + value;
		} else if ((value = getPath(url)) !== false) {
			rule.path = protocol + value;
			rule.files = getFiles(rule.path);
		}
	}

	return rule;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, list) {
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}

	var _url = url.replace(/[?#].*$/, '');
	for (var i = 0, rule; rule = list[i]; i++) {
		var pattern = rule.pattern;
		var matcher = setProtocol(rule.matcher, url);
		var files = getFiles(matcher);
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				function replaceSubMatcher(url) {
					return url.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					});
				}

				return extend({
					url: replaceSubMatcher(matcher),
					files: files && files.map(function(file) {
						return replaceSubMatcher(file);
					})
				}, rule);;
			}
		} else if (url.indexOf(pattern = setProtocol(pattern, url)) === 0) {
			var len = pattern.length;
			if (pattern.indexOf('?') != -1) {
				var filePath = url.substring(len);
				return extend({
					url: matcher + (matcher.indexOf('?') == -1 && filePath.indexOf('?') == -1 ? '?' : '') + filePath,
					files: files && files.map(function(file) {
						return file + (file.indexOf('?') == -1 && filePath.indexOf('?') == -1 ? '?' : '') + filePath;
					})
				}, rule);
			}

			if (pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
				var filePath = url.substring(len);
				return extend({
					files: files && files.map(function(file) {
						return join(file, filePath);
					}),
					url: join(matcher, filePath)
				}, rule);
			}
		}
	}
}

function getFiles(path) {

	return /^x?(file|tpl|jsonp|dust):/.test(path) ? util.removeProtocol(path, true).split('|') : null;
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\' || ch == '?';
}

/**
 * first: xxxx, xxxx?, xxx?xxxx
 * second: ?xxx, xxx?xxxx
 * @param first
 * @param second
 * @returns
 */
function join(first, second) {
	if (!first || !second) {
		return first + second;
	}

	var firstIndex = first.indexOf('?');
	var secondIndex = second.indexOf('?');
	var firstQuery = '';
	var secondQuery = '';

	if (firstIndex != -1) {
		firstQuery = first.substring(firstIndex);
		first = first.substring(0, firstIndex);
	}

	if (secondIndex != -1) {
		secondQuery = second.substring(secondIndex);
		second = second.substring(0, secondIndex);
	}

	var query = firstQuery && secondQuery ? firstQuery + second.substring(1) : (firstQuery || secondQuery);
	var url;
	var lastIndex = first.length - 1;
	var startWithSep = isPathSeparator(second[0]);
	if (isPathSeparator(first[lastIndex])) {
		url = startWithSep ? first.substring(0, lastIndex) + second : first + second;
	} else {
		url = first + (startWithSep ? '' : '/') + second;
	}

	return url + query;
}

function resolveFilter(url) {
	var filter = getRule(url, rules.filter);
	var result = {};
	if (filter) {
		util.removeProtocol(util.rule.getMatcher(filter), true)
				.split('|').forEach(function(action) {
			result[action] = true;
		});
	}

	return result;
}

exports.resolveFilter = resolveFilter;

exports.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rule;
	var _rules = {};
	var filter = resolveFilter(url);
	protocols.forEach(function(protocol) {
		if (protocol != 'filter' && !filter[protocol] && (rule = getRule(url, rules[protocol]))) {
			_rules[protocol] = rule;
		}
	});

	return _rules;
};

exports.disableDnsCache = function() {
	allowDnsCache = false;
};
<|endoftext|>


<commit_before>'use strict';
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const os = require('os');
const assert = require('assert');
const _ = require('lodash');
const util = require('util');
const {EventEmitter} = require('events');
const helpers = require('.');
const RunResult = require('./run-result');

/**
 * Wrap callback so it can't get called twice
 */
const callbackWrapper = (done) => {
  let callbackHandled = false;
  const callback = (err) => {
    if (!callbackHandled) {
      callbackHandled = true;
      done(err);
    }
  };

  return callback;
};

/**
 * This class provide a run context object to façade the complexity involved in setting
 * up a generator for testing
 * @constructor
 * @param {String|Function} Generator - Namespace or generator constructor. If the later
 *                                       is provided, then namespace is assumed to be
 *                                       'gen:test' in all cases
 * @param {Object} [settings]
 * @param {Boolean} [settings.tmpdir] - Automatically run this generator in a tmp dir
 * @param {String} [settings.resolved] - File path to the generator (only used if Generator is a constructor)
 * @param {String} [settings.namespace='gen:test'] - Namespace (only used if Generator is a constructor)
 * @param {String} [settings.runEnvironment=false] - Require the run context to call run.
 * @param {Object} [envOptions] - Options to be passed to environment.
 * @return {this}
 */

function RunContext(Generator, settings, envOptions = {}) {
  this._asyncHolds = 0;
  this.ran = false;
  this.inDirSet = false;
  this.args = [];
  this.options = {};
  this.answers = {};
  this.localConfig = null;
  this.dependencies = [];
  this.Generator = Generator;
  this.inDirCallbacks = [];
  this.lookups = [];
  this.settings = {
    namespace: 'gen:test',
    runEnvironment: false,
    ...settings
  };
  this.envOptions = envOptions;

  this.withOptions({
    force: true,
    skipCache: true,
    skipInstall: true
  });
  this.oldCwd = this.settings.oldCwd;
  if (this.settings.cwd) {
    this.cd(this.settings.cwd);
  }

  if (!this.settings.runEnvironment) {
    setTimeout(this._run.bind(this), 10);
  }
}

util.inherits(RunContext, EventEmitter);

/**
 * Hold the execution until the returned callback is triggered
 * @return {Function} Callback to notify the normal execution can resume
 */

RunContext.prototype.async = function () {
  this._asyncHolds++;

  return function () {
    this._asyncHolds--;
    this._run();
  }.bind(this);
};

/**
 * Build the generator and the environment.
 * @return {RunContext|false} this
 */
RunContext.prototype.build = function (async = false) {
  if (!this.inDirSet && this.settings.tmpdir !== false) {
    this.inTmpDir();
  }

  if (this._asyncHolds !== 0 || this.ran || this.completed) {
    if (async) {
      return false;
    }

    throw new Error('The context is not ready');
  }

  this.ran = true;
  if (this.inDirCallbacks.length > 0) {
    const targetDirectory = path.resolve(this.targetDirectory);
    this.inDirCallbacks.forEach((cb) => cb(targetDirectory));
  }

  this.targetDirectory = this.targetDirectory || process.cwd();

  const envOptions = {...this.envOptions};
  if (this.settings.runEnvironment) {
    envOptions.newErrorHandler = true;
  }

  const testEnv = helpers.createTestEnv(this.envOptions.createEnv, envOptions);
  this.env = this.envCB ? this.envCB(testEnv) || testEnv : testEnv;

  this.lookups.forEach((lookup) => {
    this.env.lookup(lookup);
  });

  helpers.registerDependencies(this.env, this.dependencies);

  let namespace;
  if (typeof this.Generator === 'string') {
    if (this.settings.runEnvironment) {
      namespace = this.Generator;
    } else {
      namespace = this.env.namespace(this.Generator);
      if (namespace !== this.Generator) {
        // Generator is a file path, it should be registered.
        this.env.register(this.Generator);
      }
    }
  } else {
    namespace = this.settings.namespace;
    this.env.registerStub(this.Generator, namespace, this.settings.resolved);
  }

  this.generator = this.env.create(namespace, {
    arguments: this.args,
    options: this.options
  });

  helpers.mockPrompt(this.env, this.answers, this.promptCallback);

  if (this.localConfig) {
    // Only mock local config when withLocalConfig was called
    helpers.mockLocalConfig(this.generator, this.localConfig);
  }

  return this;
};

/**
 * Method called when the context is ready to run the generator
 * @private
 */

RunContext.prototype._run = function () {
  if (this.build(true) === false) return false;

  const endCallback = callbackWrapper((envOrGenerator) => {
    helpers.restorePrompt(envOrGenerator);
    this.emit('end');
    this.completed = true;
  });

  const callback = callbackWrapper((err) => {
    helpers.restorePrompt(this.env);
    this.errored = true;
    this.emit('error', err);
  });

  // Yeoman-generator >=5.0.0 doesn't emits error on itself, it emits on the environment.
  this.generator.on('error', callback);

  this.env.on('error', callback);
  this.env.runLoop.once('end', () => endCallback(this.generator));
  this.emit('ready', this.generator);

  this.generator.run().catch(callback);
  return true;
};

/**
 * Run the generator on the environment and returns the promise.
 * @return {Promise} Promise
 */
RunContext.prototype.run = function () {
  if (!this.settings.runEnvironment) {
    throw new Error('Should be called with runEnvironment option');
  }

  if (!this.ran) {
    this.build();
  }

  return this.env
    .runGenerator(this.generator)
    .then(
      () =>
        new RunResult({
          env: this.env,
          fs: this.env.fs,
          settings: {
            ...this.settings
          },
          oldCwd: this.oldCwd,
          cwd: this.targetDirectory,
          envOptions: this.envOptions
        })
    )
    .finally(() => {
      helpers.restorePrompt(this.env);
    });
};

/**
 * Return a promise representing the generator run process
 * @return {Promise} Promise resolved on end or rejected on error
 */
RunContext.prototype.toPromise = function () {
  if (this.settings.runEnvironment) {
    throw new Error('RunContext with runEnvironment uses promises by default');
  }

  return new Promise(
    function (resolve, reject) {
      this.on(
        'end',
        function () {
          resolve(this.targetDirectory);
        }.bind(this)
      );
      this.on('error', reject);
    }.bind(this)
  );
};

/**
 * Promise `.then()` duck typing
 * @return {Promise}
 */
RunContext.prototype.then = function () {
  const promise = this.toPromise();
  return promise.then(...arguments);
};

/**
 * Promise `.catch()` duck typing
 * @return {Promise}
 */
RunContext.prototype.catch = function () {
  const promise = this.toPromise();
  return promise.catch(...arguments);
};

/**
 * Set the target directory.
 * @private
 * @param  {String} dirPath - Directory path (relative to CWD). Prefer passing an absolute
 *                            file path for predictable results
 * @return {this} run context instance
 */

RunContext.prototype.setDir = function (dirPath, tmpdir) {
  if (this.inDirSet) {
    this.completed = true;
    throw new Error('Test directory has already been set.');
  }

  if (tmpdir !== undefined) {
    this.settings.tmpdir = tmpdir;
  }

  this.oldCwd = this.oldCwd || process.cwd();

  this.inDirSet = true;
  this.targetDirectory = dirPath;
  return this;
};

/**
 * Clean the provided directory, then change directory into it
 * @param  {String} dirPath - Directory path (relative to CWD). Prefer passing an absolute
 *                            file path for predictable results
 * @param {Function} [cb] - callback who'll receive the folder path as argument
 * @return {this} run context instance
 */

RunContext.prototype.inDir = function (dirPath, cb) {
  this.setDir(dirPath, true);
  helpers.testDirectory(
    dirPath,
    (cb || _.noop).bind(this, path.resolve(dirPath))
  );
  return this;
};

/**
 * Register an callback to prepare the destination folder.
 * @param {Function} [cb] - callback who'll receive the folder path as argument
 * @return {this} run context instance
 */

RunContext.prototype.doInDir = function (cb) {
  this.inDirCallbacks.push(cb);
  return this;
};

/**
 * Change directory without deleting directory content.
 * @param  {String} dirPath - Directory path (relative to CWD). Prefer passing an absolute
 *                            file path for predictable results
 * @return {this} run context instance
 */
RunContext.prototype.cd = function (dirPath) {
  dirPath = path.resolve(dirPath);
  this.setDir(dirPath, false);
  try {
    process.chdir(dirPath);
  } catch (error) {
    this.completed = true;
    throw new Error(error.message + ' ' + dirPath);
  }

  return this;
};

/**
 * Creates a temporary directory and change the CWD into it
 *
 * This method is called automatically when creating a RunContext. Only use it if you need
 * to use the callback.
 *
 * @param {Function} [cb] - callback who'll receive the folder path as argument
 * @return {this} run context instance
 */
RunContext.prototype.inTmpDir = function (cb) {
  return this.inDir(
    path.join(os.tmpdir(), crypto.randomBytes(20).toString('hex')),
    cb
  );
};

/**
 * Create an environment
 *
 * This method is called automatically when creating a RunContext. Only use it if you need
 * to use the callback.
 *
 * @param {Function} [cb] - callback who'll receive the folder path as argument
 * @return {this} run context instance
 */
RunContext.prototype.withEnvironment = function (cb) {
  this.envCB = cb;
  return this;
};

/**
 * Run lookup on the environment.
 *
 * @param {Object|Array} [lookups] - lookup to run.
 * @return {this} run context instance.
 */
RunContext.prototype.withLookups = function (lookups) {
  lookups = Array.isArray(lookups) ? lookups : [lookups];
  this.lookups = this.lookups.concat(lookups);
  return this;
};

/**
 * Clean the directory used for tests inside inDir/inTmpDir
 * @param  {Boolean} force - force directory cleanup for not tmpdir
 */
RunContext.prototype.cleanTestDirectory = function (force = false) {
  if (!force && this.settings.tmpdir === false) {
    throw new Error('Cleanup test dir called with false tmpdir option.');
  }

  if (this.targetDirectory && fs.existsSync(this.targetDirectory)) {
    fs.rmdirSync(this.targetDirectory, {recursive: true});
  }
};

/**
 * Provide arguments to the run context
 * @param  {String|Array} args - command line arguments as Array or space separated string
 * @return {this}
 */

RunContext.prototype.withArguments = function (args) {
  const argsArray = _.isString(args) ? args.split(' ') : args;
  assert(
    _.isArray(argsArray),
    'args should be either a string separated by spaces or an array'
  );
  this.args = this.args.concat(argsArray);
  return this;
};

/**
 * Provide options to the run context
 * @param  {Object} options - command line options (e.g. `--opt-one=foo`)
 * @return {this}
 */

RunContext.prototype.withOptions = function (options) {
  // Add options as both kebab and camel case. This is to stay backward compatibles with
  // the switch we made to meow for options parsing.
  Object.keys(options).forEach(function (key) {
    options[_.camelCase(key)] = options[key];
    options[_.kebabCase(key)] = options[key];
  });

  this.options = _.extend(this.options, options);
  return this;
};

/**
 * Mock the prompt with dummy answers
 * @param  {Object} answers - Answers to the prompt questions
 * @return {this}
 */

RunContext.prototype.withPrompts = function (answers, callback) {
  this.answers = _.extend(this.answers, answers);
  this.promptCallback = callback;
  return this;
};

/**
 * Provide dependent generators
 * @param {Array} dependencies - paths to the generators dependencies
 * @return {this}
 * @example
 * var angular = new RunContext('../../app');
 * angular.withGenerators([
 *   '../../common',
 *   '../../controller',
 *   '../../main',
 *   [helpers.createDummyGenerator(), 'testacular:app']
 * ]);
 * angular.on('end', function () {
 *   // assert something
 * });
 */

RunContext.prototype.withGenerators = function (dependencies) {
  assert(_.isArray(dependencies), 'dependencies should be an array');
  this.dependencies = this.dependencies.concat(dependencies);
  return this;
};

/**
 * Mock the local configuration with the provided config
 * @param  {Object} localConfig - should look just like if called config.getAll()
 * @return {this}
 */
RunContext.prototype.withLocalConfig = function (localConfig) {
  assert(_.isObject(localConfig), 'config should be an object');
  this.localConfig = localConfig;
  return this;
};

module.exports = RunContext;
<commit_msg>Set newErrorHandler as default.<commit_after>'use strict';
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const os = require('os');
const assert = require('assert');
const _ = require('lodash');
const util = require('util');
const {EventEmitter} = require('events');
const helpers = require('.');
const RunResult = require('./run-result');

/**
 * Wrap callback so it can't get called twice
 */
const callbackWrapper = (done) => {
  let callbackHandled = false;
  const callback = (err) => {
    if (!callbackHandled) {
      callbackHandled = true;
      done(err);
    }
  };

  return callback;
};

/**
 * This class provide a run context object to façade the complexity involved in setting
 * up a generator for testing
 * @constructor
 * @param {String|Function} Generator - Namespace or generator constructor. If the later
 *                                       is provided, then namespace is assumed to be
 *                                       'gen:test' in all cases
 * @param {Object} [settings]
 * @param {Boolean} [settings.tmpdir] - Automatically run this generator in a tmp dir
 * @param {String} [settings.resolved] - File path to the generator (only used if Generator is a constructor)
 * @param {String} [settings.namespace='gen:test'] - Namespace (only used if Generator is a constructor)
 * @param {String} [settings.runEnvironment=false] - Require the run context to call run.
 * @param {Object} [envOptions] - Options to be passed to environment.
 * @return {this}
 */

function RunContext(Generator, settings, envOptions = {}) {
  this._asyncHolds = 0;
  this.ran = false;
  this.inDirSet = false;
  this.args = [];
  this.options = {};
  this.answers = {};
  this.localConfig = null;
  this.dependencies = [];
  this.Generator = Generator;
  this.inDirCallbacks = [];
  this.lookups = [];
  this.settings = {
    namespace: 'gen:test',
    runEnvironment: false,
    ...settings
  };
  this.envOptions = envOptions;

  this.withOptions({
    force: true,
    skipCache: true,
    skipInstall: true
  });
  this.oldCwd = this.settings.oldCwd;
  if (this.settings.cwd) {
    this.cd(this.settings.cwd);
  }

  if (!this.settings.runEnvironment) {
    setTimeout(this._run.bind(this), 10);
  }
}

util.inherits(RunContext, EventEmitter);

/**
 * Hold the execution until the returned callback is triggered
 * @return {Function} Callback to notify the normal execution can resume
 */

RunContext.prototype.async = function () {
  this._asyncHolds++;

  return function () {
    this._asyncHolds--;
    this._run();
  }.bind(this);
};

/**
 * Build the generator and the environment.
 * @return {RunContext|false} this
 */
RunContext.prototype.build = function (async = false) {
  if (!this.inDirSet && this.settings.tmpdir !== false) {
    this.inTmpDir();
  }

  if (this._asyncHolds !== 0 || this.ran || this.completed) {
    if (async) {
      return false;
    }

    throw new Error('The context is not ready');
  }

  this.ran = true;
  if (this.inDirCallbacks.length > 0) {
    const targetDirectory = path.resolve(this.targetDirectory);
    this.inDirCallbacks.forEach((cb) => cb(targetDirectory));
  }

  this.targetDirectory = this.targetDirectory || process.cwd();

  const testEnv = helpers.createTestEnv(this.envOptions.createEnv, {
    ...this.envOptions,
    newErrorHandler: true
  });
  this.env = this.envCB ? this.envCB(testEnv) || testEnv : testEnv;

  this.lookups.forEach((lookup) => {
    this.env.lookup(lookup);
  });

  helpers.registerDependencies(this.env, this.dependencies);

  let namespace;
  if (typeof this.Generator === 'string') {
    if (this.settings.runEnvironment) {
      namespace = this.Generator;
    } else {
      namespace = this.env.namespace(this.Generator);
      if (namespace !== this.Generator) {
        // Generator is a file path, it should be registered.
        this.env.register(this.Generator);
      }
    }
  } else {
    namespace = this.settings.namespace;
    this.env.registerStub(this.Generator, namespace, this.settings.resolved);
  }

  this.generator = this.env.create(namespace, {
    arguments: this.args,
    options: this.options
  });

  helpers.mockPrompt(this.env, this.answers, this.promptCallback);

  if (this.localConfig) {
    // Only mock local config when withLocalConfig was called
    helpers.mockLocalConfig(this.generator, this.localConfig);
  }

  return this;
};

/**
 * Method called when the context is ready to run the generator
 * @private
 */

RunContext.prototype._run = function () {
  if (this.build(true) === false) return false;

  const endCallback = callbackWrapper((envOrGenerator) => {
    helpers.restorePrompt(envOrGenerator);
    this.emit('end');
    this.completed = true;
  });

  const callback = callbackWrapper((err) => {
    helpers.restorePrompt(this.env);
    this.errored = true;
    this.emit('error', err);
  });

  // Yeoman-generator >=5.0.0 doesn't emits error on itself, it emits on the environment.
  this.generator.on('error', callback);

  this.env.on('error', callback);
  this.env.runLoop.once('end', () => endCallback(this.generator));
  this.emit('ready', this.generator);

  this.generator.run().catch(callback);
  return true;
};

/**
 * Run the generator on the environment and returns the promise.
 * @return {Promise} Promise
 */
RunContext.prototype.run = function () {
  if (!this.settings.runEnvironment) {
    throw new Error('Should be called with runEnvironment option');
  }

  if (!this.ran) {
    this.build();
  }

  return this.env
    .runGenerator(this.generator)
    .then(
      () =>
        new RunResult({
          env: this.env,
          fs: this.env.fs,
          settings: {
            ...this.settings
          },
          oldCwd: this.oldCwd,
          cwd: this.targetDirectory,
          envOptions: this.envOptions
        })
    )
    .finally(() => {
      helpers.restorePrompt(this.env);
    });
};

/**
 * Return a promise representing the generator run process
 * @return {Promise} Promise resolved on end or rejected on error
 */
RunContext.prototype.toPromise = function () {
  if (this.settings.runEnvironment) {
    throw new Error('RunContext with runEnvironment uses promises by default');
  }

  return new Promise(
    function (resolve, reject) {
      this.on(
        'end',
        function () {
          resolve(this.targetDirectory);
        }.bind(this)
      );
      this.on('error', reject);
    }.bind(this)
  );
};

/**
 * Promise `.then()` duck typing
 * @return {Promise}
 */
RunContext.prototype.then = function () {
  const promise = this.toPromise();
  return promise.then(...arguments);
};

/**
 * Promise `.catch()` duck typing
 * @return {Promise}
 */
RunContext.prototype.catch = function () {
  const promise = this.toPromise();
  return promise.catch(...arguments);
};

/**
 * Set the target directory.
 * @private
 * @param  {String} dirPath - Directory path (relative to CWD). Prefer passing an absolute
 *                            file path for predictable results
 * @return {this} run context instance
 */

RunContext.prototype.setDir = function (dirPath, tmpdir) {
  if (this.inDirSet) {
    this.completed = true;
    throw new Error('Test directory has already been set.');
  }

  if (tmpdir !== undefined) {
    this.settings.tmpdir = tmpdir;
  }

  this.oldCwd = this.oldCwd || process.cwd();

  this.inDirSet = true;
  this.targetDirectory = dirPath;
  return this;
};

/**
 * Clean the provided directory, then change directory into it
 * @param  {String} dirPath - Directory path (relative to CWD). Prefer passing an absolute
 *                            file path for predictable results
 * @param {Function} [cb] - callback who'll receive the folder path as argument
 * @return {this} run context instance
 */

RunContext.prototype.inDir = function (dirPath, cb) {
  this.setDir(dirPath, true);
  helpers.testDirectory(
    dirPath,
    (cb || _.noop).bind(this, path.resolve(dirPath))
  );
  return this;
};

/**
 * Register an callback to prepare the destination folder.
 * @param {Function} [cb] - callback who'll receive the folder path as argument
 * @return {this} run context instance
 */

RunContext.prototype.doInDir = function (cb) {
  this.inDirCallbacks.push(cb);
  return this;
};

/**
 * Change directory without deleting directory content.
 * @param  {String} dirPath - Directory path (relative to CWD). Prefer passing an absolute
 *                            file path for predictable results
 * @return {this} run context instance
 */
RunContext.prototype.cd = function (dirPath) {
  dirPath = path.resolve(dirPath);
  this.setDir(dirPath, false);
  try {
    process.chdir(dirPath);
  } catch (error) {
    this.completed = true;
    throw new Error(error.message + ' ' + dirPath);
  }

  return this;
};

/**
 * Creates a temporary directory and change the CWD into it
 *
 * This method is called automatically when creating a RunContext. Only use it if you need
 * to use the callback.
 *
 * @param {Function} [cb] - callback who'll receive the folder path as argument
 * @return {this} run context instance
 */
RunContext.prototype.inTmpDir = function (cb) {
  return this.inDir(
    path.join(os.tmpdir(), crypto.randomBytes(20).toString('hex')),
    cb
  );
};

/**
 * Create an environment
 *
 * This method is called automatically when creating a RunContext. Only use it if you need
 * to use the callback.
 *
 * @param {Function} [cb] - callback who'll receive the folder path as argument
 * @return {this} run context instance
 */
RunContext.prototype.withEnvironment = function (cb) {
  this.envCB = cb;
  return this;
};

/**
 * Run lookup on the environment.
 *
 * @param {Object|Array} [lookups] - lookup to run.
 * @return {this} run context instance.
 */
RunContext.prototype.withLookups = function (lookups) {
  lookups = Array.isArray(lookups) ? lookups : [lookups];
  this.lookups = this.lookups.concat(lookups);
  return this;
};

/**
 * Clean the directory used for tests inside inDir/inTmpDir
 * @param  {Boolean} force - force directory cleanup for not tmpdir
 */
RunContext.prototype.cleanTestDirectory = function (force = false) {
  if (!force && this.settings.tmpdir === false) {
    throw new Error('Cleanup test dir called with false tmpdir option.');
  }

  if (this.targetDirectory && fs.existsSync(this.targetDirectory)) {
    fs.rmdirSync(this.targetDirectory, {recursive: true});
  }
};

/**
 * Provide arguments to the run context
 * @param  {String|Array} args - command line arguments as Array or space separated string
 * @return {this}
 */

RunContext.prototype.withArguments = function (args) {
  const argsArray = _.isString(args) ? args.split(' ') : args;
  assert(
    _.isArray(argsArray),
    'args should be either a string separated by spaces or an array'
  );
  this.args = this.args.concat(argsArray);
  return this;
};

/**
 * Provide options to the run context
 * @param  {Object} options - command line options (e.g. `--opt-one=foo`)
 * @return {this}
 */

RunContext.prototype.withOptions = function (options) {
  // Add options as both kebab and camel case. This is to stay backward compatibles with
  // the switch we made to meow for options parsing.
  Object.keys(options).forEach(function (key) {
    options[_.camelCase(key)] = options[key];
    options[_.kebabCase(key)] = options[key];
  });

  this.options = _.extend(this.options, options);
  return this;
};

/**
 * Mock the prompt with dummy answers
 * @param  {Object} answers - Answers to the prompt questions
 * @return {this}
 */

RunContext.prototype.withPrompts = function (answers, callback) {
  this.answers = _.extend(this.answers, answers);
  this.promptCallback = callback;
  return this;
};

/**
 * Provide dependent generators
 * @param {Array} dependencies - paths to the generators dependencies
 * @return {this}
 * @example
 * var angular = new RunContext('../../app');
 * angular.withGenerators([
 *   '../../common',
 *   '../../controller',
 *   '../../main',
 *   [helpers.createDummyGenerator(), 'testacular:app']
 * ]);
 * angular.on('end', function () {
 *   // assert something
 * });
 */

RunContext.prototype.withGenerators = function (dependencies) {
  assert(_.isArray(dependencies), 'dependencies should be an array');
  this.dependencies = this.dependencies.concat(dependencies);
  return this;
};

/**
 * Mock the local configuration with the provided config
 * @param  {Object} localConfig - should look just like if called config.getAll()
 * @return {this}
 */
RunContext.prototype.withLocalConfig = function (localConfig) {
  assert(_.isObject(localConfig), 'config should be an object');
  this.localConfig = localConfig;
  return this;
};

module.exports = RunContext;
<|endoftext|>


<commit_before>/* jshint globalstrict: true, unused: false */
/* global cmds: false, TIME: false, DEBUG: false */
'use strict';

var i32 = function i32(n) {
    return n|0;
};

var Simulation = function() {};
Simulation.prototype.lookupOffset = function(id) {
    //print(this.name + ': ' + id + ' resolving (' + this._shift + ')');
    if (id === 'time')
        return 0;
    if (id[0] === '.')
        id = id.substr(1);
    if (id in this.offsets)
        return this._shift + this.offsets[id];
    var parts = id.split('.');
    if (parts.length === 1 && id === "" && this.name in this.offsets)
        return this._shift + this.offsets[this.name];
    var nextSim = this.modules[parts[0]];
    if (!nextSim)
        return -1;
    return nextSim.lookupOffset(parts.slice(1).join('.'));
};
Simulation.prototype.resolveAllSymbolicRefs = function() {
    var n, ctx;
    for (n in this.symRefs) {
        if (this.symRefs[n][0] === '.')
            ctx = main;
        else
            ctx = this.parent;
        this.ref[n] = ctx.lookupOffset(this.symRefs[n]);
        //print(this.name + ': ' + n + ' resolved to ' + this.ref[n]);
    }
    for (n in this.modules)
        this.modules[n].resolveAllSymbolicRefs();
};
Simulation.prototype.varNames = function() {
    var result = Object.keys(this.offsets).slice();
    var ids;
    for (v in this.modules) {
        ids = this.modules[v].varNames().map(function(n) {
            return v + '.' + n;
        });
        result = result.concat(ids);
    }
    if (this.name === 'main')
        result.push('time');

    return result;
}
Simulation.prototype.getNVars = function() {
    var nVars = Object.keys(this.offsets).length;
    var n;
    for (n in this.modules)
        nVars += this.modules[n].getNVars();
    // if we're main, claim time
    if (this.name === 'main')
        nVars++;
    return nVars;
};
Simulation.prototype.reset = function() {
    var timespec = this.timespec;
    var nSteps = i32((timespec.stop - timespec.start)/timespec.dt + 1);

    this.stepNum = 0;

    this.slab = new Float64Array(this.nVars*(nSteps));

    var curr = this.curr();
    curr[TIME] = timespec.start;
    this.saveEvery = Math.max(1, i32(timespec.savestep/timespec.dt+0.5));

    this.calcInitial(this.timespec.dt, curr);
};
Simulation.prototype.runTo = function(endTime) {
    var dt = this.timespec.dt;

    var curr = this.curr();
    var next = this.slab.subarray((this.stepNum+1)*this.nVars,
                                  (this.stepNum+2)*this.nVars);

    while (curr[TIME] <= endTime) {
        this.calcFlows(dt, curr);
        this.calcStocks(dt, curr, next);

        this.stepNum++;

        next[TIME] = curr[TIME] + dt;

        curr = next;
        next = this.slab.subarray((this.stepNum+1)*this.nVars,
                                  (this.stepNum+2)*this.nVars);
    }
};
Simulation.prototype.runToEnd = function() {
    return this.runTo(this.timespec.stop + 0.5*this.timespec.dt);
};
Simulation.prototype.curr = function() {
    return this.slab.subarray((this.stepNum)*this.nVars,
                              (this.stepNum+1)*this.nVars);
};
Simulation.prototype.setValue = function(name, value) {
    var off = this.lookupOffset(name);
    if (off === -1)
        return;
    this.curr()[off] = value;
};
Simulation.prototype.value = function(name) {
    var off = this.lookupOffset(name);
    if (off === -1)
        return;
    var saveNum = i32(this.stepNum/this.saveEvery);
    var slabOff = this.nVars*(saveNum*this.saveEvery);
    return this.slab.subarray(slabOff, slabOff + this.nVars)[off];
};
Simulation.prototype.series = function(name) {
    var saveNum = i32((this.stepNum-1)/this.saveEvery)+1;
    var time = new Float64Array(saveNum);
    var values = new Float64Array(saveNum);
    var off = this.lookupOffset(name);
    if (off === -1)
        return;
    //print(this.name + ': ' + name + ' resolved to ' + off);
    var i, curr;
    for (i=0; i < time.length; i++) {
        curr = this.slab.subarray((i*this.saveEvery)*this.nVars,
                                  (i*this.saveEvery+1)*this.nVars);
        time[i] = curr[0];
        values[i] = curr[off];
    }
    return {
        'name': name,
        'time': time,
        'values': values,
    };
};

function handleMessage(e) {
    var id = e.data[0];
    var cmd = e.data[1];
    var args = e.data.slice(2);
    var result;

    if (cmds.hasOwnProperty(cmd))
        result = cmds[cmd].apply(null, args);
    else
        result = [null, 'unknown command "' + cmd + '"'];

    if (!Array.isArray(result))
        result = [null, 'no result for [' + e.data.join(', ') + ']'];

    // TODO(bp) look into transferrable objects
    var msg = [id, result];
    postMessage(msg);
}

function initCmds(main) {
    return {
        'reset': function() {
            main.reset();
            return ['ok', null];
        },
        'set_val': function(name, val) {
            main.setValue(name, val);
            return ['ok', null];
        },
        'get_val': function() {
            var result = {};
            var i;
            for (i=0; i < arguments.length; i++)
                result[arguments[i]] = main.value(arguments[i]);
            return [result, null];
        },
        'get_series': function() {
            var result = {};
            var i;
            for (i=0; i<arguments.length; i++)
                result[arguments[i]] = main.series(arguments[i]);
            return [result, null];
        },
        'run_to': function(time) {
            main.runTo(time);
            return [main.value('time'), null];
        },
        'run_to_end': function() {
            main.runToEnd();
            return [main.value('time'), null];
        },
    };
}

function lookup(table, index) {
    var size = table.x.length;
    if (size === 0)
        return NaN;

    var x = table.x;
    var y = table.y;

    if (index <= x[0])
        return y[0];
    else if (index >= x[size - 1])
        return y[size - 1];

    // binary search seems to be the most appropriate choice here.
    var low = 0;
    var high = size;
    var mid;
    while (low < high) {
        mid = Math.floor(low + (high - low)/2);
        if (x[mid] < index) {
            low = mid + 1;
        } else {
            high = mid;
        }
    }

    var i = low;
    if (x[i] === index) {
        return y[i];
    } else {
        // slope = deltaY/deltaX
        var slope = (y[i] - y[i-1]) / (x[i] - x[i-1]);
        // y = m*x + b
        return (index - x[i-1])*slope + y[i-1];
    }
}

function max(a, b) {
    a = +a;
    b = +b;
    return a > b ? a : b;
}

function min(a, b) {
    a = +a;
    b = +b;
    return a < b ? a : b;
}

function pulse(dt, time, volume, firstPulse, interval) {
    if (time < firstPulse)
        return 0;
    var nextPulse = firstPulse;
    while (time >= nextPulse) {
        if (time < nextPulse + dt)
            return volume/dt;
        else if (interval <= 0.0)
            break;
        else
            nextPulse += interval;
    }
    return 0;
}
<commit_msg>runtime: only allocate space for what we are saving<commit_after>/* jshint globalstrict: true, unused: false */
/* global cmds: false, TIME: false, DEBUG: false */
'use strict';

var i32 = function i32(n) {
    return n|0;
};

var Simulation = function() {};
Simulation.prototype.lookupOffset = function(id) {
    //print(this.name + ': ' + id + ' resolving (' + this._shift + ')');
    if (id === 'time')
        return 0;
    if (id[0] === '.')
        id = id.substr(1);
    if (id in this.offsets)
        return this._shift + this.offsets[id];
    var parts = id.split('.');
    if (parts.length === 1 && id === "" && this.name in this.offsets)
        return this._shift + this.offsets[this.name];
    var nextSim = this.modules[parts[0]];
    if (!nextSim)
        return -1;
    return nextSim.lookupOffset(parts.slice(1).join('.'));
};
Simulation.prototype.resolveAllSymbolicRefs = function() {
    var n, ctx;
    for (n in this.symRefs) {
        if (this.symRefs[n][0] === '.')
            ctx = main;
        else
            ctx = this.parent;
        this.ref[n] = ctx.lookupOffset(this.symRefs[n]);
        //print(this.name + ': ' + n + ' resolved to ' + this.ref[n]);
    }
    for (n in this.modules)
        this.modules[n].resolveAllSymbolicRefs();
};
Simulation.prototype.varNames = function() {
    var result = Object.keys(this.offsets).slice();
    var ids;
    for (v in this.modules) {
        ids = this.modules[v].varNames().map(function(n) {
            return v + '.' + n;
        });
        result = result.concat(ids);
    }
    if (this.name === 'main')
        result.push('time');

    return result;
}
Simulation.prototype.getNVars = function() {
    var nVars = Object.keys(this.offsets).length;
    var n;
    for (n in this.modules)
        nVars += this.modules[n].getNVars();
    // if we're main, claim time
    if (this.name === 'main')
        nVars++;
    return nVars;
};
Simulation.prototype.reset = function() {
    var timespec = this.timespec;
    var nSaveSteps = i32((timespec.stop - timespec.start)/timespec.savestep + 1);

    this.stepNum = 0;

    this.slab = new Float64Array(this.nVars*(nSaveSteps + 1));

    var curr = this.curr();
    curr[TIME] = timespec.start;
    this.saveEvery = Math.max(1, i32(timespec.savestep/timespec.dt+0.5));

    this.calcInitial(this.timespec.dt, curr);
};
Simulation.prototype.runTo = function(endTime) {
    var dt = this.timespec.dt;

    var curr = this.curr();
    var next = this.slab.subarray((this.stepNum+1)*this.nVars,
                                  (this.stepNum+2)*this.nVars);

    while (curr[TIME] <= endTime) {
        this.calcFlows(dt, curr);
        this.calcStocks(dt, curr, next);

        next[TIME] = curr[TIME] + dt;

        if (this.stepNum++ % this.saveEvery !== 0) {
            curr.set(next);
        } else {
            curr = next;
            next = this.slab.subarray((i32(this.stepNum/this.saveEvery)+1)*this.nVars,
                                      (i32(this.stepNum/this.saveEvery)+2)*this.nVars);
        }
    }
};
Simulation.prototype.runToEnd = function() {
    return this.runTo(this.timespec.stop + 0.5*this.timespec.dt);
};
Simulation.prototype.curr = function() {
    return this.slab.subarray((this.stepNum)*this.nVars,
                              (this.stepNum+1)*this.nVars);
};
Simulation.prototype.setValue = function(name, value) {
    var off = this.lookupOffset(name);
    if (off === -1)
        return;
    this.curr()[off] = value;
};
Simulation.prototype.value = function(name) {
    var off = this.lookupOffset(name);
    if (off === -1)
        return;
    var saveNum = i32(this.stepNum/this.saveEvery);
    var slabOff = this.nVars*saveNum;
    return this.slab.subarray(slabOff, slabOff + this.nVars)[off];
};
Simulation.prototype.series = function(name) {
    var saveNum = i32(this.stepNum/this.saveEvery);
    var time = new Float64Array(saveNum);
    var values = new Float64Array(saveNum);
    var off = this.lookupOffset(name);
    if (off === -1)
        return;
    //print(this.name + ': ' + name + ' resolved to ' + off);
    var i, curr;
    for (i=0; i < time.length; i++) {
        curr = this.slab.subarray(i*this.nVars, (i+1)*this.nVars);
        time[i] = curr[0];
        values[i] = curr[off];
    }
    return {
        'name': name,
        'time': time,
        'values': values,
    };
};

function handleMessage(e) {
    var id = e.data[0];
    var cmd = e.data[1];
    var args = e.data.slice(2);
    var result;

    if (cmds.hasOwnProperty(cmd))
        result = cmds[cmd].apply(null, args);
    else
        result = [null, 'unknown command "' + cmd + '"'];

    if (!Array.isArray(result))
        result = [null, 'no result for [' + e.data.join(', ') + ']'];

    // TODO(bp) look into transferrable objects
    var msg = [id, result];
    postMessage(msg);
}

function initCmds(main) {
    return {
        'reset': function() {
            main.reset();
            return ['ok', null];
        },
        'set_val': function(name, val) {
            main.setValue(name, val);
            return ['ok', null];
        },
        'get_val': function() {
            var result = {};
            var i;
            for (i=0; i < arguments.length; i++)
                result[arguments[i]] = main.value(arguments[i]);
            return [result, null];
        },
        'get_series': function() {
            var result = {};
            var i;
            for (i=0; i<arguments.length; i++)
                result[arguments[i]] = main.series(arguments[i]);
            return [result, null];
        },
        'run_to': function(time) {
            main.runTo(time);
            return [main.value('time'), null];
        },
        'run_to_end': function() {
            main.runToEnd();
            return [main.value('time'), null];
        },
    };
}

function lookup(table, index) {
    var size = table.x.length;
    if (size === 0)
        return NaN;

    var x = table.x;
    var y = table.y;

    if (index <= x[0])
        return y[0];
    else if (index >= x[size - 1])
        return y[size - 1];

    // binary search seems to be the most appropriate choice here.
    var low = 0;
    var high = size;
    var mid;
    while (low < high) {
        mid = Math.floor(low + (high - low)/2);
        if (x[mid] < index) {
            low = mid + 1;
        } else {
            high = mid;
        }
    }

    var i = low;
    if (x[i] === index) {
        return y[i];
    } else {
        // slope = deltaY/deltaX
        var slope = (y[i] - y[i-1]) / (x[i] - x[i-1]);
        // y = m*x + b
        return (index - x[i-1])*slope + y[i-1];
    }
}

function max(a, b) {
    a = +a;
    b = +b;
    return a > b ? a : b;
}

function min(a, b) {
    a = +a;
    b = +b;
    return a < b ? a : b;
}

function pulse(dt, time, volume, firstPulse, interval) {
    if (time < firstPulse)
        return 0;
    var nextPulse = firstPulse;
    while (time >= nextPulse) {
        if (time < nextPulse + dt)
            return volume/dt;
        else if (interval <= 0.0)
            break;
        else
            nextPulse += interval;
    }
    return 0;
}
<|endoftext|>


<commit_before>import {findTagsInObjectURL} from './exif_helper';

var ImageLoader = {};
ImageLoader.load = function(directory, callback, offset, size, sequence) {
    var htmlImagesSrcArray = new Array(size),
        htmlImagesArray = new Array(htmlImagesSrcArray.length),
        i,
        img,
        num;

    if (sequence === false) {
        htmlImagesSrcArray[0] = directory;
    } else {
        for ( i = 0; i < htmlImagesSrcArray.length; i++) {
            num = (offset + i);
            htmlImagesSrcArray[i] = directory + 'image-' + ('00' + num).slice(-3) + '.jpg';
        }
    }
    htmlImagesArray.notLoaded = [];
    htmlImagesArray.addImage = function(image) {
        htmlImagesArray.notLoaded.push(image);
    };
    htmlImagesArray.loaded = function(loadedImg) {
        var notloadedImgs = htmlImagesArray.notLoaded;
        for (var x = 0; x < notloadedImgs.length; x++) {
            if (notloadedImgs[x] === loadedImg) {
                notloadedImgs.splice(x, 1);
                for (var y = 0; y < htmlImagesSrcArray.length; y++) {
                    var imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf('/'));
                    if (loadedImg.src.lastIndexOf(imgName) !== -1) {
                        htmlImagesArray[y] = {img: loadedImg};
                        break;
                    }
                }
                break;
            }
        }
        if (notloadedImgs.length === 0) {
            if (ENV.development) {
                console.log('Images loaded');
            }
            if (sequence === false) {
                findTagsInObjectURL(directory, ['orientation'])
                    .then(tags => {
                        htmlImagesArray[0].tags = tags;
                        callback(htmlImagesArray);
                    }).catch(e => {
                        console.log(e);
                        callback(htmlImagesArray);
                    });
            } else {
                callback(htmlImagesArray);
            }
        }
    };

    for ( i = 0; i < htmlImagesSrcArray.length; i++) {
        img = new Image();
        htmlImagesArray.addImage(img);
        addOnloadHandler(img, htmlImagesArray);
        img.src = htmlImagesSrcArray[i];
    }
};

function addOnloadHandler(img, htmlImagesArray) {
    img.onload = function() {
        htmlImagesArray.loaded(this);
    };
}

export default (ImageLoader);
<commit_msg>eslint --fix image_loader<commit_after>import { findTagsInObjectURL } from './exif_helper';

const ImageLoader = {};
ImageLoader.load = function (directory, callback, offset, size, sequence) {
    const htmlImagesSrcArray = new Array(size);
    const htmlImagesArray = new Array(htmlImagesSrcArray.length);
    let i;
    let img;
    let num;

    if (sequence === false) {
        htmlImagesSrcArray[0] = directory;
    } else {
        for (i = 0; i < htmlImagesSrcArray.length; i++) {
            num = (offset + i);
            htmlImagesSrcArray[i] = `${directory}image-${(`00${num}`).slice(-3)}.jpg`;
        }
    }
    htmlImagesArray.notLoaded = [];
    htmlImagesArray.addImage = function (image) {
        htmlImagesArray.notLoaded.push(image);
    };
    htmlImagesArray.loaded = function (loadedImg) {
        const notloadedImgs = htmlImagesArray.notLoaded;
        for (let x = 0; x < notloadedImgs.length; x++) {
            if (notloadedImgs[x] === loadedImg) {
                notloadedImgs.splice(x, 1);
                for (let y = 0; y < htmlImagesSrcArray.length; y++) {
                    const imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf('/'));
                    if (loadedImg.src.lastIndexOf(imgName) !== -1) {
                        htmlImagesArray[y] = { img: loadedImg };
                        break;
                    }
                }
                break;
            }
        }
        if (notloadedImgs.length === 0) {
            if (ENV.development) {
                console.log('Images loaded');
            }
            if (sequence === false) {
                findTagsInObjectURL(directory, ['orientation'])
                    .then((tags) => {
                        htmlImagesArray[0].tags = tags;
                        callback(htmlImagesArray);
                    }).catch((e) => {
                        console.log(e);
                        callback(htmlImagesArray);
                    });
            } else {
                callback(htmlImagesArray);
            }
        }
    };

    for (i = 0; i < htmlImagesSrcArray.length; i++) {
        img = new Image();
        htmlImagesArray.addImage(img);
        addOnloadHandler(img, htmlImagesArray);
        img.src = htmlImagesSrcArray[i];
    }
};

function addOnloadHandler(img, htmlImagesArray) {
    img.onload = function () {
        htmlImagesArray.loaded(this);
    };
}

export default (ImageLoader);
<|endoftext|>