<commit_before>'use strict'
var fs = require('graceful-fs')
var path = require('path')
var zlib = require('zlib')

var log = require('npmlog')
var realizePackageSpecifier = require('realize-package-specifier')
var tar = require('tar')
var once = require('once')
var semver = require('semver')
var readPackageTree = require('read-package-tree')
var iferr = require('iferr')
var rimraf = require('rimraf')
var clone = require('lodash.clonedeep')
var validate = require('aproba')

var npm = require('./npm.js')
var mapToRegistry = require('./utils/map-to-registry.js')
var cache = require('./cache.js')
var cachedPackageRoot = require('./cache/cached-package-root.js')
var tempFilename = require('./utils/temp-filename.js')
var getCacheStat = require('./cache/get-stat.js')
var unpack = require('./utils/tar.js').unpack
var pulseTillDone = require('./utils/pulse-till-done.js')

function andLogAndFinish (spec, tracker, done) {
  validate('SF', [spec, done])
  return function (er, pkg) {
    validate('EO', arguments)
    if (er) {
      log.silly('fetchPackageMetaData', 'error for ' + spec, er)
      if (tracker) tracker.finish()
    }
    return done(er, pkg)
  }
}

module.exports = function fetchPackageMetadata (spec, where, tracker, done) {
  if (!done) {
    done = tracker || where
    tracker = null
    if (done === where) where = null
  }
  if (typeof spec === 'object') {
    var dep = spec
    spec = dep.raw
  }
  var logAndFinish = andLogAndFinish(spec, tracker, done)
  if (!dep) {
    log.silly('fetchPackageMetaData', spec)
    return realizePackageSpecifier(spec, where, iferr(logAndFinish, function (dep) {
      fetchPackageMetadata(dep, where, tracker, done)
    }))
  }
  if (dep.type === 'version' || dep.type === 'range' || dep.type === 'tag') {
    fetchNamedPackageData(dep, addRequestedAndFinish)
  } else {
    fetchOtherPackageData(spec, dep, where, addRequestedAndFinish)
  }
  function addRequestedAndFinish (er, pkg) {
    validate('EO', arguments)
    if (pkg) {
      pkg._requested = dep
      pkg._spec = spec
      pkg._where = where
      if (!pkg._args) pkg._args = []
      pkg._args.push([pkg._spec, pkg._where])
    }
    logAndFinish(er, pkg)
  }
}

function fetchOtherPackageData (spec, dep, where, next) {
  validate('SOSF', arguments)
  log.silly('fetchOtherPackageData', spec)
  cache.add(spec, null, where, false, iferr(next, function (pkg) {
    var result = clone(pkg)
    result._inCache = true
    next(null, result)
  }))
}

var regCache = {}

function fetchNamedPackageData (dep, next) {
  validate('OF', arguments)
  log.silly('fetchNamedPackageData', dep.name || dep.rawSpec)
  mapToRegistry(dep.name || dep.rawSpec, npm.config, iferr(next, function (url, auth) {
    if (regCache[url]) {
      pickVersionFromRegistryDocument(clone(regCache[url]))
    } else {
      npm.registry.get(url, {auth: auth}, pulseTillDone('fetchMetadata', iferr(next, pickVersionFromRegistryDocument)))
    }
    function returnAndAddMetadata (pkg) {
      delete pkg._from
      delete pkg._resolved
      delete pkg._shasum
      next(null, pkg)
    }
    function pickVersionFromRegistryDocument (pkg) {
      if (!regCache[url]) regCache[url] = pkg
      var versions = Object.keys(pkg.versions).sort(semver.rcompare)

      if (dep.type === 'tag') {
        var tagVersion = pkg['dist-tags'][dep.spec]
        if (pkg.versions[tagVersion]) return returnAndAddMetadata(pkg.versions[tagVersion])
      } else {
        var latestVersion = pkg['dist-tags'].latest || versions[0]

        // Find the the most recent version less than or equal
        // to latestVersion that satisfies our spec
        for (var ii = 0; ii < versions.length; ++ii) {
          if (semver.gt(versions[ii], latestVersion)) continue
          if (semver.satisfies(versions[ii], dep.spec)) {
            return returnAndAddMetadata(pkg.versions[versions[ii]])
          }
        }

        // Failing that, try finding the most recent version that matches
        // our spec
        for (var jj = 0; jj < versions.length; ++jj) {
          if (semver.satisfies(versions[jj], dep.spec)) {
            return returnAndAddMetadata(pkg.versions[versions[jj]])
          }
        }
      }

      // And failing that, we error out
      var targets = versions.length
                  ? 'Valid install targets:\n' + JSON.stringify(versions) + '\n'
                  : 'No valid targets found.'
      var er = new Error('No compatible version found: ' +
                         dep.rawSpec + '\n' + targets)
      return next(er)
    }
  }))
}

function retryWithCached (pkg, asserter, next) {
  if (!pkg._inCache) {
    cache.add(pkg._spec, null, pkg._where, false, iferr(next, function (newpkg) {
      Object.keys(newpkg).forEach(function (key) {
        if (key[0] !== '_') return
        pkg[key] = newpkg[key]
      })
      pkg._inCache = true
      return asserter(pkg, next)
    }))
  }
  return !pkg._inCache
}

module.exports.addShrinkwrap = function addShrinkwrap (pkg, next) {
  validate('OF', arguments)
  if (pkg._shrinkwrap !== undefined) return next(null, pkg)
  if (retryWithCached(pkg, addShrinkwrap, next)) return
  pkg._shrinkwrap = null
  // FIXME: cache the shrinkwrap directly
  var pkgname = pkg.name
  var ver = pkg.version
  var tarball = path.join(cachedPackageRoot({name: pkgname, version: ver}), 'package.tgz')
  untarStream(tarball, function (er, untar) {
    if (er) {
      if (er.code === 'ENOTTARBALL') {
        pkg._shrinkwrap = null
        return next()
      }
      else {
        return next(er)
      }
    }
    if (er) return next(er)
    var foundShrinkwrap = false
    log.silly('addShrinkwrap', 'Adding shrinkwrap to ' + pkgname)
    untar.on('entry', function (entry) {
      if (!/^(?:[^\/]+[\/])npm-shrinkwrap.json$/.test(entry.path)) return
      log.silly('addShrinkwrap', 'Found shrinkwrap at ' + entry.path)
      foundShrinkwrap = true
      var shrinkwrap = ''
      entry.on('data', function (chunk) {
        shrinkwrap += chunk
      })
      entry.on('end', function () {
        untar.close()
        log.silly('addShrinkwrap', 'Done reading shrinkwrap')
        try {
          pkg._shrinkwrap = JSON.parse(shrinkwrap)
        } catch (ex) {
          var er = new Error('Error parsing ' + pkgname + '@' + ver + "'s npm-shrinkwrap.json: " + ex.message)
          er.type = 'ESHRINKWRAP'
          return next(er)
        }
        next(null, pkg)
      })
      entry.resume()
    })
    untar.on('end', function () {
      if (!foundShrinkwrap) {
        pkg._shrinkwrap = null
        next(null, pkg)
      }
    })
  })
}

module.exports.addBundled = function addBundled (pkg, next) {
  validate('OF', arguments)
  if (pkg._bundled !== undefined) return next(null, pkg)
  if (!pkg.bundleDependencies) return next(null, pkg)
  if (retryWithCached(pkg, addBundled, next)) return
  pkg._bundled = null
  var pkgname = pkg.name
  var ver = pkg.version
  var tarball = path.join(cachedPackageRoot({name: pkgname, version: ver}), 'package.tgz')
  var target = tempFilename('unpack')
  getCacheStat(iferr(next, function (cs) {
    log.verbose('addBundled', 'extract', tarball)
    unpack(tarball, target, null, null, cs.uid, cs.gid, iferr(next, function () {
      log.silly('addBundled', 'read tarball')
      readPackageTree(target, function (er, tree) {
        log.silly('cleanup', 'remove extracted module')
        rimraf(target, function () {
          if (tree) {
            pkg._bundled = tree.children
          }
          next(null, pkg)
        })
      })
    }))
  }))
}

// FIXME: hasGzipHeader / hasTarHeader / untarStream duplicate a lot
// of code from lib/utils/tar.js– these should be brought together.

function hasGzipHeader (c) {
  return c[0] === 0x1F && c[1] === 0x8B && c[2] === 0x08
}

function hasTarHeader (c) {
  return c[257] === 0x75 && // tar archives have 7573746172 at position
         c[258] === 0x73 && // 257 and 003030 or 202000 at position 262
         c[259] === 0x74 &&
         c[260] === 0x61 &&
         c[261] === 0x72 &&

       ((c[262] === 0x00 &&
         c[263] === 0x30 &&
         c[264] === 0x30) ||

        (c[262] === 0x20 &&
         c[263] === 0x20 &&
         c[264] === 0x00))
}

function untarStream (tarball, cb) {
  validate('SF', arguments)
  cb = once(cb)

  var tounpipe = []
  var stream
  var file = stream = fs.createReadStream(tarball)
  file.on('error', function (er) {
    er = new Error('Error extracting ' + tarball + ' archive: ' + er.message)
    er.code = 'EREADFILE'
    cb(er)
  })
  file.on('data', function OD (c) {
    if (hasGzipHeader(c)) {
      doGunzip()
    }
    else if (hasTarHeader(c)) {
      doUntar()
    }
    else {
      file.close()
      var er = new Error('Non-gzip/tarball ' + tarball)
      er.code = 'ENOTTARBALL'
      return cb(er)
    }
    file.removeListener('data', OD)
    file.emit('data', c)
    cb(null, stream)
  })

  function doGunzip() {
    var gunzip = stream.pipe(zlib.createGunzip())
    gunzip.on('error', function (er) {
      er = new Error('Error extracting ' + tarball + ' archive: ' + er.message)
      er.code = 'EGUNZIP'
      cb(er)
    })
    tounpipe.push([stream, gunzip])
    stream = gunzip
    doUntar()
  }

  function doUntar() {
    var untar = stream.pipe(tar.Parse())
    untar.on('error', function (er) {
      er = new Error('Error extracting ' + tarball + ' archive: ' + er.message)
      er.code = 'EUNTAR'
      cb(er)
    })
    tounpipe.push([stream, untar])
    stream = untar
    addClose()
  }

  function addClose() {
    stream.close = function () {
      tounpipe.forEach(function (streams) {
        streams[0].unpipe(streams[1])
      })

      file.close()
    }
  }
}
<commit_msg>install: Make shrinkwrap extraction work in 0.8<commit_after>'use strict'
var fs = require('graceful-fs')
var path = require('path')
var zlib = require('zlib')

var log = require('npmlog')
var realizePackageSpecifier = require('realize-package-specifier')
var tar = require('tar')
var once = require('once')
var semver = require('semver')
var readPackageTree = require('read-package-tree')
var iferr = require('iferr')
var rimraf = require('rimraf')
var clone = require('lodash.clonedeep')
var validate = require('aproba')

var npm = require('./npm.js')
var mapToRegistry = require('./utils/map-to-registry.js')
var cache = require('./cache.js')
var cachedPackageRoot = require('./cache/cached-package-root.js')
var tempFilename = require('./utils/temp-filename.js')
var getCacheStat = require('./cache/get-stat.js')
var unpack = require('./utils/tar.js').unpack
var pulseTillDone = require('./utils/pulse-till-done.js')

function andLogAndFinish (spec, tracker, done) {
  validate('SF', [spec, done])
  return function (er, pkg) {
    validate('EO', arguments)
    if (er) {
      log.silly('fetchPackageMetaData', 'error for ' + spec, er)
      if (tracker) tracker.finish()
    }
    return done(er, pkg)
  }
}

module.exports = function fetchPackageMetadata (spec, where, tracker, done) {
  if (!done) {
    done = tracker || where
    tracker = null
    if (done === where) where = null
  }
  if (typeof spec === 'object') {
    var dep = spec
    spec = dep.raw
  }
  var logAndFinish = andLogAndFinish(spec, tracker, done)
  if (!dep) {
    log.silly('fetchPackageMetaData', spec)
    return realizePackageSpecifier(spec, where, iferr(logAndFinish, function (dep) {
      fetchPackageMetadata(dep, where, tracker, done)
    }))
  }
  if (dep.type === 'version' || dep.type === 'range' || dep.type === 'tag') {
    fetchNamedPackageData(dep, addRequestedAndFinish)
  } else {
    fetchOtherPackageData(spec, dep, where, addRequestedAndFinish)
  }
  function addRequestedAndFinish (er, pkg) {
    validate('EO', arguments)
    if (pkg) {
      pkg._requested = dep
      pkg._spec = spec
      pkg._where = where
      if (!pkg._args) pkg._args = []
      pkg._args.push([pkg._spec, pkg._where])
    }
    logAndFinish(er, pkg)
  }
}

function fetchOtherPackageData (spec, dep, where, next) {
  validate('SOSF', arguments)
  log.silly('fetchOtherPackageData', spec)
  cache.add(spec, null, where, false, iferr(next, function (pkg) {
    var result = clone(pkg)
    result._inCache = true
    next(null, result)
  }))
}

var regCache = {}

function fetchNamedPackageData (dep, next) {
  validate('OF', arguments)
  log.silly('fetchNamedPackageData', dep.name || dep.rawSpec)
  mapToRegistry(dep.name || dep.rawSpec, npm.config, iferr(next, function (url, auth) {
    if (regCache[url]) {
      pickVersionFromRegistryDocument(clone(regCache[url]))
    } else {
      npm.registry.get(url, {auth: auth}, pulseTillDone('fetchMetadata', iferr(next, pickVersionFromRegistryDocument)))
    }
    function returnAndAddMetadata (pkg) {
      delete pkg._from
      delete pkg._resolved
      delete pkg._shasum
      next(null, pkg)
    }
    function pickVersionFromRegistryDocument (pkg) {
      if (!regCache[url]) regCache[url] = pkg
      var versions = Object.keys(pkg.versions).sort(semver.rcompare)

      if (dep.type === 'tag') {
        var tagVersion = pkg['dist-tags'][dep.spec]
        if (pkg.versions[tagVersion]) return returnAndAddMetadata(pkg.versions[tagVersion])
      } else {
        var latestVersion = pkg['dist-tags'].latest || versions[0]

        // Find the the most recent version less than or equal
        // to latestVersion that satisfies our spec
        for (var ii = 0; ii < versions.length; ++ii) {
          if (semver.gt(versions[ii], latestVersion)) continue
          if (semver.satisfies(versions[ii], dep.spec)) {
            return returnAndAddMetadata(pkg.versions[versions[ii]])
          }
        }

        // Failing that, try finding the most recent version that matches
        // our spec
        for (var jj = 0; jj < versions.length; ++jj) {
          if (semver.satisfies(versions[jj], dep.spec)) {
            return returnAndAddMetadata(pkg.versions[versions[jj]])
          }
        }
      }

      // And failing that, we error out
      var targets = versions.length
                  ? 'Valid install targets:\n' + JSON.stringify(versions) + '\n'
                  : 'No valid targets found.'
      var er = new Error('No compatible version found: ' +
                         dep.rawSpec + '\n' + targets)
      return next(er)
    }
  }))
}

function retryWithCached (pkg, asserter, next) {
  if (!pkg._inCache) {
    cache.add(pkg._spec, null, pkg._where, false, iferr(next, function (newpkg) {
      Object.keys(newpkg).forEach(function (key) {
        if (key[0] !== '_') return
        pkg[key] = newpkg[key]
      })
      pkg._inCache = true
      return asserter(pkg, next)
    }))
  }
  return !pkg._inCache
}

module.exports.addShrinkwrap = function addShrinkwrap (pkg, next) {
  validate('OF', arguments)
  if (pkg._shrinkwrap !== undefined) return next(null, pkg)
  if (retryWithCached(pkg, addShrinkwrap, next)) return
  pkg._shrinkwrap = null
  // FIXME: cache the shrinkwrap directly
  var pkgname = pkg.name
  var ver = pkg.version
  var tarball = path.join(cachedPackageRoot({name: pkgname, version: ver}), 'package.tgz')
  untarStream(tarball, function (er, untar) {
    if (er) {
      if (er.code === 'ENOTTARBALL') {
        pkg._shrinkwrap = null
        return next()
      }
      else {
        return next(er)
      }
    }
    if (er) return next(er)
    var foundShrinkwrap = false
    log.silly('addShrinkwrap', 'Adding shrinkwrap to ' + pkgname)
    untar.on('entry', function (entry) {
      if (!/^(?:[^\/]+[\/])npm-shrinkwrap.json$/.test(entry.path)) return
      log.silly('addShrinkwrap', 'Found shrinkwrap at ' + entry.path)
      foundShrinkwrap = true
      var shrinkwrap = ''
      entry.on('data', function (chunk) {
        shrinkwrap += chunk
      })
      entry.on('end', function () {
        untar.close()
        log.silly('addShrinkwrap', 'Done reading shrinkwrap')
        try {
          pkg._shrinkwrap = JSON.parse(shrinkwrap)
        } catch (ex) {
          var er = new Error('Error parsing ' + pkgname + '@' + ver + "'s npm-shrinkwrap.json: " + ex.message)
          er.type = 'ESHRINKWRAP'
          return next(er)
        }
        next(null, pkg)
      })
      entry.resume()
    })
    untar.on('end', function () {
      if (!foundShrinkwrap) {
        pkg._shrinkwrap = null
        next(null, pkg)
      }
    })
  })
}

module.exports.addBundled = function addBundled (pkg, next) {
  validate('OF', arguments)
  if (pkg._bundled !== undefined) return next(null, pkg)
  if (!pkg.bundleDependencies) return next(null, pkg)
  if (retryWithCached(pkg, addBundled, next)) return
  pkg._bundled = null
  var pkgname = pkg.name
  var ver = pkg.version
  var tarball = path.join(cachedPackageRoot({name: pkgname, version: ver}), 'package.tgz')
  var target = tempFilename('unpack')
  getCacheStat(iferr(next, function (cs) {
    log.verbose('addBundled', 'extract', tarball)
    unpack(tarball, target, null, null, cs.uid, cs.gid, iferr(next, function () {
      log.silly('addBundled', 'read tarball')
      readPackageTree(target, function (er, tree) {
        log.silly('cleanup', 'remove extracted module')
        rimraf(target, function () {
          if (tree) {
            pkg._bundled = tree.children
          }
          next(null, pkg)
        })
      })
    }))
  }))
}

// FIXME: hasGzipHeader / hasTarHeader / untarStream duplicate a lot
// of code from lib/utils/tar.js– these should be brought together.

function hasGzipHeader (c) {
  return c[0] === 0x1F && c[1] === 0x8B && c[2] === 0x08
}

function hasTarHeader (c) {
  return c[257] === 0x75 && // tar archives have 7573746172 at position
         c[258] === 0x73 && // 257 and 003030 or 202000 at position 262
         c[259] === 0x74 &&
         c[260] === 0x61 &&
         c[261] === 0x72 &&

       ((c[262] === 0x00 &&
         c[263] === 0x30 &&
         c[264] === 0x30) ||

        (c[262] === 0x20 &&
         c[263] === 0x20 &&
         c[264] === 0x00))
}

function dispose (stream) {
  if (stream.destroy) return stream.destroy()
  if (stream.close) return stream.close()
}

function untarStream (tarball, cb) {
  validate('SF', arguments)
  cb = once(cb)

  var tounpipe = []
  var stream
  var file = stream = fs.createReadStream(tarball)
  file.on('error', function (er) {
    er = new Error('Error extracting ' + tarball + ' archive: ' + er.message)
    er.code = 'EREADFILE'
    cb(er)
  })
  file.on('data', function OD (c) {
    if (hasGzipHeader(c)) {
      doGunzip()
    }
    else if (hasTarHeader(c)) {
      doUntar()
    }
    else {
      dispose(file)
      var er = new Error('Non-gzip/tarball ' + tarball)
      er.code = 'ENOTTARBALL'
      return cb(er)
    }
    file.removeListener('data', OD)
    file.emit('data', c)
    cb(null, stream)
  })

  function doGunzip() {
    var gunzip = stream.pipe(zlib.createGunzip())
    gunzip.on('error', function (er) {
      er = new Error('Error extracting ' + tarball + ' archive: ' + er.message)
      er.code = 'EGUNZIP'
      cb(er)
    })
    tounpipe.push(gunzip)
    stream = gunzip
    doUntar()
  }

  function doUntar() {
    var untar = stream.pipe(tar.Parse())
    untar.on('error', function (er) {
      er = new Error('Error extracting ' + tarball + ' archive: ' + er.message)
      er.code = 'EUNTAR'
      cb(er)
    })
    tounpipe.push(untar)
    stream = untar
    addClose()
  }

  function addClose() {
    stream.close = function () {
      tounpipe.forEach(dispose)

      dispose(file)
    }
  }
}
<|endoftext|>


<commit_before>define("js!CompoundComponent", ["js!utils", "js!BaseComponent"], function(utils, BaseComponent){

   var CompoundComponent = BaseComponent.extend({
      _components: null,
      _componentsIdHash: {},

      init : function(cfg){
         this._super(cfg);

         this._components = {};
         this._componentsIdHash = {};

         var components = utils.provideInnerComponents(this._container);
         for (var i = 0, l = components.length; i < l; i++){
            this._components[components[i].getId()] = components[i];
            this._componentsIdHash[components[i].name()] = components[i];
         }
      },
      getComponentByName: function(name){
         var result = null;
         if (!name){
            return null;
         }
         if (this._components.hasOwnProperty(name)){
            result = this._components[name];
         }
         else{
            for (var i in this._components){
               if(this._components.hasOwnProperty(i) && this._components[i] instanceof CompoundComponent){
                  result = this._components[i].getComponentByName(name);
               }
               if (result){
                  break;
               }
            }
         }

         return result;
      },
      destroy: function(){
         for (var i in this._componentsIdHash){
            if (this._componentsIdHash.hasOwnProperty(i)){
               this._componentsIdHash[i].destroy();
            }
         }
         this._componentsIdHash = null;
         this._components = null;
         this._super();
      }
   });

   return CompoundComponent;
});<commit_msg>type error<commit_after>define("js!CompoundComponent", ["js!utils", "js!BaseComponent"], function(utils, BaseComponent){

   var CompoundComponent = BaseComponent.extend({
      _components: null,
      _componentsIdHash: {},

      init : function(cfg){
         this._super(cfg);

         this._components = {};
         this._componentsIdHash = {};

         var components = utils.provideInnerComponents(this._container);
         for (var i = 0, l = components.length; i < l; i++){
            this._components[components[i].name()] = components[i];
            this._componentsIdHash[components[i].getId()] = components[i];
         }
      },
      getComponentByName: function(name){
         var result = null;
         if (!name){
            return null;
         }
         if (this._components.hasOwnProperty(name)){
            result = this._components[name];
         }
         else{
            for (var i in this._components){
               if(this._components.hasOwnProperty(i) && this._components[i] instanceof CompoundComponent){
                  result = this._components[i].getComponentByName(name);
               }
               if (result){
                  break;
               }
            }
         }

         return result;
      },
      destroy: function(){
         for (var i in this._componentsIdHash){
            if (this._componentsIdHash.hasOwnProperty(i)){
               this._componentsIdHash[i].destroy();
            }
         }
         this._componentsIdHash = null;
         this._components = null;
         this._super();
      }
   });

   return CompoundComponent;
});<|endoftext|>


<commit_before>'use strict';

var toUint = require('es5-ext/lib/Number/to-uint')

  , count = 0, limit = Infinity, callbacks = [];

exports.open = function () {
	++count;
};
exports.close = function () {
	if ((--count < limit) && callbacks.length) {
		do {
			callbacks.shift()();
		} while ((count < limit) && callbacks.length);
	}
};

require('child_process').exec('ulimit -n', { env: process.env },
	function (err, stdout, stderr) {
		if (!stdout) {
			return;
		}
		stdout = stdout.trim();
		if (isNaN(stdout)) {
			return;
		}
		// We subtract 25 to give eventual outer processes some air
		limit = Number(stdout) - 25;
	});

exports.isAvailable = function (padding) {
	return count < (limit - toUint(padding));
};

exports.cb = function (cb) {
	if (count < limit) {
		cb();
		return;
	}
	callbacks.push(cb);
};
<commit_msg>Allow at least 5 descriptors if detection goes wrong<commit_after>'use strict';

var toUint = require('es5-ext/lib/Number/to-uint')

  , count = 0, limit = Infinity, callbacks = [];

exports.open = function () {
	++count;
};
exports.close = function () {
	if ((--count < limit) && callbacks.length) {
		do {
			callbacks.shift()();
		} while ((count < limit) && callbacks.length);
	}
};

require('child_process').exec('ulimit -n', { env: process.env },
	function (err, stdout, stderr) {
		if (!stdout) {
			return;
		}
		stdout = stdout.trim();
		if (isNaN(stdout)) {
			return;
		}
		// We subtract 25 to give eventual outer processes some air
		limit = Math.max(Number(stdout) - 25, 5);
	});

exports.isAvailable = function (padding) {
	return count < (limit - toUint(padding));
};

exports.cb = function (cb) {
	if (count < limit) {
		cb();
		return;
	}
	callbacks.push(cb);
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name        SEDE User ID Autofiller
// @namespace   https://github.com/Glorfindel83/
// @description Automatically fills SEDE parameters 'AccountId' and 'UserId'
// @updateURL   https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/user-id-autofiller/user-id-autofiller.user.js
// @downloadURL https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/user-id-autofiller/user-id-autofiller.user.js
// @author      Glorfindel
// @version     0.2
// @match       *://data.stackexchange.com/*/query/*
// @connect     stackexchange.com
// @connect     *.stackexchange.com
// @connect     *.stackoverflow.com
// @connect     *.superuser.com
// @connect     *.serverfault.com
// @connect     *.askubuntu.com
// @connect     *.mathoverflow.net
// @connect     stackapps.com
// @grant       GM_xmlhttpRequest
// @grant       GM.xmlHttpRequest
// ==/UserScript==

/* global $ */

(function() {
    'use strict';

    $('#query-params input').each(function() {
        let input = $(this);
        let name = input.attr('name');
        switch (name.toLowerCase()) {
            case 'accountid':
                // Fetch global account ID
                GM.xmlHttpRequest({
                    method: 'GET',
                    url: 'https://stackexchange.com/users/current',
                    onload: function (data) {
                        let matches = /\/users\/(\d+)\//g.exec(data.finalUrl);
                        input.val(matches[1]);
                    }
                });
                break;
            case 'userid': {
                // Determine current site
                let queriesURL = $('#header nav a[title=Queries]')[0].href;
                var site = /https:\/\/data.stackexchange.com\/(.*)\/queries/.exec(queriesURL)[1];
                if (site == 'meta.stackexchange' || site == 'stackapps' ||
                    site.endsWith('stackoverflow') ||
                    site.endsWith('superuser') ||
                    site.endsWith('serverfault') ||
                    site.endsWith('askubuntu')) {
                    site += '.com';
                } else if (site.endsWith('mathoverflow')) {
                    site += '.net';
                } else if (site == 'ru' || site == 'pt' || site == 'ja' || site == 'es') {
                    site += '.stackoverflow.com';
                } else if (site == 'rume' || site == 'ptme' || site == 'jame' || site == 'esme') {
                    site = site.substring(0, 2) + '.meta.stackoverflow.com';
                } else if (site.startsWith('meta.')) {
                    site = site.substring(5) + '.meta.stackexchange.com';
                } else {
                    site += '.stackexchange.com';
                }

                // Fetch user ID
                GM.xmlHttpRequest({
                    method: 'GET',
                    url: 'https://' + site + '/users/current',
                    onload: function (data) {
                        let matches = /\/users\/(\d+)\//g.exec(data.finalUrl);
                        input.val(matches[1]);
                    }
                });
                break;
            }
        }
    });
})();<commit_msg>User ID autofiller v0.3: bugfix (it wasn't working for sites with custom domains)<commit_after>// ==UserScript==
// @name        SEDE User ID Autofiller
// @namespace   https://github.com/Glorfindel83/
// @description Automatically fills SEDE parameters 'AccountId' and 'UserId'
// @updateURL   https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/user-id-autofiller/user-id-autofiller.user.js
// @downloadURL https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/user-id-autofiller/user-id-autofiller.user.js
// @author      Glorfindel
// @version     0.3
// @match       *://data.stackexchange.com/*/query/*
// @connect     stackexchange.com
// @connect     *.stackexchange.com
// @connect     stackoverflow.com
// @connect     *.stackoverflow.com
// @connect     superuser.com
// @connect     *.superuser.com
// @connect     serverfault.com
// @connect     *.serverfault.com
// @connect     askubuntu.com
// @connect     *.askubuntu.com
// @connect     mathoverflow.net
// @connect     *.mathoverflow.net
// @connect     stackapps.com
// @grant       GM_xmlhttpRequest
// @grant       GM.xmlHttpRequest
// ==/UserScript==

/* global $ */

(function() {
    'use strict';

    $('#query-params input').each(function() {
        let input = $(this);
        let name = input.attr('name');
        switch (name.toLowerCase()) {
            case 'accountid':
                // Fetch global account ID
                GM.xmlHttpRequest({
                    method: 'GET',
                    url: 'https://stackexchange.com/users/current',
                    onload: function (data) {
                        let matches = /\/users\/(\d+)\//g.exec(data.finalUrl);
                        input.val(matches[1]);
                    }
                });
                break;
            case 'userid': {
                // Determine current site
                let queriesURL = $('#header nav a[title=Queries]')[0].href;
                var site = /https:\/\/data.stackexchange.com\/(.*)\/queries/.exec(queriesURL)[1];
                if (site == 'meta.stackexchange' || site == 'stackapps' ||
                    site.endsWith('stackoverflow') ||
                    site.endsWith('superuser') ||
                    site.endsWith('serverfault') ||
                    site.endsWith('askubuntu')) {
                    site += '.com';
                } else if (site.endsWith('mathoverflow')) {
                    site += '.net';
                } else if (site == 'ru' || site == 'pt' || site == 'ja' || site == 'es') {
                    site += '.stackoverflow.com';
                } else if (site == 'rume' || site == 'ptme' || site == 'jame' || site == 'esme') {
                    site = site.substring(0, 2) + '.meta.stackoverflow.com';
                } else if (site.startsWith('meta.')) {
                    site = site.substring(5) + '.meta.stackexchange.com';
                } else {
                    site += '.stackexchange.com';
                }

                // Fetch user ID
                GM.xmlHttpRequest({
                    method: 'GET',
                    url: 'https://' + site + '/users/current',
                    onload: function (data) {
                        let matches = /\/users\/(\d+)\//g.exec(data.finalUrl);
                        input.val(matches[1]);
                    }
                });
                break;
            }
        }
    });
})();<|endoftext|>


<commit_before>/*
 * grunt-hockeyAppApi
 * https://github.com/chadmilden/GruntHockeyAppApi
 *
 * Copyright (c) 2017 Chad Mildenberger
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  //updateVersion
  //test

  grunt.registerMultiTask('hockeyAppApi', 'Api calls for Hockey App', function() {
    // Merge task-specific and/or target-specific options with these defaults.

		var done = this.async();

		var _updateVersion = grunt.config.get('hockeyAppApi.updateVersion')
			const http = require('http');
			const fs = require('fs');
			const request = require('request');
		if (_updateVersion != undefined)
		{
			updateVersion(_updateVersion,done);
		}

			function updateVersion(_updateVersion, asyncHandle)
			{

			var numberOfUploads = _updateVersion.length;
			var finishedUploads = 0;

			for(var i = 0; i<_updateVersion.length; i++)
			{
				var data = _updateVersion[i];
				  console.log('File page: ' + data.filePath);

				var path = 'https://rink.hockeyapp.net/api/2/apps/'+data.appId+'/app_versions/'+data.versionID;
				 console.log('Path: ' + path);
				var data = _updateVersion[i];

				request.put({
							  headers:{
								  'X-HockeyAppToken':data.apiKey,
								   'accept': '*/*'},
							  url: path,
							  formData: {
								status: '2',
								ipa: fs.createReadStream(data.filePath)
							  }
							}, function(err,httpResponse,body)
								{

								   if (!err)
								   {
									   grunt.log.ok('file uploaded successfully');
								   }
								   else
								   {
									   grunt.log.error('error uploading: ' + err);
								   }
									finishedUploads++;
									if (numberOfUploads == finishedUploads)
									{
										asyncHandle();
										grunt.log.ok('Finished uploading all files');
									}
								});

			}
			}
    });
  };<commit_msg>multi task<commit_after>/*
 * grunt-hockeyAppApi
 * https://github.com/chadmilden/GruntHockeyAppApi
 *
 * Copyright (c) 2017 Chad Mildenberger
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  //updateVersion
  //test

  grunt.registerMultiTask('hockeyAppApi', 'Api calls for Hockey App', function() {
    // Merge task-specific and/or target-specific options with these defaults.

		var done = this.async();

		var _updateVersion = this.data.updateVersion;

			const http = require('http');
			const fs = require('fs');
			const request = require('request');
		if (_updateVersion != undefined)
		{
			updateVersion(_updateVersion,done);
		}

			function updateVersion(_updateVersion, asyncHandle)
			{

			var numberOfUploads = _updateVersion.length;
			var finishedUploads = 0;

			for(var i = 0; i<_updateVersion.length; i++)
			{
				var data = _updateVersion[i];
				  console.log('File page: ' + data.filePath);

				var path = 'https://rink.hockeyapp.net/api/2/apps/'+data.appId+'/app_versions/'+data.versionID;
				 console.log('Path: ' + path);
				var data = _updateVersion[i];

				request.put({
							  headers:{
								  'X-HockeyAppToken':data.apiKey,
								   'accept': '*/*'},
							  url: path,
							  formData: {
								status: '2',
								ipa: fs.createReadStream(data.filePath)
							  }
							}, function(err,httpResponse,body)
								{

								   if (!err)
								   {
									   grunt.log.ok('file uploaded successfully');
								   }
								   else
								   {
									   grunt.log.error('error uploading: ' + err);
								   }
									finishedUploads++;
									if (numberOfUploads == finishedUploads)
									{
										asyncHandle();
										grunt.log.ok('Finished uploading all files');
									}
								});

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


<commit_before>/**
 * @fileoverview This task helps you to run jsdoc3 to generate doc in your Grunt build sequence
 * @copyright Bertrand Chevrier 2012
 * @author Bertrand Chevrier <chevrier.bertrand@gmail.com>
 * @license MIT
 *
 * @module tasks/jsdoc-plugin
 */

/**
 * Register the jsdoc task and helpers to Grunt
 * @type GruntTask
 * @constructor
 * @param {Object} grunt - the grunt context
 */
module.exports = function jsDocTask(grunt) {
	'use strict';

	var util = require('util'),
			errorCode = {
			generic : 1,
			task	: 3
		};

	/**
	 * Register the jsdoc task to Grunt
	 */
	function registerJsdocTask() {
		var fs				= require('fs'),
			path			= require('path'),
			exec			= require('./lib/exec'),
			options			= grunt.task.current.options({'private': true}),
			done			= grunt.task.current.async(),
			srcs			= grunt.task.current.filesSrc,
			javaHome		= process.env.JAVA_HOME,
			jsDocPath		= grunt.task.current.data.jsdoc,
			jsDocNpmPath	= 'node_modules/jsdoc/jsdoc',
			timeout			= 60000,	//todo implement and move in options
			cliFlags = ['recurse', 'private', 'lenient', 'explain', 'help', 'version', 'test', 'verbose', 'nocolor', 'template', 'configure', 'destination', 'encoding', 'tutorials'],
			jsDoc;

		//validate options

		if (!options.destination) {
			// Support for old syntax where destination was provided through 'dest' key
			options.destination = grunt.task.current.files[0].dest || 'doc';
		}

		//legacy configs
		if(options.config){
			options.configure = options.config;
		}

		// Compute JSDoc flags from options
		for(var optionName in options){
			var option = options[optionName];
			if(!grunt.util._.contains(cliFlags, optionName) || !options){
				delete options[optionName];
			}
		}

		grunt.log.debug(util.inspect(options));

		//lookup jsdoc
		jsDoc = exec.lookup(grunt, jsDocNpmPath, ['node_modules/grunt-jsdoc/', jsDocPath]);

		//check if java is set
		if(!javaHome){
			grunt.log.error("JAVA_HOME is not set, but java is required by jsdoc to run.");
			grunt.fail.warn('Wrong installation/environnement', errorCode.generic);
		} else {
			grunt.log.debug("JAVA_HOME : " + javaHome);
		}

		//check if jsdoc npm module is installedz
		if(jsDoc === undefined){
			grunt.log.error('Unable to locate jsdoc');
			grunt.fail.warn('Wrong installation/environnement', errorCode.generic);
		} else {
			grunt.log.debug("jsdoc found at : " + jsDoc);
		}

		//check if there is sources to generate the doc for
		if(srcs.length === 0){
			grunt.log.error('No source files defined');
			grunt.fail.warn('Wrong configuration', errorCode.generic);
		}

		//check if jsdoc config file path is provided and does exist
		if (options.configure && !fs.existsSync(options.configure)){
			grunt.log.error('jsdoc config file path does not exist');
			grunt.fail.warn('Wrong configuration', errorCode.generic);
		}

		fs.exists(options.destination, function(exists){
			//if the destination don't exists, we create it
			if(!exists){
				grunt.file.mkdir(options.destination);
				grunt.log.debug('create destination : ' + options.destination);
			}

			//execution of the jsdoc command
			var child = exec.buildSpawned(grunt, jsDoc, srcs, options);
			child.stdout.on('data', function (data) {
				grunt.log.debug('jsdoc output : ' + data);
			});
			child.stderr.on('data', function (data) {
				grunt.log.error('An error occurs in jsdoc process:\n' + data);
				grunt.fail.warn('jsdoc failure', errorCode.task);
			});
			child.on('exit', function(code){
				if(code === 0){
					grunt.log.write('Documentation generated to ' + path.resolve(options.destination));
					done(true);
				} else {
					grunt.log.error('jsdoc terminated');
					grunt.fail.warn('jsdoc failure', errorCode.task);
				}
			});
		});
	}

	//bind the task to the grunt context
	grunt.registerMultiTask('jsdoc', 'Generates source documentation using jsdoc', registerJsdocTask);
};
<commit_msg>Typo, #29 should be fixed now.<commit_after>/**
 * @fileoverview This task helps you to run jsdoc3 to generate doc in your Grunt build sequence
 * @copyright Bertrand Chevrier 2012
 * @author Bertrand Chevrier <chevrier.bertrand@gmail.com>
 * @license MIT
 *
 * @module tasks/jsdoc-plugin
 */

/**
 * Register the jsdoc task and helpers to Grunt
 * @type GruntTask
 * @constructor
 * @param {Object} grunt - the grunt context
 */
module.exports = function jsDocTask(grunt) {
	'use strict';

	var util = require('util'),
			errorCode = {
			generic : 1,
			task	: 3
		};

	/**
	 * Register the jsdoc task to Grunt
	 */
	function registerJsdocTask() {
		var fs				= require('fs'),
			path			= require('path'),
			exec			= require('./lib/exec'),
			options			= grunt.task.current.options({'private': true}),
			done			= grunt.task.current.async(),
			srcs			= grunt.task.current.filesSrc,
			javaHome		= process.env.JAVA_HOME,
			jsDocPath		= grunt.task.current.data.jsdoc,
			jsDocNpmPath	= 'node_modules/jsdoc/jsdoc',
			timeout			= 60000,	//todo implement and move in options
			cliFlags = ['recurse', 'private', 'lenient', 'explain', 'help', 'version', 'test', 'verbose', 'nocolor', 'template', 'configure', 'destination', 'encoding', 'tutorials'],
			jsDoc;

		//validate options

		if (!options.destination) {
			// Support for old syntax where destination was provided through 'dest' key
			options.destination = grunt.task.current.files[0].dest || 'doc';
		}

		//legacy configs
		if(options.config){
			options.configure = options.config;
		}

		// Compute JSDoc flags from options
		for(var optionName in options){
			var option = options[optionName];
			if(!grunt.util._.contains(cliFlags, optionName) || !option){
				delete options[optionName];
			}
		}

		grunt.log.debug(util.inspect(options));

		//lookup jsdoc
		jsDoc = exec.lookup(grunt, jsDocNpmPath, ['node_modules/grunt-jsdoc/', jsDocPath]);

		//check if java is set
		if(!javaHome){
			grunt.log.error("JAVA_HOME is not set, but java is required by jsdoc to run.");
			grunt.fail.warn('Wrong installation/environnement', errorCode.generic);
		} else {
			grunt.log.debug("JAVA_HOME : " + javaHome);
		}

		//check if jsdoc npm module is installedz
		if(jsDoc === undefined){
			grunt.log.error('Unable to locate jsdoc');
			grunt.fail.warn('Wrong installation/environnement', errorCode.generic);
		} else {
			grunt.log.debug("jsdoc found at : " + jsDoc);
		}

		//check if there is sources to generate the doc for
		if(srcs.length === 0){
			grunt.log.error('No source files defined');
			grunt.fail.warn('Wrong configuration', errorCode.generic);
		}

		//check if jsdoc config file path is provided and does exist
		if (options.configure && !fs.existsSync(options.configure)){
			grunt.log.error('jsdoc config file path does not exist');
			grunt.fail.warn('Wrong configuration', errorCode.generic);
		}

		fs.exists(options.destination, function(exists){
			//if the destination don't exists, we create it
			if(!exists){
				grunt.file.mkdir(options.destination);
				grunt.log.debug('create destination : ' + options.destination);
			}

			//execution of the jsdoc command
			var child = exec.buildSpawned(grunt, jsDoc, srcs, options);
			child.stdout.on('data', function (data) {
				grunt.log.debug('jsdoc output : ' + data);
			});
			child.stderr.on('data', function (data) {
				grunt.log.error('An error occurs in jsdoc process:\n' + data);
				grunt.fail.warn('jsdoc failure', errorCode.task);
			});
			child.on('exit', function(code){
				if(code === 0){
					grunt.log.write('Documentation generated to ' + path.resolve(options.destination));
					done(true);
				} else {
					grunt.log.error('jsdoc terminated');
					grunt.fail.warn('jsdoc failure', errorCode.task);
				}
			});
		});
	}

	//bind the task to the grunt context
	grunt.registerMultiTask('jsdoc', 'Generates source documentation using jsdoc', registerJsdocTask);
};
<|endoftext|>


<commit_before>const httpProxy = require('http-proxy');
const url = require('url');
const fs = require('fs');
const path = require('path');

// graphql proxy for the client app

// USAGE
//
// app.use('/graphql', graphqlProxyMiddleware(GRAPHQL_ORIGIN));
const proxy = httpProxy.createProxyServer();

// restream parsed body before proxying
proxy.on('proxyReq', (proxyReq, req, res, options) => {
  if (!req.body || !Object.keys(req.body).length) {
    return;
  }

  const contentType = proxyReq.getHeader('Content-Type');
  let bodyData;

  if (contentType === 'application/json') {
    bodyData = JSON.stringify(req.body);
  }

  if (contentType === 'application/x-www-form-urlencoded') {
    bodyData = queryString.stringify(req.body);
  }

  if (bodyData) {
    proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
    proxyReq.write(bodyData);
  }
});

const proxycb = (err) => {
  console.error('proxy error:', err); // this prevents crash
};

module.exports = (graphqlOrigin) => {
  const queryMap = new Map();
  const target = url.parse(graphqlOrigin);
  const headers = { host: target.host };
  const proxyOptions = { target, headers };
  const assetsBaseDir = process.env.ASSETS_S3_BUCKET || path.resolve('build/assets');
  return (req, res) => {
    if (req.body && req.body.id && !req.body.query) {
      try {
        if (!queryMap.get(req.body.id)) {
          const queryText = fs.readFileSync(`${assetsBaseDir}/${req.body.id}.query`);
          queryMap.set(req.body.id, queryText);
        }
        req.body.query = queryMap.get(req.body.id);
      } catch (e) {
        console.error(e);
        throw new Error(e);
      }
    }
    return proxy.web(req, res, proxyOptions, proxycb);
  };
};
<commit_msg>fix up graphql middleware<commit_after>const httpProxy = require('http-proxy');
const url = require('url');
const fs = require('fs');
const path = require('path');

// graphql proxy for the client app

// USAGE
//
// app.use('/graphql', graphqlProxyMiddleware(GRAPHQL_ORIGIN));
const proxy = httpProxy.createProxyServer();

// restream parsed body before proxying
proxy.on('proxyReq', (proxyReq, req, res, options) => {
  if (!req.body || !Object.keys(req.body).length) {
    return;
  }

  const contentType = proxyReq.getHeader('Content-Type');
  let bodyData;

  if (contentType === 'application/json') {
    bodyData = JSON.stringify(req.body);
  }

  if (contentType === 'application/x-www-form-urlencoded') {
    bodyData = queryString.stringify(req.body);
  }

  if (bodyData) {
    proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
    proxyReq.write(bodyData);
  }
});

const proxycb = (err) => {
  console.error('proxy error:', err); // this prevents crash
};

module.exports = (graphqlOrigin) => {
  const queryMap = new Map();
  const target = url.parse(graphqlOrigin);
  const headers = { host: target.host };
  const proxyOptions = { target, headers };
  const assetsBaseDir = process.env.ASSETS_S3_BUCKET || path.resolve('build/assets');
  console.log(assetsBaseDir)
  return (req, res) => {
    if (req.body && req.body.id && !req.body.query) {
      try {
        if (!queryMap.get(req.body.id)) {
          const queryText = fs.readFileSync(`${assetsBaseDir}/${req.body.id}.query`);
          queryMap.set(req.body.id, queryText);
        }
        req.body.query = queryMap.get(req.body.id);
      } catch (e) {
        console.error(e);
        throw new Error(e);
      }
    }
    return proxy.web(req, res, proxyOptions, proxycb);
  };
};
<|endoftext|>


<commit_before>/*
 * grunt-jstestdriver
 * https://github.com/rickyclegg/grunt-jstestdriver
 *
 * Copyright (c) 2013 Ricky Clegg
 * Licensed under the MIT license.
 */
'use strict';
module.exports = function (grunt) {

    var INVALID_FLAGS = ['browser', 'config', 'dryRunFor', 'port', 'server', 'serverHandlerPrefix', 'canFail'];

    grunt.registerTask('jstestdriver', 'Grunt task for uniting testing using JS Test Driver.', function () {

        var options = this.options({
                tests: 'all',
                canFail: false
            }),
            config = grunt.config.get('jstestdriver'),
            done = this.async(),
            numberOfConfigs,
            numberOfPassedTests = 0,
            numberOfFailedTests = 0,
            failedTests = [];

        grunt.verbose.writeflags(options, 'Options');

        function taskComplete() {
            if (!options.canFail && failedTests.length > 0) {
                grunt.fail.fatal(failedTests.join('\n\n'));
                done(false);
            } else {
                grunt.log.ok('Total Passed: ' +
                    numberOfPassedTests + ', Fails: ' + numberOfFailedTests);
                done();
            }
        }

        function runJSTestDriver(configFileLocation, options) {
            var cp;

            function setNumberOfPassesAndFails(result) {
                var resultAsStr = result.toString();

                if (resultAsStr) {
                    numberOfPassedTests += parseInt(result.toString().split('; ')[0].split('Passed: ')[1], 10);
                    numberOfFailedTests += parseInt(result.toString().split('; ')[1].split('Fails: ')[1], 10);
                }
            }

            function hasFailedTests(result) {
                var prop, resultStr = "";

                for (prop in result) {
                    if (result.hasOwnProperty(prop)) {
                        resultStr += result[prop];
                    }
                }

                return resultStr.indexOf("Error:") > -1;
            }

            function processCompleteTests() {
                grunt.log.verbose.writeln('>> Finished running file: ' + configFileLocation);
                grunt.log.verbose.writeln('');

                numberOfConfigs -= 1;
                if (numberOfConfigs === 0) {
                    taskComplete();
                }
            }

            function processed(error, result) {
                setNumberOfPassesAndFails(result);

                if (error || hasFailedTests(result)) {
                    failedTests.push(result);
                    grunt.verbose.writeln('   ONE or MORE tests have failed in:');
                } else {
                    grunt.verbose.writeln(result);
                }

                processCompleteTests();
            }

            function getOptionsArray(options) {
                var names, name, i, l, arr = [];

                names = Object.getOwnPropertyNames(options);
                l = names.length;
                for (i = 0; i < l; i += 1) {
                    name = names[i];

                    if (INVALID_FLAGS.indexOf(name) === -1) {
                        arr.push("--" + name);
                        arr.push(options[name]);
                    } else {
                        if (name !== 'canFail') {
                            grunt.verbose.writeln('WARNING - ' +
                                name + ' is not a valid config for use with the grunt-jstestdriver!');
                        }
                    }
                }

                return arr;
            }

            cp = grunt.util.spawn({
                cmd: 'java',
                args: ["-jar",
                       'lib/jstestdriver.jar',
                       "--config",
                       configFileLocation].concat(getOptionsArray(options))
            }, processed);

            if (grunt.option('verbose')) {
                cp.stdout.pipe(process.stdout);
                cp.stderr.pipe(process.stderr);
            }
        }

        if (typeof config.files === 'string') {
            config.files = [config.files];
        }

        numberOfConfigs = config.files.length;

        grunt.util.async.forEach(config.files, function (filename) {
            runJSTestDriver(filename, options);
        }.bind(this));
    });

};
<commit_msg>Fix jstestdriver.jar path<commit_after>/*
 * grunt-jstestdriver
 * https://github.com/rickyclegg/grunt-jstestdriver
 *
 * Copyright (c) 2013 Ricky Clegg
 * Licensed under the MIT license.
 */
'use strict';
module.exports = function (grunt) {

    var INVALID_FLAGS = ['browser', 'config', 'dryRunFor', 'port', 'server', 'serverHandlerPrefix', 'canFail'];

    grunt.registerTask('jstestdriver', 'Grunt task for uniting testing using JS Test Driver.', function () {

        var options = this.options({
                tests: 'all',
                canFail: false
            }),
            config = grunt.config.get('jstestdriver'),
            done = this.async(),
            numberOfConfigs,
            numberOfPassedTests = 0,
            numberOfFailedTests = 0,
            failedTests = [];

        grunt.verbose.writeflags(options, 'Options');

        function taskComplete() {
            if (!options.canFail && failedTests.length > 0) {
                grunt.fail.fatal(failedTests.join('\n\n'));
                done(false);
            } else {
                grunt.log.ok('Total Passed: ' +
                    numberOfPassedTests + ', Fails: ' + numberOfFailedTests);
                done();
            }
        }

        function runJSTestDriver(configFileLocation, options) {
            var cp;

            function setNumberOfPassesAndFails(result) {
                var resultAsStr = result.toString();

                if (resultAsStr) {
                    numberOfPassedTests += parseInt(result.toString().split('; ')[0].split('Passed: ')[1], 10);
                    numberOfFailedTests += parseInt(result.toString().split('; ')[1].split('Fails: ')[1], 10);
                }
            }

            function hasFailedTests(result) {
                var prop, resultStr = "";

                for (prop in result) {
                    if (result.hasOwnProperty(prop)) {
                        resultStr += result[prop];
                    }
                }

                return resultStr.indexOf("Error:") > -1;
            }

            function processCompleteTests() {
                grunt.log.verbose.writeln('>> Finished running file: ' + configFileLocation);
                grunt.log.verbose.writeln('');

                numberOfConfigs -= 1;
                if (numberOfConfigs === 0) {
                    taskComplete();
                }
            }

            function processed(error, result) {
                setNumberOfPassesAndFails(result);

                if (error || hasFailedTests(result)) {
                    failedTests.push(result);
                    grunt.verbose.writeln('   ONE or MORE tests have failed in:');
                } else {
                    grunt.verbose.writeln(result);
                }

                processCompleteTests();
            }

            function getOptionsArray(options) {
                var names, name, i, l, arr = [];

                names = Object.getOwnPropertyNames(options);
                l = names.length;
                for (i = 0; i < l; i += 1) {
                    name = names[i];

                    if (INVALID_FLAGS.indexOf(name) === -1) {
                        arr.push("--" + name);
                        arr.push(options[name]);
                    } else {
                        if (name !== 'canFail') {
                            grunt.verbose.writeln('WARNING - ' +
                                name + ' is not a valid config for use with the grunt-jstestdriver!');
                        }
                    }
                }

                return arr;
            }

            cp = grunt.util.spawn({
                cmd: 'java',
                args: ["-jar",
                       __dirname + '/../lib/jstestdriver.jar',
                       "--config",
                       configFileLocation].concat(getOptionsArray(options))
            }, processed);

            if (grunt.option('verbose')) {
                cp.stdout.pipe(process.stdout);
                cp.stderr.pipe(process.stderr);
            }
        }

        if (typeof config.files === 'string') {
            config.files = [config.files];
        }

        numberOfConfigs = config.files.length;

        grunt.util.async.forEach(config.files, function (filename) {
            runJSTestDriver(filename, options);
        }.bind(this));
    });

};
<|endoftext|>


<commit_before>/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
/** @module */

var promise = require('../promises');
var step = require('../step');

var when = promise.when;

var Yield = step.Yield;
var End = step.End;
var unamb = step.unamb;
var Pair = step.Pair;
var yieldPair = step.yieldPair;

var init = {};

exports.filter = filter;
exports.takeUntil = takeUntil;
exports.take = take;
exports.takeWhile = takeWhile;
exports.distinct = distinct;
exports.distinctBy = distinctBy;

/**
 * Retain only items matching a predicate
 * stream:                           -12345678-
 * filter(x => x % 2 === 0, stream): --2-4-6-8-
 * @param {function(x:*):boolean} p filtering predicate called for each item
 * @param {Stream} stream stream to filter
 * @returns {Stream} stream containing only items for which predicate returns truthy
 */
function filter(p, stream) {
	var stepper = stream.step;
	return stream.beget(function(state) {
		return filterNext(p, stepper, state);
	}, stream.state);
}

function filterNext(p, stepper, state) {
	return when(function(i) {
		return i.done || p(i.value) ? i
			: filterNext(p, stepper, i.state);
	}, when(stepper, state));
}

/**
 * stream:          -abcd-
 * take(2, stream): -ab
 * @param {function(x:*):boolean} p
 * @param {Stream} stream stream from which to take
 * @returns {Stream} stream containing items up to, but not including, the
 * first item for which p returns falsy.
 */
function takeWhile(p, stream) {
	var stepper = stream.step;
	return stream.beget(function(s) {
		return when(function (i) {
			return i.done || p(i.value) ? i
				: new End(i.time, i.value, s.state);
		}, when(stepper, s));
	}, stream.state);
}

/**
 * stream:                        -123451234-
 * takeWhile(x => x < 5, stream): -1234
 * @param {Number} n
 * @param {Stream} stream stream from which to take
 * @returns {Stream} stream containing at most the first n items from this stream
 */
function take(n, stream) {
	var stepper = stream.step;
	return stream.beget(function(s) {
		if(s.value === 0) {
			return new End(s.time, s.value, s.state);
		}

		return when(function (i) {
			return i.done ? i
				: i.withState(new Yield(i.time, s.value - 1, i.state));
		}, when(stepper, s.state));
	}, new Yield(stream.scheduler.now(), n|0, stream.state));
}

/**
 * stream:                    -a-b-c-d-e-f-g
 * signal:                    -------x
 * takeUntil(signal, stream): -a-b-c-
 * @param {Stream} signal retain only events in stream before the first
 * event in signal
 * @param {Stream} stream events to retain
 * @returns {Stream} new stream containing only events that occur before
 * the first event in signal.
 */
function takeUntil(signal, stream) {
	return stream.begetWithDispose(stepTakeUntil, initTakeUntil(signal, stream),
		disposeTakeUntil);
}

function stepTakeUntil(s) {
	return s.time === Infinity ? stepTakeUntilSignal(s) : stepTakeUntilTime(s);
}

function stepTakeUntilTime(s) {
	return when(function(i) {
		return i.time < s.time ? i.withState(updateTakeUntilState(s, i.state))
			: new End(s.time, i.value, s);
	}, when(s.stream.step, s.state));
}

function stepTakeUntilSignal (s) {
	return unamb(function (i, index) {
		return index === 0 ? stepTakeUntilTime(updateTakeUntilTime(s, i.time))
			: i.withState(updateTakeUntilState(s, i.state));
	}, [getSignal(s), when(s.stream.step, s.state)]);
}

function disposeTakeUntil(t, x, s) {
	return s.stream.dispose(t, x, s.state);
}

function getSignal (s) {
	return s.until === void 0 ? when(s.signal.step, s.signal.state) : s.until;
}

function initTakeUntil (signal, stream) {
	return new TakeUntil(void 0, Infinity, signal, stream, stream.state);
}

function updateTakeUntilState(s, newState) {
	return new TakeUntil(s.until, s.time, s.signal, s.stream, newState);
}

function updateTakeUntilTime(s, t) {
	return new TakeUntil(void 0, t, void 0, s.stream, s.state);
}

function TakeUntil(until, time, signal, stream, state) {
	this.until = until, this.time = time, this.signal = signal;
	this.stream = stream; this.state = state;
}

/**
 * Remove adjacent duplicates, using === to detect duplicates
 * stream:           -abbcd-
 * distinct(stream): -ab-cd-
 * @param {?function(a:*, b:*):boolean} equals optional function to compare items.
 * @returns {Stream} stream with no adjacent duplicates
 */
function distinct(stream) {
	return distinctBy(same, stream);
}

/**
 * Remove adjacent duplicates using the provided equals function to detect duplicates
 * stream:           -abbcd-
 * distinct(stream): -ab-cd-
 * @param {?function(a:*, b:*):boolean} equals optional function to compare items.
 * @param {Stream} stream stream from which to omit adjacent duplicates
 * @returns {Stream} stream with no adjacent duplicates
 */
function distinctBy(equals, stream) {
	var stepper = stream.step;
	return stream.beget(function(s) {
		return stepDistinct(equals, stepper, s);
	}, new Pair(init, stream.state));
}

function stepDistinct(equals, stepper, s) {
	return when(function(i) {
		if(i.done) {
			return i;
		}

		// Always allow the first item, and all non-duplicates
		if(s.value === init || !equals(s.value, i.value)) {
			return yieldPair(i, i.value);
		}

		return stepDistinct(equals, stepper, new Pair(s.value, i.state));
	}, when(stepper, s.state));
}

function same(a, b) {
	return a === b;
}<commit_msg>Simplify takeUtil<commit_after>/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
/** @module */

var promise = require('../promises');
var step = require('../step');

var when = promise.when;

var Yield = step.Yield;
var End = step.End;
var unamb = step.unamb;
var Pair = step.Pair;
var yieldPair = step.yieldPair;

var init = {};

exports.filter = filter;
exports.takeUntil = takeUntil;
exports.take = take;
exports.takeWhile = takeWhile;
exports.distinct = distinct;
exports.distinctBy = distinctBy;

/**
 * Retain only items matching a predicate
 * stream:                           -12345678-
 * filter(x => x % 2 === 0, stream): --2-4-6-8-
 * @param {function(x:*):boolean} p filtering predicate called for each item
 * @param {Stream} stream stream to filter
 * @returns {Stream} stream containing only items for which predicate returns truthy
 */
function filter(p, stream) {
	var stepper = stream.step;
	return stream.beget(function(state) {
		return filterNext(p, stepper, state);
	}, stream.state);
}

function filterNext(p, stepper, state) {
	return when(function(i) {
		return i.done || p(i.value) ? i
			: filterNext(p, stepper, i.state);
	}, when(stepper, state));
}

/**
 * stream:          -abcd-
 * take(2, stream): -ab
 * @param {function(x:*):boolean} p
 * @param {Stream} stream stream from which to take
 * @returns {Stream} stream containing items up to, but not including, the
 * first item for which p returns falsy.
 */
function takeWhile(p, stream) {
	var stepper = stream.step;
	return stream.beget(function(s) {
		return when(function (i) {
			return i.done || p(i.value) ? i
				: new End(i.time, i.value, s.state);
		}, when(stepper, s));
	}, stream.state);
}

/**
 * stream:                        -123451234-
 * takeWhile(x => x < 5, stream): -1234
 * @param {Number} n
 * @param {Stream} stream stream from which to take
 * @returns {Stream} stream containing at most the first n items from this stream
 */
function take(n, stream) {
	var stepper = stream.step;
	return stream.beget(function(s) {
		if(s.value === 0) {
			return new End(s.time, s.value, s.state);
		}

		return when(function (i) {
			return i.done ? i
				: i.withState(new Yield(i.time, s.value - 1, i.state));
		}, when(stepper, s.state));
	}, new Yield(stream.scheduler.now(), n|0, stream.state));
}

/**
 * stream:                    -a-b-c-d-e-f-g
 * signal:                    -------x
 * takeUntil(signal, stream): -a-b-c-
 * @param {Stream} signal retain only events in stream before the first
 * event in signal
 * @param {Stream} stream events to retain
 * @returns {Stream} new stream containing only events that occur before
 * the first event in signal.
 */
function takeUntil(signal, stream) {
	return stream.begetWithDispose(stepTakeUntil, initTakeUntil(signal, stream),
		disposeTakeUntil);
}

function stepTakeUntil(s) {
	return s.time === Infinity ? stepTakeUntilSignal(s) : stepTakeUntilTime(s);
}

function stepTakeUntilTime(s) {
	return when(function(i) {
		return i.time < s.time ? i.withState(updateTakeUntilState(s, i.state))
			: new End(s.time, i.value, s);
	}, when(s.stream.step, s.state));
}

function stepTakeUntilSignal (s) {
	return unamb(function (i, index) {
		return index === 0 ? endTakeUntil(s, i)
			 : i.withState(updateTakeUntilState(s, i.state));
	}, [getSignal(s), when(s.stream.step, s.state)]);
}

function disposeTakeUntil(t, x, s) {
	return s.stream.dispose(t, x, s.state);
}

function getSignal (s) {
	return when(s.signal.step, s.signal.state);
}

function initTakeUntil (signal, stream) {
	return new TakeUntil(Infinity, signal, stream, stream.state);
}

function updateTakeUntilState(s, newState) {
	return new TakeUntil(s.time, s.signal, s.stream, newState);
}

function endTakeUntil(s, i) {
	return new End(i.time, i.value, new TakeUntil(i.time, null, s.stream, s.state));
}

function TakeUntil(time, signal, stream, state) {
	this.time = time; this.signal = signal;
	this.stream = stream; this.state = state;
}

/**
 * Remove adjacent duplicates, using === to detect duplicates
 * stream:           -abbcd-
 * distinct(stream): -ab-cd-
 * @param {Stream} stream stream from which to omit adjacent duplicates
 * @returns {Stream} stream with no adjacent duplicates
 */
function distinct(stream) {
	return distinctBy(same, stream);
}

/**
 * Remove adjacent duplicates using the provided equals function to detect duplicates
 * stream:           -abbcd-
 * distinct(stream): -ab-cd-
 * @param {?function(a:*, b:*):boolean} equals optional function to compare items.
 * @param {Stream} stream stream from which to omit adjacent duplicates
 * @returns {Stream} stream with no adjacent duplicates
 */
function distinctBy(equals, stream) {
	var stepper = stream.step;
	return stream.beget(function(s) {
		return stepDistinct(equals, stepper, s);
	}, new Pair(init, stream.state));
}

function stepDistinct(equals, stepper, s) {
	return when(function(i) {
		if(i.done) {
			return i;
		}

		// Always allow the first item, and all non-duplicates
		if(s.value === init || !equals(s.value, i.value)) {
			return yieldPair(i, i.value);
		}

		return stepDistinct(equals, stepper, new Pair(s.value, i.state));
	}, when(stepper, s.state));
}

function same(a, b) {
	return a === b;
}<|endoftext|>


<commit_before>/*
 * grunt-photoBox
 * https://github.com/stefan/grunt-photoBox
 *
 * Copyright (c) 2013 stefan judis
 * Licensed under the MIT license.
 */

'use strict';

var fs           = require( 'fs' ),
    path         = require( 'path' ),
    phantomjs    = require( 'phantomjs' ),
    phantomPath  = phantomjs.path;

/**
 * Constructor for PhotoBox
 *
 * @param  {Object}   grunt    grunt
 * @param  {Object}   options  plugin options
 * @param  {Function} callback callback
 *
 * @tested
 */
var PhotoBox = function( grunt, options, callback ) {
  this.callback          = callback;
  this.diffCount         = 0;
  this.grunt             = grunt;
  this.options           = options;
  this.options.indexPath = this.getIndexPath();
  this.pictureCount      = 0;
  this.template          = options.useImageMagick ? 'magic' : 'default';

  this.movePictures();
  this.pictures = this.getPreparedPictures();
};

/**
 * Callback for image comparision
 *
 * @param  {String} err     error
 * @param  {Object} result  result
 * @param  {Number} code    exit code
 * @param  {String} picture name of current picture iteration
 */
PhotoBox.prototype.compareCallback = function( err, result, code, picture ) {
  if ( err ) {
    this.grunt.log.error( err );
  }

  this.grunt.log.verbose.writeln(
    'CompareCallback: Result for ' + picture + ' was ' + result
  );
  this.grunt.log.verbose.writeln(
    'CompareCallback: Code for ' + picture + ' was ' + code
  );

  this.grunt.util.spawn( {
    cmd  : 'composite',
    args : this.getCompositArguments( picture )
  }, function( err, code, result ) {
    this.overlayCallback( err, code, result, picture );
  }.bind( this ) );
};

/**
 * Callback for image overlay operation
 *
 * @param  {String} err     error
 * @param  {Object} result  result
 * @param  {Number} code    exit code
 * @param  {String} picture name of current picture iteration
 *
 * @tested
 */
PhotoBox.prototype.overlayCallback = function( err, result, code, picture ) {
  if ( err ) {
    this.grunt.log.error( err );
  } else {
    this.grunt.log.ok( 'diff for ' + picture + ' generated.' );
  }

  this.grunt.log.verbose.writeln(
    'OverlayCallback: Result for ' + picture + ' was ' + result
  );
  this.grunt.log.verbose.writeln(
    'OverlayCallback: Code for ' + picture + ' was ' + code
  );

  ++this.diffCount;

  this.tookDiffHandler();
};

/**
 * Actuel function to create the diff images
 */
PhotoBox.prototype.createDiffImages = function() {
  var imgPath = this.options.indexPath + 'img/' ;

  this.grunt.log.subhead( 'PHOTOBOX STARTED DIFF GENERATION.');

  this.pictures.forEach( function( picture ) {
    picture = picture.replace( /(http:\/\/|https:\/\/)/, '').replace( /(\/)|(\|)/g, '-');
    this.grunt.log.writeln( 'started diff for ' + picture );

    var oldFileExists = this.grunt.file.exists(
                          this.options.indexPath + 'img/last/' + picture + '.png'
                        );

    var currentFileExists = this.grunt.file.exists(
                              this.options.indexPath + 'img/current/' + picture + '.png'
                            );
    if ( oldFileExists && currentFileExists ) {

      this.grunt.util.spawn( {
        cmd  : 'compare',
        args : this.getCompareArguments( picture )
      }, function( err, result, code ) {
        this.compareCallback( err, result, code, picture );
      }.bind ( this ) );

    } else {
      this.grunt.log.error(
        'Nothing to diff here - no old pictures available.'
      );

      ++this.diffCount;

      this.tookDiffHandler();
    }
  }.bind( this ) );
};

/**
 * Create index file.
 *
 * @tested
 */
PhotoBox.prototype.createIndexFile = function() {
  this.grunt.log.subhead( 'PHOTOBOX STARTED INDEX FILE GENERATION' );

  this.grunt.file.write(
    this.options.indexPath + 'index.html',
    this.grunt.template.process(
      this.grunt.file.read( path.dirname( __dirname ) + '/tpl/' + this.template + '.tpl'),
      { data : {
        pictures   : this.pictures,
        timestamps : this.getTimestamps()
      } }
    )
  );

  this.grunt.log.ok(
    'PHOTOBOX CREATED NEW \'index.html\' AT \'' + this.options.indexPath + '\'.'
  );
};

/**
 * Getter for in constructor set callback function
 * Mostly for testing purposes
 *
 * @return {Function} callback
 */
PhotoBox.prototype.getCallback = function() {
  return this.callback;
};

/**
 * Helper function to evaluate correct path for index file
 *
 * @return {String} indexPath
 *
 * @tested
 */
PhotoBox.prototype.getIndexPath = function() {
  var indexPath = this.options.indexPath;

  if ( !indexPath ) {
    this.grunt.log.error( 'No indexPath set.' );
  }

  if ( indexPath[ indexPath.length - 1 ] !== '/' ) {
    indexPath += '/';
  }

  return indexPath;
};

/**
 * Helper function to build up the arguments
 * array for the compare command
 *
 * @param  {String} picture picture
 * @return {Array}          Array including all arguments
 */
PhotoBox.prototype.getCompareArguments = function( picture ) {
  return [
    '-compose',
    'src',
    '-highlight-color',
    this.options.highlightColor,
    this.options.indexPath + 'img/current/' + picture + '.png',
    this.options.indexPath + 'img/last/' + picture + '.png',
    this.options.indexPath + 'img/diff/' + picture + '-diff.png'
  ];
};

/**
 * Helper function to build up the arguments
 * array for the composit command
 *
 * @param  {String} picture picture
 * @return {Array}          Array including all arguments
 */
PhotoBox.prototype.getCompositArguments = function( picture ) {
  return [
    '-alpha',
    'on',
    this.options.indexPath + 'img/diff/' + picture + '-diff.png',
    this.options.indexPath + 'img/last/' + picture + '.png',
    this.options.indexPath + 'img/diff/' + picture + '.png'
  ];
};

/**
 * Getter for diffCount
 * Mostly for testing purposes
 *
 * @return {Number} pictureCount
 */
PhotoBox.prototype.getDiffCount = function() {
  return this.diffCount;
};

/**
 * Getter for options
 * Mostly for testing purposes
 *
 * @return {Object} options
 */
PhotoBox.prototype.getOptions = function() {
  return this.options;
};

/**
 * Getter for pictureCount
 * Mostly for testing purposes
 *
 * @return {Number} pictureCount
 */
PhotoBox.prototype.getPictureCount = function() {
  return this.pictureCount;
};

/**
 * Getter for pictures array.
 * Mostly for testing purposes
 *
 * @return {Array} pictures
 */
PhotoBox.prototype.getPictures = function() {
  return this.pictures || null;
};

/**
 * Get prepared picture array.
 *
 * @return {Array} Array with concatenated picture information
 *
 * @tested
 */
PhotoBox.prototype.getPreparedPictures = function() {
  var pictures = [];

  this.options.urls.forEach( function( url ) {
    this.options.screenSizes.forEach( function( size ) {
      pictures.push( url + '|' + size );
    } );
  }, this );

  return pictures;
};

/**
 * Get timestampe for given file by reading timestamp.json file
 *
 * @param  {String} name name
 * @return {String}      actual timestamp
 *
 * @tested
 */
PhotoBox.prototype.getTimestamp = function( name ) {
  var timestampContent,
      timestamp;

  try {
    timestampContent = this.grunt.file.read(
                            this.options.indexPath + '/img/' + name + '/timestamp.json'
                          );

    timestamp = JSON.parse( timestampContent ).timestamp;
  } catch ( e ) {
    this.grunt.log.error(
      'Something went wrong with reading timestamp file for ' + name + ' photosession'
    );

    timestamp = 'Not available';
  }

  return timestamp;
};

/**
 * Get object with current and last timestamps
 *
 * @return {Object} timestamps
 *
 * @tested
 */
PhotoBox.prototype.getTimestamps = function() {
  return {
    current : this.getTimestamp( 'current' ),
    last    : this.getTimestamp( 'last' )
  };
};

/**
 * Move current pictures to latest directory
 *
 * @tested
 */
PhotoBox.prototype.movePictures = function() {
  // delete old images
  if ( this.grunt.file.exists( this.options.indexPath + '/img/last' ) ) {
    this.grunt.file.delete( this.options.indexPath + '/img/last' );
  }

  // delete old diffs
  if ( this.grunt.file.exists( this.options.indexPath + '/img/diff' ) ) {
    this.grunt.file.delete( this.options.indexPath + '/img/diff' );
  }

  // create new diff fole - imageMagick is not able to create it on its own
  this.grunt.file.mkdir( this.options.indexPath + '/img/diff' );

  // move current picture to old pictures
  if ( !this.grunt.file.exists( this.options.indexPath + '/img/current' ) ) {
    this.grunt.log.error(
      'No old pictures are existant. So you can compare kittens with the new pictures.'
    );
  } else {
    fs.renameSync(
      this.options.indexPath + '/img/current',
      this.options.indexPath + '/img/last',
      function( err ) {
        if ( err ) {
          this.grunt.log.error( err );
          this.grunt.verbose.error();
          this.grunt.fail.warn( 'Rename operation failed.' );
        }
      }
    );
  }
};

/**
 * Callback after phantomjs operation
 *
 * @param  {String} err     error
 * @param  {Object} result  result
 * @param  {Number} code    exit code
 * @param  {String} picture name of current picture iteration
 *
 * @tested
 */
PhotoBox.prototype.photoSessionCallback = function( err, result, code, picture ) {
  if ( err ) {
    this.grunt.log.error( 'Takin\' picture of ' + picture + 'did not work correclty...' );
    this.grunt.log.writeln( err );
  } else {
    this.grunt.log.ok( 'picture of ' + picture + ' taken.' );
  }

  this.grunt.log.verbose.writeln(
    'PhotoSessionCallback: Result for ' + picture + ' was ' + result
  );
  this.grunt.log.verbose.writeln(
    'PhotoSessionCallback: Code for ' + picture + ' was ' + code
  );

  ++this.pictureCount;

  this.tookPictureHandler();
};

/**
 * Setter for picture count
 * Mostly for testing purposes
 *
 * @param  {Number} count count
 * @return {Number}       new set count
 */
PhotoBox.prototype.setPictureCount = function( count ) {
  this.pictureCount = count;

  return this.pictureCount;
};

/**
 * Start a session of taking pictures
 */
PhotoBox.prototype.startPhotoSession = function() {
  this.grunt.log.subhead( 'PHOTOBOX STARTED PHOTO SESSION.' );

  this.writeTimestampFile();

  this.pictures.forEach( function( picture ) {
    this.grunt.log.writeln( 'started photo session for ' + picture );

    this.writeOptionsFile( {
      javascriptEnabled             : this.options.javascriptEnabled,
      loadImages                    : this.options.localToRemoteUrlAccessEnabled,
      localToRemoteUrlAccessEnabled : this.options.localToRemoteUrlAccessEnabled,
      password                      : this.options.password,
      userAgent                     : this.options.userAgent,
      userName                      : this.options.userName
    } );

    var args = [
      path.resolve(__dirname, 'photoboxScript.js'),
      picture,
      this.options.indexPath,
      this.options.indexPath + 'options.json'
    ];

    var opts = {};

    if ( this.grunt.option( 'verbose' ) ) {
      opts = {
        stdio: 'inherit'
      };
    }

    this.grunt.log.verbose.writeln( 'Command: phantomjs ' + args.join( ' ' ) + '\n' );

    this.grunt.util.spawn( {
      cmd  : phantomPath,
      args : args,
      opts : opts
    }, function( err, result, code ) {
      this.photoSessionCallback( err, result, code, picture );
    }.bind( this ) );
  }.bind( this ) );
};

/**
 * Handler for emitted 'tookDiff'
 */
PhotoBox.prototype.tookDiffHandler = function() {
  if ( this.diffCount === this.pictures.length ) {
    this.grunt.log.ok( 'PHOTOBOX FINISHED DIFF GENERATION SUCCESSFULLY.' );

    this.createIndexFile( 'default' );
    // call done() to exit grunt task
    this.callback();
  }
};

/**
 * Handler for emitted 'tookPicture'
 *
 * @tested
 */
PhotoBox.prototype.tookPictureHandler = function() {
  if ( this.pictureCount === this.pictures.length ) {
    this.grunt.log.ok( 'PHOTOBOX FINISHED PHOTO SESSION SUCCESSFULLY.' );

    if ( this.options.useImageMagick ) {
      this.grunt.log.writeln(
        '\nNOTE: You defined to use ImageMagick, make sure it is installed.'
      );

      this.createDiffImages();
    } else {
      this.createIndexFile();
      // call done() to exit grunt task
      this.callback();
    }
  }
};

/**
 * Write options file to pass it to
 * phantomjs
 * -> JSON.stringify brings only troubles
 *     as a system argument
 *
 * @param  {Object} options options
 *
 * @tested
 */
PhotoBox.prototype.writeOptionsFile = function( options ) {
  this.grunt.file.write(
    this.options.indexPath + 'options.json',
    JSON.stringify( options )
  );
};

/**
 * Write JSON file to store timestamp
 * of current photosession
 *
 * @tested
 */
PhotoBox.prototype.writeTimestampFile = function() {
  var date       = new Date(),
      dateString = date.toString();

  this.grunt.file.write(
    this.options.indexPath + '/img/current/timestamp.json',
    JSON.stringify( {
      timestamp: dateString
    } )
  );

  this.grunt.log.verbose.writeln(
    'Wrote timestamp file with ' + dateString + '.'
  );
};

module.exports = PhotoBox;
<commit_msg>improved comments<commit_after>/*
 * grunt-photoBox
 * https://github.com/stefan/grunt-photoBox
 *
 * Copyright (c) 2013 stefan judis
 * Licensed under the MIT license.
 */

'use strict';

var fs           = require( 'fs' ),
    path         = require( 'path' ),
    phantomjs    = require( 'phantomjs' ),
    phantomPath  = phantomjs.path;

/**
 * Constructor for PhotoBox
 *
 * @param  {Object}   grunt    grunt
 * @param  {Object}   options  plugin options
 * @param  {Function} callback callback
 *
 * @tested
 */
var PhotoBox = function( grunt, options, callback ) {
  this.callback          = callback;
  this.diffCount         = 0;
  this.grunt             = grunt;
  this.options           = options;
  this.options.indexPath = this.getIndexPath();
  this.pictureCount      = 0;
  this.template          = options.useImageMagick ? 'magic' : 'default';

  this.movePictures();
  this.pictures = this.getPreparedPictures();
};

/**
 * Callback for image comparision
 *
 * @param  {String} err     error
 * @param  {Object} result  result
 * @param  {Number} code    exit code
 * @param  {String} picture name of current picture iteration
 */
PhotoBox.prototype.compareCallback = function( err, result, code, picture ) {
  if ( err ) {
    this.grunt.log.error( err );
  }

  this.grunt.log.verbose.writeln(
    'CompareCallback: Result for ' + picture + ' was ' + result
  );
  this.grunt.log.verbose.writeln(
    'CompareCallback: Code for ' + picture + ' was ' + code
  );

  this.grunt.util.spawn( {
    cmd  : 'composite',
    args : this.getCompositArguments( picture )
  }, function( err, code, result ) {
    this.overlayCallback( err, code, result, picture );
  }.bind( this ) );
};

/**
 * Callback for image overlay operation
 *
 * @param  {String} err     error
 * @param  {Object} result  result
 * @param  {Number} code    exit code
 * @param  {String} picture name of current picture iteration
 *
 * @tested
 */
PhotoBox.prototype.overlayCallback = function( err, result, code, picture ) {
  if ( err ) {
    this.grunt.log.error( err );
  } else {
    this.grunt.log.ok( 'diff for ' + picture + ' generated.' );
  }

  this.grunt.log.verbose.writeln(
    'OverlayCallback: Result for ' + picture + ' was ' + result
  );
  this.grunt.log.verbose.writeln(
    'OverlayCallback: Code for ' + picture + ' was ' + code
  );

  ++this.diffCount;

  this.tookDiffHandler();
};

/**
 * Actuel function to create the diff images
 */
PhotoBox.prototype.createDiffImages = function() {
  var imgPath = this.options.indexPath + 'img/' ;

  this.grunt.log.subhead( 'PHOTOBOX STARTED DIFF GENERATION.');

  this.pictures.forEach( function( picture ) {
    picture = picture.replace( /(http:\/\/|https:\/\/)/, '').replace( /(\/)|(\|)/g, '-');
    this.grunt.log.writeln( 'started diff for ' + picture );

    var oldFileExists = this.grunt.file.exists(
                          this.options.indexPath + 'img/last/' + picture + '.png'
                        );

    var currentFileExists = this.grunt.file.exists(
                              this.options.indexPath + 'img/current/' + picture + '.png'
                            );
    if ( oldFileExists && currentFileExists ) {

      this.grunt.util.spawn( {
        cmd  : 'compare',
        args : this.getCompareArguments( picture )
      }, function( err, result, code ) {
        this.compareCallback( err, result, code, picture );
      }.bind ( this ) );

    } else {
      this.grunt.log.error(
        'Nothing to diff here - no old pictures available.'
      );

      ++this.diffCount;

      this.tookDiffHandler();
    }
  }.bind( this ) );
};

/**
 * Create index file.
 *
 * @tested
 */
PhotoBox.prototype.createIndexFile = function() {
  this.grunt.log.subhead( 'PHOTOBOX STARTED INDEX FILE GENERATION' );

  this.grunt.file.write(
    this.options.indexPath + 'index.html',
    this.grunt.template.process(
      this.grunt.file.read( path.dirname( __dirname ) + '/tpl/' + this.template + '.tpl'),
      { data : {
        pictures   : this.pictures,
        timestamps : this.getTimestamps()
      } }
    )
  );

  this.grunt.log.ok(
    'PHOTOBOX CREATED NEW \'index.html\' AT \'' + this.options.indexPath + '\'.'
  );
};

/**
 * Getter for in constructor set callback function
 * Mostly for testing purposes
 *
 * @return {Function} callback
 */
PhotoBox.prototype.getCallback = function() {
  return this.callback;
};

/**
 * Helper function to evaluate correct path for index file
 *
 * @return {String} indexPath
 *
 * @tested
 */
PhotoBox.prototype.getIndexPath = function() {
  var indexPath = this.options.indexPath;

  if ( !indexPath ) {
    this.grunt.log.error( 'No indexPath set.' );
  }

  if ( indexPath[ indexPath.length - 1 ] !== '/' ) {
    indexPath += '/';
  }

  return indexPath;
};

/**
 * Helper function to build up the arguments
 * array for the compare command
 *
 * @param  {String} picture picture
 * @return {Array}          Array including all arguments
 */
PhotoBox.prototype.getCompareArguments = function( picture ) {
  return [
    '-compose',
    'src',
    '-highlight-color',
    this.options.highlightColor,
    this.options.indexPath + 'img/current/' + picture + '.png',
    this.options.indexPath + 'img/last/' + picture + '.png',
    this.options.indexPath + 'img/diff/' + picture + '-diff.png'
  ];
};

/**
 * Helper function to build up the arguments
 * array for the composit command
 *
 * @param  {String} picture picture
 * @return {Array}          Array including all arguments
 */
PhotoBox.prototype.getCompositArguments = function( picture ) {
  return [
    '-alpha',
    'on',
    this.options.indexPath + 'img/diff/' + picture + '-diff.png',
    this.options.indexPath + 'img/last/' + picture + '.png',
    this.options.indexPath + 'img/diff/' + picture + '.png'
  ];
};

/**
 * Getter for diffCount
 * Mostly for testing purposes
 *
 * @return {Number} pictureCount
 */
PhotoBox.prototype.getDiffCount = function() {
  return this.diffCount;
};

/**
 * Getter for options
 * Mostly for testing purposes
 *
 * @return {Object} options
 */
PhotoBox.prototype.getOptions = function() {
  return this.options;
};

/**
 * Getter for pictureCount
 * Mostly for testing purposes
 *
 * @return {Number} pictureCount
 */
PhotoBox.prototype.getPictureCount = function() {
  return this.pictureCount;
};

/**
 * Getter for pictures array.
 * Mostly for testing purposes
 *
 * @return {Array} pictures
 */
PhotoBox.prototype.getPictures = function() {
  return this.pictures || null;
};

/**
 * Get prepared picture array.
 *
 * @return {Array} Array with concatenated picture information
 *
 * @tested
 */
PhotoBox.prototype.getPreparedPictures = function() {
  var pictures = [];

  this.options.urls.forEach( function( url ) {
    this.options.screenSizes.forEach( function( size ) {
      pictures.push( url + '|' + size );
    } );
  }, this );

  return pictures;
};

/**
 * Get timestampe for given file by reading timestamp.json file
 *
 * @param  {String} name name
 * @return {String}      actual timestamp
 *
 * @tested
 */
PhotoBox.prototype.getTimestamp = function( name ) {
  var timestampContent,
      timestamp;

  try {
    timestampContent = this.grunt.file.read(
                            this.options.indexPath + '/img/' + name + '/timestamp.json'
                          );

    timestamp = JSON.parse( timestampContent ).timestamp;
  } catch ( e ) {
    this.grunt.log.error(
      'Something went wrong with reading timestamp file for ' + name + ' photosession'
    );

    timestamp = 'Not available';
  }

  return timestamp;
};

/**
 * Get object with current and last timestamps
 *
 * @return {Object} timestamps
 *
 * @tested
 */
PhotoBox.prototype.getTimestamps = function() {
  return {
    current : this.getTimestamp( 'current' ),
    last    : this.getTimestamp( 'last' )
  };
};

/**
 * Move current pictures to latest directory
 *
 * @tested
 */
PhotoBox.prototype.movePictures = function() {
  // delete old images
  if ( this.grunt.file.exists( this.options.indexPath + '/img/last' ) ) {
    this.grunt.file.delete( this.options.indexPath + '/img/last' );
  }

  // delete old diffs
  if ( this.grunt.file.exists( this.options.indexPath + '/img/diff' ) ) {
    this.grunt.file.delete( this.options.indexPath + '/img/diff' );
  }

  // create new diff folder - imageMagick is not able to create it on its own
  this.grunt.file.mkdir( this.options.indexPath + '/img/diff' );

  // move current picture to old pictures
  if ( !this.grunt.file.exists( this.options.indexPath + '/img/current' ) ) {
    this.grunt.log.error(
      'No old pictures are existant. So you can compare kittens with the new pictures.'
    );
  } else {
    fs.renameSync(
      this.options.indexPath + '/img/current',
      this.options.indexPath + '/img/last',
      function( err ) {
        if ( err ) {
          this.grunt.log.error( err );
          this.grunt.verbose.error();
          this.grunt.fail.warn( 'Rename operation failed.' );
        }
      }
    );
  }
};

/**
 * Callback after phantomjs operation
 *
 * @param  {String} err     error
 * @param  {Object} result  result
 * @param  {Number} code    exit code
 * @param  {String} picture name of current picture iteration
 *
 * @tested
 */
PhotoBox.prototype.photoSessionCallback = function( err, result, code, picture ) {
  if ( err ) {
    this.grunt.log.error( 'Takin\' picture of ' + picture + 'did not work correclty...' );
    this.grunt.log.writeln( err );
  } else {
    this.grunt.log.ok( 'picture of ' + picture + ' taken.' );
  }

  this.grunt.log.verbose.writeln(
    'PhotoSessionCallback: Result for ' + picture + ' was ' + result
  );
  this.grunt.log.verbose.writeln(
    'PhotoSessionCallback: Code for ' + picture + ' was ' + code
  );

  ++this.pictureCount;

  this.tookPictureHandler();
};

/**
 * Setter for picture count
 * Mostly for testing purposes
 *
 * @param  {Number} count count
 * @return {Number}       new set count
 */
PhotoBox.prototype.setPictureCount = function( count ) {
  this.pictureCount = count;

  return this.pictureCount;
};

/**
 * Start a session of taking pictures
 */
PhotoBox.prototype.startPhotoSession = function() {
  this.grunt.log.subhead( 'PHOTOBOX STARTED PHOTO SESSION.' );

  this.writeTimestampFile();

  this.pictures.forEach( function( picture ) {
    this.grunt.log.writeln( 'started photo session for ' + picture );

    this.writeOptionsFile( {
      javascriptEnabled             : this.options.javascriptEnabled,
      loadImages                    : this.options.localToRemoteUrlAccessEnabled,
      localToRemoteUrlAccessEnabled : this.options.localToRemoteUrlAccessEnabled,
      password                      : this.options.password,
      userAgent                     : this.options.userAgent,
      userName                      : this.options.userName
    } );

    var args = [
      path.resolve(__dirname, 'photoboxScript.js'),
      picture,
      this.options.indexPath,
      this.options.indexPath + 'options.json'
    ];

    var opts = {};

    if ( this.grunt.option( 'verbose' ) ) {
      opts = {
        stdio: 'inherit'
      };
    }

    this.grunt.log.verbose.writeln( 'Command: phantomjs ' + args.join( ' ' ) + '\n' );

    this.grunt.util.spawn( {
      cmd  : phantomPath,
      args : args,
      opts : opts
    }, function( err, result, code ) {
      this.photoSessionCallback( err, result, code, picture );
    }.bind( this ) );
  }.bind( this ) );
};

/**
 * Handler for emitted 'tookDiff'
 */
PhotoBox.prototype.tookDiffHandler = function() {
  if ( this.diffCount === this.pictures.length ) {
    this.grunt.log.ok( 'PHOTOBOX FINISHED DIFF GENERATION SUCCESSFULLY.' );

    this.createIndexFile( 'default' );
    // call done() to exit grunt task
    this.callback();
  }
};

/**
 * Handler for emitted 'tookPicture'
 *
 * @tested
 */
PhotoBox.prototype.tookPictureHandler = function() {
  if ( this.pictureCount === this.pictures.length ) {
    this.grunt.log.ok( 'PHOTOBOX FINISHED PHOTO SESSION SUCCESSFULLY.' );

    if ( this.options.useImageMagick ) {
      this.grunt.log.writeln(
        '\nNOTE: You defined to use ImageMagick, make sure it is installed.'
      );

      this.createDiffImages();
    } else {
      this.createIndexFile();
      // call done() to exit grunt task
      this.callback();
    }
  }
};

/**
 * Write options file to pass it to
 * phantomjs
 * -> JSON.stringify brings only troubles
 *     as a system argument
 *
 * @param  {Object} options options
 *
 * @tested
 */
PhotoBox.prototype.writeOptionsFile = function( options ) {
  this.grunt.file.write(
    this.options.indexPath + 'options.json',
    JSON.stringify( options )
  );
};

/**
 * Write JSON file to store timestamp
 * of current photosession
 *
 * @tested
 */
PhotoBox.prototype.writeTimestampFile = function() {
  var date       = new Date(),
      dateString = date.toString();

  this.grunt.file.write(
    this.options.indexPath + '/img/current/timestamp.json',
    JSON.stringify( {
      timestamp: dateString
    } )
  );

  this.grunt.log.verbose.writeln(
    'Wrote timestamp file with ' + dateString + '.'
  );
};

module.exports = PhotoBox;
<|endoftext|>