<commit_before>var Connection = require('../lib/Connection');

var fs = require('fs'),
    cpspawn = require('child_process').spawn,
    cpexec = require('child_process').exec,
    path = require('path'),
    join = path.join,
    inspect = require('util').inspect,
    assert = require('assert');

var t = -1,
    group = path.basename(__filename, '.js') + '/',
    tempdir = join(__dirname, 'temp'),
    fixturesdir = join(__dirname, 'fixtures');

var SSHD_PORT,
    HOST_FINGERPRINT = '64254520742d3d0792e918f3ce945a64',
    PRIVATE_KEY = fs.readFileSync(join(fixturesdir, 'id_rsa')),
    USER = process.env.LOGNAME || process.env.USER || process.env.USERNAME;
    DEFAULT_SSHD_OPTS = {
      'AddressFamily': 'any',
      'AllowUsers': USER,
      'AuthorizedKeysFile': join(fixturesdir, 'authorized_keys'),
      'Banner': 'none',
      'Compression': 'no',
      'HostbasedAuthentication': 'no',
      'HostKey': join(fixturesdir, 'ssh_host_rsa_key'),
      'ListenAddress': 'localhost',
      'LogLevel': 'FATAL',
      'PasswordAuthentication': 'no',
      'PermitRootLogin': 'no',
      'Protocol': '2',
      'PubkeyAuthentication': 'yes',
      'Subsystem': 'sftp internal-sftp',
      'TCPKeepAlive': 'yes',
      'UseDNS': 'no',
      //'UsePrivilegeSeparation': 'no'
    };

var tests = [
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection();
      startServer(function() {
        var error,
            ready;
        conn.on('ready', function() {
          ready = true;
          this.end();
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    what: 'Authenticate with a key'
  },
  { run: function() {
      // use ssh-agent with a command (this test) to make agent cleanup easier
      if (!process.env.SSH_AUTH_SOCK) {
        var proc = cpspawn('ssh-agent',
                           [process.argv[0], process.argv[1], t],
                           { stdio: 'inherit' });
        proc.on('exit', function(code, signal) {
          if (code === 0 && !signal)
            next();
        });
        return;
      }

      var self = this,
          what = this.what,
          conn = new Connection();

      // add key first
      cpexec('ssh-add ' + join(fixturesdir, 'id_rsa'), function() {
        startServer(function() {
          var error,
              ready;
          conn.on('ready', function() {
            ready = true;
            this.end();
          }).on('error', function(err) {
            error = err;
          }).on('close', function() {
            assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
            assert(ready, makeMsg(what, 'Expected ready'));
            next();
          }).connect(self.config);
        });
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      agent: process.env.SSH_AUTH_SOCK
    },
    what: 'Authenticate with an agent'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection(),
          fingerprint;
      this.config.hostVerifier = function(host) {
        fingerprint = host;
        return true; // perform actual verification at the end
      };
      startServer(function() {
        var error,
            ready;
        conn.on('ready', function() {
          ready = true;
          this.end();
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          assert(HOST_FINGERPRINT === fingerprint,
                 makeMsg(what, 'Host fingerprint mismatch.\nSaw:\n'
                               + fingerprint
                               + '\nExpected:\n'
                               + HOST_FINGERPRINT));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY,
      hostHash: 'md5'
    },
    what: 'Verify host fingerprint'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection(),
          bannerPath = join(fixturesdir, 'banner'),
          bannerSent = fs.readFileSync(bannerPath,
                                       { encoding: 'utf8' });
      startServer({ 'Banner': bannerPath }, function() {
        var error,
            ready,
            bannerRecvd;
        conn.on('banner', function(msg) {
          bannerRecvd = msg;
        }).on('ready', function() {
          ready = true;
          this.exec('uptime', function(err) {
            assert(!err, makeMsg(what, 'Unexpected exec error: ' + err));
            conn.end();
          });
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          assert(bannerSent === bannerRecvd,
                 makeMsg(what, 'Banner mismatch.\nSaw:\n'
                               + inspect(bannerRecvd)
                               + '\nExpected:\n'
                               + inspect(bannerSent)));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    what: 'Banner message'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection();
      startServer(function() {
        var error,
            ready;
        conn.on('ready', function() {
          ready = true;
          this.exec('uptime', function(err) {
            assert(!err, makeMsg(what, 'Unexpected exec error: ' + err));
            conn.end();
          });
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    what: 'Simple exec'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection(),
          SSH2NODETEST = 'Hello from node.js!!!';
      startServer({ 'AcceptEnv': 'SSH2NODETEST' }, function() {
        var error,
            ready,
            envvar;
        conn.on('ready', function() {
          ready = true;
          this.exec('echo $SSH2NODETEST',
                    { env: { SSH2NODETEST: SSH2NODETEST } },
                    function(err, stream) {
            assert(!err, makeMsg(what, 'Unexpected exec error: ' + err));
            stream.stderr.resume();
            stream.setEncoding('ascii');
            stream.on('data', function(d) {
              if (!envvar)
                envvar = d;
              else
                envvar += d;
            }).on('end', function() {
              conn.end();
            });
          });
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          assert(envvar === SSH2NODETEST,
                 makeMsg(what, 'Environment variable mismatch.\nSaw:\n'
                               + envvar
                               + '\nExpected:\n'
                               + SSH2NODETEST));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    what: 'Exec with environment set'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection();
      startServer(function() {
        var error,
            ready;
        conn.on('ready', function() {
          ready = true;
          this.shell(function(err) {
            assert(!err, makeMsg(what, 'Unexpected shell error: ' + err));
            conn.end();
          });
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    what: 'Simple shell'
  },
];

function startServer(opts, listencb, exitcb) {
  var sshdOpts = {},
      cmd,
      key,
      val;
  for (key in DEFAULT_SSHD_OPTS)
    sshdOpts[key] = DEFAULT_SSHD_OPTS[key];
  if (typeof opts === 'function') {
    exitcb = listencb;
    listencb = opts;
    opts = undefined;
  }
  if (opts) {
    for (key in opts)
      sshdOpts[key] = opts[key];
  }

  cmd = '`which sshd` -p '
        + SSHD_PORT
        + ' -Dde -f '
        + join(fixturesdir, 'sshd_config');

  for (key in sshdOpts) {
    val = ''+sshdOpts[key];
    if (val.indexOf(' ') > -1)
      val = '"' + val + '"';
    cmd += ' -o ' + key + '=' + val;
  }

  tests[t].config.port = SSHD_PORT;

  stopWaiting = false;
  cpexec(cmd, function(err, stdout, stderr) {
    stopWaiting = true;
    //exitcb(err, stdout, stderr);
  });
  waitForSshd(listencb);
}

function cleanupTemp() {
  // clean up any temporary files left over
  fs.readdirSync(tempdir).forEach(function(file) {
    if (file !== '.gitignore')
      fs.unlinkSync(join(tempdir, file));
  });
}

function next() {
  if (process.argv.length > 2 || t === tests.length - 1)
    return;
  cleanupTemp();
  var v = tests[++t];
  v.run.call(v);
}

function makeMsg(what, msg) {
  return '[' + group + what + ']: ' + msg;
}

var stopWaiting = false;
function waitForSshd(cb) {
  if (stopWaiting)
    return;
  cpexec('lsof -a -u '
         + USER
         + ' -c sshd -i tcp@localhost:'
         + SSHD_PORT
         + ' &>/dev/null', function(err, stdout) {
    if (err) {
      return setTimeout(function() {
        waitForSshd(cb);
      }, 50);
    }
    cb();
  });
}

function cleanup(cb) {
  cleanupTemp();
  cpexec('lsof -Fp -a -u '
         + USER
         + ' -c sshd -i tcp@localhost:'
         + SSHD_PORT, function(err, stdout) {
    if (!err) {
      var pid = parseInt(stdout.trim().replace(/[^\d]/g, ''), 10);
      if (typeof pid === 'number' && !isNaN(pid))
        process.kill(pid);
    }
    cb();
  });
}

process.once('uncaughtException', function(err) {
  cleanup(function() {
    throw err;
  });
});
process.once('exit', function() {
  cleanup(function() {
    assert(t === tests.length - 1,
           makeMsg('_exit',
                   'Only finished ' + (t + 1) + '/' + tests.length + ' tests'));
  });
});

// find an unused port for sshd to listen on ...
cpexec('netstat -nl --inet --inet6', function(err, stdout) {
  assert(!err, 'Unable to find a free port for starting sshd');
  var portsInUse = stdout.trim()
                         .split('\n')
                         .slice(2) // skip two header lines
                         .map(function(line) {
                           var addr = line.split(/[ \t]+/g)[3];
                           return parseInt(
                            addr.substr(addr.lastIndexOf(':') + 1),
                            10
                           );
                         });
  for (var port = 1025; port < 65535; ++port) {
    if (portsInUse.indexOf(port)) {
      SSHD_PORT = port;
      // start tests
      return next();
    }
  }
  assert(false, 'Unable to find a free port for starting sshd');
});

// check for forked process
if (process.argv.length > 2) {
  var testnum = parseInt(process.argv[2], 10);
  if (!isNaN(testnum))
    t = testnum - 1;
}
  <commit_msg>test: improve existing integration tests<commit_after>var Connection = require('../lib/Connection');

var fs = require('fs'),
    cpspawn = require('child_process').spawn,
    cpexec = require('child_process').exec,
    path = require('path'),
    join = path.join,
    inspect = require('util').inspect,
    assert = require('assert');

var t = -1,
    group = path.basename(__filename, '.js') + '/',
    tempdir = join(__dirname, 'temp'),
    fixturesdir = join(__dirname, 'fixtures');

var SSHD_PORT,
    HOST_FINGERPRINT = '64254520742d3d0792e918f3ce945a64',
    PRIVATE_KEY = fs.readFileSync(join(fixturesdir, 'id_rsa')),
    USER = process.env.LOGNAME || process.env.USER || process.env.USERNAME;
    DEFAULT_SSHD_OPTS = {
      'AddressFamily': 'any',
      'AllowUsers': USER,
      'AuthorizedKeysFile': join(fixturesdir, 'authorized_keys'),
      'Banner': 'none',
      'Compression': 'no',
      'HostbasedAuthentication': 'no',
      'HostKey': join(fixturesdir, 'ssh_host_rsa_key'),
      'ListenAddress': 'localhost',
      'LogLevel': 'FATAL',
      'PasswordAuthentication': 'no',
      'PermitRootLogin': 'no',
      'Protocol': '2',
      'PubkeyAuthentication': 'yes',
      'Subsystem': 'sftp internal-sftp',
      'TCPKeepAlive': 'yes',
      'UseDNS': 'no',
      //'UsePrivilegeSeparation': 'no'
    };

var tests = [
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection();
      startServer(function() {
        var error,
            ready;
        conn.on('ready', function() {
          ready = true;
          this.end();
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    what: 'Authenticate with a key'
  },
  { run: function() {
      // use ssh-agent with a command (this test) to make agent cleanup easier
      if (!process.env.SSH_AUTH_SOCK) {
        var proc = cpspawn('ssh-agent',
                           [process.argv[0], process.argv[1], t],
                           { stdio: 'inherit' });
        proc.on('exit', function(code, signal) {
          if (code === 0 && !signal)
            next();
        });
        return;
      }

      var self = this,
          what = this.what,
          conn = new Connection();

      // add key first
      cpexec('ssh-add ' + join(fixturesdir, 'id_rsa'), function() {
        startServer(function() {
          var error,
              ready;
          conn.on('ready', function() {
            ready = true;
            this.end();
          }).on('error', function(err) {
            error = err;
          }).on('close', function() {
            assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
            assert(ready, makeMsg(what, 'Expected ready'));
            next();
          }).connect(self.config);
        });
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      agent: process.env.SSH_AUTH_SOCK
    },
    what: 'Authenticate with an agent'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection(),
          fingerprint;
      this.config.hostVerifier = function(host) {
        fingerprint = host;
        return true; // perform actual verification at the end
      };
      startServer(function() {
        var error,
            ready;
        conn.on('ready', function() {
          ready = true;
          this.end();
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          assert(HOST_FINGERPRINT === fingerprint,
                 makeMsg(what, 'Host fingerprint mismatch.\nSaw:\n'
                               + inspect(fingerprint)
                               + '\nExpected:\n'
                               + inspect(HOST_FINGERPRINT)));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY,
      hostHash: 'md5'
    },
    what: 'Verify host fingerprint'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection(),
          bannerPath = join(fixturesdir, 'banner'),
          bannerSent = fs.readFileSync(bannerPath,
                                       { encoding: 'utf8' });
      startServer({ 'Banner': bannerPath }, function() {
        var error,
            ready,
            bannerRecvd;
        conn.on('banner', function(msg) {
          bannerRecvd = msg;
        }).on('ready', function() {
          ready = true;
          this.exec('uptime', function(err) {
            assert(!err, makeMsg(what, 'Unexpected exec error: ' + err));
            conn.end();
          });
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          assert(bannerSent === bannerRecvd,
                 makeMsg(what, 'Banner mismatch.\nSaw:\n'
                               + inspect(bannerRecvd)
                               + '\nExpected:\n'
                               + inspect(bannerSent)));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    what: 'Banner message'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection();
      startServer(function() {
        var error,
            ready;
        conn.on('ready', function() {
          ready = true;
          this.exec('uptime', function(err) {
            assert(!err, makeMsg(what, 'Unexpected exec error: ' + err));
            conn.end();
          });
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    what: 'Simple exec'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection();
      startServer({ 'AcceptEnv': 'SSH2NODETEST' }, function() {
        var error,
            ready,
            out;
        conn.on('ready', function() {
          ready = true;
          this.exec('echo -n $SSH2NODETEST',
                    { env: { SSH2NODETEST: self.expected } },
                    function(err, stream) {
            assert(!err, makeMsg(what, 'Unexpected exec error: ' + err));
            stream.stderr.resume();
            stream.on('data', function(d) {
              if (!out)
                out = d;
              else
                out += d;
            }).on('end', function() {
              conn.end();
            }).setEncoding('ascii');
          });
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          assert(out === self.expected,
                 makeMsg(what, 'Environment variable mismatch.\nSaw:\n'
                               + inspect(out)
                               + '\nExpected:\n'
                               + inspect(self.expected)));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    expected: 'Hello from node.js!!!',
    what: 'Exec with environment set'
  },
  { run: function() {
      var self = this,
          what = this.what,
          conn = new Connection();
      startServer(function() {
        var error,
            ready;
        conn.on('ready', function() {
          ready = true;
          this.shell(function(err) {
            assert(!err, makeMsg(what, 'Unexpected shell error: ' + err));
            conn.end();
          });
        }).on('error', function(err) {
          error = err;
        }).on('close', function() {
          assert(!error, makeMsg(what, 'Unexpected client error: ' + error));
          assert(ready, makeMsg(what, 'Expected ready'));
          next();
        }).connect(self.config);
      });
    },
    config: {
      host: 'localhost',
      username: USER,
      privateKey: PRIVATE_KEY
    },
    what: 'Simple shell'
  },
];

function startServer(opts, listencb, exitcb) {
  var sshdOpts = {},
      cmd,
      key,
      val;
  for (key in DEFAULT_SSHD_OPTS)
    sshdOpts[key] = DEFAULT_SSHD_OPTS[key];
  if (typeof opts === 'function') {
    exitcb = listencb;
    listencb = opts;
    opts = undefined;
  }
  if (opts) {
    for (key in opts)
      sshdOpts[key] = opts[key];
  }

  cmd = '`which sshd` -p '
        + SSHD_PORT
        + ' -Dde -f '
        + join(fixturesdir, 'sshd_config');

  for (key in sshdOpts) {
    val = ''+sshdOpts[key];
    if (val.indexOf(' ') > -1)
      val = '"' + val + '"';
    cmd += ' -o ' + key + '=' + val;
  }

  tests[t].config.port = SSHD_PORT;

  stopWaiting = false;
  cpexec(cmd, function(err, stdout, stderr) {
    stopWaiting = true;
    //exitcb(err, stdout, stderr);
  });
  waitForSshd(listencb);
}

function cleanupTemp() {
  // clean up any temporary files left over
  fs.readdirSync(tempdir).forEach(function(file) {
    if (file !== '.gitignore')
      fs.unlinkSync(join(tempdir, file));
  });
}

function next() {
  if (process.argv.length > 2 || t === tests.length - 1)
    return;
  cleanupTemp();
  var v = tests[++t];
  v.run.call(v);
}

function makeMsg(what, msg) {
  return '[' + group + what + ']: ' + msg;
}

var stopWaiting = false;
function waitForSshd(cb) {
  if (stopWaiting)
    return;
  cpexec('lsof -a -u '
         + USER
         + ' -c sshd -i tcp@localhost:'
         + SSHD_PORT
         + ' &>/dev/null', function(err, stdout) {
    if (err) {
      return setTimeout(function() {
        waitForSshd(cb);
      }, 50);
    }
    cb();
  });
}

function cleanup(cb) {
  cleanupTemp();
  cpexec('lsof -Fp -a -u '
         + USER
         + ' -c sshd -i tcp@localhost:'
         + SSHD_PORT, function(err, stdout) {
    if (!err) {
      var pid = parseInt(stdout.trim().replace(/[^\d]/g, ''), 10);
      if (typeof pid === 'number' && !isNaN(pid))
        process.kill(pid);
    }
    cb();
  });
}

process.once('uncaughtException', function(err) {
  cleanup(function() {
    throw err;
  });
});
process.once('exit', function() {
  cleanup(function() {
    assert(t === tests.length - 1,
           makeMsg('_exit',
                   'Only finished ' + (t + 1) + '/' + tests.length + ' tests'));
  });
});

// find an unused port for sshd to listen on ...
cpexec('netstat -nl --inet --inet6', function(err, stdout) {
  assert(!err, 'Unable to find a free port for starting sshd');
  var portsInUse = stdout.trim()
                         .split('\n')
                         .slice(2) // skip two header lines
                         .map(function(line) {
                           var addr = line.split(/[ \t]+/g)[3];
                           return parseInt(
                            addr.substr(addr.lastIndexOf(':') + 1),
                            10
                           );
                         });
  for (var port = 1025; port < 65535; ++port) {
    if (portsInUse.indexOf(port)) {
      SSHD_PORT = port;
      // start tests
      return next();
    }
  }
  assert(false, 'Unable to find a free port for starting sshd');
});

// check for forked process
if (process.argv.length > 2) {
  var testnum = parseInt(process.argv[2], 10);
  if (!isNaN(testnum))
    t = testnum - 1;
}
  <|endoftext|>


<commit_before>import { ShaderCache } from './Renderer/shader_cache.js';

import iblFiltering from './shaders/ibl_filtering.frag';
import panoramaToCubeMap from './shaders/panorama_to_cubemap.frag';
import debugOutput from './shaders/debug.frag';
import fullscreenShader from './shaders/fullscreen.vert';

// How to use:
// set canvas/context in constructor
// init(input: panorama image)
// filterAll()
// fetch texture IDs

class iblSampler
{
    constructor(view)
    {

        this.gl = view.context;

        this.textureSize = 256;
        this.sampleCount = 64;
        this.lodBias = 0.0;
        this.mipmapCount = undefined;

        this.lambertianTextureID = undefined;
        this.ggxTextureID = undefined;
        this.sheenTextureID = undefined;

        this.inputTextureID = undefined;
        this.cubemapTextureID = undefined;
        this.framebuffer = undefined;

        const shaderSources = new Map();

        shaderSources.set("fullscreen.vert", fullscreenShader);
        shaderSources.set("panorama_to_cubemap.frag", panoramaToCubeMap);
        shaderSources.set("ibl_filtering.frag", iblFiltering);
        shaderSources.set("debug.frag", debugOutput);

        this.shaderCache = new ShaderCache(shaderSources, view.renderer.webGl);

    }

    /////////////////////////////////////////////////////////////////////

    loadTextureHDR(image)
    {

        var texture = this.gl.createTexture();

        this.gl.bindTexture( this.gl.TEXTURE_2D,  texture);

        var internalFormat = this.gl.RGB32F;
        var format = this.gl.RGB;
        var type = this.gl.FLOAT;
        var data = undefined;

        if (image.dataFloat instanceof Float32Array)
        {
            internalFormat = this.gl.RGB32F;
            format = this.gl.RGB;
            type = this.gl.FLOAT;
            data = image.dataFloat;
        }
        else if (image instanceof Image)
        {
            internalFormat = this.gl.RGBA;
            format = this.gl.RGBA;
            type = this.gl.UNSIGNED_BYTE;
            data = image;
        }

        this.gl.texImage2D(
            this.gl.TEXTURE_2D,
            0, //level
            internalFormat,
            image.width,
            image.height,
            0, //border
            format,
            type,
            data);

        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_WRAP_S,  this.gl.MIRRORED_REPEAT);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_WRAP_T,  this.gl.MIRRORED_REPEAT);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_MAG_FILTER,  this.gl.LINEAR);

        return texture;
    }

    createCubemapTexture(withMipmaps)
    {
        var targetTexture =  this.gl.createTexture();
        this.gl.bindTexture( this.gl.TEXTURE_CUBE_MAP, targetTexture);

        // define size and format of level 0
        const level = 0;
        const internalFormat = this.use8bit ? this.gl.RGBA8 : this.gl.RGBA32F;
        const border = 0;
        const format = this.gl.RGBA;
        const type = this.use8bit ? this.gl.UNSIGNED_BYTE : this.gl.FLOAT;
        const data = null;

        for(var i = 0; i < 6; ++i)
        {
            this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, internalFormat,
                this.textureSize, this.textureSize, border,
                format, type, data);

        }

        if(withMipmaps)
        {
            this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR_MIPMAP_LINEAR);
        }
        else
        {
            this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR);
        }

        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MAG_FILTER,  this.gl.LINEAR);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_S,  this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_T,  this.gl.CLAMP_TO_EDGE);

        return targetTexture;
    }

    init(panoramaImage)
    {
        if (!this.gl.getExtension('EXT_color_buffer_float'))
        {
            this.use8bit = true;
        }

        this.inputTextureID = this.loadTextureHDR(panoramaImage);

        this.cubemapTextureID = this.createCubemapTexture(true);

        this.framebuffer = this.gl.createFramebuffer();

        this.lambertianTextureID = this.createCubemapTexture(false);
        this.ggxTextureID = this.createCubemapTexture(true);
        this.sheenTextureID = this.createCubemapTexture(true);

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.ggxTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.sheenTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

        this.mipmapLevels = Math.floor(Math.log2(this.textureSize))+1;
    }

    filterAll()
    {
        this.panoramaToCubeMap();
        this.cubeMapToLambertian();
        this.cubeMapToGGX();
        this.cubeMapToSheen();

        this.gl.bindFramebuffer(  this.gl.FRAMEBUFFER, null);
    }

    panoramaToCubeMap()
    {
        for(var i = 0; i < 6; ++i)
        {
            this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
            var side = i;
            this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_CUBE_MAP_POSITIVE_X+side, this.cubemapTextureID, 0);

            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);

            this.gl.viewport(0, 0,  this.textureSize,  this.textureSize);

            this.gl.clearColor(1.0, 0.0, 0.0, 0.0);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT| this.gl.DEPTH_BUFFER_BIT);

            const vertexHash = this.shaderCache.selectShader("fullscreen.vert", []);
            const fragmentHash = this.shaderCache.selectShader("panorama_to_cubemap.frag", []);

            var shader = this.shaderCache.getShaderProgram(fragmentHash, vertexHash);
            this.gl.useProgram(shader.program);

            //  TEXTURE0 = active.
            this.gl.activeTexture(this.gl.TEXTURE0+0);

            // Bind texture ID to active texture
            this.gl.bindTexture(this.gl.TEXTURE_2D, this.inputTextureID);

            // map shader uniform to texture unit (TEXTURE0)
            const location = this.gl.getUniformLocation(shader.program,"u_panorama");
            this.gl.uniform1i(location, this.gl.TEXTURE0+0); // texture unit 0 (TEXTURE0)

            shader.updateUniform("u_currentFace", i);

            //fullscreen triangle
            this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
        }

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

    }

    applyFilter(
        distribution,
        roughness,
        targetMipLevel,
        targetTexture)
    {
        var currentTextureSize =  this.textureSize>>(targetMipLevel);

        for(var i = 0; i < 6; ++i)
        {

            this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
            var side = i;
            this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_CUBE_MAP_POSITIVE_X+side, targetTexture, targetMipLevel);

            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, targetTexture);

            this.gl.viewport(0, 0, currentTextureSize, currentTextureSize);

            this.gl.clearColor(1.0, 0.0, 0.0, 0.0);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT| this.gl.DEPTH_BUFFER_BIT);

            const vertexHash = this.shaderCache.selectShader("fullscreen.vert", []);
            const fragmentHash = this.shaderCache.selectShader("ibl_filtering.frag", []);

            var shader = this.shaderCache.getShaderProgram(fragmentHash, vertexHash);
            this.gl.useProgram(shader.program);

            //  TEXTURE0 = active.
            this.gl.activeTexture(this.gl.TEXTURE0+0);

            // Bind texture ID to active texture
            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);

            // map shader uniform to texture unit (TEXTURE0)
            const location = this.gl.getUniformLocation(shader.program,"u_cubemapTexture");
            this.gl.uniform1i(location, 0); // texture unit 0

            shader.updateUniform("u_roughness", roughness);
            shader.updateUniform("u_sampleCount", this.sampleCount)
            shader.updateUniform("u_width", this.textureSize);
            shader.updateUniform("u_lodBias", this.lodBias);
            shader.updateUniform("u_distribution", distribution);
            shader.updateUniform("u_currentFace", i);

            //fullscreen triangle
            this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);

        }

    }

    cubeMapToLambertian()
    {
        this.applyFilter(
            0,
            0.0,
            0,
            this.lambertianTextureID);
    }

    cubeMapToGGX()
    {
        for(var currentMipLevel = 0; currentMipLevel < this.mipmapLevels; ++currentMipLevel)
        {
            const roughness =  (currentMipLevel) /  (this.mipmapLevels - 1);
            this.applyFilter(
                1,
                roughness,
                currentMipLevel,
                this.ggxTextureID);
        }
    }

    cubeMapToSheen()
    {
        for(var currentMipLevel = 0; currentMipLevel < this.mipmapLevels; ++currentMipLevel)
        {
            const roughness =  (currentMipLevel) /  (this.mipmapLevels - 1);
            this.applyFilter(
                2,
                roughness,
                currentMipLevel,
                this.sheenTextureID);
        }
    }

    destroy()
    {
        this.shaderCache.destroy();
    }
}

export { iblSampler };
<commit_msg>extend RGB32F textures to RGBA32F in headless mode<commit_after>import { ShaderCache } from './Renderer/shader_cache.js';

import iblFiltering from './shaders/ibl_filtering.frag';
import panoramaToCubeMap from './shaders/panorama_to_cubemap.frag';
import debugOutput from './shaders/debug.frag';
import fullscreenShader from './shaders/fullscreen.vert';

// How to use:
// set canvas/context in constructor
// init(input: panorama image)
// filterAll()
// fetch texture IDs

class iblSampler
{
    constructor(view)
    {

        this.gl = view.context;

        this.textureSize = 256;
        this.sampleCount = 64;
        this.lodBias = 0.0;
        this.mipmapCount = undefined;

        this.lambertianTextureID = undefined;
        this.ggxTextureID = undefined;
        this.sheenTextureID = undefined;

        this.inputTextureID = undefined;
        this.cubemapTextureID = undefined;
        this.framebuffer = undefined;

        const shaderSources = new Map();

        shaderSources.set("fullscreen.vert", fullscreenShader);
        shaderSources.set("panorama_to_cubemap.frag", panoramaToCubeMap);
        shaderSources.set("ibl_filtering.frag", iblFiltering);
        shaderSources.set("debug.frag", debugOutput);

        this.shaderCache = new ShaderCache(shaderSources, view.renderer.webGl);

    }

    /////////////////////////////////////////////////////////////////////

    loadTextureHDR(image)
    {

        var texture = this.gl.createTexture();

        this.gl.bindTexture( this.gl.TEXTURE_2D,  texture);

        var internalFormat = this.gl.RGB32F;
        var format = this.gl.RGB;
        var type = this.gl.FLOAT;
        var data = undefined;

        if (image.dataFloat instanceof Float32Array && typeof(this.gl.RGB32F) !== 'undefined')
        {
            internalFormat = this.gl.RGB32F;
            format = this.gl.RGB;
            type = this.gl.FLOAT;
            data = image.dataFloat;
        }
        else if(image.dataFloat instanceof Float32Array)
        {
            // workaround for node-gles not supporting RGB32F
            internalFormat = this.gl.RGBA32F;
            format = this.gl.RGBA;
            type = this.gl.FLOAT;

            const numPixels = image.dataFloat.length / 3;
            data = new Float32Array(numPixels * 4);
            for(let i = 0; i < numPixels; ++i)
            {
                // copy the pixels and padd the alpha channel
                data[i] = image.dataFloat[i];
                data[i+1] = image.dataFloat[i+1];
                data[i+2] = image.dataFloat[i+2];
                data[i+3] = 0;
            }
        }
        else if (typeof(Image) !== 'undefined' && image instanceof Image)
        {
            internalFormat = this.gl.RGBA;
            format = this.gl.RGBA;
            type = this.gl.UNSIGNED_BYTE;
            data = image;
        }
        else
        {
            console.error("loadTextureHDR failed, unsupported HDR image");
            return;
        }

        this.gl.texImage2D(
            this.gl.TEXTURE_2D,
            0, //level
            internalFormat,
            image.width,
            image.height,
            0, //border
            format,
            type,
            data);

        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_WRAP_S,  this.gl.MIRRORED_REPEAT);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_WRAP_T,  this.gl.MIRRORED_REPEAT);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_MAG_FILTER,  this.gl.LINEAR);

        return texture;
    }

    createCubemapTexture(withMipmaps)
    {
        var targetTexture =  this.gl.createTexture();
        this.gl.bindTexture( this.gl.TEXTURE_CUBE_MAP, targetTexture);

        // define size and format of level 0
        const level = 0;
        const internalFormat = this.use8bit ? this.gl.RGBA8 : this.gl.RGBA32F;
        const border = 0;
        const format = this.gl.RGBA;
        const type = this.use8bit ? this.gl.UNSIGNED_BYTE : this.gl.FLOAT;
        const data = null;

        for(var i = 0; i < 6; ++i)
        {
            this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, internalFormat,
                this.textureSize, this.textureSize, border,
                format, type, data);

        }

        if(withMipmaps)
        {
            this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR_MIPMAP_LINEAR);
        }
        else
        {
            this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR);
        }

        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MAG_FILTER,  this.gl.LINEAR);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_S,  this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_T,  this.gl.CLAMP_TO_EDGE);

        return targetTexture;
    }

    init(panoramaImage)
    {
        if (!this.gl.getExtension('EXT_color_buffer_float'))
        {
            this.use8bit = true;
        }

        this.inputTextureID = this.loadTextureHDR(panoramaImage);

        this.cubemapTextureID = this.createCubemapTexture(true);

        this.framebuffer = this.gl.createFramebuffer();

        this.lambertianTextureID = this.createCubemapTexture(false);
        this.ggxTextureID = this.createCubemapTexture(true);
        this.sheenTextureID = this.createCubemapTexture(true);

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.ggxTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.sheenTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

        this.mipmapLevels = Math.floor(Math.log2(this.textureSize))+1;
    }

    filterAll()
    {
        this.panoramaToCubeMap();
        this.cubeMapToLambertian();
        this.cubeMapToGGX();
        this.cubeMapToSheen();

        this.gl.bindFramebuffer(  this.gl.FRAMEBUFFER, null);
    }

    panoramaToCubeMap()
    {
        for(var i = 0; i < 6; ++i)
        {
            this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
            var side = i;
            this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_CUBE_MAP_POSITIVE_X+side, this.cubemapTextureID, 0);

            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);

            this.gl.viewport(0, 0,  this.textureSize,  this.textureSize);

            this.gl.clearColor(1.0, 0.0, 0.0, 0.0);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT| this.gl.DEPTH_BUFFER_BIT);

            const vertexHash = this.shaderCache.selectShader("fullscreen.vert", []);
            const fragmentHash = this.shaderCache.selectShader("panorama_to_cubemap.frag", []);

            var shader = this.shaderCache.getShaderProgram(fragmentHash, vertexHash);
            this.gl.useProgram(shader.program);

            //  TEXTURE0 = active.
            this.gl.activeTexture(this.gl.TEXTURE0+0);

            // Bind texture ID to active texture
            this.gl.bindTexture(this.gl.TEXTURE_2D, this.inputTextureID);

            // map shader uniform to texture unit (TEXTURE0)
            const location = this.gl.getUniformLocation(shader.program,"u_panorama");
            this.gl.uniform1i(location, this.gl.TEXTURE0+0); // texture unit 0 (TEXTURE0)

            shader.updateUniform("u_currentFace", i);

            //fullscreen triangle
            this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
        }

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

    }

    applyFilter(
        distribution,
        roughness,
        targetMipLevel,
        targetTexture)
    {
        var currentTextureSize =  this.textureSize>>(targetMipLevel);

        for(var i = 0; i < 6; ++i)
        {

            this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
            var side = i;
            this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_CUBE_MAP_POSITIVE_X+side, targetTexture, targetMipLevel);

            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, targetTexture);

            this.gl.viewport(0, 0, currentTextureSize, currentTextureSize);

            this.gl.clearColor(1.0, 0.0, 0.0, 0.0);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT| this.gl.DEPTH_BUFFER_BIT);

            const vertexHash = this.shaderCache.selectShader("fullscreen.vert", []);
            const fragmentHash = this.shaderCache.selectShader("ibl_filtering.frag", []);

            var shader = this.shaderCache.getShaderProgram(fragmentHash, vertexHash);
            this.gl.useProgram(shader.program);

            //  TEXTURE0 = active.
            this.gl.activeTexture(this.gl.TEXTURE0+0);

            // Bind texture ID to active texture
            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);

            // map shader uniform to texture unit (TEXTURE0)
            const location = this.gl.getUniformLocation(shader.program,"u_cubemapTexture");
            this.gl.uniform1i(location, 0); // texture unit 0

            shader.updateUniform("u_roughness", roughness);
            shader.updateUniform("u_sampleCount", this.sampleCount)
            shader.updateUniform("u_width", this.textureSize);
            shader.updateUniform("u_lodBias", this.lodBias);
            shader.updateUniform("u_distribution", distribution);
            shader.updateUniform("u_currentFace", i);

            //fullscreen triangle
            this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);

        }

    }

    cubeMapToLambertian()
    {
        this.applyFilter(
            0,
            0.0,
            0,
            this.lambertianTextureID);
    }

    cubeMapToGGX()
    {
        for(var currentMipLevel = 0; currentMipLevel < this.mipmapLevels; ++currentMipLevel)
        {
            const roughness =  (currentMipLevel) /  (this.mipmapLevels - 1);
            this.applyFilter(
                1,
                roughness,
                currentMipLevel,
                this.ggxTextureID);
        }
    }

    cubeMapToSheen()
    {
        for(var currentMipLevel = 0; currentMipLevel < this.mipmapLevels; ++currentMipLevel)
        {
            const roughness =  (currentMipLevel) /  (this.mipmapLevels - 1);
            this.applyFilter(
                2,
                roughness,
                currentMipLevel,
                this.sheenTextureID);
        }
    }

    destroy()
    {
        this.shaderCache.destroy();
    }
}

export { iblSampler };
<|endoftext|>


<commit_before>var GroupBuilder = (function(){
    var initialGroups = {};
    var externalGroups = {};
    var iteratedGroups = [];
    var _strategy = "byMaximumSize";
    var _presentStudentsOnly = false;
    var _parameters = {
        byTotalGroups:5,
        byMaximumSize:4
    };
    var renderMember = function(member){
        return $("<div />",{
            class:"groupBuilderMember",
            text:member
        });
    };
    var groupSetKey = function(groupSet){
        var rel = groupSet.foreignRelationship;
        if(rel){
            return sprintf("%s@%s",rel.key,rel.system);
        }
        return groupSet.name;
    }
    var flatInitialGroups = function(){
        return _.flatMap(initialGroups,function(groupSet){
            return _.map(groupSet.groups,function(group){
                var r = {};
                _.each(group.members,function(m){
                    r[m.name] = true;
                });
                return r;
            });
        });
    };
    var renderExternalGroups = function(){
        var container = $(".jAlert .groupSlideDialog");
        var importV = container.find(".importGroups").empty();

        $("<input />",{
            type:"radio",
            name:"groupSetSelector",
            id:"clearGroups"
        }).on("click",function(){
            initialGroups = {};
            doSimulation();
        }).appendTo(importV).prop("checked",true);
        $("<label />",{
            for:"clearGroups"
                }).append($("<span />",{
                    class:"icon-txt",
                    text:"No long-term groups"
                })).appendTo(importV);

        var groupId = 0;
        console.log(externalGroups);
        _.each(externalGroups,function(orgUnit){
            _.each(orgUnit,function(groupCat){
                var ou = groupCat.orgUnit;
                if(ou){
                    var ouV = $("<div />",{
                    }).appendTo(importV);
                    var groupSet = groupCat.groupSet;
                    var groupSetV = $("<div />",{
                    }).appendTo(ouV);
                    var groupSetHeader = $("<div />",{
                        class:"flex-container-responsive"
                    }).appendTo(groupSetV);
                    var inputId = sprintf("structuralGroup_%s",groupId);
                    var cacheKey = groupSetKey(groupSet);
                    var inputV = $("<input />",{
                        type:"radio",
                        name:"groupSetSelector",
                        id:inputId
                    }).on("click",function(){
                        if(cacheKey in initialGroups){
                            delete initialGroups[cacheKey];
                        }
                        else{
                            initialGroups = {cacheKey:groupSet};
                        }
                        doSimulation();
                    }).appendTo(groupSetHeader);
                    inputV.prop("checked",cacheKey in initialGroups);
                    $("<label />",{
                        for:inputId
                    }).append($("<span />",{
                        class:"icon-txt",
                        text:sprintf("Copy %s from %s",groupSet.name,ou.name)
                    })).appendTo(groupSetHeader);

                    _.each(groupCat.groups,function(group){
                        var groupV = $("<div />",{
                            class:"groupBuilderGroup"
                        }).appendTo(groupSetV);
                        _.each(group.members,function(member){
                            renderMember(member.name).appendTo(groupV);
                        });
                    });
                    groupId++;
                }
            });
        });
    }
    var simulate = function(strategy,parameter,presentStudentsOnly){
        console.log(strategy,parameter,presentStudentsOnly);
        var groups = flatInitialGroups();
	console.log("flatInitialGroups",groups);
        var participants;
        switch(presentStudentsOnly){
        case "allPresent": participants = Participants.getParticipants();
            break;
        default: participants = Participants.getPossibleParticipants();
            break;
        }
        var attendees = _.without(participants, Conversations.getCurrentConversation().author);
        attendees = _.omitBy(attendees,function(k){
            return _.some(groups,function(g){
                return k in g;
            });
        });
        var filler;
        switch(strategy){
        case "byTotalGroups":
            for(var i = groups.length; i < parseInt(parameter);i++){
                groups.push({});
            }
            filler = function(p){
                var targetG = _.sortBy(groups,function(g){return _.keys(g).length})
                targetG[0][p] = true;
            }
            break;
        case "byMaximumSize":
            filler = function(p){
                var targetG = _.find(groups,function(g){
                    return _.keys(g).length < parseInt(parameter);
                });
                if(!targetG){
                    targetG = {};
                    groups.push(targetG);
                }
                targetG[p] = true;
            }
            break;
        }
        _.each(attendees,filler);
        console.log(groups);
        return _.map(groups,_.keys);
    }
    var renderGroupScopes = function(container){
        _.each([
            ["Show me all my enrolled students","allEnrolled"],
            ["Only show me students who are here right now","allPresent"]],function(params){
                $("<option />",{
                    text:params[0],
                    value:params[1]
                }).appendTo(container);
            });
    }
    var renderStrategies = function(container){
        _.each([
            ["there are","byTotalGroups"],
            ["each has","byMaximumSize"]],function(params){
                $("<option />",{
                    text:params[0],
                    value:params[1]
                }).appendTo(container);
            });
    }
    var render = function(){
        var container = $("#groupsPopup");
        var composition = $("#groupComposition");
        var importV = container.find(".importGroups").empty();
        var groupsV = container.find(".groups").empty();
        var slide = Conversations.getCurrentSlide();
        if(slide){
            _.each(slide.groupSets,function(groupSet){
                _.each(_.sortBy(groupSet.groups,"title"),function(group){
                    var g = $("<div />",{
                        class:"groupBuilderGroup"
                    }).appendTo(groupsV).droppable({
                        drop:function(e,ui){
                            var members = $(ui.draggable).find(".groupBuilderMember").addBack(".groupBuilderMember");
                            console.log("Dropped",$(ui.draggable),members);
                            _.each(members,function(memberV){
                                var member = $(memberV).text();
                                console.log("Drop member",member);
                                if(! _.includes(group.members,member)){
                                    _.each(groupSet.groups,function(g){
                                        g.members = _.without(g.members,member);
                                    });
                                    group.members.push(member);
                                    render();
                                    Conversations.overrideAllocation(slide);
                                }
                            });
                            e.preventDefault();
                        }
                    });
                    $("<div />",{
                        class:"title",
                        text:sprintf("Group %s",group.title)
                    }).appendTo(g);
                    _.each(group.members,function(member){
                        renderMember(member).appendTo(g).draggable();
                    });
                });
            });
        }
    };
    var doSimulation = function(simulated){
        var container = $(".jAlert .groupSlideDialog");
        var groupsV = container.find(".groups");
        simulated = simulated || simulate(_strategy,_parameters[_strategy],_presentStudentsOnly);
        groupsV.empty();
        _.each(simulated,function(group){
            console.log(group);
            var g = $("<div />",{
                class:"groupBuilderGroup ghost"
            });
            _.each(group,function(member){
                renderMember(member).draggable().appendTo(g);
            });
            g.droppable({
                drop:function(e,ui){
                    var member = $(ui.draggable).text();
                    _.each(simulated,function(gr){
                        if(_.includes(gr,member)){
                            gr.splice(gr.indexOf(member),1);
                        }
                    });
                    group.push(member);
                    iteratedGroups = simulated;
                    doSimulation(simulated);
                    e.preventDefault();
                }
            });
            g.appendTo(groupsV);
        });
	iteratedGroups = simulated;
    };
    var showAddGroupSlideDialogFunc = function(){
        getGroupsProviders();
        var container = $("#groupSlideDialog").clone().show();
        var jAlert = $.jAlert({
            title:"Add Group page",
            width:"75%",
            content:container[0].outerHTML,
            btns:[{
                text:"Add page",
                theme:'green',
                closeAlert:true,
                onClick:function(){
                    var seed = iteratedGroups.length > 0 ? iteratedGroups : _.map(flatInitialGroups(),_.keys);
                    Conversations.addGroupSlide(_strategy, parseInt(_parameters[_strategy]), seed);
                    initialGroups = {};
                    iteratedGroups = [];
                    externalGroups = {};
                }
            }]
        });
        container = $(".jAlert .groupSlideDialog");
        var strategySelect = container.find(".strategySelect");
        var parameterSelect = container.find(".parameterSelect");
        var groupScope = container.find(".presentStudentsOnly");
        var groupsV = container.find(".groups");
        renderStrategies(strategySelect);
        renderGroupScopes(groupScope);

        container.on("change",".presentStudentsOnly",function(){
            _presentStudentsOnly = $(this).val();
            console.log(_presentStudentsOnly);
            doSimulation();
        });
        container.on("change",".strategySelect",function(){
            _strategy = $(this).val();
            console.log("Strategy set:",_strategy);
            parameterSelect.empty();
            switch(_strategy){
            case "byTotalGroups":
                _.each(_.range(2,10),function(i){
                    $("<option />",{
                        text:sprintf("%s groups in total",i),
                        value:i.toString()
                    }).appendTo(parameterSelect);
                });
                parameterSelect.val(_parameters[_strategy]).change();
                break;
            case "byMaximumSize":
                _.each(_.range(1,10),function(i){
                    $("<option />",{
                        text:i == 1 ? "only one member" : sprintf("at most %s members",i),
                        value:i.toString()
                    }).appendTo(parameterSelect);
                });
                parameterSelect.val(_parameters[_strategy]).change();
                break;
            }
        });
        container.on("change",".parameterSelect",function(){
            console.log("change parameter",_parameters);
            _parameters[_strategy] = $(this).val();
            doSimulation();
        });
        strategySelect.val(_strategy).change();
    };
    Progress.groupProvidersReceived["GroupBuilder"] = function(args){
        var select = $(".jAlert .ouSelector").empty();
        $("<option />",{
            text:"no starting groups",
            value:"NONE",
            selected:true
        }).appendTo(select);
        _.each(args.groupsProviders,function(provider){
            $("<option />",{
                text:provider,
                value:provider
            }).appendTo(select);
        });
        select.on("change",function(){
            var choice = $(this).val();
            if(choice != "NONE"){
                getOrgUnitsFromGroupProviders(choice);
            }
        });
    };
    Progress.groupsReceived["GroupBuilder"] = function(args){
        var byOrgUnit = externalGroups[args.orgUnit.name];
        if (byOrgUnit === undefined){
            byOrgUnit = {};
            externalGroups[args.orgUnit.name] = byOrgUnit;
        }
        byOrgUnit[args.groupSet.name] = args;
        renderExternalGroups();
    };
    Progress.onBackstageShow["GroupBuilder"] = function(backstage){
        if(backstage == "groups"){
            render();
        }
    };
    Progress.currentSlideJidReceived["GroupBuilder"] = function(){
        if(currentBackstage == "groups"){
            render();
        }
    };
    Progress.conversationDetailsReceived["GroupBuilder"] = function(){
        if(currentBackstage == "groups"){
            render();
        }
    };
    return {
        showAddGroupSlideDialog:showAddGroupSlideDialogFunc,
        getExternalGroups:function(){
            return externalGroups;
        }
    };
})();
<commit_msg>METL-360 fixes in progress<commit_after>var GroupBuilder = (function(){
    var initialGroups = {};
    var externalGroups = {};
    var iteratedGroups = [];
    var _strategy = "byMaximumSize";
    var _presentStudentsOnly = false;
    var _parameters = {
        byTotalGroups:5,
        byMaximumSize:4
    };
    var renderMember = function(member){
        return $("<div />",{
            class:"groupBuilderMember",
            text:member
        });
    };
    var groupSetKey = function(groupSet){
        var rel = groupSet.foreignRelationship;
        if(rel){
            return sprintf("%s@%s",rel.key,rel.system);
        }
        return groupSet.name;
    }
    var flatInitialGroups = function(){
        return _.flatMap(initialGroups,function(groupSet){
            return _.map(groupSet.groups,function(group){
                var r = {};
                _.each(group.members,function(m){
                    r[m.name] = true;
                });
                return r;
            });
        });
    };
    var renderExternalGroups = function(){
        var container = $(".jAlert .groupSlideDialog");
        var importV = container.find(".importGroups").empty();

        $("<input />",{
            type:"radio",
            name:"groupSetSelector",
            id:"clearGroups"
        }).on("click",function(){
            initialGroups = {};
	    iteratedGroups = [];
            doSimulation();
        }).appendTo(importV).prop("checked",true);
        $("<label />",{
            for:"clearGroups"
                }).append($("<span />",{
                    class:"icon-txt",
                    text:"No long-term groups"
                })).appendTo(importV);

        var groupId = 0;
        console.log(externalGroups);
        _.each(externalGroups,function(orgUnit){
            _.each(orgUnit,function(groupCat){
                var ou = groupCat.orgUnit;
                if(ou){
                    var ouV = $("<div />",{
                    }).appendTo(importV);
                    var groupSet = groupCat.groupSet;
                    var groupSetV = $("<div />",{
                    }).appendTo(ouV);
                    var groupSetHeader = $("<div />",{
                        class:"flex-container-responsive"
                    }).appendTo(groupSetV);
                    var inputId = sprintf("structuralGroup_%s",groupId);
                    var cacheKey = groupSetKey(groupSet);
                    var inputV = $("<input />",{
                        type:"radio",
                        name:"groupSetSelector",
                        id:inputId
                    }).on("click",function(){
                        if(cacheKey in initialGroups){
                            delete initialGroups[cacheKey];
                        }
                        else{
                            initialGroups = {cacheKey:groupSet};
                        }
			console.log("Toggling",cacheKey);
			iteratedGroups = [];
                        doSimulation(flatInitialGroups());
                    }).appendTo(groupSetHeader);
                    inputV.prop("checked",cacheKey in initialGroups);
                    $("<label />",{
                        for:inputId
                    }).append($("<span />",{
                        class:"icon-txt",
                        text:sprintf("Copy %s from %s",groupSet.name,ou.name)
                    })).appendTo(groupSetHeader);

                    _.each(groupCat.groups,function(group){
                        var groupV = $("<div />",{
                            class:"groupBuilderGroup"
                        }).appendTo(groupSetV);
                        _.each(group.members,function(member){
                            renderMember(member.name).appendTo(groupV);
                        });
                    });
                    groupId++;
                }
            });
        });
    }
    var simulate = function(strategy,parameter,presentStudentsOnly){
        console.log(strategy,parameter,presentStudentsOnly);
        var groups = flatInitialGroups();
	console.log("flatInitialGroups",groups);
        var participants;
        switch(presentStudentsOnly){
        case "allPresent": participants = Participants.getParticipants();
            break;
        default: participants = Participants.getPossibleParticipants();
            break;
        }
        var attendees = _.without(participants, Conversations.getCurrentConversation().author);
        attendees = _.omitBy(attendees,function(k){
            return _.some(groups,function(g){
                return k in g;
            });
        });
        var filler;
        switch(strategy){
        case "byTotalGroups":
            for(var i = groups.length; i < parseInt(parameter);i++){
                groups.push({});
            }
            filler = function(p){
                var targetG = _.sortBy(groups,function(g){return _.keys(g).length})
                targetG[0][p] = true;
            }
            break;
        case "byMaximumSize":
            filler = function(p){
                var targetG = _.find(groups,function(g){
                    return _.keys(g).length < parseInt(parameter);
                });
                if(!targetG){
                    targetG = {};
                    groups.push(targetG);
                }
                targetG[p] = true;
            }
            break;
        }
        _.each(attendees,filler);
        console.log(groups);
        return _.map(groups,_.keys);
    }
    var renderGroupScopes = function(container){
        _.each([
            ["Show me all my enrolled students","allEnrolled"],
            ["Only show me students who are here right now","allPresent"]],function(params){
                $("<option />",{
                    text:params[0],
                    value:params[1]
                }).appendTo(container);
            });
    }
    var renderStrategies = function(container){
        _.each([
            ["there are","byTotalGroups"],
            ["each has","byMaximumSize"]],function(params){
                $("<option />",{
                    text:params[0],
                    value:params[1]
                }).appendTo(container);
            });
    }
    var render = function(){
        var container = $("#groupsPopup");
        var composition = $("#groupComposition");
        var importV = container.find(".importGroups").empty();
        var groupsV = container.find(".groups").empty();
        var slide = Conversations.getCurrentSlide();
        if(slide){
            _.each(slide.groupSets,function(groupSet){
                _.each(_.sortBy(groupSet.groups,"title"),function(group){
                    var g = $("<div />",{
                        class:"groupBuilderGroup"
                    }).appendTo(groupsV).droppable({
                        drop:function(e,ui){
                            var members = $(ui.draggable).find(".groupBuilderMember").addBack(".groupBuilderMember");
                            console.log("Dropped",$(ui.draggable),members);
                            _.each(members,function(memberV){
                                var member = $(memberV).text();
                                console.log("Drop member",member);
                                if(! _.includes(group.members,member)){
                                    _.each(groupSet.groups,function(g){
                                        g.members = _.without(g.members,member);
                                    });
                                    group.members.push(member);
                                    render();
                                    Conversations.overrideAllocation(slide);
                                }
                            });
                            e.preventDefault();
                        }
                    });
                    $("<div />",{
                        class:"title",
                        text:sprintf("Group %s",group.title)
                    }).appendTo(g);
                    _.each(group.members,function(member){
                        renderMember(member).appendTo(g).draggable();
                    });
                });
            });
        }
    };
    var doSimulation = function(simulated){
        var container = $(".jAlert .groupSlideDialog");
        var groupsV = container.find(".groups");
        simulated = simulated || simulate(_strategy,_parameters[_strategy],_presentStudentsOnly);
        groupsV.empty();
        _.each(simulated,function(group){
            console.log(group);
            var g = $("<div />",{
                class:"groupBuilderGroup ghost"
            });
            _.each(group,function(member){
                renderMember(member).draggable().appendTo(g);
            });
            g.droppable({
                drop:function(e,ui){
                    var member = $(ui.draggable).text();
                    _.each(simulated,function(gr){
                        if(_.includes(gr,member)){
                            gr.splice(gr.indexOf(member),1);
                        }
                    });
                    group.push(member);
                    iteratedGroups = simulated;
                    doSimulation(simulated);
                    e.preventDefault();
                }
            });
            g.appendTo(groupsV);
        });
	iteratedGroups = simulated;
    };
    var showAddGroupSlideDialogFunc = function(){
        getGroupsProviders();
        var container = $("#groupSlideDialog").clone().show();
        var jAlert = $.jAlert({
            title:"Add Group page",
            width:"75%",
            content:container[0].outerHTML,
            btns:[{
                text:"Add page",
                theme:'green',
                closeAlert:true,
                onClick:function(){
                    var seed = iteratedGroups.length > 0 ? iteratedGroups : _.map(flatInitialGroups(),_.keys);
                    Conversations.addGroupSlide(_strategy, parseInt(_parameters[_strategy]), seed);
                    initialGroups = {};
                    iteratedGroups = [];
                    externalGroups = {};
                }
            }]
        });
        container = $(".jAlert .groupSlideDialog");
        var strategySelect = container.find(".strategySelect");
        var parameterSelect = container.find(".parameterSelect");
        var groupScope = container.find(".presentStudentsOnly");
        var groupsV = container.find(".groups");
        renderStrategies(strategySelect);
        renderGroupScopes(groupScope);

        container.on("change",".presentStudentsOnly",function(){
            _presentStudentsOnly = $(this).val();
            console.log(_presentStudentsOnly);
            doSimulation();
        });
        container.on("change",".strategySelect",function(){
            _strategy = $(this).val();
            console.log("Strategy set:",_strategy);
            parameterSelect.empty();
            switch(_strategy){
            case "byTotalGroups":
                _.each(_.range(2,10),function(i){
                    $("<option />",{
                        text:sprintf("%s groups in total",i),
                        value:i.toString()
                    }).appendTo(parameterSelect);
                });
                parameterSelect.val(_parameters[_strategy]).change();
                break;
            case "byMaximumSize":
                _.each(_.range(1,10),function(i){
                    $("<option />",{
                        text:i == 1 ? "only one member" : sprintf("at most %s members",i),
                        value:i.toString()
                    }).appendTo(parameterSelect);
                });
                parameterSelect.val(_parameters[_strategy]).change();
                break;
            }
        });
        container.on("change",".parameterSelect",function(){
            console.log("change parameter",_parameters);
            _parameters[_strategy] = $(this).val();
            doSimulation();
        });
        strategySelect.val(_strategy).change();
    };
    Progress.groupProvidersReceived["GroupBuilder"] = function(args){
        var select = $(".jAlert .ouSelector").empty();
        $("<option />",{
            text:"no starting groups",
            value:"NONE",
            selected:true
        }).appendTo(select);
        _.each(args.groupsProviders,function(provider){
            $("<option />",{
                text:provider,
                value:provider
            }).appendTo(select);
        });
        select.on("change",function(){
            var choice = $(this).val();
            if(choice != "NONE"){
                getOrgUnitsFromGroupProviders(choice);
            }
        });
    };
    Progress.groupsReceived["GroupBuilder"] = function(args){
        var byOrgUnit = externalGroups[args.orgUnit.name];
        if (byOrgUnit === undefined){
            byOrgUnit = {};
            externalGroups[args.orgUnit.name] = byOrgUnit;
        }
        byOrgUnit[args.groupSet.name] = args;
        renderExternalGroups();
    };
    Progress.onBackstageShow["GroupBuilder"] = function(backstage){
        if(backstage == "groups"){
            render();
        }
    };
    Progress.currentSlideJidReceived["GroupBuilder"] = function(){
        if(currentBackstage == "groups"){
            render();
        }
    };
    Progress.conversationDetailsReceived["GroupBuilder"] = function(){
        if(currentBackstage == "groups"){
            render();
        }
    };
    return {
        showAddGroupSlideDialog:showAddGroupSlideDialogFunc,
        getExternalGroups:function(){
            return externalGroups;
        }
    };
})();
<|endoftext|>


<commit_before>/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs        = require('fs');

describe('Test package.json and io-package.json', function() {
    it('Test package files', function (done) {
        console.log();

        var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
        var ioPackage = JSON.parse(fileContentIOPackage);

        var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
        var npmPackage = JSON.parse(fileContentNPMPackage);

        expect(ioPackage).to.be.an('object');
        expect(npmPackage).to.be.an('object');

        expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
        expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;

        expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);

        if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
            console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
            console.log();
        }

        expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
        expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;

        if (ioPackage.common.name.indexOf('template') !== 0) {
            if (Array.isArray(ioPackage.common.authors)) {
                expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
                if (ioPackage.common.authors.length === 1) {
                    expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
                }
            }
            else {
                expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
            }
        }
        else {
            console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
            console.log();
        }
        expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
        expect(fs.existsSync(__dirname + '/../LICENSE'), 'ERROR: LICENSE needs to exist! Please create one.').to.be.true;
        if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
            console.log('WARNING: titleLang is not existing in io-package.json. Please add');
            console.log();
        }
        if (
            ioPackage.common.title.indexOf('iobroker') !== -1 ||
            ioPackage.common.title.indexOf('ioBroker') !== -1 ||
            ioPackage.common.title.indexOf('adapter') !== -1 ||
            ioPackage.common.title.indexOf('Adapter') !== -1
        ) {
            console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');
            console.log();
        }

        if (ioPackage.common.name.indexOf('vis-') !== 0) {
            if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
                console.log('WARNING: Admin3 support is missing! Please add it');
                console.log();
            }
            if (ioPackage.common.materialize) {
                expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
            }
        }

        expect(fs.existsSync(__dirname + '/../LICENSE'), 'A LICENSE must exist');
        var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
        expect(fileContentReadme.indexOf('## License'), 'The README.md needs to have a section ## License').not.equal(-1);
        expect(fileContentReadme.indexOf('## Changelog'), 'The README.md needs to have a section ## Changelog').not.equal(-1);
        expect(fileContentReadme.indexOf('## Changelog'), 'The README.md needs to have a section ## License').to.be.below(fileContentReadme.indexOf('## License'));
        done();
    });
});
<commit_msg>update basic package-file testing<commit_after>/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs        = require('fs');

describe('Test package.json and io-package.json', function() {
    it('Test package files', function (done) {
        console.log();

        var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
        var ioPackage = JSON.parse(fileContentIOPackage);

        var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
        var npmPackage = JSON.parse(fileContentNPMPackage);

        expect(ioPackage).to.be.an('object');
        expect(npmPackage).to.be.an('object');

        expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
        expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;

        expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);

        if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
            console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
            console.log();
        }

        expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
        expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;

        if (ioPackage.common.name.indexOf('template') !== 0) {
            if (Array.isArray(ioPackage.common.authors)) {
                expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
                if (ioPackage.common.authors.length === 1) {
                    expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
                }
            }
            else {
                expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
            }
        }
        else {
            console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
            console.log();
        }
        expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
        expect(fs.existsSync(__dirname + '/../LICENSE'), 'ERROR: LICENSE needs to exist! Please create one.').to.be.true;
        if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
            console.log('WARNING: titleLang is not existing in io-package.json. Please add');
            console.log();
        }
        if (
            ioPackage.common.title.indexOf('iobroker') !== -1 ||
            ioPackage.common.title.indexOf('ioBroker') !== -1 ||
            ioPackage.common.title.indexOf('adapter') !== -1 ||
            ioPackage.common.title.indexOf('Adapter') !== -1
        ) {
            console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');
            console.log();
        }

        if (ioPackage.common.name.indexOf('vis-') !== 0) {
            if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
                console.log('WARNING: Admin3 support is missing! Please add it');
                console.log();
            }
            if (ioPackage.common.materialize) {
                expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
            }
        }

        expect(fs.existsSync(__dirname + '/../LICENSE'), 'A LICENSE must exist');
        var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
        if (fileContentReadme.indexOf('## License') === -1) {
            console.log('Warning: The README.md should have a section ## License');
            console.log();
        }
        if (fileContentReadme.indexOf('## Changelog') === -1) {
            console.log('Warning: The README.md should have a section ## Changelog');
            console.log();
        }
        done();
    });
});
<|endoftext|>