<commit_before>exports = module.exports = lifecycle
exports.cmd = cmd
exports.makeEnv = makeEnv

var log = require('npmlog')
var spawn = require('./spawn')
var npm = require('../npm.js')
var path = require('path')
var fs = require('graceful-fs')
var chain = require('slide').chain
var Stream = require('stream').Stream
var PATH = 'PATH'
var uidNumber = require('uid-number')
var umask = require('./umask')

// windows calls it's path 'Path' usually, but this is not guaranteed.
if (process.platform === 'win32') {
  PATH = 'Path'
  Object.keys(process.env).forEach(function (e) {
    if (e.match(/^PATH$/i)) {
      PATH = e
    }
  })
}

function lifecycle (pkg, stage, wd, unsafe, failOk, cb) {
  if (typeof cb !== 'function') {
    cb = failOk
    failOk = false
  }
  if (typeof cb !== 'function') {
    cb = unsafe
    unsafe = false
  }
  if (typeof cb !== 'function') {
    cb = wd
    wd = null
  }

  while (pkg && pkg._data) pkg = pkg._data
  if (!pkg) return cb(new Error('Invalid package data'))

  log.info(stage, pkg._id)
  if (!pkg.scripts || npm.config.get('ignore-scripts')) pkg.scripts = {}

  validWd(wd || path.resolve(npm.dir, pkg.name), function (er, wd) {
    if (er) return cb(er)

    unsafe = unsafe || npm.config.get('unsafe-perm')

    if ((wd.indexOf(npm.dir) !== 0 ||
          wd.indexOf(pkg.name) !== wd.length - pkg.name.length) &&
        !unsafe && pkg.scripts[stage]) {
      log.warn(
        'cannot run in wd',
        '%s %s (wd=%s)', pkg._id, pkg.scripts[stage], wd
      )
      return cb()
    }

    // set the env variables, then run scripts as a child process.
    var env = makeEnv(pkg)
    env.npm_lifecycle_event = stage
    env.npm_node_execpath = env.NODE = env.NODE || process.execPath
    env.npm_execpath = require.main.filename

    // 'nobody' typically doesn't have permission to write to /tmp
    // even if it's never used, sh freaks out.
    if (!npm.config.get('unsafe-perm')) env.TMPDIR = wd

    lifecycle_(pkg, stage, wd, env, unsafe, failOk, cb)
  })
}

function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) {
  var pathArr = []
  var p = wd.split('node_modules')
  var acc = path.resolve(p.shift())

  p.forEach(function (pp) {
    pathArr.unshift(path.join(acc, 'node_modules', '.bin'))
    acc = path.join(acc, 'node_modules', pp)
  })
  pathArr.unshift(path.join(acc, 'node_modules', '.bin'))

  // we also unshift the bundled node-gyp-bin folder so that
  // the bundled one will be used for installing things.
  pathArr.unshift(path.join(__dirname, '..', '..', 'bin', 'node-gyp-bin'))

  if (env[PATH]) pathArr.push(env[PATH])
  env[PATH] = pathArr.join(process.platform === 'win32' ? ';' : ':')

  var packageLifecycle = pkg.scripts && pkg.scripts.hasOwnProperty(stage)

  if (packageLifecycle) {
    // define this here so it's available to all scripts.
    env.npm_lifecycle_script = pkg.scripts[stage]
  }

  function done (er) {
    if (er) {
      if (npm.config.get('force')) {
        log.info('forced, continuing', er)
        er = null
      } else if (failOk) {
        log.warn('continuing anyway', er.message)
        er = null
      }
    }
    cb(er)
  }

  chain(
    [
      packageLifecycle && [runPackageLifecycle, pkg, env, wd, unsafe],
      [runHookLifecycle, pkg, env, wd, unsafe]
    ],
    done
  )
}

function validWd (d, cb) {
  fs.stat(d, function (er, st) {
    if (er || !st.isDirectory()) {
      var p = path.dirname(d)
      if (p === d) {
        return cb(new Error('Could not find suitable wd'))
      }
      return validWd(p, cb)
    }
    return cb(null, d)
  })
}

function runPackageLifecycle (pkg, env, wd, unsafe, cb) {
  // run package lifecycle scripts in the package root, or the nearest parent.
  var stage = env.npm_lifecycle_event
  var cmd = env.npm_lifecycle_script

  var note = '\n> ' + pkg._id + ' ' + stage + ' ' + wd +
             '\n> ' + cmd + '\n'
  runCmd(note, cmd, pkg, env, stage, wd, unsafe, cb)
}

var running = false
var queue = []
function dequeue () {
  running = false
  if (queue.length) {
    var r = queue.shift()
    runCmd.apply(null, r)
  }
}

function runCmd (note, cmd, pkg, env, stage, wd, unsafe, cb) {
  if (running) {
    queue.push([note, cmd, pkg, env, stage, wd, unsafe, cb])
    return
  }

  running = true
  log.pause()
  var user = unsafe ? null : npm.config.get('user')
  var group = unsafe ? null : npm.config.get('group')

  if (log.level !== 'silent') {
    log.clearProgress()
    console.log(note)
    log.showProgress()
  }
  log.verbose('unsafe-perm in lifecycle', unsafe)

  if (process.platform === 'win32') {
    unsafe = true
  }

  if (unsafe) {
    runCmd_(cmd, pkg, env, wd, stage, unsafe, 0, 0, cb)
  } else {
    uidNumber(user, group, function (er, uid, gid) {
      runCmd_(cmd, pkg, env, wd, stage, unsafe, uid, gid, cb)
    })
  }
}

function runCmd_ (cmd, pkg, env, wd, stage, unsafe, uid, gid, cb_) {

  function cb (er) {
    cb_.apply(null, arguments)
    log.resume()
    process.nextTick(dequeue)
  }

  var conf = {
    cwd: wd,
    env: env,
    stdio: [ 0, 1, 2 ]
  }

  if (!unsafe) {
    conf.uid = uid ^ 0
    conf.gid = gid ^ 0
  }

  var sh = 'sh'
  var shFlag = '-c'

  if (process.platform === 'win32') {
    sh = process.env.comspec || 'cmd'
    shFlag = '/c'
    conf.windowsVerbatimArguments = true
  }

  log.disableProgress()
  var proc = spawn(sh, [shFlag, cmd], conf)

  proc.on('error', procError)
  proc.on('close', function (code, signal) {
    if (signal) {
      process.kill(process.pid, signal)
    } else if (code) {
      var er = new Error('Exit status ' + code)
    }
    procError(er)
  })

  function procError (er) {
    log.enableProgress()
    if (er && !npm.ROLLBACK) {
      log.info(pkg._id, 'Failed to exec ' + stage + ' script')
      er.message = pkg._id + ' ' + stage + ': `' + cmd + '`\n' +
                   er.message
      if (er.code !== 'EPERM') {
        er.code = 'ELIFECYCLE'
      }
      er.pkgid = pkg._id
      er.stage = stage
      er.script = cmd
      er.pkgname = pkg.name
      return cb(er)
    } else if (er) {
      log.error(pkg._id + '.' + stage, er)
      log.error(pkg._id + '.' + stage, 'continuing anyway')
      return cb()
    }
    cb(er)
  }
}

function runHookLifecycle (pkg, env, wd, unsafe, cb) {
  // check for a hook script, run if present.
  var stage = env.npm_lifecycle_event
  var hook = path.join(npm.dir, '.hooks', stage)
  var cmd = hook

  fs.stat(hook, function (er) {
    if (er) return cb()
    var note = '\n> ' + pkg._id + ' ' + stage + ' ' + wd +
               '\n> ' + cmd
    runCmd(note, hook, pkg, env, stage, wd, unsafe, cb)
  })
}

function makeEnv (data, prefix, env) {
  prefix = prefix || 'npm_package_'
  if (!env) {
    env = {}
    for (var i in process.env) if (!i.match(/^npm_/)) {
      env[i] = process.env[i]
    }

    // npat asks for tap output
    if (npm.config.get('npat')) env.TAP = 1

    // express and others respect the NODE_ENV value.
    if (npm.config.get('production')) env.NODE_ENV = 'production'

  } else if (!data.hasOwnProperty('_lifecycleEnv')) {
    Object.defineProperty(data, '_lifecycleEnv',
      {
        value: env,
        enumerable: false
      }
    )
  }

  for (i in data) if (i.charAt(0) !== '_') {
    var envKey = (prefix + i).replace(/[^a-zA-Z0-9_]/g, '_')
    if (i === 'readme') {
      continue
    }
    if (data[i] && typeof data[i] === 'object') {
      try {
        // quick and dirty detection for cyclical structures
        JSON.stringify(data[i])
        makeEnv(data[i], envKey + '_', env)
      } catch (ex) {
        // usually these are package objects.
        // just get the path and basic details.
        var d = data[i]
        makeEnv(
          { name: d.name, version: d.version, path: d.path },
          envKey + '_',
          env
        )
      }
    } else {
      env[envKey] = String(data[i])
      env[envKey] = env[envKey].indexOf('\n') !== -1 ?
                      JSON.stringify(env[envKey]) :
                      env[envKey]
    }

  }

  if (prefix !== 'npm_package_') return env

  prefix = 'npm_config_'
  var pkgConfig = {}
  var keys = npm.config.keys
  var pkgVerConfig = {}
  var namePref = data.name + ':'
  var verPref = data.name + '@' + data.version + ':'

  keys.forEach(function (i) {
    if (i.charAt(0) === '_' && i.indexOf('_' + namePref) !== 0) {
      return
    }
    var value = npm.config.get(i)
    if (value instanceof Stream || Array.isArray(value)) return
    if (i.match(/umask/)) value = umask.toString(value)
    if (!value) value = ''
    else if (typeof value === 'number') value = '' + value
    else if (typeof value !== 'string') value = JSON.stringify(value)

    value = value.indexOf('\n') !== -1
          ? JSON.stringify(value)
          : value
    i = i.replace(/^_+/, '')
    var k
    if (i.indexOf(namePref) === 0) {
      k = i.substr(namePref.length).replace(/[^a-zA-Z0-9_]/g, '_')
      pkgConfig[k] = value
    } else if (i.indexOf(verPref) === 0) {
      k = i.substr(verPref.length).replace(/[^a-zA-Z0-9_]/g, '_')
      pkgVerConfig[k] = value
    }
    var envKey = (prefix + i).replace(/[^a-zA-Z0-9_]/g, '_')
    env[envKey] = value
  })

  prefix = 'npm_package_config_'
  ;[pkgConfig, pkgVerConfig].forEach(function (conf) {
    for (var i in conf) {
      var envKey = (prefix + i)
      env[envKey] = conf[i]
    }
  })

  return env
}

function cmd (stage) {
  function CMD (args, cb) {
    npm.commands['run-script']([stage].concat(args), cb)
  }
  CMD.usage = 'npm ' + stage + ' [-- <args>]'
  var installedShallow = require('./completion/installed-shallow.js')
  CMD.completion = function (opts, cb) {
    installedShallow(opts, function (d) {
      return d.scripts && d.scripts[stage]
    }, cb)
  }
  return CMD
}
<commit_msg>lifecycle: Don't enable the progress bar if it was previously disabled<commit_after>exports = module.exports = lifecycle
exports.cmd = cmd
exports.makeEnv = makeEnv

var log = require('npmlog')
var spawn = require('./spawn')
var npm = require('../npm.js')
var path = require('path')
var fs = require('graceful-fs')
var chain = require('slide').chain
var Stream = require('stream').Stream
var PATH = 'PATH'
var uidNumber = require('uid-number')
var umask = require('./umask')

// windows calls it's path 'Path' usually, but this is not guaranteed.
if (process.platform === 'win32') {
  PATH = 'Path'
  Object.keys(process.env).forEach(function (e) {
    if (e.match(/^PATH$/i)) {
      PATH = e
    }
  })
}

function lifecycle (pkg, stage, wd, unsafe, failOk, cb) {
  if (typeof cb !== 'function') {
    cb = failOk
    failOk = false
  }
  if (typeof cb !== 'function') {
    cb = unsafe
    unsafe = false
  }
  if (typeof cb !== 'function') {
    cb = wd
    wd = null
  }

  while (pkg && pkg._data) pkg = pkg._data
  if (!pkg) return cb(new Error('Invalid package data'))

  log.info(stage, pkg._id)
  if (!pkg.scripts || npm.config.get('ignore-scripts')) pkg.scripts = {}

  validWd(wd || path.resolve(npm.dir, pkg.name), function (er, wd) {
    if (er) return cb(er)

    unsafe = unsafe || npm.config.get('unsafe-perm')

    if ((wd.indexOf(npm.dir) !== 0 ||
          wd.indexOf(pkg.name) !== wd.length - pkg.name.length) &&
        !unsafe && pkg.scripts[stage]) {
      log.warn(
        'cannot run in wd',
        '%s %s (wd=%s)', pkg._id, pkg.scripts[stage], wd
      )
      return cb()
    }

    // set the env variables, then run scripts as a child process.
    var env = makeEnv(pkg)
    env.npm_lifecycle_event = stage
    env.npm_node_execpath = env.NODE = env.NODE || process.execPath
    env.npm_execpath = require.main.filename

    // 'nobody' typically doesn't have permission to write to /tmp
    // even if it's never used, sh freaks out.
    if (!npm.config.get('unsafe-perm')) env.TMPDIR = wd

    lifecycle_(pkg, stage, wd, env, unsafe, failOk, cb)
  })
}

function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) {
  var pathArr = []
  var p = wd.split('node_modules')
  var acc = path.resolve(p.shift())

  p.forEach(function (pp) {
    pathArr.unshift(path.join(acc, 'node_modules', '.bin'))
    acc = path.join(acc, 'node_modules', pp)
  })
  pathArr.unshift(path.join(acc, 'node_modules', '.bin'))

  // we also unshift the bundled node-gyp-bin folder so that
  // the bundled one will be used for installing things.
  pathArr.unshift(path.join(__dirname, '..', '..', 'bin', 'node-gyp-bin'))

  if (env[PATH]) pathArr.push(env[PATH])
  env[PATH] = pathArr.join(process.platform === 'win32' ? ';' : ':')

  var packageLifecycle = pkg.scripts && pkg.scripts.hasOwnProperty(stage)

  if (packageLifecycle) {
    // define this here so it's available to all scripts.
    env.npm_lifecycle_script = pkg.scripts[stage]
  }

  function done (er) {
    if (er) {
      if (npm.config.get('force')) {
        log.info('forced, continuing', er)
        er = null
      } else if (failOk) {
        log.warn('continuing anyway', er.message)
        er = null
      }
    }
    cb(er)
  }

  chain(
    [
      packageLifecycle && [runPackageLifecycle, pkg, env, wd, unsafe],
      [runHookLifecycle, pkg, env, wd, unsafe]
    ],
    done
  )
}

function validWd (d, cb) {
  fs.stat(d, function (er, st) {
    if (er || !st.isDirectory()) {
      var p = path.dirname(d)
      if (p === d) {
        return cb(new Error('Could not find suitable wd'))
      }
      return validWd(p, cb)
    }
    return cb(null, d)
  })
}

function runPackageLifecycle (pkg, env, wd, unsafe, cb) {
  // run package lifecycle scripts in the package root, or the nearest parent.
  var stage = env.npm_lifecycle_event
  var cmd = env.npm_lifecycle_script

  var note = '\n> ' + pkg._id + ' ' + stage + ' ' + wd +
             '\n> ' + cmd + '\n'
  runCmd(note, cmd, pkg, env, stage, wd, unsafe, cb)
}

var running = false
var queue = []
function dequeue () {
  running = false
  if (queue.length) {
    var r = queue.shift()
    runCmd.apply(null, r)
  }
}

function runCmd (note, cmd, pkg, env, stage, wd, unsafe, cb) {
  if (running) {
    queue.push([note, cmd, pkg, env, stage, wd, unsafe, cb])
    return
  }

  running = true
  log.pause()
  var user = unsafe ? null : npm.config.get('user')
  var group = unsafe ? null : npm.config.get('group')

  if (log.level !== 'silent') {
    log.clearProgress()
    console.log(note)
    log.showProgress()
  }
  log.verbose('unsafe-perm in lifecycle', unsafe)

  if (process.platform === 'win32') {
    unsafe = true
  }

  if (unsafe) {
    runCmd_(cmd, pkg, env, wd, stage, unsafe, 0, 0, cb)
  } else {
    uidNumber(user, group, function (er, uid, gid) {
      runCmd_(cmd, pkg, env, wd, stage, unsafe, uid, gid, cb)
    })
  }
}

function runCmd_ (cmd, pkg, env, wd, stage, unsafe, uid, gid, cb_) {

  function cb (er) {
    cb_.apply(null, arguments)
    log.resume()
    process.nextTick(dequeue)
  }

  var conf = {
    cwd: wd,
    env: env,
    stdio: [ 0, 1, 2 ]
  }

  if (!unsafe) {
    conf.uid = uid ^ 0
    conf.gid = gid ^ 0
  }

  var sh = 'sh'
  var shFlag = '-c'

  if (process.platform === 'win32') {
    sh = process.env.comspec || 'cmd'
    shFlag = '/c'
    conf.windowsVerbatimArguments = true
  }

  var progressEnabled = log.progressEnabled
  if (progressEnabled) log.disableProgress()
  var proc = spawn(sh, [shFlag, cmd], conf)

  proc.on('error', procError)
  proc.on('close', function (code, signal) {
    if (signal) {
      process.kill(process.pid, signal)
    } else if (code) {
      var er = new Error('Exit status ' + code)
    }
    procError(er)
  })

  function procError (er) {
    if (progressEnabled) log.enableProgress()
    if (er && !npm.ROLLBACK) {
      log.info(pkg._id, 'Failed to exec ' + stage + ' script')
      er.message = pkg._id + ' ' + stage + ': `' + cmd + '`\n' +
                   er.message
      if (er.code !== 'EPERM') {
        er.code = 'ELIFECYCLE'
      }
      er.pkgid = pkg._id
      er.stage = stage
      er.script = cmd
      er.pkgname = pkg.name
      return cb(er)
    } else if (er) {
      log.error(pkg._id + '.' + stage, er)
      log.error(pkg._id + '.' + stage, 'continuing anyway')
      return cb()
    }
    cb(er)
  }
}

function runHookLifecycle (pkg, env, wd, unsafe, cb) {
  // check for a hook script, run if present.
  var stage = env.npm_lifecycle_event
  var hook = path.join(npm.dir, '.hooks', stage)
  var cmd = hook

  fs.stat(hook, function (er) {
    if (er) return cb()
    var note = '\n> ' + pkg._id + ' ' + stage + ' ' + wd +
               '\n> ' + cmd
    runCmd(note, hook, pkg, env, stage, wd, unsafe, cb)
  })
}

function makeEnv (data, prefix, env) {
  prefix = prefix || 'npm_package_'
  if (!env) {
    env = {}
    for (var i in process.env) if (!i.match(/^npm_/)) {
      env[i] = process.env[i]
    }

    // npat asks for tap output
    if (npm.config.get('npat')) env.TAP = 1

    // express and others respect the NODE_ENV value.
    if (npm.config.get('production')) env.NODE_ENV = 'production'

  } else if (!data.hasOwnProperty('_lifecycleEnv')) {
    Object.defineProperty(data, '_lifecycleEnv',
      {
        value: env,
        enumerable: false
      }
    )
  }

  for (i in data) if (i.charAt(0) !== '_') {
    var envKey = (prefix + i).replace(/[^a-zA-Z0-9_]/g, '_')
    if (i === 'readme') {
      continue
    }
    if (data[i] && typeof data[i] === 'object') {
      try {
        // quick and dirty detection for cyclical structures
        JSON.stringify(data[i])
        makeEnv(data[i], envKey + '_', env)
      } catch (ex) {
        // usually these are package objects.
        // just get the path and basic details.
        var d = data[i]
        makeEnv(
          { name: d.name, version: d.version, path: d.path },
          envKey + '_',
          env
        )
      }
    } else {
      env[envKey] = String(data[i])
      env[envKey] = env[envKey].indexOf('\n') !== -1 ?
                      JSON.stringify(env[envKey]) :
                      env[envKey]
    }

  }

  if (prefix !== 'npm_package_') return env

  prefix = 'npm_config_'
  var pkgConfig = {}
  var keys = npm.config.keys
  var pkgVerConfig = {}
  var namePref = data.name + ':'
  var verPref = data.name + '@' + data.version + ':'

  keys.forEach(function (i) {
    if (i.charAt(0) === '_' && i.indexOf('_' + namePref) !== 0) {
      return
    }
    var value = npm.config.get(i)
    if (value instanceof Stream || Array.isArray(value)) return
    if (i.match(/umask/)) value = umask.toString(value)
    if (!value) value = ''
    else if (typeof value === 'number') value = '' + value
    else if (typeof value !== 'string') value = JSON.stringify(value)

    value = value.indexOf('\n') !== -1
          ? JSON.stringify(value)
          : value
    i = i.replace(/^_+/, '')
    var k
    if (i.indexOf(namePref) === 0) {
      k = i.substr(namePref.length).replace(/[^a-zA-Z0-9_]/g, '_')
      pkgConfig[k] = value
    } else if (i.indexOf(verPref) === 0) {
      k = i.substr(verPref.length).replace(/[^a-zA-Z0-9_]/g, '_')
      pkgVerConfig[k] = value
    }
    var envKey = (prefix + i).replace(/[^a-zA-Z0-9_]/g, '_')
    env[envKey] = value
  })

  prefix = 'npm_package_config_'
  ;[pkgConfig, pkgVerConfig].forEach(function (conf) {
    for (var i in conf) {
      var envKey = (prefix + i)
      env[envKey] = conf[i]
    }
  })

  return env
}

function cmd (stage) {
  function CMD (args, cb) {
    npm.commands['run-script']([stage].concat(args), cb)
  }
  CMD.usage = 'npm ' + stage + ' [-- <args>]'
  var installedShallow = require('./completion/installed-shallow.js')
  CMD.completion = function (opts, cb) {
    installedShallow(opts, function (d) {
      return d.scripts && d.scripts[stage]
    }, cb)
  }
  return CMD
}
<|endoftext|>


<commit_before>var async = require('async');
var path = require('path');
var temp = require('temp');
var child = require('child_process');
var which = require('which');
var fs = require('fs');
var mkdirp = require('mkdirp');
var extend = require('extend');

function compile(name, args) {
  var options = extend({
    cwd: process.cwd()
  }, arguments[2] === undefined ? {} : arguments[2]);

  args = args.slice();

  var dirpath = temp.mkdirSync('scripts');
  var pathname = args[0].replace(/\.[^\.]+$/, '.js');

  var filename = path.join(dirpath, pathname);
  var dirname = path.dirname(filename);
  mkdirp.sync(dirname);

  var scripts = {};
  scripts[filename] = pathname;

  switch (name) {
    case 'browserify':
      command = which.sync('watchify');
      args.unshift(
        '--outfile',
        filename
      );
      break;

    case 'webpack':
      command = which.sync('webpack');
      args.unshift(
        '--watch',
        '--output-file',
        filename
      );
      break;

    case 'tsc':
      command = which.sync('tsc');
      args.unshift(
        '--watch',
        '--out',
        filename
      );
      break;

    case 'coffee':
      command = which.sync('coffee');
      args.unshift(
        '--watch',
        '--compile',
        '--join',
        filename
      );
      break;

    case 'babel':
      command = which.sync('babel');
      args.unshift(
        '--watch',
        '--out-file',
        filename
      );
      break;
  }

  var compiler = child.spawn(command, args, options);
  var filenames = Object.keys(scripts);
  async.until(function() {
    return filenames.every(function(filename) {
      return fs.existsSync(filename) && fs.statSync(filename).size > 1;
    });
  }, function(callback) {
    setTimeout(callback, 100);
  }, function() {
    compiler.emit('ready', scripts);
  });

  return compiler;
}

module.exports = compile;
<commit_msg>Remove duplication of compile command assignment<commit_after>var async = require('async');
var path = require('path');
var temp = require('temp');
var child = require('child_process');
var which = require('which');
var fs = require('fs');
var mkdirp = require('mkdirp');
var extend = require('extend');

function compile(name, args) {
  var options = extend({
    cwd: process.cwd()
  }, arguments[2] === undefined ? {} : arguments[2]);

  args = args.slice();

  var dirpath = temp.mkdirSync('scripts');
  var pathname = args[0].replace(/\.[^\.]+$/, '.js');

  var filename = path.join(dirpath, pathname);
  var dirname = path.dirname(filename);
  mkdirp.sync(dirname);

  var scripts = {};
  scripts[filename] = pathname;

  var command = (function() {
    return (name === 'browserify' ? which.sync('watchify') : which.sync(name));
  }());

  switch (name) {
    case 'browserify':
      args.unshift(
        '--outfile',
        filename
      );
      break;

    case 'webpack':
      args.unshift(
        '--watch',
        '--output-file',
        filename
      );
      break;

    case 'tsc':
      args.unshift(
        '--watch',
        '--out',
        filename
      );
      break;

    case 'coffee':
      args.unshift(
        '--watch',
        '--compile',
        '--join',
        filename
      );
      break;

    case 'babel':
      args.unshift(
        '--watch',
        '--out-file',
        filename
      );
      break;
  }

  var compiler = child.spawn(command, args, options);
  var filenames = Object.keys(scripts);
  async.until(function() {
    return filenames.every(function(filename) {
      return fs.existsSync(filename) && fs.statSync(filename).size > 1;
    });
  }, function(callback) {
    setTimeout(callback, 100);
  }, function() {
    compiler.emit('ready', scripts);
  });

  return compiler;
}

module.exports = compile;
<|endoftext|>


<commit_before>'use strict';
/**
 * @file Upload all contacts from all accounts modified since last call to upload.
 *
 */

var retrieve = require('../helpers/retrieve.js');

/**
 * Build a uuid for each DB file.
 *
 * @param {int} uid User ID (available on the tokens)
 * @param {path} path File path
 */
var _identifier = function(uid, path) {
  return 'https://dropbox.com/' + uid + path;
};

var uploadFile = function(identifier, metadatas, dropboxTokens, cluestrClient, cb) {
  console.log("UPPING", identifier);

  var document = {
    identifier: identifier,
    actions: {
      'show': 'https://www.dropbox.com/home' + encodeURIComponent(metadatas.path)
    },
    metadatas: {
      filename: metadatas.path.substr(metadatas.path.lastIndexOf('/') + 1),
      path: metadatas.path
    },
    binary_document_type: "file",
    user_access: [cluestrClient.accessToken]
  };

  // Stream the file from DB servers
  retrieve.getFile(dropboxTokens, metadatas.path, function(status, reply) {
    if(status !== 200) {
      throw new Error([identifier, status, reply]);
    }

    if(!reply) {
      // Empty file
      cb();
    }
    else {
      // File to send
      var fileConfig = {
        file: reply,
        filename: document.metadatas.filename,
        knownLength: reply.length
      };

      // Let's roll.
      cluestrClient.sendDocumentAndFile(document, fileConfig, cb);
    }

  });
};

var deleteFile = function(identifier, cluestrClient, cb) {
  console.log("DELING", identifier);
  cluestrClient.deleteDocument(identifier, cb);
};

/**
 * Run the task of uploading a document to Cluestr.
 * This function will be used as a queue
 * @see https://github.com/caolan/async#queueworker-concurrency
 *
 * @param {Object} task Task param (keys: `dropboxTokens`, `path`, `cluestrClient`)
 * @param {Function} cb Callback once task has been processed.
 */
module.exports = function(task, cluestrClient, dropboxTokens, cb) {
  var throwCb = function(err) {
    if(err) {
      throw err;
    }
    cb();
  };

  var path = task[0];
  var metadatas = task[1];

  var identifier = _identifier(dropboxTokens.uid, path);
  if(!metadatas) {
    // File has been removed
    return deleteFile(identifier, cluestrClient, throwCb);
  }
  else {
    // Upload file onto Cluestr
    return uploadFile(identifier, metadatas, dropboxTokens, cluestrClient, throwCb);
  }
};
<commit_msg>Should work everywhere -- error handling<commit_after>'use strict';
/**
 * @file Upload all contacts from all accounts modified since last call to upload.
 *
 */

var retrieve = require('../helpers/retrieve.js');

/**
 * Build a uuid for each DB file.
 *
 * @param {int} uid User ID (available on the tokens)
 * @param {path} path File path
 */
var _identifier = function(uid, path) {
  return 'https://dropbox.com/' + uid + path;
};

var uploadFile = function(identifier, metadatas, dropboxTokens, cluestrClient, cb) {
  console.log("UPPING", identifier);

  var document = {
    identifier: identifier,
    actions: {
      'show': 'https://www.dropbox.com/home' + encodeURIComponent(metadatas.path)
    },
    metadatas: {
      filename: metadatas.path.substr(metadatas.path.lastIndexOf('/') + 1),
      path: metadatas.path
    },
    binary_document_type: "file",
    user_access: [cluestrClient.accessToken]
  };

  // Stream the file from DB servers
  retrieve.getFile(dropboxTokens, metadatas.path, function(status, reply) {
    if(status !== 200 || !reply) {
      console.log("Failure to retrieve datas or empty file: ", [identifier, status, reply])
      return cb();
    }

    // File to send
    var fileConfig = {
      file: reply,
      filename: document.metadatas.filename,
      knownLength: reply.length
    };

    // Let's roll.
    cluestrClient.sendDocumentAndFile(document, fileConfig, cb);
  });
};

var deleteFile = function(identifier, cluestrClient, cb) {
  console.log("DELING", identifier);
  cluestrClient.deleteDocument(identifier, cb);
};

/**
 * Run the task of uploading a document to Cluestr.
 * This function will be used as a queue
 * @see https://github.com/caolan/async#queueworker-concurrency
 *
 * @param {Object} task Task param (keys: `dropboxTokens`, `path`, `cluestrClient`)
 * @param {Function} cb Callback once task has been processed.
 */
module.exports = function(task, cluestrClient, dropboxTokens, cb) {
  var throwCb = function(err) {
    if(err) {
      throw err;
    }
    cb();
  };

  var path = task[0];
  var metadatas = task[1];

  var identifier = _identifier(dropboxTokens.uid, path);
  if(!metadatas) {
    // File has been removed
    return deleteFile(identifier, cluestrClient, throwCb);
  }
  else {
    // Upload file onto Cluestr
    return uploadFile(identifier, metadatas, dropboxTokens, cluestrClient, throwCb);
  }
};
<|endoftext|>


<commit_before>/* eslint-env node, mocha */
import { expect } from 'chai'
import fetchMock from 'lets-fetch/mock'
import dyeCategories from '../../src/gw2wiki/dyeCategories.js'

dyeCategories.__set__('fetch', fetchMock)

describe('gw2wiki > dyeCategories', function () {
  this.timeout(20000)
  beforeEach(() => {
    fetchMock.enableMocking(false)
    fetchMock.reset()
  })

  it('gets the correct dye categories {LIVE}', async () => {
    let categories = await dyeCategories()
    let keys = Object.keys(categories)

    expect(keys).to.deep.equal(['materials', 'sets', 'colors', 'birthdays'])

    let materials = categories.materials
    expect(Object.keys(materials)).to.contain('Celestial Dye')
    expect(Object.values(materials)).to.contain('Vibrant dyes')
    expect(materials['Abyss Dye']).to.equal('Natural metallic dyes')

    let sets = categories.sets
    expect(Object.keys(sets)).to.contain('Celestial Dye')
    expect(Object.values(sets)).to.contain('Starter dyes')
    expect(sets['Abyss Dye']).to.equal('Rare dyes')
    expect(sets['Flame Dye']).to.equal('Flame Dye Kit')
    expect(sets['Imperial Red Dye']).to.equal('Crimson Lion Dye Kit')

    let colors = categories.colors
    expect(Object.keys(colors)).to.contain('Celestial Dye')
    expect(Object.values(colors)).to.contain('Blue dyes')
    expect(colors['Abyss Dye']).to.equal('Gray dyes')

    let birthdays = categories.birthdays
    expect(Object.keys(birthdays)).to.contain('Deep Glacial Teal Dye')
    expect(birthdays['Electro Pink Dye']).to.deep.equal([
      'Celebratory Dye Pack',
      'Jubilant Dye Pack'
    ])
    expect(birthdays['Enameled Strawberry Dye']).to.deep.equal([])
  })
})
<commit_msg>Enameled strawberry is part of a set now<commit_after>/* eslint-env node, mocha */
import { expect } from 'chai'
import fetchMock from 'lets-fetch/mock'
import dyeCategories from '../../src/gw2wiki/dyeCategories.js'

dyeCategories.__set__('fetch', fetchMock)

describe('gw2wiki > dyeCategories', function () {
  this.timeout(20000)
  beforeEach(() => {
    fetchMock.enableMocking(false)
    fetchMock.reset()
  })

  it('gets the correct dye categories {LIVE}', async () => {
    let categories = await dyeCategories()
    let keys = Object.keys(categories)

    expect(keys).to.deep.equal(['materials', 'sets', 'colors', 'birthdays'])

    let materials = categories.materials
    expect(Object.keys(materials)).to.contain('Celestial Dye')
    expect(Object.values(materials)).to.contain('Vibrant dyes')
    expect(materials['Abyss Dye']).to.equal('Natural metallic dyes')

    let sets = categories.sets
    expect(Object.keys(sets)).to.contain('Celestial Dye')
    expect(Object.values(sets)).to.contain('Starter dyes')
    expect(sets['Abyss Dye']).to.equal('Rare dyes')
    expect(sets['Flame Dye']).to.equal('Flame Dye Kit')
    expect(sets['Imperial Red Dye']).to.equal('Crimson Lion Dye Kit')

    let colors = categories.colors
    expect(Object.keys(colors)).to.contain('Celestial Dye')
    expect(Object.values(colors)).to.contain('Blue dyes')
    expect(colors['Abyss Dye']).to.equal('Gray dyes')

    let birthdays = categories.birthdays
    expect(Object.keys(birthdays)).to.contain('Deep Glacial Teal Dye')
    expect(birthdays['Electro Pink Dye']).to.deep.equal([
      'Celebratory Dye Pack',
      'Jubilant Dye Pack'
    ])
    expect(birthdays['Enameled Strawberry Dye']).to.deep.equal(['Dedicated Dye Kit'])
  })
})
<|endoftext|>


<commit_before>var inlineRequire = require('../lib/inline-require.js');
var serialize = require('../lib/serialize.js');
var nearley = require('../lib/nearley.js');

function Compile(structure, opts) {
	var un = 0;
	function unique(name) {
		return name + '$' + (++un);
	}

    function joiner(d) {
        return d.join('');
    }

	var outputRules = [];
    var body = [];

	function buildProcessedRule(name, rule) {
		var tokenList = [];

		rule.tokens.forEach(function(token) {
			if (token.type && token.type === 'literal') {
				var str = token.data;
				if (str.length > 1) {
					var rules = str.split("").map(function(d) {
						return { literal: d };
					});

					var newname = unique(name);
					buildProcessedRule(newname, {tokens: rules, postprocessor: joiner});
					tokenList.push(newname);
				} else if (str.length === 1) {
					tokenList.push({ literal: str });
				}
			} else if (typeof(token) === 'string') {
				if (token !== 'null') tokenList.push(token);
            } else if (token instanceof RegExp) {
                tokenList.push(token);
			} else {
                throw new Error("Should never get here");
            }
		});

		var out = nearley.rule(name, tokenList, rule.postprocessor);

		outputRules.push(out);
	}

	var firstName;

	structure.forEach(function(productionRule) {
		if (productionRule.body) {
			body.push(productionRule.body);
		} else {
			var rules = productionRule.rules;
			if (!firstName) firstName = productionRule.name;
			rules.forEach(function(rule) {
				buildProcessedRule(productionRule.name, rule);
			});
		}
	});

	var output = "";
	var ws = "\n    ";

	output += "// Generated automatically by nearley.\n";
	output += opts.export + " = function() {";

	output += ws + "var nearley = " + inlineRequire(require, '../lib/nearley.js');
	output += ws + "var id = function(a){return a[0];};";
	output += ws;
    body.forEach(function (stmt) {
        output += ws + stmt;
    });
    output += ws + "var rules = " + serialize(outputRules);
	output += ws;
	output += ws + "return new nearley.Parser(rules, " + JSON.stringify(firstName) + ");";

	output += "\n};";

	return output;
}

module.exports = Compile;
<commit_msg>Use output helpers to make the emitter robust to change<commit_after>var inlineRequire = require('../lib/inline-require.js');
var serialize = require('../lib/serialize.js');
var nearley = require('../lib/nearley.js');

function Compile(structure, opts) {
	var un = 0;
	function unique(name) {
		return name + '$' + (++un);
	}

    function joiner(d) {
        return d.join('');
    }

    function id(d) {
        return d[0];
    }

	var outputRules = [];
    var body = [];

	function buildProcessedRule(name, rule) {
		var tokenList = [];

		rule.tokens.forEach(function(token) {
			if (token.type && token.type === 'literal') {
				var str = token.data;
				if (str.length > 1) {
					var rules = str.split("").map(function(d) {
						return { literal: d };
					});

					var newname = unique(name);
					buildProcessedRule(newname, {tokens: rules, postprocessor: joiner});
					tokenList.push(newname);
				} else if (str.length === 1) {
					tokenList.push({ literal: str });
				}
			} else if (typeof(token) === 'string') {
				if (token !== 'null') tokenList.push(token);
            } else if (token instanceof RegExp) {
                tokenList.push(token);
			} else {
                throw new Error("Should never get here");
            }
		});

		var out = nearley.rule(name, tokenList, rule.postprocessor);

		outputRules.push(out);
	}

	var firstName;

	structure.forEach(function(productionRule) {
		if (productionRule.body) {
			body.push(productionRule.body);
		} else {
			var rules = productionRule.rules;
			if (!firstName) firstName = productionRule.name;
			rules.forEach(function(rule) {
				buildProcessedRule(productionRule.name, rule);
			});
		}
	});

    function variable(v, value) {
        return "var " + assign(v, value);
    }

    function assign(v, value) {
        return v + " = " + value + ";\n";
    }

    function comment(value) {
        return "// " + value + "\n";
    }

    function wrap(body) {
        return "function () {\n" + indent(body) + "}";
    }

    function indent(body) {
        return body.replace(/^/mg, '    ');
    }

    function retn(stmt) {
        return "return " + stmt;
    }

    function call(fn, params) {
        return fn + "(" + params.join(', ') + ");";
    }

	var output = "";

	output += comment("Generated automatically by nearley.");
	output += assign(opts.export, wrap(
        variable('nearley', inlineRequire(require, '../lib/nearley.js')) +
        variable('id', serialize(id)) +
        body.join('\n') +
        "\n" +
        retn(call("new nearley.Parser", [serialize(outputRules), JSON.stringify(firstName)]))
    ));

	return output;
}

module.exports = Compile;
<|endoftext|>


<commit_before>(function () {
if (typeof(window) === 'undefined') {
    var nearley = require('../lib/nearley.js');
} else {
    var nearley = window.nearley;
}

function Compile(structure, opts) {
    var uns = {};

    function unique(name) {
        var un = uns[name] = (uns[name] || 0) + 1;
        return name + '$' + un;
    }

    var outputRules = [];
    var body = []; // @directives list

    function buildProcessedRule(ruleName, rule) {
        var tokenList = [];

        rule.tokens.forEach(function(token) {
            if (token.literal) {
                var str = token.literal;
                if (str.length > 1) {
                    var rules = str.split("").map(function(d) {
                        return {
                            literal: d
                        };
                    });

                    var newname = unique(ruleName + "$string");
                    buildProcessedRule(newname, {
                        tokens: rules,
                        postprocess: joiner
                    });
                    tokenList.push(newname);
                } else if (str.length === 1) {
                    tokenList.push({
                        literal: str
                    });
                }
            } else if (token.subexpression) {
                var data = token.subexpression;
                var name = unique(ruleName + "$subexpression");
                structure.push({"name": name, "rules": data});
                tokenList.push(name);
            } else if (token.ebnf) {
                var data = token.ebnf;
                var modifier = token.modifier;
                var name = unique(ruleName + "$ebnf");

                switch (modifier) {
                    case ":+":
                        structure.push({
                            "name": name,
                            "rules": [{
                                "tokens": [data],
                            }, {
                                "tokens": [data, name],
                                "postprocess": arrconcat
                            }]
                        });
                        tokenList.push(name);
                        break;
                    case ":*":
                        structure.push({
                            "name": name,
                            "rules": [{
                                "tokens": [],
                            }, {
                                "tokens": [data, name],
                                "postprocess": arrconcat
                            }]
                        });
                        tokenList.push(name);
                        break;
                    case ":?":
                        structure.push({
                            "name": name,
                            "rules": [{
                                "tokens": [data],
                                "postprocess": "id"
                            }, {
                                "tokens": [],
                                "postprocess": "function(d) {return null;}"
                            }]
                        });
                        tokenList.push(name);
                        break;
                }
            } else if (typeof(token) === 'string') {
                if (token !== 'null') tokenList.push(token);
            } else if (token instanceof RegExp) {
                tokenList.push(token);
            } else {
                throw new Error("Should never get here");
            }
        });

        var out = new nearley.Rule(
            ruleName,
            tokenList,
            (opts.nojs ? null : rule.postprocess)
        );

        outputRules.push(out);
    }

    var firstName;

    for (var i = 0; i < structure.length; i++) {
        var productionRule = structure[i];
        if (productionRule.body) {
            // This isn't a rule, it's an @directive.
            if (!opts.nojs) body.push(productionRule.body);
        } else {
            var rules = productionRule.rules;
            if (!firstName) firstName = productionRule.name;
            rules.forEach(function(rule) {
                buildProcessedRule(productionRule.name, rule);
            });
        }
    }

    return {
        rules: outputRules,
        body: body,
        start: firstName
    };
}

function arrconcat(d) {
    return [d[0]].concat(d[1]);
}

function joiner(d) {
    return d.join('');
}

if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
    module.exports = Compile;
} else {
    window.Compile = Compile;
}
})();
<commit_msg>lib/compile: factor out uniquer<commit_after>(function () {
if (typeof(window) === 'undefined') {
    var nearley = require('../lib/nearley.js');
} else {
    var nearley = window.nearley;
}

function Compile(structure, opts) {
    var unique = uniquer();

    var outputRules = [];
    var body = []; // @directives list

    function buildProcessedRule(ruleName, rule) {
        var tokenList = [];

        rule.tokens.forEach(function(token) {
            if (token.literal) {
                var str = token.literal;
                if (str.length > 1) {
                    var rules = str.split("").map(function(d) {
                        return {
                            literal: d
                        };
                    });

                    var newname = unique(ruleName + "$string");
                    buildProcessedRule(newname, {
                        tokens: rules,
                        postprocess: joiner
                    });
                    tokenList.push(newname);
                } else if (str.length === 1) {
                    tokenList.push({
                        literal: str
                    });
                }
            } else if (token.subexpression) {
                var data = token.subexpression;
                var name = unique(ruleName + "$subexpression");
                structure.push({"name": name, "rules": data});
                tokenList.push(name);
            } else if (token.ebnf) {
                var data = token.ebnf;
                var modifier = token.modifier;
                var name = unique(ruleName + "$ebnf");

                switch (modifier) {
                    case ":+":
                        structure.push({
                            "name": name,
                            "rules": [{
                                "tokens": [data],
                            }, {
                                "tokens": [data, name],
                                "postprocess": arrconcat
                            }]
                        });
                        tokenList.push(name);
                        break;
                    case ":*":
                        structure.push({
                            "name": name,
                            "rules": [{
                                "tokens": [],
                            }, {
                                "tokens": [data, name],
                                "postprocess": arrconcat
                            }]
                        });
                        tokenList.push(name);
                        break;
                    case ":?":
                        structure.push({
                            "name": name,
                            "rules": [{
                                "tokens": [data],
                                "postprocess": "id"
                            }, {
                                "tokens": [],
                                "postprocess": "function(d) {return null;}"
                            }]
                        });
                        tokenList.push(name);
                        break;
                }
            } else if (typeof(token) === 'string') {
                if (token !== 'null') tokenList.push(token);
            } else if (token instanceof RegExp) {
                tokenList.push(token);
            } else {
                throw new Error("Should never get here");
            }
        });

        var out = new nearley.Rule(
            ruleName,
            tokenList,
            (opts.nojs ? null : rule.postprocess)
        );

        outputRules.push(out);
    }

    var firstName;

    for (var i = 0; i < structure.length; i++) {
        var productionRule = structure[i];
        if (productionRule.body) {
            // This isn't a rule, it's an @directive.
            if (!opts.nojs) body.push(productionRule.body);
        } else {
            var rules = productionRule.rules;
            if (!firstName) firstName = productionRule.name;
            rules.forEach(function(rule) {
                buildProcessedRule(productionRule.name, rule);
            });
        }
    }

    return {
        rules: outputRules,
        body: body,
        start: firstName
    };
}

function uniquer() {
    var uns = {};
    return unique;
    function unique(name) {
        var un = uns[name] = (uns[name] || 0) + 1;
        return name + '$' + un;
    }
}

function arrconcat(d) {
    return [d[0]].concat(d[1]);
}

function joiner(d) {
    return d.join('');
}

if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
    module.exports = Compile;
} else {
    window.Compile = Compile;
}
})();
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from "../../../Modal";
import RateLimitedFunc from '../../../ratelimitedfunc';

import { linkifyElement } from '../../../HtmlUtils';
import AccessibleButton from '../elements/AccessibleButton';
import ManageIntegsButton from '../elements/ManageIntegsButton';
import {CancelButton} from './SimpleRoomHeader';
import SettingsStore from "../../../settings/SettingsStore";
import RoomHeaderButtons from '../right_panel/RoomHeaderButtons';
import E2EIcon from './E2EIcon';

module.exports = createReactClass({
    displayName: 'RoomHeader',

    propTypes: {
        room: PropTypes.object,
        oobData: PropTypes.object,
        inRoom: PropTypes.bool,
        onSettingsClick: PropTypes.func,
        onPinnedClick: PropTypes.func,
        onSearchClick: PropTypes.func,
        onLeaveClick: PropTypes.func,
        onCancelClick: PropTypes.func,
        e2eStatus: PropTypes.string,
    },

    getDefaultProps: function() {
        return {
            editing: false,
            inRoom: false,
            onCancelClick: null,
        };
    },

    UNSAFE_componentWillMount: function() {
        this._topic = createRef();
    },

    componentDidMount: function() {
        const cli = MatrixClientPeg.get();
        cli.on("RoomState.events", this._onRoomStateEvents);
        cli.on("Room.accountData", this._onRoomAccountData);

        // When a room name occurs, RoomState.events is fired *before*
        // room.name is updated. So we have to listen to Room.name as well as
        // RoomState.events.
        if (this.props.room) {
            this.props.room.on("Room.name", this._onRoomNameChange);
        }
    },

    componentDidUpdate: function() {
        if (this._topic.current) {
            linkifyElement(this._topic.current);
        }
    },

    componentWillUnmount: function() {
        if (this.props.room) {
            this.props.room.removeListener("Room.name", this._onRoomNameChange);
        }
        const cli = MatrixClientPeg.get();
        if (cli) {
            cli.removeListener("RoomState.events", this._onRoomStateEvents);
            cli.removeListener("Room.accountData", this._onRoomAccountData);
        }
    },

    _onRoomStateEvents: function(event, state) {
        if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
            return;
        }

        // redisplay the room name, topic, etc.
        this._rateLimitedUpdate();
    },

    _onRoomAccountData: function(event, room) {
        if (!this.props.room || room.roomId !== this.props.room.roomId) return;
        if (event.getType() !== "im.vector.room.read_pins") return;

        this._rateLimitedUpdate();
    },

    _rateLimitedUpdate: new RateLimitedFunc(function() {
        /* eslint-disable babel/no-invalid-this */
        this.forceUpdate();
    }, 500),

    _onRoomNameChange: function(room) {
        this.forceUpdate();
    },

    onShareRoomClick: function(ev) {
        const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
        Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
            target: this.props.room,
        });
    },

    _hasUnreadPins: function() {
        const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
        if (!currentPinEvent) return false;
        if (currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0) {
            return false; // no pins == nothing to read
        }

        const readPinsEvent = this.props.room.getAccountData("im.vector.room.read_pins");
        if (readPinsEvent && readPinsEvent.getContent()) {
            const readStateEvents = readPinsEvent.getContent().event_ids || [];
            if (readStateEvents) {
                return !readStateEvents.includes(currentPinEvent.getId());
            }
        }

        // There's pins, and we haven't read any of them
        return true;
    },

    _hasPins: function() {
        const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
        if (!currentPinEvent) return false;

        return !(currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0);
    },

    render: function() {
        const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");

        let searchStatus = null;
        let cancelButton = null;
        let settingsButton = null;
        let pinnedEventsButton = null;

        const e2eIcon = this.props.e2eStatus ?
            <E2EIcon status={this.props.e2eStatus} /> :
            undefined;

        const joinRules = this.props.room && this.props.room.currentState.getStateEvents("m.room.join_rules", "");
        const joinRule = joinRules && joinRules.getContent().join_rule;
        const joinRuleClass = classNames("mx_RoomHeader_PrivateIcon",
                                         {"mx_RoomHeader_isPrivate": joinRule === "invite"});

        if (this.props.onCancelClick) {
            cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
        }

        // don't display the search count until the search completes and
        // gives us a valid (possibly zero) searchCount.
        if (this.props.searchInfo &&
            this.props.searchInfo.searchCount !== undefined &&
            this.props.searchInfo.searchCount !== null) {
            searchStatus =

                { _t("(~%(count)s results)", { count: this.props.searchInfo.searchCount }) }

;
        }

        // XXX: this is a bit inefficient - we could just compare room.name for 'Empty room'...
        let settingsHint = false;
        const members = this.props.room ? this.props.room.getJoinedMembers() : undefined;
        if (members) {
            if (members.length === 1 && members[0].userId === MatrixClientPeg.get().credentials.userId) {
                const nameEvent = this.props.room.currentState.getStateEvents('m.room.name', '');
                if (!nameEvent || !nameEvent.getContent().name) {
                    settingsHint = true;
                }
            }
        }

        let roomName = _t("Join Room");
        if (this.props.oobData && this.props.oobData.name) {
            roomName = this.props.oobData.name;
        } else if (this.props.room) {
            roomName = this.props.room.name;
        }

        const textClasses = classNames('mx_RoomHeader_nametext', { mx_RoomHeader_settingsHint: settingsHint });
        const name =

{ roomName }

                { searchStatus }

;

        let topic;
        if (this.props.room) {
            const ev = this.props.room.currentState.getStateEvents('m.room.topic', '');
            if (ev) {
                topic = ev.getContent().topic;
            }
        }
        const topicElement =

{ topic }
;
        const avatarSize = 28;
        let roomAvatar;
        if (this.props.room) {
            roomAvatar = (<RoomAvatar
                room={this.props.room}
                width={avatarSize}
                height={avatarSize}
                oobData={this.props.oobData}
                viewAvatarOnClick={true} />);
        }

        if (this.props.onSettingsClick) {
            settingsButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_settingsButton"
                    onClick={this.props.onSettingsClick}
                    title={_t("Settings")}
                >
                </AccessibleButton>;
        }

        if (this.props.onPinnedClick && SettingsStore.isFeatureEnabled('feature_pinning')) {
            let pinsIndicator = null;
            if (this._hasUnreadPins()) {
                pinsIndicator = (
);
            } else if (this._hasPins()) {
                pinsIndicator = (
);
            }

            pinnedEventsButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_pinnedButton"
                                  onClick={this.props.onPinnedClick} title={_t("Pinned Messages")}>
                    { pinsIndicator }
                </AccessibleButton>;
        }

//        var leave_button;
//        if (this.props.onLeaveClick) {
//            leave_button =
//

//                    <TintableSvg src={require("../../../../res/img/leave.svg")} width="26" height="20"/>
//
;
//        }

        let forgetButton;
        if (this.props.onForgetClick) {
            forgetButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_forgetButton"
                    onClick={this.props.onForgetClick}
                    title={_t("Forget room")}
                >
                </AccessibleButton>;
        }

        let searchButton;
        if (this.props.onSearchClick && this.props.inRoom) {
            searchButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_searchButton"
                    onClick={this.props.onSearchClick}
                    title={_t("Search")}
                >
                </AccessibleButton>;
        }

        let shareRoomButton;
        if (this.props.inRoom) {
            shareRoomButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_shareButton"
                    onClick={this.onShareRoomClick}
                    title={_t('Share room')}
                >
                </AccessibleButton>;
        }

        let manageIntegsButton;
        if (this.props.room && this.props.room.roomId && this.props.inRoom) {
            manageIntegsButton = <ManageIntegsButton
                room={this.props.room}
            />;
        }

        const rightRow =

                { settingsButton }
                { pinnedEventsButton }
                { shareRoomButton }
                { manageIntegsButton }
                { forgetButton }
                { searchButton }

;

        return (

{ roomAvatar }

                    { e2eIcon }

                    { name }
                    { topicElement }
                    { cancelButton }
                    { rightRow }
                    <RoomHeaderButtons />

        );
    },
});
<commit_msg>Place RoomHeader privacy padlocks behind the crosssigning feature flag<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from "../../../Modal";
import RateLimitedFunc from '../../../ratelimitedfunc';

import { linkifyElement } from '../../../HtmlUtils';
import AccessibleButton from '../elements/AccessibleButton';
import ManageIntegsButton from '../elements/ManageIntegsButton';
import {CancelButton} from './SimpleRoomHeader';
import SettingsStore from "../../../settings/SettingsStore";
import RoomHeaderButtons from '../right_panel/RoomHeaderButtons';
import E2EIcon from './E2EIcon';

module.exports = createReactClass({
    displayName: 'RoomHeader',

    propTypes: {
        room: PropTypes.object,
        oobData: PropTypes.object,
        inRoom: PropTypes.bool,
        onSettingsClick: PropTypes.func,
        onPinnedClick: PropTypes.func,
        onSearchClick: PropTypes.func,
        onLeaveClick: PropTypes.func,
        onCancelClick: PropTypes.func,
        e2eStatus: PropTypes.string,
    },

    getDefaultProps: function() {
        return {
            editing: false,
            inRoom: false,
            onCancelClick: null,
        };
    },

    UNSAFE_componentWillMount: function() {
        this._topic = createRef();
    },

    componentDidMount: function() {
        const cli = MatrixClientPeg.get();
        cli.on("RoomState.events", this._onRoomStateEvents);
        cli.on("Room.accountData", this._onRoomAccountData);

        // When a room name occurs, RoomState.events is fired *before*
        // room.name is updated. So we have to listen to Room.name as well as
        // RoomState.events.
        if (this.props.room) {
            this.props.room.on("Room.name", this._onRoomNameChange);
        }
    },

    componentDidUpdate: function() {
        if (this._topic.current) {
            linkifyElement(this._topic.current);
        }
    },

    componentWillUnmount: function() {
        if (this.props.room) {
            this.props.room.removeListener("Room.name", this._onRoomNameChange);
        }
        const cli = MatrixClientPeg.get();
        if (cli) {
            cli.removeListener("RoomState.events", this._onRoomStateEvents);
            cli.removeListener("Room.accountData", this._onRoomAccountData);
        }
    },

    _onRoomStateEvents: function(event, state) {
        if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
            return;
        }

        // redisplay the room name, topic, etc.
        this._rateLimitedUpdate();
    },

    _onRoomAccountData: function(event, room) {
        if (!this.props.room || room.roomId !== this.props.room.roomId) return;
        if (event.getType() !== "im.vector.room.read_pins") return;

        this._rateLimitedUpdate();
    },

    _rateLimitedUpdate: new RateLimitedFunc(function() {
        /* eslint-disable babel/no-invalid-this */
        this.forceUpdate();
    }, 500),

    _onRoomNameChange: function(room) {
        this.forceUpdate();
    },

    onShareRoomClick: function(ev) {
        const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
        Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
            target: this.props.room,
        });
    },

    _hasUnreadPins: function() {
        const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
        if (!currentPinEvent) return false;
        if (currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0) {
            return false; // no pins == nothing to read
        }

        const readPinsEvent = this.props.room.getAccountData("im.vector.room.read_pins");
        if (readPinsEvent && readPinsEvent.getContent()) {
            const readStateEvents = readPinsEvent.getContent().event_ids || [];
            if (readStateEvents) {
                return !readStateEvents.includes(currentPinEvent.getId());
            }
        }

        // There's pins, and we haven't read any of them
        return true;
    },

    _hasPins: function() {
        const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
        if (!currentPinEvent) return false;

        return !(currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0);
    },

    render: function() {
        const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");

        let searchStatus = null;
        let cancelButton = null;
        let settingsButton = null;
        let pinnedEventsButton = null;

        const e2eIcon = this.props.e2eStatus ?
            <E2EIcon status={this.props.e2eStatus} /> :
            undefined;

        const joinRules = this.props.room && this.props.room.currentState.getStateEvents("m.room.join_rules", "");
        const joinRule = joinRules && joinRules.getContent().join_rule;
        const joinRuleClass = classNames("mx_RoomHeader_PrivateIcon",
                                         {"mx_RoomHeader_isPrivate": joinRule === "invite"});
        const privateIcon = SettingsStore.isFeatureEnabled("feature_cross_signing") ?

 :
            undefined;

        if (this.props.onCancelClick) {
            cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
        }

        // don't display the search count until the search completes and
        // gives us a valid (possibly zero) searchCount.
        if (this.props.searchInfo &&
            this.props.searchInfo.searchCount !== undefined &&
            this.props.searchInfo.searchCount !== null) {
            searchStatus =

                { _t("(~%(count)s results)", { count: this.props.searchInfo.searchCount }) }

;
        }

        // XXX: this is a bit inefficient - we could just compare room.name for 'Empty room'...
        let settingsHint = false;
        const members = this.props.room ? this.props.room.getJoinedMembers() : undefined;
        if (members) {
            if (members.length === 1 && members[0].userId === MatrixClientPeg.get().credentials.userId) {
                const nameEvent = this.props.room.currentState.getStateEvents('m.room.name', '');
                if (!nameEvent || !nameEvent.getContent().name) {
                    settingsHint = true;
                }
            }
        }

        let roomName = _t("Join Room");
        if (this.props.oobData && this.props.oobData.name) {
            roomName = this.props.oobData.name;
        } else if (this.props.room) {
            roomName = this.props.room.name;
        }

        const textClasses = classNames('mx_RoomHeader_nametext', { mx_RoomHeader_settingsHint: settingsHint });
        const name =

{ roomName }

                { searchStatus }

;

        let topic;
        if (this.props.room) {
            const ev = this.props.room.currentState.getStateEvents('m.room.topic', '');
            if (ev) {
                topic = ev.getContent().topic;
            }
        }
        const topicElement =

{ topic }
;
        const avatarSize = 28;
        let roomAvatar;
        if (this.props.room) {
            roomAvatar = (<RoomAvatar
                room={this.props.room}
                width={avatarSize}
                height={avatarSize}
                oobData={this.props.oobData}
                viewAvatarOnClick={true} />);
        }

        if (this.props.onSettingsClick) {
            settingsButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_settingsButton"
                    onClick={this.props.onSettingsClick}
                    title={_t("Settings")}
                >
                </AccessibleButton>;
        }

        if (this.props.onPinnedClick && SettingsStore.isFeatureEnabled('feature_pinning')) {
            let pinsIndicator = null;
            if (this._hasUnreadPins()) {
                pinsIndicator = (
);
            } else if (this._hasPins()) {
                pinsIndicator = (
);
            }

            pinnedEventsButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_pinnedButton"
                                  onClick={this.props.onPinnedClick} title={_t("Pinned Messages")}>
                    { pinsIndicator }
                </AccessibleButton>;
        }

//        var leave_button;
//        if (this.props.onLeaveClick) {
//            leave_button =
//

//                    <TintableSvg src={require("../../../../res/img/leave.svg")} width="26" height="20"/>
//
;
//        }

        let forgetButton;
        if (this.props.onForgetClick) {
            forgetButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_forgetButton"
                    onClick={this.props.onForgetClick}
                    title={_t("Forget room")}
                >
                </AccessibleButton>;
        }

        let searchButton;
        if (this.props.onSearchClick && this.props.inRoom) {
            searchButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_searchButton"
                    onClick={this.props.onSearchClick}
                    title={_t("Search")}
                >
                </AccessibleButton>;
        }

        let shareRoomButton;
        if (this.props.inRoom) {
            shareRoomButton =
                <AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_shareButton"
                    onClick={this.onShareRoomClick}
                    title={_t('Share room')}
                >
                </AccessibleButton>;
        }

        let manageIntegsButton;
        if (this.props.room && this.props.room.roomId && this.props.inRoom) {
            manageIntegsButton = <ManageIntegsButton
                room={this.props.room}
            />;
        }

        const rightRow =

                { settingsButton }
                { pinnedEventsButton }
                { shareRoomButton }
                { manageIntegsButton }
                { forgetButton }
                { searchButton }

;

        return (

{ roomAvatar }

                    { e2eIcon }
                    { privateIcon }
                    { name }
                    { topicElement }
                    { cancelButton }
                    { rightRow }
                    <RoomHeaderButtons />

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


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var React = require('react');
var sdk = require('../../../index');
var dis = require("../../../dispatcher");
var MatrixClientPeg = require('../../../MatrixClientPeg');
var Modal = require("../../../Modal");

var linkify = require('linkifyjs');
var linkifyElement = require('linkifyjs/element');
var linkifyMatrix = require('../../../linkify-matrix');

linkifyMatrix(linkify);

module.exports = React.createClass({
    displayName: 'RoomHeader',

    propTypes: {
        room: React.PropTypes.object,
        oobData: React.PropTypes.object,
        editing: React.PropTypes.bool,
        onSettingsClick: React.PropTypes.func,
        onSaveClick: React.PropTypes.func,
        onSearchClick: React.PropTypes.func,
        onLeaveClick: React.PropTypes.func,
    },

    getDefaultProps: function() {
        return {
            editing: false,
            onSettingsClick: function() {},
            onSaveClick: function() {},
        };
    },

    componentWillReceiveProps: function(newProps) {
        if (newProps.editing) {
            var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
            var name = this.props.room.currentState.getStateEvents('m.room.name', '');

            this.setState({
                name: name ? name.getContent().name : '',
                defaultName: this.props.room.getDefaultRoomName(MatrixClientPeg.get().credentials.userId),
                topic: topic ? topic.getContent().topic : '',
            });
        }
    },

    componentDidUpdate: function() {
        if (this.refs.topic) {
            linkifyElement(this.refs.topic, linkifyMatrix.options);
        }
    },

    onVideoClick: function(e) {
        dis.dispatch({
            action: 'place_call',
            type: e.shiftKey ? "screensharing" : "video",
            room_id: this.props.room.roomId
        });
    },

    onVoiceClick: function() {
        dis.dispatch({
            action: 'place_call',
            type: "voice",
            room_id: this.props.room.roomId
        });
    },

    onNameChanged: function(value) {
        this.setState({ name : value });
    },

    onTopicChanged: function(value) {
        this.setState({ topic : value });
    },

    onAvatarPickerClick: function(ev) {
        if (this.refs.file_label) {
            this.refs.file_label.click();
        }
    },

    onAvatarSelected: function(ev) {
        var self = this;
        var changeAvatar = this.refs.changeAvatar;
        if (!changeAvatar) {
            console.error("No ChangeAvatar found to upload image to!");
            return;
        }
        changeAvatar.onFileSelected(ev).catch(function(err) {
            var errMsg = (typeof err === "string") ? err : (err.error || "");
            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createDialog(ErrorDialog, {
                title: "Error",
                description: "Failed to set avatar. " + errMsg
            });
        }).done();
    },

    getRoomName: function() {
        return this.state.name;
    },

    getTopic: function() {
        return this.state.topic;
    },

    render: function() {
        var EditableText = sdk.getComponent("elements.EditableText");
        var RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
        var ChangeAvatar = sdk.getComponent("settings.ChangeAvatar");
        var TintableSvg = sdk.getComponent("elements.TintableSvg");

        var header;
        if (this.props.simpleHeader) {
            var cancel;
            if (this.props.onCancelClick) {
                cancel =
            }
            header =

                        { this.props.simpleHeader }
                        { cancel }

        }
        else {
            var name = null;
            var searchStatus = null;
            var topic_el = null;
            var cancel_button = null;
            var save_button = null;
            var settings_button = null;
            if (this.props.editing) {

                // calculate permissions.  XXX: this should be done on mount or something, and factored out with RoomSettings
                var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
                var events_levels = (power_levels ? power_levels.events : {}) || {};
                var user_id = MatrixClientPeg.get().credentials.userId;

                if (power_levels) {
                    power_levels = power_levels.getContent();
                    var default_user_level = parseInt(power_levels.users_default || 0);
                    var user_levels = power_levels.users || {};
                    var current_user_level = user_levels[user_id];
                    if (current_user_level == undefined) current_user_level = default_user_level;
                } else {
                    var default_user_level = 0;
                    var user_levels = [];
                    var current_user_level = 0;
                }
                var state_default = parseInt((power_levels ? power_levels.state_default : 0) || 0);

                var room_avatar_level = state_default;
                if (events_levels['m.room.avatar'] !== undefined) {
                    room_avatar_level = events_levels['m.room.avatar'];
                }
                var can_set_room_avatar = current_user_level >= room_avatar_level;

                var room_name_level = state_default;
                if (events_levels['m.room.name'] !== undefined) {
                    room_name_level = events_levels['m.room.name'];
                }
                var can_set_room_name = current_user_level >= room_name_level;

                var room_topic_level = state_default;
                if (events_levels['m.room.topic'] !== undefined) {
                    room_topic_level = events_levels['m.room.topic'];
                }
                var can_set_room_topic = current_user_level >= room_topic_level;

                var placeholderName = "Unnamed Room";
                if (this.state.defaultName && this.state.defaultName !== 'Empty room') {
                    placeholderName += " (" + this.state.defaultName + ")";
                }

                save_button =
Save

                cancel_button =

            }

            if (can_set_room_name) {
                name =

                        <EditableText
                             className="mx_RoomHeader_nametext mx_RoomHeader_editable"
                             placeholderClassName="mx_RoomHeader_placeholder"
                             placeholder={ placeholderName }
                             blurToCancel={ false }
                             onValueChanged={ this.onNameChanged }
                             initialValue={ this.state.name }/>

            }
            else {
                var searchStatus;
                // don't display the search count until the search completes and
                // gives us a valid (possibly zero) searchCount.
                if (this.props.searchInfo && this.props.searchInfo.searchCount !== undefined && this.props.searchInfo.searchCount !== null) {
                    searchStatus =
 (~{ this.props.searchInfo.searchCount } results)
;
                }

                // XXX: this is a bit inefficient - we could just compare room.name for 'Empty room'...
                var settingsHint = false;
                var members = this.props.room ? this.props.room.getJoinedMembers() : undefined;
                if (members) {
                    if (members.length === 1 && members[0].userId === MatrixClientPeg.get().credentials.userId) {
                        var name = this.props.room.currentState.getStateEvents('m.room.name', '');
                        if (!name || !name.getContent().name) {
                            settingsHint = true;
                        }
                    }
                }

                var roomName;
                if (this.props.oobData && this.props.oobData.name) {
                    roomName = this.props.oobData.name;
                } else {
                    this.props.room.name;
                }

                name =

{ roomName }

                        { searchStatus }

                            <TintableSvg src="img/settings.svg" width="12" height="12"/>

            }

            if (can_set_room_topic) {
                topic_el =
                    <EditableText
                         className="mx_RoomHeader_topic mx_RoomHeader_editable"
                         placeholderClassName="mx_RoomHeader_placeholder"
                         placeholder="Add a topic"
                         blurToCancel={ false }
                         onValueChanged={ this.onTopicChanged }
                         initialValue={ this.state.topic }/>
            } else {
                var topic = this.props.room ? this.props.room.currentState.getStateEvents('m.room.topic', '') : '';
                if (topic) topic_el =
{ topic.getContent().topic }
;
            }

            var roomAvatar = null;
            if (can_set_room_avatar) {
                roomAvatar = (

                            <ChangeAvatar ref="changeAvatar" room={this.props.room} showUploadSection={false} width={48} height={48} />

                            <input id="avatarInput" type="file" onChange={ this.onAvatarSelected }/>

                );
            }
            else {
                roomAvatar = (

                        <RoomAvatar room={this.props.room} width={48} height={48} oobData={this.props.oobData} />

                );
            }

            var leave_button;
            if (this.props.onLeaveClick) {
                leave_button =

                        <TintableSvg src="img/leave.svg" width="26" height="20"/>

;
            }

            var forget_button;
            if (this.props.onForgetClick) {
                forget_button =

                        <TintableSvg src="img/leave.svg" width="26" height="20"/>

;
            }

            var right_row;
            if (!this.props.editing) {
                right_row =

                        { forget_button }
                        { leave_button }

                            <TintableSvg src="img/search.svg" width="21" height="19"/>

;
            }

            header =

                            { roomAvatar }

                            { name }
                            { topic_el }

                    {save_button}
                    {cancel_button}
                    {right_row}

        }

        return (

                { header }

        );
    },
});
<commit_msg>Fix rooms we don't have any info about<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var React = require('react');
var sdk = require('../../../index');
var dis = require("../../../dispatcher");
var MatrixClientPeg = require('../../../MatrixClientPeg');
var Modal = require("../../../Modal");

var linkify = require('linkifyjs');
var linkifyElement = require('linkifyjs/element');
var linkifyMatrix = require('../../../linkify-matrix');

linkifyMatrix(linkify);

module.exports = React.createClass({
    displayName: 'RoomHeader',

    propTypes: {
        room: React.PropTypes.object,
        oobData: React.PropTypes.object,
        editing: React.PropTypes.bool,
        onSettingsClick: React.PropTypes.func,
        onSaveClick: React.PropTypes.func,
        onSearchClick: React.PropTypes.func,
        onLeaveClick: React.PropTypes.func,
    },

    getDefaultProps: function() {
        return {
            editing: false,
            onSettingsClick: function() {},
            onSaveClick: function() {},
        };
    },

    componentWillReceiveProps: function(newProps) {
        if (newProps.editing) {
            var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
            var name = this.props.room.currentState.getStateEvents('m.room.name', '');

            this.setState({
                name: name ? name.getContent().name : '',
                defaultName: this.props.room.getDefaultRoomName(MatrixClientPeg.get().credentials.userId),
                topic: topic ? topic.getContent().topic : '',
            });
        }
    },

    componentDidUpdate: function() {
        if (this.refs.topic) {
            linkifyElement(this.refs.topic, linkifyMatrix.options);
        }
    },

    onVideoClick: function(e) {
        dis.dispatch({
            action: 'place_call',
            type: e.shiftKey ? "screensharing" : "video",
            room_id: this.props.room.roomId
        });
    },

    onVoiceClick: function() {
        dis.dispatch({
            action: 'place_call',
            type: "voice",
            room_id: this.props.room.roomId
        });
    },

    onNameChanged: function(value) {
        this.setState({ name : value });
    },

    onTopicChanged: function(value) {
        this.setState({ topic : value });
    },

    onAvatarPickerClick: function(ev) {
        if (this.refs.file_label) {
            this.refs.file_label.click();
        }
    },

    onAvatarSelected: function(ev) {
        var self = this;
        var changeAvatar = this.refs.changeAvatar;
        if (!changeAvatar) {
            console.error("No ChangeAvatar found to upload image to!");
            return;
        }
        changeAvatar.onFileSelected(ev).catch(function(err) {
            var errMsg = (typeof err === "string") ? err : (err.error || "");
            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createDialog(ErrorDialog, {
                title: "Error",
                description: "Failed to set avatar. " + errMsg
            });
        }).done();
    },

    getRoomName: function() {
        return this.state.name;
    },

    getTopic: function() {
        return this.state.topic;
    },

    render: function() {
        var EditableText = sdk.getComponent("elements.EditableText");
        var RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
        var ChangeAvatar = sdk.getComponent("settings.ChangeAvatar");
        var TintableSvg = sdk.getComponent("elements.TintableSvg");

        var header;
        if (this.props.simpleHeader) {
            var cancel;
            if (this.props.onCancelClick) {
                cancel =
            }
            header =

                        { this.props.simpleHeader }
                        { cancel }

        }
        else {
            var name = null;
            var searchStatus = null;
            var topic_el = null;
            var cancel_button = null;
            var save_button = null;
            var settings_button = null;
            if (this.props.editing) {

                // calculate permissions.  XXX: this should be done on mount or something, and factored out with RoomSettings
                var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
                var events_levels = (power_levels ? power_levels.events : {}) || {};
                var user_id = MatrixClientPeg.get().credentials.userId;

                if (power_levels) {
                    power_levels = power_levels.getContent();
                    var default_user_level = parseInt(power_levels.users_default || 0);
                    var user_levels = power_levels.users || {};
                    var current_user_level = user_levels[user_id];
                    if (current_user_level == undefined) current_user_level = default_user_level;
                } else {
                    var default_user_level = 0;
                    var user_levels = [];
                    var current_user_level = 0;
                }
                var state_default = parseInt((power_levels ? power_levels.state_default : 0) || 0);

                var room_avatar_level = state_default;
                if (events_levels['m.room.avatar'] !== undefined) {
                    room_avatar_level = events_levels['m.room.avatar'];
                }
                var can_set_room_avatar = current_user_level >= room_avatar_level;

                var room_name_level = state_default;
                if (events_levels['m.room.name'] !== undefined) {
                    room_name_level = events_levels['m.room.name'];
                }
                var can_set_room_name = current_user_level >= room_name_level;

                var room_topic_level = state_default;
                if (events_levels['m.room.topic'] !== undefined) {
                    room_topic_level = events_levels['m.room.topic'];
                }
                var can_set_room_topic = current_user_level >= room_topic_level;

                var placeholderName = "Unnamed Room";
                if (this.state.defaultName && this.state.defaultName !== 'Empty room') {
                    placeholderName += " (" + this.state.defaultName + ")";
                }

                save_button =
Save

                cancel_button =

            }

            if (can_set_room_name) {
                name =

                        <EditableText
                             className="mx_RoomHeader_nametext mx_RoomHeader_editable"
                             placeholderClassName="mx_RoomHeader_placeholder"
                             placeholder={ placeholderName }
                             blurToCancel={ false }
                             onValueChanged={ this.onNameChanged }
                             initialValue={ this.state.name }/>

            }
            else {
                var searchStatus;
                // don't display the search count until the search completes and
                // gives us a valid (possibly zero) searchCount.
                if (this.props.searchInfo && this.props.searchInfo.searchCount !== undefined && this.props.searchInfo.searchCount !== null) {
                    searchStatus =
 (~{ this.props.searchInfo.searchCount } results)
;
                }

                // XXX: this is a bit inefficient - we could just compare room.name for 'Empty room'...
                var settingsHint = false;
                var members = this.props.room ? this.props.room.getJoinedMembers() : undefined;
                if (members) {
                    if (members.length === 1 && members[0].userId === MatrixClientPeg.get().credentials.userId) {
                        var name = this.props.room.currentState.getStateEvents('m.room.name', '');
                        if (!name || !name.getContent().name) {
                            settingsHint = true;
                        }
                    }
                }

                var roomName = 'Join Room';
                if (this.props.oobData && this.props.oobData.name) {
                    roomName = this.props.oobData.name;
                } else if (this.props.room) {
                    this.props.room.name;
                }

                name =

{ roomName }

                        { searchStatus }

                            <TintableSvg src="img/settings.svg" width="12" height="12"/>

            }

            if (can_set_room_topic) {
                topic_el =
                    <EditableText
                         className="mx_RoomHeader_topic mx_RoomHeader_editable"
                         placeholderClassName="mx_RoomHeader_placeholder"
                         placeholder="Add a topic"
                         blurToCancel={ false }
                         onValueChanged={ this.onTopicChanged }
                         initialValue={ this.state.topic }/>
            } else {
                var topic = this.props.room ? this.props.room.currentState.getStateEvents('m.room.topic', '') : '';
                if (topic) topic_el =
{ topic.getContent().topic }
;
            }

            var roomAvatar = null;
            if (can_set_room_avatar) {
                roomAvatar = (

                            <ChangeAvatar ref="changeAvatar" room={this.props.room} showUploadSection={false} width={48} height={48} />

                            <input id="avatarInput" type="file" onChange={ this.onAvatarSelected }/>

                );
            }
            else if (this.props.room || (this.props.oobData && this.props.oobData.name)) {
                roomAvatar = (

                        <RoomAvatar room={this.props.room} width={48} height={48} oobData={this.props.oobData} />

                );
            }

            var leave_button;
            if (this.props.onLeaveClick) {
                leave_button =

                        <TintableSvg src="img/leave.svg" width="26" height="20"/>

;
            }

            var forget_button;
            if (this.props.onForgetClick) {
                forget_button =

                        <TintableSvg src="img/leave.svg" width="26" height="20"/>

;
            }

            var right_row;
            if (!this.props.editing) {
                right_row =

                        { forget_button }
                        { leave_button }

                            <TintableSvg src="img/search.svg" width="21" height="19"/>

;
            }

            header =

                            { roomAvatar }

                            { name }
                            { topic_el }

                    {save_button}
                    {cancel_button}
                    {right_row}

        }

        return (

                { header }

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


<commit_before>var sane = require('sane')
var RSVP = require('rsvp')
var logger = require('heimdalljs-logger')('broccoli:watcherAdapter')

function defaultFilterFunction(name) {
  return /^[^\.]/.test(name)
}

module.exports = WatcherAdapter
RSVP.EventTarget.mixin(WatcherAdapter.prototype)
function WatcherAdapter(options) {
  this.options = options || {}
  this.options.filter = this.options.filter || defaultFilterFunction
}

WatcherAdapter.prototype.watch = function(watchedPaths) {
  var self = this

  this.watchers = []
  this.readyPromises = []
  watchedPaths.forEach(function(watchedPath) {
    var watcher = new sane(watchedPath, self.options)
    function bindFileEvent(event) {
      watcher.on(event, function(filepath, root, stat) {
        logger.debug(event, root + '/' + filepath)
        self.trigger('change')
      })
    }
    bindFileEvent('change')
    bindFileEvent('add')
    bindFileEvent('delete')
    watcher.on('error', function(err) {
      logger.debug('error', err)
      self.trigger('error', err)
    })
    var readyPromise = new RSVP.Promise(function(resolve, reject) {
      watcher.on('ready', function() {
        logger.debug('ready', watchedPath)
        resolve()
      })
    })
    self.watchers.push(watcher)
    self.readyPromises.push(readyPromise)
  })
  return RSVP.Promise.all(this.readyPromises)
}

WatcherAdapter.prototype.quit = function () {
  for (var i = 0; i < this.watchers.length; i++) {
    this.watchers[i].close()
  }
}
<commit_msg>Fix linting failure.<commit_after>var sane = require('sane')
var RSVP = require('rsvp')
var logger = require('heimdalljs-logger')('broccoli:watcherAdapter')

function defaultFilterFunction(name) {
  return /^[^.]/.test(name)
}

module.exports = WatcherAdapter
RSVP.EventTarget.mixin(WatcherAdapter.prototype)
function WatcherAdapter(options) {
  this.options = options || {}
  this.options.filter = this.options.filter || defaultFilterFunction
}

WatcherAdapter.prototype.watch = function(watchedPaths) {
  var self = this

  this.watchers = []
  this.readyPromises = []
  watchedPaths.forEach(function(watchedPath) {
    var watcher = new sane(watchedPath, self.options)
    function bindFileEvent(event) {
      watcher.on(event, function(filepath, root, stat) {
        logger.debug(event, root + '/' + filepath)
        self.trigger('change')
      })
    }
    bindFileEvent('change')
    bindFileEvent('add')
    bindFileEvent('delete')
    watcher.on('error', function(err) {
      logger.debug('error', err)
      self.trigger('error', err)
    })
    var readyPromise = new RSVP.Promise(function(resolve, reject) {
      watcher.on('ready', function() {
        logger.debug('ready', watchedPath)
        resolve()
      })
    })
    self.watchers.push(watcher)
    self.readyPromises.push(readyPromise)
  })
  return RSVP.Promise.all(this.readyPromises)
}

WatcherAdapter.prototype.quit = function () {
  for (var i = 0; i < this.watchers.length; i++) {
    this.watchers[i].close()
  }
}
<|endoftext|>