<commit_before>var hash = require('./murmerhash');
var Promise = null;
var isCordova = typeof cordova !== 'undefined';

/* Cordova File Cache x */
function FileCache(options){
  var self = this;
  // cordova-promise-fs
  this._fs = options.fs;
  if(!this._fs) {
    throw new Error('Missing required option "fs". Add an instance of cordova-promise-fs.');
  }
  // Use Promises from fs.
  Promise = this._fs.Promise;

  // 'mirror' mirrors files structure from "serverRoot" to "localRoot"
  // 'hash' creates a 1-deep filestructure, where the filenames are hashed server urls (with extension)
  this._mirrorMode = options.mode !== 'hash';
  this._retry = options.retry || [500,1500,8000];
  this._cacheBuster = !!options.cacheBuster;

  // normalize path
  this.localRoot = this._fs.normalize(options.localRoot || 'data');
  this.serverRoot = this._fs.normalize(options.serverRoot || '');

  // set internal variables
  this._downloading = [];    // download promises
  this._added = [];          // added files
  this._cached = {};         // cached files

  // list existing cache contents
  this.ready = this._fs.ensure(this.localRoot)
  .then(function(entry){
    self.localInternalURL = isCordova? entry.toInternalURL(): entry.toURL();
    self.localUrl = entry.toURL();
    return self.list();
  });
}

FileCache.hash = hash;

/**
 * Helper to cache all 'internalURL' and 'URL' for quick synchronous access
 * to the cached files.
 */
FileCache.prototype.list = function list(){
  var self = this;
  return new Promise(function(resolve,reject){
    self._fs.list(self.localRoot,'rfe').then(function(entries){
      self._cached = {};
      entries = entries.map(function(entry){
        var fullPath = self._fs.normalize(entry.fullPath);
        self._cached[fullPath] = {
          toInternalURL: isCordova? entry.toInternalURL(): entry.toURL(),
          toURL: entry.toURL(),
        };
        return fullPath;
      });
      resolve(entries);
    },function(){
      resolve([]);
    });
  });
};

FileCache.prototype.add = function add(urls){
  if(!urls) urls = [];
  if(typeof urls === 'string') urls = [urls];
  var self = this;
  urls.forEach(function(url){
    url = self.toServerURL(url);
    if(self._added.indexOf(url) === -1) {
      self._added.push(url);
    }
  });
  return self.isDirty();
};

FileCache.prototype.remove = function remove(urls,returnPromises){
  if(!urls) urls = [];
  var promises = [];
  if(typeof urls === 'string') urls = [urls];
  var self = this;
  urls.forEach(function(url){
    var index = self._added.indexOf(self.toServerURL(url));
    if(index >= 0) self._added.splice(index,1);
    var path = self.toPath(url);
    promises.push(self._fs.remove(path));
    delete self._cached[path];
  });
  return returnPromises? Promise.all(promises): self.isDirty();
};

FileCache.prototype.getDownloadQueue = function(){
  var self = this;
  var queue = self._added.filter(function(url){
    return !self.isCached(url);
  });
  return queue;
};

FileCache.prototype.getAdded = function() {
  return this._added;
};

FileCache.prototype.isDirty = function isDirty(){
  return this.getDownloadQueue().length > 0;
};

FileCache.prototype.download = function download(onprogress,includeFileProgressEvents){
  var fs = this._fs;
  var self = this;
  includeFileProgressEvents = includeFileProgressEvents || false;
  self.abort();

  return new Promise(function(resolve,reject){
    // make sure cache directory exists and that
    // we have retrieved the latest cache contents
    // to avoid downloading files we already have!
    fs.ensure(self.localRoot).then(function(){
      return self.list();
    }).then(function(){
      // no dowloads needed, resolve
      if(!self.isDirty()) {
        resolve(self);
        return;
      }

      // keep track of number of downloads!
      var queue = self.getDownloadQueue();
      var done = self._downloading.length;
      var total = self._downloading.length + queue.length;
      var percentage = 0;

      // download every file in the queue (which is the diff from _added with _cached)
      queue.forEach(function(url){
        var path = self.toPath(url);
        // augment progress event with done/total stats
        var onSingleDownloadProgress;
        if(typeof onprogress === 'function') {
          onSingleDownloadProgress = function(ev){
            ev.queueIndex = done;
            ev.queueSize = total;
            ev.url = url;
            ev.path = path;
            ev.percentage = done / total;
            if(ev.loaded > 0 && ev.total > 0 && done !== total){
               ev.percentage += (ev.loaded / ev.total) / total;
            }
            ev.percentage = Math.max(percentage,ev.percentage);
            percentage = ev.percentage;
            onprogress(ev);
          };
        }

        // callback
        var onDone = function(){
          done++;
          onSingleDownloadProgress(new ProgressEvent());

          // when we're done
          if(done === total) {
            // reset downloads
            self._downloading = [];
            // check if we got everything
            self.list().then(function(){
              // final progress event!
              if(onSingleDownloadProgress) onSingleDownloadProgress(new ProgressEvent());
              // Yes, we're not dirty anymore!
              if(!self.isDirty()) {
                resolve(self);
              // Aye, some files got left behind!
              } else {
                reject(self.getDownloadQueue());
              }
            },reject);
          }
        };
        var downloadUrl = url;
        if(self._cacheBuster) downloadUrl += "?"+Date.now();
        var download = fs.download(downloadUrl,path,{retry:self._retry},includeFileProgressEvents? onSingleDownloadProgress: undefined);
        download.then(onDone,onDone);
        self._downloading.push(download);
      });
    },reject);
  });
};

FileCache.prototype.abort = function abort(){
  this._downloading.forEach(function(download){
    download.abort();
  });
  this._downloading = [];
};

FileCache.prototype.isCached = function isCached(url){
  url = this.toPath(url);
  return !!this._cached[url];
};

FileCache.prototype.clear = function clear(){
  var self = this;
  this._cached = {};
  return this._fs.removeDir(this.localRoot).then(function(){
    return self._fs.ensure(self.localRoot);
  });
};

/**
 * Helpers to output to various formats
 */
FileCache.prototype.toInternalURL = function toInternalURL(url){
  path = this.toPath(url);
  if(this._cached[path]) return this._cached[path].toInternalURL;
  return url;
};

FileCache.prototype.get = function get(url){
  path = this.toPath(url);
  if(this._cached[path]) return this._cached[path].toURL;
  return this.toServerURL(url);
};

FileCache.prototype.toDataURL = function toDataURL(url){
  return this._fs.toDataURL(this.toPath(url));
};

FileCache.prototype.toURL = function toURL(url){
  path = this.toPath(url);
  return this._cached[path]? this._cached[path].toURL: url;
};

FileCache.prototype.toServerURL = function toServerURL(path){
  path = this._fs.normalize(path);
  return path.indexOf('://') < 0? this.serverRoot + path: path;
};

/**
 * Helper to transform remote URL to a local path (for cordova-promise-fs)
 */
FileCache.prototype.toPath = function toPath(url){
  if(this._mirrorMode) {
    var query = url.indexOf('?');
    if(query > -1){
      url = url.substr(0,query);
    }
    url = url = this._fs.normalize(url || '');
    len = this.serverRoot.length;
    if(url.substr(0,len) !== this.serverRoot) {
      return this.localRoot + url;
    } else {
      return this.localRoot + url.substr(len);
    }
  } else {
    var ext = url.substr(url.lastIndexOf('.'));
    if ((ext.indexOf("?") > 0) || (ext.indexOf("/") > 0)) {
      ext = ".txt";
    }
    return this.localRoot + hash(url) + ext;
  }
};

module.exports = FileCache;<commit_msg>weird programming error fix<commit_after>var hash = require('./murmerhash');
var Promise = null;
var isCordova = typeof cordova !== 'undefined';

/* Cordova File Cache x */
function FileCache(options){
  var self = this;
  // cordova-promise-fs
  this._fs = options.fs;
  if(!this._fs) {
    throw new Error('Missing required option "fs". Add an instance of cordova-promise-fs.');
  }
  // Use Promises from fs.
  Promise = this._fs.Promise;

  // 'mirror' mirrors files structure from "serverRoot" to "localRoot"
  // 'hash' creates a 1-deep filestructure, where the filenames are hashed server urls (with extension)
  this._mirrorMode = options.mode !== 'hash';
  this._retry = options.retry || [500,1500,8000];
  this._cacheBuster = !!options.cacheBuster;

  // normalize path
  this.localRoot = this._fs.normalize(options.localRoot || 'data');
  this.serverRoot = this._fs.normalize(options.serverRoot || '');

  // set internal variables
  this._downloading = [];    // download promises
  this._added = [];          // added files
  this._cached = {};         // cached files

  // list existing cache contents
  this.ready = this._fs.ensure(this.localRoot)
  .then(function(entry){
    self.localInternalURL = isCordova? entry.toInternalURL(): entry.toURL();
    self.localUrl = entry.toURL();
    return self.list();
  });
}

FileCache.hash = hash;

/**
 * Helper to cache all 'internalURL' and 'URL' for quick synchronous access
 * to the cached files.
 */
FileCache.prototype.list = function list(){
  var self = this;
  return new Promise(function(resolve,reject){
    self._fs.list(self.localRoot,'rfe').then(function(entries){
      self._cached = {};
      entries = entries.map(function(entry){
        var fullPath = self._fs.normalize(entry.fullPath);
        self._cached[fullPath] = {
          toInternalURL: isCordova? entry.toInternalURL(): entry.toURL(),
          toURL: entry.toURL(),
        };
        return fullPath;
      });
      resolve(entries);
    },function(){
      resolve([]);
    });
  });
};

FileCache.prototype.add = function add(urls){
  if(!urls) urls = [];
  if(typeof urls === 'string') urls = [urls];
  var self = this;
  urls.forEach(function(url){
    url = self.toServerURL(url);
    if(self._added.indexOf(url) === -1) {
      self._added.push(url);
    }
  });
  return self.isDirty();
};

FileCache.prototype.remove = function remove(urls,returnPromises){
  if(!urls) urls = [];
  var promises = [];
  if(typeof urls === 'string') urls = [urls];
  var self = this;
  urls.forEach(function(url){
    var index = self._added.indexOf(self.toServerURL(url));
    if(index >= 0) self._added.splice(index,1);
    var path = self.toPath(url);
    promises.push(self._fs.remove(path));
    delete self._cached[path];
  });
  return returnPromises? Promise.all(promises): self.isDirty();
};

FileCache.prototype.getDownloadQueue = function(){
  var self = this;
  var queue = self._added.filter(function(url){
    return !self.isCached(url);
  });
  return queue;
};

FileCache.prototype.getAdded = function() {
  return this._added;
};

FileCache.prototype.isDirty = function isDirty(){
  return this.getDownloadQueue().length > 0;
};

FileCache.prototype.download = function download(onprogress,includeFileProgressEvents){
  var fs = this._fs;
  var self = this;
  includeFileProgressEvents = includeFileProgressEvents || false;
  self.abort();

  return new Promise(function(resolve,reject){
    // make sure cache directory exists and that
    // we have retrieved the latest cache contents
    // to avoid downloading files we already have!
    fs.ensure(self.localRoot).then(function(){
      return self.list();
    }).then(function(){
      // no dowloads needed, resolve
      if(!self.isDirty()) {
        resolve(self);
        return;
      }

      // keep track of number of downloads!
      var queue = self.getDownloadQueue();
      var done = self._downloading.length;
      var total = self._downloading.length + queue.length;
      var percentage = 0;

      // download every file in the queue (which is the diff from _added with _cached)
      queue.forEach(function(url){
        var path = self.toPath(url);
        // augment progress event with done/total stats
        var onSingleDownloadProgress;
        if(typeof onprogress === 'function') {
          onSingleDownloadProgress = function(ev){
            ev.queueIndex = done;
            ev.queueSize = total;
            ev.url = url;
            ev.path = path;
            ev.percentage = done / total;
            if(ev.loaded > 0 && ev.total > 0 && done !== total){
               ev.percentage += (ev.loaded / ev.total) / total;
            }
            ev.percentage = Math.max(percentage,ev.percentage);
            percentage = ev.percentage;
            onprogress(ev);
          };
        }

        // callback
        var onDone = function(){
          done++;
          onSingleDownloadProgress(new ProgressEvent());

          // when we're done
          if(done === total) {
            // reset downloads
            self._downloading = [];
            // check if we got everything
            self.list().then(function(){
              // final progress event!
              if(onSingleDownloadProgress) onSingleDownloadProgress(new ProgressEvent());
              // Yes, we're not dirty anymore!
              if(!self.isDirty()) {
                resolve(self);
              // Aye, some files got left behind!
              } else {
                reject(self.getDownloadQueue());
              }
            },reject);
          }
        };
        var downloadUrl = url;
        if(self._cacheBuster) downloadUrl += "?"+Date.now();
        var download = fs.download(downloadUrl,path,{retry:self._retry},includeFileProgressEvents? onSingleDownloadProgress: undefined);
        download.then(onDone,onDone);
        self._downloading.push(download);
      });
    },reject);
  });
};

FileCache.prototype.abort = function abort(){
  this._downloading.forEach(function(download){
    download.abort();
  });
  this._downloading = [];
};

FileCache.prototype.isCached = function isCached(url){
  url = this.toPath(url);
  return !!this._cached[url];
};

FileCache.prototype.clear = function clear(){
  var self = this;
  this._cached = {};
  return this._fs.removeDir(this.localRoot).then(function(){
    return self._fs.ensure(self.localRoot);
  });
};

/**
 * Helpers to output to various formats
 */
FileCache.prototype.toInternalURL = function toInternalURL(url){
  path = this.toPath(url);
  if(this._cached[path]) return this._cached[path].toInternalURL;
  return url;
};

FileCache.prototype.get = function get(url){
  path = this.toPath(url);
  if(this._cached[path]) return this._cached[path].toURL;
  return this.toServerURL(url);
};

FileCache.prototype.toDataURL = function toDataURL(url){
  return this._fs.toDataURL(this.toPath(url));
};

FileCache.prototype.toURL = function toURL(url){
  path = this.toPath(url);
  return this._cached[path]? this._cached[path].toURL: url;
};

FileCache.prototype.toServerURL = function toServerURL(path){
  path = this._fs.normalize(path);
  return path.indexOf('://') < 0? this.serverRoot + path: path;
};

/**
 * Helper to transform remote URL to a local path (for cordova-promise-fs)
 */
FileCache.prototype.toPath = function toPath(url){
  if(this._mirrorMode) {
    var query = url.indexOf('?');
    if(query > -1){
      url = url.substr(0,query);
    }
    url = this._fs.normalize(url || '');
    var len = this.serverRoot.length;
    if(url.substr(0,len) !== this.serverRoot) {
      return this.localRoot + url;
    } else {
      return this.localRoot + url.substr(len);
    }
  } else {
    var ext = url.substr(url.lastIndexOf('.'));
    if ((ext.indexOf("?") > 0) || (ext.indexOf("/") > 0)) {
      ext = ".txt";
    }
    return this.localRoot + hash(url) + ext;
  }
};

module.exports = FileCache;<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var port = process.env.PORT || 3000;

app.set('port', port);

// body parser middleware
app.use(bodyParser.urlencoded({ extended: true }));

app.listen(port, function(){
    console.log("Server started ..!");
});
<commit_msg>no need i hope<commit_after>var express = require('express');
var app = express();
var port = process.env.PORT || 3000;

app.set('port', port);

app.listen(port, function(){
    console.log("Server started ..!");
});
<|endoftext|>


<commit_before>#! /usr/bin/env node

"use strict";

const process = require("process");
const fs = require("fs");
const path = require("path");

const findUp = require("find-up");
const semver = require("semver");
const Config = require("truffle-config");
const Resolver = require("truffle-resolver");
const tsort = require("tsort");
const parser = require("solidity-parser-antlr");

const PRAGAMA_SOLIDITY_VERSION_REGEX = /^\s*pragma\ssolidity\s+(.*?)\s*;/;
const SUPPORTED_VERSION_DECLARATION_REGEX = /^\^?\d+(\.\d+){1,2}$/;
const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+).*$/gm;

function unique(array) {
  return [...new Set(array)];
}

function resolve(importPath) {
  const config = Config.default();
  const resolver = new Resolver(config);

  return new Promise((resolve, reject) => {
    resolver.resolve(importPath, (err, fileContents, filePath) => {
      if (err) {
        reject(err);
        return;
      }

      resolve({ fileContents, filePath });
    });
  });
}

function getDirPath(filePath) {
  return filePath.substring(0, filePath.lastIndexOf(path.sep));
}

function getDependencies(filePath, fileContents) {
  try {
    let ast = parser.parse(fileContents)
    let imports = [];
    parser.visit(ast, {
      ImportDirective: function(node) {
        imports.push(getNormalizedDependencyPath(node.path, filePath))
      }
    })
    return imports
  } catch (error) {
    throw new Error(
      "Could not parse " + filePath + " for extracting its imports."
    );
  }
}

function getNormalizedDependencyPath(dependency, filePath) {
  if (dependency.startsWith("./") || dependency.startsWith("../")) {
    dependency = path.join(getDirPath(filePath), dependency);
    dependency = path.normalize(dependency);
  }
  return dependency;
}

async function dependenciesDfs(graph, visitedFiles, filePath) {
  visitedFiles.push(filePath);

  const resolved = await resolve(filePath);

  const dependencies = getDependencies(
    resolved.filePath,
    resolved.fileContents
  );

  for (let dependency of dependencies) {
    graph.add(dependency, filePath);

    const resolvedDependency = await resolve(dependency);

    if (!visitedFiles.includes(dependency)) {
      await dependenciesDfs(graph, visitedFiles, dependency);
    }
  }
}

async function getSortedFilePaths(entryPoints) {
  const graph = tsort();
  const visitedFiles = [];

  for (const entryPoint of entryPoints) {
    await dependenciesDfs(graph, visitedFiles, entryPoint);
  }

  let topologicalSortedFiles;
  try {
    topologicalSortedFiles = graph.sort();
  } catch (e) {
    if (e.toString().includes("Error: There is a cycle in the graph.")) {
      const message =
        "There is a cycle in the dependency" +
        " graph, can't compute topological ordering. Files:\n\t" +
        visitedFiles.join("\n\t");
      throw new Error(message);
    }
  }

  // If an entry has no dependency it won't be included in the graph, so we
  // add them and then dedup the array
  const withEntries = topologicalSortedFiles.concat(entryPoints);

  const files = unique(withEntries);

  return files;
}

async function printFileWithoutPragma(filePath) {
  const resolved = await resolve(filePath);
  const output = resolved.fileContents
    .replace(PRAGAMA_SOLIDITY_VERSION_REGEX, "")
    .replace(IMPORT_SOLIDITY_REGEX, "");

  console.log(output.trim());
}

async function getFileCompilerVersionDeclaration(filePath) {
  const resolved = await resolve(filePath);

  const matched = resolved.fileContents.match(PRAGAMA_SOLIDITY_VERSION_REGEX);

  if (matched === null) {
    return undefined;
  }

  const version = matched[1];

  if (!SUPPORTED_VERSION_DECLARATION_REGEX.test(version)) {
    throw new Error(
      `Unsupported compiler version declaration in ${filePath}: ${version}. Only pinned or ^ versions are supported.`
    );
  }

  return version;
}

async function normalizeCompilerVersionDeclarations(files) {
  let pinnedVersion;
  let pinnedVersionFile;

  let maxCaretVersion;
  let maxCaretVersionFile;

  for (const file of files) {
    const version = await getFileCompilerVersionDeclaration(file);

    if (version === undefined) {
      continue;
    }

    if (version.startsWith("^")) {
      if (maxCaretVersion == undefined) {
        maxCaretVersion = version;
        maxCaretVersionFile = file;
      } else {
        if (semver.gt(version.substr(1), maxCaretVersion.substr(1))) {
          maxCaretVersion = version;
          maxCaretVersionFile = file;
        }
      }
    } else {
      if (pinnedVersion === undefined) {
        pinnedVersion = version;
        pinnedVersionFile = file;
      } else if (pinnedVersion !== version) {
        throw new Error(
          "Differernt pinned compiler versions in " +
            pinnedVersionFile +
            " and " +
            file
        );
      }
    }

    if (maxCaretVersion !== undefined && pinnedVersion !== undefined) {
      if (!semver.satisfies(pinnedVersion, maxCaretVersion)) {
        throw new Error(
          "Incompatible compiler version declarations in " +
            maxCaretVersionFile +
            " and " +
            pinnedVersionFile
        );
      }
    }
  }

  if (pinnedVersion !== undefined) {
    return pinnedVersion;
  }

  return maxCaretVersion;
}

async function printContactenation(files) {
  const version = await normalizeCompilerVersionDeclarations(files);

  if (version) {
    console.log("pragma solidity " + version + ";");
  }

  for (const file of files) {
    console.log("\n// File: " + file + "\n");
    await printFileWithoutPragma(file);
  }
}

async function getTruffleRoot() {
  let truffleConfigPath = await findUp(["truffle.js", "truffle-config.js"]);
  if (!truffleConfigPath) {
    throw new Error(`
      Truffle Flattener must be run inside a Truffle project:
      truffle.js or truffle-config.js not found
    `);
  }

  return getDirPath(truffleConfigPath);
}

function getFilePathsFromTruffleRoot(filePaths, truffleRoot) {
  return filePaths.map(f => path.relative(truffleRoot, path.resolve(f)));
}

async function main(filePaths) {
  if (!filePaths.length) {
    console.error("Usage: truffle-flattener <files>");
    return;
  }

  try {
    const truffleRoot = await getTruffleRoot();
    const filePathsFromTruffleRoot = getFilePathsFromTruffleRoot(
      filePaths,
      truffleRoot
    );

    process.chdir(truffleRoot);

    const sortedFiles = await getSortedFilePaths(filePathsFromTruffleRoot);
    await printContactenation(sortedFiles);
  } catch (error) {
    console.log(error, error.stack);
  }
}

main(process.argv.slice(2));
<commit_msg>walk back<commit_after>#! /usr/bin/env node

"use strict";

const process = require("process");
const fs = require("fs");
const path = require("path");

const findUp = require("find-up");
const semver = require("semver");
const Config = require("truffle-config");
const Resolver = require("truffle-resolver");
const tsort = require("tsort");
const parser = require("solidity-parser-antlr");

const PRAGAMA_SOLIDITY_VERSION_REGEX = /^\s*pragma\ssolidity\s+(.*?)\s*;/;
const SUPPORTED_VERSION_DECLARATION_REGEX = /^\^?\d+(\.\d+){1,2}$/;
const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+).*$/gm;

function unique(array) {
  return [...new Set(array)];
}

function resolve(importPath) {
  const config = Config.default();
  const resolver = new Resolver(config);

  return new Promise((resolve, reject) => {
    resolver.resolve(importPath, (err, fileContents, filePath) => {
      if (err) {
        reject(err);
        return;
      }

      resolve({ fileContents, filePath });
    });
  });
}

function getDirPath(filePath) {
  return filePath.substring(0, filePath.lastIndexOf(path.sep));
}

function getDependencies(filePath, fileContents) {
  try {
    let ast = parser.parse(fileContents)
    let imports = [];
    parser.visit(ast, {
      ImportDirective: function(node) {
        imports.push(getNormalizedDependencyPath(node.path, filePath))
      }
    })
    return imports
  } catch (error) {
    throw new Error(
      "Could not parse " + filePath + " for extracting its imports."
    );
  }
}

function getNormalizedDependencyPath(dependency, filePath) {
  if (dependency.startsWith("./") || dependency.startsWith("../")) {
    dependency = path.join(getDirPath(filePath), dependency);
    dependency = path.normalize(dependency);
  }
  return dependency;
}

async function dependenciesDfs(graph, visitedFiles, filePath) {
  visitedFiles.push(filePath);

  const resolved = await resolve(filePath);

  const dependencies = getDependencies(
    resolved.filePath,
    resolved.fileContents
  );

  for (let dependency of dependencies) {
    graph.add(dependency, filePath);

    const resolvedDependency = await resolve(dependency);

    if (!visitedFiles.includes(dependency)) {
      await dependenciesDfs(graph, visitedFiles, dependency);
    }
  }
}

async function getSortedFilePaths(entryPoints) {
  const graph = tsort();
  const visitedFiles = [];

  for (const entryPoint of entryPoints) {
    await dependenciesDfs(graph, visitedFiles, entryPoint);
  }

  let topologicalSortedFiles;
  try {
    topologicalSortedFiles = graph.sort();
  } catch (e) {
    if (e.toString().includes("Error: There is a cycle in the graph.")) {
      const message =
        "There is a cycle in the dependency" +
        " graph, can't compute topological ordering. Files:\n\t" +
        visitedFiles.join("\n\t");
      throw new Error(message);
    }
  }

  // If an entry has no dependency it won't be included in the graph, so we
  // add them and then dedup the array
  const withEntries = topologicalSortedFiles.concat(entryPoints);

  const files = unique(withEntries);

  return files;
}

async function printFileWithoutPragma(filePath, log) {
  const resolved = await resolve(filePath);
  const output = resolved.fileContents
    .replace(PRAGAMA_SOLIDITY_VERSION_REGEX, "")
    .replace(IMPORT_SOLIDITY_REGEX, "");

  log(output.trim());
}

async function getFileCompilerVersionDeclaration(filePath) {
  const resolved = await resolve(filePath);

  const matched = resolved.fileContents.match(PRAGAMA_SOLIDITY_VERSION_REGEX);

  if (matched === null) {
    return undefined;
  }

  const version = matched[1];

  if (!SUPPORTED_VERSION_DECLARATION_REGEX.test(version)) {
    throw new Error(
      `Unsupported compiler version declaration in ${filePath}: ${version}. Only pinned or ^ versions are supported.`
    );
  }

  return version;
}

async function normalizeCompilerVersionDeclarations(files) {
  let pinnedVersion;
  let pinnedVersionFile;

  let maxCaretVersion;
  let maxCaretVersionFile;

  for (const file of files) {
    const version = await getFileCompilerVersionDeclaration(file);

    if (version === undefined) {
      continue;
    }

    if (version.startsWith("^")) {
      if (maxCaretVersion == undefined) {
        maxCaretVersion = version;
        maxCaretVersionFile = file;
      } else {
        if (semver.gt(version.substr(1), maxCaretVersion.substr(1))) {
          maxCaretVersion = version;
          maxCaretVersionFile = file;
        }
      }
    } else {
      if (pinnedVersion === undefined) {
        pinnedVersion = version;
        pinnedVersionFile = file;
      } else if (pinnedVersion !== version) {
        throw new Error(
          "Differernt pinned compiler versions in " +
            pinnedVersionFile +
            " and " +
            file
        );
      }
    }

    if (maxCaretVersion !== undefined && pinnedVersion !== undefined) {
      if (!semver.satisfies(pinnedVersion, maxCaretVersion)) {
        throw new Error(
          "Incompatible compiler version declarations in " +
            maxCaretVersionFile +
            " and " +
            pinnedVersionFile
        );
      }
    }
  }

  if (pinnedVersion !== undefined) {
    return pinnedVersion;
  }

  return maxCaretVersion;
}

async function printContactenation(files, log) {
  const version = await normalizeCompilerVersionDeclarations(files);

  if (version) {
    log("pragma solidity " + version + ";");
  }

  for (const file of files) {
    log("\n// File: " + file + "\n");
    await printFileWithoutPragma(file, log);
  }
}

async function getTruffleRoot() {
  let truffleConfigPath = await findUp(["truffle.js", "truffle-config.js"]);
  if (!truffleConfigPath) {
    throw new Error(`
      Truffle Flattener must be run inside a Truffle project:
      truffle.js or truffle-config.js not found
    `);
  }

  return getDirPath(truffleConfigPath);
}

function getFilePathsFromTruffleRoot(filePaths, truffleRoot) {
  return filePaths.map(f => path.relative(truffleRoot, path.resolve(f)));
}

async function flatten(filePaths, log) {
  try {
    const truffleRoot = await getTruffleRoot();
    const filePathsFromTruffleRoot = getFilePathsFromTruffleRoot(
      filePaths,
      truffleRoot
    );

    process.chdir(truffleRoot);

    const sortedFiles = await getSortedFilePaths(filePathsFromTruffleRoot);
    await printContactenation(sortedFiles, log);
  } catch (error) {
    console.error(error, error.stack);
  }
}

async function main(filePaths) {
  if (!filePaths.length) {
    console.error("Usage: truffle-flattener <files>");
    return;
  }

  await flatten(filePaths, str => console.log(str));
}

if (require.main === module) {
  main(process.argv.slice(2));
}

module.exports = async function(filePaths) {
  let res = "";
  await flatten(filePaths, str => (res += str));
  return res;
};
<|endoftext|>


<commit_before>'use strict';

const util = require('util');

const naming = require('bem-naming');

const stringifyEntity = naming.stringify;
const typeOfEntity = naming.typeOf;

module.exports = class BemEntityName {
    constructor(obj) {
        if (!obj.block) {
             throw new Error('This is not valid BEM entity: the field `block` is undefined.');
        }

        this._obj = { block: obj.block };
        obj.elem && (this._obj.elem = obj.elem);

        const modName = (typeof obj.mod === 'string' ? obj.mod : obj.mod && obj.mod.name) || obj.modName;

        if (modName) {
            const modVal = obj.hasOwnProperty('modVal') || obj.mod && obj.mod.hasOwnProperty('val')
                ? obj.mod && obj.mod.val || obj.modVal
                : true;

            this._obj.mod = {
                name: modName,
                val: modVal
            };
        }
    }
    /**
     * Returns the name of block to which this entity belongs.
     *
     * @returns {string} name of entity block.
     */
    get block() { return this._obj.block; }
    /**
     * Returns the element name of this entity.
     *
     * If entity is not element or modifier of element then returns empty string.
     *
     * @returns {string} name of entity element.
     */
    get elem() { return this._obj.elem; }
    /**
     * Returns the modifier of this entity.
     *
     * If entity is not modifier then returns empty object.
     *
     * @returns {object} entity modifier.
     */
    get mod() { return this._obj.mod || {}; }
    /**
     * Returns id for this entity.
     *
     * @returns {string} id of entity.
     */
    get id() {
        if (this._id) { return this._id; }

        const entity = { block: this._obj.block };

        this.elem && (entity.elem = this.elem);
        this.mod.name && (entity.modName = this.mod.name);
        this.mod.val && (entity.modVal = this.mod.val);

        this._id = stringifyEntity(entity);

        return this._id;
    }
    /**
     * Returns type for this entity.
     *
     * @returns {string} type of entity.
     */
    get type() {
        if (this._type) { return this._type; }

        const entity = { block: this._obj.block };

        this.elem && (entity.elem = this.elem);
        this.mod.name && (entity.modName = this.mod.name);
        this.mod.val && (entity.modVal = this.mod.val);

        this._type = typeOfEntity(entity);

        return this._type;
    }

    toString() { return this.id;  }
    valueOf()  { return this._obj; }
    /**
     * Returns object representing the entity name. Is needed for debug in Node.js.
     *
     * In Node.js, `console.log()` calls `util.inspect()` on each argument without a formatting placeholder.
     * This method will be called to get custom string representation of the object.
     *
     * The representation object contains only `block`, `elem` and `mod` fields
     * without private and deprecated fields (`modName` and `modVal`).
     *
     * @param {integer} depth — tells inspect how many times to recurse while formatting the object.
     * @param {object} options — An optional `options` object may be passed
     *                         	 that alters certain aspects of the formatted string.
     *
     * @returns {object}
     * @example
     * const BemEntityName = require('bem-entity-name');
     * const name = new BemEntityName({ block: 'button' });
     *
     * console.log(name); // { block: 'button' }
     */
    inspect(depth, options) {
        const stringRepresentation = util.inspect(this._obj, options);

        return `BemEntityName ${stringRepresentation}`;
    }
    /**
     * Determines whether specified entity is the deepEqual entity.
     *
     * @param {object} entity - the entity to compare.
     *
     * @returns {boolean} A Boolean indicating whether or not specified entity is the deepEqual entity.
     */
    isEqual(entity) {
        return entity && (this.id === entity.id);
    }
};
<commit_msg>docs(jsdoc): add jsdoc for `toString()` and `valueOf()` methods<commit_after>'use strict';

const util = require('util');

const naming = require('bem-naming');

const stringifyEntity = naming.stringify;
const typeOfEntity = naming.typeOf;

module.exports = class BemEntityName {
    constructor(obj) {
        if (!obj.block) {
             throw new Error('This is not valid BEM entity: the field `block` is undefined.');
        }

        this._obj = { block: obj.block };
        obj.elem && (this._obj.elem = obj.elem);

        const modName = (typeof obj.mod === 'string' ? obj.mod : obj.mod && obj.mod.name) || obj.modName;

        if (modName) {
            const modVal = obj.hasOwnProperty('modVal') || obj.mod && obj.mod.hasOwnProperty('val')
                ? obj.mod && obj.mod.val || obj.modVal
                : true;

            this._obj.mod = {
                name: modName,
                val: modVal
            };
        }
    }
    /**
     * Returns the name of block to which this entity belongs.
     *
     * @returns {string} name of entity block.
     */
    get block() { return this._obj.block; }
    /**
     * Returns the element name of this entity.
     *
     * If entity is not element or modifier of element then returns empty string.
     *
     * @returns {string} name of entity element.
     */
    get elem() { return this._obj.elem; }
    /**
     * Returns the modifier of this entity.
     *
     * If entity is not modifier then returns empty object.
     *
     * @returns {object} entity modifier.
     */
    get mod() { return this._obj.mod || {}; }
    /**
     * Returns id for this entity.
     *
     * @returns {string} id of entity.
     */
    get id() {
        if (this._id) { return this._id; }

        const entity = { block: this._obj.block };

        this.elem && (entity.elem = this.elem);
        this.mod.name && (entity.modName = this.mod.name);
        this.mod.val && (entity.modVal = this.mod.val);

        this._id = stringifyEntity(entity);

        return this._id;
    }
    /**
     * Returns type for this entity.
     *
     * @returns {string} type of entity.
     */
    get type() {
        if (this._type) { return this._type; }

        const entity = { block: this._obj.block };

        this.elem && (entity.elem = this.elem);
        this.mod.name && (entity.modName = this.mod.name);
        this.mod.val && (entity.modVal = this.mod.val);

        this._type = typeOfEntity(entity);

        return this._type;
    }
    /**
     * Returns string representing the entity name.
     *
     * @returns {string}
     * @example
     * const BemEntityName = require('bem-entity-name');
     * const name = new BemEntityName({ block: 'button' });
     *
     * console.log(`name: ${name}`); // button
     */
    toString() { return this.id; }
    /**
     * Returns object representing the entity name. Is needed for debug in Node.js.
     *
     * In some browsers `console.log()` calls `valueOf()` on each argument.
     * This method will be called to get custom string representation of the object.
     *
     * The representation object contains only `block`, `elem` and `mod` fields
     * without private and deprecated fields (`modName` and `modVal`).
     *
     * @returns {object}
     * @example
     * const BemEntityName = require('bem-entity-name');
     * const name = new BemEntityName({ block: 'button' });
     *
     * console.log(name); // { block: 'button' }
     */
    valueOf() { return this._obj; }
    /**
     * Returns object representing the entity name. Is needed for debug in Node.js.
     *
     * In Node.js, `console.log()` calls `util.inspect()` on each argument without a formatting placeholder.
     * This method will be called to get custom string representation of the object.
     *
     * The representation object contains only `block`, `elem` and `mod` fields
     * without private and deprecated fields (`modName` and `modVal`).
     *
     * @param {integer} depth — tells inspect how many times to recurse while formatting the object.
     * @param {object} options — An optional `options` object may be passed
     *                         	 that alters certain aspects of the formatted string.
     *
     * @returns {object}
     * @example
     * const BemEntityName = require('bem-entity-name');
     * const name = new BemEntityName({ block: 'button' });
     *
     * console.log(name); // { block: 'button' }
     */
    inspect(depth, options) {
        const stringRepresentation = util.inspect(this._obj, options);

        return `BemEntityName ${stringRepresentation}`;
    }
    /**
     * Determines whether specified entity is the deepEqual entity.
     *
     * @param {object} entity - the entity to compare.
     *
     * @returns {boolean} A Boolean indicating whether or not specified entity is the deepEqual entity.
     */
    isEqual(entity) {
        return entity && (this.id === entity.id);
    }
};
<|endoftext|>


<commit_before>module.exports = {
  'ecmaFeatures': {
    'globalReturn': true,
    'jsx': true,
    'modules': true
  },

  'env': {
    'browser': false,
    'es6': true,
    'node': true
  },

  'globals': {
    'document': false,
    'escape': false,
    'navigator': false,
    'unescape': false,
    'window': false
  },

  'plugins': [
    'react'
  ],

  'rules': {
    'block-scoped-var': 0,
    'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
    'camelcase': 0,
    'comma-dangle': [2, 'never'],
    'comma-spacing': [2, { 'before': false, 'after': true }],
    'comma-style': [2, 'last'],
    'complexity': 0,
    'consistent-return': 0,
    'consistent-this': 0,
    'curly': [2, 'multi-line'],
    'default-case': 0,
    'dot-location': [2, 'property'],
    'dot-notation': 0,
    'eol-last': 2,
    'eqeqeq': [2, 'allow-null'],
    'func-names': 0,
    'func-style': 0,
    'generator-star-spacing': [2, 'both'],
    'guard-for-in': 0,
    'handle-callback-err': [2, '^(err|error|anySpecificError)$' ],
    'indent': [2, 2],
    'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
    'linebreak-style': [2, 'unix'],
    'max-depth': 0,
    'max-len': 0,
    'max-nested-callbacks': 0,
    'max-params': 0,
    'max-statements': 0,
    'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }],
    'new-parens': 2,
    'no-alert': 0,
    'no-array-constructor': 2,
    'no-bitwise': 0,
    'no-caller': 2,
    'no-catch-shadow': 0,
    'no-cond-assign': 2,
    'no-console': 0,
    'no-constant-condition': 0,
    'no-continue': 0,
    'no-control-regex': 2,
    'no-debugger': 2,
    'no-delete-var': 2,
    'no-div-regex': 0,
    'no-dupe-args': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-else-return': 0,
    'no-empty': 0,
    'no-empty-class': 2,
    'no-empty-label': 2,
    'no-eq-null': 0,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 2,
    'no-extra-parens': 0,
    'no-extra-semi': 0,
    'no-extra-strict': 0,
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implied-eval': 2,
    'no-inline-comments': 0,
    'no-inner-declarations': [2, 'functions'],
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-iterator': 2,
    'no-label-var': 2,
    'no-labels': 2,
    'no-lone-blocks': 0,
    'no-lonely-if': 0,
    'no-loop-func': 0,
    'no-mixed-requires': 0,
    'no-mixed-spaces-and-tabs': [2, false],
    'no-multi-spaces': 2,
    'no-multi-str': 2,
    'no-multiple-empty-lines': [2, { 'max': 1 }],
    'no-native-reassign': 2,
    'no-negated-in-lhs': 2,
    'no-nested-ternary': 0,
    'no-new': 2,
    'no-new-func': 2,
    'no-new-object': 2,
    'no-new-require': 2,
    'no-new-wrappers': 2,
    'no-obj-calls': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-path-concat': 0,
    'no-plusplus': 0,
    'no-process-env': 0,
    'no-process-exit': 0,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-regex-spaces': 2,
    'no-reserved-keys': 0,
    'no-restricted-modules': 0,
    'no-return-assign': 2,
    'no-script-url': 0,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow': 0,
    'no-shadow-restricted-names': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-sync': 0,
    'no-ternary': 0,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-undef': 2,
    'no-undef-init': 2,
    'no-undefined': 0,
    'no-underscore-dangle': 0,
    'no-unneeded-ternary': 2,
    'no-unreachable': 2,
    'no-unused-expressions': 0,
    'no-unused-vars': [2, { 'vars': 'all', 'args': 'none' }],
    'no-use-before-define': 0,
    'no-var': 0,
    'no-void': 0,
    'no-warning-comments': 0,
    'no-with': 2,
    'no-wrap-func': 2,
    'one-var': 0,
    'operator-assignment': 0,
    'padded-blocks': 0,
    'quote-props': 0,
    'quotes': [2, 'single', 'avoid-escape'],
    'radix': 2,
    'react/display-name': 2,
    'react/jsx-boolean-value': 2,
    'react/jsx-no-undef': 2,
    'react/jsx-quotes': [2, 'single', 'avoid-escape'],
    'react/jsx-sort-prop-types': 0,
    'react/jsx-sort-props': 0,
    'react/jsx-uses-react': 2,
    'react/jsx-uses-vars': 2,
    'react/no-did-mount-set-state': 2,
    'react/no-did-update-set-state': 2,
    'react/no-multi-comp': 2,
    'react/no-unknown-property': 2,
    'react/prop-types': 2,
    'react/react-in-jsx-scope': 2,
    'react/self-closing-comp': 2,
    'react/sort-comp': 0,
    'react/wrap-multilines': 2,
    'semi': [2, 'never'],
    'semi-spacing': 0,
    'sort-vars': 0,
    'space-after-keywords': [2, 'always'],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, 'always'],
    'space-in-brackets': 0,
    'space-in-parens': [2, 'never'],
    'space-infix-ops': 2,
    'space-return-throw-case': 2,
    'space-unary-ops': [2, { 'words': true, 'nonwords': false }],
    'spaced-line-comment': [2, 'always'],
    'strict': 0,
    'use-isnan': 2,
    'valid-jsdoc': 0,
    'valid-typeof': 2,
    'vars-on-top': 0,
    'wrap-iife': [2, 'any'],
    'wrap-regex': 0,
    'yoda': [2, 'never']
  }
}
<commit_msg>Revert rule: enforce LF endings<commit_after>module.exports = {
  'ecmaFeatures': {
    'globalReturn': true,
    'jsx': true,
    'modules': true
  },

  'env': {
    'browser': false,
    'es6': true,
    'node': true
  },

  'globals': {
    'document': false,
    'escape': false,
    'navigator': false,
    'unescape': false,
    'window': false
  },

  'plugins': [
    'react'
  ],

  'rules': {
    'block-scoped-var': 0,
    'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
    'camelcase': 0,
    'comma-dangle': [2, 'never'],
    'comma-spacing': [2, { 'before': false, 'after': true }],
    'comma-style': [2, 'last'],
    'complexity': 0,
    'consistent-return': 0,
    'consistent-this': 0,
    'curly': [2, 'multi-line'],
    'default-case': 0,
    'dot-location': [2, 'property'],
    'dot-notation': 0,
    'eol-last': 2,
    'eqeqeq': [2, 'allow-null'],
    'func-names': 0,
    'func-style': 0,
    'generator-star-spacing': [2, 'both'],
    'guard-for-in': 0,
    'handle-callback-err': [2, '^(err|error|anySpecificError)$' ],
    'indent': [2, 2],
    'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
    'linebreak-style': 0,
    'max-depth': 0,
    'max-len': 0,
    'max-nested-callbacks': 0,
    'max-params': 0,
    'max-statements': 0,
    'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }],
    'new-parens': 2,
    'no-alert': 0,
    'no-array-constructor': 2,
    'no-bitwise': 0,
    'no-caller': 2,
    'no-catch-shadow': 0,
    'no-cond-assign': 2,
    'no-console': 0,
    'no-constant-condition': 0,
    'no-continue': 0,
    'no-control-regex': 2,
    'no-debugger': 2,
    'no-delete-var': 2,
    'no-div-regex': 0,
    'no-dupe-args': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-else-return': 0,
    'no-empty': 0,
    'no-empty-class': 2,
    'no-empty-label': 2,
    'no-eq-null': 0,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 2,
    'no-extra-parens': 0,
    'no-extra-semi': 0,
    'no-extra-strict': 0,
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implied-eval': 2,
    'no-inline-comments': 0,
    'no-inner-declarations': [2, 'functions'],
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-iterator': 2,
    'no-label-var': 2,
    'no-labels': 2,
    'no-lone-blocks': 0,
    'no-lonely-if': 0,
    'no-loop-func': 0,
    'no-mixed-requires': 0,
    'no-mixed-spaces-and-tabs': [2, false],
    'no-multi-spaces': 2,
    'no-multi-str': 2,
    'no-multiple-empty-lines': [2, { 'max': 1 }],
    'no-native-reassign': 2,
    'no-negated-in-lhs': 2,
    'no-nested-ternary': 0,
    'no-new': 2,
    'no-new-func': 2,
    'no-new-object': 2,
    'no-new-require': 2,
    'no-new-wrappers': 2,
    'no-obj-calls': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-path-concat': 0,
    'no-plusplus': 0,
    'no-process-env': 0,
    'no-process-exit': 0,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-regex-spaces': 2,
    'no-reserved-keys': 0,
    'no-restricted-modules': 0,
    'no-return-assign': 2,
    'no-script-url': 0,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow': 0,
    'no-shadow-restricted-names': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-sync': 0,
    'no-ternary': 0,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-undef': 2,
    'no-undef-init': 2,
    'no-undefined': 0,
    'no-underscore-dangle': 0,
    'no-unneeded-ternary': 2,
    'no-unreachable': 2,
    'no-unused-expressions': 0,
    'no-unused-vars': [2, { 'vars': 'all', 'args': 'none' }],
    'no-use-before-define': 0,
    'no-var': 0,
    'no-void': 0,
    'no-warning-comments': 0,
    'no-with': 2,
    'no-wrap-func': 2,
    'one-var': 0,
    'operator-assignment': 0,
    'padded-blocks': 0,
    'quote-props': 0,
    'quotes': [2, 'single', 'avoid-escape'],
    'radix': 2,
    'react/display-name': 2,
    'react/jsx-boolean-value': 2,
    'react/jsx-no-undef': 2,
    'react/jsx-quotes': [2, 'single', 'avoid-escape'],
    'react/jsx-sort-prop-types': 0,
    'react/jsx-sort-props': 0,
    'react/jsx-uses-react': 2,
    'react/jsx-uses-vars': 2,
    'react/no-did-mount-set-state': 2,
    'react/no-did-update-set-state': 2,
    'react/no-multi-comp': 2,
    'react/no-unknown-property': 2,
    'react/prop-types': 2,
    'react/react-in-jsx-scope': 2,
    'react/self-closing-comp': 2,
    'react/sort-comp': 0,
    'react/wrap-multilines': 2,
    'semi': [2, 'never'],
    'semi-spacing': 0,
    'sort-vars': 0,
    'space-after-keywords': [2, 'always'],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, 'always'],
    'space-in-brackets': 0,
    'space-in-parens': [2, 'never'],
    'space-infix-ops': 2,
    'space-return-throw-case': 2,
    'space-unary-ops': [2, { 'words': true, 'nonwords': false }],
    'spaced-line-comment': [2, 'always'],
    'strict': 0,
    'use-isnan': 2,
    'valid-jsdoc': 0,
    'valid-typeof': 2,
    'vars-on-top': 0,
    'wrap-iife': [2, 'any'],
    'wrap-regex': 0,
    'yoda': [2, 'never']
  }
}
<|endoftext|>


<commit_before>var split2 = require('split2')
var through2 = require('through2')
var parsePrefix = require('irc-prefix-parser')

var parseMessage = function(data) {
    var message = {
        tags: {},
        prefix: null,
        command: null,
        params: []
    }

    // position and nextspace are used by the parser as a reference.
    var position = 0
    var nextspace = 0

    // The first thing we check for is IRCv3.2 message tags.
    // http://ircv3.atheme.org/specification/message-tags-3.2

    if (data.charCodeAt(0) === 64) {
        var nextspace = data.indexOf(' ')

        if (nextspace === -1) {
            // Malformed IRC message.
            return null
        }

        // Tags are split by a semi colon.
        var rawTags = data.slice(1, nextspace).split(';')

        for (var i = 0; i < rawTags.length; i++) {
            // Tags delimited by an equals sign are key=value tags.
            // If there's no equals, we assign the tag a value of true.
            var tag = rawTags[i]
            var pair = tag.split('=')
            message.tags[pair[0]] = pair[1] || true
        }

        position = nextspace + 1
    }

    // Skip any trailing whitespace.
    while (data.charCodeAt(position) === 32) {
        position++
    }

    // Extract the message's prefix if present. Prefixes are prepended
    // with a colon.

    if (data.charCodeAt(position) === 58) {
        nextspace = data.indexOf(' ', position)

        // If there's nothing after the prefix, deem this message to be
        // malformed.
        if (nextspace === -1) {
            // Malformed IRC message.
            return null
        }

        message.prefix = data.slice(position + 1, nextspace)
        position = nextspace + 1

        // Skip any trailing whitespace.
        while (data.charCodeAt(position) === 32) {
            position++
        }
    }

    nextspace = data.indexOf(' ', position)

    // If there's no more whitespace left, extract everything from the
    // current position to the end of the string as the command.
    if (nextspace === -1) {
        if (data.length > position) {
            message.command = data.slice(position)
        }

        return message
    }

    // Else, the command is the current position up to the next space. After
    // that, we expect some parameters.
    message.command = data.slice(position, nextspace)

    position = nextspace + 1

    // Skip any trailing whitespace.
    while (data.charCodeAt(position) === 32) {
        position++
    }

    while (position < data.length) {
        nextspace = data.indexOf(' ', position)

        // If the character is a colon, we've got a trailing parameter.
        // At this point, there are no extra params, so we push everything
        // from after the colon to the end of the string, to the params array
        // and break out of the loop.
        if (data.charCodeAt(position) === 58) {
            message.params.push(data.slice(position + 1))
            break
        }

        // If we still have some whitespace...
        if (nextspace !== -1) {
            // Push whatever's between the current position and the next
            // space to the params array.
            message.params.push(data.slice(position, nextspace))
            position = nextspace + 1

            // Skip any trailing whitespace and continue looping.
            while (data.charCodeAt(position) === 32) {
                position++
            }

            continue
        }

        // If we don't have any more whitespace and the param isn't trailing,
        // push everything remaining to the params array.
        if (nextspace === -1) {
            message.params.push(data.slice(position))
            break
        }
    }
    return message
}

var parserStream = function(options) {
    var options = options || {}
    var convertTimestamps = options.convertTimestamps || false
    var parsePrefix = options.parsePrefix || false

    var split = split2()
    var parser = through2.obj(function(chunk, encoding, done) {

        var parsed = parseMessage(chunk.toString())
        var timestamp = parsed.tags.time

        // support for IRCv3.2 server-time spec
        if (timestamp && convertTimestamps) {
            var epoch = Date.parse(timestamp)

            parsed.tags.time = new Date(epoch)
        }

        if (parsePrefix) {
            parsed.prefix = parsePrefix(parsed.prefix)
        }

        if (parsed === null) {
            this.emit('error', new Error('invalid IRC message'))
        }

        this.push(parsed)

        done()
    })

    return split.pipe(parser)
}

module.exports = {
    parseMessage: parseMessage,
    parserStream: parserStream
}
<commit_msg>Fix #14<commit_after>var split2 = require('split2')
var through2 = require('through2')
var parsePrefix = require('irc-prefix-parser')

var parseMessage = function(data) {
    var message = {
        tags: {},
        prefix: null,
        command: null,
        params: []
    }

    // position and nextspace are used by the parser as a reference.
    var position = 0
    var nextspace = 0

    // The first thing we check for is IRCv3.2 message tags.
    // http://ircv3.atheme.org/specification/message-tags-3.2

    if (data.charCodeAt(0) === 64) {
        var nextspace = data.indexOf(' ')

        if (nextspace === -1) {
            // Malformed IRC message.
            return null
        }

        // Tags are split by a semi colon.
        var rawTags = data.slice(1, nextspace).split(';')

        for (var i = 0; i < rawTags.length; i++) {
            // Tags delimited by an equals sign are key=value tags.
            // If there's no equals, we assign the tag a value of true.
            var tag = rawTags[i]
            var pair = tag.split('=')
            message.tags[pair[0]] = pair[1] || true
        }

        position = nextspace + 1
    }

    // Skip any trailing whitespace.
    while (data.charCodeAt(position) === 32) {
        position++
    }

    // Extract the message's prefix if present. Prefixes are prepended
    // with a colon.

    if (data.charCodeAt(position) === 58) {
        nextspace = data.indexOf(' ', position)

        // If there's nothing after the prefix, deem this message to be
        // malformed.
        if (nextspace === -1) {
            // Malformed IRC message.
            return null
        }

        message.prefix = data.slice(position + 1, nextspace)
        position = nextspace + 1

        // Skip any trailing whitespace.
        while (data.charCodeAt(position) === 32) {
            position++
        }
    }

    nextspace = data.indexOf(' ', position)

    // If there's no more whitespace left, extract everything from the
    // current position to the end of the string as the command.
    if (nextspace === -1) {
        if (data.length > position) {
            message.command = data.slice(position)
            return message
        }

        return null
    }

    // Else, the command is the current position up to the next space. After
    // that, we expect some parameters.
    message.command = data.slice(position, nextspace)

    position = nextspace + 1

    // Skip any trailing whitespace.
    while (data.charCodeAt(position) === 32) {
        position++
    }

    while (position < data.length) {
        nextspace = data.indexOf(' ', position)

        // If the character is a colon, we've got a trailing parameter.
        // At this point, there are no extra params, so we push everything
        // from after the colon to the end of the string, to the params array
        // and break out of the loop.
        if (data.charCodeAt(position) === 58) {
            message.params.push(data.slice(position + 1))
            break
        }

        // If we still have some whitespace...
        if (nextspace !== -1) {
            // Push whatever's between the current position and the next
            // space to the params array.
            message.params.push(data.slice(position, nextspace))
            position = nextspace + 1

            // Skip any trailing whitespace and continue looping.
            while (data.charCodeAt(position) === 32) {
                position++
            }

            continue
        }

        // If we don't have any more whitespace and the param isn't trailing,
        // push everything remaining to the params array.
        if (nextspace === -1) {
            message.params.push(data.slice(position))
            break
        }
    }
    return message
}

var parserStream = function(options) {
    var options = options || {}
    var convertTimestamps = options.convertTimestamps || false
    var parsePrefix = options.parsePrefix || false

    var split = split2()
    var parser = through2.obj(function(chunk, encoding, done) {

        var parsed = parseMessage(chunk.toString())
        var timestamp = parsed.tags.time

        // support for IRCv3.2 server-time spec
        if (timestamp && convertTimestamps) {
            var epoch = Date.parse(timestamp)

            parsed.tags.time = new Date(epoch)
        }

        if (parsePrefix) {
            parsed.prefix = parsePrefix(parsed.prefix)
        }

        if (parsed === null) {
            this.emit('error', new Error('invalid IRC message'))
        }

        this.push(parsed)

        done()
    })

    return split.pipe(parser)
}

module.exports = {
    parseMessage: parseMessage,
    parserStream: parserStream
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Image, ImageBackground, Platform, StyleSheet, TouchableOpacity, View, ViewPropTypes } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Video from 'react-native-video'; // eslint-disable-line

const BackgroundImage = ImageBackground || Image; // fall back to Image if RN < 0.46

const styles = StyleSheet.create({
  preloadingPlaceholder: {
    backgroundColor: 'black',
    justifyContent: 'center',
    alignItems: 'center',
  },
  thumbnail: {
    backgroundColor: 'black',
    justifyContent: 'center',
    alignItems: 'center',
  },
  playButton: {
    backgroundColor: 'rgba(0, 0, 0, 0.6)',
    width: 64,
    height: 64,
    borderRadius: 32,
    justifyContent: 'center',
    alignItems: 'center',
  },
  playArrow: {
    color: 'white',
  },
  video: Platform.Version >= 24 ? {} : {
    backgroundColor: 'black',
  },
  controls: {
    backgroundColor: 'rgba(0, 0, 0, 0.6)',
    height: 48,
    marginTop: -48,
    flexDirection: 'row',
    alignItems: 'center',
  },
  playControl: {
    color: 'white',
    padding: 8,
  },
  extraControl: {
    color: 'white',
    padding: 8,
  },
  seekBar: {
    alignItems: 'center',
    height: 30,
    flexGrow: 1,
    flexDirection: 'row',
    paddingHorizontal: 10,
    marginLeft: -10,
    marginRight: -5,
  },
  seekBarFullWidth: {
    marginLeft: 0,
    marginRight: 0,
    paddingHorizontal: 0,
    marginTop: -3,
    height: 3,
  },
  seekBarProgress: {
    height: 3,
    backgroundColor: '#F00',
  },
  seekBarKnob: {
    width: 20,
    height: 20,
    marginHorizontal: -8,
    marginVertical: -10,
    borderRadius: 10,
    backgroundColor: '#F00',
    transform: [{ scale: 0.8 }],
    zIndex: 1,
  },
  seekBarBackground: {
    backgroundColor: 'rgba(255, 255, 255, 0.5)',
    height: 3,
  },
  overlayButton: {
    flex: 1,
  },
});

export default class VideoPlayer extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isStarted: props.autoplay,
      isPlaying: props.autoplay,
      width: 200,
      progress: 0,
      isMuted: props.defaultMuted,
      isControlsVisible: !props.hideControlsOnStart,
      duration: 0,
      isSeeking: false,
    };

    this.seekBarWidth = 200;
    this.wasPlayingBeforeSeek = props.autoplay;
    this.seekTouchStart = 0;
    this.seekProgressStart = 0;

    this.onLayout = this.onLayout.bind(this);
    this.onStartPress = this.onStartPress.bind(this);
    this.onProgress = this.onProgress.bind(this);
    this.onEnd = this.onEnd.bind(this);
    this.onLoad = this.onLoad.bind(this);
    this.onPlayPress = this.onPlayPress.bind(this);
    this.onMutePress = this.onMutePress.bind(this);
    this.showControls = this.showControls.bind(this);
    this.onToggleFullScreen = this.onToggleFullScreen.bind(this);
    this.onSeekBarLayout = this.onSeekBarLayout.bind(this);
    this.onSeekGrant = this.onSeekGrant.bind(this);
    this.onSeekRelease = this.onSeekRelease.bind(this);
    this.onSeek = this.onSeek.bind(this);
  }

  componentDidMount() {
    if (this.props.autoplay) {
      this.hideControls();
    }
  }

  componentWillUnmount() {
    if (this.controlsTimeout) {
      clearTimeout(this.controlsTimeout);
      this.controlsTimeout = null;
    }
  }

  onLayout(event) {
    const { width } = event.nativeEvent.layout;
    this.setState({
      width,
    });
  }

  onStartPress() {
    if (this.props.onStart) {
      this.props.onStart();
    }

    this.setState(state => ({
      isPlaying: true,
      isStarted: true,
      progress: state.progress === 1 ? 0 : state.progress,
    }));

    this.hideControls();
  }

  onProgress(event) {
    if (this.state.isSeeking) {
      return;
    }
    if (this.props.onProgress) {
      this.props.onProgress(event);
    }
    this.setState({
      progress: event.currentTime / (this.props.duration || this.state.duration),
    });
  }

  onEnd(event) {
    if (this.props.onEnd) {
      this.props.onEnd(event);
    }

    if (this.props.endWithThumbnail) {
      this.setState({ isStarted: false });
      this.player.dismissFullscreenPlayer();
    }

    this.setState({ progress: 1 });

    this.player.seek(0);
    if (!this.props.loop) {
      this.setState({
        isPlaying: false,
      });
    }
  }

  onLoad(event) {
    if (this.props.onLoad) {
      this.props.onLoad(event);
    }

    const { duration } = event;
    this.setState({ duration });
  }

  onPlayPress() {
    if (this.props.onPlayPress) {
      this.props.onPlayPress();
    }

    this.setState({
      isPlaying: !this.state.isPlaying,
    });
    this.showControls();
  }

  onMutePress() {
    this.setState({
      isMuted: !this.state.isMuted,
    });
    this.showControls();
  }

  onToggleFullScreen() {
    this.player.presentFullscreenPlayer();
  }

  onSeekBarLayout({ nativeEvent }) {
    const customStyle = this.props.customStyles.seekBar;
    let padding = 0;
    if (customStyle && customStyle.paddingHorizontal) {
      padding = customStyle.paddingHorizontal * 2;
    } else if (customStyle) {
      padding = customStyle.paddingLeft || 0;
      padding += customStyle.paddingRight ? customStyle.paddingRight : 0;
    } else {
      padding = 20;
    }

    this.seekBarWidth = nativeEvent.layout.width - padding;
  }

  onSeekStartResponder() {
    return true;
  }

  onSeekMoveResponder() {
    return true;
  }

  onSeekGrant(e) {
    this.seekTouchStart = e.nativeEvent.pageX;
    this.seekProgressStart = this.state.progress;
    this.wasPlayingBeforeSeek = this.state.isPlaying;
    this.setState({
      isSeeking: true,
      isPlaying: false,
    });
  }

  onSeekRelease() {
    this.setState({
      isSeeking: false,
      isPlaying: this.wasPlayingBeforeSeek,
    });
    this.showControls();
  }

  onSeek(e) {
    const diff = e.nativeEvent.pageX - this.seekTouchStart;
    const ratio = 100 / this.seekBarWidth;
    const progress = this.seekProgressStart + ((ratio * diff) / 100);

    this.setState({
      progress,
    });

    this.player.seek(progress * this.state.duration);
  }

  getSizeStyles() {
    const { videoWidth, videoHeight } = this.props;
    const { width } = this.state;
    const ratio = videoHeight / videoWidth;
    return {
      height: width * ratio,
      width,
    };
  }

  hideControls() {
    if (this.props.onHideControls) {
      this.props.onHideControls();
    }

    if (this.props.disableControlsAutoHide) {
      return;
    }

    if (this.controlsTimeout) {
      clearTimeout(this.controlsTimeout);
      this.controlsTimeout = null;
    }
    this.controlsTimeout = setTimeout(() => {
      this.setState({ isControlsVisible: false });
    }, this.props.controlsTimeout);
  }

  showControls() {
    if (this.props.onShowControls) {
      this.props.onShowControls();
    }

    this.setState({
      isControlsVisible: true,
    });
    this.hideControls();
  }

  seek(t) {
    this.player.seek(t);
  }

  stop() {
    this.setState({
      isPlaying: false,
      progress: 0,
    });
    this.seek(0);
    this.showControls();
  }

  pause() {
    this.setState({
      isPlaying: false,
    });
    this.showControls();
  }

  resume() {
    this.setState({
      isPlaying: true,
    });
    this.showControls();
  }

  renderStartButton() {
    const { customStyles } = this.props;
    return (
      <TouchableOpacity
        style={[styles.playButton, customStyles.playButton]}
        onPress={this.onStartPress}
      >
        <Icon style={[styles.playArrow, customStyles.playArrow]} name="play-arrow" size={42} />
      </TouchableOpacity>
    );
  }

  renderThumbnail() {
    const { thumbnail, style, customStyles, ...props } = this.props;
    return (
      <BackgroundImage
        {...props}
        style={[
          styles.thumbnail,
          this.getSizeStyles(),
          style,
          customStyles.thumbnail,
        ]}
        source={thumbnail}
      >
        {this.renderStartButton()}
      </BackgroundImage>
    );
  }

  renderSeekBar(fullWidth) {
    const { customStyles, disableSeek } = this.props;
    return (
      <View
        style={[
          styles.seekBar,
          fullWidth ? styles.seekBarFullWidth : {},
          customStyles.seekBar,
          fullWidth ? customStyles.seekBarFullWidth : {},
        ]}
        onLayout={this.onSeekBarLayout}
      >
        <View
          style={[
            { flexGrow: this.state.progress },
            styles.seekBarProgress,
            customStyles.seekBarProgress,
          ]}
        />
        { !fullWidth && !disableSeek ? (
          <View
            style={[
              styles.seekBarKnob,
              customStyles.seekBarKnob,
              this.state.isSeeking ? { transform: [{ scale: 1 }] } : {},
              this.state.isSeeking ? customStyles.seekBarKnobSeeking : {},
            ]}
            hitSlop={{ top: 20, bottom: 20, left: 10, right: 20 }}
            onStartShouldSetResponder={this.onSeekStartResponder}
            onMoveShouldSetPanResponder={this.onSeekMoveResponder}
            onResponderGrant={this.onSeekGrant}
            onResponderMove={this.onSeek}
            onResponderRelease={this.onSeekRelease}
            onResponderTerminate={this.onSeekRelease}
          />
        ) : null }
        <View style={[
          styles.seekBarBackground,
          { flexGrow: 1 - this.state.progress },
          customStyles.seekBarBackground,
        ]} />
      </View>
    );
  }

  renderControls() {
    const { customStyles } = this.props;
    return (
      <View style={[styles.controls, customStyles.controls]}>
        <TouchableOpacity
          onPress={this.onPlayPress}
          style={[customStyles.controlButton, customStyles.playControl]}
        >
          <Icon
            style={[styles.playControl, customStyles.controlIcon, customStyles.playIcon]}
            name={this.state.isPlaying ? 'pause' : 'play-arrow'}
            size={32}
          />
        </TouchableOpacity>
        {this.renderSeekBar()}
        {this.props.muted ? null : (
          <TouchableOpacity onPress={this.onMutePress} style={customStyles.controlButton}>
            <Icon
              style={[styles.extraControl, customStyles.controlIcon]}
              name={this.state.isMuted ? 'volume-off' : 'volume-up'}
              size={24}
            />
          </TouchableOpacity>
        )}
        {(Platform.OS === 'android' || this.props.disableFullscreen) ? null : (
          <TouchableOpacity onPress={this.onToggleFullScreen} style={customStyles.controlButton}>
            <Icon
              style={[styles.extraControl, customStyles.controlIcon]}
              name="fullscreen"
              size={32}
            />
          </TouchableOpacity>
        )}
      </View>
    );
  }

  renderVideo() {
    const {
      video,
      style,
      resizeMode,
      pauseOnPress,
      fullScreenOnLongPress,
      customStyles,
      ...props
    } = this.props;
    return (
      <View style={customStyles.videoWrapper}>
        <Video
          {...props}
          style={[
            styles.video,
            this.getSizeStyles(),
            style,
            customStyles.video,
          ]}
          ref={p => { this.player = p; }}
          muted={this.props.muted || this.state.isMuted}
          paused={!this.state.isPlaying}
          onProgress={this.onProgress}
          onEnd={this.onEnd}
          onLoad={this.onLoad}
          source={video}
          resizeMode={resizeMode}
        />
        <View
          style={[
            this.getSizeStyles(),
            { marginTop: -this.getSizeStyles().height },
          ]}
        >
          <TouchableOpacity
            style={styles.overlayButton}
            onPress={() => {
              this.showControls();
              if (pauseOnPress)
                this.onPlayPress();
            }}
            onLongPress={() => {
              if (fullScreenOnLongPress && Platform.OS !== 'android')
                this.onToggleFullScreen();
            }}
          />
        </View>
        {((!this.state.isPlaying) || this.state.isControlsVisible)
          ? this.renderControls() : this.renderSeekBar(true)}
      </View>
    );
  }

  renderContent() {
    const { thumbnail, style } = this.props;
    const { isStarted } = this.state;

    if (!isStarted && thumbnail) {
      return this.renderThumbnail();
    } else if (!isStarted) {
      return (
        <View style={[styles.preloadingPlaceholder, this.getSizeStyles(), style]}>
          {this.renderStartButton()}
        </View>
      );
    }
    return this.renderVideo();
  }

  render() {
    return (
      <View onLayout={this.onLayout} style={this.props.customStyles.wrapper}>
        {this.renderContent()}
      </View>
    );
  }
}

VideoPlayer.propTypes = {
  video: Video.propTypes.source,
  thumbnail: Image.propTypes.source,
  videoWidth: PropTypes.number,
  videoHeight: PropTypes.number,
  duration: PropTypes.number,
  autoplay: PropTypes.bool,
  defaultMuted: PropTypes.bool,
  muted: PropTypes.bool,
  style: ViewPropTypes.style,
  controlsTimeout: PropTypes.number,
  disableControlsAutoHide: PropTypes.bool,
  disableFullscreen: PropTypes.bool,
  loop: PropTypes.bool,
  resizeMode: Video.propTypes.resizeMode,
  hideControlsOnStart: PropTypes.bool,
  endWithThumbnail: PropTypes.bool,
  disableSeek: PropTypes.bool,
  pauseOnPress: PropTypes.bool,
  fullScreenOnLongPress: PropTypes.bool,
  customStyles: PropTypes.shape({
    wrapper: ViewPropTypes.style,
    video: Video.propTypes.style,
    videoWrapper: ViewPropTypes.style,
    controls: ViewPropTypes.style,
    playControl: TouchableOpacity.propTypes.style,
    controlButton: TouchableOpacity.propTypes.style,
    controlIcon: Icon.propTypes.style,
    playIcon: Icon.propTypes.style,
    seekBar: ViewPropTypes.style,
    seekBarFullWidth: ViewPropTypes.style,
    seekBarProgress: ViewPropTypes.style,
    seekBarKnob: ViewPropTypes.style,
    seekBarKnobSeeking: ViewPropTypes.style,
    seekBarBackground: ViewPropTypes.style,
    thumbnail: Image.propTypes.style,
    playButton: TouchableOpacity.propTypes.style,
    playArrow: Icon.propTypes.style,
  }),
  onEnd: PropTypes.func,
  onProgress: PropTypes.func,
  onLoad: PropTypes.func,
  onStart: PropTypes.func,
  onPlayPress: PropTypes.func,
  onHideControls: PropTypes.func,
  onShowControls: PropTypes.func,
};

VideoPlayer.defaultProps = {
  videoWidth: 1280,
  videoHeight: 720,
  autoplay: false,
  controlsTimeout: 2000,
  loop: false,
  resizeMode: 'contain',
  disableSeek: false,
  pauseOnPress: false,
  fullScreenOnLongPress: false,
  customStyles: {},
};
<commit_msg>Fix video repeating when loop turned off<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Image, ImageBackground, Platform, StyleSheet, TouchableOpacity, View, ViewPropTypes } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Video from 'react-native-video'; // eslint-disable-line

const BackgroundImage = ImageBackground || Image; // fall back to Image if RN < 0.46

const styles = StyleSheet.create({
  preloadingPlaceholder: {
    backgroundColor: 'black',
    justifyContent: 'center',
    alignItems: 'center',
  },
  thumbnail: {
    backgroundColor: 'black',
    justifyContent: 'center',
    alignItems: 'center',
  },
  playButton: {
    backgroundColor: 'rgba(0, 0, 0, 0.6)',
    width: 64,
    height: 64,
    borderRadius: 32,
    justifyContent: 'center',
    alignItems: 'center',
  },
  playArrow: {
    color: 'white',
  },
  video: Platform.Version >= 24 ? {} : {
    backgroundColor: 'black',
  },
  controls: {
    backgroundColor: 'rgba(0, 0, 0, 0.6)',
    height: 48,
    marginTop: -48,
    flexDirection: 'row',
    alignItems: 'center',
  },
  playControl: {
    color: 'white',
    padding: 8,
  },
  extraControl: {
    color: 'white',
    padding: 8,
  },
  seekBar: {
    alignItems: 'center',
    height: 30,
    flexGrow: 1,
    flexDirection: 'row',
    paddingHorizontal: 10,
    marginLeft: -10,
    marginRight: -5,
  },
  seekBarFullWidth: {
    marginLeft: 0,
    marginRight: 0,
    paddingHorizontal: 0,
    marginTop: -3,
    height: 3,
  },
  seekBarProgress: {
    height: 3,
    backgroundColor: '#F00',
  },
  seekBarKnob: {
    width: 20,
    height: 20,
    marginHorizontal: -8,
    marginVertical: -10,
    borderRadius: 10,
    backgroundColor: '#F00',
    transform: [{ scale: 0.8 }],
    zIndex: 1,
  },
  seekBarBackground: {
    backgroundColor: 'rgba(255, 255, 255, 0.5)',
    height: 3,
  },
  overlayButton: {
    flex: 1,
  },
});

export default class VideoPlayer extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isStarted: props.autoplay,
      isPlaying: props.autoplay,
      width: 200,
      progress: 0,
      isMuted: props.defaultMuted,
      isControlsVisible: !props.hideControlsOnStart,
      duration: 0,
      isSeeking: false,
    };

    this.seekBarWidth = 200;
    this.wasPlayingBeforeSeek = props.autoplay;
    this.seekTouchStart = 0;
    this.seekProgressStart = 0;

    this.onLayout = this.onLayout.bind(this);
    this.onStartPress = this.onStartPress.bind(this);
    this.onProgress = this.onProgress.bind(this);
    this.onEnd = this.onEnd.bind(this);
    this.onLoad = this.onLoad.bind(this);
    this.onPlayPress = this.onPlayPress.bind(this);
    this.onMutePress = this.onMutePress.bind(this);
    this.showControls = this.showControls.bind(this);
    this.onToggleFullScreen = this.onToggleFullScreen.bind(this);
    this.onSeekBarLayout = this.onSeekBarLayout.bind(this);
    this.onSeekGrant = this.onSeekGrant.bind(this);
    this.onSeekRelease = this.onSeekRelease.bind(this);
    this.onSeek = this.onSeek.bind(this);
  }

  componentDidMount() {
    if (this.props.autoplay) {
      this.hideControls();
    }
  }

  componentWillUnmount() {
    if (this.controlsTimeout) {
      clearTimeout(this.controlsTimeout);
      this.controlsTimeout = null;
    }
  }

  onLayout(event) {
    const { width } = event.nativeEvent.layout;
    this.setState({
      width,
    });
  }

  onStartPress() {
    if (this.props.onStart) {
      this.props.onStart();
    }

    this.setState(state => ({
      isPlaying: true,
      isStarted: true,
      progress: state.progress === 1 ? 0 : state.progress,
    }));

    this.hideControls();
  }

  onProgress(event) {
    if (this.state.isSeeking) {
      return;
    }
    if (this.props.onProgress) {
      this.props.onProgress(event);
    }
    this.setState({
      progress: event.currentTime / (this.props.duration || this.state.duration),
    });
  }

  onEnd(event) {
    if (this.props.onEnd) {
      this.props.onEnd(event);
    }

    if (this.props.endWithThumbnail) {
      this.setState({ isStarted: false });
      this.player.dismissFullscreenPlayer();
    }

    this.setState({ progress: 1 });

    if (!this.props.loop) {
      this.setState(
        { isPlaying: false },
        () => this.player.seek(0)
      );
    } else {
      this.player.seek(0);
    }
  }

  onLoad(event) {
    if (this.props.onLoad) {
      this.props.onLoad(event);
    }

    const { duration } = event;
    this.setState({ duration });
  }

  onPlayPress() {
    if (this.props.onPlayPress) {
      this.props.onPlayPress();
    }

    this.setState({
      isPlaying: !this.state.isPlaying,
    });
    this.showControls();
  }

  onMutePress() {
    this.setState({
      isMuted: !this.state.isMuted,
    });
    this.showControls();
  }

  onToggleFullScreen() {
    this.player.presentFullscreenPlayer();
  }

  onSeekBarLayout({ nativeEvent }) {
    const customStyle = this.props.customStyles.seekBar;
    let padding = 0;
    if (customStyle && customStyle.paddingHorizontal) {
      padding = customStyle.paddingHorizontal * 2;
    } else if (customStyle) {
      padding = customStyle.paddingLeft || 0;
      padding += customStyle.paddingRight ? customStyle.paddingRight : 0;
    } else {
      padding = 20;
    }

    this.seekBarWidth = nativeEvent.layout.width - padding;
  }

  onSeekStartResponder() {
    return true;
  }

  onSeekMoveResponder() {
    return true;
  }

  onSeekGrant(e) {
    this.seekTouchStart = e.nativeEvent.pageX;
    this.seekProgressStart = this.state.progress;
    this.wasPlayingBeforeSeek = this.state.isPlaying;
    this.setState({
      isSeeking: true,
      isPlaying: false,
    });
  }

  onSeekRelease() {
    this.setState({
      isSeeking: false,
      isPlaying: this.wasPlayingBeforeSeek,
    });
    this.showControls();
  }

  onSeek(e) {
    const diff = e.nativeEvent.pageX - this.seekTouchStart;
    const ratio = 100 / this.seekBarWidth;
    const progress = this.seekProgressStart + ((ratio * diff) / 100);

    this.setState({
      progress,
    });

    this.player.seek(progress * this.state.duration);
  }

  getSizeStyles() {
    const { videoWidth, videoHeight } = this.props;
    const { width } = this.state;
    const ratio = videoHeight / videoWidth;
    return {
      height: width * ratio,
      width,
    };
  }

  hideControls() {
    if (this.props.onHideControls) {
      this.props.onHideControls();
    }

    if (this.props.disableControlsAutoHide) {
      return;
    }

    if (this.controlsTimeout) {
      clearTimeout(this.controlsTimeout);
      this.controlsTimeout = null;
    }
    this.controlsTimeout = setTimeout(() => {
      this.setState({ isControlsVisible: false });
    }, this.props.controlsTimeout);
  }

  showControls() {
    if (this.props.onShowControls) {
      this.props.onShowControls();
    }

    this.setState({
      isControlsVisible: true,
    });
    this.hideControls();
  }

  seek(t) {
    this.player.seek(t);
  }

  stop() {
    this.setState({
      isPlaying: false,
      progress: 0,
    });
    this.seek(0);
    this.showControls();
  }

  pause() {
    this.setState({
      isPlaying: false,
    });
    this.showControls();
  }

  resume() {
    this.setState({
      isPlaying: true,
    });
    this.showControls();
  }

  renderStartButton() {
    const { customStyles } = this.props;
    return (
      <TouchableOpacity
        style={[styles.playButton, customStyles.playButton]}
        onPress={this.onStartPress}
      >
        <Icon style={[styles.playArrow, customStyles.playArrow]} name="play-arrow" size={42} />
      </TouchableOpacity>
    );
  }

  renderThumbnail() {
    const { thumbnail, style, customStyles, ...props } = this.props;
    return (
      <BackgroundImage
        {...props}
        style={[
          styles.thumbnail,
          this.getSizeStyles(),
          style,
          customStyles.thumbnail,
        ]}
        source={thumbnail}
      >
        {this.renderStartButton()}
      </BackgroundImage>
    );
  }

  renderSeekBar(fullWidth) {
    const { customStyles, disableSeek } = this.props;
    return (
      <View
        style={[
          styles.seekBar,
          fullWidth ? styles.seekBarFullWidth : {},
          customStyles.seekBar,
          fullWidth ? customStyles.seekBarFullWidth : {},
        ]}
        onLayout={this.onSeekBarLayout}
      >
        <View
          style={[
            { flexGrow: this.state.progress },
            styles.seekBarProgress,
            customStyles.seekBarProgress,
          ]}
        />
        { !fullWidth && !disableSeek ? (
          <View
            style={[
              styles.seekBarKnob,
              customStyles.seekBarKnob,
              this.state.isSeeking ? { transform: [{ scale: 1 }] } : {},
              this.state.isSeeking ? customStyles.seekBarKnobSeeking : {},
            ]}
            hitSlop={{ top: 20, bottom: 20, left: 10, right: 20 }}
            onStartShouldSetResponder={this.onSeekStartResponder}
            onMoveShouldSetPanResponder={this.onSeekMoveResponder}
            onResponderGrant={this.onSeekGrant}
            onResponderMove={this.onSeek}
            onResponderRelease={this.onSeekRelease}
            onResponderTerminate={this.onSeekRelease}
          />
        ) : null }
        <View style={[
          styles.seekBarBackground,
          { flexGrow: 1 - this.state.progress },
          customStyles.seekBarBackground,
        ]} />
      </View>
    );
  }

  renderControls() {
    const { customStyles } = this.props;
    return (
      <View style={[styles.controls, customStyles.controls]}>
        <TouchableOpacity
          onPress={this.onPlayPress}
          style={[customStyles.controlButton, customStyles.playControl]}
        >
          <Icon
            style={[styles.playControl, customStyles.controlIcon, customStyles.playIcon]}
            name={this.state.isPlaying ? 'pause' : 'play-arrow'}
            size={32}
          />
        </TouchableOpacity>
        {this.renderSeekBar()}
        {this.props.muted ? null : (
          <TouchableOpacity onPress={this.onMutePress} style={customStyles.controlButton}>
            <Icon
              style={[styles.extraControl, customStyles.controlIcon]}
              name={this.state.isMuted ? 'volume-off' : 'volume-up'}
              size={24}
            />
          </TouchableOpacity>
        )}
        {(Platform.OS === 'android' || this.props.disableFullscreen) ? null : (
          <TouchableOpacity onPress={this.onToggleFullScreen} style={customStyles.controlButton}>
            <Icon
              style={[styles.extraControl, customStyles.controlIcon]}
              name="fullscreen"
              size={32}
            />
          </TouchableOpacity>
        )}
      </View>
    );
  }

  renderVideo() {
    const {
      video,
      style,
      resizeMode,
      pauseOnPress,
      fullScreenOnLongPress,
      customStyles,
      ...props
    } = this.props;
    return (
      <View style={customStyles.videoWrapper}>
        <Video
          {...props}
          style={[
            styles.video,
            this.getSizeStyles(),
            style,
            customStyles.video,
          ]}
          ref={p => { this.player = p; }}
          muted={this.props.muted || this.state.isMuted}
          paused={!this.state.isPlaying}
          onProgress={this.onProgress}
          onEnd={this.onEnd}
          onLoad={this.onLoad}
          source={video}
          resizeMode={resizeMode}
        />
        <View
          style={[
            this.getSizeStyles(),
            { marginTop: -this.getSizeStyles().height },
          ]}
        >
          <TouchableOpacity
            style={styles.overlayButton}
            onPress={() => {
              this.showControls();
              if (pauseOnPress)
                this.onPlayPress();
            }}
            onLongPress={() => {
              if (fullScreenOnLongPress && Platform.OS !== 'android')
                this.onToggleFullScreen();
            }}
          />
        </View>
        {((!this.state.isPlaying) || this.state.isControlsVisible)
          ? this.renderControls() : this.renderSeekBar(true)}
      </View>
    );
  }

  renderContent() {
    const { thumbnail, style } = this.props;
    const { isStarted } = this.state;

    if (!isStarted && thumbnail) {
      return this.renderThumbnail();
    } else if (!isStarted) {
      return (
        <View style={[styles.preloadingPlaceholder, this.getSizeStyles(), style]}>
          {this.renderStartButton()}
        </View>
      );
    }
    return this.renderVideo();
  }

  render() {
    return (
      <View onLayout={this.onLayout} style={this.props.customStyles.wrapper}>
        {this.renderContent()}
      </View>
    );
  }
}

VideoPlayer.propTypes = {
  video: Video.propTypes.source,
  thumbnail: Image.propTypes.source,
  videoWidth: PropTypes.number,
  videoHeight: PropTypes.number,
  duration: PropTypes.number,
  autoplay: PropTypes.bool,
  defaultMuted: PropTypes.bool,
  muted: PropTypes.bool,
  style: ViewPropTypes.style,
  controlsTimeout: PropTypes.number,
  disableControlsAutoHide: PropTypes.bool,
  disableFullscreen: PropTypes.bool,
  loop: PropTypes.bool,
  resizeMode: Video.propTypes.resizeMode,
  hideControlsOnStart: PropTypes.bool,
  endWithThumbnail: PropTypes.bool,
  disableSeek: PropTypes.bool,
  pauseOnPress: PropTypes.bool,
  fullScreenOnLongPress: PropTypes.bool,
  customStyles: PropTypes.shape({
    wrapper: ViewPropTypes.style,
    video: Video.propTypes.style,
    videoWrapper: ViewPropTypes.style,
    controls: ViewPropTypes.style,
    playControl: TouchableOpacity.propTypes.style,
    controlButton: TouchableOpacity.propTypes.style,
    controlIcon: Icon.propTypes.style,
    playIcon: Icon.propTypes.style,
    seekBar: ViewPropTypes.style,
    seekBarFullWidth: ViewPropTypes.style,
    seekBarProgress: ViewPropTypes.style,
    seekBarKnob: ViewPropTypes.style,
    seekBarKnobSeeking: ViewPropTypes.style,
    seekBarBackground: ViewPropTypes.style,
    thumbnail: Image.propTypes.style,
    playButton: TouchableOpacity.propTypes.style,
    playArrow: Icon.propTypes.style,
  }),
  onEnd: PropTypes.func,
  onProgress: PropTypes.func,
  onLoad: PropTypes.func,
  onStart: PropTypes.func,
  onPlayPress: PropTypes.func,
  onHideControls: PropTypes.func,
  onShowControls: PropTypes.func,
};

VideoPlayer.defaultProps = {
  videoWidth: 1280,
  videoHeight: 720,
  autoplay: false,
  controlsTimeout: 2000,
  loop: false,
  resizeMode: 'contain',
  disableSeek: false,
  pauseOnPress: false,
  fullScreenOnLongPress: false,
  customStyles: {},
};
<|endoftext|>


<commit_before>/* global window,document */
'use strict';

const duration = 1000;

const ease = function(t, b, c, d) {
  if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
  return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
};

const animate = function(startTime, start, end) {
  const time = new Date().getTime();
  const difference = end - start;
  const goingUp = difference < 0;

  if (difference === 0) {
    return;
  }

  let to = Math.round(ease(time - startTime, start, difference, duration));

  if (!goingUp && to > end) {
    to = end;
  }

  if (goingUp && to < end) {
    to = end;
  }

  window.scrollTo(0, to);

  if (to === end) {
    return;
  }

  if (to < 0) {
    return;
  }

  window.requestAnimationFrame(() => animate(startTime, start, end));
};

const scroll = function(el) {
  el.addEventListener('click', (e) => {
    e.preventDefault();
    const target = document.querySelector(el.getAttribute('href'));
    const rect = target.getBoundingClientRect();
    const offset = rect.top + window.scrollY;
    const startTime = new Date();
    animate(startTime.getTime(), window.scrollY, offset);
  });
};

const init = function(query) {
  if (!query) {
    query = document.querySelectorAll('[data-smooth]');
  }
  for (let i = 0, c = query.length; i < c; i++) {
    const el = query[i];
    scroll(el);
  }
};

export default init;

window.addEventListener('DOMContentLoaded', () => {
  init();
});
<commit_msg>set hash<commit_after>/* global window,document */
'use strict';

const duration = 1000;

const ease = function(t, b, c, d) {
  if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
  return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
};

const animate = function(startTime, start, end) {
  const time = new Date().getTime();
  const difference = end - start;
  const goingUp = difference < 0;

  if (difference === 0) {
    return;
  }

  let to = Math.round(ease(time - startTime, start, difference, duration));

  if (!goingUp && to > end) {
    to = end;
  }

  if (goingUp && to < end) {
    to = end;
  }

  window.scrollTo(0, to);

  if (to === end) {
    return;
  }

  if (to < 0) {
    return;
  }

  window.requestAnimationFrame(() => animate(startTime, start, end));
};

const scroll = function(el) {
  el.addEventListener('click', (e) => {
    e.preventDefault();
    const hash = el.getAttribute('href');
    const target = document.querySelector(hash);
    const rect = target.getBoundingClientRect();
    const offset = rect.top + window.scrollY;
    const startTime = new Date();
    animate(startTime.getTime(), window.scrollY, offset);
    window.location.hash = hash;
  });
};

const init = function(query) {
  if (!query) {
    query = document.querySelectorAll('[data-smooth]');
  }
  for (let i = 0, c = query.length; i < c; i++) {
    const el = query[i];
    scroll(el);
  }
};

export default init;

window.addEventListener('DOMContentLoaded', () => {
  init();
});
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var stack = require('simple-stack-common');
var envs = require('envs');
var assets = require('simple-assets');
var urlparse = require('url').parse;
var extname = require('path').extname;

/**
 * Forwarding headers
 */

var headers = {
  host: 'x-orig-host',
  path: 'x-orig-path',
  port: 'x-orig-port',
  proto: 'x-orig-proto'
};

/**
 * Create a poe-kit
 */

exports = module.exports = function(opts) {
  opts = opts || {};

  // get the root of the app
  var root = opts.root || process.cwd();

  // create the app
  var app = stack({
    base: headers
  });

  // expose the app root
  app.set('root', root);

  // use jade as the view engine
  app.set('view engine', 'jade');
  app.engine('jade', require('jade').__express);

  // load the package.json for locals
  var pkg = loadPackage(root);

  app.locals({
    app: pkg.name,
    description: pkg.description,
    author: pkg.author,
    env: {
      BROWSER_ENV: envs('NODE_ENV', 'production')
    }
  });

  // expose a way to set browser environment variables
  app.env = function(key, value) {
    app.locals.env[key] = value;
  };

  // serve static assets
  app.useBefore('router', '/build', 'build-headers', function(req, res, next) {
    var env = req.get('x-env');
    var maxAge = env === 'production' ? 31557600 : 0;
    res.set('cache-control', 'public, max-age=' + maxAge);
    next();
  });
  app.useBefore('router', '/build', 'build', stack.middleware.static(root + '/build'));

  // setup the cookie parser
  app.useBefore('router', stack.middleware.cookieParser());

  // set asset paths
  app.useBefore('router', initAssetLocals(opts.cdn || envs('CDN_URL') || ''));

  // set enabled feature flags
  app.useBefore('router', initFeatureFlags(opts.enabledFeatures || envs('ENABLED_FEATURES') || ''));

  // serve a noscript page
  app.get('/noscript', function(req, res) {
    res.cookie('noscript', '1');
    res.render('noscript');
  });

  return app;
};

/**
 * Expose the middleware
 */

exports.middleware = stack.middleware;

/**
 * Load the package.json from root
 *
 * @param {String} root
 * @return {Object}
 */

function loadPackage(root) {
  try {
    return require(root + '/package.json');
  } catch (err) {
    return {};
  };
}

/**
 * Initialize the asset locals middleware
 */

function initAssetLocals(cdn) {
  function lookup(file, base, min) {
    return cdn + base + '/' + assets(file, min);
  }

  function styles(min, base) {
    return [
      lookup('build/style.css', base, min)
    ];
  }

  function scripts(min, base) {
    return [
      lookup('build/vendor.js', base, min),
      lookup('build/app.js', base, min)
    ];
  }

  return function assetLocals(req, res, next) {
    var min = req.get('x-env') === 'production';
    var base = urlparse(req.base).pathname;

    if (base === '/') base = '';

    function asset(path, doMin) {
      if (typeof doMin === 'undefined') doMin = min;
      return lookup(path, base, doMin);
    }

    res.locals({
      pretty: min,
      styles: styles(min, base),
      scripts: scripts(min, base),
      requireScript: asset('build/require.js'),
      ieFixesScript: asset('build/ie-fixes.js'),
      loader: asset('build/script.js'),
      noscriptRedirect: !req.cookies.noscript,
      pretty: !min,
      basePath: req.get('x-orig-path') || '/',
      asset: asset
    });
    next();
  };
}

/**
 * Initialize the feature flags middleware
 */

function initFeatureFlags(enabled) {
  return function features(req, res, next) {
    if (req.get('x-env') !== 'production') return next();
    if (enabled && enabled !== req.cookies.features) res.cookie('features', enabled);
    next();
  };
}
<commit_msg>allow x-cdn-url override<commit_after>/**
 * Module dependencies
 */

var stack = require('simple-stack-common');
var envs = require('envs');
var assets = require('simple-assets');
var urlparse = require('url').parse;
var extname = require('path').extname;

/**
 * Forwarding headers
 */

var headers = {
  host: 'x-orig-host',
  path: 'x-orig-path',
  port: 'x-orig-port',
  proto: 'x-orig-proto'
};

/**
 * Create a poe-kit
 */

exports = module.exports = function(opts) {
  opts = opts || {};

  // get the root of the app
  var root = opts.root || process.cwd();

  // create the app
  var app = stack({
    base: headers
  });

  // expose the app root
  app.set('root', root);

  // use jade as the view engine
  app.set('view engine', 'jade');
  app.engine('jade', require('jade').__express);

  // load the package.json for locals
  var pkg = loadPackage(root);

  app.locals({
    app: pkg.name,
    description: pkg.description,
    author: pkg.author,
    env: {
      BROWSER_ENV: envs('NODE_ENV', 'production')
    }
  });

  // expose a way to set browser environment variables
  app.env = function(key, value) {
    app.locals.env[key] = value;
  };

  // serve static assets
  app.useBefore('router', '/build', 'build-headers', function(req, res, next) {
    var env = req.get('x-env');
    var maxAge = env === 'production' ? 31557600 : 0;
    res.set('cache-control', 'public, max-age=' + maxAge);
    next();
  });
  app.useBefore('router', '/build', 'build', stack.middleware.static(root + '/build'));

  // setup the cookie parser
  app.useBefore('router', stack.middleware.cookieParser());

  // set asset paths
  app.useBefore('router', initAssetLocals(opts.cdn || envs('CDN_URL') || ''));

  // set enabled feature flags
  app.useBefore('router', initFeatureFlags(opts.enabledFeatures || envs('ENABLED_FEATURES') || ''));

  // serve a noscript page
  app.get('/noscript', function(req, res) {
    res.cookie('noscript', '1');
    res.render('noscript');
  });

  return app;
};

/**
 * Expose the middleware
 */

exports.middleware = stack.middleware;

/**
 * Load the package.json from root
 *
 * @param {String} root
 * @return {Object}
 */

function loadPackage(root) {
  try {
    return require(root + '/package.json');
  } catch (err) {
    return {};
  };
}

/**
 * Initialize the asset locals middleware
 */

function initAssetLocals(cdn) {
  function lookup(file, base, min) {
    return base + '/' + assets(file, min);
  }

  function styles(min, base) {
    return [
      lookup('build/style.css', base, min)
    ];
  }

  function scripts(min, base) {
    return [
      lookup('build/vendor.js', base, min),
      lookup('build/app.js', base, min)
    ];
  }

  return function assetLocals(req, res, next) {
    var min = req.get('x-env') === 'production';
    var base = urlparse(req.base).pathname;

    if (base === '/') base = '';
    base = (req.get('x-cdn-url') || cdn || '') + base;

    function asset(path, doMin) {
      if (typeof doMin === 'undefined') doMin = min;
      return lookup(path, base, doMin);
    }

    res.locals({
      pretty: min,
      styles: styles(min, base),
      scripts: scripts(min, base),
      requireScript: asset('build/require.js'),
      ieFixesScript: asset('build/ie-fixes.js'),
      loader: asset('build/script.js'),
      noscriptRedirect: !req.cookies.noscript,
      pretty: !min,
      basePath: req.get('x-orig-path') || '/',
      asset: asset
    });
    next();
  };
}

/**
 * Initialize the feature flags middleware
 */

function initFeatureFlags(enabled) {
  return function features(req, res, next) {
    if (req.get('x-env') !== 'production') return next();
    if (enabled && enabled !== req.cookies.features) res.cookie('features', enabled);
    next();
  };
}
<|endoftext|>