<commit_before>/*jshint -W030, -W020 */

DEBUG = !!process.env.VELOCITY_DEBUG;

(function () {

  'use strict';

  var path = Npm.require('path');
  var fs = Npm.require('fs-extra');

  // this library extends the string prototype
  Npm.require('colors');

  var FRAMEWORK_NAME = 'cucumber';
  var BINARY = process.env.CUKE_MONKEY_PATH || Npm.require('cuke-monkey').bin;
  if (
    process.env.NODE_ENV !== 'development' || !process.env.IS_MIRROR ||
    process.env[FRAMEWORK_NAME.toUpperCase()] === '0' ||
    process.env.VELOCITY === '0' ||
    process.env.FRAMEWORK.indexOf(FRAMEWORK_NAME) !== 0
  ) {
    return;
  }

  DEBUG && console.log('[xolvio:cucumber] Mirror server is initializing');

  var _velocityConnection,
      _velocityTestFiles,
      _cukeMonkeyProc;

  Meteor.startup(function () {

    _startTheMonkey();

    DEBUG && console.log('[xolvio:cucumber] Connecting to hub');
    _velocityConnection = DDP.connect(process.env.PARENT_URL);
    _velocityConnection.subscribe('VelocityTestFiles');

    _velocityTestFiles = new Mongo.Collection('velocityTestFiles', {
      connection: _velocityConnection
    });

    _velocityConnection.onReconnect = function () {
      DEBUG && console.log('[xolvio:cucumber] Connected to hub.');
      var debouncedRun = _.debounce(Meteor.bindEnvironment(_findAndRun), 1000);
      _velocityTestFiles.find({targetFramework: FRAMEWORK_NAME}).observe({
        added: debouncedRun,
        removed: debouncedRun,
        changed: debouncedRun
      });

      process.on('SIGUSR2', Meteor.bindEnvironment(debouncedRun));
      process.on('message', Meteor.bindEnvironment(function (message) {
        if (message.refresh && message.refresh === 'client') {
          debouncedRun();
        }
      }));

    };

  });

  function _findAndRun () {

    var findAndRun = function () {
      var feature = _velocityTestFiles.findOne({
        targetFramework: FRAMEWORK_NAME,
        status: 'TODO'
      });
      if (feature) {
        _velocityTestFiles.update(feature._id, {$set: {status: 'DOING'}});
        _run(feature, findAndRun);
      }
    };

    if (!!process.env.CUCUMBER_NODES) {
      DEBUG && console.log('[xolvio:cucumber] Running in split-features mode');
      findAndRun();
    } else {
      DEBUG && console.log('[xolvio:cucumber] Running in batch-features mode.');
      _run();
    }

  }

  function _run (feature) {
    if (feature) {
      console.log('[xolvio:cucumber] Mirror with pid', process.pid, 'is working on', feature.absolutePath);
      // TODO call cuke-monkey with a single file to run
    } else {
      console.log('[xolvio:cucumber] Cucumber is running'.yellow);
      _velocityConnection.call('velocity/reports/reset', {framework: FRAMEWORK_NAME});

      try {
        HTTP.get('http://localhost:' + _getServerPort() + '/interrupt');

        var response = HTTP.get('http://localhost:' + _getServerPort() + '/run');
        var results = JSON.parse(response.content);
        if (results.length === 0) {
          console.log('[xolvio:cucumber] No features found. Be sure to annotate scenarios'.yellow,
            'you want to run in watch mode with the'.yellow, '@dev'.cyan, 'tag'.yellow);
        }
        _processFeatures(results);
      } catch (e) {
        console.error('[xolvio:cucumber] Bad response from cuke-monkey server. Try rerunning'.red);
        return;
      }

      if (feature) {
        _velocityTestFiles.update(feature._id, {$set: {status: 'DONE'}});
      } else {
        _velocityConnection.call('velocity/reports/completed', {framework: FRAMEWORK_NAME});
      }

    }

  }

  function _startTheMonkey () {

    DEBUG && console.log('[xolvio:cucumber] Starting the monkey');
    _cukeMonkeyProc = new sanjo.LongRunningChildProcess('cukeMonkey');
    if (_cukeMonkeyProc.isRunning()) {
      DEBUG && console.log('[xolvio:cucumber] The monkey is already running');
      return;
    }

    var args = _getArgs();

    fs.chmodSync(BINARY, parseInt('555', 8));
    var nodePath = process.execPath;
    var nodeDir = path.dirname(nodePath);
    var env = _.clone(process.env);
    // Expose the Meteor node binary path for the script that is run
    env.PATH = nodeDir + ':' + env.PATH;
    var spawnOptions = {
      cwd: path.resolve(process.env.VELOCITY_MAIN_APP_PATH, 'tests', FRAMEWORK_NAME),
      stdio: 'inherit',
      env: env
    };

    args.push('--server');
    args.push('--serverPort=' + _getServerPort());

    DEBUG && console.log('[xolvio:cucumber] Starting the monkey with', BINARY, args, spawnOptions);

    _cukeMonkeyProc.spawn({
      command: BINARY,
      args: args,
      options: spawnOptions
    });

    DEBUG && console.log('[xolvio:cucumber] Cuke-Monkey process forked with pid', _cukeMonkeyProc.getPid());

  }

  function _getServerPort () {
    return process.env.CUCUMBER_SERVER_PORT ? process.env.CUCUMBER_SERVER_PORT : 8866;
  }

  function _getArgs () {

    var args = [];

    args.push('-r');
    args.push(path.join(process.env.VELOCITY_MAIN_APP_PATH, 'tests', 'cucumber', 'features'));
    args.push('--snippets');
    args.push('--ddp=' + process.env.ROOT_URL);
    args.push('--log=error');

    if (DEBUG || process.env.DEBUG) {
      args.push('--debug');
    }

    if (process.env.MONKEY_OPTIONS) {
      var monkeyOptions = process.env.MONKEY_OPTIONS.split(' ');
      while (monkeyOptions.length != 0) {
        args.push(monkeyOptions.pop());
      }
      return args;
    }

    if (process.env.CUCUMBER_COFFEE_SNIPPETS) {
      args.push('--coffee');
    }

    if (process.env.CUCUMBER_TAGS) {
      args.push('--tags=' + process.env.CUCUMBER_TAGS);
    } else if (!process.env.CI) {
      args.push('--tags=@dev');
    } else {
      args.push('--tags=~@ignore');
    }

    if (process.env.CUCUMBER_FORMAT) {
      args.push('--format=' + process.env.CUCUMBER_FORMAT);
    }

    if (process.env.WD_TIMEOUT_ASYNC_SCRIPT) {
      args.push('--timeoutsAsyncScript=' + process.env.WD_TIMEOUT_ASYNC_SCRIPT);
    }

    if (process.env.SELENIUM_BROWSER) {
      args.push('--browser=' + process.env.SELENIUM_BROWSER);
    }

    if (process.env.HUB_HOST) {
      args.push('--host=' + process.env.HUB_HOST);
    }

    if (process.env.HUB_PORT) {
      args.push('--port=' + process.env.HUB_PORT);
    }

    if (process.env.HUB_USER) {
      args.push('--user=' + process.env.HUB_USER);
    }

    if (process.env.HUB_KEY) {
      args.push('--key=' + process.env.HUB_KEY);
    }

    if (process.env.HUB_PLATFORM) {
      args.push('--platform=' + process.env.HUB_PLATFORM);
    }

    if (process.env.HUB_VERSION) {
      args.push('--version=' + process.env.HUB_VERSION);
    }

    return args;

  }

  function _processFeatures (features) {
    _.each(features, function (feature) {
      _processFeature(feature);
    });
  }

  function _processFeature (feature) {
    _.each(feature.elements, function (element) {
      _processFeatureElements(element, feature);
    });
  }

  function _processFeatureElements (element, feature) {
    _.each(element.steps, function (step) {
      _processStep(element, step, feature);
    });
  }

  function _processStep (element, step, feature) {

    if (element.type === 'background') {
      return;
    }

    var report = {
      id: element.id + step.keyword + step.name,
      framework: FRAMEWORK_NAME,
      name: step.keyword + step.name,
      result: step.result.status,
      ancestors: [element.name, feature.name]
    };
    if (step.result.duration) {
      report.duration = Math.round(step.result.duration / 1000000);
    }
    if (step.result.error_message) {

      if (step.result.error_message.name) {
        report.failureType = step.result.error_message.name;
        report.failureStackTrace = _conditionError(step.result.error_message.message);
      } else {
        report.failureStackTrace = _conditionError(step.result.error_message);
      }
    }

    if (step.result.status === 'undefined') {
      report.result = 'failed';
      report.failureStackTrace = 'This step has not been defined';
    }

    if (step.result.status === 'skipped') {
      report.result = 'failed';
      report.failureStackTrace = 'This step was skipped';
    }

    // skip before/after if they have no errors
    if (!report.failureStackTrace && (step.keyword.trim() === 'Before' || step.keyword.trim() === 'After')) {
      return;
    }

    _velocityConnection.call('velocity/reports/submit', report);

  }

  function _conditionError (errorMessage) {

    if (DEBUG) {
      console.error(errorMessage.red);
      return errorMessage;
    }

    var msg = '',
        insertDots = false,
        DOTS = '  ...\n';

    try {
      _.each(errorMessage.split('\n'), function (line, index) {
        line = line.trim();
        if (index === 0) {
          msg += line;
          return;
        }
        if (line.indexOf('/tests/cucumber/') !== -1) {
          if (insertDots) {
            msg += DOTS;
            insertDots = false;
          }
          msg += ((index !== 0 ? '  ' : '') +
          line.replace(process.env.VELOCITY_MAIN_APP_PATH, '') + '\n');
        } else {
          insertDots = true;
        }
      });
      if (insertDots) {
        msg += DOTS;
      }

      // TODO deal with this error for users
      // Cannot call method 'convertScenarioOutlinesToScenarios' of undefined

      msg = msg.substring(0, msg.length);
      //console.log(msg.magenta);
    } catch (e) {
      msg = errorMessage;
    }

    return msg;
  }

})();
<commit_msg>notes for parallel working<commit_after>/*jshint -W030, -W020 */

DEBUG = !!process.env.VELOCITY_DEBUG;

(function () {

  'use strict';

  var path = Npm.require('path');
  var fs = Npm.require('fs-extra');

  // this library extends the string prototype
  Npm.require('colors');

  var FRAMEWORK_NAME = 'cucumber';
  var BINARY = process.env.CUKE_MONKEY_PATH || Npm.require('cuke-monkey').bin;
  if (
    process.env.NODE_ENV !== 'development' || !process.env.IS_MIRROR ||
    process.env[FRAMEWORK_NAME.toUpperCase()] === '0' ||
    process.env.VELOCITY === '0' ||
    process.env.FRAMEWORK.indexOf(FRAMEWORK_NAME) !== 0
  ) {
    return;
  }

  DEBUG && console.log('[xolvio:cucumber] Mirror server is initializing');

  var _velocityConnection,
      _velocityTestFiles,
      _cukeMonkeyProc;

  Meteor.startup(function () {

    _startTheMonkey();

    DEBUG && console.log('[xolvio:cucumber] Connecting to hub');
    _velocityConnection = DDP.connect(process.env.PARENT_URL);
    _velocityConnection.subscribe('VelocityTestFiles');

    _velocityTestFiles = new Mongo.Collection('velocityTestFiles', {
      connection: _velocityConnection
    });

    _velocityConnection.onReconnect = function () {
      DEBUG && console.log('[xolvio:cucumber] Connected to hub.');
      var debouncedRun = _.debounce(Meteor.bindEnvironment(_findAndRun), 1000);
      _velocityTestFiles.find({targetFramework: FRAMEWORK_NAME}).observe({
        added: debouncedRun,
        removed: debouncedRun,
        changed: debouncedRun
      });

      process.on('SIGUSR2', Meteor.bindEnvironment(debouncedRun));
      process.on('message', Meteor.bindEnvironment(function (message) {
        if (message.refresh && message.refresh === 'client') {
          debouncedRun();
        }
      }));

    };

  });

  function _findAndRun () {

    var findAndRun = function () {
      var feature = _velocityTestFiles.findOne({
        targetFramework: FRAMEWORK_NAME,
        status: 'TODO'
      });
      if (feature) {
        _velocityTestFiles.update(feature._id, {$set: {status: 'DOING'}});
        _run(feature, findAndRun);
      }
    };

    if (!!process.env.CUCUMBER_NODES) {
      DEBUG && console.log('[xolvio:cucumber] Running in split-features mode');
      findAndRun();
    } else {
      DEBUG && console.log('[xolvio:cucumber] Running in batch-features mode.');
      _run();
    }

  }

  // TODO add callback here so findAndRun can be run again after a worker has finished running
  function _run (feature) {
    if (feature) {
      console.log('[xolvio:cucumber] Mirror with pid', process.pid, 'is working on', feature.absolutePath);
      // TODO call cuke-monkey with a single file to run
    } else {
      console.log('[xolvio:cucumber] Cucumber is running'.yellow);
      _velocityConnection.call('velocity/reports/reset', {framework: FRAMEWORK_NAME});

      try {
        HTTP.get('http://localhost:' + _getServerPort() + '/interrupt');

        // TODO modify cuke-monkey server to take a param to run one feature only
        var response = HTTP.get('http://localhost:' + _getServerPort() + '/run');
        var results = JSON.parse(response.content);
        if (results.length === 0) {
          console.log('[xolvio:cucumber] No features found. Be sure to annotate scenarios'.yellow,
            'you want to run in watch mode with the'.yellow, '@dev'.cyan, 'tag'.yellow);
        }
        _processFeatures(results);
      } catch (e) {
        console.error('[xolvio:cucumber] Bad response from cuke-monkey server. Try rerunning'.red);
        return;
      }

      if (feature) {
        _velocityTestFiles.update(feature._id, {$set: {status: 'DONE'}});
      } else {
        _velocityConnection.call('velocity/reports/completed', {framework: FRAMEWORK_NAME});
      }

    }

  }

  function _startTheMonkey () {

    DEBUG && console.log('[xolvio:cucumber] Starting the monkey');
    // TODO add node ID to cuke monkey instance
    _cukeMonkeyProc = new sanjo.LongRunningChildProcess('cukeMonkey');
    if (_cukeMonkeyProc.isRunning()) {
      DEBUG && console.log('[xolvio:cucumber] The monkey is already running');
      return;
    }

    var args = _getArgs();

    fs.chmodSync(BINARY, parseInt('555', 8));
    var nodePath = process.execPath;
    var nodeDir = path.dirname(nodePath);
    var env = _.clone(process.env);
    // Expose the Meteor node binary path for the script that is run
    env.PATH = nodeDir + ':' + env.PATH;
    var spawnOptions = {
      cwd: path.resolve(process.env.VELOCITY_MAIN_APP_PATH, 'tests', FRAMEWORK_NAME),
      stdio: 'inherit',
      env: env
    };

    args.push('--server');
    args.push('--serverPort=' + _getServerPort());

    DEBUG && console.log('[xolvio:cucumber] Starting the monkey with', BINARY, args, spawnOptions);

    _cukeMonkeyProc.spawn({
      command: BINARY,
      args: args,
      options: spawnOptions
    });

    DEBUG && console.log('[xolvio:cucumber] Cuke-Monkey process forked with pid', _cukeMonkeyProc.getPid());

  }

  function _getServerPort () {
    return process.env.CUCUMBER_SERVER_PORT ? process.env.CUCUMBER_SERVER_PORT : 8866;
  }

  function _getArgs () {

    var args = [];

    args.push('-r');
    args.push(path.join(process.env.VELOCITY_MAIN_APP_PATH, 'tests', 'cucumber', 'features'));
    args.push('--snippets');
    args.push('--ddp=' + process.env.ROOT_URL);
    args.push('--log=error');

    if (DEBUG || process.env.DEBUG) {
      args.push('--debug');
    }

    if (process.env.MONKEY_OPTIONS) {
      var monkeyOptions = process.env.MONKEY_OPTIONS.split(' ');
      while (monkeyOptions.length != 0) {
        args.push(monkeyOptions.pop());
      }
      return args;
    }

    if (process.env.CUCUMBER_COFFEE_SNIPPETS) {
      args.push('--coffee');
    }

    if (process.env.CUCUMBER_TAGS) {
      args.push('--tags=' + process.env.CUCUMBER_TAGS);
    } else if (!process.env.CI) {
      args.push('--tags=@dev');
    } else {
      args.push('--tags=~@ignore');
    }

    if (process.env.CUCUMBER_FORMAT) {
      args.push('--format=' + process.env.CUCUMBER_FORMAT);
    }

    if (process.env.WD_TIMEOUT_ASYNC_SCRIPT) {
      args.push('--timeoutsAsyncScript=' + process.env.WD_TIMEOUT_ASYNC_SCRIPT);
    }

    if (process.env.SELENIUM_BROWSER) {
      args.push('--browser=' + process.env.SELENIUM_BROWSER);
    }

    if (process.env.HUB_HOST) {
      args.push('--host=' + process.env.HUB_HOST);
    }

    if (process.env.HUB_PORT) {
      args.push('--port=' + process.env.HUB_PORT);
    }

    if (process.env.HUB_USER) {
      args.push('--user=' + process.env.HUB_USER);
    }

    if (process.env.HUB_KEY) {
      args.push('--key=' + process.env.HUB_KEY);
    }

    if (process.env.HUB_PLATFORM) {
      args.push('--platform=' + process.env.HUB_PLATFORM);
    }

    if (process.env.HUB_VERSION) {
      args.push('--version=' + process.env.HUB_VERSION);
    }

    return args;

  }

  function _processFeatures (features) {
    _.each(features, function (feature) {
      _processFeature(feature);
    });
  }

  function _processFeature (feature) {
    _.each(feature.elements, function (element) {
      _processFeatureElements(element, feature);
    });
  }

  function _processFeatureElements (element, feature) {
    _.each(element.steps, function (step) {
      _processStep(element, step, feature);
    });
  }

  function _processStep (element, step, feature) {

    if (element.type === 'background') {
      return;
    }

    var report = {
      id: element.id + step.keyword + step.name,
      framework: FRAMEWORK_NAME,
      name: step.keyword + step.name,
      result: step.result.status,
      ancestors: [element.name, feature.name]
    };
    if (step.result.duration) {
      report.duration = Math.round(step.result.duration / 1000000);
    }
    if (step.result.error_message) {

      if (step.result.error_message.name) {
        report.failureType = step.result.error_message.name;
        report.failureStackTrace = _conditionError(step.result.error_message.message);
      } else {
        report.failureStackTrace = _conditionError(step.result.error_message);
      }
    }

    if (step.result.status === 'undefined') {
      report.result = 'failed';
      report.failureStackTrace = 'This step has not been defined';
    }

    if (step.result.status === 'skipped') {
      report.result = 'failed';
      report.failureStackTrace = 'This step was skipped';
    }

    // skip before/after if they have no errors
    if (!report.failureStackTrace && (step.keyword.trim() === 'Before' || step.keyword.trim() === 'After')) {
      return;
    }

    _velocityConnection.call('velocity/reports/submit', report);

  }

  function _conditionError (errorMessage) {

    if (DEBUG) {
      console.error(errorMessage.red);
      return errorMessage;
    }

    var msg = '',
        insertDots = false,
        DOTS = '  ...\n';

    try {
      _.each(errorMessage.split('\n'), function (line, index) {
        line = line.trim();
        if (index === 0) {
          msg += line;
          return;
        }
        if (line.indexOf('/tests/cucumber/') !== -1) {
          if (insertDots) {
            msg += DOTS;
            insertDots = false;
          }
          msg += ((index !== 0 ? '  ' : '') +
          line.replace(process.env.VELOCITY_MAIN_APP_PATH, '') + '\n');
        } else {
          insertDots = true;
        }
      });
      if (insertDots) {
        msg += DOTS;
      }

      // TODO deal with this error for users
      // Cannot call method 'convertScenarioOutlinesToScenarios' of undefined

      msg = msg.substring(0, msg.length);
      //console.log(msg.magenta);
    } catch (e) {
      msg = errorMessage;
    }

    return msg;
  }

})();
<|endoftext|>


<commit_before>// TODO rename to file driver
// TODO support ttyrec (via opts.format == 'ttyrec')

import Stream from '../stream';

function asciicast({ url }, { feed, now, setTimeout, onFinish }, { idleTimeLimit, startAt }) {
  let cols;
  let rows;
  let frames;
  let duration;
  let effectiveStartAt;
  let timeoutId;
  let nextFrameIndex = 0;
  let elapsedVirtualTime = 0;
  let startTime;
  let pauseElapsedTime;

  async function load() {
    if (!frames) {
      const res = await fetch(url);
      const asciicast = parseAsciicast(await res.text());
      cols = asciicast.cols;
      rows = asciicast.rows;
      idleTimeLimit = idleTimeLimit ?? asciicast.idleTimeLimit
      const result = prepareFrames(asciicast.frames, idleTimeLimit, startAt);
      frames = result.frames;
      effectiveStartAt = result.effectiveStartAt;
      duration = frames[frames.length - 1][0];
    }
  }

  function scheduleNextFrame() {
    const nextFrame = frames[nextFrameIndex];

    if (nextFrame) {
      const t = nextFrame[0] * 1000;
      const elapsedWallTime = now() - startTime;
      let timeout = t - elapsedWallTime;

      if (timeout < 0) {
        timeout = 0;
      }

      timeoutId = setTimeout(runFrame, timeout);
    } else {
      timeoutId = null;
      pauseElapsedTime = duration * 1000;
      onFinish();
    }
  }

  function runFrame() {
    let frame = frames[nextFrameIndex];
    let elapsedWallTime;

    do {
      feed(frame[1]);
      elapsedVirtualTime = frame[0] * 1000;
      frame = frames[++nextFrameIndex];
      elapsedWallTime = now() - startTime;
    } while (frame && (elapsedWallTime > frame[0] * 1000));

    scheduleNextFrame();
  }

  function pause() {
    clearTimeout(timeoutId);
    timeoutId = null;
    pauseElapsedTime = now() - startTime;
  }

  function resume() {
    startTime = now() - pauseElapsedTime;
    pauseElapsedTime = null;
    scheduleNextFrame();
  }

  function seek(where) {
    const isPlaying = !!timeoutId;

    if (isPlaying) {
      pause();
    }

    if (typeof where === 'string') {
      if (where === '<<') {
        where = (((pauseElapsedTime ?? 0) / (duration * 1000)) - 0.1) * duration;
      } else if (where === '>>') {
        where = (((pauseElapsedTime ?? 0) / (duration * 1000)) + 0.1) * duration;
      } else if (where[where.length - 1] === '%') {
        where = (parseFloat(where.substring(0, where.length - 1)) / 100) * duration;
      }
    }

    const targetTime = Math.min(Math.max(where, 0), duration) * 1000;

    if (targetTime < elapsedVirtualTime) {
      feed('\x1bc'); // reset terminal
      nextFrameIndex = 0;
      elapsedVirtualTime = 0;
    }

    let frame = frames[nextFrameIndex];

    while (frame && (frame[0] * 1000 < targetTime)) {
      feed(frame[1]);
      elapsedVirtualTime = frame[0] * 1000;
      frame = frames[++nextFrameIndex];
    }

    pauseElapsedTime = targetTime;

    if (isPlaying) {
      resume();
    }
  }

  function getPoster(time) {
    const posterTime = time * 1000;
    const poster = [];
    let nextFrameIndex = 0;
    let frame = frames[0];

    while (frame && (frame[0] * 1000 < posterTime)) {
      poster.push(frame[1]);
      frame = frames[++nextFrameIndex];
    }

    return poster;
  }

  return {
    init: async () => {
      await load();

      return { cols, rows, duration };
    },

    start: async () => {
      await load();
      seek(effectiveStartAt);
      resume();
    },

    stop: () => {
      clearTimeout(timeoutId);
    },

    pauseOrResume: () => {
      if (timeoutId) {
        pause();
        return false;
      } else {
        resume();
        return true;
      }
    },

    seek: where => {
      return seek(where);
    },

    getPoster: t => {
      return getPoster(t);
    },

    getCurrentTime: () => {
      if (timeoutId) {
        return (now() - startTime) / 1000;
      } else {
        return (pauseElapsedTime ?? 0) / 1000;
      }
    }
  }
}

function parseAsciicast(json) {
  try {
    return parseAsciicastV2(json);
  } catch (_error) {
    // not a v2 format - let's try parsing as v1
    return parseAsciicastV1(json);
  }
}

function parseAsciicastV1(json) {
  const asciicast = JSON.parse(json);
  let time = 0;

  const frames = new Stream(asciicast.stdout).map(e => {
    time += e[0];
    return [time, e[1]];
  });

  return {
    cols: asciicast.width,
    rows: asciicast.height,
    frames: frames
  }
}

function parseAsciicastV2(jsonl) {
  const lines = jsonl.split('\n');
  const header = JSON.parse(lines[0]);

  if (header.version !== 2) {
    throw 'not asciicast v2 format';
  }

  const frames = new Stream(lines)
    .drop(1)
    .filter(l => l[0] === '[')
    .map(l => JSON.parse(l))
    .filter(e => e[1] === 'o')
    .map(e => [e[0], e[2]]);

  return {
    cols: header.width,
    rows: header.height,
    idleTimeLimit: header.idle_time_limit,
    frames: frames
  }
}

function batchFrames(frames) {
  const maxFrameTime = 1.0 / 60;
  let prevFrame;

  return frames.transform(emit => {
    let ic = 0;
    let oc = 0;

    return {
      step: frame => {
        ic++;

        if (prevFrame === undefined) {
          prevFrame = frame;
          return;
        }

        if (frame[0] - prevFrame[0] < maxFrameTime) {
          prevFrame[1] += frame[1];
        } else {
          emit(prevFrame);
          prevFrame = frame;
          oc++;
        }
      },

      flush: () => {
        if (prevFrame !== undefined) {
          emit(prevFrame);
          oc++;
        }

        console.debug(`batched ${ic} frames to ${oc} frames`);
      }
    }
  });
}

function prepareFrames(frames, idleTimeLimit = Infinity, startAt = 0) {
  let prevT = 0;
  let shift = 0;
  let effectiveStartAt = startAt;

  const fs = Array.from(batchFrames(frames).map(e => {
    const delay = e[0] - prevT;
    const delta = delay - idleTimeLimit;
    prevT = e[0];

    if (delta > 0) {
      shift += delta;

      if (e[0] < startAt) {
        effectiveStartAt -= delta;
      }
    }

    return [e[0] - shift, e[1]];
  }));

  return {
    frames: fs,
    effectiveStartAt: effectiveStartAt
  }
}

export { asciicast };
<commit_msg>Check response status when fetching asciicast file<commit_after>// TODO rename to file driver
// TODO support ttyrec (via opts.format == 'ttyrec')

import Stream from '../stream';

function asciicast({ url }, { feed, now, setTimeout, onFinish }, { idleTimeLimit, startAt }) {
  let cols;
  let rows;
  let frames;
  let duration;
  let effectiveStartAt;
  let timeoutId;
  let nextFrameIndex = 0;
  let elapsedVirtualTime = 0;
  let startTime;
  let pauseElapsedTime;

  async function load() {
    if (!frames) {
      const response = await fetch(url);

      if (!response.ok) {
        throw `failed fetching asciicast file: ${response.statusText} (${response.status})`
      }

      const asciicast = parseAsciicast(await response.text());
      cols = asciicast.cols;
      rows = asciicast.rows;
      idleTimeLimit = idleTimeLimit ?? asciicast.idleTimeLimit
      const result = prepareFrames(asciicast.frames, idleTimeLimit, startAt);
      frames = result.frames;
      effectiveStartAt = result.effectiveStartAt;
      duration = frames[frames.length - 1][0];
    }
  }

  function scheduleNextFrame() {
    const nextFrame = frames[nextFrameIndex];

    if (nextFrame) {
      const t = nextFrame[0] * 1000;
      const elapsedWallTime = now() - startTime;
      let timeout = t - elapsedWallTime;

      if (timeout < 0) {
        timeout = 0;
      }

      timeoutId = setTimeout(runFrame, timeout);
    } else {
      timeoutId = null;
      pauseElapsedTime = duration * 1000;
      onFinish();
    }
  }

  function runFrame() {
    let frame = frames[nextFrameIndex];
    let elapsedWallTime;

    do {
      feed(frame[1]);
      elapsedVirtualTime = frame[0] * 1000;
      frame = frames[++nextFrameIndex];
      elapsedWallTime = now() - startTime;
    } while (frame && (elapsedWallTime > frame[0] * 1000));

    scheduleNextFrame();
  }

  function pause() {
    clearTimeout(timeoutId);
    timeoutId = null;
    pauseElapsedTime = now() - startTime;
  }

  function resume() {
    startTime = now() - pauseElapsedTime;
    pauseElapsedTime = null;
    scheduleNextFrame();
  }

  function seek(where) {
    const isPlaying = !!timeoutId;

    if (isPlaying) {
      pause();
    }

    if (typeof where === 'string') {
      if (where === '<<') {
        where = (((pauseElapsedTime ?? 0) / (duration * 1000)) - 0.1) * duration;
      } else if (where === '>>') {
        where = (((pauseElapsedTime ?? 0) / (duration * 1000)) + 0.1) * duration;
      } else if (where[where.length - 1] === '%') {
        where = (parseFloat(where.substring(0, where.length - 1)) / 100) * duration;
      }
    }

    const targetTime = Math.min(Math.max(where, 0), duration) * 1000;

    if (targetTime < elapsedVirtualTime) {
      feed('\x1bc'); // reset terminal
      nextFrameIndex = 0;
      elapsedVirtualTime = 0;
    }

    let frame = frames[nextFrameIndex];

    while (frame && (frame[0] * 1000 < targetTime)) {
      feed(frame[1]);
      elapsedVirtualTime = frame[0] * 1000;
      frame = frames[++nextFrameIndex];
    }

    pauseElapsedTime = targetTime;

    if (isPlaying) {
      resume();
    }
  }

  function getPoster(time) {
    const posterTime = time * 1000;
    const poster = [];
    let nextFrameIndex = 0;
    let frame = frames[0];

    while (frame && (frame[0] * 1000 < posterTime)) {
      poster.push(frame[1]);
      frame = frames[++nextFrameIndex];
    }

    return poster;
  }

  return {
    init: async () => {
      await load();

      return { cols, rows, duration };
    },

    start: async () => {
      await load();
      seek(effectiveStartAt);
      resume();
    },

    stop: () => {
      clearTimeout(timeoutId);
    },

    pauseOrResume: () => {
      if (timeoutId) {
        pause();
        return false;
      } else {
        resume();
        return true;
      }
    },

    seek: where => {
      return seek(where);
    },

    getPoster: t => {
      return getPoster(t);
    },

    getCurrentTime: () => {
      if (timeoutId) {
        return (now() - startTime) / 1000;
      } else {
        return (pauseElapsedTime ?? 0) / 1000;
      }
    }
  }
}

function parseAsciicast(json) {
  try {
    return parseAsciicastV2(json);
  } catch (_error) {
    // not a v2 format - let's try parsing as v1
    return parseAsciicastV1(json);
  }
}

function parseAsciicastV1(json) {
  const asciicast = JSON.parse(json);
  let time = 0;

  const frames = new Stream(asciicast.stdout).map(e => {
    time += e[0];
    return [time, e[1]];
  });

  return {
    cols: asciicast.width,
    rows: asciicast.height,
    frames: frames
  }
}

function parseAsciicastV2(jsonl) {
  const lines = jsonl.split('\n');
  const header = JSON.parse(lines[0]);

  if (header.version !== 2) {
    throw 'not asciicast v2 format';
  }

  const frames = new Stream(lines)
    .drop(1)
    .filter(l => l[0] === '[')
    .map(l => JSON.parse(l))
    .filter(e => e[1] === 'o')
    .map(e => [e[0], e[2]]);

  return {
    cols: header.width,
    rows: header.height,
    idleTimeLimit: header.idle_time_limit,
    frames: frames
  }
}

function batchFrames(frames) {
  const maxFrameTime = 1.0 / 60;
  let prevFrame;

  return frames.transform(emit => {
    let ic = 0;
    let oc = 0;

    return {
      step: frame => {
        ic++;

        if (prevFrame === undefined) {
          prevFrame = frame;
          return;
        }

        if (frame[0] - prevFrame[0] < maxFrameTime) {
          prevFrame[1] += frame[1];
        } else {
          emit(prevFrame);
          prevFrame = frame;
          oc++;
        }
      },

      flush: () => {
        if (prevFrame !== undefined) {
          emit(prevFrame);
          oc++;
        }

        console.debug(`batched ${ic} frames to ${oc} frames`);
      }
    }
  });
}

function prepareFrames(frames, idleTimeLimit = Infinity, startAt = 0) {
  let prevT = 0;
  let shift = 0;
  let effectiveStartAt = startAt;

  const fs = Array.from(batchFrames(frames).map(e => {
    const delay = e[0] - prevT;
    const delta = delay - idleTimeLimit;
    prevT = e[0];

    if (delta > 0) {
      shift += delta;

      if (e[0] < startAt) {
        effectiveStartAt -= delta;
      }
    }

    return [e[0] - shift, e[1]];
  }));

  return {
    frames: fs,
    effectiveStartAt: effectiveStartAt
  }
}

export { asciicast };
<|endoftext|>


<commit_before>'use strict';

var I = require('immutable');

var _assert = function(condition, message) {
  if (!condition)
    throw new Error(message || 'assertion error');
};

var _isElement = function _isElement(dsImpl, D) {
  return typeof D == 'number' && D >= 1 && D <= dsImpl.size;
};

var _elements = function _elements(dsImpl) {
  return I.Range(1, dsImpl.size+1);
};

var _isIndex = function _isIndex(dsImpl, i) {
  return typeof i == 'number' && i >= 0 && i <= dsImpl.dim;
};

var _indices = function _indices(dsImpl) {
  return I.Range(0, dsImpl.dim+1);
};

var _index = function offset(dsImpl, i, D) {
  return i * dsImpl.size + D - 1;
};

var _get = function offset(dsImpl, list, i, D) {
  return list.get(_index(dsImpl, i, D));
};

var _set = function offset(dsImpl, list, i, D, x) {
  return list.set(_index(dsImpl, i, D), x);
};

var _s = function _s(dsImpl, i, D) {
  if (_isElement(dsImpl, D) && _isIndex(dsImpl, i))
    return _get(dsImpl, dsImpl.s, i, D);
};

var _v = function _v(dsImpl, i, j, D) {
  if (_isElement(dsImpl, D) && _isIndex(dsImpl, i) && _isIndex(dsImpl, j)) {
    if (j == i+1)
      return _get(dsImpl, dsImpl.v, i, D);
    else if (j == i-1)
      return _get(dsImpl, dsImpl.v, j, D);
    else if (_get(dsImpl, dsImpl.s, i, D) == _get(dsImpl, dsImpl.s, j, D))
      return 2;
    else
      return 1;
  }
};

var _merge = function _merge(a, b) {
  return a.withMutations(function(list) {
    b.forEach(function(x, i) {
      if (x !== undefined)
        list.set(i, x);
    });
  });
};

var _precheckPairings = function _checkPairings(specs, size) {
  specs.forEach(function(p) {
    _assert(p.size == 1 || p.size == 2,
            'expected pair or singleton, got '+p);

    var D = p.get(0);
    var E = p.size > 1 ? p.get(1) : p.get(0);

    _assert(typeof D == 'number' && D % 1 == 0 && D > 0,
            'expected a positive integer, got '+D);
    _assert(D <= size,
            'expected at most '+size+', got '+D);

    _assert(typeof E == 'number' && E % 1 == 0 && E >= 0,
            'expected a non-negative integer, got '+E);
    _assert(E <= size,
            'expected at most '+size+', got '+E);
  });
};

var _withPairings = function _withPairings(dsImpl, i, inputs) {
  var specs = I.List(inputs).map(I.List);
  _precheckPairings(specs, dsImpl.size);

  _assert(typeof i == 'number' && i % 1 == 0 && i >= 0 && i <= dsImpl.dim,
          'expected an integer between 0 and '+dsImpl.dim+', got i');

  var sNew = I.List().withMutations(function(list) {
    var dangling = [];

    specs.forEach(function(p) {
      var D = p.get(0);
      var E = p.size > 1 ? p.get(1) : p.get(0);
      var Di = _get(dsImpl, list, i, D);
      var Ei = _get(dsImpl, list, i, E);

      _assert(Di === undefined || Di == E,
              'conflicting partners '+Di+' and '+E+' for '+D);
      _assert(Ei === undefined || Ei == D,
              'conflicting partners '+Ei+' and '+D+' for '+E);

      dangling.push(_get(dsImpl, dsImpl.s, i, D));
      dangling.push(_get(dsImpl, dsImpl.s, i, E));

      _set(dsImpl, list, i, D, E);
      _set(dsImpl, list, i, E, D);
    });

    dangling.forEach(function(D) {
      if (D && _get(dsImpl, list, i, D) === undefined)
        _set(dsImpl, list, i, D, 0);
    });
  });

  return _fromData(dsImpl.dim, _merge(dsImpl.s, sNew), dsImpl.v);
};

var _precheckBranchings = function _checkBranchings(specs, size) {
  specs.forEach(function(p) {
    _assert(p.size == 2, 'expected pair, got '+p);

    var D = p.get(0);
    var v = p.get(1);

    _assert(typeof D == 'number' && D % 1 == 0 && D > 0,
            'expected a positive integer, got '+D);
    _assert(D <= size,
            'expected at most '+size+', got '+D);

    _assert(typeof v == 'number' && v % 1 == 0 && v >= 0,
            'expected a non-negative integer, got '+v);
  });
};

var _withBranchings = function _withBranchings(dsImpl, i, inputs) {
  var specs = I.List(inputs).map(I.List);
  _precheckBranchings(specs, dsImpl.size);

  _assert(typeof i == 'number' && i % 1 == 0 && i >= 0 && i <= dsImpl.dim-1,
          'expected integer between 0 and '+dsImpl.dim-1+', got i');

  var vNew = I.List().withMutations(function(list) {
    specs.forEach(function(p) {
      var D = p.get(0);
      var v = p.get(1);
      var vD = _get(dsImpl, list, i, D);

      _assert(vD === undefined || vD == v,
              'conflicting values '+vD+' and '+v+' for '+D);

      var E = D;
      do {
        E = _get(dsImpl, dsImpl.s, i, E) || E;
        _set(dsImpl, list, i, E, v);
        E = _get(dsImpl, dsImpl.s, i+1, E) || E;
        _set(dsImpl, list, i, E, v);
      }
      while (E != D);
    });
  });

  return _fromData(dsImpl.dim, dsImpl.s, _merge(dsImpl.v, vNew));
};

var _fromData = function _fromData(dim, sData, vData) {
  var s = I.List(sData);
  var v = I.List(vData);

  var _ds = {
    s   : s,
    v   : v,
    dim : dim,
    size: v.size / dim
  };

  return {
    isElement: function(D)       { return _isElement(_ds, D); },
    elements : function()        { return _elements(_ds); },
    isIndex  : function(i)       { return _isIndex(_ds, i); },
    indices  : function()        { return _indices(_ds); },
    s        : function(i, D)    { return _s(_ds, i, D); },
    v        : function(i, j, D) { return _v(_ds, i, j, D); },
    toString : function()        { return stringify(this); },

    withPairings  : function(i, inputs) {
      return _withPairings(_ds, i, inputs);
    },
    withBranchings: function(i, inputs) {
      return _withBranchings(_ds, i, inputs);
    }
  }
};

var build = function build(dim, size, pairingsFn, branchingsFn) {
  var s = I.List().setSize((dim+1) * size);
  var v = I.List().setSize(dim * size);
  var ds0 = _fromData(dim, s, v);

  var ds1 = I.Range(0, dim+1).reduce(
    function(tmp, i) {
      return tmp.withPairings(i, pairingsFn(ds0, i));
    },
    ds0);

  var ds2 = I.Range(0, dim).reduce(
    function(tmp, i) {
      return tmp.withBranchings(i, branchingsFn(ds1, i));
    },
    ds1);

  return ds2;
};

var _parseInts = function _parseInts(str) {
  return str.trim().split(/\s+/).map(function(s) { return parseInt(s); });
};

var parse = function parse(str) {
  var parts = str.trim().replace(/^</, '').replace(/>$/, '').split(/:/);
  if (parts[0].match(/\d+\.\d+/))
    parts.shift();

  var dims = _parseInts(parts[0]);
  var size = dims[0];
  var dim  = dims[1] || 2;

  var gluings = parts[1].split(/,/).map(_parseInts);
  var degrees = parts[2].split(/,/).map(_parseInts);

  var s = new Array((dim+1) * size);
  var v = new Array(dim * size);

  var get = function get(a, i, D) { return a[i * size + D - 1]; };
  var set = function set(a, i, D, x) { a[i * size + D - 1] = x; };

  for (var i = 0; i <= dim; ++i) {
    var k = -1;
    for (var D = 1; D <= size; ++D) {
      if (!get(s, i, D)) {
        var E = gluings[i][++k];
        set(s, i, D, E);
        set(s, i, E, D);
      }
    }
  }

  for (var i = 0; i < dim; ++i) {
    var k = -1;
    for (var D = 1; D <= size; ++D) {
      if (!get(v, i, D)) {
        var m = degrees[i][++k];
        var E = D;
        var r = 0;

        do {
          E = get(s, i, E) || E;
          E = get(s, i+1, E) || E;
          ++r;
        }
        while (E != D);

        var b = m / r;

        do {
          E = get(s, i, E) || E;
          set(v, i, E, b);
          E = get(s, i+1, E) || E;
          set(v, i, E, b);
        }
        while (E != D);
      }
    }
  }

  return _fromData(dim, s, v);
};

var orbitReps1 = function orbitReps1(ds, i) {
  return ds.elements().filter(function(D) {
    return (ds.s(i, D) || D) >= D;
  });
};

var orbit2 = function orbit2(ds, i, j, D) {
  return I.Set().withMutations(function(set) {
    var E = D;
    do {
      E = ds.s(i, E) || E;
      set.add(E);
      E = ds.s(j, E) || E;
      set.add(E);
    }
    while (E != D);
  });
};

var orbitReps2 = function orbitReps2(ds, i, j) {
  var seen = new Array(ds.elements().size + 1);
  var result = [];

  ds.elements().forEach(function(D) {
    if (!seen[D]) {
      var E = D;

      do {
        E = ds.s(i, E) || E;
        seen[E] = true;
        E = ds.s(i+1, E) || E;
        seen[E] = true;
      }
      while (E != D);

      result.push(D);
    }
  });

  return I.List(result);
};

var stringify = function stringify(ds) {
  var sDefs = ds.indices()
    .map(function(i) {
      return orbitReps1(ds, i)
        .map(function(D) { return ds.s(i, D) || 0; })
        .join(' ');
    })
    .join(',');

  var mDefs = ds.indices()
    .filter(function(i) { return ds.isIndex(i+1); })
    .map(function(i) {
      return orbitReps2(ds, i, i+1)
        .map(function(D) { return m(ds, i, i+1, D) || 0; })
        .join(' ');
    })
    .join(',');

  var n = ds.elements().size;
  var d = ds.indices().size - 1;

  return '<1.1:'+n+(d == 2 ? '' : ' '+d)+':'+sDefs+':'+mDefs+'>';
};

var r = function r(ds, i, j, D) {
  var k = 0;
  var E = D;

  do {
    E = ds.s(i, E) || E;
    E = ds.s(j, E) || E;
    ++k;
  }
  while (E != D);

  return k;
};

var m = function m(ds, i, j, D) {
  return ds.v(i, j, D) * r(ds, i, j, D);
};

module.exports = {
  isElement: function(ds, D)       { return ds.isElement(D); },
  elements : function(ds)          { return ds.elements(); },
  isIndex  : function(ds, i)       { return ds.isIndex(i); },
  indices  : function(ds)          { return ds.indices(); },
  s        : function(ds, i, D)    { return ds.s(i, D); },
  v        : function(ds, i, j, D) { return ds.v(i, j, D); },

  r        : r,
  m        : m,
  dim      : function(ds) { return ds.indices().size - 1; },
  size     : function(ds) { return ds.elements().size; },

  build    : build,
  parse    : parse,
  stringify: stringify,

  orbitReps1: orbitReps1,
  orbit2    : orbit2,
  orbitReps2: orbitReps2,

  withPairings: function(ds, i, pairings) {
    return ds.withPairings(i, pairings);
  },

  withBranchings: function(ds, i, branchings) {
    return ds.withBranchings(i, branchings);
  }
};

if (require.main == module) {
  var ds = parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>');

  console.log(stringify(ds));
  console.log('' + ds);

  console.log('' + ds.withPairings(1, [[2,1]]));
  console.log('' + ds.withBranchings(0, [[2,3],[1,5]]));
}
<commit_msg>add a function for parsing a list of symbols<commit_after>'use strict';

var I = require('immutable');

var _assert = function(condition, message) {
  if (!condition)
    throw new Error(message || 'assertion error');
};

var _isElement = function _isElement(dsImpl, D) {
  return typeof D == 'number' && D >= 1 && D <= dsImpl.size;
};

var _elements = function _elements(dsImpl) {
  return I.Range(1, dsImpl.size+1);
};

var _isIndex = function _isIndex(dsImpl, i) {
  return typeof i == 'number' && i >= 0 && i <= dsImpl.dim;
};

var _indices = function _indices(dsImpl) {
  return I.Range(0, dsImpl.dim+1);
};

var _index = function offset(dsImpl, i, D) {
  return i * dsImpl.size + D - 1;
};

var _get = function offset(dsImpl, list, i, D) {
  return list.get(_index(dsImpl, i, D));
};

var _set = function offset(dsImpl, list, i, D, x) {
  return list.set(_index(dsImpl, i, D), x);
};

var _s = function _s(dsImpl, i, D) {
  if (_isElement(dsImpl, D) && _isIndex(dsImpl, i))
    return _get(dsImpl, dsImpl.s, i, D);
};

var _v = function _v(dsImpl, i, j, D) {
  if (_isElement(dsImpl, D) && _isIndex(dsImpl, i) && _isIndex(dsImpl, j)) {
    if (j == i+1)
      return _get(dsImpl, dsImpl.v, i, D);
    else if (j == i-1)
      return _get(dsImpl, dsImpl.v, j, D);
    else if (_get(dsImpl, dsImpl.s, i, D) == _get(dsImpl, dsImpl.s, j, D))
      return 2;
    else
      return 1;
  }
};

var _merge = function _merge(a, b) {
  return a.withMutations(function(list) {
    b.forEach(function(x, i) {
      if (x !== undefined)
        list.set(i, x);
    });
  });
};

var _precheckPairings = function _checkPairings(specs, size) {
  specs.forEach(function(p) {
    _assert(p.size == 1 || p.size == 2,
            'expected pair or singleton, got '+p);

    var D = p.get(0);
    var E = p.size > 1 ? p.get(1) : p.get(0);

    _assert(typeof D == 'number' && D % 1 == 0 && D > 0,
            'expected a positive integer, got '+D);
    _assert(D <= size,
            'expected at most '+size+', got '+D);

    _assert(typeof E == 'number' && E % 1 == 0 && E >= 0,
            'expected a non-negative integer, got '+E);
    _assert(E <= size,
            'expected at most '+size+', got '+E);
  });
};

var _withPairings = function _withPairings(dsImpl, i, inputs) {
  var specs = I.List(inputs).map(I.List);
  _precheckPairings(specs, dsImpl.size);

  _assert(typeof i == 'number' && i % 1 == 0 && i >= 0 && i <= dsImpl.dim,
          'expected an integer between 0 and '+dsImpl.dim+', got i');

  var sNew = I.List().withMutations(function(list) {
    var dangling = [];

    specs.forEach(function(p) {
      var D = p.get(0);
      var E = p.size > 1 ? p.get(1) : p.get(0);
      var Di = _get(dsImpl, list, i, D);
      var Ei = _get(dsImpl, list, i, E);

      _assert(Di === undefined || Di == E,
              'conflicting partners '+Di+' and '+E+' for '+D);
      _assert(Ei === undefined || Ei == D,
              'conflicting partners '+Ei+' and '+D+' for '+E);

      dangling.push(_get(dsImpl, dsImpl.s, i, D));
      dangling.push(_get(dsImpl, dsImpl.s, i, E));

      _set(dsImpl, list, i, D, E);
      _set(dsImpl, list, i, E, D);
    });

    dangling.forEach(function(D) {
      if (D && _get(dsImpl, list, i, D) === undefined)
        _set(dsImpl, list, i, D, 0);
    });
  });

  return _fromData(dsImpl.dim, _merge(dsImpl.s, sNew), dsImpl.v);
};

var _precheckBranchings = function _checkBranchings(specs, size) {
  specs.forEach(function(p) {
    _assert(p.size == 2, 'expected pair, got '+p);

    var D = p.get(0);
    var v = p.get(1);

    _assert(typeof D == 'number' && D % 1 == 0 && D > 0,
            'expected a positive integer, got '+D);
    _assert(D <= size,
            'expected at most '+size+', got '+D);

    _assert(typeof v == 'number' && v % 1 == 0 && v >= 0,
            'expected a non-negative integer, got '+v);
  });
};

var _withBranchings = function _withBranchings(dsImpl, i, inputs) {
  var specs = I.List(inputs).map(I.List);
  _precheckBranchings(specs, dsImpl.size);

  _assert(typeof i == 'number' && i % 1 == 0 && i >= 0 && i <= dsImpl.dim-1,
          'expected integer between 0 and '+dsImpl.dim-1+', got i');

  var vNew = I.List().withMutations(function(list) {
    specs.forEach(function(p) {
      var D = p.get(0);
      var v = p.get(1);
      var vD = _get(dsImpl, list, i, D);

      _assert(vD === undefined || vD == v,
              'conflicting values '+vD+' and '+v+' for '+D);

      var E = D;
      do {
        E = _get(dsImpl, dsImpl.s, i, E) || E;
        _set(dsImpl, list, i, E, v);
        E = _get(dsImpl, dsImpl.s, i+1, E) || E;
        _set(dsImpl, list, i, E, v);
      }
      while (E != D);
    });
  });

  return _fromData(dsImpl.dim, dsImpl.s, _merge(dsImpl.v, vNew));
};

var _fromData = function _fromData(dim, sData, vData) {
  var s = I.List(sData);
  var v = I.List(vData);

  var _ds = {
    s   : s,
    v   : v,
    dim : dim,
    size: v.size / dim
  };

  return {
    isElement: function(D)       { return _isElement(_ds, D); },
    elements : function()        { return _elements(_ds); },
    isIndex  : function(i)       { return _isIndex(_ds, i); },
    indices  : function()        { return _indices(_ds); },
    s        : function(i, D)    { return _s(_ds, i, D); },
    v        : function(i, j, D) { return _v(_ds, i, j, D); },
    toString : function()        { return stringify(this); },

    withPairings  : function(i, inputs) {
      return _withPairings(_ds, i, inputs);
    },
    withBranchings: function(i, inputs) {
      return _withBranchings(_ds, i, inputs);
    }
  }
};

var build = function build(dim, size, pairingsFn, branchingsFn) {
  var s = I.List().setSize((dim+1) * size);
  var v = I.List().setSize(dim * size);
  var ds0 = _fromData(dim, s, v);

  var ds1 = I.Range(0, dim+1).reduce(
    function(tmp, i) {
      return tmp.withPairings(i, pairingsFn(ds0, i));
    },
    ds0);

  var ds2 = I.Range(0, dim).reduce(
    function(tmp, i) {
      return tmp.withBranchings(i, branchingsFn(ds1, i));
    },
    ds1);

  return ds2;
};

var _parseInts = function _parseInts(str) {
  return str.trim().split(/\s+/).map(function(s) { return parseInt(s); });
};

var parse = function parse(str) {
  var parts = str.trim().replace(/^</, '').replace(/>$/, '').split(/:/);
  if (parts[0].match(/\d+\.\d+/))
    parts.shift();

  var dims = _parseInts(parts[0]);
  var size = dims[0];
  var dim  = dims[1] || 2;

  var gluings = parts[1].split(/,/).map(_parseInts);
  var degrees = parts[2].split(/,/).map(_parseInts);

  var s = new Array((dim+1) * size);
  var v = new Array(dim * size);

  var get = function get(a, i, D) { return a[i * size + D - 1]; };
  var set = function set(a, i, D, x) { a[i * size + D - 1] = x; };

  for (var i = 0; i <= dim; ++i) {
    var k = -1;
    for (var D = 1; D <= size; ++D) {
      if (!get(s, i, D)) {
        var E = gluings[i][++k];
        set(s, i, D, E);
        set(s, i, E, D);
      }
    }
  }

  for (var i = 0; i < dim; ++i) {
    var k = -1;
    for (var D = 1; D <= size; ++D) {
      if (!get(v, i, D)) {
        var m = degrees[i][++k];
        var E = D;
        var r = 0;

        do {
          E = get(s, i, E) || E;
          E = get(s, i+1, E) || E;
          ++r;
        }
        while (E != D);

        var b = m / r;

        do {
          E = get(s, i, E) || E;
          set(v, i, E, b);
          E = get(s, i+1, E) || E;
          set(v, i, E, b);
        }
        while (E != D);
      }
    }
  }

  return _fromData(dim, s, v);
};

var orbitReps1 = function orbitReps1(ds, i) {
  return ds.elements().filter(function(D) {
    return (ds.s(i, D) || D) >= D;
  });
};

var orbit2 = function orbit2(ds, i, j, D) {
  return I.Set().withMutations(function(set) {
    var E = D;
    do {
      E = ds.s(i, E) || E;
      set.add(E);
      E = ds.s(j, E) || E;
      set.add(E);
    }
    while (E != D);
  });
};

var orbitReps2 = function orbitReps2(ds, i, j) {
  var seen = new Array(ds.elements().size + 1);
  var result = [];

  ds.elements().forEach(function(D) {
    if (!seen[D]) {
      var E = D;

      do {
        E = ds.s(i, E) || E;
        seen[E] = true;
        E = ds.s(i+1, E) || E;
        seen[E] = true;
      }
      while (E != D);

      result.push(D);
    }
  });

  return I.List(result);
};

var stringify = function stringify(ds) {
  var sDefs = ds.indices()
    .map(function(i) {
      return orbitReps1(ds, i)
        .map(function(D) { return ds.s(i, D) || 0; })
        .join(' ');
    })
    .join(',');

  var mDefs = ds.indices()
    .filter(function(i) { return ds.isIndex(i+1); })
    .map(function(i) {
      return orbitReps2(ds, i, i+1)
        .map(function(D) { return m(ds, i, i+1, D) || 0; })
        .join(' ');
    })
    .join(',');

  var n = ds.elements().size;
  var d = ds.indices().size - 1;

  return '<1.1:'+n+(d == 2 ? '' : ' '+d)+':'+sDefs+':'+mDefs+'>';
};

var r = function r(ds, i, j, D) {
  var k = 0;
  var E = D;

  do {
    E = ds.s(i, E) || E;
    E = ds.s(j, E) || E;
    ++k;
  }
  while (E != D);

  return k;
};

var m = function m(ds, i, j, D) {
  return ds.v(i, j, D) * r(ds, i, j, D);
};

module.exports = {
  isElement: function(ds, D)       { return ds.isElement(D); },
  elements : function(ds)          { return ds.elements(); },
  isIndex  : function(ds, i)       { return ds.isIndex(i); },
  indices  : function(ds)          { return ds.indices(); },
  s        : function(ds, i, D)    { return ds.s(i, D); },
  v        : function(ds, i, j, D) { return ds.v(i, j, D); },

  r        : r,
  m        : m,
  dim      : function(ds) { return ds.indices().size - 1; },
  size     : function(ds) { return ds.elements().size; },

  build    : build,
  parse    : parse,
  stringify: stringify,

  orbitReps1: orbitReps1,
  orbit2    : orbit2,
  orbitReps2: orbitReps2,

  withPairings: function(ds, i, pairings) {
    return ds.withPairings(i, pairings);
  },

  withBranchings: function(ds, i, branchings) {
    return ds.withBranchings(i, branchings);
  },

  parseSymbols: function(text) {
    return text
      .split('\n')
      .filter(function(line) {
        var t = line.trim();
        return t.length > 0 && t[0] != '#';
      })
      .map(parse);
  }
};

if (require.main == module) {
  var ds = parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>');

  console.log(stringify(ds));
  console.log('' + ds);

  console.log('' + ds.withPairings(1, [[2,1]]));
  console.log('' + ds.withBranchings(0, [[2,3],[1,5]]));
}
<|endoftext|>


<commit_before>import { Component } from './component';

export let i = 0;

/**
 *
 * @param {any} defaultValue
 */
export function createContext(defaultValue) {
	let id = '__cC' + i++;

	function Consumer(props, context) {
		let value = id in context ? context[id].props.value : defaultValue;
		this.state = { value };
	}
	Consumer.prototype = new Component();
	Consumer.prototype.componentDidMount = function() {
		if (id in this.context) this.context[id]._subscribers.push(this);
	};
	Component.prototype.componentWillUnmount = function() {
		let ctx = this.context;
		if (id in ctx) {
			let s = ctx[id]._subscribers;
			s.splice(s.indexOf(this), 1);
		}
	};
	Consumer.prototype.render = function(props, state) {
		return props.children(state.value);
	};

	let ctx = { [id]: null };

	function Provider() {
		Component.call(this);
	}
	Provider.prototype._subscribers = [];
	Provider.prototype.getChildContext = function() {
		ctx[id] = this;
		return ctx;
	};
	Provider.prototype.componentDidUpdate = function() {
		let v = this.props.value;
		this._subscribers.forEach(c => v!==c.state.value && c.setState({ value: v }));
	};
	Provider.prototype.render = function(props) {
		return props.children;
	};

	return {
		_id: id,
		_defaultValue: defaultValue,
		Provider,
		Consumer
	};
}
<commit_msg>Remove unneeded constructor call<commit_after>import { Component } from './component';

export let i = 0;

/**
 *
 * @param {any} defaultValue
 */
export function createContext(defaultValue) {
	let id = '__cC' + i++;

	function Consumer(props, context) {
		let value = id in context ? context[id].props.value : defaultValue;
		this.state = { value };
	}
	Consumer.prototype = new Component();
	Consumer.prototype.componentDidMount = function() {
		if (id in this.context) this.context[id]._subscribers.push(this);
	};
	Component.prototype.componentWillUnmount = function() {
		let ctx = this.context;
		if (id in ctx) {
			let s = ctx[id]._subscribers;
			s.splice(s.indexOf(this), 1);
		}
	};
	Consumer.prototype.render = function(props, state) {
		return props.children(state.value);
	};

	let ctx = { [id]: null };

	function Provider() {}
	Provider.prototype._subscribers = [];
	Provider.prototype.getChildContext = function() {
		ctx[id] = this;
		return ctx;
	};
	Provider.prototype.componentDidUpdate = function() {
		let v = this.props.value;
		this._subscribers.forEach(c => v!==c.state.value && c.setState({ value: v }));
	};
	Provider.prototype.render = function(props) {
		return props.children;
	};

	return {
		_id: id,
		_defaultValue: defaultValue,
		Provider,
		Consumer
	};
}
<|endoftext|>


<commit_before>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import ResourceBase from './base/ResourceBase';

export default class JobResult extends ResourceBase {
  get resourcePath() {
    return '/jobs/{job_id}/revisions/{job_revision_id}/result';
  }

  get resourceName() {
    return 'job-result';
  }

  /**
   * Job result archive url
   * @returns {string} - Archive url
   */
  get archiveUrl() {
    return `${this.url}/archive`;
  }

  /**
   * Job result preview url, usable in an `<img>` tag
   * @returns {string} - Preview url
   */
  get previewUrl() {
    return `${this.url}/preview`;
  }
}
<commit_msg>::downloadPreview<commit_after>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import ResourceBase from './base/ResourceBase';
import ApiError from '../errors/ApiError';
import {fetch} from '../utils/requests';

export default class JobResult extends ResourceBase {
  get resourcePath() {
    return '/jobs/{job_id}/revisions/{job_revision_id}/result';
  }

  get resourceName() {
    return 'job-result';
  }

  /**
   * Job result archive url
   * @returns {string} - Archive url
   */
  get archiveUrl() {
    return `${this.url}/archive`;
  }

  /**
   * Job result preview url, usable in an `<img>` tag
   * @returns {string} - Preview url
   */
  get previewUrl() {
    return `${this.url}/preview`;
  }

  /**
   * Get image base64 representation
   * @returns {Promise} - Resolves with a {@link String} containing a blob reference to the image and rejects with {@link ApiError}
   */
  downloadPreview() {
    const headers = {
      Accept: 'application/json',
      Authorization: this.api.auth.token.toString(),
    };

    return fetch(this.previewUrl, {headers})
      .then(res => {
        if (res.ok) {
          return res.blob();
        } else {
          return res.json().then(data => {
            const err = data.error;

            throw new ApiError(err.type, err.message, res.status);
          });
        }
      })
      .then(blob => (window.URL || window.webkitURL).createObjectURL(blob));
  }
}
<|endoftext|>


<commit_before>/**
 * curl js! plugin
 *
 * (c) copyright 2010-2012 Brian Cavalier and John Hann
 *
 * curl is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * 		http://www.opensource.org/licenses/mit-license.php
 *
 */
/**
 * usage:
 *  require(['ModuleA', 'js!myNonAMDFile.js!order', 'js!anotherFile.js!order], function (ModuleA) {
 * 		var a = new ModuleA();
 * 		document.body.appendChild(a.domNode);
 * 	});
 *
 * Specify the !order suffix for files that must be evaluated in order.
 * Using the !order option and requiring js files more than once doesn't make
 * much sense since files are loaded exactly once.
 *
 * Specify the !exports=someGlobalVar option to return a global variable to
 * the module depending on the javascript file. Using this option also allows
 * positive error feedback to the loader since it can now detect if the
 * javascript file failed to load correctly.
 *
 * Async=false rules learned from @getify's LABjs!
 * http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
 *
 */
(function (global, doc) {
"use strict";
	var cache = {},
		queue = [],
		supportsAsyncFalse = doc.createElement('script').async == true,
		readyStates = { 'loaded': 1, 'interactive': 1, 'complete': 1 },
		orsc = 'onreadystatechange',
		head = doc['head'] || doc.getElementsByTagName('head')[0],
		waitForOrderedScript,
		undef;

	function nameWithExt (name, defaultExt) {
		return name.lastIndexOf('.') <= name.lastIndexOf('/') ?
			name + '.' + defaultExt : name;
	}

	// TODO: find a way to reuse the loadScript from curl.js
	function loadScript (def, success, failure) {
		// script processing rules learned from RequireJS

		var deadline, el;

		// default deadline is very far in the future (5 min)
		// devs should set something reasonable if they want to use it
		deadline = new Date().valueOf() + (def.timeoutMsec || 300) * 1000;

		// insert script
		el = doc.createElement('script');

		// initial script processing
		function process (ev) {
			ev = ev || global.event;
			// detect when it's done loading
			if (ev.type == 'load' || readyStates[el.readyState]) {
				// release event listeners
				el.onload = el[orsc] = el.onerror = "";
				if (def.exports) def.resolved = testGlobalVar(def.exports);
				if (!def.exports || def.resolved) {
					success(el);
				}
				else {
					fail();
				}
			}
		}

		function fail () {
			// some browsers send an event, others send a string,
			// but none of them send anything useful, so just say we failed:
			el.onload = el[orsc] = el.onerror = "";
			if (failure) {
				failure(new Error('Script error or http error: ' + def.url));
			}
		}

		// some browsers (Opera and IE6-8) don't support onerror and don't fire
		// readystatechange if the script fails to load so we need to poll.
		// this poller only runs if def.exports is specified and failure callback
		// is defined (see below)
		function poller () {
			// if the script loaded
			if (el.onload && readyStates[el.readyState]) {
				process({});
			}
			// if neither process or fail as run and our deadline is in the past
			else if (el.onload && deadline < new Date()) {
				fail();
			}
			else {
				setTimeout(poller, 10);
			}
		}
		if (failure && def.exports) setTimeout(poller, 10);

		// set type first since setting other properties could
		// prevent us from setting this later
		el.type = def.mimetype || 'text/javascript';
		// using dom0 event handlers instead of wordy w3c/ms
		el.onload = el[orsc] = process;
		el.onerror = fail;
		el.charset = def.charset || 'utf-8';
		el.async = !def.order;
		el.src = def.url;

		// use insertBefore to keep IE from throwing Operation Aborted (thx Bryan Forbes!)
		head.insertBefore(el, head.firstChild);

	}

	function fetch (def, promise) {

		loadScript(def,
			function () {
				// if there's another queued script
				var next = queue.shift();
				waitForOrderedScript = queue.length > 0;
				if (next) {
					// go get it (from cache hopefully)
					fetch.apply(null, next);
				}
				promise['resolve'](def.resolved || true);
			},
			function (ex) {
				promise['reject'](ex);
			}
		);

	}

	function testGlobalVar (varName) {
		try {
			return eval('global.' + varName);
		}
		catch (ex) {
			return undef;
		}
	}

	define(/*=='js',==*/ {

		// the !options force us to cache ids in the plugin
		'dynamic': true,

		'load': function (name, require, callback, config) {

			var order, exportsPos, exports, prefetch, def, promise;

			order = name.indexOf('!order') > 0; // can't be zero
			exportsPos = name.indexOf('!exports=');
			exports = exportsPos > 0 && name.substr(exportsPos + 9); // must be last option!
			prefetch = 'prefetch' in config ? config['prefetch'] : true;
			name = order || exportsPos > 0 ? name.substr(0, name.indexOf('!')) : name;

			// if we've already fetched this resource, get it out of the cache
			if (name in cache) {
				callback(cache[name]);
			}
			else {
				cache[name] = undef;
				def = {
					name: name,
					url: require['toUrl'](nameWithExt(name, 'js')),
					order: order,
					exports: exports,
					timeoutMsec: config['timeout']
				};
				promise = {
					'resolve': function (o) {
						cache[name] = o;
						(callback['resolve'] || callback)(o);
					},
					'reject': callback['reject'] || function (ex) { throw ex; }
				};

				// if this script has to wait for another
				// or if we're loading, but not executing it
				if (order && !supportsAsyncFalse && waitForOrderedScript) {
					// push onto the stack of scripts that will be fetched
					// from cache. do this before fetch in case IE has file cached.
					queue.push([def, promise]);
					// if we're prefetching
					if (prefetch) {
						// go get the file under an unknown mime type
						def.mimetype = 'text/cache';
						loadScript(def,
							// remove the fake script when loaded
							function (el) { el.parentNode.removeChild(el); },
							false
						);
						def.mimetype = '';
					}
				}
				// otherwise, just go get it
				else {
					waitForOrderedScript = waitForOrderedScript || order;
					fetch(def, promise);
				}
			}

		}

	});

}(this, document));
<commit_msg>works in rhino now<commit_after>/**
 * curl js! plugin
 *
 * (c) copyright 2010-2012 Brian Cavalier and John Hann
 *
 * curl is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * 		http://www.opensource.org/licenses/mit-license.php
 *
 */
/**
 * usage:
 *  require(['ModuleA', 'js!myNonAMDFile.js!order', 'js!anotherFile.js!order], function (ModuleA) {
 * 		var a = new ModuleA();
 * 		document.body.appendChild(a.domNode);
 * 	});
 *
 * Specify the !order suffix for files that must be evaluated in order.
 * Using the !order option and requiring js files more than once doesn't make
 * much sense since files are loaded exactly once.
 *
 * Specify the !exports=someGlobalVar option to return a global variable to
 * the module depending on the javascript file. Using this option also allows
 * positive error feedback to the loader since it can now detect if the
 * javascript file failed to load correctly.
 *
 * Async=false rules learned from @getify's LABjs!
 * http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
 *
 */
(function (global, doc) {
"use strict";
	var cache = {},
		queue = [],
		supportsAsyncFalse = doc && doc.createElement('script').async == true,
		readyStates = { 'loaded': 1, 'interactive': 1, 'complete': 1 },
		orsc = 'onreadystatechange',
		head = doc && (doc['head'] || doc.getElementsByTagName('head')[0]),
		waitForOrderedScript,
		undef;

	function nameWithExt (name, defaultExt) {
		return name.lastIndexOf('.') <= name.lastIndexOf('/') ?
			name + '.' + defaultExt : name;
	}

	// TODO: find a way to reuse the loadScript from curl.js
	function loadScript (def, success, failure) {
		// script processing rules learned from RequireJS

		var deadline, el;

		// default deadline is very far in the future (5 min)
		// devs should set something reasonable if they want to use it
		deadline = new Date().valueOf() + (def.timeoutMsec || 300) * 1000;

		// insert script
		el = doc.createElement('script');

		// initial script processing
		function process (ev) {
			ev = ev || global.event;
			// detect when it's done loading
			if (ev.type == 'load' || readyStates[el.readyState]) {
				// release event listeners
				el.onload = el[orsc] = el.onerror = "";
				if (def.exports) def.resolved = testGlobalVar(def.exports);
				if (!def.exports || def.resolved) {
					success(el);
				}
				else {
					fail();
				}
			}
		}

		function fail () {
			// some browsers send an event, others send a string,
			// but none of them send anything useful, so just say we failed:
			el.onload = el[orsc] = el.onerror = "";
			if (failure) {
				failure(new Error('Script error or http error: ' + def.url));
			}
		}

		// some browsers (Opera and IE6-8) don't support onerror and don't fire
		// readystatechange if the script fails to load so we need to poll.
		// this poller only runs if def.exports is specified and failure callback
		// is defined (see below)
		function poller () {
			// if the script loaded
			if (el.onload && readyStates[el.readyState]) {
				process({});
			}
			// if neither process or fail as run and our deadline is in the past
			else if (el.onload && deadline < new Date()) {
				fail();
			}
			else {
				setTimeout(poller, 10);
			}
		}
		if (failure && def.exports) setTimeout(poller, 10);

		// set type first since setting other properties could
		// prevent us from setting this later
		el.type = def.mimetype || 'text/javascript';
		// using dom0 event handlers instead of wordy w3c/ms
		el.onload = el[orsc] = process;
		el.onerror = fail;
		el.charset = def.charset || 'utf-8';
		el.async = !def.order;
		el.src = def.url;

		// use insertBefore to keep IE from throwing Operation Aborted (thx Bryan Forbes!)
		head.insertBefore(el, head.firstChild);

	}

	function fetch (def, promise) {

		loadScript(def,
			function () {
				// if there's another queued script
				var next = queue.shift();
				waitForOrderedScript = queue.length > 0;
				if (next) {
					// go get it (from cache hopefully)
					fetch.apply(null, next);
				}
				promise['resolve'](def.resolved || true);
			},
			function (ex) {
				promise['reject'](ex);
			}
		);

	}

	function testGlobalVar (varName) {
		try {
			return eval('global.' + varName);
		}
		catch (ex) {
			return undef;
		}
	}

	define(/*=='js',==*/ {

		// the !options force us to cache ids in the plugin
		'dynamic': true,

		'load': function (name, require, callback, config) {

			var order, exportsPos, exports, prefetch, def, promise;

			order = name.indexOf('!order') > 0; // can't be zero
			exportsPos = name.indexOf('!exports=');
			exports = exportsPos > 0 && name.substr(exportsPos + 9); // must be last option!
			prefetch = 'prefetch' in config ? config['prefetch'] : true;
			name = order || exportsPos > 0 ? name.substr(0, name.indexOf('!')) : name;

			// if we've already fetched this resource, get it out of the cache
			if (name in cache) {
				callback(cache[name]);
			}
			else {
				cache[name] = undef;
				def = {
					name: name,
					url: require['toUrl'](nameWithExt(name, 'js')),
					order: order,
					exports: exports,
					timeoutMsec: config['timeout']
				};
				promise = {
					'resolve': function (o) {
						cache[name] = o;
						(callback['resolve'] || callback)(o);
					},
					'reject': callback['reject'] || function (ex) { throw ex; }
				};

				// if this script has to wait for another
				// or if we're loading, but not executing it
				if (order && !supportsAsyncFalse && waitForOrderedScript) {
					// push onto the stack of scripts that will be fetched
					// from cache. do this before fetch in case IE has file cached.
					queue.push([def, promise]);
					// if we're prefetching
					if (prefetch) {
						// go get the file under an unknown mime type
						def.mimetype = 'text/cache';
						loadScript(def,
							// remove the fake script when loaded
							function (el) { el.parentNode.removeChild(el); },
							false
						);
						def.mimetype = '';
					}
				}
				// otherwise, just go get it
				else {
					waitForOrderedScript = waitForOrderedScript || order;
					fetch(def, promise);
				}
			}

		}

	});

}(this, this.document));
<|endoftext|>


<commit_before>/** MIT License (c) copyright B Cavalier & J Hann */

/**
 * curl js! plugin
 *
 * Licensed under the MIT License at:
 * 		http://www.opensource.org/licenses/mit-license.php
 *
 */

/**
 * usage:
 *  require(['ModuleA', 'js!myNonAMDFile.js!order', 'js!anotherFile.js!order], function (ModuleA) {
 * 		var a = new ModuleA();
 * 		document.body.appendChild(a.domNode);
 * 	});
 *
 * Specify the !order suffix for files that must be evaluated in order.
 * Using the !order option and requiring js files more than once doesn't make
 * much sense since files are loaded exactly once.
 *
 * Specify the !exports=someGlobalVar option to return a global variable to
 * the module depending on the javascript file. Using this option also allows
 * positive error feedback to the loader since it can now detect if the
 * javascript file failed to load correctly.
 *
 * Async=false rules learned from @getify's LABjs!
 * http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
 *
 */
(function (global, doc) {
define(/*=='js',==*/ ['curl/_privileged'], function (priv) {
"use strict";
	var cache = {},
		queue = [],
		supportsAsyncFalse = doc && doc.createElement('script').async == true,
		waitForOrderedScript,
		undef;

	function nameWithExt (name, defaultExt) {
		return name.lastIndexOf('.') <= name.lastIndexOf('/') ?
			name + '.' + defaultExt : name;
	}

	function loadScript (def, success, failure) {
		// script processing rules learned from RequireJS

		var deadline, completed;

		// default deadline is very far in the future (5 min)
		// devs should set something reasonable if they want to use it
		deadline = new Date().valueOf() + (def.timeoutMsec || 300000);

		// initial script processing
		function process () {
			completed = true;
			if (def.exports) def.resolved = testGlobalVar(def.exports);
			if (!def.exports || def.resolved) {
				success();
			}
			else {
				failure();
			}
		}

		function fail (ex) {
			// Exception is squashed by curl.js unfortunately
			completed = true;
			failure(ex);
		}

		// some browsers (Opera and IE6-8) don't support onerror and don't fire
		// readystatechange if the script fails to load so we need to poll.
		// this poller only runs if def.exports is specified and failure callback
		// is defined (see below)
		function poller () {
			// if the script loaded
			if (!completed) {
				// if neither process or fail as run and our deadline is in the past
				if (deadline < new Date()) {
					failure();
				}
				else {
					setTimeout(poller, 10);
				}
			}
		}
		if (failure && def.exports) setTimeout(poller, 10);

		priv.core.loadScript(def, process, fail);

	}

	function fetch (def, promise) {

		loadScript(def,
			function () {
				// if there's another queued script
				var next = queue.shift();
				waitForOrderedScript = queue.length > 0;
				if (next) {
					// go get it (from cache hopefully)
					fetch.apply(null, next);
				}
				promise['resolve'](def.resolved || true);
			},
			function (ex) {
				promise['reject'](ex);
			}
		);

	}

	function testGlobalVar (varName) {
		try {
			return eval('global.' + varName);
		}
		catch (ex) {
			return undef;
		}
	}

	return {

		// the !options force us to cache ids in the plugin
		'dynamic': true,

		'load': function (name, require, callback, config) {

			var order, exportsPos, exports, prefetch, def, promise;

			order = name.indexOf('!order') > 0; // can't be zero
			exportsPos = name.indexOf('!exports=');
			exports = exportsPos > 0 && name.substr(exportsPos + 9); // must be last option!
			prefetch = 'prefetch' in config ? config['prefetch'] : true;
			name = order || exportsPos > 0 ? name.substr(0, name.indexOf('!')) : name;

			// if we've already fetched this resource, get it out of the cache
			if (name in cache) {
				callback(cache[name]);
			}
			else {
				cache[name] = undef;
				def = {
					name: name,
					url: require['toUrl'](nameWithExt(name, 'js')),
					order: order,
					exports: exports,
					timeoutMsec: config['timeout']
				};
				promise = {
					'resolve': function (o) {
						cache[name] = o;
						(callback['resolve'] || callback)(o);
					},
					'reject': callback['reject'] || function (ex) { throw ex; }
				};

				// if this script has to wait for another
				// or if we're loading, but not executing it
				if (order && !supportsAsyncFalse && waitForOrderedScript) {
					// push onto the stack of scripts that will be fetched
					// from cache. do this before fetch in case IE has file cached.
					queue.push([def, promise]);
					// if we're prefetching
					if (prefetch) {
						// go get the file under an unknown mime type
						def.mimetype = 'text/cache';
						loadScript(def,
							// remove the fake script when loaded
							function (el) { el.parentNode.removeChild(el); },
							function () {}
						);
						def.mimetype = '';
					}
				}
				// otherwise, just go get it
				else {
					waitForOrderedScript = waitForOrderedScript || order;
					fetch(def, promise);
				}
			}

		}

	};
});
}(this, this.document));
<commit_msg>fix test for global variable when file is compressed<commit_after>/** MIT License (c) copyright B Cavalier & J Hann */

/**
 * curl js! plugin
 *
 * Licensed under the MIT License at:
 * 		http://www.opensource.org/licenses/mit-license.php
 *
 */

/**
 * usage:
 *  require(['ModuleA', 'js!myNonAMDFile.js!order', 'js!anotherFile.js!order], function (ModuleA) {
 * 		var a = new ModuleA();
 * 		document.body.appendChild(a.domNode);
 * 	});
 *
 * Specify the !order suffix for files that must be evaluated in order.
 * Using the !order option and requiring js files more than once doesn't make
 * much sense since files are loaded exactly once.
 *
 * Specify the !exports=someGlobalVar option to return a global variable to
 * the module depending on the javascript file. Using this option also allows
 * positive error feedback to the loader since it can now detect if the
 * javascript file failed to load correctly.
 *
 * Async=false rules learned from @getify's LABjs!
 * http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
 *
 */
(function (global, doc, testGlobalVar) {
define(/*=='js',==*/ ['curl/_privileged'], function (priv) {
"use strict";
	var cache = {},
		queue = [],
		supportsAsyncFalse = doc && doc.createElement('script').async == true,
		waitForOrderedScript,
		undef;

	function nameWithExt (name, defaultExt) {
		return name.lastIndexOf('.') <= name.lastIndexOf('/') ?
			name + '.' + defaultExt : name;
	}

	function loadScript (def, success, failure) {
		// script processing rules learned from RequireJS

		var deadline, completed;

		// default deadline is very far in the future (5 min)
		// devs should set something reasonable if they want to use it
		deadline = new Date().valueOf() + (def.timeoutMsec || 300000);

		// initial script processing
		function process () {
			completed = true;
			if (def.exports) def.resolved = testGlobalVar(def.exports);
			if (!def.exports || def.resolved) {
				success();
			}
			else {
				failure();
			}
		}

		function fail (ex) {
			// Exception is squashed by curl.js unfortunately
			completed = true;
			failure(ex);
		}

		// some browsers (Opera and IE6-8) don't support onerror and don't fire
		// readystatechange if the script fails to load so we need to poll.
		// this poller only runs if def.exports is specified and failure callback
		// is defined (see below)
		function poller () {
			// if the script loaded
			if (!completed) {
				// if neither process or fail as run and our deadline is in the past
				if (deadline < new Date()) {
					failure();
				}
				else {
					setTimeout(poller, 10);
				}
			}
		}
		if (failure && def.exports) setTimeout(poller, 10);

		priv.core.loadScript(def, process, fail);

	}

	function fetch (def, promise) {

		loadScript(def,
			function () {
				// if there's another queued script
				var next = queue.shift();
				waitForOrderedScript = queue.length > 0;
				if (next) {
					// go get it (from cache hopefully)
					fetch.apply(null, next);
				}
				promise['resolve'](def.resolved || true);
			},
			function (ex) {
				promise['reject'](ex);
			}
		);

	}

	return {

		// the !options force us to cache ids in the plugin
		'dynamic': true,

		'load': function (name, require, callback, config) {

			var order, exportsPos, exports, prefetch, def, promise;

			order = name.indexOf('!order') > 0; // can't be zero
			exportsPos = name.indexOf('!exports=');
			exports = exportsPos > 0 && name.substr(exportsPos + 9); // must be last option!
			prefetch = 'prefetch' in config ? config['prefetch'] : true;
			name = order || exportsPos > 0 ? name.substr(0, name.indexOf('!')) : name;

			// if we've already fetched this resource, get it out of the cache
			if (name in cache) {
				callback(cache[name]);
			}
			else {
				cache[name] = undef;
				def = {
					name: name,
					url: require['toUrl'](nameWithExt(name, 'js')),
					order: order,
					exports: exports,
					timeoutMsec: config['timeout']
				};
				promise = {
					'resolve': function (o) {
						cache[name] = o;
						(callback['resolve'] || callback)(o);
					},
					'reject': callback['reject'] || function (ex) { throw ex; }
				};

				// if this script has to wait for another
				// or if we're loading, but not executing it
				if (order && !supportsAsyncFalse && waitForOrderedScript) {
					// push onto the stack of scripts that will be fetched
					// from cache. do this before fetch in case IE has file cached.
					queue.push([def, promise]);
					// if we're prefetching
					if (prefetch) {
						// go get the file under an unknown mime type
						def.mimetype = 'text/cache';
						loadScript(def,
							// remove the fake script when loaded
							function (el) { el.parentNode.removeChild(el); },
							function () {}
						);
						def.mimetype = '';
					}
				}
				// otherwise, just go get it
				else {
					waitForOrderedScript = waitForOrderedScript || order;
					fetch(def, promise);
				}
			}

		}

	};
});
}(
	this,
	this.document,
	function () { try { return eval(arguments[0]); } catch (ex) { return; } }
));
<|endoftext|>