<commit_before>export function trace(clientId: ?string, title: string, value: Object | string) {
  let prefix = '';
  if (window.performance) {
    prefix = '[' + (window.performance.now() / 1000).toFixed(3) + ']';
  }
  if (clientId) {
    prefix = prefix + '[' + clientId + ']';
  }

  if (isEdge()) {
    console.log(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
  else {
    console.info(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
}

function browser() {
  const ua = window.navigator.userAgent.toLocaleLowerCase();
  if (ua.indexOf('edge') !== -1) {
    return 'edge';
  }
  else if (ua.indexOf('chrome')  !== -1 && ua.indexOf('edge') === -1) {
    return 'chrome';
  }
  else if (ua.indexOf('safari')  !== -1 && ua.indexOf('chrome') === -1) {
    return 'safari';
  }
  else if (ua.indexOf('opera')   !== -1) {
    return 'opera';
  }
  else if (ua.indexOf('firefox') !== -1) {
    return 'firefox';
  }
  return ;
}

function isPlanB() {
  return browser() === 'chrome' || browser() === 'safari';
}

export function isUnifiedChrome() {
  if (browser() !== 'chrome') {
    return false;
  }
  const ua = window.navigator.userAgent.toLocaleLowerCase();
  const splitedUserAgent = /chrome\/([\d.]+)/.exec(ua);
  if (!splitedUserAgent || splitedUserAgent.length < 2) {
    return false;
  }
  return 71 <= parseInt(splitedUserAgent[1]);
}

export function isUnifiedSafari() {
  if (browser() !== 'safari') {
    return false;
  }
  const appVersion = window.navigator.appVersion.toLowerCase();
  const version = /version\/([\d.]+)/.exec(appVersion).pop();
  return 12.0 < parseFloat(version);
}

export function isEdge() {
  return browser() === 'edge';
}

export function isSafari() {
  return browser() === 'safari';
}

export function isChrome() {
  return browser() === 'chrome';
}

export function replaceAnswerSdp(sdp) {
  let ssrcPattern = new RegExp(/m=video[\s\S]*?(a=ssrc:(\d+)\scname:.+\r\n(a=ssrc:\2\smsid:.+\r\na=ssrc:\2\smslabel:.+\r\na=ssrc:\2\slabel:.+\r\n)?)/);  // eslint-disable-line
  const found = sdp.match(ssrcPattern);
  if (!found) {
    return sdp;
  }

  const ssrcAttributes = found[1];
  ssrcPattern = found[1];
  const ssrcId = parseInt(found[2]);
  const ssrcIdPattern = new RegExp(ssrcId.toString(), 'g');
  const ssrcGroup = ['a=ssrc-group:SIM'];
  const ssrcAttributeList = [];
  for (let i = 0; i < 3; i += 1) {
    ssrcGroup.push((ssrcId + i).toString());
    ssrcAttributeList.push(ssrcAttributes.replace(ssrcIdPattern, (ssrcId + i).toString()));
  }
  return sdp.replace(ssrcPattern, [ssrcGroup.join(' '), '\r\n', ssrcAttributeList.join('')].join(''));
}

export function createSignalingMessage(offerSDP, role, channelId, metadata, options) {
  const message = {
    type: 'connect',
    role: role,
    channel_id: channelId,
    metadata: metadata,
    sdp: offerSDP,
    userAgent: window.navigator.userAgent,
    audio: true,
    video: true
  };
  Object.keys(message).forEach(key => {
    if (message[key] === undefined) {
      message[key] = null;
    }
  });
  if ('multistream' in options && options.multistream === true) {
    // multistream
    message.multistream = true;
    if (!isUnifiedChrome() && !isUnifiedSafari() && isPlanB()) {
      message.plan_b = true;
    }
    // spotlight
    if ('spotlight' in options) {
      message.spotlight = options.spotlight;
    }
  } else if ('simulcast' in options || 'simulcastQuality' in options) {
    if (!(isUnifiedSafari() || isChrome())) {
      throw new Error('Simulcast can not be used with this browse be used with this browserr');
    }
    // simulcast
    if ('simulcast' in options && options.simulcast === true) {
      message.simulcast = true;
    }
    const simalcastQualities = ['low', 'middle', 'high'];
    if ('simulcastQuality' in options && 0 <= simalcastQualities.indexOf(options.simulcastQuality)) {
      message.simulcast = {
        quality: options.simulcastQuality
      };
    }
  }
  // parse options
  const audioPropertyKeys = ['audioCodecType', 'audioBitRate'];
  const videoPropertyKeys = ['videoCodecType', 'videoBitRate'];
  const copyOptions = Object.assign({}, options);
  Object.keys(copyOptions).forEach(key => {
    if (key === 'audio' && typeof copyOptions[key] === 'boolean') return;
    if (key === 'video' && typeof copyOptions[key] === 'boolean') return;
    if (0 <= audioPropertyKeys.indexOf(key) && copyOptions[key] !== null) return;
    if (0 <= videoPropertyKeys.indexOf(key) && copyOptions[key] !== null) return;
    delete copyOptions[key];
  });

  if ('audio' in copyOptions) {
    message.audio = copyOptions.audio;
  }
  const hasAudioProperty = Object.keys(copyOptions).some(key => {
    return 0 <= audioPropertyKeys.indexOf(key);
  });
  if (message.audio && hasAudioProperty) {
    message.audio = {};
    if ('audioCodecType' in copyOptions) {
      message.audio['codec_type'] = copyOptions.audioCodecType;
    }
    if ('audioBitRate' in copyOptions) {
      message.audio['bit_rate'] = copyOptions.audioBitRate;
    }
  }

  if ('video' in copyOptions) {
    message.video = copyOptions.video;
  }
  const hasVideoProperty = Object.keys(copyOptions).some(key => {
    return 0 <= videoPropertyKeys.indexOf(key);
  });
  if (message.video && hasVideoProperty) {
    message.video = {};
    if ('videoCodecType' in copyOptions) {
      message.video['codec_type'] = copyOptions.videoCodecType;
    }
    if ('videoBitRate' in copyOptions) {
      message.video['bit_rate'] = copyOptions.videoBitRate;
    }
  }

  return message;
}
<commit_msg>Safari の Unified Plan 判定方法を変更する<commit_after>export function trace(clientId: ?string, title: string, value: Object | string) {
  let prefix = '';
  if (window.performance) {
    prefix = '[' + (window.performance.now() / 1000).toFixed(3) + ']';
  }
  if (clientId) {
    prefix = prefix + '[' + clientId + ']';
  }

  if (isEdge()) {
    console.log(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
  else {
    console.info(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
}

function browser() {
  const ua = window.navigator.userAgent.toLocaleLowerCase();
  if (ua.indexOf('edge') !== -1) {
    return 'edge';
  }
  else if (ua.indexOf('chrome')  !== -1 && ua.indexOf('edge') === -1) {
    return 'chrome';
  }
  else if (ua.indexOf('safari')  !== -1 && ua.indexOf('chrome') === -1) {
    return 'safari';
  }
  else if (ua.indexOf('opera')   !== -1) {
    return 'opera';
  }
  else if (ua.indexOf('firefox') !== -1) {
    return 'firefox';
  }
  return ;
}

function isPlanB() {
  return browser() === 'chrome' || browser() === 'safari';
}

export function isUnifiedChrome() {
  if (browser() !== 'chrome') {
    return false;
  }
  const ua = window.navigator.userAgent.toLocaleLowerCase();
  const splitedUserAgent = /chrome\/([\d.]+)/.exec(ua);
  if (!splitedUserAgent || splitedUserAgent.length < 2) {
    return false;
  }
  return 71 <= parseInt(splitedUserAgent[1]);
}

export function isUnifiedSafari(sdp) {
  if (browser() !== 'safari') {
    return false;
  }
  return sdp.includes('a=group:BUNDLE 0 1');
}

export function isEdge() {
  return browser() === 'edge';
}

export function isSafari() {
  return browser() === 'safari';
}

export function isChrome() {
  return browser() === 'chrome';
}

export function replaceAnswerSdp(sdp) {
  let ssrcPattern = new RegExp(/m=video[\s\S]*?(a=ssrc:(\d+)\scname:.+\r\n(a=ssrc:\2\smsid:.+\r\na=ssrc:\2\smslabel:.+\r\na=ssrc:\2\slabel:.+\r\n)?)/);  // eslint-disable-line
  const found = sdp.match(ssrcPattern);
  if (!found) {
    return sdp;
  }

  const ssrcAttributes = found[1];
  ssrcPattern = found[1];
  const ssrcId = parseInt(found[2]);
  const ssrcIdPattern = new RegExp(ssrcId.toString(), 'g');
  const ssrcGroup = ['a=ssrc-group:SIM'];
  const ssrcAttributeList = [];
  for (let i = 0; i < 3; i += 1) {
    ssrcGroup.push((ssrcId + i).toString());
    ssrcAttributeList.push(ssrcAttributes.replace(ssrcIdPattern, (ssrcId + i).toString()));
  }
  return sdp.replace(ssrcPattern, [ssrcGroup.join(' '), '\r\n', ssrcAttributeList.join('')].join(''));
}

export function createSignalingMessage(offerSDP, role, channelId, metadata, options) {
  const message = {
    type: 'connect',
    role: role,
    channel_id: channelId,
    metadata: metadata,
    sdp: offerSDP,
    userAgent: window.navigator.userAgent,
    audio: true,
    video: true
  };
  Object.keys(message).forEach(key => {
    if (message[key] === undefined) {
      message[key] = null;
    }
  });
  if ('multistream' in options && options.multistream === true) {
    // multistream
    message.multistream = true;
    if (!isUnifiedChrome() && !isUnifiedSafari(offerSDP) && isPlanB()) {
      message.plan_b = true;
    }
    // spotlight
    if ('spotlight' in options) {
      message.spotlight = options.spotlight;
    }
  } else if ('simulcast' in options || 'simulcastQuality' in options) {
    if (!(isUnifiedSafari() || isChrome())) {
      throw new Error('Simulcast can not be used with this browse be used with this browserr');
    }
    // simulcast
    if ('simulcast' in options && options.simulcast === true) {
      message.simulcast = true;
    }
    const simalcastQualities = ['low', 'middle', 'high'];
    if ('simulcastQuality' in options && 0 <= simalcastQualities.indexOf(options.simulcastQuality)) {
      message.simulcast = {
        quality: options.simulcastQuality
      };
    }
  }
  // parse options
  const audioPropertyKeys = ['audioCodecType', 'audioBitRate'];
  const videoPropertyKeys = ['videoCodecType', 'videoBitRate'];
  const copyOptions = Object.assign({}, options);
  Object.keys(copyOptions).forEach(key => {
    if (key === 'audio' && typeof copyOptions[key] === 'boolean') return;
    if (key === 'video' && typeof copyOptions[key] === 'boolean') return;
    if (0 <= audioPropertyKeys.indexOf(key) && copyOptions[key] !== null) return;
    if (0 <= videoPropertyKeys.indexOf(key) && copyOptions[key] !== null) return;
    delete copyOptions[key];
  });

  if ('audio' in copyOptions) {
    message.audio = copyOptions.audio;
  }
  const hasAudioProperty = Object.keys(copyOptions).some(key => {
    return 0 <= audioPropertyKeys.indexOf(key);
  });
  if (message.audio && hasAudioProperty) {
    message.audio = {};
    if ('audioCodecType' in copyOptions) {
      message.audio['codec_type'] = copyOptions.audioCodecType;
    }
    if ('audioBitRate' in copyOptions) {
      message.audio['bit_rate'] = copyOptions.audioBitRate;
    }
  }

  if ('video' in copyOptions) {
    message.video = copyOptions.video;
  }
  const hasVideoProperty = Object.keys(copyOptions).some(key => {
    return 0 <= videoPropertyKeys.indexOf(key);
  });
  if (message.video && hasVideoProperty) {
    message.video = {};
    if ('videoCodecType' in copyOptions) {
      message.video['codec_type'] = copyOptions.videoCodecType;
    }
    if ('videoBitRate' in copyOptions) {
      message.video['bit_rate'] = copyOptions.videoBitRate;
    }
  }

  return message;
}
<|endoftext|>


<commit_before>/*globals define*/
'use strict';

const childProcess = require('child_process');
const spawn = childProcess.exec;
const Logger = require('./Logger');
const logger = new Logger();
const PROJECT_CONFIG = 'webgme-setup.json';

var _ = require('lodash'),
    fs = require('fs'),
    path = require('path'),
    exists = require('exists-file'),
    assert = require('assert'),
    R = require('ramda');

var getRootPath = function(startPath) {
    // Walk back from current path until you find a webgme-setup.json file
    var abspath = path.resolve(startPath || '.'),
        previousPath;

    while (abspath !== previousPath) {
        if (isProjectRoot(abspath)) {
            return abspath;
        }
        previousPath = abspath;
        abspath = path.dirname(abspath);
    }
    return null;
};

var isProjectRoot = function(abspath) {
    // Check for webgme-setup.json file
    if (!exists(abspath)) {
        return null;
    }

    var files = fs.readdirSync(abspath);
    return files.filter(function(file) {
        return file === PROJECT_CONFIG;
    }).length > 0;
};

/**
 * Save file and create directories as needed.
 *
 * @param {File} file
 * @return {undefined}
 */
var saveFile = function(file) {
    var dir = path.dirname(file.name);

    createDir(dir);
    fs.writeFileSync(file.name, file.content);
};

/**
 * Create directory as needed.
 *
 * @param {String} dir
 * @return {undefined}
 */
var createDir = function(dir) {
    var dirs = path.resolve(dir).split(path.sep),
        shortDir,
        i = 1;

    while (i++ < dirs.length) {
        shortDir = dirs.slice(0,i).join(path.sep);
        if (!exists(shortDir)) {
            fs.mkdirSync(shortDir);
        }
    }
};

var saveFilesFromBlobClient = function(blobClient) {
    var artifactNames = Object.keys(blobClient.artifacts);
    for (var i = artifactNames.length; i--;) {
        blobClient.artifacts[artifactNames[i]].files.forEach(saveFile);
    }
};

/* * * * * * * Config Settings * * * * * * * */
var getConfig = function(startPath) {
    var root = getRootPath(startPath),
        config;

    if (!root) {
        return null;
    }

    config = fs.readFileSync(path.join(root, PROJECT_CONFIG));
    return JSON.parse(config);
};

var saveConfig = function(config) {
    var root = getRootPath();
    var configText = JSON.stringify(config, null, 2);
    fs.writeFileSync(path.join(root, PROJECT_CONFIG), configText);
};

/**
 * Update the WebGME config based on the paths in the webgme-setup.json.
 *
 * @return {undefined}
 */
var updateWebGMEConfig = function(startPath) {
    var root = getRootPath(startPath),
        content = getWebGMEConfigContent(startPath),
        templatePath = path.join(__dirname, 'res', 'config.template.js.ejs'),
        template = _.template(fs.readFileSync(templatePath)),
        configPath = path.join(root, 'config', 'config.webgme.js');

    // Add webgme app name to the content
    var appName = require(path.join(root, 'package.json')).name;
    content.appName = appName.replace(/-/g, '_');

    // Add default layout info
    var config = getConfig(startPath);
    if (config) {
        Object.keys(config).forEach(type => {
            Object.keys(config[type].layouts || {}).forEach(layout => {
                if (config[type].layouts[layout].enabled) {
                    content.defaultLayout = layout;
                }
            });
        });
    }

    // Add router info
    content.routers = [];
    Object.keys(config).forEach(type => {
        Object.keys(config[type].routers || {}).forEach(name => {
            let router = config[type].routers[name];
            router.name = name;
            router.srcFile = `${router.src}/${name}.js`;
            content.routers.push(router);
        });
    });

    // Create the WebGME config file
    fs.writeFileSync(configPath, template(content));
};

/**
 * Get the paths from a config (sub) object such as "components" or
 * "dependencies"
 *
 * Input example: {
 *   plugins: {
 *      ...
 *   },
 *   seeds: {
 *      ...
 *   }
 * }
 *
 * @param {Object} config
 * @return {String[]}
 */
var getPathsFromConfigGroup = function(config) {
    return R.mapObj(function(componentType) {  // ie, plugin/seed/etc OBJECT
        return R.values(componentType).map(function(component) {
            return component.src || component.path;
        });
    }, config);
};

var unique = function(array) {
    var duplicates = {};
    array.forEach(function(key) {
        duplicates[key] = 1;
    });
    return Object.keys(duplicates);
};

var getWebGMEConfigContent = function(startPath) {
    var arrays,
        config = getConfig(startPath),
        paths = {},
        categories = ['components', 'dependencies'],
        configGroupPaths = categories
            .map(function(type) {
                return getPathsFromConfigGroup(config[type]);
            }
        );

    // Merge the arrays for each componentType
    Object.keys(configGroupPaths[0]).forEach(function(type) {
        arrays = configGroupPaths.map(function(group) {
            // Remove duplicates
            return unique(group[type]);
        });

        paths[type] = arrays.reduce(R.concat)  // Merge all paths
            .map(R.replace.bind(R, /\\/g, '/'));  // Convert to use '/' for path separator
    });

    // Update visualizers to use the 'panel' entry (if applicable)
    if (config.components.visualizers) {
        // add the components
        let local = Object.keys(config.components.visualizers)
            .map(name => config.components.visualizers[name].panel);

        // and the dependencies
        paths.visualizers = Object.keys(config.dependencies.visualizers)
            .map(name => [
                'node_modules',
                config.dependencies.visualizers[name].project,
                config.dependencies.visualizers[name].panel].join('/'))
            .concat(local);

    }

    // Set the requirejsPaths to be an array of all dependency paths
    paths.requirejsPaths = getRequireJSPaths(config);
    return paths;
};

var getRequireJSPaths = function(config) {
    var componentTypes = Object.keys(config.dependencies),
        components,
        paths = [],
        names,
        i;

    for (i = componentTypes.length; i--;) {
        components = config.dependencies[componentTypes[i]];
        names = Object.keys(components);
        for (var j = names.length; j--;) {
            paths.push({
                name: names[j],
                path: components[names[j]].src || components[names[j]].path
            });
        }
    }

    // If it has any visualizers, add some boilerplate
    if (hasVisualizers(config)) {
        ['panels', 'widgets'].forEach(function(name) {
            paths.push({
                name: name,
                path: './src/visualizers/'+name
            });
        });

        // Add all dependent visualizers
        // These are in the format 'widgets/DepViz': './node_modules/<project>/<path>'
        var vizConfig = config.dependencies.visualizers,
            dependentVizs = Object.keys(vizConfig),
            depName,
            depPath,
            project;

        for (i = dependentVizs.length; i--;) {
            depName = vizConfig[dependentVizs[i]].src.split('/');
            depName.pop();
            depName = depName.join('/');

            project = vizConfig[dependentVizs[i]].project.toLowerCase();
            depPath = ['.', 'node_modules', project,
                vizConfig[dependentVizs[i]].panel].join('/');

            paths.push({
                name: depName,
                path: depPath
            });

            depPath = ['.', 'node_modules', project,
                vizConfig[dependentVizs[i]].widget].join('/');

            paths.push({
                name: depName.replace('panel', 'widget'),
                path: depPath
            });
        }
    }

    if (!paths.length) {
        paths = null;
    }

    return paths;
};

/**
 * Check that the config contains at least one visualizer (in either components
 * or dependencies)
 *
 * @param config
 * @return {Boolean}
 */
var hasVisualizers = function(config) {
    return ['components', 'dependencies']
        .reduce(function(prev, type) {
            return prev || (config[type].visualizers &&
                Object.keys(config[type].visualizers).length > 0);
        }, false);
};

var getConfigPath = function(project) {
    return path.join(getRootPath(), 'node_modules',
        project, PROJECT_CONFIG);
};

/**
 * Get the GME path for the given dependent project or the working project
 * if unspecified
 *
 * @param {String} project
 * @return {String} path
 */
var getGMEConfigPath = function(project) {
    var gmeConfigPath,
        projectPath = '';

    if (project) {
        projectPath = path.join('node_modules', project);
    }
    gmeConfigPath = path.join(getRootPath(), projectPath, 'config');

    return gmeConfigPath;
};

/**
 * Find the first path containing the given item.
 *
 * @param {String[]} pathType
 * @param {String} item
 * @return {String} path containing the item
 */
var getPathContaining = function(paths, item) {
    var validPaths = paths.filter(function(p) {
        return exists(p) && fs.readdirSync(p).indexOf(item) +
            fs.readdirSync(p).indexOf(item+'.js') !== -2;
    });
    return validPaths.length ? validPaths[0] : null;
};

/**
 * Get the name of the package installed with "npmPackage"
 *
 * @param {String} npmPackage
 * @return {String} name
 */
var getPackageName = function(npmPackage) {
    // FIXME: It currently assumes everything is a github url. Should support
    // hashes, packages, etc
    // Ideally, we could use an npm feature to do this
    if (npmPackage[0] === '.') {  // File path
        return npmPackage.split(path.sep).pop();
    }

    // Github url: project/repo
    return npmPackage.split('/').pop().replace(/#.*$/, '');
};

var loadPaths = function(requirejs) {
    requirejs.config({
        nodeRequire: require,
        baseUrl: __dirname,
        paths: {
            text: __dirname+'/../node_modules/webgme-engine/src/common/lib/requirejs/text',
            coreplugins: __dirname+'/../node_modules/webgme-engine/src/plugin/coreplugins',
            plugin: __dirname + '/../node_modules/webgme-engine/src/plugin',
            common: __dirname + '/../node_modules/webgme-engine/src/common',

            'plugin/PluginGenerator/PluginGenerator': __dirname + '/../node_modules/webgme-engine/src/plugin/coreplugins/PluginGenerator/',
            'plugin/AddOnGenerator/AddOnGenerator': __dirname + '/../node_modules/webgme-engine/src/plugin/coreplugins/AddOnGenerator/',
            'plugin/DecoratorGenerator/DecoratorGenerator': __dirname + '/../node_modules/webgme-engine/src/plugin/coreplugins/DecoratorGenerator/',
            'plugin/LayoutGenerator/LayoutGenerator': __dirname + '/../node_modules/webgme-engine/src/plugin/coreplugins/LayoutGenerator/',
            'plugin/VisualizerGenerator/VisualizerGenerator': __dirname + '/../node_modules/webgme-engine/src/plugin/coreplugins/VisualizerGenerator/'
        }
    });
};

var normalizePath = function(dirs) {
    if (path.sep === '\\') {
        return dirs.replace(/\\/g, '/');
    }
    return dirs;
};

const installProject = function(projectName, isDev, callback) {
    let projectRoot = getRootPath();
    let cmd = isDev ?
        `npm install ${projectName} --save-dev`:
        `npm install ${projectName} --save`;
    let job = spawn(cmd, {cwd: projectRoot});

    logger.info(cmd);
    logger.writeStream(job.stdout);
    logger.errorStream(job.stderr);

    job.on('close', code => {
        logger.info(`npm exited with: ${code}`);
        if (code === 0) {  // Success!
            return callback(null);
        } else {
            let err = `Could not find project (${projectName})!`;
            logger.error(err);
            return callback(err);
        }
    });
};

module.exports = {
    PROJECT_CONFIG: PROJECT_CONFIG,
    saveConfig: saveConfig,
    getConfig: getConfig,
    getRootPath: getRootPath,
    getGMEConfigPath: getGMEConfigPath,
    getPathContaining: getPathContaining,
    getConfigPath: getConfigPath,
    updateWebGMEConfig: updateWebGMEConfig,
    saveFilesFromBlobClient: saveFilesFromBlobClient,
    saveFile: saveFile,
    loadPaths: loadPaths,
    getPackageName: getPackageName,
    normalize: normalizePath,
    installProject: installProject,
    mkdir: createDir
};
<commit_msg>Use require to resolve webgme-engine path. Fixes #231 (#232)<commit_after>/*globals define*/
'use strict';

const childProcess = require('child_process');
const spawn = childProcess.exec;
const Logger = require('./Logger');
const logger = new Logger();
const PROJECT_CONFIG = 'webgme-setup.json';

var _ = require('lodash'),
    fs = require('fs'),
    path = require('path'),
    exists = require('exists-file'),
    assert = require('assert'),
    R = require('ramda');

var getRootPath = function(startPath) {
    // Walk back from current path until you find a webgme-setup.json file
    var abspath = path.resolve(startPath || '.'),
        previousPath;

    while (abspath !== previousPath) {
        if (isProjectRoot(abspath)) {
            return abspath;
        }
        previousPath = abspath;
        abspath = path.dirname(abspath);
    }
    return null;
};

var isProjectRoot = function(abspath) {
    // Check for webgme-setup.json file
    if (!exists(abspath)) {
        return null;
    }

    var files = fs.readdirSync(abspath);
    return files.filter(function(file) {
        return file === PROJECT_CONFIG;
    }).length > 0;
};

/**
 * Save file and create directories as needed.
 *
 * @param {File} file
 * @return {undefined}
 */
var saveFile = function(file) {
    var dir = path.dirname(file.name);

    createDir(dir);
    fs.writeFileSync(file.name, file.content);
};

/**
 * Create directory as needed.
 *
 * @param {String} dir
 * @return {undefined}
 */
var createDir = function(dir) {
    var dirs = path.resolve(dir).split(path.sep),
        shortDir,
        i = 1;

    while (i++ < dirs.length) {
        shortDir = dirs.slice(0,i).join(path.sep);
        if (!exists(shortDir)) {
            fs.mkdirSync(shortDir);
        }
    }
};

var saveFilesFromBlobClient = function(blobClient) {
    var artifactNames = Object.keys(blobClient.artifacts);
    for (var i = artifactNames.length; i--;) {
        blobClient.artifacts[artifactNames[i]].files.forEach(saveFile);
    }
};

/* * * * * * * Config Settings * * * * * * * */
var getConfig = function(startPath) {
    var root = getRootPath(startPath),
        config;

    if (!root) {
        return null;
    }

    config = fs.readFileSync(path.join(root, PROJECT_CONFIG));
    return JSON.parse(config);
};

var saveConfig = function(config) {
    var root = getRootPath();
    var configText = JSON.stringify(config, null, 2);
    fs.writeFileSync(path.join(root, PROJECT_CONFIG), configText);
};

/**
 * Update the WebGME config based on the paths in the webgme-setup.json.
 *
 * @return {undefined}
 */
var updateWebGMEConfig = function(startPath) {
    var root = getRootPath(startPath),
        content = getWebGMEConfigContent(startPath),
        templatePath = path.join(__dirname, 'res', 'config.template.js.ejs'),
        template = _.template(fs.readFileSync(templatePath)),
        configPath = path.join(root, 'config', 'config.webgme.js');

    // Add webgme app name to the content
    var appName = require(path.join(root, 'package.json')).name;
    content.appName = appName.replace(/-/g, '_');

    // Add default layout info
    var config = getConfig(startPath);
    if (config) {
        Object.keys(config).forEach(type => {
            Object.keys(config[type].layouts || {}).forEach(layout => {
                if (config[type].layouts[layout].enabled) {
                    content.defaultLayout = layout;
                }
            });
        });
    }

    // Add router info
    content.routers = [];
    Object.keys(config).forEach(type => {
        Object.keys(config[type].routers || {}).forEach(name => {
            let router = config[type].routers[name];
            router.name = name;
            router.srcFile = `${router.src}/${name}.js`;
            content.routers.push(router);
        });
    });

    // Create the WebGME config file
    fs.writeFileSync(configPath, template(content));
};

/**
 * Get the paths from a config (sub) object such as "components" or
 * "dependencies"
 *
 * Input example: {
 *   plugins: {
 *      ...
 *   },
 *   seeds: {
 *      ...
 *   }
 * }
 *
 * @param {Object} config
 * @return {String[]}
 */
var getPathsFromConfigGroup = function(config) {
    return R.mapObj(function(componentType) {  // ie, plugin/seed/etc OBJECT
        return R.values(componentType).map(function(component) {
            return component.src || component.path;
        });
    }, config);
};

var unique = function(array) {
    var duplicates = {};
    array.forEach(function(key) {
        duplicates[key] = 1;
    });
    return Object.keys(duplicates);
};

var getWebGMEConfigContent = function(startPath) {
    var arrays,
        config = getConfig(startPath),
        paths = {},
        categories = ['components', 'dependencies'],
        configGroupPaths = categories
            .map(function(type) {
                return getPathsFromConfigGroup(config[type]);
            }
        );

    // Merge the arrays for each componentType
    Object.keys(configGroupPaths[0]).forEach(function(type) {
        arrays = configGroupPaths.map(function(group) {
            // Remove duplicates
            return unique(group[type]);
        });

        paths[type] = arrays.reduce(R.concat)  // Merge all paths
            .map(R.replace.bind(R, /\\/g, '/'));  // Convert to use '/' for path separator
    });

    // Update visualizers to use the 'panel' entry (if applicable)
    if (config.components.visualizers) {
        // add the components
        let local = Object.keys(config.components.visualizers)
            .map(name => config.components.visualizers[name].panel);

        // and the dependencies
        paths.visualizers = Object.keys(config.dependencies.visualizers)
            .map(name => [
                'node_modules',
                config.dependencies.visualizers[name].project,
                config.dependencies.visualizers[name].panel].join('/'))
            .concat(local);

    }

    // Set the requirejsPaths to be an array of all dependency paths
    paths.requirejsPaths = getRequireJSPaths(config);
    return paths;
};

var getRequireJSPaths = function(config) {
    var componentTypes = Object.keys(config.dependencies),
        components,
        paths = [],
        names,
        i;

    for (i = componentTypes.length; i--;) {
        components = config.dependencies[componentTypes[i]];
        names = Object.keys(components);
        for (var j = names.length; j--;) {
            paths.push({
                name: names[j],
                path: components[names[j]].src || components[names[j]].path
            });
        }
    }

    // If it has any visualizers, add some boilerplate
    if (hasVisualizers(config)) {
        ['panels', 'widgets'].forEach(function(name) {
            paths.push({
                name: name,
                path: './src/visualizers/'+name
            });
        });

        // Add all dependent visualizers
        // These are in the format 'widgets/DepViz': './node_modules/<project>/<path>'
        var vizConfig = config.dependencies.visualizers,
            dependentVizs = Object.keys(vizConfig),
            depName,
            depPath,
            project;

        for (i = dependentVizs.length; i--;) {
            depName = vizConfig[dependentVizs[i]].src.split('/');
            depName.pop();
            depName = depName.join('/');

            project = vizConfig[dependentVizs[i]].project.toLowerCase();
            depPath = ['.', 'node_modules', project,
                vizConfig[dependentVizs[i]].panel].join('/');

            paths.push({
                name: depName,
                path: depPath
            });

            depPath = ['.', 'node_modules', project,
                vizConfig[dependentVizs[i]].widget].join('/');

            paths.push({
                name: depName.replace('panel', 'widget'),
                path: depPath
            });
        }
    }

    if (!paths.length) {
        paths = null;
    }

    return paths;
};

/**
 * Check that the config contains at least one visualizer (in either components
 * or dependencies)
 *
 * @param config
 * @return {Boolean}
 */
var hasVisualizers = function(config) {
    return ['components', 'dependencies']
        .reduce(function(prev, type) {
            return prev || (config[type].visualizers &&
                Object.keys(config[type].visualizers).length > 0);
        }, false);
};

var getConfigPath = function(project) {
    return path.join(getRootPath(), 'node_modules',
        project, PROJECT_CONFIG);
};

/**
 * Get the GME path for the given dependent project or the working project
 * if unspecified
 *
 * @param {String} project
 * @return {String} path
 */
var getGMEConfigPath = function(project) {
    var gmeConfigPath,
        projectPath = '';

    if (project) {
        projectPath = path.join('node_modules', project);
    }
    gmeConfigPath = path.join(getRootPath(), projectPath, 'config');

    return gmeConfigPath;
};

/**
 * Find the first path containing the given item.
 *
 * @param {String[]} pathType
 * @param {String} item
 * @return {String} path containing the item
 */
var getPathContaining = function(paths, item) {
    var validPaths = paths.filter(function(p) {
        return exists(p) && fs.readdirSync(p).indexOf(item) +
            fs.readdirSync(p).indexOf(item+'.js') !== -2;
    });
    return validPaths.length ? validPaths[0] : null;
};

/**
 * Get the name of the package installed with "npmPackage"
 *
 * @param {String} npmPackage
 * @return {String} name
 */
var getPackageName = function(npmPackage) {
    // FIXME: It currently assumes everything is a github url. Should support
    // hashes, packages, etc
    // Ideally, we could use an npm feature to do this
    if (npmPackage[0] === '.') {  // File path
        return npmPackage.split(path.sep).pop();
    }

    // Github url: project/repo
    return npmPackage.split('/').pop().replace(/#.*$/, '');
};

var loadPaths = function(requirejs) {
    const webgmeEngineRoot = path.dirname(require.resolve('webgme-engine'));
    requirejs.config({
        nodeRequire: require,
        baseUrl: __dirname,
        paths: {
            text: `${webgmeEngineRoot}/src/common/lib/requirejs/text`,
            coreplugins: `${webgmeEngineRoot}/src/plugin/coreplugins`,
            plugin: `${webgmeEngineRoot}/src/plugin`,
            common: `${webgmeEngineRoot}/src/common`,

            'plugin/PluginGenerator/PluginGenerator': `${webgmeEngineRoot}/src/plugin/coreplugins/PluginGenerator/`,
            'plugin/AddOnGenerator/AddOnGenerator': `${webgmeEngineRoot}/src/plugin/coreplugins/AddOnGenerator/`,
            'plugin/DecoratorGenerator/DecoratorGenerator': `${webgmeEngineRoot}/src/plugin/coreplugins/DecoratorGenerator/`,
            'plugin/LayoutGenerator/LayoutGenerator': `${webgmeEngineRoot}/src/plugin/coreplugins/LayoutGenerator/`,
            'plugin/VisualizerGenerator/VisualizerGenerator': `${webgmeEngineRoot}/src/plugin/coreplugins/VisualizerGenerator/`
        }
    });
};

var normalizePath = function(dirs) {
    if (path.sep === '\\') {
        return dirs.replace(/\\/g, '/');
    }
    return dirs;
};

const installProject = function(projectName, isDev, callback) {
    let projectRoot = getRootPath();
    let cmd = isDev ?
        `npm install ${projectName} --save-dev`:
        `npm install ${projectName} --save`;
    let job = spawn(cmd, {cwd: projectRoot});

    logger.info(cmd);
    logger.writeStream(job.stdout);
    logger.errorStream(job.stderr);

    job.on('close', code => {
        logger.info(`npm exited with: ${code}`);
        if (code === 0) {  // Success!
            return callback(null);
        } else {
            let err = `Could not find project (${projectName})!`;
            logger.error(err);
            return callback(err);
        }
    });
};

module.exports = {
    PROJECT_CONFIG: PROJECT_CONFIG,
    saveConfig: saveConfig,
    getConfig: getConfig,
    getRootPath: getRootPath,
    getGMEConfigPath: getGMEConfigPath,
    getPathContaining: getPathContaining,
    getConfigPath: getConfigPath,
    updateWebGMEConfig: updateWebGMEConfig,
    saveFilesFromBlobClient: saveFilesFromBlobClient,
    saveFile: saveFile,
    loadPaths: loadPaths,
    getPackageName: getPackageName,
    normalize: normalizePath,
    installProject: installProject,
    mkdir: createDir
};
<|endoftext|>


<commit_before>/*
 * moleculer
 * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer)
 * MIT Licensed
 */

"use strict";

const Promise 	= require("bluebird");
const chalk		= require("chalk");
const os 	 	= require("os");

const lut = [];
for (let i=0; i<256; i++) { lut[i] = (i<16?"0":"")+(i).toString(16); }

const RegexCache = new Map();

const deprecateList = [];

function circularReplacer() {
	const seen = new WeakSet();
	return function(key, value) {
		if (typeof value === "object" && value !== null) {
			if (seen.has(value)) {
				//delete this[key];
				return;
			}
			seen.add(value);
		}
		return value;
	};
}

const utils = {

	// Fast UUID generator: e7 https://jsperf.com/uuid-generator-opt/18
	generateToken() {
		const d0 = Math.random()*0xffffffff|0;
		const d1 = Math.random()*0xffffffff|0;
		const d2 = Math.random()*0xffffffff|0;
		const d3 = Math.random()*0xffffffff|0;
		return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+"-"+
			lut[d1&0xff]+lut[d1>>8&0xff]+"-"+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+"-"+
			lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+"-"+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
			lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
	},

	/**
	 * Get default NodeID (computerName)
	 *
	 * @returns
	 */
	getNodeID() {
		return os.hostname().toLowerCase() + "-" + process.pid;
	},

	/**
	 * Get list of local IPs
	 *
	 * @returns
	 */
	getIpList() {
		const list = [];
		const interfaces = os.networkInterfaces();
		for (let iface in interfaces) {
			for (let i in interfaces[iface]) {
				const f = interfaces[iface][i];
				if (f.family === "IPv4" && !f.internal) {
					list.push(f.address);
					break;
				}
			}
		}
		return list;
	},

	/**
	 * Delay for Promises
	 *
	 * @param {any} ms
	 * @returns
	 */
	delay(ms) {
		/* istanbul ignore next */
		return () => new Promise((resolve) => setTimeout(resolve, ms));
	},

	/**
	 * Check the param is a Promise instance
	 *
	 * @param {any} p
	 * @returns
	 */
	isPromise(p) {
		return (p != null && typeof p.then === "function");
	},

	/**
	 * Clear `require` cache. Used for service hot reloading
	 *
	 * @param {String} filename
	 */
	clearRequireCache(filename) {
		/* istanbul ignore next */
		Object.keys(require.cache).forEach(function(key) {
			if (key == filename) {
				delete require.cache[key];
			}
		});
	},

	/**
	 * String matcher to handle dot-separated event/action names.
	 *
	 * @param {String} text
	 * @param {String} pattern
	 * @returns {Boolean}
	 */
	match(text, pattern) {
		// Simple patterns
		if (pattern.indexOf("?") == -1) {

			// Exact match (eg. "prefix.event")
			const firstStarPosition = pattern.indexOf("*");
			if (firstStarPosition == -1) {
				return pattern === text;
			}

			// Eg. "prefix**"
			const len = pattern.length;
			if (len > 2 && pattern.endsWith("**") && firstStarPosition > len - 3) {
				pattern = pattern.substring(0, len - 2);
				return text.startsWith(pattern);
			}

			// Eg. "prefix*"
			if (len > 1 && pattern.endsWith("*") && firstStarPosition > len - 2) {
				pattern = pattern.substring(0, len - 1);
				if (text.startsWith(pattern)) {
					return text.indexOf(".", len) == -1;
				}
				return false;
			}

			// Accept simple text, without point character (*)
			if (len == 1 && firstStarPosition == 0) {
				return text.indexOf(".") == -1;
			}

			// Accept all inputs (**)
			if (len == 2 && firstStarPosition == 0 && pattern.lastIndexOf("*") == 1) {
				return true;
			}
		}

		// Regex (eg. "prefix.ab?cd.*.foo")
		let regex = RegexCache.get(pattern);
		if (regex == null) {
			if (pattern.startsWith("$")) {
				pattern = "\\" + pattern;
			}
			pattern = pattern.replace(/\?/g, ".");
			pattern = pattern.replace(/\*\*/g, "§§§");
			pattern = pattern.replace(/\*/g, "[^\\.]*");
			pattern = pattern.replace(/§§§/g, ".*");

			pattern = "^" + pattern + "$";

			// eslint-disable-next-line security/detect-non-literal-regexp
			regex = new RegExp(pattern, "g");
			RegexCache.set(pattern, regex);
		}
		return regex.test(text);
	},

	/**
	 * Deprecate a method or property
	 *
	 * @param {Object|Function|String} prop
	 * @param {String} msg
	 */
	deprecate(prop, msg) {
		if (arguments.length == 1)
			msg = prop;

		if (deprecateList.indexOf(prop) === -1) {
			// eslint-disable-next-line no-console
			console.warn(chalk.yellow.bold(`DeprecationWarning: ${msg}`));
			deprecateList.push(prop);
		}
	},

	/**
	 * Remove circular references & Functions from the JS object
	 *
	 * @param {Object|Array} obj
	 * @returns {Object|Array}
	 */
	safetyObject(obj) {
		return JSON.parse(JSON.stringify(obj, circularReplacer()));
	},

	/**
	 * Sets a variable on an object based on its dot path.
	 *
	 * @param {Object} obj
	 * @param {String} path
	 * @param {*} value
	 * @returns {Object}
	 */
	dotSet(obj, path, value) {
		const parts = path.split(".");
		const part = parts.shift();
		if (parts.length > 0) {
			if (!(part in obj)) {
				obj[part] = {};
			} else {
				if (typeof obj[part] !== "object") {
					throw new Error("Value already set and it's not an object");
				}
			}
			obj[part] = utils.dotSet(obj[part], parts.join("."), value);
			return obj;
		}
		obj[path] = value;
		return obj;
	}

};

module.exports = utils;
<commit_msg>Fix Regex caching issue #560<commit_after>/*
 * moleculer
 * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer)
 * MIT Licensed
 */

"use strict";

const Promise 	= require("bluebird");
const chalk		= require("chalk");
const os 	 	= require("os");

const lut = [];
for (let i=0; i<256; i++) { lut[i] = (i<16?"0":"")+(i).toString(16); }

const RegexCache = new Map();

const deprecateList = [];

function circularReplacer() {
	const seen = new WeakSet();
	return function(key, value) {
		if (typeof value === "object" && value !== null) {
			if (seen.has(value)) {
				//delete this[key];
				return;
			}
			seen.add(value);
		}
		return value;
	};
}

const utils = {

	// Fast UUID generator: e7 https://jsperf.com/uuid-generator-opt/18
	generateToken() {
		const d0 = Math.random()*0xffffffff|0;
		const d1 = Math.random()*0xffffffff|0;
		const d2 = Math.random()*0xffffffff|0;
		const d3 = Math.random()*0xffffffff|0;
		return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+"-"+
			lut[d1&0xff]+lut[d1>>8&0xff]+"-"+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+"-"+
			lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+"-"+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
			lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
	},

	/**
	 * Get default NodeID (computerName)
	 *
	 * @returns
	 */
	getNodeID() {
		return os.hostname().toLowerCase() + "-" + process.pid;
	},

	/**
	 * Get list of local IPs
	 *
	 * @returns
	 */
	getIpList() {
		const list = [];
		const interfaces = os.networkInterfaces();
		for (let iface in interfaces) {
			for (let i in interfaces[iface]) {
				const f = interfaces[iface][i];
				if (f.family === "IPv4" && !f.internal) {
					list.push(f.address);
					break;
				}
			}
		}
		return list;
	},

	/**
	 * Delay for Promises
	 *
	 * @param {any} ms
	 * @returns
	 */
	delay(ms) {
		/* istanbul ignore next */
		return () => new Promise((resolve) => setTimeout(resolve, ms));
	},

	/**
	 * Check the param is a Promise instance
	 *
	 * @param {any} p
	 * @returns
	 */
	isPromise(p) {
		return (p != null && typeof p.then === "function");
	},

	/**
	 * Clear `require` cache. Used for service hot reloading
	 *
	 * @param {String} filename
	 */
	clearRequireCache(filename) {
		/* istanbul ignore next */
		Object.keys(require.cache).forEach(function(key) {
			if (key == filename) {
				delete require.cache[key];
			}
		});
	},

	/**
	 * String matcher to handle dot-separated event/action names.
	 *
	 * @param {String} text
	 * @param {String} pattern
	 * @returns {Boolean}
	 */
	match(text, pattern) {
		// Simple patterns
		if (pattern.indexOf("?") == -1) {

			// Exact match (eg. "prefix.event")
			const firstStarPosition = pattern.indexOf("*");
			if (firstStarPosition == -1) {
				return pattern === text;
			}

			// Eg. "prefix**"
			const len = pattern.length;
			if (len > 2 && pattern.endsWith("**") && firstStarPosition > len - 3) {
				pattern = pattern.substring(0, len - 2);
				return text.startsWith(pattern);
			}

			// Eg. "prefix*"
			if (len > 1 && pattern.endsWith("*") && firstStarPosition > len - 2) {
				pattern = pattern.substring(0, len - 1);
				if (text.startsWith(pattern)) {
					return text.indexOf(".", len) == -1;
				}
				return false;
			}

			// Accept simple text, without point character (*)
			if (len == 1 && firstStarPosition == 0) {
				return text.indexOf(".") == -1;
			}

			// Accept all inputs (**)
			if (len == 2 && firstStarPosition == 0 && pattern.lastIndexOf("*") == 1) {
				return true;
			}
		}

		// Regex (eg. "prefix.ab?cd.*.foo")
		const origPattern = pattern;
		let regex = RegexCache.get(origPattern);
		if (regex == null) {
			if (pattern.startsWith("$")) {
				pattern = "\\" + pattern;
			}
			pattern = pattern.replace(/\?/g, ".");
			pattern = pattern.replace(/\*\*/g, "§§§");
			pattern = pattern.replace(/\*/g, "[^\\.]*");
			pattern = pattern.replace(/§§§/g, ".*");

			pattern = "^" + pattern + "$";

			// eslint-disable-next-line security/detect-non-literal-regexp
			regex = new RegExp(pattern, "");
			RegexCache.set(origPattern, regex);
		}
		return regex.test(text);
	},

	/**
	 * Deprecate a method or property
	 *
	 * @param {Object|Function|String} prop
	 * @param {String} msg
	 */
	deprecate(prop, msg) {
		if (arguments.length == 1)
			msg = prop;

		if (deprecateList.indexOf(prop) === -1) {
			// eslint-disable-next-line no-console
			console.warn(chalk.yellow.bold(`DeprecationWarning: ${msg}`));
			deprecateList.push(prop);
		}
	},

	/**
	 * Remove circular references & Functions from the JS object
	 *
	 * @param {Object|Array} obj
	 * @returns {Object|Array}
	 */
	safetyObject(obj) {
		return JSON.parse(JSON.stringify(obj, circularReplacer()));
	},

	/**
	 * Sets a variable on an object based on its dot path.
	 *
	 * @param {Object} obj
	 * @param {String} path
	 * @param {*} value
	 * @returns {Object}
	 */
	dotSet(obj, path, value) {
		const parts = path.split(".");
		const part = parts.shift();
		if (parts.length > 0) {
			if (!(part in obj)) {
				obj[part] = {};
			} else {
				if (typeof obj[part] !== "object") {
					throw new Error("Value already set and it's not an object");
				}
			}
			obj[part] = utils.dotSet(obj[part], parts.join("."), value);
			return obj;
		}
		obj[path] = value;
		return obj;
	}

};

module.exports = utils;
<|endoftext|>


<commit_before>const JSON = require('json3')
const glob = require('glob')
const path = require('path')
const { join, basename } = path
const { readdirSync, existsSync, statSync, lstatSync } = require('fs')
const toISOString = require('@segment/to-iso-string')

const exists = existsSync

/**
 * Ignored directories.
 */
var ignore = ['node_modules', '.git']

exports.inherits = require('util').inherits

/**
 * Ignored files.
 *
 * @api private
 * @param {string} path
 * @return {boolean}
 */
function ignored (path) {
  return !~ignore.indexOf(path)
}

/**
 * Lookup files in the given `dir`.
 *
 * @api private
 * @param {string} dir
 * @param {string[]} [ext=['.js']]
 * @param {Array} [ret=[]]
 * @return {Array}
 */
exports.files = function (dir, ext, ret) {
  ret = ret || []
  ext = ext || ['js']

  var re = new RegExp('\\.(' + ext.join('|') + ')$')

  readdirSync(dir)
    .filter(ignored)
    .forEach(function (path) {
      path = join(dir, path)
      if (lstatSync(path).isDirectory()) {
        exports.files(path, ext, ret)
      } else if (path.match(re)) {
        ret.push(path)
      }
    })

  return ret
}

/**
 * Strip the function definition from `str`, and re-indent for pre whitespace.
 *
 * @param {string} str
 * @return {string}
 */
exports.clean = function (str) {
  str = str
    .replace(/\r\n?|[\n\u2028\u2029]/g, '\n').replace(/^\uFEFF/, '')
    // (traditional)->  space/name     parameters    body     (lambda)-> parameters       body   multi-statement/single          keep body content
    .replace(/^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/, '$1$2$3')

  var spaces = str.match(/^\n?( *)/)[1].length
  var tabs = str.match(/^\n?(\t*)/)[1].length
  var re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs || spaces) + '}', 'gm')

  str = str.replace(re, '')

  return str.trim()
}

/**
 * If a value could have properties, and has none, this function is called,
 * which returns a string representation of the empty value.
 *
 * Functions w/ no properties return `'[Function]'`
 * Arrays w/ length === 0 return `'[]'`
 * Objects w/ no properties return `'{}'`
 * All else: return result of `value.toString()`
 *
 * @api private
 * @param {*} value The value to inspect.
 * @param {string} typeHint The type of the value
 * @returns {string}
 */
function emptyRepresentation (value, typeHint) {
  switch (typeHint) {
    case 'function':
      return '[Function]'
    case 'object':
      return '{}'
    case 'array':
      return '[]'
    default:
      return value.toString()
  }
}

/**
 * Takes some variable and asks `Object.prototype.toString()` what it thinks it
 * is.
 *
 * @api private
 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
 * @param {*} value The value to test.
 * @returns {string} Computed type
 * @example
 * type({}) // 'object'
 * type([]) // 'array'
 * type(1) // 'number'
 * type(false) // 'boolean'
 * type(Infinity) // 'number'
 * type(null) // 'null'
 * type(new Date()) // 'date'
 * type(/foo/) // 'regexp'
 * type('type') // 'string'
 * type(global) // 'global'
 * type(new String('foo') // 'object'
 */
var type = exports.type = function type (value) {
  if (value === undefined) {
    return 'undefined'
  } else if (value === null) {
    return 'null'
  } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
    return 'buffer'
  }
  return Object.prototype.toString.call(value)
    .replace(/^\[.+\s(.+?)]$/, '$1')
    .toLowerCase()
}

/**
 * Stringify `value`. Different behavior depending on type of value:
 *
 * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
 * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
 * - If `value` is an *empty* object, function, or array, return result of function
 *   {@link emptyRepresentation}.
 * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
 *   JSON.stringify().
 *
 * @api private
 * @see exports.type
 * @param {*} value
 * @return {string}
 */
exports.stringify = function (value) {
  var typeHint = type(value)

  if (!~['object', 'array', 'function'].indexOf(typeHint)) {
    if (typeHint === 'buffer') {
      var json = value.toJSON()
      // Based on the toJSON result
      return jsonStringify(json.data && json.type ? json.data : json, 2)
        .replace(/,(\n|$)/g, '$1')
    }

    // IE7/IE8 has a bizarre String constructor; needs to be coerced
    // into an array and back to obj.
    if (typeHint === 'string' && typeof value === 'object') {
      value = value.split('').reduce(function (acc, char, idx) {
        acc[idx] = char
        return acc
      }, {})
      typeHint = 'object'
    } else {
      return jsonStringify(value)
    }
  }

  for (var prop in value) {
    if (Object.prototype.hasOwnProperty.call(value, prop)) {
      return jsonStringify(exports.canonicalize(value, null, typeHint), 2).replace(/,(\n|$)/g, '$1')
    }
  }

  return emptyRepresentation(value, typeHint)
}

/**
 * like JSON.stringify but more sense.
 *
 * @api private
 * @param {Object}  object
 * @param {number=} spaces
 * @param {number=} depth
 * @returns {*}
 */
function jsonStringify (object, spaces, depth) {
  if (typeof spaces === 'undefined') {
    // primitive types
    return _stringify(object)
  }

  depth = depth || 1
  var space = spaces * depth
  var str = Array.isArray(object) ? '[' : '{'
  var end = Array.isArray(object) ? ']' : '}'
  var length = typeof object.length === 'number' ? object.length : exports.keys(object).length
  // `.repeat()` polyfill
  function repeat (s, n) {
    return new Array(n).join(s)
  }

  function _stringify (val) {
    switch (type(val)) {
      case 'null':
      case 'undefined':
        val = '[' + val + ']'
        break
      case 'array':
      case 'object':
        val = jsonStringify(val, spaces, depth + 1)
        break
      case 'boolean':
      case 'regexp':
      case 'symbol':
      case 'number':
        val = val === 0 && (1 / val) === -Infinity // `-0`
          ? '-0'
          : val.toString()
        break
      case 'date':
        var sDate
        if (isNaN(val.getTime())) { // Invalid date
          sDate = val.toString()
        } else {
          sDate = val.toISOString ? val.toISOString() : toISOString(val)
        }
        val = '[Date: ' + sDate + ']'
        break
      case 'buffer':
        var json = val.toJSON()
        // Based on the toJSON result
        json = json.data && json.type ? json.data : json
        val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']'
        break
      default:
        val = (val === '[Function]' || val === '[Circular]')
          ? val
          : JSON.stringify(val) // string
    }
    return val
  }

  for (var i in object) {
    if (!Object.prototype.hasOwnProperty.call(object, i)) {
      continue // not my business
    }
    --length
    str += '\n ' + repeat(' ', space) +
      (Array.isArray(object) ? '' : '"' + i + '": ') + // key
      _stringify(object[i]) +                    // value
      (length ? ',' : '')                       // comma
  }

  return str +
    // [], {}
    (str.length !== 1 ? '\n' + repeat(' ', --space) + end : end)
}

/**
 * Return a new Thing that has the keys in sorted order. Recursive.
 *
 * If the Thing...
 * - has already been seen, return string `'[Circular]'`
 * - is `undefined`, return string `'[undefined]'`
 * - is `null`, return value `null`
 * - is some other primitive, return the value
 * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
 * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
 * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
 *
 * @api private
 * @see {@link exports.stringify}
 * @param {*} value Thing to inspect.  May or may not have properties.
 * @param {Array} [stack=[]] Stack of seen values
 * @param {string} [typeHint] Type hint
 * @return {(Object|Array|Function|string|undefined)}
 */
exports.canonicalize = function canonicalize (value, stack, typeHint) {
  var canonicalizedObj
  /* eslint-disable no-unused-vars */
  var prop
  /* eslint-enable no-unused-vars */
  typeHint = typeHint || type(value)
  function withStack (value, fn) {
    stack.push(value)
    fn()
    stack.pop()
  }

  stack = stack || []

  if (stack.indexOf(value) !== -1) {
    return '[Circular]'
  }

  switch (typeHint) {
    case 'undefined':
    case 'buffer':
    case 'null':
      canonicalizedObj = value
      break
    case 'array':
      withStack(value, function () {
        canonicalizedObj = exports.map(value, function (item) {
          return exports.canonicalize(item, stack)
        })
      })
      break
    case 'function':
      /* eslint-disable guard-for-in */
      for (prop in value) {
        canonicalizedObj = {}
        break
      }
      /* eslint-enable guard-for-in */
      if (!canonicalizedObj) {
        canonicalizedObj = emptyRepresentation(value, typeHint)
        break
      }
    /* falls through */
    case 'object':
      canonicalizedObj = canonicalizedObj || {}
      withStack(value, function () {
        exports.forEach(exports.keys(value).sort(), function (key) {
          canonicalizedObj[key] = exports.canonicalize(value[key], stack)
        })
      })
      break
    case 'date':
    case 'number':
    case 'regexp':
    case 'boolean':
    case 'symbol':
      canonicalizedObj = value
      break
    default:
      canonicalizedObj = value + ''
  }

  return canonicalizedObj
}

/**
 * Lookup file names at the given `path`.
 *
 * @api public
 * @param {string} path Base path to start searching from.
 * @param {string[]} extensions File extensions to look for.
 * @param {boolean} recursive Whether or not to recurse into subdirectories.
 * @return {string[]} An array of paths.
 */
exports.lookupFiles = function lookupFiles (path, extensions, recursive) {
  var files = []
  var re = new RegExp('\\.(' + extensions.join('|') + ')$')

  if (!exists(path)) {
    if (exists(path + '.js')) {
      path += '.js'
    } else {
      files = glob.sync(path)
      if (!files.length) {
        throw new Error("cannot resolve path (or pattern) '" + path + "'")
      }
      return files
    }
  }

  try {
    var stat = statSync(path)
    if (stat.isFile()) {
      return path
    }
  } catch (err) {
    // ignore error
    return
  }

  readdirSync(path).forEach(function (file) {
    file = join(path, file)
    try {
      var stat = statSync(file)
      if (stat.isDirectory()) {
        if (recursive) {
          files = files.concat(lookupFiles(file, extensions, recursive))
        }
        return
      }
    } catch (err) {
      // ignore error
      return
    }
    if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') {
      return
    }
    files.push(file)
  })

  return files
}

/**
 * @summary
 * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
 * @description
 * When invoking this function you get a filter function that get the Error.stack as an input,
 * and return a prettify output.
 * (i.e: strip Mocha and internal node functions from stack trace).
 * @returns {Function}
 */
exports.stackTraceFilter = function () {
  // TODO: Replace with `process.browser`
  var is = typeof document === 'undefined' ? { node: true } : { browser: true }
  var slash = path.sep
  var cwd
  if (is.node) {
    cwd = process.cwd() + slash
  } else {
    cwd = (typeof location === 'undefined'
      ? window.location
      : window.location).href.replace(/\/[^/]*$/, '/')
    slash = '/'
  }

  function isMochaInternal (line) {
    return (~line.indexOf('node_modules' + slash + 'mocha' + slash)) ||
      (~line.indexOf('node_modules' + slash + 'mocha.js')) ||
      (~line.indexOf('bower_components' + slash + 'mocha.js')) ||
      (~line.indexOf(slash + 'mocha.js'))
  }

  function isNodeInternal (line) {
    return (~line.indexOf('(timers.js:')) ||
      (~line.indexOf('(events.js:')) ||
      (~line.indexOf('(node.js:')) ||
      (~line.indexOf('(module.js:')) ||
      (~line.indexOf('GeneratorFunctionPrototype.next (native)')) ||
      false
  }

  return function (stack) {
    stack = stack.split('\n')

    stack = stack.reduce(function (list, line) {
      if (isMochaInternal(line)) {
        return list
      }

      if (is.node && isNodeInternal(line)) {
        return list
      }

      // Clean up cwd(absolute)
      if (/\(?.+:\d+:\d+\)?$/.test(line)) {
        line = line.replace(cwd, '')
      }

      list.push(line)
      return list
    }, [])

    return stack.join('\n')
  }
}
<commit_msg>refactor: remove disused code<commit_after>const JSON = require('json3')
const glob = require('glob')
const path = require('path')
const { join, basename } = path
const { readdirSync, existsSync, statSync, lstatSync } = require('fs')
const toISOString = require('@segment/to-iso-string')

const exists = existsSync

/**
 * Ignored directories.
 */
var ignore = ['node_modules', '.git']

exports.inherits = require('util').inherits

/**
 * If a value could have properties, and has none, this function is called,
 * which returns a string representation of the empty value.
 *
 * Functions w/ no properties return `'[Function]'`
 * Arrays w/ length === 0 return `'[]'`
 * Objects w/ no properties return `'{}'`
 * All else: return result of `value.toString()`
 *
 * @api private
 * @param {*} value The value to inspect.
 * @param {string} typeHint The type of the value
 * @returns {string}
 */
function emptyRepresentation (value, typeHint) {
  switch (typeHint) {
    case 'function':
      return '[Function]'
    case 'object':
      return '{}'
    case 'array':
      return '[]'
    default:
      return value.toString()
  }
}

/**
 * Takes some variable and asks `Object.prototype.toString()` what it thinks it
 * is.
 *
 * @api private
 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
 * @param {*} value The value to test.
 * @returns {string} Computed type
 * @example
 * type({}) // 'object'
 * type([]) // 'array'
 * type(1) // 'number'
 * type(false) // 'boolean'
 * type(Infinity) // 'number'
 * type(null) // 'null'
 * type(new Date()) // 'date'
 * type(/foo/) // 'regexp'
 * type('type') // 'string'
 * type(global) // 'global'
 * type(new String('foo') // 'object'
 */
var type = exports.type = function type (value) {
  if (value === undefined) {
    return 'undefined'
  } else if (value === null) {
    return 'null'
  } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
    return 'buffer'
  }
  return Object.prototype.toString.call(value)
    .replace(/^\[.+\s(.+?)]$/, '$1')
    .toLowerCase()
}

/**
 * Stringify `value`. Different behavior depending on type of value:
 *
 * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
 * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
 * - If `value` is an *empty* object, function, or array, return result of function
 *   {@link emptyRepresentation}.
 * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
 *   JSON.stringify().
 *
 * @api private
 * @see exports.type
 * @param {*} value
 * @return {string}
 */
exports.stringify = function (value) {
  var typeHint = type(value)

  if (!~['object', 'array', 'function'].indexOf(typeHint)) {
    if (typeHint === 'buffer') {
      var json = value.toJSON()
      // Based on the toJSON result
      return jsonStringify(json.data && json.type ? json.data : json, 2)
        .replace(/,(\n|$)/g, '$1')
    }

    // IE7/IE8 has a bizarre String constructor; needs to be coerced
    // into an array and back to obj.
    if (typeHint === 'string' && typeof value === 'object') {
      value = value.split('').reduce(function (acc, char, idx) {
        acc[idx] = char
        return acc
      }, {})
      typeHint = 'object'
    } else {
      return jsonStringify(value)
    }
  }

  for (var prop in value) {
    if (Object.prototype.hasOwnProperty.call(value, prop)) {
      return jsonStringify(exports.canonicalize(value, null, typeHint), 2).replace(/,(\n|$)/g, '$1')
    }
  }

  return emptyRepresentation(value, typeHint)
}

/**
 * like JSON.stringify but more sense.
 *
 * @api private
 * @param {Object}  object
 * @param {number=} spaces
 * @param {number=} depth
 * @returns {*}
 */
function jsonStringify (object, spaces, depth) {
  if (typeof spaces === 'undefined') {
    // primitive types
    return _stringify(object)
  }

  depth = depth || 1
  var space = spaces * depth
  var str = Array.isArray(object) ? '[' : '{'
  var end = Array.isArray(object) ? ']' : '}'
  var length = typeof object.length === 'number' ? object.length : exports.keys(object).length
  // `.repeat()` polyfill
  function repeat (s, n) {
    return new Array(n).join(s)
  }

  function _stringify (val) {
    switch (type(val)) {
      case 'null':
      case 'undefined':
        val = '[' + val + ']'
        break
      case 'array':
      case 'object':
        val = jsonStringify(val, spaces, depth + 1)
        break
      case 'boolean':
      case 'regexp':
      case 'symbol':
      case 'number':
        val = val === 0 && (1 / val) === -Infinity // `-0`
          ? '-0'
          : val.toString()
        break
      case 'date':
        var sDate
        if (isNaN(val.getTime())) { // Invalid date
          sDate = val.toString()
        } else {
          sDate = val.toISOString ? val.toISOString() : toISOString(val)
        }
        val = '[Date: ' + sDate + ']'
        break
      case 'buffer':
        var json = val.toJSON()
        // Based on the toJSON result
        json = json.data && json.type ? json.data : json
        val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']'
        break
      default:
        val = (val === '[Function]' || val === '[Circular]')
          ? val
          : JSON.stringify(val) // string
    }
    return val
  }

  for (var i in object) {
    if (!Object.prototype.hasOwnProperty.call(object, i)) {
      continue // not my business
    }
    --length
    str += '\n ' + repeat(' ', space) +
      (Array.isArray(object) ? '' : '"' + i + '": ') + // key
      _stringify(object[i]) +                    // value
      (length ? ',' : '')                       // comma
  }

  return str +
    // [], {}
    (str.length !== 1 ? '\n' + repeat(' ', --space) + end : end)
}

/**
 * Return a new Thing that has the keys in sorted order. Recursive.
 *
 * If the Thing...
 * - has already been seen, return string `'[Circular]'`
 * - is `undefined`, return string `'[undefined]'`
 * - is `null`, return value `null`
 * - is some other primitive, return the value
 * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
 * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
 * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
 *
 * @api private
 * @see {@link exports.stringify}
 * @param {*} value Thing to inspect.  May or may not have properties.
 * @param {Array} [stack=[]] Stack of seen values
 * @param {string} [typeHint] Type hint
 * @return {(Object|Array|Function|string|undefined)}
 */
exports.canonicalize = function canonicalize (value, stack, typeHint) {
  var canonicalizedObj
  /* eslint-disable no-unused-vars */
  var prop
  /* eslint-enable no-unused-vars */
  typeHint = typeHint || type(value)
  function withStack (value, fn) {
    stack.push(value)
    fn()
    stack.pop()
  }

  stack = stack || []

  if (stack.indexOf(value) !== -1) {
    return '[Circular]'
  }

  switch (typeHint) {
    case 'undefined':
    case 'buffer':
    case 'null':
      canonicalizedObj = value
      break
    case 'array':
      withStack(value, function () {
        canonicalizedObj = exports.map(value, function (item) {
          return exports.canonicalize(item, stack)
        })
      })
      break
    case 'function':
      /* eslint-disable guard-for-in */
      for (prop in value) {
        canonicalizedObj = {}
        break
      }
      /* eslint-enable guard-for-in */
      if (!canonicalizedObj) {
        canonicalizedObj = emptyRepresentation(value, typeHint)
        break
      }
    /* falls through */
    case 'object':
      canonicalizedObj = canonicalizedObj || {}
      withStack(value, function () {
        exports.forEach(exports.keys(value).sort(), function (key) {
          canonicalizedObj[key] = exports.canonicalize(value[key], stack)
        })
      })
      break
    case 'date':
    case 'number':
    case 'regexp':
    case 'boolean':
    case 'symbol':
      canonicalizedObj = value
      break
    default:
      canonicalizedObj = value + ''
  }

  return canonicalizedObj
}
<|endoftext|>


<commit_before>import crc from 'easy-crc32'

export function getSanitizedPath(path) {
    return path
        .replace(/^\//, '')
        .replace(/\/$/, '')
}
export function getLocCrc(filepath, line) {
    return crc.calculate(getSanitizedPath(filepath)) + crc.calculate(line + '')
}
<commit_msg>Use interpolation<commit_after>import crc from 'easy-crc32'

export function getSanitizedPath(path) {
    return path
        .replace(/^\//, '')
        .replace(/\/$/, '')
}
export function getLocCrc(filepath, line) {
    return crc.calculate(getSanitizedPath(filepath)) + crc.calculate(`${ line }`)
}
<|endoftext|>


<commit_before>const getStyle = function (el, prop) {
  if (!el || el === window) return null;
  let value = (getComputedStyle ?
    getComputedStyle(el, null).getPropertyValue(prop) : el.style[prop]) || el.style[prop];
  return value;
};

const checkOverflow = function (el) {
  let info = getStyle(el, "overflow") + getStyle(el, "overflow-y") + getStyle(el, "overflow-x");
  return /scroll|auto/.test(info);
};

const getScrollParent = (el) => {
  if (!(el instanceof HTMLElement)) {
    console.error(`${el} is not an HTMLElement`);
    return null;
  }
  let parent = el;
  while (parent) {
    if (parent === document.body || parent === document.documentElement) break;
    if (!parent.parentNode) return null;

    if (checkOverflow(parent)) return parent;
    parent = parent.parentNode;
  }
  return window;
};

const checkInView = function (el, scrollParent = window, offset = 0) {
  let clientH, clientW;
  let offsetTop = 0, offsetLeft = 0;
  let isInView;
  clientH = document.documentElement.clientHeight || document.body.clientHeight;
  clientW = document.documentElement.clientWidth || document.body.clientWidth;

  // use getBoundingClientRect
  let top, left, right, bottom, rect;
  rect = el.getBoundingClientRect();
  top = rect.top - offset;
  left = rect.left - offset;
  bottom = rect.bottom + offset;
  right = rect.right + offset;
  isInView = top < clientH && bottom > 0 && left < clientW && right > 0

  if(scrollParent!==window){
    let scrollTop = scrollParent.scrollTop;
    let scrollLeft = scrollParent.scrollLeft;
    let width = el.offsetWidth, height = el.offsetHeight;
    clientH = scrollParent.clientHeight;
    clientW = scrollParent.clientWidth;
    while (el != scrollParent && el != null) {
      let borderWidth = parseInt(getStyle(el, "border-width"));
      offsetTop += el.offsetTop + borderWidth;
      offsetLeft += el.offsetLeft + borderWidth;
      el = el.offsetParent;
    }
    isInView = isInView && (scrollTop + clientH > offsetTop - offset && offsetTop + height + offset > scrollTop
      && scrollLeft + clientW > offsetLeft - offset && offsetLeft + width + offset > scrollLeft)
  }
  return isInView;
}

const throttle = function (fn, interval) {
  let begin = 0, setTm;
  if (!interval || interval <= 0) return fn;
  return function () {
    const now = Date.now();
    if (now - begin > interval) {
      begin = now;
      fn.apply(this, arguments);
    }
    else {
      clearTimeout(setTm);
      setTm = setTimeout(fn.bind(this, ...arguments), interval - (now - begin));
    }
  }
}

const debounce = function (fn, idle) {
  let setTm;
  if (!idle || idle <= 0) return fn;
  return function () {
    clearTimeout(setTm);
    setTm = setTimeout(fn.bind(this, ...arguments), idle);
  }
}

export {
  getScrollParent,
  getStyle,
  checkOverflow,
  checkInView,
  throttle,
  debounce
}

<commit_msg>optimize code style<commit_after>const getStyle = function (el, prop) {
  if (!el || el === window) return null;
  let value = (getComputedStyle ?
    getComputedStyle(el, null).getPropertyValue(prop) : el.style[prop]) || el.style[prop];
  return value;
};

const checkOverflow = function (el) {
  let info = getStyle(el, "overflow") + getStyle(el, "overflow-y") + getStyle(el, "overflow-x");
  return /scroll|auto/.test(info);
};

const getScrollParent = (el) => {
  if (!(el instanceof HTMLElement)) {
    console.error(`${el} is not an HTMLElement`);
    return null;
  }
  let parent = el;
  while (parent) {
    if (parent === document.body || parent === document.documentElement) break;
    if (!parent.parentNode) return null;

    if (checkOverflow(parent)) return parent;
    parent = parent.parentNode;
  }
  return window;
};

const checkInView = function (el, scrollParent = window, offset = 0) {
  let clientH, clientW;
  let offsetTop = 0, offsetLeft = 0;
  let isInView;
  clientH = document.documentElement.clientHeight || document.body.clientHeight;
  clientW = document.documentElement.clientWidth || document.body.clientWidth;

  // use getBoundingClientRect
  let top, left, right, bottom, rect;
  rect = el.getBoundingClientRect();
  top = rect.top - offset;
  left = rect.left - offset;
  bottom = rect.bottom + offset;
  right = rect.right + offset;
  isInView = top < clientH && bottom > 0 && left < clientW && right > 0

  if (scrollParent !== window) {
    let scrollTop = scrollParent.scrollTop;
    let scrollLeft = scrollParent.scrollLeft;
    let width = el.offsetWidth, height = el.offsetHeight;
    clientH = scrollParent.clientHeight;
    clientW = scrollParent.clientWidth;
    while (el && el !== scrollParent) {
      let borderWidth = parseInt(getStyle(el, "border-width"));
      offsetTop += el.offsetTop + borderWidth;
      offsetLeft += el.offsetLeft + borderWidth;
      el = el.offsetParent;
    }
    isInView = isInView && (scrollTop + clientH > offsetTop - offset && offsetTop + height + offset > scrollTop
      && scrollLeft + clientW > offsetLeft - offset && offsetLeft + width + offset > scrollLeft)
  }
  return isInView;
}

const throttle = function (fn, interval) {
  let begin = 0, setTm;
  if (!interval || interval <= 0) return fn;
  return function () {
    const now = Date.now();
    if (now - begin > interval) {
      begin = now;
      fn.apply(this, arguments);
    }
    else {
      clearTimeout(setTm);
      setTm = setTimeout(fn.bind(this, ...arguments), interval - (now - begin));
    }
  }
}

const debounce = function (fn, idle) {
  let setTm;
  if (!idle || idle <= 0) return fn;
  return function () {
    clearTimeout(setTm);
    setTm = setTimeout(fn.bind(this, ...arguments), idle);
  }
}

export {
  getScrollParent,
  getStyle,
  checkOverflow,
  checkInView,
  throttle,
  debounce
}

<|endoftext|>


<commit_before>import { cancelled, put, take } from 'redux-saga/effects'

export const noop = x => x

export function * syncChannel (channel, successActionCreator, transform = noop) {
  try {
    while (true) {
      const data = yield take(channel)
      yield put(successActionCreator(transform(data)))
    }
  } finally {
    if (yield cancelled()) channel.close()
  }
}
<commit_msg>Give a failureActionCreator to be dispatched on channel failure<commit_after>import { cancelled, put, take } from 'redux-saga/effects'

export const noop = x => x

export function * syncChannel (channel, successActionCreator, transform = noop, failureActionCreator = null) {
  try {
    while (true) {
      const data = yield take(channel)
      yield put(successActionCreator(transform(data)))
    }
  } catch (err) {
    if (failureActionCreator) yield put(failureActionCreator(err))
  } finally {
    if (yield cancelled()) channel.close()
  }
}
<|endoftext|>


<commit_before>import fs from "fs";
import InputStream from "./InputStream";

export function isGreyspace(string) {
    if (string === "" ||
        string === " " ||
        string === "\n" ||
        /^\s*$/.test(string)) {
        return true;
    }

    if (string === undefined || string === null)
        throw new Error("passed undefined or null to isGreyspace");

    let stream = new InputStream(string);

    while (!stream.atEnd()) {
        let consumed = stream.consume(/\s+/) ||
            stream.consume(/\/\/[^\n]*/) ||
            stream.consume(/\/\*[\W\S]*?\*\//);
        if (!consumed) return false;
    }

    return stream.atEnd();
}

export function log(...messages) {
    let data = messages.join(" ") + "\n";
    fs.writeSync(3, data);
}
<commit_msg>Catch EBADF error thrown when file descriptor 3 is not available.<commit_after>import fs from "fs";
import InputStream from "./InputStream";

export function isGreyspace(string) {
    if (string === "" ||
        string === " " ||
        string === "\n" ||
        /^\s*$/.test(string)) {
        return true;
    }

    if (string === undefined || string === null)
        throw new Error("passed undefined or null to isGreyspace");

    let stream = new InputStream(string);

    while (!stream.atEnd()) {
        let consumed = stream.consume(/\s+/) ||
            stream.consume(/\/\/[^\n]*/) ||
            stream.consume(/\/\*[\W\S]*?\*\//);
        if (!consumed) return false;
    }

    return stream.atEnd();
}

export function log(...messages) {
    let data = messages.join(" ") + "\n";
    try {
        fs.writeSync(3, data);
    } catch (error) {
        if (error.code === "EBADF") {
            return;
        } else {
            throw error;
        }
    }
}
<|endoftext|>


<commit_before>/* vogue.js
 * A tool for web developers. Vogue watches for changes to CSS files and
 * informs the web browser using them to reload those stylesheets.
 *
 * Created by Andrew Davey ~ http://aboutcode.net
 *
 * Vogue runs on nodeJS and uses socket.io for real-time communication between
 * browser and server.
 */

var http = require('http')
  , fs   = require('fs')
  , path = require('path')
  , opt  = require('parseopt')
  , io   = require('socket.io');

var VogueClient = require('./VogueClient').VogueClient
  , Watcher     = require('./Watcher').Watcher;

var options = getOptions()
  , server  = http.createServer(handleHttpRequest)
  , socket  = io.listen(server)
  , watcher = new Watcher(options.webDirectory);

server.listen(options.port);
socket.on('connection', function(clientSocket) {
  watcher.addClient(new VogueClient(clientSocket, watcher));
});

console.log('Watching directory: ' + options.webDirectory);
console.log('Listening for clients: http://localhost:' + options.port + '/');

function handleHttpRequest(request, response) {
  if (request.url === '/') {
    sendAboutPage(response);
  } else if (request.url === '/vogue-client.js') {
    sendVogueClient(response);
  }
}

function sendAboutPage(response) {
  fs.readFile(__dirname + '/client/about.htm', function(e, fileData) {
    var html = fileData.toString();
    html = html.replace(/\{port\}/g, options.port.toString());
    response.writeHead(200, { 'Content-Type': 'text/html' });
    response.write(html);
    response.end();
  });
}

function sendVogueClient(response) {
  fs.readFile(__dirname + '/client/vogue-client.js', function(e, fileData) {
    var script = fileData.toString();
    script = script.replace(/\{port\}/g, options.port.toString());
    response.writeHead(200, { 'Content-Type': 'text/javascript' });
    response.write(script);
    response.end();
  });
}

function getOptions() {
  var data = createOptionParser().parse();
  if (!data) process.exit(1); // Some kind of parsing error

  // The directory to watch is given as the first argument after the options.
  // So we'll put it into the options we return for simplicity.
  data.options.webDirectory = getDirectoryToWatch(data.arguments);
  return data.options;

  function createOptionParser() {
    var parser = new opt.OptionParser({
      options: [
        {
          name: ['--port', '-p'],
          type: 'int',
          help: 'Port to run Vogue server on',
          default: 8001
        },
        {
          name: ['--help','-h','-?'],
          type: 'flag',
          help: 'Show this help message',
          onOption: function (value) {
            if (value) {
              parser.usage('First argument after options should be the path to the website\'s root directory. Otherwise the current directory is used.\ne.g. vogue -p 8001 ./myweb');
            }
            // returning true cancels any further option parsing
            // and parser.parse() returns null
            return value;
          }
        }
      ]
    });
    return parser;
  }

  function getDirectoryToWatch(arguments) {
    var dir = path.join(
      process.cwd(),
      (arguments.length > 0) ? arguments[0] : ''
    );

    try {
      var stats = fs.statSync(dir);
      if (!stats.isDirectory()) {
        console.error('Path is not a directory: ' + dir);
        process.exit(1);
      }
    } catch (e) {
      console.error('Path not found: ' + dir);
      process.exit(1);
    }

    return dir;
  }
}
<commit_msg>Support absolute path for website directory.<commit_after>/* vogue.js
 * A tool for web developers. Vogue watches for changes to CSS files and
 * informs the web browser using them to reload those stylesheets.
 *
 * Created by Andrew Davey ~ http://aboutcode.net
 *
 * Vogue runs on nodeJS and uses socket.io for real-time communication between
 * browser and server.
 */

var http = require('http')
  , fs   = require('fs')
  , path = require('path')
  , opt  = require('parseopt')
  , io   = require('socket.io');

var VogueClient = require('./VogueClient').VogueClient
  , Watcher     = require('./Watcher').Watcher;

var options = getOptions()
  , server  = http.createServer(handleHttpRequest)
  , socket  = io.listen(server)
  , watcher = new Watcher(options.webDirectory);

server.listen(options.port);
socket.on('connection', function(clientSocket) {
  watcher.addClient(new VogueClient(clientSocket, watcher));
});

console.log('Watching directory: ' + options.webDirectory);
console.log('Listening for clients: http://localhost:' + options.port + '/');

function handleHttpRequest(request, response) {
  if (request.url === '/') {
    sendAboutPage(response);
  } else if (request.url === '/vogue-client.js') {
    sendVogueClient(response);
  }
}

function sendAboutPage(response) {
  fs.readFile(__dirname + '/client/about.htm', function(e, fileData) {
    var html = fileData.toString();
    html = html.replace(/\{port\}/g, options.port.toString());
    response.writeHead(200, { 'Content-Type': 'text/html' });
    response.write(html);
    response.end();
  });
}

function sendVogueClient(response) {
  fs.readFile(__dirname + '/client/vogue-client.js', function(e, fileData) {
    var script = fileData.toString();
    script = script.replace(/\{port\}/g, options.port.toString());
    response.writeHead(200, { 'Content-Type': 'text/javascript' });
    response.write(script);
    response.end();
  });
}

function getOptions() {
  var data = createOptionParser().parse();
  if (!data) process.exit(1); // Some kind of parsing error

  // The directory to watch is given as the first argument after the options.
  // So we'll put it into the options we return for simplicity.
  data.options.webDirectory = getDirectoryToWatch(data.arguments);
  return data.options;

  function createOptionParser() {
    var parser = new opt.OptionParser({
      options: [
        {
          name: ['--port', '-p'],
          type: 'int',
          help: 'Port to run Vogue server on',
          default: 8001
        },
        {
          name: ['--help','-h','-?'],
          type: 'flag',
          help: 'Show this help message',
          onOption: function (value) {
            if (value) {
              parser.usage('First argument after options should be the path to the website\'s root directory. Otherwise the current directory is used.\ne.g. vogue -p 8001 ./myweb');
            }
            // returning true cancels any further option parsing
            // and parser.parse() returns null
            return value;
          }
        }
      ]
    });
    return parser;
  }

  function getDirectoryToWatch(arguments) {
    var dir;
    if (arguments.length > 0) {
      if (/^\//.test(arguments[0])) {
        dir = arguments[0];
      } else {
        dir = path.join(process.cwd(), arguments[0]);
      }
    } else {
      dir = process.cwd();
    }

    try {
      var stats = fs.statSync(dir);
      if (!stats.isDirectory()) {
        console.error('Path is not a directory: ' + dir);
        process.exit(1);
      }
    } catch (e) {
      console.error('Path not found: ' + dir);
      process.exit(1);
    }

    return dir;
  }
}
<|endoftext|>


<commit_before>/* Vim-sytle UI */
const WIMUI=()=>{
    /* simple objects */
    let current_state='normal';
    const multiplier=1,
          multiplier_str=['',''],
          get_multiplier=(multstr)=>{/* [String] -> Int */ return multstr.reduce((a,b)=>a*(b||1),1)},
          reset_multiplier=()=>{multiplier=1; multiplier_str=['',''];},
          initial_state='normal';

    /* State machine tries to match: Chords, then Sequences, then Atoms. */
    const chord={
        'C-[':{act:'escape',code:'BracketLeft',mods:[2]},
        'C-g':{act:'escape',code:'KeyG',mods:[2]},
        'C-d':{act:'motion',code:'KeyD',mods:[2]},
        'C-u':{act:'motion',code:'KeyU',mods:[2]},
        'C-v':{act:'visual',code:'KeyV',mods:[2]}
    };

    const seq={/* {Seq:{Action,ReverseName,MinMilliseconds?}} */
        'fd':{act:'escape',rn:'df',dt:500}
    };

    const atom=(()=>{/* {Char:[Type]} */
        let xs={/* {Type:[Char]} */
            edit:'oOpPrxX',
            find_char:'fFtT',
            insert:'aAiI',
            modifier:'ai',
            motion:'hjkl',
            mult_0:'123456789',
            mult_N:'0123456789',
            repeat:'.',
            surround:'s',
            text_object:'0^$%{}()[]<>`"\'bBeEpwWG',
            undo:'u',
            verb:'cdy',
            visual:'vV'
        };
        let as='';
        for(let i=32;i<127;++i){as+=String.fromCharCode(i);} xs.ascii=as;
        let t={};
        for(let x in xs){[...xs[x]].forEach(y=>t[y]?t[y].push(x):t[y]=[x]);}/* invert xs table */
        t.Escape=['escape'];
        return t;
    })();

    const st={/* StateTable : {State:{Event->State}} */
    };

    const update=(input)=>{
        const [ok_chord, ok_seq]=((n)=>{
            /* Test input for chord. */
            for(let x in chord){
                let i=chord[x];
                if((Array.from(n.KC).indexOf(i.code)>-1)&&(0>i.mods||i.mods.some(y=>y==n.KS[3][0]))){
                    i.name=x;
                    return [i,null];
                }
            }
            /* Test input for sequence. */
            for(let x in seq){
                let i=seq[x];
                if(n.KS[0].join('').startsWith(i.rn)){
                    let j=(!i.dt)?i:(i.dt>n.KS[2].slice(0,x.length).reduce((a,b)=>a-b))?i:null;
                    return [null,j];
                }
            }
            return [null,null];
        })(input);

        let action='nop', fn=unexpected_event;
        const tf=(e,msg)=>{
            if((fn=st[current_state][e.act])){action=e.act;}
            else{console.log(msg);}
        };

        if(ok_chord){tf(ok_chord,'bad chord');}
        else if(ok_seq){tf(ok_seq,'bad sequence');}
        else{
            let et=atom[input.KS[0][0]]||[];
            for(let i=0;i<et.length;++i){
                if(et.length && (fn=st[current_state][et[i]])){action=et[i]; break;}
            }
        }

        if(action==='nop'){return;}

        /* Otherwise compute next state. */
        let obj={}, next_state=fn.call(this,obj);
        if(!next_state){next_state=current_state;}
        if(!st[next_state]){next_state=unexpected_state(action,next_state);}

        /* TODO output
         Option 1: Accumulate actual keys and (upon successful state change)
         export the key sequence to external handling function.
         Option 2: Immediately pass (state, key) tuples to handling function.
         */
        current_state=next_state;
    };

    const unexpected_event=(e)=>{
        console.log(`unexpected event: ${e}`);
        reset_multiplier();
        return initial_state;
    };

    const unexpected_state=(e,s)=>{
        console.log(`unexpected state: ${e}`);
        return unexpected_event(e);
    };

    return ({update});
};
<commit_msg>Trying a new state table approach.<commit_after>/* Vim-sytle UI */
const WIMUI=()=>{
    /* simple objects */
    let current_state='normal';
    const multiplier=1,
          multiplier_str=['',''],
          get_multiplier=(multstr)=>{/* [String] -> Int */ return multstr.reduce((a,b)=>a*(b||1),1);},
          reset_multiplier=()=>{multiplier=1; multiplier_str=['',''];},
          initial_state='normal';

    /* State machine tries to match: Chords, then Sequences, then Atoms. */
    const chord={
        'C-[':{act:'escape',code:'BracketLeft',mods:[2]},
        'C-g':{act:'escape',code:'KeyG',mods:[2]},
        'C-d':{act:'motion',code:'KeyD',mods:[2]},
        'C-u':{act:'motion',code:'KeyU',mods:[2]},
        'C-v':{act:'visual',code:'KeyV',mods:[2]}
    };

    const seq={/* {Seq:{Action,ReverseName,MinMilliseconds?}} */
        'fd':{act:'escape',rn:'df',dt:500}
    };

    const atom=(()=>{/* {Char:[Type]} */
        let xs={/* {Type:[Char]} */
            bracket:'[{()}]',
            edit:'oOpPrxX',
            find_char:'fFtT',
            insert:'aAiI',
            modifier:'ai',
            motion:'hjkl',
            mult_0:'123456789',
            mult_N:'0123456789',
            repeat:'.',
            surround:'s',
            text_object:'0^$%{}()[]<>`"\'bBeEpwWG',
            undo:'u',
            verb:'cdy',
            visual:'vV'
        };
        let as='';
        for(let i=32;i<127;++i){as+=String.fromCharCode(i);} xs.ascii=as;
        let t={};
        for(let x in xs){[...xs[x]].forEach(y=>t[y]?t[y].push(x):t[y]=[x]);}/* invert xs table */
        t.Escape=['escape'];
        return t;
    })();

    const st={/* StateTable : {State:{Event->State}} */
        mult_0:'',
        verb:{
            modifier:{
                seek:'ascii',
                text_object:null
            },
            motion:null,
            seek:'ascii',
            text_object:null
        },
        motion:null,
        seek:'ascii',
        surround:{
            c:{bracket:'bracket'},
            d:'bracket',
            y:{
                bracket:'bracket',
                modifier:{
                    motion:'bracket',
                    seek:{ascii:'bracket'},
                    text_object:'bracket'
                },
                seek:{ascii:'bracket'},
                text_object:'bracket'
            }
        },
        text_object:null
    };

    const update=(input)=>{
        const [ok_chord, ok_seq]=((n)=>{
            /* Test input for chord. */
            for(let x in chord){
                let i=chord[x];
                if((Array.from(n.KC).indexOf(i.code)>-1)&&(0>i.mods||i.mods.some(y=>y==n.KS[3][0]))){
                    i.name=x;
                    return [i,null];
                }
            }
            /* Test input for sequence. */
            for(let x in seq){
                let i=seq[x];
                if(n.KS[0].join('').startsWith(i.rn)){
                    let j=(!i.dt)?i:(i.dt>n.KS[2].slice(0,x.length).reduce((a,b)=>a-b))?i:null;
                    return [null,j];
                }
            }
            return [null,null];
        })(input);

        let action='nop', fn=unexpected_event;
        const tf=(e,msg)=>{
            if((fn=st[current_state][e.act])){action=e.act;}
            else{console.log(msg);}
        };

        if(ok_chord){tf(ok_chord,'bad chord');}
        else if(ok_seq){tf(ok_seq,'bad sequence');}
        else{
            let et=atom[input.KS[0][0]]||[];
            for(let i=0;i<et.length;++i){
                if(et.length && (fn=st[current_state][et[i]])){action=et[i]; break;}
            }
        }

        if(action==='nop'){return;}

        /* Otherwise compute next state. */
        let obj={}, next_state=fn.call(this,obj);
        if(!next_state){next_state=current_state;}
        if(!st[next_state]){next_state=unexpected_state(action,next_state);}

        /* TODO output
         Option 1: Accumulate actual keys and (upon successful state change)
         export the key sequence to external handling function.
         Option 2: Immediately pass (state, key) tuples to handling function.
         */
        current_state=next_state;
    };

    const unexpected_event=(e)=>{
        console.log(`unexpected event: ${e}`);
        reset_multiplier();
        return initial_state;
    };

    const unexpected_state=(e,s)=>{
        console.log(`unexpected state: ${e}`);
        return unexpected_event(e);
    };

    return ({update});
};
<|endoftext|>


<commit_before>(function (root) {

    function EventListener() {
        this._ = {};
    }

    EventListener.prototype.on = function (ev, fn) {
        var l = this._[ev] = this._[ev] || [];
        l.push(fn);
        return this;
    };

    EventListener.prototype.emit = function (_ev, ev, data) {
        var self = this
          , l = this._[ev]
          , args = Array.prototype.slice.call(arguments, 2)
          ;

        if (!l || !l.length) {
            return this;
        }

        l.forEach(function (fn) {
            fn.apply(_ev, args);
        });
    };

    var _workers = {};

    function Worky(script) {

        var self = {};

        if (this.constructor !== Worky && script) {
            return new Worky(script);
        }

        if (!script) {
            self = Object.create(EventListener.prototype);
        } else {
            self = this;
        }

        EventListener.call(self);
        self.is_worker = !script;

        // Initialize the worker interface
        if (script) {
            self.worker = new Worker(script);
            self.worker.worky = this;
            self.worker.onmessage = function (ev) {
                ev.data = Object(ev.data);
                self.emit(ev, ev.data.event, ev.data);
            };
            self.on("__init", function (ev, data) {
                debugger
            });
        } else {
            self.emit = function (ev, data) {
                root.postMessage(new Worky.Message(ev, data));
            };
            return self;
        }
    }

    Worky.prototype = Object.create(EventListener.prototype);
    Worky.prototype.constructor = Worky;
    Worky.Message = function (ev, data) {
        this.event = ev;
        this.data = data;
    };

    root.Worky = Worky;
})(self);
<commit_msg>Removed obsolete __init event<commit_after>(function (root) {

    function EventListener() {
        this._ = {};
    }

    EventListener.prototype.on = function (ev, fn) {
        var l = this._[ev] = this._[ev] || [];
        l.push(fn);
        return this;
    };

    EventListener.prototype.emit = function (_ev, ev, data) {
        var self = this
          , l = this._[ev]
          , args = Array.prototype.slice.call(arguments, 2)
          ;

        if (!l || !l.length) {
            return this;
        }

        l.forEach(function (fn) {
            fn.apply(_ev, args);
        });
    };

    var _workers = {};

    function Worky(script) {

        var self = {};

        if (this.constructor !== Worky && script) {
            return new Worky(script);
        }

        if (!script) {
            self = Object.create(EventListener.prototype);
        } else {
            self = this;
        }

        EventListener.call(self);
        self.is_worker = !script;

        // Initialize the worker interface
        if (script) {
            self.worker = new Worker(script);
            self.worker.worky = this;
            self.worker.onmessage = function (ev) {
                ev.data = Object(ev.data);
                self.emit(ev, ev.data.event, ev.data);
            };
        } else {
            self.emit = function (ev, data) {
                root.postMessage(new Worky.Message(ev, data));
            };
            return self;
        }
    }

    Worky.prototype = Object.create(EventListener.prototype);
    Worky.prototype.constructor = Worky;
    Worky.Message = function (ev, data) {
        this.event = ev;
        this.data = data;
    };

    root.Worky = Worky;
})(self);
<|endoftext|>