<commit_before>/*!
 * node-sass: lib/extensions.js
 */

var eol = require('os').EOL,
    fs = require('fs'),
    pkg = require('../package.json'),
    path = require('path'),
    defaultBinaryPath = path.join(__dirname, '..', 'vendor');

function getHumanPlatform(arg) {
  switch (arg || process.platform) {
    case 'darwin': return 'OS X';
    case 'freebsd': return 'FreeBSD';
    case 'linux': return 'Linux';
    case 'win32': return 'Windows';
    default: return false;
  }
}

function getHumanArchitecture(arg) {
  switch (arg || process.arch) {
    case 'ia32': return '32-bit';
    case 'x86': return '32-bit';
    case 'x64': return '64-bit';
    default: return false;
  }
}

function getHumanNodeVersion(arg) {
  switch (parseInt(arg || process.versions.modules, 10)) {
    case 11: return 'Node 0.10.x';
    case 14: return 'Node 0.12.x';
    case 42: return 'io.js 1.x';
    case 43: return 'io.js 1.1.x';
    case 44: return 'io.js 2.x';
    case 45: return 'io.js 3.x';
    case 46: return 'Node.js 4.x';
    case 47: return 'Node.js 5.x';
    case 48: return 'Node.js 6.x';
    default: return false;
  }
}

function getHumanEnvironment(env) {
  var binding = env.replace(/_binding\.node$/, ''),
      parts = binding.split('-'),
      platform = getHumanPlatform(parts[0]),
      arch = getHumanArchitecture(parts[1]),
      runtime = getHumanNodeVersion(parts[2]);

  if (parts.length !== 3) {
    return 'Unknown environment (' + binding + ')';
  }

  if (!platform) {
    platform = 'Unsupported platform (' + parts[0] + ')';
  }

  if (!arch) {
    arch = 'Unsupported architecture (' + parts[1] + ')';
  }

  if (!runtime) {
    runtime = 'Unsupported runtime (' + parts[2] + ')';
  }

  return [
    platform, arch, 'with', runtime,
  ].join(' ');
}

function getInstalledBinaries() {
  return fs.readdirSync(defaultBinaryPath);
}

function isSupportedEnvironment(platform, arch, runtime) {
  return (
    false !== getHumanPlatform(platform) &&
    false !== getHumanArchitecture(arch) &&
    false !== getHumanNodeVersion(runtime)
  );
}

/**
 * Get the value of a CLI argument
 *
 * @param {String} name
 * @param {Array} args
 * @api private
 */

function getArgument(name, args) {
  var flags = args || process.argv.slice(2),
      index = flags.lastIndexOf(name);

  if (index === -1 || index + 1 >= flags.length) {
    return null;
  }

  return flags[index + 1];
}

/**
 * Get binary name.
 * If environment variable SASS_BINARY_NAME,
 * .npmrc variable sass_binary_name or
 * process argument --binary-name is provided,
 * return it as is, otherwise make default binary
 * name: {platform}-{arch}-{v8 version}.node
 *
 * @api public
 */

function getBinaryName() {
  var binaryName;

  if (getArgument('--sass-binary-name')) {
    binaryName = getArgument('--sass-binary-name');
  } else if (process.env.SASS_BINARY_NAME) {
    binaryName = process.env.SASS_BINARY_NAME;
  } else if (process.env.npm_config_sass_binary_name) {
    binaryName = process.env.npm_config_sass_binary_name;
  } else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryName) {
    binaryName = pkg.nodeSassConfig.binaryName;
  } else {
    binaryName = [process.platform, '-',
                  process.arch, '-',
                  process.versions.modules].join('');
  }

  return [binaryName, 'binding.node'].join('_');
}

/**
 * Determine the URL to fetch binary file from.
 * By default fetch from the node-sass distribution
 * site on GitHub.
 *
 * The default URL can be overriden using
 * the environment variable SASS_BINARY_SITE,
 * .npmrc variable sass_binary_site or
 * or a command line option --sass-binary-site:
 *
 *   node scripts/install.js --sass-binary-site http://example.com/
 *
 * The URL should to the mirror of the repository
 * laid out as follows:
 *
 * SASS_BINARY_SITE/
 *
 *  v3.0.0
 *  v3.0.0/freebsd-x64-14_binding.node
 *  ....
 *  v3.0.0
 *  v3.0.0/freebsd-ia32-11_binding.node
 *  v3.0.0/freebsd-x64-42_binding.node
 *  ... etc. for all supported versions and platforms
 *
 * @api public
 */

function getBinaryUrl() {
  var site = getArgument('--sass-binary-site') ||
             process.env.SASS_BINARY_SITE  ||
             process.env.npm_config_sass_binary_site ||
             (pkg.nodeSassConfig && pkg.nodeSassConfig.binarySite) ||
             'https://github.com/sass/node-sass/releases/download';

	return [site, 'v' + pkg.version, getBinaryName()].join('/');
}

/**
 * Get binary path.
 * If environment variable SASS_BINARY_PATH,
 * .npmrc variable sass_binary_path or
 * process argument --sass-binary-path is provided,
 * select it by appending binary name, otherwise
 * make default binary path using binary name.
 * Once the primary selection is made, check if
 * callers wants to throw if file not exists before
 * returning.
 *
 * @api public
 */

function getBinaryPath() {
  var binaryPath;

  if (getArgument('--sass-binary-path')) {
    binaryPath = getArgument('--sass-binary-path');
  } else if (process.env.SASS_BINARY_PATH) {
    binaryPath = process.env.SASS_BINARY_PATH;
  } else if (process.env.npm_config_sass_binary_path) {
    binaryPath = process.env.npm_config_sass_binary_path;
  } else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryPath) {
    binaryPath = pkg.nodeSassConfig.binaryPath;
  } else {
    binaryPath = path.join(defaultBinaryPath, getBinaryName().replace(/_/, '/'));
  }

  return binaryPath;
}

/**
 * Does the supplied binary path exist
 *
 * @param {String} binaryPath
 * @api public
 */

function hasBinary(binaryPath) {
  return fs.existsSync(binaryPath);
}

/**
 * Get Sass version information
 *
 * @api public
 */

function getVersionInfo(binding) {
  return [
           ['node-sass', pkg.version, '(Wrapper)', '[JavaScript]'].join('\t'),
           ['libsass  ', binding.libsassVersion(), '(Sass Compiler)', '[C/C++]'].join('\t'),
  ].join(eol);
}

module.exports.hasBinary = hasBinary;
module.exports.getBinaryUrl = getBinaryUrl;
module.exports.getBinaryName = getBinaryName;
module.exports.getBinaryPath = getBinaryPath;
module.exports.getVersionInfo = getVersionInfo;
module.exports.getHumanEnvironment = getHumanEnvironment;
module.exports.getInstalledBinaries = getInstalledBinaries;
module.exports.isSupportedEnvironment = isSupportedEnvironment;
<commit_msg>Docs: Add jsdocs for human readable public methods (#1528)<commit_after>/*!
 * node-sass: lib/extensions.js
 */

var eol = require('os').EOL,
    fs = require('fs'),
    pkg = require('../package.json'),
    path = require('path'),
    defaultBinaryPath = path.join(__dirname, '..', 'vendor');

/**
 * Get the human readable name of the Platform that is running
 *
 * @param  {string} platform - An OS platform to match, or null to fallback to
 * the current process platform
 * @return {Object} The name of the platform if matched, false otherwise
 *
 * @api public
 */
function getHumanPlatform(platform) {
  switch (platform || process.platform) {
    case 'darwin': return 'OS X';
    case 'freebsd': return 'FreeBSD';
    case 'linux': return 'Linux';
    case 'win32': return 'Windows';
    default: return false;
  }
}

/**
 * Provides a more readable version of the architecture
 *
 * @param  {string} arch - An instruction architecture name to match, or null to
 * lookup the current process architecture
 * @return {Object} The value of the process architecture, or false if unknown
 *
 * @api public
 */
function getHumanArchitecture(arch) {
  switch (arch || process.arch) {
    case 'ia32': return '32-bit';
    case 'x86': return '32-bit';
    case 'x64': return '64-bit';
    default: return false;
  }
}

/**
 * Get the friendly name of the Node environment being run
 *
 * @param  {Object} abi - A Node Application Binary Interface value, or null to
 * fallback to the current Node ABI
 * @return {Object} Returns a string name of the Node environment or false if
 * unmatched
 *
 * @api public
 */
function getHumanNodeVersion(abi) {
  switch (parseInt(abi || process.versions.modules, 10)) {
    case 11: return 'Node 0.10.x';
    case 14: return 'Node 0.12.x';
    case 42: return 'io.js 1.x';
    case 43: return 'io.js 1.1.x';
    case 44: return 'io.js 2.x';
    case 45: return 'io.js 3.x';
    case 46: return 'Node.js 4.x';
    case 47: return 'Node.js 5.x';
    case 48: return 'Node.js 6.x';
    default: return false;
  }
}

/**
 * Get a human readable description of where node-sass is running to support
 * user error reporting when something goes wrong
 *
 * @param  {string} env - The name of the native bindings that is to be parsed
 * @return {string} A description of what os, architecture, and Node version
 * that is being run
 *
 * @api public
 */
function getHumanEnvironment(env) {
  var binding = env.replace(/_binding\.node$/, ''),
      parts = binding.split('-'),
      platform = getHumanPlatform(parts[0]),
      arch = getHumanArchitecture(parts[1]),
      runtime = getHumanNodeVersion(parts[2]);

  if (parts.length !== 3) {
    return 'Unknown environment (' + binding + ')';
  }

  if (!platform) {
    platform = 'Unsupported platform (' + parts[0] + ')';
  }

  if (!arch) {
    arch = 'Unsupported architecture (' + parts[1] + ')';
  }

  if (!runtime) {
    runtime = 'Unsupported runtime (' + parts[2] + ')';
  }

  return [
    platform, arch, 'with', runtime,
  ].join(' ');
}

/**
 * Get the value of the binaries under the default path
 *
 * @return {Array} The currently installed node-sass bindings
 *
 * @api public
 */
function getInstalledBinaries() {
  return fs.readdirSync(defaultBinaryPath);
}

/**
 * Check that an environment matches the whitelisted values or the current
 * environment if no parameters are passed
 *
 * @param  {string} platform - The name of the OS platform(darwin, win32, etc...)
 * @param  {string} arch - The instruction set architecture of the Node environment
 * @param  {string} abi - The Node Application Binary Interface
 * @return {Boolean} True, if node-sass supports the current platform, false otherwise
 *
 * @api public
 */
function isSupportedEnvironment(platform, arch, abi) {
  return (
    false !== getHumanPlatform(platform) &&
    false !== getHumanArchitecture(arch) &&
    false !== getHumanNodeVersion(abi)
  );
}

/**
 * Get the value of a CLI argument
 *
 * @param {String} name
 * @param {Array} args
 * @api private
 */

function getArgument(name, args) {
  var flags = args || process.argv.slice(2),
      index = flags.lastIndexOf(name);

  if (index === -1 || index + 1 >= flags.length) {
    return null;
  }

  return flags[index + 1];
}

/**
 * Get binary name.
 * If environment variable SASS_BINARY_NAME,
 * .npmrc variable sass_binary_name or
 * process argument --binary-name is provided,
 * return it as is, otherwise make default binary
 * name: {platform}-{arch}-{v8 version}.node
 *
 * @api public
 */

function getBinaryName() {
  var binaryName;

  if (getArgument('--sass-binary-name')) {
    binaryName = getArgument('--sass-binary-name');
  } else if (process.env.SASS_BINARY_NAME) {
    binaryName = process.env.SASS_BINARY_NAME;
  } else if (process.env.npm_config_sass_binary_name) {
    binaryName = process.env.npm_config_sass_binary_name;
  } else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryName) {
    binaryName = pkg.nodeSassConfig.binaryName;
  } else {
    binaryName = [process.platform, '-',
                  process.arch, '-',
                  process.versions.modules].join('');
  }

  return [binaryName, 'binding.node'].join('_');
}

/**
 * Determine the URL to fetch binary file from.
 * By default fetch from the node-sass distribution
 * site on GitHub.
 *
 * The default URL can be overriden using
 * the environment variable SASS_BINARY_SITE,
 * .npmrc variable sass_binary_site or
 * or a command line option --sass-binary-site:
 *
 *   node scripts/install.js --sass-binary-site http://example.com/
 *
 * The URL should to the mirror of the repository
 * laid out as follows:
 *
 * SASS_BINARY_SITE/
 *
 *  v3.0.0
 *  v3.0.0/freebsd-x64-14_binding.node
 *  ....
 *  v3.0.0
 *  v3.0.0/freebsd-ia32-11_binding.node
 *  v3.0.0/freebsd-x64-42_binding.node
 *  ... etc. for all supported versions and platforms
 *
 * @api public
 */

function getBinaryUrl() {
  var site = getArgument('--sass-binary-site') ||
             process.env.SASS_BINARY_SITE  ||
             process.env.npm_config_sass_binary_site ||
             (pkg.nodeSassConfig && pkg.nodeSassConfig.binarySite) ||
             'https://github.com/sass/node-sass/releases/download';

	return [site, 'v' + pkg.version, getBinaryName()].join('/');
}

/**
 * Get binary path.
 * If environment variable SASS_BINARY_PATH,
 * .npmrc variable sass_binary_path or
 * process argument --sass-binary-path is provided,
 * select it by appending binary name, otherwise
 * make default binary path using binary name.
 * Once the primary selection is made, check if
 * callers wants to throw if file not exists before
 * returning.
 *
 * @api public
 */

function getBinaryPath() {
  var binaryPath;

  if (getArgument('--sass-binary-path')) {
    binaryPath = getArgument('--sass-binary-path');
  } else if (process.env.SASS_BINARY_PATH) {
    binaryPath = process.env.SASS_BINARY_PATH;
  } else if (process.env.npm_config_sass_binary_path) {
    binaryPath = process.env.npm_config_sass_binary_path;
  } else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryPath) {
    binaryPath = pkg.nodeSassConfig.binaryPath;
  } else {
    binaryPath = path.join(defaultBinaryPath, getBinaryName().replace(/_/, '/'));
  }

  return binaryPath;
}

/**
 * Does the supplied binary path exist
 *
 * @param {String} binaryPath
 * @api public
 */

function hasBinary(binaryPath) {
  return fs.existsSync(binaryPath);
}

/**
 * Get Sass version information
 *
 * @api public
 */

function getVersionInfo(binding) {
  return [
           ['node-sass', pkg.version, '(Wrapper)', '[JavaScript]'].join('\t'),
           ['libsass  ', binding.libsassVersion(), '(Sass Compiler)', '[C/C++]'].join('\t'),
  ].join(eol);
}

module.exports.hasBinary = hasBinary;
module.exports.getBinaryUrl = getBinaryUrl;
module.exports.getBinaryName = getBinaryName;
module.exports.getBinaryPath = getBinaryPath;
module.exports.getVersionInfo = getVersionInfo;
module.exports.getHumanEnvironment = getHumanEnvironment;
module.exports.getInstalledBinaries = getInstalledBinaries;
module.exports.isSupportedEnvironment = isSupportedEnvironment;
<|endoftext|>


<commit_before>const path = require('path')
const _ = require('lodash')
const fse = require('fs-extra')
const Promise = require('bluebird')
const config = require('./config')
const logger = require('./logger')

Promise.promisifyAll(fse)

class FileSaver {
  constructor() {
    this.files = {}
    this.enabled = true
  }

  enable() {
    this.enabled = true
  }

  disable() {
    this.enabled = false
  }

  save(path, data) {
    const prevData = _.get(this.files, path)
    if (prevData !== data) {
      _.set(this.files, path, data)
      return this._saveToFile(path, data)
    }
    return Promise.resolve()
  }

  saveEmail(name, type, data) {
    const path = [name, `${name}.${type}`]
    this.save(path, data)
  }

  saveCss(directory, filename, data) {
    const path = directory
      ? [directory, 'css', filename]
      : ['.common', filename]
    this.save(path, data)
  }

  async _saveToFile(pathArr, data) {
    if (!this.enabled) return Promise.resolve()

    const filePath = path.join(config.dirs.build, ...pathArr)

    if (_.isNil(data)) {
      logger.error(`Did not save ${filePath} because email is ${data}`)
      return
    }

    try {
      await fse.outputFileAsync(filePath, data)
      return filePath
    } catch (err) {
      logger.error(`Failed to save file: ${filePath}\n${err}`)
    }
  }
}

module.exports = new FileSaver()
<commit_msg>simplify syntax<commit_after>const path = require('path')
const _ = require('lodash')
const fse = require('fs-extra')
const Promise = require('bluebird')
const config = require('./config')
const logger = require('./logger')

Promise.promisifyAll(fse)

class FileSaver {
  constructor() {
    this.files = {}
    this.enabled = true
  }

  enable() {
    this.enabled = true
  }

  disable() {
    this.enabled = false
  }

  save(path, data) {
    const prevData = _.get(this.files, path)
    if (prevData !== data) {
      _.set(this.files, path, data)
      return this._saveToFile(path, data)
    }
    return Promise.resolve()
  }

  saveEmail(name, type, data) {
    const path = [name, `${name}.${type}`]
    this.save(path, data)
  }

  saveCss(directory, filename, data) {
    const path = directory
      ? [directory, 'css', filename]
      : ['.common', filename]
    this.save(path, data)
  }

  async _saveToFile(pathArr, data) {
    if (!this.enabled) return

    const filePath = path.join(config.dirs.build, ...pathArr)

    if (_.isNil(data)) {
      logger.error(`Did not save ${filePath} because email is ${data}`)
      return
    }

    try {
      await fse.outputFileAsync(filePath, data)
      return filePath
    } catch (err) {
      logger.error(`Failed to save file: ${filePath}\n${err}`)
    }
  }
}

module.exports = new FileSaver()
<|endoftext|>


<commit_before>'user strict';
var util = require('util');
var fs = require('fs');
var events = require('events');
var ipAddress = require('ip').address();
var path = require('path');
var winston = require('winston');

// re-usable regular expressions
var startsWithUnderscore = /^_/;
var hasDots = /\./g;

// settings for bulk requests
var MIN_LOGSENE_BULK_SIZE = 200;
var MAX_LOGSENE_BULK_SIZE = 10000;

// upper limit a user could set
var MAX_LOGSENE_BULK_SIZE_BYTES = 20 * 1024 * 1024;
// lower limit a user could set
var MIN_LOGSENE_BULK_SIZE_BYTES = 1024 * 1024;
var MAX_LOGSENE_BUFFER_SIZE = Number(process.env.LOGSENE_BULK_SIZE_BYTES) || 1024 * 1024 * 3; // max 3 MB per http request
// check limits set by users, and adjust if those would lead to problematic settings
if (MAX_LOGSENE_BUFFER_SIZE > MAX_LOGSENE_BULK_SIZE_BYTES) {
    MAX_LOGSENE_BUFFER_SIZE = MAX_LOGSENE_BULK_SIZE_BYTES
}
if (MAX_LOGSENE_BUFFER_SIZE < MIN_LOGSENE_BULK_SIZE_BYTES) {
    MAX_LOGSENE_BUFFER_SIZE = MIN_LOGSENE_BULK_SIZE_BYTES
}
var LOGSENE_BULK_SIZE = Number(process.env.LOGSENE_BULK_SIZE) || 1000; // max 1000 messages per bulk req.
if (LOGSENE_BULK_SIZE > MAX_LOGSENE_BULK_SIZE) {
    LOGSENE_BULK_SIZE = MAX_LOGSENE_BULK_SIZE
}
if (LOGSENE_BULK_SIZE < MIN_LOGSENE_BULK_SIZE) {
    LOGSENE_BULK_SIZE = MIN_LOGSENE_BULK_SIZE
}
var MAX_MESSAGE_FIELD_SIZE = Number(process.env.LOGSENE_MAX_MESSAGE_FIELD_SIZE) || 1024 * 240 // 240 K, leave
var LOG_GLOBAL_PATH = process.env.LOGFILE_PATH || '/logs/fluent-containers.';
var ROTATE_MB = parseInt(process.env.ROTATE_MB) || 10;
var ROTATE_MAX_FILES = parseInt(process.env.ROTATE_MAX_FILES) || 10;

process.setMaxListeners(0);

function fileLogger(token, podName) {
    if (!token) {
        throw new Error('token not specified')
    }
    if (!podName) {
        throw new Error('podName not specified')
    }

    this.token = token;
    let path = `${LOG_GLOBAL_PATH}${token}.${podName}.to.spl`;

    this.logger = new winston.Logger({
        transports: [
            new winston.transports.File({
                filename: path,
                zippedArchive: true,
                timestamp: false,
                maxsize: 1024 * 1024 * ROTATE_MB,
                maxFiles: ROTATE_MAX_FILES,
                json: false,
                showLevel: false
            })
        ], exitOnError: false
    })
    this.logger.handleExceptions(new winston.transports.Console({
        humanReadableUnhandledException: true, json: false
    }));

    events.EventEmitter.call(this);
}
util.inherits(fileLogger, events.EventEmitter);

fileLogger.prototype.log = function (level, message, fields, callback) {
    if (fields && fields._type) {
        delete fields._type
    }
    var msg = {
        message: message, severity: level, host: this.hostname, ip: ipAddress
    };
    for (var x in fields) {
        // rename fields for Elasticsearch 2.x
        if (startsWithUnderscore.test(x) || hasDots.test(x)) {
            msg[x.replace(/\./g, '_').replace(/^_+/, '')] = fields[x]
        } else {
            if (!(typeof fields[x] === 'function')) {
                msg[x] = fields[x]
            }
        }
    }
    var _index = this.token;
    if (fields && typeof (fields._index) === 'function') {
        _index = fields._index(msg)
    }
    if (msg.message && Buffer.byteLength(msg.message, 'utf8') > MAX_MESSAGE_FIELD_SIZE) {
        var cutMsg = new Buffer(MAX_MESSAGE_FIELD_SIZE);
        cutMsg.write(msg.message);
        msg.message = cutMsg.toString();
        if (msg.originalLine) {
            // when message is too large and logagent added originalLine,
            // this should be removed to stay under the limits in receiver
            delete msg.originalLine
        }
        msg.logsene_client_warning = 'Warning: message field too large > ' + MAX_MESSAGE_FIELD_SIZE + ' bytes'
        console.log(msg.logsene_client_warning);
    }

    var logRowFormatted = `INDEX: ${_index} | TIMESTAMP: ${msg["@timestamp"].toISOString()} | HOST: ${msg.host} | MESSAGE: ${msg.message} | POD_NAME: ${msg.kubernetes.pod_name} | NAMESPACE: ${msg.kubernetes.namespace} | NAMECONTAIER: ${msg.kubernetes.container_name} | SEVERITY: ${msg.severity}`;
    this.logger.log('info', logRowFormatted);

    this.emit('logged', {msg: msg, _index: _index});
    if (callback) {
        callback(null, msg)
    }
};

module.exports = fileLogger;
<commit_msg>Logfilename with podname<commit_after>'user strict';
var util = require('util');
var fs = require('fs');
var events = require('events');
var ipAddress = require('ip').address();
var path = require('path');
var winston = require('winston');

// re-usable regular expressions
var startsWithUnderscore = /^_/;
var hasDots = /\./g;

// settings for bulk requests
var MIN_LOGSENE_BULK_SIZE = 200;
var MAX_LOGSENE_BULK_SIZE = 10000;

// upper limit a user could set
var MAX_LOGSENE_BULK_SIZE_BYTES = 20 * 1024 * 1024;
// lower limit a user could set
var MIN_LOGSENE_BULK_SIZE_BYTES = 1024 * 1024;
var MAX_LOGSENE_BUFFER_SIZE = Number(process.env.LOGSENE_BULK_SIZE_BYTES) || 1024 * 1024 * 3; // max 3 MB per http request
// check limits set by users, and adjust if those would lead to problematic settings
if (MAX_LOGSENE_BUFFER_SIZE > MAX_LOGSENE_BULK_SIZE_BYTES) {
    MAX_LOGSENE_BUFFER_SIZE = MAX_LOGSENE_BULK_SIZE_BYTES
}
if (MAX_LOGSENE_BUFFER_SIZE < MIN_LOGSENE_BULK_SIZE_BYTES) {
    MAX_LOGSENE_BUFFER_SIZE = MIN_LOGSENE_BULK_SIZE_BYTES
}
var LOGSENE_BULK_SIZE = Number(process.env.LOGSENE_BULK_SIZE) || 1000; // max 1000 messages per bulk req.
if (LOGSENE_BULK_SIZE > MAX_LOGSENE_BULK_SIZE) {
    LOGSENE_BULK_SIZE = MAX_LOGSENE_BULK_SIZE
}
if (LOGSENE_BULK_SIZE < MIN_LOGSENE_BULK_SIZE) {
    LOGSENE_BULK_SIZE = MIN_LOGSENE_BULK_SIZE
}
var MAX_MESSAGE_FIELD_SIZE = Number(process.env.LOGSENE_MAX_MESSAGE_FIELD_SIZE) || 1024 * 240 // 240 K, leave
var LOG_GLOBAL_PATH = process.env.LOGFILE_PATH || '/logs/fluent-containers.';
var ROTATE_MB = parseInt(process.env.ROTATE_MB) || 10;
var ROTATE_MAX_FILES = parseInt(process.env.ROTATE_MAX_FILES) || 10;

process.setMaxListeners(0);

function fileLogger(token, podName) {
    if (!token) {
        throw new Error('token not specified')
    }
    if (!podName) {
        throw new Error('podName not specified')
    }

    this.token = token;
    let path = `${LOG_GLOBAL_PATH}${token}.${podName}.to.spl`;
    console.log('path is: ', path);

    this.logger = new winston.Logger({
        transports: [
            new winston.transports.File({
                filename: path,
                zippedArchive: true,
                timestamp: false,
                maxsize: 1024 * 1024 * ROTATE_MB,
                maxFiles: ROTATE_MAX_FILES,
                json: false,
                showLevel: false
            })
        ], exitOnError: false
    })
    this.logger.handleExceptions(new winston.transports.Console({
        humanReadableUnhandledException: true, json: false
    }));

    events.EventEmitter.call(this);
}
util.inherits(fileLogger, events.EventEmitter);

fileLogger.prototype.log = function (level, message, fields, callback) {
    if (fields && fields._type) {
        delete fields._type
    }
    var msg = {
        message: message, severity: level, host: this.hostname, ip: ipAddress
    };
    for (var x in fields) {
        // rename fields for Elasticsearch 2.x
        if (startsWithUnderscore.test(x) || hasDots.test(x)) {
            msg[x.replace(/\./g, '_').replace(/^_+/, '')] = fields[x]
        } else {
            if (!(typeof fields[x] === 'function')) {
                msg[x] = fields[x]
            }
        }
    }
    var _index = this.token;
    if (fields && typeof (fields._index) === 'function') {
        _index = fields._index(msg)
    }
    if (msg.message && Buffer.byteLength(msg.message, 'utf8') > MAX_MESSAGE_FIELD_SIZE) {
        var cutMsg = new Buffer(MAX_MESSAGE_FIELD_SIZE);
        cutMsg.write(msg.message);
        msg.message = cutMsg.toString();
        if (msg.originalLine) {
            // when message is too large and logagent added originalLine,
            // this should be removed to stay under the limits in receiver
            delete msg.originalLine
        }
        msg.logsene_client_warning = 'Warning: message field too large > ' + MAX_MESSAGE_FIELD_SIZE + ' bytes'
        console.log(msg.logsene_client_warning);
    }

    var logRowFormatted = `INDEX: ${_index} | TIMESTAMP: ${msg["@timestamp"].toISOString()} | HOST: ${msg.host} | MESSAGE: ${msg.message} | POD_NAME: ${msg.kubernetes.pod_name} | NAMESPACE: ${msg.kubernetes.namespace} | NAMECONTAIER: ${msg.kubernetes.container_name} | SEVERITY: ${msg.severity}`;
    this.logger.log('info', logRowFormatted);

    this.emit('logged', {msg: msg, _index: _index});
    if (callback) {
        callback(null, msg)
    }
};

module.exports = fileLogger;
<|endoftext|>


<commit_before>var SqlString = exports;

SqlString.escapeId = function (val, forbidQualified) {
  if (forbidQualified) {
    return '`' + val.replace(/`/g, '``') + '`';
  }
  return '`' + val.replace(/`/g, '``').replace(/\./g, '`.`') + '`';
};

SqlString.escape = function(val, stringifyObjects, timeZone, dialect) {
  if (val === undefined || val === null) {
    return 'NULL';
  }

  switch (typeof val) {
    case 'boolean': return (val) ? 'true' : 'false';
    case 'number': return val+'';
  }

  if (val instanceof Date) {
    val = SqlString.dateToString(val, timeZone || "Z");
  }

  if (Buffer.isBuffer(val)) {
    return SqlString.bufferToString(val);
  }

  if (Array.isArray(val)) {
    return SqlString.arrayToList(val, timeZone);
  }

  if (typeof val === 'object') {
    if (stringifyObjects) {
      val = val.toString();
    } else {
      return SqlString.objectToValues(val, timeZone);
    }
  }

  if (dialect == "postgres") {
    // http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
    val = val.replace(/'/g, "''");
  } else {
    val = val.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) {
      switch(s) {
        case "\0": return "\\0";
        case "\n": return "\\n";
        case "\r": return "\\r";
        case "\b": return "\\b";
        case "\t": return "\\t";
        case "\x1a": return "\\Z";
        default: return "\\"+s;
      }
    });
  }
  return "'"+val+"'";
};

SqlString.arrayToList = function(array, timeZone) {
  return array.map(function(v) {
    if (Array.isArray(v)) return '(' + SqlString.arrayToList(v) + ')';
    return SqlString.escape(v, true, timeZone);
  }).join(', ');
};

SqlString.format = function(sql, values, timeZone, dialect) {
  values = [].concat(values);

  return sql.replace(/\?/g, function(match) {
    if (!values.length) {
      return match;
    }

    return SqlString.escape(values.shift(), false, timeZone, dialect);
  });
};

SqlString.dateToString = function(date, timeZone) {
  var dt = new Date(date);

  if (timeZone != 'local') {
    var tz = convertTimezone(timeZone);

    dt.setTime(dt.getTime() + (dt.getTimezoneOffset() * 60000));
    if (tz !== false) {
      dt.setTime(dt.getTime() + (tz * 60000));
    }
  }

  var year   = dt.getFullYear();
  var month  = zeroPad(dt.getMonth() + 1);
  var day    = zeroPad(dt.getDate());
  var hour   = zeroPad(dt.getHours());
  var minute = zeroPad(dt.getMinutes());
  var second = zeroPad(dt.getSeconds());

  return year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second;
};

SqlString.bufferToString = function(buffer) {
  var hex = '';
  try {
    hex = buffer.toString('hex');
  } catch (err) {
    // node v0.4.x does not support hex / throws unknown encoding error
    for (var i = 0; i < buffer.length; i++) {
      var byte = buffer[i];
      hex += zeroPad(byte.toString(16));
    }
  }

  return "X'" + hex+ "'";
};

SqlString.objectToValues = function(object, timeZone) {
  var values = [];
  for (var key in object) {
    var value = object[key];
    if(typeof value === 'function') {
      continue;
    }

    values.push(this.escapeId(key) + ' = ' + SqlString.escape(value, true, timeZone));
  }

  return values.join(', ');
};

function zeroPad(number) {
  return (number < 10) ? '0' + number : number;
}

function convertTimezone(tz) {
  if (tz == "Z") return 0;

  var m = tz.match(/([\+\-\s])(\d\d):?(\d\d)?/);
  if (m) {
    return (m[1] == '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60;
  }
  return false;
}
<commit_msg>Fix string escaping for sqlite<commit_after>var SqlString = exports;

SqlString.escapeId = function (val, forbidQualified) {
  if (forbidQualified) {
    return '`' + val.replace(/`/g, '``') + '`';
  }
  return '`' + val.replace(/`/g, '``').replace(/\./g, '`.`') + '`';
};

SqlString.escape = function(val, stringifyObjects, timeZone, dialect) {
  if (val === undefined || val === null) {
    return 'NULL';
  }

  switch (typeof val) {
    case 'boolean': return (val) ? 'true' : 'false';
    case 'number': return val+'';
  }

  if (val instanceof Date) {
    val = SqlString.dateToString(val, timeZone || "Z");
  }

  if (Buffer.isBuffer(val)) {
    return SqlString.bufferToString(val);
  }

  if (Array.isArray(val)) {
    return SqlString.arrayToList(val, timeZone);
  }

  if (typeof val === 'object') {
    if (stringifyObjects) {
      val = val.toString();
    } else {
      return SqlString.objectToValues(val, timeZone);
    }
  }

  if (dialect === "postgres" || dialect === "sqlite") {
    // http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
    // http://stackoverflow.com/q/603572/130598
    val = val.replace(/'/g, "''");
  } else {
    val = val.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) {
      switch(s) {
        case "\0": return "\\0";
        case "\n": return "\\n";
        case "\r": return "\\r";
        case "\b": return "\\b";
        case "\t": return "\\t";
        case "\x1a": return "\\Z";
        default: return "\\"+s;
      }
    });
  }
  return "'"+val+"'";
};

SqlString.arrayToList = function(array, timeZone) {
  return array.map(function(v) {
    if (Array.isArray(v)) return '(' + SqlString.arrayToList(v) + ')';
    return SqlString.escape(v, true, timeZone);
  }).join(', ');
};

SqlString.format = function(sql, values, timeZone, dialect) {
  values = [].concat(values);

  return sql.replace(/\?/g, function(match) {
    if (!values.length) {
      return match;
    }

    return SqlString.escape(values.shift(), false, timeZone, dialect);
  });
};

SqlString.dateToString = function(date, timeZone) {
  var dt = new Date(date);

  if (timeZone != 'local') {
    var tz = convertTimezone(timeZone);

    dt.setTime(dt.getTime() + (dt.getTimezoneOffset() * 60000));
    if (tz !== false) {
      dt.setTime(dt.getTime() + (tz * 60000));
    }
  }

  var year   = dt.getFullYear();
  var month  = zeroPad(dt.getMonth() + 1);
  var day    = zeroPad(dt.getDate());
  var hour   = zeroPad(dt.getHours());
  var minute = zeroPad(dt.getMinutes());
  var second = zeroPad(dt.getSeconds());

  return year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second;
};

SqlString.bufferToString = function(buffer) {
  var hex = '';
  try {
    hex = buffer.toString('hex');
  } catch (err) {
    // node v0.4.x does not support hex / throws unknown encoding error
    for (var i = 0; i < buffer.length; i++) {
      var byte = buffer[i];
      hex += zeroPad(byte.toString(16));
    }
  }

  return "X'" + hex+ "'";
};

SqlString.objectToValues = function(object, timeZone) {
  var values = [];
  for (var key in object) {
    var value = object[key];
    if(typeof value === 'function') {
      continue;
    }

    values.push(this.escapeId(key) + ' = ' + SqlString.escape(value, true, timeZone));
  }

  return values.join(', ');
};

function zeroPad(number) {
  return (number < 10) ? '0' + number : number;
}

function convertTimezone(tz) {
  if (tz == "Z") return 0;

  var m = tz.match(/([\+\-\s])(\d\d):?(\d\d)?/);
  if (m) {
    return (m[1] == '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60;
  }
  return false;
}
<|endoftext|>


<commit_before>/**
 * Get a value from a property "path" (dot.notation).
 * path('Object.key.key.value'[, {'key': {'key': {'value': 123}}}]);
 *
 * @public
 * @param {string} The path in "dot" notation.
 * @param {array} The data objects, which are used to search the path.
 */
exports.path = function getPathValue (path, scopes, count) {

    if (!path) {
        return;
    }

    // get scope
    count = count || 0;
    var scope = scopes[count];

    // if no scope is found
    if (!scope) {
        return;
    }

    // prepare path
    var o = path;
    path = path.split('.');

    // find keys in paths or return
    for (var i = 0; i < path.length; ++i) {
        if ((scope = scope[path[i]]) === undefined) {
            return scopes[++count] ? getPathValue(o, scopes, count) : undefined;
        }
    }

    return scope;
};

/**
 * Create a flat object {key1: {key2: "value"}} => {"key1.key2": "value"}
 *
 * @public
 * @param {string} The object, which is flattened.
 */
exports.flat = function (object) {
    var output = {};
    var value;
    var newKey;

    // recusrive handler
    function step(obj, prev) {
        for (var key in obj) {
            value = obj[key];
            newKey = prev + key;

            if (typeof value === 'object' && value !== null && !Array.isArray(value)) {

                if (Object.keys(value).length) {
                    step(value, newKey + '.');
                    continue;
                }
            }

            output[newKey] = value;
        }
    }

    // start recursive loop
    step(object, '');

    return output;
};

/**
 * Unflatten dot-notation keys {"key1.key2": "value"} => {key1: {key2: "value"}}
 *
 * @public
 * @param {string} The object, which is unflattened.
 */
exports.deep = function (object) {
    var result = {};
    var parentObj = result;
    var key;
    var subkeys;
    var subkey;
    var last;
    var keys = Object.keys(object);

    for (var i = 0; i < keys.length; ++i) {

        key = keys[i];
        subkeys = key.split('.');
        last = subkeys.pop();

        for (var ii = 0; ii < subkeys.length; ++ii) {
            subkey = subkeys[ii];
            parentObj[subkey] = parentObj[subkey] === undefined ? ((subkeys[ii + 1] || last) === "0" ? [] : {}) : parentObj[subkey];
            parentObj = parentObj[subkey];
        }

        parentObj[last] = object[key];
        parentObj = result;
    }

    return result;
};

/**
 * Retruns a random string.
 *
 * @public
 * @param {number} The length of the random string.
 */
exports.uid = function (len) {
    len = len || 23;
    for (var i = 0, random = ''; i < len; ++i) {
        random += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[0 | Math.random() * 62];
    }
    return random;
};

/**
 * Check the role access for a cache item.
 *
 * @public
 * @param {object} The cached item.
 * @param {string} The role name.
 */
exports.access = function (item, role) {

    var roles = item && (item._roles || item.roles || {}) || {};
    role = role && role.role ? role.role : role;

    if (roles['*'] || (typeof role === 'string' && roles[role])) {
        return true;
    }
};

// client side process.nextTick hack
// TODO creat PR to replace setTimout in nextTick for https://github.com/defunctzombie/node-process
/*if (typeof window !== 'undefined') {

    var queue = [];

    window.addEventListener('message', function (event) {
        var source = event.source;

        if ((source === window || source === null) && event.data === '_F_nT') {
            event.stopPropagation();
            if (queue.length > 0) {
                (queue.shift())();
            }
        }
    }, true);
}

exports.nextTick = function (fn) {

    var args = [].slice.apply(arguments);

    if (global.process) {
        return process.nextTick.apply(this, args);
    }

    // browser
    queue.push(function () {
        fn.apply(this, args.slice(1));
    });
    window.postMessage('_F_nT', '*');
};*/
<commit_msg>uid method is only needed for ws messages<commit_after>/**
 * Get a value from a property "path" (dot.notation).
 * path('Object.key.key.value'[, {'key': {'key': {'value': 123}}}]);
 *
 * @public
 * @param {string} The path in "dot" notation.
 * @param {array} The data objects, which are used to search the path.
 */
exports.path = function getPathValue (path, scopes, count) {

    if (!path) {
        return;
    }

    // get scope
    count = count || 0;
    var scope = scopes[count];

    // if no scope is found
    if (!scope) {
        return;
    }

    // prepare path
    var o = path;
    path = path.split('.');

    // find keys in paths or return
    for (var i = 0; i < path.length; ++i) {
        if ((scope = scope[path[i]]) === undefined) {
            return scopes[++count] ? getPathValue(o, scopes, count) : undefined;
        }
    }

    return scope;
};

/**
 * Create a flat object {key1: {key2: "value"}} => {"key1.key2": "value"}
 *
 * @public
 * @param {string} The object, which is flattened.
 */
exports.flat = function (object) {
    var output = {};
    var value;
    var newKey;

    // recusrive handler
    function step(obj, prev) {
        for (var key in obj) {
            value = obj[key];
            newKey = prev + key;

            if (typeof value === 'object' && value !== null && !Array.isArray(value)) {

                if (Object.keys(value).length) {
                    step(value, newKey + '.');
                    continue;
                }
            }

            output[newKey] = value;
        }
    }

    // start recursive loop
    step(object, '');

    return output;
};

/**
 * Unflatten dot-notation keys {"key1.key2": "value"} => {key1: {key2: "value"}}
 *
 * @public
 * @param {string} The object, which is unflattened.
 */
exports.deep = function (object) {
    var result = {};
    var parentObj = result;
    var key;
    var subkeys;
    var subkey;
    var last;
    var keys = Object.keys(object);

    for (var i = 0; i < keys.length; ++i) {

        key = keys[i];
        subkeys = key.split('.');
        last = subkeys.pop();

        for (var ii = 0; ii < subkeys.length; ++ii) {
            subkey = subkeys[ii];
            parentObj[subkey] = parentObj[subkey] === undefined ? ((subkeys[ii + 1] || last) === "0" ? [] : {}) : parentObj[subkey];
            parentObj = parentObj[subkey];
        }

        parentObj[last] = object[key];
        parentObj = result;
    }

    return result;
};

/**
 * Check the role access for a cache item.
 *
 * @public
 * @param {object} The cached item.
 * @param {string} The role name.
 */
exports.access = function (item, role) {

    var roles = item && (item._roles || item.roles || {}) || {};
    role = role && role.role ? role.role : role;

    if (roles['*'] || (typeof role === 'string' && roles[role])) {
        return true;
    }
};

// client side process.nextTick hack
// TODO creat PR to replace setTimout in nextTick for https://github.com/defunctzombie/node-process
/*if (typeof window !== 'undefined') {

    var queue = [];

    window.addEventListener('message', function (event) {
        var source = event.source;

        if ((source === window || source === null) && event.data === '_F_nT') {
            event.stopPropagation();
            if (queue.length > 0) {
                (queue.shift())();
            }
        }
    }, true);
}

exports.nextTick = function (fn) {

    var args = [].slice.apply(arguments);

    if (global.process) {
        return process.nextTick.apply(this, args);
    }

    // browser
    queue.push(function () {
        fn.apply(this, args.slice(1));
    });
    window.postMessage('_F_nT', '*');
};*/
<|endoftext|>


<commit_before>var fsPath = require('path')

function streamFile(req, res, next, gfs) {
	var path = req.path.replace(/^\/data/, '')
	var extname = fsPath.extname(path)
	var model = path.split('/')[1]
	var cacheControl = 'public'

	switch(model) {
		case 'dist':
		case 'graph':
			cacheControl = 'public'
			break;
	}

	return gfs.stat(path)
	.then(function(stat) {
		if (!stat)
			return res.status(404).send();

		if (req.header('If-None-Match') === stat.md5)
			return res.status(304).send();

		if (req.headers.range) {
			// stream partial file range
			var parts = req.headers.range.replace(/bytes[=:]/, "").split("-");
			var partialstart = parts[0];
			var partialend = parts[1];

			// start&end offset are inclusive, end is optional
			var start = parseInt(partialstart, 10);
			var end = partialend ? parseInt(partialend, 10) : (stat.length - 1);

			var chunksize = (end - start) + 1;

			res.writeHeader(206, {
				'Content-Range': 'bytes ' + start + '-' + end + '/' + stat.length,
				'Accept-Ranges': 'bytes',
				'Cache-Control': cacheControl,
				'Content-Length': chunksize,
				'Expires': 'Sun, 17-Jan-2038 19:14:07 GMT',
				'Content-Type': stat.contentType
			});

			var range = {startPos: start, endPos: end};
			gfs.createReadStream(path, range)
			.on('error', next)
			.pipe(res);
		} else {
			// stream whole file in a single request
			res.header('Content-Type', stat.contentType);

			// only accept range-requests on audio and video
			var rangeableTypes = ['.mp3', '.m4a', '.ogg', '.mp4', '.ogm', '.ogv']
			if (rangeableTypes.indexOf(extname) !== -1)
					res.header('Accept-Ranges', 'bytes')

			res.header('ETag', stat.md5);
			res.header('Content-Length', stat.length)
			res.header('Cache-Control', cacheControl)
			res.header('Expires', 'Sun, 17-Jan-2038 19:14:07 GMT')

			gfs.createReadStream(path)
			.on('error', next)
			.pipe(res)

		}
	})
}

module.exports = streamFile
<commit_msg>improve streamFile cache settings<commit_after>var fsPath = require('path')

function streamFile(req, res, next, gfs) {
	var path = req.path.replace(/^\/data/, '')
	var extname = fsPath.extname(path)
	var model = path.split('/')[1]
	var cacheControl
	var expires = ''

	switch(model) {
		case 'dist':
		case 'graph':
			// minimal caching, please
			cacheControl = 'public, must-revalidate'
			break;
		default:
			// strong caching
 			cacheControl = 'public'
 			expires = 'Sun, 17-Jan-2038 19:14:07 GMT'
 			break;
	}

	return gfs.stat(path)
	.then(function(stat) {
		if (!stat)
			return res.status(404).send();

		if (req.header('If-None-Match') === stat.md5)
			return res.status(304).send();

		res.header('Content-Type', stat.contentType)
		res.header('Cache-Control', cacheControl)

		if (expires)
			res.header('Expires', expires)

		if (req.headers.range) {
			// stream partial file range
			var parts = req.headers.range.replace(/bytes[=:]/, "").split("-");
			var partialstart = parts[0];
			var partialend = parts[1];

			// start&end offset are inclusive, end is optional
			var start = parseInt(partialstart, 10);
			var end = partialend ? parseInt(partialend, 10) : (stat.length - 1);

			var chunksize = (end - start) + 1;

			res.header('Content-Range', 'bytes ' + start + '-' + end + '/' + stat.length);
			res.header('Content-Length', chunksize)
			res.header('Accept-Ranges', 'bytes')

			res.status(206);

			var range = {startPos: start, endPos: end};
			gfs.createReadStream(path, range)
			.on('error', next)
			.pipe(res);
		} else {
			// stream whole file in a single request

			// only accept range-requests on audio and video
			var rangeableTypes = ['.mp3', '.m4a', '.ogg', '.mp4', '.ogm', '.ogv']
			if (rangeableTypes.indexOf(extname) !== -1)
					res.header('Accept-Ranges', 'bytes')

			res.header('ETag', stat.md5);
			res.header('Content-Length', stat.length)
			res.header('Cache-Control', cacheControl)
			res.header('Expires', expires)

			gfs.createReadStream(path)
			.on('error', next)
			.pipe(res)

		}
	})
}

module.exports = streamFile
<|endoftext|>


<commit_before>"use strict";
const github = require("simple-github");
const auth = require("./auth");

class GithubAPI {
    constructor(pr, options) {
        this.pr = pr;
        this.options = options || {};
    }

    requestAuthHeaders() {
        return auth(this.pr.installation);
    }

    setAuthHeaders(headers) {
        this._gh = github({ headers: headers, debug: this.options.debug });
    }

    request(url, options, body) {
        return this._gh.request(url, this.getRequestOptions(options, body));
    }

    getRequestOptions(_options, body) {
        _options = _options || {};
        let options = {
            owner: this.pr.owner,
            repo: this.pr.repo,
            number: this.pr.number
        };
        for (var k in _options) {
            options[k] = _options[k];
        }
        if (body) options.body = body;
        return options;
     }
}

module.exports = GithubAPI;<commit_msg>Temp log<commit_after>"use strict";
const github = require("simple-github");
const auth = require("./auth");

class GithubAPI {
    constructor(pr, options) {
        this.pr = pr;
        this.options = options || {};
    }

    requestAuthHeaders() {
        return auth(this.pr.installation);
    }

    setAuthHeaders(headers) {
        this._gh = github({ headers: headers, debug: this.options.debug });
    }

    request(url, options, body) {
        return this._gh.request(url, this.getRequestOptions(options, body));
    }

    getRequestOptions(_options, body) {
        _options = _options || {};
        let options = {
            owner: this.pr.owner,
            repo: this.pr.repo,
            number: this.pr.number
        };
        for (var k in _options) {
            options[k] = _options[k];
        }
        if (body) {
            options.body = body;
            console.log("request body:", JSON.stringify(options.body, null, 4));
        }
        return options;
     }
}

module.exports = GithubAPI;<|endoftext|>


<commit_before>/***
*
* styleguide.js
*
* Parses CSS/LESS/SASS files with KSS notation and generates a styleguide
*
* Heavily influenced by node-kss and gulp-kss projects
*
***/

'use strict';

var path = require('path'),
  jade = require('jade'),
  through = require('through2'),
  gulp = require('gulp'),
  rename = require('gulp-rename'),
  mustache = require('gulp-mustache'),
  postcss = require('gulp-postcss'),
  replace = require('gulp-replace'),
  minimatch = require('minimatch'),
  fs = require('fs-extra'),
  File = require('vinyl'),
  LocalCache = require('node-localcache'),
  crypto = require('crypto'),
  Q = require('q'),
  _ = require('lodash'),
  markdown = require('./modules/markdown'),
  atRules = require('./modules/at-rules'),
  variableParser = require('./modules/variable-parser'),
  kssParser = require('./modules/kss-parser'),
  angularFiles = require('./modules/angular-files'),
  pseudoSelectors = require('./modules/pseudo-selectors'),
  wrapperMarkup = require('./modules/wrapper-markup'),
  sectionReferences = require('./modules/section-references'),
  common = require('./modules/common'),
  addSection = require('./modules/helpers/add-section'),
  sgServer = require('./server'),
  distPath = path.join(__dirname, 'dist'),
  fileHashes = {},
  jadeCache,
  serverInstance,
  bemtoPath = require.resolve('bemto.jade');

function socketIsOpen() {
  return serverInstance && serverInstance.io;
}

function emitProgressStart() {
  if (socketIsOpen()) {
    serverInstance.io.emitProgressStart();
  }
}

function emitProgressEnd() {
  if (socketIsOpen()) {
    serverInstance.io.emitProgressEnd();
  }
}

function emitCompileError(error) {
  if (socketIsOpen()) {
    serverInstance.io.emitCompileError(error);
  }
}

function emitCompileSuccess() {
  if (socketIsOpen()) {
    serverInstance.io.emitCompileSuccess();
  }
}

function groupModuleFiles(allModules) {
  // Group modules by module name
  var namedModules = _.groupBy(allModules, function(module) {
    return module.name;
  });

  // Commbile files from every module that has the same name
  return _.map(namedModules, function(modules, moduleName) {
    var files = _.uniq(_.reduce(modules, function(files, singleModule) {
      return files.concat(singleModule.files);
    }, []));
    return {
      name: moduleName,
      files: files
    };
  });
}

function generateJadeMarkup(json, options) {
  jadeCache = new LocalCache(path.resolve(options.rootPath, 'jadecache.json'));

  var rootPath = path.resolve(__dirname, '../');
  rootPath = path.relative(rootPath, bemtoPath);

  // path to bemto
  var bemtoinclude = 'include ./' + rootPath + '\n',
      jadeOptions = {
        filename: __dirname,
        basedir: process.cwd(),
        compileDebug: true,
        pretty: true
      };

  _.each(json.sections, convertToJade);

  var parentref;
  function convertToJade(section) {
    var cacheKey, jadestring, md5;

    if (!section.markup) { return; }
    if (/<[a-z][\s\S]*>/i.test(section.markup)) { return; }

    section.markupJade = section.markup;

    if (section.reference) {
      parentref = section.reference;
      cacheKey = section.reference;
    } else {
      cacheKey = parentref + ' - ' + section.name;
    }

    jadestring = bemtoinclude + section.markup;
    md5 = crypto.createHash('md5').update(jadestring).digest('hex');

    if (jadeCache.getItem(cacheKey) && jadeCache.getItem(cacheKey).key === md5) {
      section.markup = jadeCache.getItem(cacheKey).value;
    } else {
      section.markup = jade.render(jadestring, jadeOptions);
      jadeCache.setItem(cacheKey, {
        key : md5,
        value : section.markup
      });
    }

    if (section.modifiers) {
      _.each(section.modifiers, convertToJade);
    }
  }
}

function generateSectionWrapperMarkup(json) {
  json.section = wrapperMarkup.generateSectionWrapperMarkup(json.sections);
}

function replaceSectionReferences(json) {
  json.sections = sectionReferences.replace(json.sections);
}

function copyUsedOptionsToJsonConfig(opt, json) {
  var used = ['appRoot', 'extraHead', 'beforeBody', 'afterBody', 'commonClass', 'title', 'disableEncapsulation', 'disableHtml5Mode', 'readOnly', 'sideNav', 'additionalNgDependencies'];
  json.config = {};
  used.forEach(function(prop) {
    json.config[prop] = _.cloneDeep(opt[prop]);
  });
  return json;
}

function copyUsedOptionsToInlineJsonConfig(opt, json) {
  var used = ['title', 'disableEncapsulation', 'disableHtml5Mode', 'sideNav', 'additionalNgDependencies'];
  json.config = {};
  used.forEach(function(prop) {
    json.config[prop] = _.cloneDeep(opt[prop]);
  });
  return json;
}

function addFileHashesAndReplaceAbsolutePaths(json) {
  if (!json) {
    return;
  }

  if (json.variables) {
    json.variables.forEach(function(variable) {
      variable.fileHash = fileHashes[variable.file];
      variable.file = basenameOf(variable.file);
    });
  }

  if (json.variables) {
    json.sections.forEach(function(section) {
      section.fileHash = fileHashes[section.file];
      section.file = basenameOf(section.file);
    });
  }
}

function basenameOf(p) {
  return path.basename(p);
}

function appendUsedVariablesToEachBlock(opt, styleguide) {
  // Go trough every styleguide style block and find used variables
  styleguide.sections.forEach(function(section) {
    // It is possible that the CSS is not valid anymore after it is splitted to sections
    // We need to catch possible parsing errors
    try {
      if (section.css) {
        section.variables = variableParser.findVariables(section.css, section.syntax, opt);
      }
    } catch (e) {
      console.error('Could not parse used variables from section', section.reference + ':');
      console.error(e.toString());
    }

    variableParser.findModifierVariables(section.modifiers).forEach(function(varName) {
      if (!section.variables || section.variables.indexOf(varName) === -1) {
        section.variables = section.variables || [];
        section.variables.push(varName);
      }
    });
    return section;
  });
}

function filterFiles(files, filter) {
  var filtered = {};
  Object.keys(files).forEach(function(filePath) {
    if (minimatch(filePath, filter)) {
      filtered[filePath] = files[filePath];
    }
  });
  return filtered;
}

module.exports.generate = function(options) {
  var opt = common.sanitizeOptions(options),
    filesBuffer = {},
    throughOpts = {
      objectMode: true,
      allowHalfOpen: false
    },
    styleguideProcessors = _.extend(
        {
          10: replaceSectionReferences,
          20: generateSectionWrapperMarkup
        },
        opt.styleguideProcessors
    );

  function bufferFileContents(file, enc, done) {
    if (file.isNull()) {
      return;
    }
    if (file.isStream()) {
      return console.error('Styleguide does not support streams!');
    }

    // Exclude empty files
    if (file.contents.toString('utf8') !== '') {
      filesBuffer[file.path] = file.contents.toString('utf8');
      var hash = crypto.createHash('md5').update(file.path).digest('hex');
      fileHashes[file.path] = hash;
      fileHashes[hash] = file.path;
    }

    // Make sure file goes through the next gulp plugin
    // jshint -W040
    this.push(file);
    // jshint +W040
    done();
  }

  emitProgressStart();

  // A stream through which each file will pass
  return through(throughOpts, bufferFileContents, function(callback) {
      var _this = this,
        // Styleguide object to be built
        styleguide = {},
        // Parse KSS sections
        parseKSSPromise = kssParser.parseKssSections(filesBuffer, opt.kssOpt, opt.parsers),
        // Filter variable files
        // File paths are full absolute paths so we need to add wildcard prefix
        // Also empty wildcard should return all files
        variableFiles = opt.styleVariables ? filterFiles(filesBuffer, '**/' + opt.styleVariables) : filesBuffer,
        // Parse variable decarations from files
        parseVariablesPromise = variableParser.parseVariableDeclarationsFromFiles(variableFiles, opt);

      Q.all([parseKSSPromise, parseVariablesPromise]).spread(function(sections, variables) {
        styleguide.sections = sections;
        styleguide.variables = variables;

        // Extend config with Angular directives declared in KSS
        opt.filesConfig = angularFiles.add(opt.filesConfig, sections);

        function pushAllFiles() {
          return through.obj(function(file, enc, cb) {
            _this.push(file);
            cb();
          });
        }

        function processOverviewMarkdown(opt) {
          return Q.Promise(function(resolve) {
            if (!opt.overviewPath) {
              resolve();
            }
            markdown.getStream(opt.overviewPath)
              .pipe(rename(function(path) {
                path.basename = 'overview';
                path.extname = '.html';
              }))
              .pipe(pushAllFiles())
              .on('finish', resolve);
          });
        }

        options.enableJade && generateJadeMarkup(styleguide, options);

        _.each(styleguideProcessors, function(processor) {
          processor(styleguide);
        });

        copyUsedOptionsToJsonConfig(opt, styleguide);
        appendUsedVariablesToEachBlock(opt, styleguide);
        addFileHashesAndReplaceAbsolutePaths(styleguide);

        // Create JSON containing KSS data
        _this.push(new File({
          path: 'styleguide.json',
          contents: new Buffer(JSON.stringify(styleguide))
        }));

        var stylesCompiled,
          overviewProcessed,
          filesCopied,
          indexHtmlProcessed;

        overviewProcessed = processOverviewMarkdown(opt);

        var cssSrc = [distPath + '/css/styleguide-app.css', distPath + '/css/styleguide_helper_elements.css'];

        // Copy all files (except index.html) from dist from to output stream
        filesCopied = Q.Promise(function(resolve) {
          //gulp.src([distPath + '/**', '!' + distPath + '/index.html', + '!' + distPath + '**/*.css'])
          var copySrc = [distPath + '/**', '!' + distPath + '/index.html'];

          copySrc = copySrc.concat(cssSrc.map(function(item){
            return '!' + item;
          }));

          gulp.src(copySrc)
            .pipe(pushAllFiles())
            .on('finish', resolve);
        });

        // Compile UI styles
        stylesCompiled = Q.Promise(function(resolve) {

          // If we have custom colors defined. Overwrite default empty file
          if (opt.customColors) {
            try {
              fs.copySync(opt.customColors, distPath + '/css/_styleguide_custom_variables.css');
              // Add directory of custom colors file to SASS includePaths
              // This allows to including external files in custom variable file
            } catch (err) {
              console.log('ERROR: customColors option is defined but the file is not copyable. Using default colors.');
            }
          }
          gulp.src(cssSrc)
            .pipe(replace('{{{appRoot}}}', opt.appRoot))
            .pipe(postcss([
              require('postcss-partial-import'),
              require('postcss-mixins'),
              require('cssnext'),
              require('postcss-advanced-variables'),
              require('postcss-conditionals'),
              require('postcss-color-function'),
              require('postcss-color-alpha'),
              require('postcss-nested'),
              require('postcss-custom-media'),
              require('autoprefixer'),
              require('postcss-inline-comment')
            ]))
            // Strings hack
            .pipe(replace(/url\((.*)\)/g, function(replacement, parsedPath) {
              return 'url(\'' + parsedPath.replace(/'/g, '') + '\')';
            }))
            .pipe(pushAllFiles())
            .on('finish', resolve);
        });

        // Process index.html
        indexHtmlProcessed = Q.Promise(function(resolve) {
          gulp.src([distPath + '/index.html'])
            .pipe(mustache({
              title: opt.title,
              extraHead: opt.extraHead,
              beforeBody: opt.beforeBody,
              afterBody: opt.afterBody,
              sideNav: opt.sideNav,
              styleguideConfig: JSON.stringify(copyUsedOptionsToInlineJsonConfig(opt, {}).config),
              appRoot: opt.appRoot,
              socketIo: opt.server,
              filesConfig: JSON.stringify(groupModuleFiles(opt.filesConfig))
            }))
            .pipe(pushAllFiles())
            .on('finish', resolve);
        });

        Q.all([stylesCompiled, overviewProcessed, filesCopied, indexHtmlProcessed])
          .then(function() {
            if (opt.server) {
              opt.fileHashes = fileHashes;
              startServer(opt);
            }
            emitCompileSuccess();
          })
          .catch(function(error) {
            console.error(error.stack || error.message);
            emitCompileError(error);
          })
          .finally(callback);
      }).catch(function(error) {
        console.error(error.stack || error.message);
        emitCompileError(error);
        callback();
      });
    }
  ).on('error', console.error.bind(console)).on('end', function() {
    emitProgressEnd();
  });
};

/* Build pseudo styles and ay rules from preprocessed CSS styles */

module.exports.applyStyles = function() {
  var throughOpts = {
    objectMode: true,
    allowHalfOpen: false
  }, stylesBuffer = '',
    pseudoStylesBuffer = '',
    atRulesBuffer = '';

  function bufferFileContents(file, enc, done) {
    var pseudoStylesPromise,
      atRulesPromise;

    // Make sure file goes through the next gulp plugin
    // jshint -W040
    this.push(file);
    // jshint +W040

    // Process only CSS files
    // For example gulp-ruby-sass generatsd sourcemaps to the stream that we do not want to include
    if (path.extname(file.path) !== '.css') {
      done();
      return;
    }

    // Add styles to common stylesheet
    stylesBuffer += file.contents.toString();

    // Create stylesheet that contains pseudo styles
    pseudoStylesPromise = Q.Promise(function(resolve) {
      pseudoStylesBuffer += pseudoSelectors.stylesFromString(file.contents.toString(), {
        source: file.path
      });
      resolve();
    });

    // Create stylesheet that contains at-rules
    atRulesPromise = Q.Promise(function(resolve) {
      atRulesBuffer += atRules.stylesFromString(file.contents.toString(), {
        source: file.path
      });
      resolve();
    });

    Q.all([pseudoStylesPromise, atRulesPromise]).then(function() {
      done();
    });
  }

  return through(throughOpts, bufferFileContents, function(cb) {
    this.push(new File({
      path: 'styleguide.css',
      contents: new Buffer(stylesBuffer)
    }));

    this.push(new File({
      path: 'styleguide_pseudo_styles.css',
      contents: new Buffer(pseudoStylesBuffer)
    }));

    this.push(new File({
      path: 'styleguide_at_rules.css',
      contents: new Buffer(atRulesBuffer)
    }));

    cb();
  }).on('end', function() {
    if (socketIsOpen()) {
      serverInstance.io.emitStylesChanged();
    }
  });
};

/* Built-in server */

module.exports.server = function(options) {
  return startServer(options);
};

function startServer(options) {
  // Ignore start server if we already have instance running
  if (!serverInstance) {
    var port = options.port;
    serverInstance = sgServer(options);
    serverInstance.app.set('port', port);
    serverInstance.server.listen(serverInstance.app.get('port'), function() {
      console.log('Express server listening on port ' + serverInstance.server.address().port);
    }).on('error', function(error) {
      if (error.code === 'EADDRINUSE') {
        console.error('Port:' + port + ' is already in use.');
        console.error('Please provide port using --port <port>');
      }
    });
  }
  return serverInstance;
}

module.exports.addSection = addSection;
<commit_msg>Allow to run multiple styleguide servers on different ports. Fix #1019<commit_after>/***
*
* styleguide.js
*
* Parses CSS/LESS/SASS files with KSS notation and generates a styleguide
*
* Heavily influenced by node-kss and gulp-kss projects
*
***/

'use strict';

var path = require('path'),
  jade = require('jade'),
  through = require('through2'),
  gulp = require('gulp'),
  rename = require('gulp-rename'),
  mustache = require('gulp-mustache'),
  postcss = require('gulp-postcss'),
  replace = require('gulp-replace'),
  minimatch = require('minimatch'),
  fs = require('fs-extra'),
  File = require('vinyl'),
  LocalCache = require('node-localcache'),
  crypto = require('crypto'),
  Q = require('q'),
  _ = require('lodash'),
  markdown = require('./modules/markdown'),
  atRules = require('./modules/at-rules'),
  variableParser = require('./modules/variable-parser'),
  kssParser = require('./modules/kss-parser'),
  angularFiles = require('./modules/angular-files'),
  pseudoSelectors = require('./modules/pseudo-selectors'),
  wrapperMarkup = require('./modules/wrapper-markup'),
  sectionReferences = require('./modules/section-references'),
  common = require('./modules/common'),
  addSection = require('./modules/helpers/add-section'),
  sgServer = require('./server'),
  distPath = path.join(__dirname, 'dist'),
  fileHashes = {},
  jadeCache,
  serverInstance,
  bemtoPath = require.resolve('bemto.jade');

function socketIsOpen() {
  return serverInstance && serverInstance.io;
}

function emitProgressStart() {
  if (socketIsOpen()) {
    serverInstance.io.emitProgressStart();
  }
}

function emitProgressEnd() {
  if (socketIsOpen()) {
    serverInstance.io.emitProgressEnd();
  }
}

function emitCompileError(error) {
  if (socketIsOpen()) {
    serverInstance.io.emitCompileError(error);
  }
}

function emitCompileSuccess() {
  if (socketIsOpen()) {
    serverInstance.io.emitCompileSuccess();
  }
}

function groupModuleFiles(allModules) {
  // Group modules by module name
  var namedModules = _.groupBy(allModules, function(module) {
    return module.name;
  });

  // Commbile files from every module that has the same name
  return _.map(namedModules, function(modules, moduleName) {
    var files = _.uniq(_.reduce(modules, function(files, singleModule) {
      return files.concat(singleModule.files);
    }, []));
    return {
      name: moduleName,
      files: files
    };
  });
}

function generateJadeMarkup(json, options) {
  jadeCache = new LocalCache(path.resolve(options.rootPath, 'jadecache.json'));

  var rootPath = path.resolve(__dirname, '../');
  rootPath = path.relative(rootPath, bemtoPath);

  // path to bemto
  var bemtoinclude = 'include ./' + rootPath + '\n',
      jadeOptions = {
        filename: __dirname,
        basedir: process.cwd(),
        compileDebug: true,
        pretty: true
      };

  _.each(json.sections, convertToJade);

  var parentref;
  function convertToJade(section) {
    var cacheKey, jadestring, md5;

    if (!section.markup) { return; }
    if (/<[a-z][\s\S]*>/i.test(section.markup)) { return; }

    section.markupJade = section.markup;

    if (section.reference) {
      parentref = section.reference;
      cacheKey = section.reference;
    } else {
      cacheKey = parentref + ' - ' + section.name;
    }

    jadestring = bemtoinclude + section.markup;
    md5 = crypto.createHash('md5').update(jadestring).digest('hex');

    if (jadeCache.getItem(cacheKey) && jadeCache.getItem(cacheKey).key === md5) {
      section.markup = jadeCache.getItem(cacheKey).value;
    } else {
      section.markup = jade.render(jadestring, jadeOptions);
      jadeCache.setItem(cacheKey, {
        key : md5,
        value : section.markup
      });
    }

    if (section.modifiers) {
      _.each(section.modifiers, convertToJade);
    }
  }
}

function generateSectionWrapperMarkup(json) {
  json.section = wrapperMarkup.generateSectionWrapperMarkup(json.sections);
}

function replaceSectionReferences(json) {
  json.sections = sectionReferences.replace(json.sections);
}

function copyUsedOptionsToJsonConfig(opt, json) {
  var used = ['appRoot', 'extraHead', 'beforeBody', 'afterBody', 'commonClass', 'title', 'disableEncapsulation', 'disableHtml5Mode', 'readOnly', 'sideNav', 'additionalNgDependencies'];
  json.config = {};
  used.forEach(function(prop) {
    json.config[prop] = _.cloneDeep(opt[prop]);
  });
  return json;
}

function copyUsedOptionsToInlineJsonConfig(opt, json) {
  var used = ['title', 'disableEncapsulation', 'disableHtml5Mode', 'sideNav', 'additionalNgDependencies'];
  json.config = {};
  used.forEach(function(prop) {
    json.config[prop] = _.cloneDeep(opt[prop]);
  });
  return json;
}

function addFileHashesAndReplaceAbsolutePaths(json) {
  if (!json) {
    return;
  }

  if (json.variables) {
    json.variables.forEach(function(variable) {
      variable.fileHash = fileHashes[variable.file];
      variable.file = basenameOf(variable.file);
    });
  }

  if (json.variables) {
    json.sections.forEach(function(section) {
      section.fileHash = fileHashes[section.file];
      section.file = basenameOf(section.file);
    });
  }
}

function basenameOf(p) {
  return path.basename(p);
}

function appendUsedVariablesToEachBlock(opt, styleguide) {
  // Go trough every styleguide style block and find used variables
  styleguide.sections.forEach(function(section) {
    // It is possible that the CSS is not valid anymore after it is splitted to sections
    // We need to catch possible parsing errors
    try {
      if (section.css) {
        section.variables = variableParser.findVariables(section.css, section.syntax, opt);
      }
    } catch (e) {
      console.error('Could not parse used variables from section', section.reference + ':');
      console.error(e.toString());
    }

    variableParser.findModifierVariables(section.modifiers).forEach(function(varName) {
      if (!section.variables || section.variables.indexOf(varName) === -1) {
        section.variables = section.variables || [];
        section.variables.push(varName);
      }
    });
    return section;
  });
}

function filterFiles(files, filter) {
  var filtered = {};
  Object.keys(files).forEach(function(filePath) {
    if (minimatch(filePath, filter)) {
      filtered[filePath] = files[filePath];
    }
  });
  return filtered;
}

module.exports.generate = function(options) {
  var opt = common.sanitizeOptions(options),
    filesBuffer = {},
    throughOpts = {
      objectMode: true,
      allowHalfOpen: false
    },
    styleguideProcessors = _.extend(
        {
          10: replaceSectionReferences,
          20: generateSectionWrapperMarkup
        },
        opt.styleguideProcessors
    );

  function bufferFileContents(file, enc, done) {
    if (file.isNull()) {
      return;
    }
    if (file.isStream()) {
      return console.error('Styleguide does not support streams!');
    }

    // Exclude empty files
    if (file.contents.toString('utf8') !== '') {
      filesBuffer[file.path] = file.contents.toString('utf8');
      var hash = crypto.createHash('md5').update(file.path).digest('hex');
      fileHashes[file.path] = hash;
      fileHashes[hash] = file.path;
    }

    // Make sure file goes through the next gulp plugin
    // jshint -W040
    this.push(file);
    // jshint +W040
    done();
  }

  emitProgressStart();

  // A stream through which each file will pass
  return through(throughOpts, bufferFileContents, function(callback) {
      var _this = this,
        // Styleguide object to be built
        styleguide = {},
        // Parse KSS sections
        parseKSSPromise = kssParser.parseKssSections(filesBuffer, opt.kssOpt, opt.parsers),
        // Filter variable files
        // File paths are full absolute paths so we need to add wildcard prefix
        // Also empty wildcard should return all files
        variableFiles = opt.styleVariables ? filterFiles(filesBuffer, '**/' + opt.styleVariables) : filesBuffer,
        // Parse variable decarations from files
        parseVariablesPromise = variableParser.parseVariableDeclarationsFromFiles(variableFiles, opt);

      Q.all([parseKSSPromise, parseVariablesPromise]).spread(function(sections, variables) {
        styleguide.sections = sections;
        styleguide.variables = variables;

        // Extend config with Angular directives declared in KSS
        opt.filesConfig = angularFiles.add(opt.filesConfig, sections);

        function pushAllFiles() {
          return through.obj(function(file, enc, cb) {
            _this.push(file);
            cb();
          });
        }

        function processOverviewMarkdown(opt) {
          return Q.Promise(function(resolve) {
            if (!opt.overviewPath) {
              resolve();
            }
            markdown.getStream(opt.overviewPath)
              .pipe(rename(function(path) {
                path.basename = 'overview';
                path.extname = '.html';
              }))
              .pipe(pushAllFiles())
              .on('finish', resolve);
          });
        }

        options.enableJade && generateJadeMarkup(styleguide, options);

        _.each(styleguideProcessors, function(processor) {
          processor(styleguide);
        });

        copyUsedOptionsToJsonConfig(opt, styleguide);
        appendUsedVariablesToEachBlock(opt, styleguide);
        addFileHashesAndReplaceAbsolutePaths(styleguide);

        // Create JSON containing KSS data
        _this.push(new File({
          path: 'styleguide.json',
          contents: new Buffer(JSON.stringify(styleguide))
        }));

        var stylesCompiled,
          overviewProcessed,
          filesCopied,
          indexHtmlProcessed;

        overviewProcessed = processOverviewMarkdown(opt);

        var cssSrc = [distPath + '/css/styleguide-app.css', distPath + '/css/styleguide_helper_elements.css'];

        // Copy all files (except index.html) from dist from to output stream
        filesCopied = Q.Promise(function(resolve) {
          //gulp.src([distPath + '/**', '!' + distPath + '/index.html', + '!' + distPath + '**/*.css'])
          var copySrc = [distPath + '/**', '!' + distPath + '/index.html'];

          copySrc = copySrc.concat(cssSrc.map(function(item){
            return '!' + item;
          }));

          gulp.src(copySrc)
            .pipe(pushAllFiles())
            .on('finish', resolve);
        });

        // Compile UI styles
        stylesCompiled = Q.Promise(function(resolve) {

          // If we have custom colors defined. Overwrite default empty file
          if (opt.customColors) {
            try {
              fs.copySync(opt.customColors, distPath + '/css/_styleguide_custom_variables.css');
              // Add directory of custom colors file to SASS includePaths
              // This allows to including external files in custom variable file
            } catch (err) {
              console.log('ERROR: customColors option is defined but the file is not copyable. Using default colors.');
            }
          }
          gulp.src(cssSrc)
            .pipe(replace('{{{appRoot}}}', opt.appRoot))
            .pipe(postcss([
              require('postcss-partial-import'),
              require('postcss-mixins'),
              require('cssnext'),
              require('postcss-advanced-variables'),
              require('postcss-conditionals'),
              require('postcss-color-function'),
              require('postcss-color-alpha'),
              require('postcss-nested'),
              require('postcss-custom-media'),
              require('autoprefixer'),
              require('postcss-inline-comment')
            ]))
            // Strings hack
            .pipe(replace(/url\((.*)\)/g, function(replacement, parsedPath) {
              return 'url(\'' + parsedPath.replace(/'/g, '') + '\')';
            }))
            .pipe(pushAllFiles())
            .on('finish', resolve);
        });

        // Process index.html
        indexHtmlProcessed = Q.Promise(function(resolve) {
          gulp.src([distPath + '/index.html'])
            .pipe(mustache({
              title: opt.title,
              extraHead: opt.extraHead,
              beforeBody: opt.beforeBody,
              afterBody: opt.afterBody,
              sideNav: opt.sideNav,
              styleguideConfig: JSON.stringify(copyUsedOptionsToInlineJsonConfig(opt, {}).config),
              appRoot: opt.appRoot,
              socketIo: opt.server,
              filesConfig: JSON.stringify(groupModuleFiles(opt.filesConfig))
            }))
            .pipe(pushAllFiles())
            .on('finish', resolve);
        });

        Q.all([stylesCompiled, overviewProcessed, filesCopied, indexHtmlProcessed])
          .then(function() {
            if (opt.server) {
              opt.fileHashes = fileHashes;
              startServer(opt);
            }
            emitCompileSuccess();
          })
          .catch(function(error) {
            console.error(error.stack || error.message);
            emitCompileError(error);
          })
          .finally(callback);
      }).catch(function(error) {
        console.error(error.stack || error.message);
        emitCompileError(error);
        callback();
      });
    }
  ).on('error', console.error.bind(console)).on('end', function() {
    emitProgressEnd();
  });
};

/* Build pseudo styles and ay rules from preprocessed CSS styles */

module.exports.applyStyles = function() {
  var throughOpts = {
    objectMode: true,
    allowHalfOpen: false
  }, stylesBuffer = '',
    pseudoStylesBuffer = '',
    atRulesBuffer = '';

  function bufferFileContents(file, enc, done) {
    var pseudoStylesPromise,
      atRulesPromise;

    // Make sure file goes through the next gulp plugin
    // jshint -W040
    this.push(file);
    // jshint +W040

    // Process only CSS files
    // For example gulp-ruby-sass generatsd sourcemaps to the stream that we do not want to include
    if (path.extname(file.path) !== '.css') {
      done();
      return;
    }

    // Add styles to common stylesheet
    stylesBuffer += file.contents.toString();

    // Create stylesheet that contains pseudo styles
    pseudoStylesPromise = Q.Promise(function(resolve) {
      pseudoStylesBuffer += pseudoSelectors.stylesFromString(file.contents.toString(), {
        source: file.path
      });
      resolve();
    });

    // Create stylesheet that contains at-rules
    atRulesPromise = Q.Promise(function(resolve) {
      atRulesBuffer += atRules.stylesFromString(file.contents.toString(), {
        source: file.path
      });
      resolve();
    });

    Q.all([pseudoStylesPromise, atRulesPromise]).then(function() {
      done();
    });
  }

  return through(throughOpts, bufferFileContents, function(cb) {
    this.push(new File({
      path: 'styleguide.css',
      contents: new Buffer(stylesBuffer)
    }));

    this.push(new File({
      path: 'styleguide_pseudo_styles.css',
      contents: new Buffer(pseudoStylesBuffer)
    }));

    this.push(new File({
      path: 'styleguide_at_rules.css',
      contents: new Buffer(atRulesBuffer)
    }));

    cb();
  }).on('end', function() {
    if (socketIsOpen()) {
      serverInstance.io.emitStylesChanged();
    }
  });
};

/* Built-in server */

module.exports.server = function(options) {
  return startServer(options);
};

function startServer(options) {
  // Ignore start server if we already have instance running on the same port
  if (!serverInstance) {
    serverInstance = {};
  }
  if (!serverInstance[options.port]) {
    var port = options.port;
    serverInstance[port] = sgServer(options);
    serverInstance[port].app.set('port', port);
    serverInstance[port].server.listen(serverInstance[port].app.get('port'), function() {
      console.log('Express server listening on port ' + serverInstance[port].server.address().port);
    }).on('error', function(error) {
      if (error.code === 'EADDRINUSE') {
        console.error('Port:' + port + ' is already in use.');
        console.error('Please provide port using --port <port>');
      }
    });
  }
  return serverInstance[options.port];
}

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


<commit_before>/**
 * Create a new stylesheet
 */
function Stylesheet() {
  var styleElement = document.createElement('style'),
    head = document.getElementsByTagName('head')[0];

  head.appendChild(styleElement);

  // Safari does not see the new stylesheet unless you append something.
  // However!  IE will blow chunks, so ... filter it thusly:
  if (!window.createPopup) {
    styleElement.appendChild(document.createTextNode(''));
  }

  this.sheet = document.styleSheets[document.styleSheets.length - 1];
}

/**
 * Add A rule to the stylesheet
 * @param {string}  selector The CSS selector
 * @param {string}  rules    The CSS rules
 * @param {?number} index    Where to enter in the CSSStyleSheet
 */
Stylesheet.prototype.addRule = function(selector, rules, index) {
  if (!selector || typeof selector !== 'string') {
    throw new Error('selector should be a string!');
  }

  if (!rules || typeof rules !== 'string') {
    throw new Error('rules should be a string!');
  }

  if (typeof index !== 'undefined' && typeof index !== 'number') {
    throw new Error('index should be a number!');
  }

  if (this.sheet.insertRule) {
    this.sheet.insertRule(selector + '{' + rules + '}', index);
  } else {
    this.sheet.addRule(selector, rules, index);
  }
};

/**
 * Delete a rule by the selector
 * @param {string} selector The CSS selector to delete
 */
Stylesheet.prototype.deleteRule = function(selector) {
  if (!selector || typeof selector !== 'string') {
    throw new Error('selector should be a string!');
  }

  var rules = this.sheet.cssRules,
    i = rules.length - 1;

  for (i; i >= 0; i--) {
    if (rules[i].selectorText === selector) {
      this.sheet.deleteRule(i);
    }
  }
};

/**
 * Clear the stylesheet and delete all rules
 */
Stylesheet.prototype.clear = function() {
  for (var i = this.sheet.cssRules.length - 1; i >= 0; i--) {
    this.sheet.deleteRule(i);
  }
};

// make module Node.js compatible
if (typeof module === "object") { // eslint-disable-line
  module.exports = Stylesheet;
}
<commit_msg>fix: always pass an index to insertRule<commit_after>/**
 * Create a new stylesheet
 */
function Stylesheet() {
  var styleElement = document.createElement('style'),
    head = document.getElementsByTagName('head')[0];

  head.appendChild(styleElement);

  // Safari does not see the new stylesheet unless you append something.
  // However!  IE will blow chunks, so ... filter it thusly:
  if (!window.createPopup) {
    styleElement.appendChild(document.createTextNode(''));
  }

  this.sheet = document.styleSheets[document.styleSheets.length - 1];
}

/**
 * Add A rule to the stylesheet
 * @param {string}  selector The CSS selector
 * @param {string}  rules    The CSS rules
 * @param {?number} index    Where to enter in the CSSStyleSheet
 */
Stylesheet.prototype.addRule = function(selector, rules, index) {
  if (!selector || typeof selector !== 'string') {
    throw new Error('selector should be a string!');
  }

  if (!rules || typeof rules !== 'string') {
    throw new Error('rules should be a string!');
  }

  if (typeof index !== 'undefined' && typeof index !== 'number') {
    throw new Error('index should be a number!');
  }

  if (this.sheet.insertRule) {
    this.sheet.insertRule(selector + '{' + rules + '}', index || 0);
  } else {
    this.sheet.addRule(selector, rules, index);
  }
};

/**
 * Delete a rule by the selector
 * @param {string} selector The CSS selector to delete
 */
Stylesheet.prototype.deleteRule = function(selector) {
  if (!selector || typeof selector !== 'string') {
    throw new Error('selector should be a string!');
  }

  var rules = this.sheet.cssRules,
    i = rules.length - 1;

  for (i; i >= 0; i--) {
    if (rules[i].selectorText === selector) {
      this.sheet.deleteRule(i);
    }
  }
};

/**
 * Clear the stylesheet and delete all rules
 */
Stylesheet.prototype.clear = function() {
  for (var i = this.sheet.cssRules.length - 1; i >= 0; i--) {
    this.sheet.deleteRule(i);
  }
};

// make module Node.js compatible
if (typeof module === "object") { // eslint-disable-line
  module.exports = Stylesheet;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Resin.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const electron = require('electron');
const path = require('path');
const elevate = require('../src/elevate');
const packageJSON = require('../../package.json');
let mainWindow = null;

electron.app.on('window-all-closed', electron.app.quit);

electron.app.on('ready', function() {

  // No menu bar
  electron.Menu.setApplicationMenu(null);

  elevate.require(electron.app, packageJSON.displayName, function(error) {

    if (error) {
      electron.dialog.showErrorBox('Elevation Error', error.message);
      return process.exit(1);
    }

    mainWindow = new electron.BrowserWindow({
      width: 800,
      height: 380,
      useContentSize: true,
      show: false,
      resizable: false,
      fullscreen: false,
      titleBarStyle: 'hidden-inset',
      icon: path.join(__dirname, '..', '..', 'assets', 'icon.png')
    });

    // Prevent flash of white when starting the application
    // https://github.com/atom/electron/issues/2172
    mainWindow.webContents.on('did-finish-load', function() {

      // The flash of white is still present for a very short
      // while after the WebView reports it finished loading
      setTimeout(function() {
        mainWindow.show();
      }, 100);

    });

    mainWindow.on('closed', function() {
      mainWindow = null;
    });

    // For some reason, Electron shortcuts are registered
    // globally, which means that the app listers for shorcuts
    // even if its not currently focused, potentially interferring
    // with shorcuts registered by other applications.
    // As a workaround, we register all shortcuts when the windows
    // gains focus, and unregister them when the windows loses focus.
    // See http://electron.atom.io/docs/api/global-shortcut/

    mainWindow.on('focus', function() {
      electron.globalShortcut.register('CmdOrCtrl+Alt+I', function() {
        mainWindow.webContents.openDevTools();
      });
    });

    mainWindow.on('blur', function() {
      electron.globalShortcut.unregisterAll();
    });

    mainWindow.loadURL(`file://${path.join(__dirname, 'index.html')}`);
  });
});

<commit_msg>Prevent exernal resources from being loaded by the WebView (#456)<commit_after>/*
 * Copyright 2016 Resin.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const electron = require('electron');
const path = require('path');
const elevate = require('../src/elevate');
const packageJSON = require('../../package.json');
let mainWindow = null;

electron.app.on('window-all-closed', electron.app.quit);

electron.app.on('ready', function() {

  // No menu bar
  electron.Menu.setApplicationMenu(null);

  elevate.require(electron.app, packageJSON.displayName, function(error) {

    if (error) {
      electron.dialog.showErrorBox('Elevation Error', error.message);
      return process.exit(1);
    }

    mainWindow = new electron.BrowserWindow({
      width: 800,
      height: 380,
      useContentSize: true,
      show: false,
      resizable: false,
      fullscreen: false,
      titleBarStyle: 'hidden-inset',
      icon: path.join(__dirname, '..', '..', 'assets', 'icon.png')
    });

    // Prevent flash of white when starting the application
    // https://github.com/atom/electron/issues/2172
    mainWindow.webContents.on('did-finish-load', function() {

      // The flash of white is still present for a very short
      // while after the WebView reports it finished loading
      setTimeout(function() {
        mainWindow.show();
      }, 100);

    });

    mainWindow.on('closed', function() {
      mainWindow = null;
    });

    // For some reason, Electron shortcuts are registered
    // globally, which means that the app listers for shorcuts
    // even if its not currently focused, potentially interferring
    // with shorcuts registered by other applications.
    // As a workaround, we register all shortcuts when the windows
    // gains focus, and unregister them when the windows loses focus.
    // See http://electron.atom.io/docs/api/global-shortcut/

    mainWindow.on('focus', function() {
      electron.globalShortcut.register('CmdOrCtrl+Alt+I', function() {
        mainWindow.webContents.openDevTools();
      });
    });

    mainWindow.on('blur', function() {
      electron.globalShortcut.unregisterAll();
    });

    // Prevent external resources from being loaded (like images)
    // when dropping them on the WebView.
    // See https://github.com/electron/electron/issues/5919
    mainWindow.webContents.on('will-navigate', function(event) {
      event.preventDefault();
    });

    mainWindow.loadURL(`file://${path.join(__dirname, 'index.html')}`);
  });
});

<|endoftext|>