<commit_before>"use strict";

var logger = require('./server/logger.js').get('appium')
  , fs = require('fs')
  , request = require('request')
  , _ = require('underscore')
  , path = require('path')
  , exec = require('child_process').exec
  , osType = require('os').type()
  , tempdir = require('./tempdir')
  , AdmZip = require('adm-zip');

exports.downloadFile = function (fileUrl, suffix, cb) {
  // We will be downloading the files to a directory, so make sure it's there
  // This step is not required if you have manually created the directory
  tempdir.open({prefix: 'appium-app', suffix: suffix}, function (err, info) {
    var file = fs.createWriteStream(info.path);
    request(fileUrl).pipe(file).on('close', function () {
      logger.debug(fileUrl + ' downloaded to ' + info.path);
      cb(info.path);
    });
  });
};

exports.copyLocalZip = function (localZipPath, cb) {
  logger.debug("Copying local zip to tmp dir");
  fs.stat(localZipPath, function (err) {
    if (err) return cb(err);
    tempdir.open({prefix: 'appium-app', suffix: '.zip'}, function (err, info) {
      var infile = fs.createReadStream(localZipPath);
      var outfile = fs.createWriteStream(info.path);
      infile.pipe(outfile).on('close', function () {
        logger.debug(localZipPath + ' copied to ' + info.path);
        cb(null, info.path);
      });
    });
  });
};

exports.unzipFile = function (zipPath, cb) {
  logger.debug("Unzipping " + zipPath);
  exports.testZipArchive(zipPath, function (err, valid) {
    if (valid) {
      if (exports.isWindows()) {
        var zip = new AdmZip(zipPath);
        zip.extractAllTo(path.dirname(zipPath), true);
        logger.debug("Unzip successful");
        cb(null, null);
      } else {
        var execEnv = _.clone(process.env);
        delete execEnv.UNZIP;
        var execOpts = {cwd: path.dirname(zipPath), maxBuffer: 524288,
                        env: execEnv};
        exec('unzip -o ' + zipPath, execOpts, function (err, stderr, stdout) {
          if (!err) {
            logger.debug("Unzip successful");
            cb(null, stderr);
          } else {
            logger.error("Unzip threw error " + err);
            logger.error("Stderr: " + stderr);
            logger.error("Stdout: " + stdout);
            cb("Archive could not be unzipped, check appium logs.", null);
          }
        });
      }
    } else {
      cb(err, null);
    }
  });
};

exports.testZipArchive = function (zipPath, cb) {
  logger.debug("Testing zip archive: " + zipPath);
  if (exports.isWindows()) {
    if (fs.existsSync(zipPath)) {
      logger.debug("Zip archive tested clean");
      cb(null, true);
    } else {
      cb("Zip archive was not found.", false);
    }
  } else {
    var execEnv = _.clone(process.env);
    delete execEnv.UNZIP;
    var execOpts = {cwd: path.dirname(zipPath), maxBuffer: 524288,
                    env: execEnv};
    exec("unzip -tq " + zipPath, execOpts, function (err, stderr, stdout) {
      if (!err) {
        if (/No errors detected/.exec(stderr)) {
          logger.debug("Zip archive tested clean");
          cb(null, true);
        } else {
          logger.error("Zip file " + zipPath + " was not valid");
          logger.error("Stderr: " + stderr);
          logger.error("Stdout: " + stdout);
          cb("Zip archive did not test successfully, check appium server logs " +
             "for output", false);
        }
      } else {
        logger.error("Test zip archive threw error " + err);
        logger.error("Stderr: " + stderr);
        logger.error("Stdout: " + stdout);
        cb("Error testing zip archive, are you sure this is a zip file? " + err, null);
      }
    });
  }
};

exports.unzipApp = function (zipPath, appExt, cb) {
  exec("find " + path.dirname(zipPath) + " -type d -name '*" + appExt + "' | xargs rm -rf " + path.dirname(zipPath) +
    "/Payload*", function (error /*, stdout, stderr*/) {
    if (!error) {
      exports.unzipFile(zipPath, function (err, output) {
        if (!err) {
          var relaxedRegStr = "(?:creating|inflating|extracting): (.+" + appExt + ")/?";
          // in the strict regex, we check for an entry which ends with the
          // extension
          var strictReg = new RegExp(relaxedRegStr + "$", 'm');
          // otherwise, we allow an entry which contains the extension, but we
          // need to be careful, because it might be a false positive
          var relaxedReg = new RegExp(relaxedRegStr, 'm');
          var strictMatch = strictReg.exec(output);
          var relaxedMatch = relaxedReg.exec(output);
          var getAppPath = function (match) {
            return path.resolve(path.dirname(zipPath), match[1]);
          };
          if (strictMatch) {
            cb(null, getAppPath(strictMatch));
          } else if (relaxedMatch) {
            logger.debug("Got a relaxed match for app in zip, be careful for app match errors");
            cb(null, getAppPath(relaxedMatch));
          } else {
            cb("App zip unzipped OK, but we couldn't find a .app bundle in it. " +
              "Make sure your archive contains the .app package and nothing else",
              null);
          }
        } else {
          cb(err, null);
        }
      });
    } else {
      cb(error, null);
    }
  });
};

exports.getUser = function (cb) {
  logger.debug("Determining current user");
  exec("whoami", { maxBuffer: 524288 }, function (err, stdout) {
    if (err) {
      logger.error(err);
      cb(err);
    } else {
      logger.debug("User is " + stdout.trim());
      cb(null, stdout.trim());
    }
  });
};

exports.multiResolve = function (roots) {
  var args = Array.prototype.slice.call(arguments, 1);
  var paths = [];
  _.each(roots, function (root) {
    var resolveArgs = [root].concat(args);
    paths.push(path.resolve.apply(null, resolveArgs));
  });
  return paths;
};

exports.delay = function (secs) {
  var date = new Date();
  var curDate = null;
  do { curDate = new Date(); }
  while (curDate - date < (secs * 1000.0));
};

exports.escapeSpecialChars = function (str, quoteEscape) {
  if (typeof str !== "string") {
    return str;
  }
  if (typeof quoteEscape === "undefined") {
    quoteEscape = false;
  }
  str = str
        .replace(/[\\]/g, '\\\\')
        .replace(/[\/]/g, '\\/')
        .replace(/[\b]/g, '\\b')
        .replace(/[\f]/g, '\\f')
        .replace(/[\n]/g, '\\n')
        .replace(/[\r]/g, '\\r')
        .replace(/[\t]/g, '\\t')
        .replace(/[\"]/g, '\\"')
        .replace(/\\'/g, "\\'");
  if (quoteEscape) {
    var re = new RegExp(quoteEscape, "g");
    str = str.replace(re, "\\" + quoteEscape);
  }
  return str;
};

var warningsEmitted = {};
var deprecationWarnings = [];

var warningText = _.template(
  "[DEPRECATED] The <%= deprecated %> <%= kind %> has been deprecated and will " +
  "be removed."
);

var replacementText = _.template(
  "  Please use the <%= replacement %> <%= kind %> instead."
);

exports.formatDeprecationWarning = function (kind, deprecated, replacement) {
  var warning = warningText({kind: kind, deprecated: deprecated});
  if (typeof replacement !== 'undefined') {
    warning += replacementText({kind: kind, replacement: replacement});
  }
  return warning;
};

exports.logDeprecationWarning = function (kind, deprecated, replacement) {
  if (!_.has(warningsEmitted, kind)) {
    warningsEmitted[kind] = {};
  }
  if (!_.has(warningsEmitted[kind], deprecated)) {
    var warning = exports.formatDeprecationWarning(kind, deprecated, replacement);
    deprecationWarnings.push(warning);
    logger.warn(warning);
    warningsEmitted[kind][deprecated] = true;
  }
};

exports.logCustomDeprecationWarning = function (kind, deprecated, msg) {
  if (!_.has(warningsEmitted, kind)) {
    warningsEmitted[kind] = {};
  }
  if (!_.has(warningsEmitted[kind], deprecated)) {
    deprecationWarnings.push(msg);
    logger.warn(msg);
    warningsEmitted[kind][deprecated] = true;
  }
};

exports.logFinalDeprecationWarning = function () {
  var numWarnings = deprecationWarnings.length;
  if (numWarnings > 0) {
    logger.warn("[DEPRECATED] You used " + numWarnings + " deprecated" +
                " capabilities during this session.  Please check the logs" +
                " as they will be removed in a future version of Appium.");
  }
};

exports.getDeprecationWarnings = function () {
  return deprecationWarnings;
};

exports.clearWarnings = function () {
  warningsEmitted = {};
  deprecationWarnings = [];
};

exports.rotateImage = function (imgPath, deg, cb) {
  logger.debug("Rotating image " + imgPath + " " + deg + " degrees");
  var scriptPath = require('appium-uiauto').rotate;
  var cmd = "osascript " + scriptPath + " " + JSON.stringify(imgPath) +
            " " + deg;
  exec(cmd, { maxBuffer: 524288 }, function (err, stdout) {
    if (err) return cb(err);
    console.log(stdout);
    cb(null);
  });
};

exports.isWindows = function () {
  return osType === 'Windows_NT';
};

exports.isMac = function () {
  return osType === 'Darwin';
};

exports.isLinux = function () {
  return !exports.isWindows() && !exports.isMac();
};

exports.macVersionArray = function (cb) {
  var versions = [];
  if (exports.isMac()) {
    exec("sw_vers -productVersion", function (err, stdout) {
      if (err) return cb(err);
      stdout = stdout.trim();
      if (/\d+\.\d+\.\d+/.test(stdout)) {
        _.each(stdout.split("."), function (ver) {
          versions.push(parseInt(ver, 10));
        });
        cb(null, versions);
      } else {
        cb(new Error("Could not parse version information from: " + stdout));
      }
    });
  } else {
    cb(null, versions);
  }
};

exports.getGitRev = function (cb) {
  var cwd = path.resolve(__dirname, "..");
  exec("git rev-parse HEAD", {cwd: cwd, maxBuffer: 524288}, function (err, stdout) {
    if (err) return cb(err);
    cb(null, stdout.trim());
  });
};

exports.getAppiumConfig = function () {
  var configPath = path.resolve(__dirname, "..", ".appiumconfig.json");
  var config
    , msg;
  try {
    config = require(configPath);
  } catch (e) {
    if (e.code === "MODULE_NOT_FOUND") {
      msg = "Could not find config file: " + configPath +
                 "; looks like config hasn't been run." +
                 " Please run reset.sh or appium configure.";
      logger.error(msg);
      throw new Error(msg);
    } else {
      msg = "Invalid config file at " + configPath +
                " please re-run reset.sh or appium config";
      logger.error(msg);
      throw new Error(msg);
    }
  }

  return config;
};

exports.iosConfigured = function () {
  return typeof exports.getAppiumConfig().ios !== "undefined";
};

exports.truncateDecimals = function (number, digits) {
  var multiplier = Math.pow(10, digits),
      adjustedNum = number * multiplier,
      truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);

  return truncatedNum / multiplier;
};
<commit_msg>Fix EPERM error in Windows when using a remote apk already signed<commit_after>"use strict";

var logger = require('./server/logger.js').get('appium')
  , fs = require('fs')
  , request = require('request')
  , _ = require('underscore')
  , path = require('path')
  , exec = require('child_process').exec
  , osType = require('os').type()
  , tempdir = require('./tempdir')
  , AdmZip = require('adm-zip');

exports.downloadFile = function (fileUrl, suffix, cb) {
  // We will be downloading the files to a directory, so make sure it's there
  // This step is not required if you have manually created the directory
  tempdir.open({prefix: 'appium-app', suffix: suffix}, function (err, info) {
    fs.close(info.fd);
    var file = fs.createWriteStream(info.path);
    request(fileUrl).pipe(file).on('close', function () {
      logger.debug(fileUrl + ' downloaded to ' + info.path);
      cb(info.path);
    });
  });
};

exports.copyLocalZip = function (localZipPath, cb) {
  logger.debug("Copying local zip to tmp dir");
  fs.stat(localZipPath, function (err) {
    if (err) return cb(err);
    tempdir.open({prefix: 'appium-app', suffix: '.zip'}, function (err, info) {
      var infile = fs.createReadStream(localZipPath);
      var outfile = fs.createWriteStream(info.path);
      infile.pipe(outfile).on('close', function () {
        logger.debug(localZipPath + ' copied to ' + info.path);
        cb(null, info.path);
      });
    });
  });
};

exports.unzipFile = function (zipPath, cb) {
  logger.debug("Unzipping " + zipPath);
  exports.testZipArchive(zipPath, function (err, valid) {
    if (valid) {
      if (exports.isWindows()) {
        var zip = new AdmZip(zipPath);
        zip.extractAllTo(path.dirname(zipPath), true);
        logger.debug("Unzip successful");
        cb(null, null);
      } else {
        var execEnv = _.clone(process.env);
        delete execEnv.UNZIP;
        var execOpts = {cwd: path.dirname(zipPath), maxBuffer: 524288,
                        env: execEnv};
        exec('unzip -o ' + zipPath, execOpts, function (err, stderr, stdout) {
          if (!err) {
            logger.debug("Unzip successful");
            cb(null, stderr);
          } else {
            logger.error("Unzip threw error " + err);
            logger.error("Stderr: " + stderr);
            logger.error("Stdout: " + stdout);
            cb("Archive could not be unzipped, check appium logs.", null);
          }
        });
      }
    } else {
      cb(err, null);
    }
  });
};

exports.testZipArchive = function (zipPath, cb) {
  logger.debug("Testing zip archive: " + zipPath);
  if (exports.isWindows()) {
    if (fs.existsSync(zipPath)) {
      logger.debug("Zip archive tested clean");
      cb(null, true);
    } else {
      cb("Zip archive was not found.", false);
    }
  } else {
    var execEnv = _.clone(process.env);
    delete execEnv.UNZIP;
    var execOpts = {cwd: path.dirname(zipPath), maxBuffer: 524288,
                    env: execEnv};
    exec("unzip -tq " + zipPath, execOpts, function (err, stderr, stdout) {
      if (!err) {
        if (/No errors detected/.exec(stderr)) {
          logger.debug("Zip archive tested clean");
          cb(null, true);
        } else {
          logger.error("Zip file " + zipPath + " was not valid");
          logger.error("Stderr: " + stderr);
          logger.error("Stdout: " + stdout);
          cb("Zip archive did not test successfully, check appium server logs " +
             "for output", false);
        }
      } else {
        logger.error("Test zip archive threw error " + err);
        logger.error("Stderr: " + stderr);
        logger.error("Stdout: " + stdout);
        cb("Error testing zip archive, are you sure this is a zip file? " + err, null);
      }
    });
  }
};

exports.unzipApp = function (zipPath, appExt, cb) {
  exec("find " + path.dirname(zipPath) + " -type d -name '*" + appExt + "' | xargs rm -rf " + path.dirname(zipPath) +
    "/Payload*", function (error /*, stdout, stderr*/) {
    if (!error) {
      exports.unzipFile(zipPath, function (err, output) {
        if (!err) {
          var relaxedRegStr = "(?:creating|inflating|extracting): (.+" + appExt + ")/?";
          // in the strict regex, we check for an entry which ends with the
          // extension
          var strictReg = new RegExp(relaxedRegStr + "$", 'm');
          // otherwise, we allow an entry which contains the extension, but we
          // need to be careful, because it might be a false positive
          var relaxedReg = new RegExp(relaxedRegStr, 'm');
          var strictMatch = strictReg.exec(output);
          var relaxedMatch = relaxedReg.exec(output);
          var getAppPath = function (match) {
            return path.resolve(path.dirname(zipPath), match[1]);
          };
          if (strictMatch) {
            cb(null, getAppPath(strictMatch));
          } else if (relaxedMatch) {
            logger.debug("Got a relaxed match for app in zip, be careful for app match errors");
            cb(null, getAppPath(relaxedMatch));
          } else {
            cb("App zip unzipped OK, but we couldn't find a .app bundle in it. " +
              "Make sure your archive contains the .app package and nothing else",
              null);
          }
        } else {
          cb(err, null);
        }
      });
    } else {
      cb(error, null);
    }
  });
};

exports.getUser = function (cb) {
  logger.debug("Determining current user");
  exec("whoami", { maxBuffer: 524288 }, function (err, stdout) {
    if (err) {
      logger.error(err);
      cb(err);
    } else {
      logger.debug("User is " + stdout.trim());
      cb(null, stdout.trim());
    }
  });
};

exports.multiResolve = function (roots) {
  var args = Array.prototype.slice.call(arguments, 1);
  var paths = [];
  _.each(roots, function (root) {
    var resolveArgs = [root].concat(args);
    paths.push(path.resolve.apply(null, resolveArgs));
  });
  return paths;
};

exports.delay = function (secs) {
  var date = new Date();
  var curDate = null;
  do { curDate = new Date(); }
  while (curDate - date < (secs * 1000.0));
};

exports.escapeSpecialChars = function (str, quoteEscape) {
  if (typeof str !== "string") {
    return str;
  }
  if (typeof quoteEscape === "undefined") {
    quoteEscape = false;
  }
  str = str
        .replace(/[\\]/g, '\\\\')
        .replace(/[\/]/g, '\\/')
        .replace(/[\b]/g, '\\b')
        .replace(/[\f]/g, '\\f')
        .replace(/[\n]/g, '\\n')
        .replace(/[\r]/g, '\\r')
        .replace(/[\t]/g, '\\t')
        .replace(/[\"]/g, '\\"')
        .replace(/\\'/g, "\\'");
  if (quoteEscape) {
    var re = new RegExp(quoteEscape, "g");
    str = str.replace(re, "\\" + quoteEscape);
  }
  return str;
};

var warningsEmitted = {};
var deprecationWarnings = [];

var warningText = _.template(
  "[DEPRECATED] The <%= deprecated %> <%= kind %> has been deprecated and will " +
  "be removed."
);

var replacementText = _.template(
  "  Please use the <%= replacement %> <%= kind %> instead."
);

exports.formatDeprecationWarning = function (kind, deprecated, replacement) {
  var warning = warningText({kind: kind, deprecated: deprecated});
  if (typeof replacement !== 'undefined') {
    warning += replacementText({kind: kind, replacement: replacement});
  }
  return warning;
};

exports.logDeprecationWarning = function (kind, deprecated, replacement) {
  if (!_.has(warningsEmitted, kind)) {
    warningsEmitted[kind] = {};
  }
  if (!_.has(warningsEmitted[kind], deprecated)) {
    var warning = exports.formatDeprecationWarning(kind, deprecated, replacement);
    deprecationWarnings.push(warning);
    logger.warn(warning);
    warningsEmitted[kind][deprecated] = true;
  }
};

exports.logCustomDeprecationWarning = function (kind, deprecated, msg) {
  if (!_.has(warningsEmitted, kind)) {
    warningsEmitted[kind] = {};
  }
  if (!_.has(warningsEmitted[kind], deprecated)) {
    deprecationWarnings.push(msg);
    logger.warn(msg);
    warningsEmitted[kind][deprecated] = true;
  }
};

exports.logFinalDeprecationWarning = function () {
  var numWarnings = deprecationWarnings.length;
  if (numWarnings > 0) {
    logger.warn("[DEPRECATED] You used " + numWarnings + " deprecated" +
                " capabilities during this session.  Please check the logs" +
                " as they will be removed in a future version of Appium.");
  }
};

exports.getDeprecationWarnings = function () {
  return deprecationWarnings;
};

exports.clearWarnings = function () {
  warningsEmitted = {};
  deprecationWarnings = [];
};

exports.rotateImage = function (imgPath, deg, cb) {
  logger.debug("Rotating image " + imgPath + " " + deg + " degrees");
  var scriptPath = require('appium-uiauto').rotate;
  var cmd = "osascript " + scriptPath + " " + JSON.stringify(imgPath) +
            " " + deg;
  exec(cmd, { maxBuffer: 524288 }, function (err, stdout) {
    if (err) return cb(err);
    console.log(stdout);
    cb(null);
  });
};

exports.isWindows = function () {
  return osType === 'Windows_NT';
};

exports.isMac = function () {
  return osType === 'Darwin';
};

exports.isLinux = function () {
  return !exports.isWindows() && !exports.isMac();
};

exports.macVersionArray = function (cb) {
  var versions = [];
  if (exports.isMac()) {
    exec("sw_vers -productVersion", function (err, stdout) {
      if (err) return cb(err);
      stdout = stdout.trim();
      if (/\d+\.\d+\.\d+/.test(stdout)) {
        _.each(stdout.split("."), function (ver) {
          versions.push(parseInt(ver, 10));
        });
        cb(null, versions);
      } else {
        cb(new Error("Could not parse version information from: " + stdout));
      }
    });
  } else {
    cb(null, versions);
  }
};

exports.getGitRev = function (cb) {
  var cwd = path.resolve(__dirname, "..");
  exec("git rev-parse HEAD", {cwd: cwd, maxBuffer: 524288}, function (err, stdout) {
    if (err) return cb(err);
    cb(null, stdout.trim());
  });
};

exports.getAppiumConfig = function () {
  var configPath = path.resolve(__dirname, "..", ".appiumconfig.json");
  var config
    , msg;
  try {
    config = require(configPath);
  } catch (e) {
    if (e.code === "MODULE_NOT_FOUND") {
      msg = "Could not find config file: " + configPath +
                 "; looks like config hasn't been run." +
                 " Please run reset.sh or appium configure.";
      logger.error(msg);
      throw new Error(msg);
    } else {
      msg = "Invalid config file at " + configPath +
                " please re-run reset.sh or appium config";
      logger.error(msg);
      throw new Error(msg);
    }
  }

  return config;
};

exports.iosConfigured = function () {
  return typeof exports.getAppiumConfig().ios !== "undefined";
};

exports.truncateDecimals = function (number, digits) {
  var multiplier = Math.pow(10, digits),
      adjustedNum = number * multiplier,
      truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);

  return truncatedNum / multiplier;
};
<|endoftext|>


<commit_before>/**
 * @file ResizableTh component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

// Components
import {Resizable} from 'react-resizable';

// Vendors
import classNames from 'classnames';

class ResizableTh extends Component {

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    handleResize = (e, {size}) => {
        const {onResize} = this.props;
        onResize && onResize(size?.width, e);
    };

    handleResizeStart = (e, {size}) => {
        const {onResizeStart} = this.props;
        onResizeStart && onResizeStart(size?.width, e);
    };

    handleResizeStop = (e, {size}) => {
        const {onResizeStop} = this.props;
        onResizeStop && onResizeStop(size?.width, e);
    };

    render() {

        const {children, resizable, activated, deactivated, width} = this.props;

        return resizable && width && !isNaN(width) ?
            <Resizable className={classNames('resizable-th', {
                activated,
                deactivated
            })}
                       width={width}
                       height={0}
                       onResize={this.handleResize}
                       onResizeStart={this.handleResizeStart}
                       onResizeStop={this.handleResizeStop}>
                {children}
            </Resizable>
            :
            children;

    }
}

ResizableTh.propTypes = {

    children: PropTypes.any,

    resizable: PropTypes.bool,
    activated: PropTypes.bool,
    deactivated: PropTypes.bool,
    width: PropTypes.number,
    minWidth: PropTypes.number,
    maxWidth: PropTypes.number,

    onResize: PropTypes.func,
    onResizeStart: PropTypes.func,
    onResizeStop: PropTypes.func

};

ResizableTh.defaultProps = {
    minWidth: 40,
    maxWidth: Infinity
};

export default ResizableTh;
<commit_msg>update ResizableTh<commit_after>/**
 * @file ResizableTh component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

// Components
import {Resizable} from 'react-resizable';

// Vendors
import classNames from 'classnames';

class ResizableTh extends Component {

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    handleResize = (e, {size}) => {
        const {onResize} = this.props;
        onResize && onResize(size?.width, e);
    };

    handleResizeStart = (e, {size}) => {
        const {onResizeStart} = this.props;
        onResizeStart && onResizeStart(size?.width, e);
    };

    handleResizeStop = (e, {size}) => {
        const {onResizeStop} = this.props;
        onResizeStop && onResizeStop(size?.width, e);
    };

    render() {

        const {children, resizable, activated, deactivated, width} = this.props;

        return resizable ?
            <Resizable className={classNames('resizable-th', {
                activated,
                deactivated
            })}
                       width={width}
                       height={0}
                       onResize={this.handleResize}
                       onResizeStart={this.handleResizeStart}
                       onResizeStop={this.handleResizeStop}>
                {children}
            </Resizable>
            :
            children;

    }
}

ResizableTh.propTypes = {

    children: PropTypes.any,

    resizable: PropTypes.bool,
    activated: PropTypes.bool,
    deactivated: PropTypes.bool,
    width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    minWidth: PropTypes.number,
    maxWidth: PropTypes.number,

    onResize: PropTypes.func,
    onResizeStart: PropTypes.func,
    onResizeStop: PropTypes.func

};

ResizableTh.defaultProps = {
    minWidth: 40,
    maxWidth: Infinity
};

export default ResizableTh;
<|endoftext|>


<commit_before>/** @babel */

import path from 'path'
import Builder from '../builder'
import BuilderRegistry from '../builder-registry'

const MISSING_PACKAGE_PATTERN = /there is no package called ‘([^’]+)’/g
const OUTPUT_PATH_PATTERN = /\[\d+\]\s+"(.*)"/

export default class KnitrBuilder extends Builder {
  executable = 'Rscript'

  constructor () {
    super()
    this.builderRegistry = new BuilderRegistry()
  }

  static canProcess (filePath) {
    return path.extname(filePath) === '.Rnw'
  }

  async run (filePath, jobname, shouldRebuild) {
    const args = this.constructArgs(filePath)
    const { statusCode, stdout } = await this.execRscript(filePath, args, 'Error')
    if (statusCode !== 0) return statusCode

    const texFilePath = this.resolveOutputPath(filePath, stdout)
    const builder = this.getResultBuilder(texFilePath)
    const code = await builder.run(texFilePath, jobname, shouldRebuild)

    if (code === 0) {
      const args = this.constructPatchSynctexArgs(texFilePath, jobname)
      await this.execRscript(filePath, args, 'Warning')
    }

    return code
  }

  async execRscript (filePath, args, type) {
    const command = `${this.executable} ${args.join(' ')}`
    const options = this.constructChildProcessOptions(filePath)

    let { statusCode, stdout, stderr } = await latex.process.executeChildProcess(command, options)

    if (statusCode !== 0) {
      // Parse error message to detect missing libraries.
      let match
      while ((match = MISSING_PACKAGE_PATTERN.exec(stderr)) !== null) {
        const text = `The R package "${match[1]}" could not be loaded.`
        latex.log.showMessage({ type, text })
        statusCode = -1
      }
    }

    return { statusCode, stdout }
  }

  constructArgs (filePath) {
    const args = [
      '-e "library(knitr)"',
      '-e "opts_knit$set(concordance = TRUE)"',
      `-e "knit('${filePath.replace(/\\/g, '\\\\')}')"`
    ]

    return args
  }

  constructPatchSynctexArgs (filePath, jobname) {
    const args = [
      '-e "library(patchSynctex)"',
      `-e "patchSynctex('${filePath.replace(/\\/g, '\\\\')}')"`
    ]

    return args
  }

  resolveOutputPath (sourcePath, stdout) {
    const candidatePath = OUTPUT_PATH_PATTERN.exec(stdout)[1]
    if (path.isAbsolute(candidatePath)) {
      return candidatePath
    }

    const sourceDir = path.dirname(sourcePath)
    return path.join(sourceDir, candidatePath)
  }

  // TODO: Move into base class, or perhaps introduce some new build pipeline concept?
  getResultBuilder (filePath) {
    const BuilderImpl = this.builderRegistry.getBuilder(filePath)
    return new BuilderImpl()
  }
}
<commit_msg>Add quotes for win32<commit_after>/** @babel */

import path from 'path'
import Builder from '../builder'
import BuilderRegistry from '../builder-registry'

const MISSING_PACKAGE_PATTERN = /there is no package called [‘']([^’']+)[’']/g
const OUTPUT_PATH_PATTERN = /\[\d+\]\s+"(.*)"/

export default class KnitrBuilder extends Builder {
  executable = 'Rscript'

  constructor () {
    super()
    this.builderRegistry = new BuilderRegistry()
  }

  static canProcess (filePath) {
    return path.extname(filePath) === '.Rnw'
  }

  async run (filePath, jobname, shouldRebuild) {
    const args = this.constructArgs(filePath)
    const { statusCode, stdout } = await this.execRscript(filePath, args, 'Error')
    if (statusCode !== 0) return statusCode

    const texFilePath = this.resolveOutputPath(filePath, stdout)
    const builder = this.getResultBuilder(texFilePath)
    const code = await builder.run(texFilePath, jobname, shouldRebuild)

    if (code === 0) {
      const outputDirectory = atom.config.get('latex.outputDirectory')
      if (outputDirectory) {
        const args = this.constructPatchSynctexArgs(texFilePath, jobname)
        await this.execRscript(filePath, args, 'Warning')
      } else {
        latex.log.info('Using an output directory is not compatible with patchSynctex.')
      }
    }

    return code
  }

  async execRscript (filePath, args, type) {
    const command = `${this.executable} ${args.join(' ')}`
    const options = this.constructChildProcessOptions(filePath)

    let { statusCode, stdout, stderr } = await latex.process.executeChildProcess(command, options)

    if (statusCode !== 0) {
      // Parse error message to detect missing libraries.
      let match
      while ((match = MISSING_PACKAGE_PATTERN.exec(stderr)) !== null) {
        const text = `The R package "${match[1]}" could not be loaded.`
        latex.log.showMessage({ type, text })
        statusCode = -1
      }
    }

    return { statusCode, stdout }
  }

  constructArgs (filePath) {
    const args = [
      '-e "library(knitr)"',
      '-e "opts_knit$set(concordance = TRUE)"',
      `-e "knit('${filePath.replace(/\\/g, '\\\\')}')"`
    ]

    return args
  }

  constructPatchSynctexArgs (filePath, jobname) {
    const args = [
      '-e "library(patchSynctex)"',
      `-e "patchSynctex('${filePath.replace(/\\/g, '\\\\')}')"`
    ]

    return args
  }

  resolveOutputPath (sourcePath, stdout) {
    const candidatePath = OUTPUT_PATH_PATTERN.exec(stdout)[1]
    if (path.isAbsolute(candidatePath)) {
      return candidatePath
    }

    const sourceDir = path.dirname(sourcePath)
    return path.join(sourceDir, candidatePath)
  }

  // TODO: Move into base class, or perhaps introduce some new build pipeline concept?
  getResultBuilder (filePath) {
    const BuilderImpl = this.builderRegistry.getBuilder(filePath)
    return new BuilderImpl()
  }
}
<|endoftext|>


<commit_before>/* global describe */
/* global it */

const should = require('should')
const sw = require('../lib/stopword.js')

describe('general stopwordiness:', function () {
  it('should remove stopwords, should default to english, should preserve case', function () {
    const oldString = 'a really Interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString)
    should.exist(sw.removeStopwords(oldString))
    newString.should.eql([ 'really', 'Interesting', 'string', 'words' ])
  })

  it('should remove custom stopwords', function () {
    const oldString = 'a really interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString, ['interesting'])
    newString.should.eql([ 'a', 'really', 'string', 'with', 'some', 'words' ])
  })

  it('should not remove any stopwords', function () {
    const oldString = 'a really interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString, [])
    newString.should.eql([ 'a', 'really', 'interesting', 'string', 'with', 'some', 'words' ])
  })

  it('should remove stopwords that have a non standard separator', function () {
    const oldString = 'a.really,interesting string.with,some.words'.split(/[\\., ]+/)
    const newString = sw.removeStopwords(oldString)
    newString.should.eql([ 'really', 'interesting', 'string', 'words' ])
  })

  it('should specify a custom input separator', function () {
    const oldString = 'a-really-interesting-string-with-some words'.split('-')
    const newString = sw.removeStopwords(oldString)
    newString.should.eql([ 'really', 'interesting', 'string', 'some words' ])
  })

  it('should remove norwegian stopwords', function () {
    const oldString = 'dette er en tekst som har nørske tegn øæåø øæåø æææ'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.no)
    newString.should.eql([ 'tekst', 'nørske', 'tegn', 'øæåø', 'øæåø', 'æææ' ])
  })

  it('should remove swedish stopwords and preserve case', function () {
    const oldString = 'Trädgårdsägare är beredda att pröva vad som helst för att bli av med de hatade mördarsniglarna åäö'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.sv)
    newString.should.eql([ 'Trädgårdsägare', 'beredda', 'pröva', 'helst', 'hatade', 'mördarsniglarna', 'åäö' ])
  })

  it('should remove danish stopwords', function () {
    const oldString = 'gæsterne i musikhuset i aarhus bør fremover sende en venlig tanke til den afdøde købmand herman salling når de skal til koncert eller se teater i det aarhusianske kulturhus æøå'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.da)
    newString.should.eql([ 'gæsterne', 'musikhuset', 'aarhus', 'bør', 'fremover', 'sende', 'venlig', 'tanke', 'afdøde', 'købmand', 'herman', 'salling', 'koncert', 'teater', 'aarhusianske', 'kulturhus', 'æøå' ])
  })

  it('should remove hindu stopwords', function () {
    const oldString = 'सूत्रों ने बताया कि ऊर्जा मंत्रालय के अंतर्गत काम करने वाले ब्यूरो ऑफ एनर्जी एफ़िशंसी एक योजना पर काम कर रहा है। मकसद ऐसे घरों को बढ़ावा देना है जहां ऊर्जा का प्रभावशाली इस्तेमाल हो सके। यानी यहां रोशनी या कूलिंग के लिए संसाधनों की कम डिमांड विकसित की जा सके। इसी कोशिश के तहत वर्तमान रेजिडेंशल इमारतों में भी नए उपकरणों का इस्तेमाल करके ऊर्जा के कम और प्रभावशाली इस्तेमाल को बढ़ावा देना है। बता दें कि मोदी सरकार की ओर से उजाला स्कीम ऊर्जा संसाधनों के बेहतर इस्तेमाल की दिशा में ही उठाया गया कदम है। इसके तहत सरकार ने पुराने फिलामेंट वाले और सीएफएल बल्बों को आधुनिक एलईडी लाइट्स से बदला है। बढ़ते शहरी दायरे और पर्यावरण पर पड़ते प्रभाव के मद्देनजर के जरिए सरकारी कोशिशों को नई ताकत मिलने की उम्मीद है। इससे प्रॉपर्टी सेक्टर में नौकरियां पैदा होने की भी पूरी गुंजाइश है।'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.hi)
    newString.should.eql([ 'सूत्रों', 'ने', 'बताया', 'कि', 'ऊर्जा', 'मंत्रालय', 'के', 'अंतर्गत', 'काम', 'करने', 'वाले', 'ब्यूरो', 'ऑफ', 'एनर्जी', 'एफ़िशंसी', 'एक', 'योजना', 'पर', 'काम', 'कर', 'रहा', 'है।', 'मकसद', 'ऐसे', 'घरों', 'को', 'बढ़ावा', 'देना', 'है', 'जहां', 'ऊर्जा', 'का', 'प्रभावशाली', 'इस्तेमाल', 'हो', 'सके।', 'यानी', 'यहां', 'रोशनी', 'या', 'कूलिंग', 'के', 'लिए', 'संसाधनों', 'की', 'कम', 'डिमांड', 'विकसित', 'की', 'जा', 'सके।', 'इसी', 'कोशिश', 'के', 'तहत', 'वर्तमान', 'रेजिडेंशल', 'इमारतों', 'में', 'भी', 'नए', 'उपकरणों', 'का', 'इस्तेमाल', 'करके', 'ऊर्जा', 'के', 'कम', 'और', 'प्रभावशाली', 'इस्तेमाल', 'को', 'बढ़ावा', 'देना', 'है।', 'बता', 'दें', 'कि', 'मोदी', 'सरकार', 'की', 'ओर', 'से', 'उजाला', 'स्कीम', 'ऊर्जा', 'संसाधनों', 'के', 'बेहतर', 'इस्तेमाल', 'की', 'दिशा', 'में', 'ही', 'उठाया', 'गया', 'कदम', 'है।', 'इसके', 'तहत', 'सरकार', 'ने', 'पुराने', 'फिलामेंट', 'वाले', 'और', 'सीएफएल', 'बल्बों', 'को', 'आधुनिक', 'एलईडी', 'लाइट्स', 'से', 'बदला', 'है।', 'बढ़ते', 'शहरी', 'दायरे', 'और', 'पर्यावरण', 'पर', 'पड़ते', 'प्रभाव', 'के', 'मद्देनजर', 'के', 'जरिए', 'सरकारी', 'कोशिशों', 'को', 'नई', 'ताकत', 'मिलने', 'की', 'उम्मीद', 'है।', 'इससे', 'प्रॉपर्टी', 'सेक्टर', 'में', 'नौकरियां', 'पैदा', 'होने', 'की', 'भी', 'पूरी', 'गुंजाइश', 'है।' ])
  })

})

describe('error handling', function () {
  it('should return throw an error if specified language is not supported', function () {
    (function () {
      sw.getStopwords('xx')
    }).should.throw()
  })
})
<commit_msg>Update test.js<commit_after>/* global describe */
/* global it */

const should = require('should')
const sw = require('../lib/stopword.js')

describe('general stopwordiness:', function () {
  it('should remove stopwords, should default to english, should preserve case', function () {
    const oldString = 'a really Interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString)
    should.exist(sw.removeStopwords(oldString))
    newString.should.eql([ 'really', 'Interesting', 'string', 'words' ])
  })

  it('should remove custom stopwords', function () {
    const oldString = 'a really interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString, ['interesting'])
    newString.should.eql([ 'a', 'really', 'string', 'with', 'some', 'words' ])
  })

  it('should not remove any stopwords', function () {
    const oldString = 'a really interesting string with some words'.split(' ')
    const newString = sw.removeStopwords(oldString, [])
    newString.should.eql([ 'a', 'really', 'interesting', 'string', 'with', 'some', 'words' ])
  })

  it('should remove stopwords that have a non standard separator', function () {
    const oldString = 'a.really,interesting string.with,some.words'.split(/[\\., ]+/)
    const newString = sw.removeStopwords(oldString)
    newString.should.eql([ 'really', 'interesting', 'string', 'words' ])
  })

  it('should specify a custom input separator', function () {
    const oldString = 'a-really-interesting-string-with-some words'.split('-')
    const newString = sw.removeStopwords(oldString)
    newString.should.eql([ 'really', 'interesting', 'string', 'some words' ])
  })

  it('should remove norwegian stopwords', function () {
    const oldString = 'dette er en tekst som har nørske tegn øæåø øæåø æææ'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.no)
    newString.should.eql([ 'tekst', 'nørske', 'tegn', 'øæåø', 'øæåø', 'æææ' ])
  })

  it('should remove swedish stopwords and preserve case', function () {
    const oldString = 'Trädgårdsägare är beredda att pröva vad som helst för att bli av med de hatade mördarsniglarna åäö'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.sv)
    newString.should.eql([ 'Trädgårdsägare', 'beredda', 'pröva', 'helst', 'hatade', 'mördarsniglarna', 'åäö' ])
  })

  it('should remove danish stopwords', function () {
    const oldString = 'gæsterne i musikhuset i aarhus bør fremover sende en venlig tanke til den afdøde købmand herman salling når de skal til koncert eller se teater i det aarhusianske kulturhus æøå'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.da)
    newString.should.eql([ 'gæsterne', 'musikhuset', 'aarhus', 'bør', 'fremover', 'sende', 'venlig', 'tanke', 'afdøde', 'købmand', 'herman', 'salling', 'koncert', 'teater', 'aarhusianske', 'kulturhus', 'æøå' ])
  })

  it('should remove hindu stopwords', function () {
    const oldString = 'केंद्र सरकार पर्यावरण के माकूल घरों ग्रीन होम्स को बढ़ावा देने की दिशा में गंभीरता से सोच रही है। ग्रीन हाउसिंग सोसायटी डिवेलप करने के लिए सरकार सस्ते लोन और कम रजिस्ट्रेशन फीस जैसी सुविधाएं देगी। क्लाइमेट चेंज से लड़ने की दिशा में केंद्र इस तरह की रिहायशी कॉलोनियां विकसित करने की दिशा में सोच रही है। बता दें कि ग्रीन होम्स वे घर हैं, जो पर्यावरण को ध्यान में रखकर विकसित किए जाते हैं। इनमें पर्यावरण के नजरिए से एनर्जी जल संसाधन और बिल्डिंग मटीरियल्स का प्रभावशाली इस्तेमाल किया जाता है। सरकारी सूत्रों ने बताया कि ग्रीन होम्स को बढ़ावा देने के लिए ही इससे जुड़े नियम-कायदे यानी एनर्जी कंजर्वेशन बिल्डिंग कोड फॉर रेजिडेंशल सेक्टर तैयार किया जा चुका है। ये नियम में सरकारी और कमर्शल इमारतों से संबंधित कोड की तर्ज पर ही हैं। ऊर्जा मंत्री पीयूष गोयल सोमवार को पेश कर सकते हैं। इसे भारतीय रीयल्टी सेक्टर में इको फ्रेंडली निर्माण की दिशा में बड़ा कदम माना जा सकता है। सूत्रों ने बताया कि ऊर्जा मंत्रालय के अंतर्गत काम करने वाले ब्यूरो ऑफ एनर्जी एफ़िशंसी एक योजना पर काम कर रहा है। मकसद ऐसे घरों को बढ़ावा देना है जहां ऊर्जा का प्रभावशाली इस्तेमाल हो सके। यानी यहां रोशनी या कूलिंग के लिए संसाधनों की कम डिमांड विकसित की जा सके। इसी कोशिश के तहत वर्तमान रेजिडेंशल इमारतों में भी नए उपकरणों का इस्तेमाल करके ऊर्जा के कम और प्रभावशाली इस्तेमाल को बढ़ावा देना है।'.split(' ')
    const newString = sw.removeStopwords(oldString, sw.hi)
    newString.should.eql([ 'है।' ])
  })

})

describe('error handling', function () {
  it('should return throw an error if specified language is not supported', function () {
    (function () {
      sw.getStopwords('xx')
    }).should.throw()
  })
})
<|endoftext|>


<commit_before>describe("lib/utils", () => {
  jest.useFakeTimers();

  let utils = require("./../../src/lib/utils");
  let ldClient = require("ldclient-js").default;

  const mockLdClient = (() => {
    ldClient.identify = jest.fn();

    ldClient.initialize = jest.fn().mockImplementation(() => ({
      on: (event, callback) => {
        setTimeout(() => {
          callback();
        }, 1000);
      },
      variation: () => {}
    }));
  });
  mockLdClient();

  it("exports functions", () => {
    expect(utils["ldClientWrapper"]).toBeDefined();
    expect(utils["ldOverrideFlag"]).toBeDefined();
    expect(utils["getAllFeatureFlags"]).toBeDefined();
  });

  describe("ldClientWrapper", () => {
    const key = "my key";
    const user = { key: "my user" };
    const options = { baseUrl: "http://test" };

    describe("proxies to ldClient", () => {
      beforeEach(() => {
        // `ldClientWrapper` is a singleton, we need to reset the module cache with each test
        // to be able to properly assert each instance of `ldClientWrapper`
        jest.resetModules();
        utils = require("./../../src/lib/utils");
        ldClient = require("ldclient-js").default;
        mockLdClient();
      });

      it("initializes with key, user and default options parameter", () => {
        utils.ldClientWrapper(key, user);
        expect(ldClient.initialize).toBeCalledWith(key, user, {});
      });

      it("initializes with key, user and options", () => {
        utils.ldClientWrapper(key, user, options);
        expect(ldClient.initialize).toBeCalledWith(key, user, options);
      });
    });

    it("does not instantiate ldclient-js more than once", () => {
      utils.ldClientWrapper(key, user);
      utils.ldClientWrapper(key, user);
      utils.ldClientWrapper(key, user);
      expect(ldClient.initialize).toHaveBeenCalledTimes(1);
    });

    it("onReady calls wait for ready event to fire", () => {
      const callbackMock = jest.fn();
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      expect(callbackMock.mock.instances.length).toBe(0);
    });

    it("executes all onReady calls after ldClient is ready", () => {
      const callbackMock = jest.fn();
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      jest.runAllTimers();
      expect(callbackMock.mock.instances.length).toBe(3);
    });
  });

  describe("getAllFeatureFlags", () => {

  });

  describe("identify", () => {
    it("ldclient-js identify is called", () => {
      utils.identify(1234, {}).then( () => {
        expect(ldClient.identify).toHaveBeenCalled();
      });
    });
  });

  describe("track", () => {
    it("ldclient-js track is called", () => {
      utils.track(1234, {}).then( () => {
        expect(ldClient.track).toHaveBeenCalled();
      });
    });
  });

  describe("ldOverrideFlag", () => {

  });

  describe("feature", () => {
    const key = "my key";
    const user = { key: "my user" };
    const featureFlag = "home-test";

    it("feature is called", async () => {
      const spy = jest.spyOn(utils, "feature");
      await utils.feature(key, user, featureFlag);
      expect(spy).toHaveBeenCalled();
    });
  });

});
<commit_msg>using jest.fn & change name<commit_after>describe("lib/utils", () => {
  jest.useFakeTimers();

  let utils = require("./../../src/lib/utils");
  let ldClient = require("ldclient-js").default;

  const mockLdClient = (() => {
    ldClient.identify = jest.fn();

    ldClient.initialize = jest.fn().mockImplementation(() => ({
      on: (event, callback) => {
        setTimeout(() => {
          callback();
        }, 1000);
      },
      variation: jest.fn
    }));
  });
  mockLdClient();

  it("exports functions", () => {
    expect(utils["ldClientWrapper"]).toBeDefined();
    expect(utils["ldOverrideFlag"]).toBeDefined();
    expect(utils["getAllFeatureFlags"]).toBeDefined();
  });

  describe("ldClientWrapper", () => {
    const key = "my key";
    const user = { key: "my user" };
    const options = { baseUrl: "http://test" };

    describe("proxies to ldClient", () => {
      beforeEach(() => {
        // `ldClientWrapper` is a singleton, we need to reset the module cache with each test
        // to be able to properly assert each instance of `ldClientWrapper`
        jest.resetModules();
        utils = require("./../../src/lib/utils");
        ldClient = require("ldclient-js").default;
        mockLdClient();
      });

      it("initializes with key, user and default options parameter", () => {
        utils.ldClientWrapper(key, user);
        expect(ldClient.initialize).toBeCalledWith(key, user, {});
      });

      it("initializes with key, user and options", () => {
        utils.ldClientWrapper(key, user, options);
        expect(ldClient.initialize).toBeCalledWith(key, user, options);
      });
    });

    it("does not instantiate ldclient-js more than once", () => {
      utils.ldClientWrapper(key, user);
      utils.ldClientWrapper(key, user);
      utils.ldClientWrapper(key, user);
      expect(ldClient.initialize).toHaveBeenCalledTimes(1);
    });

    it("onReady calls wait for ready event to fire", () => {
      const callbackMock = jest.fn();
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      expect(callbackMock.mock.instances.length).toBe(0);
    });

    it("executes all onReady calls after ldClient is ready", () => {
      const callbackMock = jest.fn();
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      utils.ldClientWrapper(key, user).onReady(callbackMock);
      jest.runAllTimers();
      expect(callbackMock.mock.instances.length).toBe(3);
    });
  });

  describe("getAllFeatureFlags", () => {

  });

  describe("identify", () => {
    it("ldclient-js identify is called", () => {
      utils.identify(1234, {}).then( () => {
        expect(ldClient.identify).toHaveBeenCalled();
      });
    });
  });

  describe("track", () => {
    it("ldclient-js track is called", () => {
      utils.track(1234, {}).then( () => {
        expect(ldClient.track).toHaveBeenCalled();
      });
    });
  });

  describe("ldOverrideFlag", () => {

  });

  describe("feature", () => {
    const key = "my key";
    const user = { key: "my user" };
    const featureFlag = "home-test";

    it("is called", async () => {
      const spy = jest.spyOn(utils, "feature");
      await utils.feature(key, user, featureFlag);
      expect(spy).toHaveBeenCalled();
    });
  });

});
<|endoftext|>


<commit_before>// Copyright 2017-2018, University of Colorado Boulder

/**
 * A static object used to send aria-live updates to a screen reader. These are alerts that are independent of user
 * focus. This will simply reference 'aria-live' elements in the HTML document and update their content. ARIA
 * attributes specify the behavior of timing for the alerts. The following HTML elements must be in the document
 *
 *

 *

 *

 *

 *
 * It was discovered that cycling through these alerts prevented a VoiceOver bug where alerts would interrupt each
 * other. Starting from the first element, content is set on each element in order and cycles through.
 *
 * It is a commonly known (but not specified) limitation that aria-live elements must exist in the document before
 * the page has finished loading  to work with screen readers. Screen readers do not add observers to aria-live
 * elements that are added to the document after page load. So these elements must exist in the document before the
 * `onload` event triggers.
 *
 * Many aria-live and related attributes were tested, but none were well supported or particularly useful for PhET sims,
 * see https://github.com/phetsims/chipper/issues/472.
 *
 * NOTE: ariaHerald needs to be initialized before use as a singleton.
 * As of this writing (Nov 2017) this initialization occurs in Sim.js. Therefore if something uses ariaHerald
 * before Sim.js has initialized this file, the result will be a silent no-op.
 *
 * @author Jesse Greenberg
 * @author John Blanco
 */

define( require => {
  'use strict';

  // modules
  const AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  const Emitter = require( 'AXON/Emitter' );
  const EmitterIO = require( 'AXON/EmitterIO' );
  const sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  const timer = require( 'PHET_CORE/timer' );

  // by default, clear old text so sequential updates with identical text are announced, see updateLiveElement()
  const DEFAULT_WITH_CLEAR = true;

  // DOM elements which will receive the updated content. By having four elements and cycling through each one, we
  // can get around a VoiceOver bug where a new alert would interrupt the previous alert if it wasn't finished
  // speaking, see https://github.com/phetsims/scenery-phet/issues/362
  const politeElement1 = document.getElementById( 'polite-1' );
  const politeElement2 = document.getElementById( 'polite-2' );
  const politeElement3 = document.getElementById( 'polite-3' );
  const politeElement4 = document.getElementById( 'polite-4' );
  const ariaLiveElements = [ politeElement1, politeElement2, politeElement3, politeElement4 ];

  class AriaHerald {

    constructor() {
      // {boolean} - whether or not this instance has been initialized or not
      this.initialized = false;

      // index of current aria-live element to use, updated every time an event triggers
      this.elementIndex = 0;

      // @public {null|Emitter} - set in initialize method. Emit whenever we announce.
      this.announcingEmitter = null;
    }

    /**
     * Initialize AriaHerald to allow usage of its features. If not initialized, then it will no-op. This allows
     * AriaHerald to be disabled completely if a11y is not enabled.
     */
    initialize() {

      // verify that all DOM elements with aria-live are in the document
      assert && assert( document.getElementById( 'aria-live-elements' ), 'No alert container element found in document' );
      assert && assert( politeElement1, 'aria-live element 1 missing from document, all are required' );
      assert && assert( politeElement2, 'aria-live element 2 missing from document, all are required' );
      assert && assert( politeElement3, 'aria-live element 3 missing from document, all are required' );
      assert && assert( politeElement4, 'aria-live element 4 missing from document, all are required' );

      this.initialized = true;

      this.announcingEmitter = new Emitter( {
        phetioType: EmitterIO( [ { type: 'string' }, { type: 'boolean' } ] )
      } );

      // no need to be removed, exists for the lifetime of the simulation.
      this.announcingEmitter.addListener( ( textContent, withClear ) => {
        const element = ariaLiveElements[ this.elementIndex ];
        this.updateLiveElement( element, textContent, withClear );

        // update index for next time
        this.elementIndex = ( this.elementIndex + 1 ) % ariaLiveElements.length;
      } );
    }

    /**
     * Announce a polite alert.  This alert should be announced when the user has finished their current interaction or
     * after other utterances in the screen reader's queue are finished.
     * @public
     *
     * @param {string} textContent - the polite content to announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announcePolite( textContent, withClear ) {

      // or the default to support propper emitter typing
      this.announcingEmitter.emit( textContent, withClear || DEFAULT_WITH_CLEAR );
    }

    /**
     * Update an element with the 'aria-live' attribute by setting its text content.
     * If using withClear, old element text content will be explicitly removed before new text content is set.  This will
     * allow sequential alerts with identical text content to be announced multiple times in a row, which some screen
     * readers might have prevented.
     *
     * @param {HTMLElement} liveElement - the HTML element that will send the alert to the assistive technology
     * @param {string} textContent - the content to be announced
     * @param {boolean} [withClear] - optional, whether or not to remove the old text content before updating the element
     * @private
     */
    updateLiveElement( liveElement, textContent, withClear ) {

      // no-op if not initialized
      if ( !this.initialized ) {
        return;
      }

      withClear = ( withClear === undefined ) ? DEFAULT_WITH_CLEAR : withClear;
      assert && assert( typeof withClear === 'boolean', 'withClear must be of type boolean' );

      // clearing the old content allows repeated alerts
      if ( withClear ) { liveElement.textContent = ''; }

      AccessibilityUtil.setTextContent( liveElement, textContent );

      // after a small delay, remove this alert content from the DOM so that it cannot be found again - must occur
      // after a delay for screen reader to register the change in text content
      timer.setTimeout( () => { liveElement.textContent = ''; }, 200 );
    }
  }

  return sceneryPhet.register( 'ariaHerald', new AriaHerald() );
} );<commit_msg>Emitter to use argumentTypes with Validator validation, see https://github.com/phetsims/axon/issues/182<commit_after>// Copyright 2017-2018, University of Colorado Boulder

/**
 * A static object used to send aria-live updates to a screen reader. These are alerts that are independent of user
 * focus. This will simply reference 'aria-live' elements in the HTML document and update their content. ARIA
 * attributes specify the behavior of timing for the alerts. The following HTML elements must be in the document
 *
 *

 *

 *

 *

 *
 * It was discovered that cycling through these alerts prevented a VoiceOver bug where alerts would interrupt each
 * other. Starting from the first element, content is set on each element in order and cycles through.
 *
 * It is a commonly known (but not specified) limitation that aria-live elements must exist in the document before
 * the page has finished loading  to work with screen readers. Screen readers do not add observers to aria-live
 * elements that are added to the document after page load. So these elements must exist in the document before the
 * `onload` event triggers.
 *
 * Many aria-live and related attributes were tested, but none were well supported or particularly useful for PhET sims,
 * see https://github.com/phetsims/chipper/issues/472.
 *
 * NOTE: ariaHerald needs to be initialized before use as a singleton.
 * As of this writing (Nov 2017) this initialization occurs in Sim.js. Therefore if something uses ariaHerald
 * before Sim.js has initialized this file, the result will be a silent no-op.
 *
 * @author Jesse Greenberg
 * @author John Blanco
 */

define( require => {
  'use strict';

  // modules
  const AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  const Emitter = require( 'AXON/Emitter' );
  const sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  const timer = require( 'PHET_CORE/timer' );

  // by default, clear old text so sequential updates with identical text are announced, see updateLiveElement()
  const DEFAULT_WITH_CLEAR = true;

  // DOM elements which will receive the updated content. By having four elements and cycling through each one, we
  // can get around a VoiceOver bug where a new alert would interrupt the previous alert if it wasn't finished
  // speaking, see https://github.com/phetsims/scenery-phet/issues/362
  const politeElement1 = document.getElementById( 'polite-1' );
  const politeElement2 = document.getElementById( 'polite-2' );
  const politeElement3 = document.getElementById( 'polite-3' );
  const politeElement4 = document.getElementById( 'polite-4' );
  const ariaLiveElements = [ politeElement1, politeElement2, politeElement3, politeElement4 ];

  class AriaHerald {

    constructor() {
      // {boolean} - whether or not this instance has been initialized or not
      this.initialized = false;

      // index of current aria-live element to use, updated every time an event triggers
      this.elementIndex = 0;

      // @public {null|Emitter} - set in initialize method. Emit whenever we announce.
      this.announcingEmitter = null;
    }

    /**
     * Initialize AriaHerald to allow usage of its features. If not initialized, then it will no-op. This allows
     * AriaHerald to be disabled completely if a11y is not enabled.
     */
    initialize() {

      // verify that all DOM elements with aria-live are in the document
      assert && assert( document.getElementById( 'aria-live-elements' ), 'No alert container element found in document' );
      assert && assert( politeElement1, 'aria-live element 1 missing from document, all are required' );
      assert && assert( politeElement2, 'aria-live element 2 missing from document, all are required' );
      assert && assert( politeElement3, 'aria-live element 3 missing from document, all are required' );
      assert && assert( politeElement4, 'aria-live element 4 missing from document, all are required' );

      this.initialized = true;

      this.announcingEmitter = new Emitter( {
        argumentTypes: [ { valueType: 'string' }, { valueType: 'boolean' } ]
      } );

      // no need to be removed, exists for the lifetime of the simulation.
      this.announcingEmitter.addListener( ( textContent, withClear ) => {
        const element = ariaLiveElements[ this.elementIndex ];
        this.updateLiveElement( element, textContent, withClear );

        // update index for next time
        this.elementIndex = ( this.elementIndex + 1 ) % ariaLiveElements.length;
      } );
    }

    /**
     * Announce a polite alert.  This alert should be announced when the user has finished their current interaction or
     * after other utterances in the screen reader's queue are finished.
     * @public
     *
     * @param {string} textContent - the polite content to announce
     * @param {boolean} [withClear] - optional, whether or not to remove the old content from the alert before updating
     */
    announcePolite( textContent, withClear ) {

      // or the default to support propper emitter typing
      this.announcingEmitter.emit( textContent, withClear || DEFAULT_WITH_CLEAR );
    }

    /**
     * Update an element with the 'aria-live' attribute by setting its text content.
     * If using withClear, old element text content will be explicitly removed before new text content is set.  This will
     * allow sequential alerts with identical text content to be announced multiple times in a row, which some screen
     * readers might have prevented.
     *
     * @param {HTMLElement} liveElement - the HTML element that will send the alert to the assistive technology
     * @param {string} textContent - the content to be announced
     * @param {boolean} [withClear] - optional, whether or not to remove the old text content before updating the element
     * @private
     */
    updateLiveElement( liveElement, textContent, withClear ) {

      // no-op if not initialized
      if ( !this.initialized ) {
        return;
      }

      withClear = ( withClear === undefined ) ? DEFAULT_WITH_CLEAR : withClear;
      assert && assert( typeof withClear === 'boolean', 'withClear must be of type boolean' );

      // clearing the old content allows repeated alerts
      if ( withClear ) { liveElement.textContent = ''; }

      AccessibilityUtil.setTextContent( liveElement, textContent );

      // after a small delay, remove this alert content from the DOM so that it cannot be found again - must occur
      // after a delay for screen reader to register the change in text content
      timer.setTimeout( () => { liveElement.textContent = ''; }, 200 );
    }
  }

  return sceneryPhet.register( 'ariaHerald', new AriaHerald() );
} );<|endoftext|>


<commit_before>define([
    'jquery',
    'knockout',
    'underscore',
    'dropzone',
    'viewmodels/widget',
    'bindings/dropzone'
], function($, ko, _, Dropzone, WidgetViewModel) {
    /**
     * registers a text-widget component for use in forms
     * @function external:"ko.components".text-widget
     * @param {object} params
     * @param {string} params.value - the value being managed
     * @param {function} params.config - observable containing config object
     * @param {string} params.config().instructions - label to use alongside the text input
     */
    return ko.components.register('file-widget', {
        viewModel: function(params) {
            var self = this;
            params.configKeys = ['acceptedFiles', 'maxFilesize'];

            WidgetViewModel.apply(this, [params]);

            if (this.form) {
                this.form.on('after-update', function(req, tile) {
                    if ((self.tile === tile || _.contains(tile.tiles, self.tile)) && req.status === 200) {
                        if (self.filesForUpload().length > 0) {
                            self.filesForUpload.removeAll();
                        }
                        var data = req.responseJSON.data[self.node.nodeid];
                        if (Array.isArray(data)) {
                            self.uploadedFiles(data)
                        }
                        self.dropzone.removeAllFiles(true);
                        self.formData.delete('file-list_' + self.node.nodeid);
                    }
                });
                this.form.on('tile-reset', function(tile) {
                    if ((self.tile === tile || _.contains(tile.tiles, self.tile))) {
                        if (self.filesForUpload().length > 0) {
                            self.filesForUpload.removeAll();
                        }
                        if (Array.isArray(self.value())) {
                            self.uploadedFiles(self.value())
                        }
                        self.dropzone.removeAllFiles(true);
                        self.formData.delete('file-list_' + self.node.nodeid);
                    }
                });
            }
            this.maxFilesize(1);
            this.acceptedFiles.subscribe(function(val) {
                if (self.dropzone) {
                    self.dropzone.hiddenFileInput.setAttribute("accept", val);
                }
            });
            this.maxFilesize.subscribe(function(val) {
                if (self.dropzone) {
                    self.dropzone.options.maxFilesize = val;
                }
            });

            this.filesForUpload = ko.observableArray();
            this.uploadedFiles = ko.observableArray();
            if (Array.isArray(self.value())) {
                this.uploadedFiles(self.value());
            }
            this.removeFile = function(file) {
                var filesForUpload = self.filesForUpload();
                var uploadedFiles = self.uploadedFiles();
                if (file.file_id) {
                    file = _.find(uploadedFiles, function (uploadedFile) {
                        return file.file_id ===  ko.unwrap(uploadedFile.file_id);
                    });
                    self.uploadedFiles.remove(file);
                } else {
                    file = filesForUpload[file.index];
                    self.filesForUpload.remove(file);
                }
            }

            this.formatSize = function (file) {
                var bytes = ko.unwrap(file.size);
                if(bytes == 0) return '0 Byte';
                var k = 1000; // or 1024 for binary
                var dm = 2;
                var sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PB', 'EB', 'ZB', 'YB'];
                var i = Math.floor(Math.log(bytes) / Math.log(k));
                return '' + parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
            };

            this.filesJSON = ko.computed(function() {
                var filesForUpload = self.filesForUpload();
                var uploadedFiles = self.uploadedFiles();
                return ko.toJS(uploadedFiles.concat(
                    _.map(filesForUpload, function(file, i) {
                        return {
                            name: file.name,
                            accepted: file.accepted,
                            height: file.height,
                            lastModified: file.lastModified,
                            size: file.size,
                            status: file.status,
                            type: file.type,
                            width: file.width,
                            url: null,
                            file_id: null,
                            index: i,
                            content: URL.createObjectURL(file),
                            error: file.error
                        };
                    })
                ));
            }).extend({throttle: 100});

            this.filesJSON.subscribe(function(value) {
                if (_.contains(self.formData.keys(), 'file-list_' + self.node.nodeid)) {
                    self.formData.delete('file-list_' + self.node.nodeid);
                }
                _.each(self.filesForUpload(), function(file) {
                    if (file.accepted) {
                        self.formData.append('file-list_' + self.node.nodeid, file, file.name);
                    }
                });
                self.value(
                    value.filter(function(file) {
                        return file.accepted;
                    })
                );
            });

            this.dropzoneOptions = {
                url: "/target-url",
                dictDefaultMessage: '',
                autoProcessQueue: false,
                thumbnailWidth: 50,
                thumbnailHeight: 50,
                parallelUploads: 20,
                previewTemplate: $("template#file-widget-dz-preview").html(),
                autoQueue: false,
                previewsContainer: ".dz-previews",
                clickable: ".fileinput-button",
                acceptedFiles: this.acceptedFiles(),
                maxFilesize: this.maxFilesize(),
                init: function() {
                    self.dropzone = this;

                    this.on("addedfile", function(file) {
                        self.filesForUpload.push(file);
                    });

                    this.on("error", function(file, error) {
                        file.error = error;
                        self.filesForUpload.valueHasMutated()
                    });

                    this.on("removedfile", function(file) {
                        self.filesForUpload.remove(file);
                    });
                }
            };

            this.reset = function() {
                if (self.dropzone) {
                    self.dropzone.removeAllFiles(true);
                    self.uploadedFiles.removeAll();
                    self.filesForUpload.removeAll();
                }
            };

            this.displayValue = ko.computed(function() {
                return self.uploadedFiles().length;
            });
        },
        template: {
            require: 'text!widget-templates/file'
        }
    });
});
<commit_msg>removing test code<commit_after>define([
    'jquery',
    'knockout',
    'underscore',
    'dropzone',
    'viewmodels/widget',
    'bindings/dropzone'
], function($, ko, _, Dropzone, WidgetViewModel) {
    /**
     * registers a text-widget component for use in forms
     * @function external:"ko.components".text-widget
     * @param {object} params
     * @param {string} params.value - the value being managed
     * @param {function} params.config - observable containing config object
     * @param {string} params.config().instructions - label to use alongside the text input
     */
    return ko.components.register('file-widget', {
        viewModel: function(params) {
            var self = this;
            params.configKeys = ['acceptedFiles', 'maxFilesize'];

            WidgetViewModel.apply(this, [params]);

            if (this.form) {
                this.form.on('after-update', function(req, tile) {
                    if ((self.tile === tile || _.contains(tile.tiles, self.tile)) && req.status === 200) {
                        if (self.filesForUpload().length > 0) {
                            self.filesForUpload.removeAll();
                        }
                        var data = req.responseJSON.data[self.node.nodeid];
                        if (Array.isArray(data)) {
                            self.uploadedFiles(data)
                        }
                        self.dropzone.removeAllFiles(true);
                        self.formData.delete('file-list_' + self.node.nodeid);
                    }
                });
                this.form.on('tile-reset', function(tile) {
                    if ((self.tile === tile || _.contains(tile.tiles, self.tile))) {
                        if (self.filesForUpload().length > 0) {
                            self.filesForUpload.removeAll();
                        }
                        if (Array.isArray(self.value())) {
                            self.uploadedFiles(self.value())
                        }
                        self.dropzone.removeAllFiles(true);
                        self.formData.delete('file-list_' + self.node.nodeid);
                    }
                });
            }
            this.acceptedFiles.subscribe(function(val) {
                if (self.dropzone) {
                    self.dropzone.hiddenFileInput.setAttribute("accept", val);
                }
            });
            this.maxFilesize.subscribe(function(val) {
                if (self.dropzone) {
                    self.dropzone.options.maxFilesize = val;
                }
            });

            this.filesForUpload = ko.observableArray();
            this.uploadedFiles = ko.observableArray();
            if (Array.isArray(self.value())) {
                this.uploadedFiles(self.value());
            }
            this.removeFile = function(file) {
                var filesForUpload = self.filesForUpload();
                var uploadedFiles = self.uploadedFiles();
                if (file.file_id) {
                    file = _.find(uploadedFiles, function (uploadedFile) {
                        return file.file_id ===  ko.unwrap(uploadedFile.file_id);
                    });
                    self.uploadedFiles.remove(file);
                } else {
                    file = filesForUpload[file.index];
                    self.filesForUpload.remove(file);
                }
            }

            this.formatSize = function (file) {
                var bytes = ko.unwrap(file.size);
                if(bytes == 0) return '0 Byte';
                var k = 1000; // or 1024 for binary
                var dm = 2;
                var sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PB', 'EB', 'ZB', 'YB'];
                var i = Math.floor(Math.log(bytes) / Math.log(k));
                return '' + parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
            };

            this.filesJSON = ko.computed(function() {
                var filesForUpload = self.filesForUpload();
                var uploadedFiles = self.uploadedFiles();
                return ko.toJS(uploadedFiles.concat(
                    _.map(filesForUpload, function(file, i) {
                        return {
                            name: file.name,
                            accepted: file.accepted,
                            height: file.height,
                            lastModified: file.lastModified,
                            size: file.size,
                            status: file.status,
                            type: file.type,
                            width: file.width,
                            url: null,
                            file_id: null,
                            index: i,
                            content: URL.createObjectURL(file),
                            error: file.error
                        };
                    })
                ));
            }).extend({throttle: 100});

            this.filesJSON.subscribe(function(value) {
                if (_.contains(self.formData.keys(), 'file-list_' + self.node.nodeid)) {
                    self.formData.delete('file-list_' + self.node.nodeid);
                }
                _.each(self.filesForUpload(), function(file) {
                    if (file.accepted) {
                        self.formData.append('file-list_' + self.node.nodeid, file, file.name);
                    }
                });
                self.value(
                    value.filter(function(file) {
                        return file.accepted;
                    })
                );
            });

            this.dropzoneOptions = {
                url: "/target-url",
                dictDefaultMessage: '',
                autoProcessQueue: false,
                thumbnailWidth: 50,
                thumbnailHeight: 50,
                parallelUploads: 20,
                previewTemplate: $("template#file-widget-dz-preview").html(),
                autoQueue: false,
                previewsContainer: ".dz-previews",
                clickable: ".fileinput-button",
                acceptedFiles: this.acceptedFiles(),
                maxFilesize: this.maxFilesize(),
                init: function() {
                    self.dropzone = this;

                    this.on("addedfile", function(file) {
                        self.filesForUpload.push(file);
                    });

                    this.on("error", function(file, error) {
                        file.error = error;
                        self.filesForUpload.valueHasMutated()
                    });

                    this.on("removedfile", function(file) {
                        self.filesForUpload.remove(file);
                    });
                }
            };

            this.reset = function() {
                if (self.dropzone) {
                    self.dropzone.removeAllFiles(true);
                    self.uploadedFiles.removeAll();
                    self.filesForUpload.removeAll();
                }
            };

            this.displayValue = ko.computed(function() {
                return self.uploadedFiles().length;
            });
        },
        template: {
            require: 'text!widget-templates/file'
        }
    });
});
<|endoftext|>


<commit_before>// JavaScript Document

// Scripts written by __gulp_init_author_name__ @ __gulp_init_author_company__

module.exports = {
    html(gulp, plugins, ran_tasks, on_error) {
        // task-specific plugins
        const TEMPLATE = require("gulp-template");

        // copy binaries
        const COPY_BINARIES = (html_directory, source = [`${global.settings.paths.src}/**/*`, `!${global.settings.paths.src}{/assets,/assets/**}`]) => {
            return gulp.src(source)
                // prevent breaking on error
                .pipe(plugins.plumber({ errorHandler: on_error }))
                // check if source is newer than destination
                .pipe(plugins.gulpif(!plugins.argv.dist, plugins.newer(html_directory)))
                // check if a file is a binary
                .pipe(plugins.is_binary())
                // skip file if it's not a binary
                .pipe(plugins.through.obj((file, enc, next) => {
                    if (!file.isBinary()) {
                        next();
                        return;
                    }

                    // go to next file
                    next(null, file);
                }))
                // output to compiled directory
                .pipe(gulp.dest(html_directory));
        };

        // copy composer
        const COPY_COMPOSER = (html_directory, source = [`${global.settings.paths.vendor}/**/*`]) => {
            return gulp.src(source)
                // prevent breaking on error
                .pipe(plugins.plumber({ errorHandler: on_error }))
                // check if source is newer than destination
                .pipe(plugins.gulpif(!plugins.argv.dist, plugins.newer(html_directory)))
                // output to compiled directory
                .pipe(gulp.dest(`${html_directory}/functions`));
        };

        // process HTML
        const PROCESS_HTML = (html_directory, source = [`${global.settings.paths.src}/**/*`, `!${global.settings.paths.src}/assets`]) => {
            // read data from package.json
            const JSON_DATA = plugins.json.readFileSync("package.json");

            return gulp.src(source)
                // prevent breaking on error
                .pipe(plugins.plumber({ errorHandler: on_error }))
                // check if source is newer than destination
                .pipe(plugins.gulpif(!plugins.argv.dist, plugins.newer(html_directory)))
                // check if file is a binary
                .pipe(plugins.is_binary())
                // skip file if it's a binary
                .pipe(plugins.through.obj((file, enc, next) => {
                    if (file.isBinary()) {
                        next();
                        return;
                    }

                    // go to next file
                    next(null, file);
                }))
                // replace variables
                .pipe(TEMPLATE({
                    name: JSON_DATA.name,
                    pwa_name: JSON_DATA.progressiveWebApp.name + (!plugins.argv.dist ? " - DEV" : ""),
                    pwa_short_name: JSON_DATA.progressiveWebApp.short_name,
                    pwa_theme_color: JSON_DATA.progressiveWebApp.theme_color,
                    description: JSON_DATA.description,
                    version: JSON_DATA.version,
                    repository: JSON_DATA.repository.url,
                    license: JSON_DATA.license,
                }))
                // output to compiled directory
                .pipe(gulp.dest(html_directory));
        };

        // html task, copies binaries, converts includes & variables in HTML
        return new Promise ((resolve) => {
            // set HTML directory
            const HTML_DIRECTORY = plugins.argv.dist ? global.settings.paths.dist : global.settings.paths.dev;

            // process all non-asset files
            const BINARIES = COPY_BINARIES(HTML_DIRECTORY, [`${global.settings.paths.src}/**/*`, `!${global.settings.paths.src}{/assets,/assets/**}`, `!${global.settings.paths.src}/**/*.{jpg,png,svg}`]);
            const COMPOSER = COPY_COMPOSER(HTML_DIRECTORY, [`${global.settings.paths.vendor}/**/*`]);
            const HTML     = PROCESS_HTML(HTML_DIRECTORY, [`${global.settings.paths.src}/**/*`, `!${global.settings.paths.src}{/assets,/assets/**}`, `!${global.settings.paths.src}/**/*.{jpg,png,svg}`]);

            // merge both steams back in to one
            plugins.merge(BINARIES, COMPOSER, HTML)
                // prevent breaking on error
                .pipe(plugins.plumber({ errorHandler: on_error }))
                // notify that task is complete, if not part of default or watch
                .pipe(plugins.gulpif(gulp.seq.indexOf("html") > gulp.seq.indexOf("default"), plugins.notify({
                    title:   "Success!",
                    message: "HTML task complete!",
                    onLast:  true,
                })))
                // push task to ran_tasks array
                .on("data", () => {
                    if (ran_tasks.indexOf("html") < 0) {
                        ran_tasks.push("html");
                    }
                })
                .on("end", () => {
                    resolve();
                });
        });
    }
};
<commit_msg>Exclude images from html task; Close #181<commit_after>// JavaScript Document

// Scripts written by __gulp_init_author_name__ @ __gulp_init_author_company__

module.exports = {
    html(gulp, plugins, ran_tasks, on_error) {
        // task-specific plugins
        const TEMPLATE = require("gulp-template");

        // copy binaries
        const COPY_BINARIES = (html_directory, source = [`${global.settings.paths.src}/**/*`, `!${global.settings.paths.src}{/assets,/assets/**}`]) => {
            return gulp.src(source)
                // prevent breaking on error
                .pipe(plugins.plumber({ errorHandler: on_error }))
                // check if source is newer than destination
                .pipe(plugins.gulpif(!plugins.argv.dist, plugins.newer(html_directory)))
                // check if a file is a binary
                .pipe(plugins.is_binary())
                // skip file if it's not a binary
                .pipe(plugins.through.obj((file, enc, next) => {
                    if (!file.isBinary()) {
                        next();
                        return;
                    }

                    // go to next file
                    next(null, file);
                }))
                // output to compiled directory
                .pipe(gulp.dest(html_directory));
        };

        // copy composer
        const COPY_COMPOSER = (html_directory, source = [`${global.settings.paths.vendor}/**/*`]) => {
            return gulp.src(source)
                // prevent breaking on error
                .pipe(plugins.plumber({ errorHandler: on_error }))
                // check if source is newer than destination
                .pipe(plugins.gulpif(!plugins.argv.dist, plugins.newer(html_directory)))
                // output to compiled directory
                .pipe(gulp.dest(`${html_directory}/functions`));
        };

        // process HTML
        const PROCESS_HTML = (html_directory, source = [`${global.settings.paths.src}/**/*`, `!${global.settings.paths.src}/assets`, `!${global.settings.paths.src}/**/*.{jpg,png,svg}`]) => {
            // read data from package.json
            const JSON_DATA = plugins.json.readFileSync("package.json");

            return gulp.src(source)
                // prevent breaking on error
                .pipe(plugins.plumber({ errorHandler: on_error }))
                // check if source is newer than destination
                .pipe(plugins.gulpif(!plugins.argv.dist, plugins.newer(html_directory)))
                // check if file is a binary
                .pipe(plugins.is_binary())
                // skip file if it's a binary
                .pipe(plugins.through.obj((file, enc, next) => {
                    if (file.isBinary()) {
                        next();
                        return;
                    }

                    // go to next file
                    next(null, file);
                }))
                // replace variables
                .pipe(TEMPLATE({
                    name: JSON_DATA.name,
                    pwa_name: JSON_DATA.progressiveWebApp.name + (!plugins.argv.dist ? " - DEV" : ""),
                    pwa_short_name: JSON_DATA.progressiveWebApp.short_name,
                    pwa_theme_color: JSON_DATA.progressiveWebApp.theme_color,
                    description: JSON_DATA.description,
                    version: JSON_DATA.version,
                    repository: JSON_DATA.repository.url,
                    license: JSON_DATA.license,
                }))
                // output to compiled directory
                .pipe(gulp.dest(html_directory));
        };

        // html task, copies binaries, converts includes & variables in HTML
        return new Promise ((resolve) => {
            // set HTML directory
            const HTML_DIRECTORY = plugins.argv.dist ? global.settings.paths.dist : global.settings.paths.dev;

            // process all non-asset files
            const BINARIES = COPY_BINARIES(HTML_DIRECTORY, [`${global.settings.paths.src}/**/*`, `!${global.settings.paths.src}{/assets,/assets/**}`, `!${global.settings.paths.src}/**/*.{jpg,png,svg}`]);
            const COMPOSER = COPY_COMPOSER(HTML_DIRECTORY, [`${global.settings.paths.vendor}/**/*`]);
            const HTML     = PROCESS_HTML(HTML_DIRECTORY, [`${global.settings.paths.src}/**/*`, `!${global.settings.paths.src}{/assets,/assets/**}`, `!${global.settings.paths.src}/**/*.{jpg,png,svg}`]);

            // merge both steams back in to one
            plugins.merge(BINARIES, COMPOSER, HTML)
                // prevent breaking on error
                .pipe(plugins.plumber({ errorHandler: on_error }))
                // notify that task is complete, if not part of default or watch
                .pipe(plugins.gulpif(gulp.seq.indexOf("html") > gulp.seq.indexOf("default"), plugins.notify({
                    title:   "Success!",
                    message: "HTML task complete!",
                    onLast:  true,
                })))
                // push task to ran_tasks array
                .on("data", () => {
                    if (ran_tasks.indexOf("html") < 0) {
                        ran_tasks.push("html");
                    }
                })
                .on("end", () => {
                    resolve();
                });
        });
    }
};
<|endoftext|>


<commit_before>import test from 'ava';
import ansiCodes from './fixtures/ansi-codes';
import m from '.';

const consumptionChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+1234567890-=[]{};\':"./>?,<\\|';

// Testing against codes found at: http://ascii-table.com/ansi-escape-sequences-vt-100.php
test('match ansi code in a string', t => {
	t.regex('foo\u001B[4mcake\u001B[0m', m());
	t.regex('\u001B[4mcake\u001B[0m', m());
	t.regex('foo\u001B[4mcake\u001B[0m', m());
	t.regex('\u001B[0m\u001B[4m\u001B[42m\u001B[31mfoo\u001B[39m\u001B[49m\u001B[24mfoo\u001B[0m', m());
	t.regex('foo\u001B[mfoo', m());
});

test('match ansi code from ls command', t => {
	t.regex('\u001B[00;38;5;244m\u001B[m\u001B[00;38;5;33mfoo\u001B[0m', m());
});

test('match reset;setfg;setbg;italics;strike;underline sequence in a string', t => {
	t.regex('\u001B[0;33;49;3;9;4mbar\u001B[0m', m());
	t.is('foo\u001B[0;33;49;3;9;4mbar'.match(m())[0], '\u001B[0;33;49;3;9;4m');
});

test('match clear tabs sequence in a string', t => {
	t.regex('foo\u001B[0gbar', m());
	t.is('foo\u001B[0gbar'.match(m())[0], '\u001B[0g');
});

test('match clear line from cursor right in a string', t => {
	t.regex('foo\u001B[Kbar', m());
	t.is('foo\u001B[Kbar'.match(m())[0], '\u001B[K');
});

test('match clear screen in a string', t => {
	t.regex('foo\u001B[2Jbar', m());
	t.is('foo\u001B[2Jbar'.match(m())[0], '\u001B[2J');
});

// Testing against extended codes (excluding codes ending in 0-9)
for (const codeSet of Object.keys(ansiCodes)) {
	for (const el of ansiCodes[codeSet]) {
		const code = el[0];
		const codeInfo = el[1];
		const skip = /\d$/.test(code);
		const skipText = skip ? '[SKIP] ' : '';
		const ecode = `\u001B${code}`;

		test(`${skipText}${code} → ${codeInfo[0]}`, t => {
			if (skip) {
				t.pass();
				return;
			}

			const string = `hel${ecode}lo`;
			t.regex(string, m());
			t.is(string.match(m())[0], ecode);
			t.is(string.replace(m(), ''), 'hello');
		});

		test(`${skipText}${code} should not overconsume`, t => {
			if (skip) {
				t.pass();
				return;
			}

			for (const c of consumptionChars) {
				const string = ecode + c;
				t.regex(string, m());
				t.is(string.match(m())[0], ecode);
				t.is(string.replace(m(), ''), c);
			}
		});
	}
}
<commit_msg>Add failing test for #21 (#22)<commit_after>import test from 'ava';
import ansiCodes from './fixtures/ansi-codes';
import m from '.';

const consumptionChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+1234567890-=[]{};\':"./>?,<\\|';

// Testing against codes found at: http://ascii-table.com/ansi-escape-sequences-vt-100.php
test('match ansi code in a string', t => {
	t.regex('foo\u001B[4mcake\u001B[0m', m());
	t.regex('\u001B[4mcake\u001B[0m', m());
	t.regex('foo\u001B[4mcake\u001B[0m', m());
	t.regex('\u001B[0m\u001B[4m\u001B[42m\u001B[31mfoo\u001B[39m\u001B[49m\u001B[24mfoo\u001B[0m', m());
	t.regex('foo\u001B[mfoo', m());
});

test('match ansi code from ls command', t => {
	t.regex('\u001B[00;38;5;244m\u001B[m\u001B[00;38;5;33mfoo\u001B[0m', m());
});

test('match reset;setfg;setbg;italics;strike;underline sequence in a string', t => {
	t.regex('\u001B[0;33;49;3;9;4mbar\u001B[0m', m());
	t.is('foo\u001B[0;33;49;3;9;4mbar'.match(m())[0], '\u001B[0;33;49;3;9;4m');
});

test('match clear tabs sequence in a string', t => {
	t.regex('foo\u001B[0gbar', m());
	t.is('foo\u001B[0gbar'.match(m())[0], '\u001B[0g');
});

test('match clear line from cursor right in a string', t => {
	t.regex('foo\u001B[Kbar', m());
	t.is('foo\u001B[Kbar'.match(m())[0], '\u001B[K');
});

test('match clear screen in a string', t => {
	t.regex('foo\u001B[2Jbar', m());
	t.is('foo\u001B[2Jbar'.match(m())[0], '\u001B[2J');
});

test.failing('match "change icon name and window title" in string', t => {
	t.is('\u001B]0;sg@tota:~/git/\u0007\u001B[01;32m[sg@tota\u001B[01;37m misc-tests\u001B[01;32m]$'.match(m())[0], '\u001B]0;sg@tota:~/git/\u0007');
});

// Testing against extended codes (excluding codes ending in 0-9)
for (const codeSet of Object.keys(ansiCodes)) {
	for (const el of ansiCodes[codeSet]) {
		const code = el[0];
		const codeInfo = el[1];
		const skip = /\d$/.test(code);
		const skipText = skip ? '[SKIP] ' : '';
		const ecode = `\u001B${code}`;

		test(`${skipText}${code} → ${codeInfo[0]}`, t => {
			if (skip) {
				t.pass();
				return;
			}

			const string = `hel${ecode}lo`;
			t.regex(string, m());
			t.is(string.match(m())[0], ecode);
			t.is(string.replace(m(), ''), 'hello');
		});

		test(`${skipText}${code} should not overconsume`, t => {
			if (skip) {
				t.pass();
				return;
			}

			for (const c of consumptionChars) {
				const string = ecode + c;
				t.regex(string, m());
				t.is(string.match(m())[0], ecode);
				t.is(string.replace(m(), ''), c);
			}
		});
	}
}
<|endoftext|>


<commit_before>var assert = require('assert');
var lineInfo = require('../');

describe('A line', function () {
  before(function () {
    this.params = {
      line: 'git checkoutworld',
      cursor: 'git checkout'.length
    };
  });

  describe('when analyzed', function () {
    before(function () {
      this.actual = lineInfo(this.params);
    });

    it('matches expected `line` content', function () {
      assert.deepEqual(this.actual.line, {
        value: 'git checkoutworld',
        index: 'git checkout'.length,
        partialLeft: 'git checkout',
        partialRight: 'world'
      });
    });

    it('matches expeted `words` content', function () {
      assert.deepEqual(this.actual.words, {
        value: ['git', 'checkoutworld'],
        index: 1,
        partialLeft: ['git', 'checkout'],
        partialRight: ['world']
      });
    });

    it('matches expeted `words` content', function () {
      assert.deepEqual(this.actual.word, {
        value: 'checkoutworld',
        index: 'checkout'.length,
        partialLeft: 'checkout',
        partialRight: 'world'
      });
    });
  });
});
<commit_msg>Adding a regression test for empty right content<commit_after>var assert = require('assert');
var lineInfo = require('../');

describe('A line', function () {
  before(function () {
    this.params = {
      line: 'git checkoutworld',
      cursor: 'git checkout'.length
    };
  });

  describe('when analyzed', function () {
    before(function () {
      this.actual = lineInfo(this.params);
    });

    it('matches expected `line` content', function () {
      assert.deepEqual(this.actual.line, {
        value: 'git checkoutworld',
        index: 'git checkout'.length,
        partialLeft: 'git checkout',
        partialRight: 'world'
      });
    });

    it('matches expeted `words` content', function () {
      assert.deepEqual(this.actual.words, {
        value: ['git', 'checkoutworld'],
        index: 1,
        partialLeft: ['git', 'checkout'],
        partialRight: ['world']
      });
    });

    it('matches expeted `words` content', function () {
      assert.deepEqual(this.actual.word, {
        value: 'checkoutworld',
        index: 'checkout'.length,
        partialLeft: 'checkout',
        partialRight: 'world'
      });
    });
  });
});

describe('A line with no right content', function () {
  before(function () {
    this.params = {
      line: 'npm publish',
      cursor: 'npm publish'.length
    };
  });

  describe('when analyzed', function () {
    before(function () {
      this.actual = lineInfo(this.params);
    });

    it('provides right content info', function () {

    });
  });
});<|endoftext|>