<commit_before>import test from 'ava';
import users from '../lib/users';
import userRoutes from '../lib/routes/users';

const model = {
  'name': 'Users',
  'description': 'Test users description',
  'id': 'users',
  'attributes': [
    {
      'type': 'email',
      'id': 'email',
      'name': 'Email',
      'description': 'Your email is your username',
    },
  ],
};

test('Exports exist', t => {
  t.is(typeof userRoutes, 'function', 'Submodule `routes` exists and is a function');
  t.is(typeof users, 'function', 'Submodule `model`, is the primary for `users`, and is a function');
});

test('Users, with config, merged with correct param', t => {
  return users(model)
    .then(result => {
      t.is(result[0].name, 'Users', 'Get users content type name');
      t.is(result[0].description, 'Test users description', 'Get users content type desc');
      t.is(result[0].id, 'users', 'Get users content type id');
      t.is(result[0].attributes[0].name, 'Email', 'Email content type');
    });
});

test('Users merged with correct param', t => {
  return users()
    .then(result => {
      t.is(result[0].name, 'Users', 'Get users content type name');
      t.is(result[0].description, 'An individual user', 'Get users content type desc');
      t.is(result[0].id, 'users', 'Get users content type id');
      t.is(result[0].attributes[1].name, 'Password', 'Password attribute name');
      t.is(result[0].attributes[1].id, 'password', 'Password attribute id');
    });
});
<commit_msg>:boom: tests have identifier; PR is breaking change<commit_after>import test from 'ava';
import users from '../lib/users';
import userRoutes from '../lib/routes/users';

const model = {
  'name': 'Users',
  'description': 'Test users description',
  'id': 'users',
  'identifier': 'email',
  'attributes': [
    {
      'type': 'email',
      'id': 'email',
      'name': 'Email',
      'description': 'Your email is your username',
    },
  ],
};

test('Exports exist', t => {
  t.is(typeof userRoutes, 'function', 'Submodule `routes` exists and is a function');
  t.is(typeof users, 'function', 'Submodule `model`, is the primary for `users`, and is a function');
});

test('Users, with config, merged with correct param', t => {
  return users(model)
    .then(result => {
      t.is(result[0].name, 'Users', 'Get users content type name');
      t.is(result[0].description, 'Test users description', 'Get users content type desc');
      t.is(result[0].id, 'users', 'Get users content type id');
      t.is(result[0].attributes[0].name, 'Email', 'Email content type');
    });
});

test('Users merged with correct param', t => {
  return users()
    .then(result => {
      t.is(result[0].name, 'Users', 'Get users content type name');
      t.is(result[0].description, 'An individual user', 'Get users content type desc');
      t.is(result[0].id, 'users', 'Get users content type id');
      t.is(result[0].attributes[1].name, 'Password', 'Password attribute name');
      t.is(result[0].attributes[1].id, 'password', 'Password attribute id');
    });
});
<|endoftext|>


<commit_before>'use strict';

var http = require('http');
var os = require('os');
var ifaces = os.networkInterfaces();
var request = require('request');
var EventSource = require('eventsource');
var fs = require('fs');
var util = require('util');
var url = require('url');
var querystring = require('querystring');

var log_file = fs.createWriteStream('error.log', {flags : 'w'});

var urlToThinkAutomatic = 'https://api.thinkautomatic.io/v1/';
var deviceConf;
var serverPort;
var sceneTriggerData = {};
var sceneSelectHistory = [];
var rooms = [];

function logError(err) {
  console.log(util.format(err) + '\n');
  log_file.write(util.format(err) + '\n');
}

function directUrl() {
  var ipAddress;

  if (serverPort) {
    Object.keys(ifaces).forEach(function (ifname) {
      ifaces[ifname].forEach(function (iface) {
        if ('IPv4' !== iface.family || iface.internal !== false) {
          // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
          return;
        }

        ipAddress = iface.address;
      });
    });

    return "http://" + ipAddress + ":" + serverPort.toString() + "/";
  }
  else
  {
    return null;
  }
}

function updateThinkDeviceConf(newData, cb) {
  var parsedData;

  try
  {
    parsedData = JSON.parse(newData);

    if (parsedData['deviceId']) {
      deviceConf = parsedData;
      fs.writeFile('device.conf', JSON.stringify(deviceConf), function (err) {
      if (err)
        logError(err);
      cb(err);
      });
    }
    else {
      throw newData;
    }
  }
  catch (err) {
    logError(err);
    cb(err);
    return;
  }
}

function scheduleKeepAlive(err) {
  setTimeout(function() { sendKeepAlive(); }, 15 * 60 * 1000);
}

function sendKeepAlive() {
  console.log('Sending keepAlive');
  request.post({url: urlToThinkAutomatic + 'devices/' + deviceConf['deviceId'] + '/keepAlive',
                qs: { access_token: deviceConf['deviceToken'] },
                form: { directUrl: directUrl() }}, function(err,httpResponse,body) {
    updateThinkDeviceConf(body, scheduleKeepAlive);
  });
}

function updateSceneTriggerData(newData) {
  sceneTriggerData = newData;
  fs.writeFile('sceneData.conf', JSON.stringify(sceneTriggerData), function (err) {
    if (err)
      logError(err);
  });
}

function handleErr(err, res) {
  res.writeHead(200, {"Content-Type": "text/html"});
  res.end(util.format(err) + '\n');
}

function handleReq(req, res) {
  var parsedUrl = url.parse(req.url);
  var params = querystring.parse(parsedUrl.query);

  if (parsedUrl.pathname === '/link') {
    if (!params['linkToken']) {
      handleErr("No linkToken provided", res);
    }
    else {
      request.post({url: urlToThinkAutomatic + 'devices/' + deviceConf['deviceId'] + '/link',
                    qs: { access_token: deviceConf['deviceToken'] },
                    form: { linkToken: params['linkToken'] }}, function(err,httpResponse,body) {
        if (err) {
          handleErr(err, res);
        }
        else {
          updateThinkDeviceConf(body, function (err) {
              if (err) {
                handleErr(err, res);
              }
              else {
                if (params['successRedirect']) {
                  res.writeHead(302, {'Location': params['successRedirect']});
                  res.end();
                }
                else {
                  handleErr("Link succeeded but no successRedirect supplied");
                }
              }
            });
        }
      });
    }
  }
  else {
    handleErr("Invalid route", res);
  }
}

var onopen = function () {
  console.log('Connection to platform is opened');
}

var onmessage = function(data) {
  console.log('Received from platform:');
  console.log(data);
}

var onerror = function() {
  console.log('Error receiving data from platform');
}

function on(eventName, cb) {
  switch(eventName){
    case 'open':
      onopen = cb;
      break;
    case 'message':
      onmessage = cb;
      break;
    case 'error':
      onerror = cb;
      break;
  }
}

function startEventSource(cb)
{
  var src = new EventSource(deviceConf['eventStreamUrl']);

  src.onopen = function(){
    onopen();
  };
  src.onmessage = function(e) {
    try
    {
      var parsedData = JSON.parse(e.data);

      if (parsedData['sceneTriggerData'])
        updateSceneTriggerData(parsedData);
      else
        onmessage(parsedData);
    }
    catch (err)
    {
      logError(err);
    }
  };
  src.onerror = function() {
    onerror();
  };

  sendKeepAlive();

  if (typeof cb === 'function')
    cb();
}

function run(cb) {
  if (deviceConf['homeId']) {
    console.log('Device already associated with a home, not starting http server for discovery');
    startEventSource(cb);
  }
  else {
    // Configure our HTTP server
    var server = http.createServer(function (req, res) {
      handleReq(req, res);
    });

    server.on('error', function(e) {
      logError('Unable to start discover server on port ' + serverPort.toString());
      serverPort = serverPort + 1;
      run(cb);
    });

    if (serverPort < 3300) {
      console.log('Attempting to start discovery server on port ' + serverPort.toString());
      server.listen(serverPort, function(){
        console.log('Discovery http server running at ' + directUrl());
        startEventSource(cb);
      });
    }
    else {
      logError('Unable to start discover server, exiting.');
    }
  }
}

function connect(deviceProperties, cb)
{
  serverPort = 3205;

  deviceProperties['directUrl'] = directUrl();

  fs.readFile('sceneData.conf', 'utf8', function (err,data) {
    if (!err) {
      sceneTriggerData = JSON.parse(data);
    }
    fs.readFile('device.conf', 'utf8', function (err,data) {
      if (err)
      {
        console.log('Creating new device.');
        request.post({url: urlToThinkAutomatic + 'devices',
                    form: deviceProperties }, function(err,httpResponse,body) {
          updateThinkDeviceConf(body, function () { run(cb); });
        });
      }
      else
      {
        deviceConf = JSON.parse(data);
        console.log('Updating device.');
        request.patch({url: urlToThinkAutomatic + 'devices/' + deviceConf['deviceId'],
                    qs: { access_token: deviceConf['deviceToken'] },
                    form: deviceProperties }, function(err,httpResponse,body) {
          updateThinkDeviceConf(body, function () { run(cb); });
        });
      }
    });
  });
}

function matchElem(elem, selector) {
  var oneMatch = false;
  var keyArray = Object.keys(selector);

  for (var i = 0; i < keyArray.length; i++) {
    var key = keyArray[i];
    if (elem[key]) {
      if (elem[key] !== selector[key])
        return false;
      oneMatch = true;
    }
  };
  return oneMatch;
}

function findElem(elemArray, selector) {
  for (var i = 0; i < elemArray.length; i++) {
    if (matchElem(elemArray[i], selector))
      return elemArray[i];
  };
  return null;
}

function purgeRoomFromSceneCache(roomId) {
  var room = findElem(rooms, {roomId:roomId});
  var currSceneId = 0;

  if (room !== null)
    currSceneId = room['sceneId'];

  for(var i = sceneSelectHistory.length; i--;) {
    if (sceneSelectHistory[i]["roomId"] == roomId &&
        sceneSelectHistory[i]["sceneId"] !== currSceneId)
      sceneSelectHistory.splice(i, 1);
  }

  console.log('sceneSelectHistory_2');
  console.log(sceneSelectHistory);
}

function setRoomTimeout(roomId, sceneId) {
  var room = findElem(rooms, {roomId:roomId});
  var timeout = setTimeout(function() { purgeRoomFromSceneCache(roomId); }, 20000 );

  if (room == null) {
    rooms.push({ roomId:roomId, sceneId:sceneId, timeout:timeout });
  }
  else {
    clearTimeout(room['timeout']);
    room['timeout'] = timeout;
    room['sceneId'] = sceneId;
  }
}

function selectCachedScene(deviceSelector, deviceProperties, cb) {
  if (sceneTriggerData['sceneTriggerData']) {
    var devices = sceneTriggerData['sceneTriggerData']['devices'];
    if (devices) {
      var device = findElem(devices, deviceSelector);
      if (device && device['actions']) {
        var action = findElem(device['actions'], deviceProperties);
        if (action && action['scenes']) {
          var scenes = action['scenes'];
          for (var i = 0; i < scenes.length; i++) {
            var scene = scenes[i];
            if (findElem(sceneSelectHistory, scene) == null) {
              var fullScene = findElem(sceneTriggerData['sceneTriggerData']['scenes'], scene);
              if (fullScene) {
                fullScene['commands'].forEach(function (command) { onmessage(command); });
                setRoomTimeout(fullScene.roomId, fullScene.sceneId);
                if (fullScene.level == 0)
                  purgeRoomFromSceneCache(fullScene.roomId);
                else
                  sceneSelectHistory.push({"sceneId":fullScene.sceneId, "roomId":fullScene.roomId});
                deviceProperties['sceneId'] = fullScene.sceneId;
                deviceProperties['silent'] = 'true';
                cb(deviceProperties);
                return;
              }
            }
          };
        }
      }
    }
  }
  cb(deviceProperties);
}

function patch(deviceSelector, deviceProperties, cb) {
  // check if deviceSelector was omitted
  if ((typeof deviceProperties === 'function') ||
      (typeof deviceProperties === 'undefined')) {
    cb = deviceProperties;
    deviceProperties = deviceSelector;
    request.patch({url: urlToThinkAutomatic + 'devices/' + deviceConf['deviceId'],
                  qs: { access_token: deviceConf['deviceToken'] },
                  form: deviceProperties }, function(err,httpResponse,body) {
                    if (typeof cb === 'function')
                      cb(body);
                  });
  }
  else {
    selectCachedScene(deviceSelector, deviceProperties, function (devicePropertiesWithScene) {
      devicePropertiesWithScene['hubId'] = deviceConf['deviceId'];
      console.log('devicePropertiesWithScene');
      console.log(devicePropertiesWithScene);
      console.log('sceneSelectHistory');
      console.log(sceneSelectHistory);

      if (typeof deviceSelector === 'number') {
        request.patch({url: urlToThinkAutomatic + 'devices/' + deviceSelector.toString(),
                      qs: { access_token: deviceConf['deviceToken'] },
                      form: devicePropertiesWithScene }, function(err,httpResponse,body) {
                        if (typeof cb === 'function')
                          cb(body);
                      });
      }
      else {
        request.patch({url: urlToThinkAutomatic + 'devices',
                      qs: { access_token: deviceConf['deviceToken'], where: deviceSelector },
                      form: devicePropertiesWithScene }, function(err,httpResponse,body) {
                        if (typeof cb === 'function')
                          cb(body);
                      });
      }
    });
  }
}

module.exports =  {
  connect: connect,
  patch: patch,
  on: on
};

<commit_msg>Added support for local inter-device/hub scene selection to reduce latency<commit_after>'use strict';

var http = require('http');
var os = require('os');
var ifaces = os.networkInterfaces();
var request = require('request');
var EventSource = require('eventsource');
var fs = require('fs');
var util = require('util');
var url = require('url');
var querystring = require('querystring');

var log_file = fs.createWriteStream('error.log', {flags : 'w'});

var urlToThinkAutomatic = 'https://api.thinkautomatic.io/v1/';
var deviceConf;
var serverPort;
var sceneTriggerData = {};
var sceneSelectHistory = [];
var rooms = [];

function logError(err) {
  console.log(util.format(err) + '\n');
  log_file.write(util.format(err) + '\n');
}

function directUrl() {
  var ipAddress;

  if (serverPort) {
    Object.keys(ifaces).forEach(function (ifname) {
      ifaces[ifname].forEach(function (iface) {
        if ('IPv4' !== iface.family || iface.internal !== false) {
          // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
          return;
        }

        ipAddress = iface.address;
      });
    });

    return "http://" + ipAddress + ":" + serverPort.toString() + "/";
  }
  else
  {
    return null;
  }
}

function updateThinkDeviceConf(newData, cb) {
  var parsedData;

  try
  {
    parsedData = JSON.parse(newData);

    if (parsedData['deviceId']) {
      deviceConf = parsedData;
      fs.writeFile('device.conf', JSON.stringify(deviceConf), function (err) {
      if (err)
        logError(err);
      cb(err);
      });
    }
    else {
      throw newData;
    }
  }
  catch (err) {
    logError(err);
    cb(err);
    return;
  }
}

function scheduleKeepAlive(err) {
  setTimeout(function() { sendKeepAlive(); }, 15 * 60 * 1000);
}

function sendKeepAlive() {
  console.log('Sending keepAlive');
  request.post({url: urlToThinkAutomatic + 'devices/' + deviceConf['deviceId'] + '/keepAlive',
                qs: { access_token: deviceConf['deviceToken'] },
                form: { directUrl: directUrl() }}, function(err,httpResponse,body) {
    updateThinkDeviceConf(body, scheduleKeepAlive);
  });
}

function updateSceneTriggerData(newData) {
  sceneTriggerData = newData;
  fs.writeFile('sceneData.conf', JSON.stringify(sceneTriggerData), function (err) {
    if (err)
      logError(err);
  });
}

function handleErr(err, res) {
  res.writeHead(200, {"Content-Type": "text/html"});
  res.end(util.format(err) + '\n');
}

function handleReq(req, res) {
  var parsedUrl = url.parse(req.url);
  var params = querystring.parse(parsedUrl.query);

  if (parsedUrl.pathname == '/link') {
    if (!params['linkToken']) {
      handleErr("No linkToken provided", res);
    }
    else {
      request.post({url: urlToThinkAutomatic + 'devices/' + deviceConf['deviceId'] + '/link',
                    qs: { access_token: deviceConf['deviceToken'] },
                    form: { linkToken: params['linkToken'] }}, function(err,httpResponse,body) {
        if (err) {
          handleErr(err, res);
        }
        else {
          updateThinkDeviceConf(body, function (err) {
              if (err) {
                handleErr(err, res);
              }
              else {
                if (params['successRedirect']) {
                  res.writeHead(302, {'Location': params['successRedirect']});
                  res.end();
                }
                else {
                  handleErr("Link succeeded but no successRedirect supplied");
                }
              }
            });
        }
      });
    }
  }
  else if (parsedUrl.pathname == '/')
  {
    console.log('Selecting scene ' + params['sceneId']);
    selectScene(findElem(sceneTriggerData['sceneTriggerData']['scenes'], params));
    res.writeHead(200, {"Content-Type": "text/html"});
    res.end();
  }
  else {
    handleErr("Invalid route", res);
  }
}

var onopen = function () {
  console.log('Connection to platform is opened');
}

var onmessage = function(data) {
  console.log('Received from platform:');
  console.log(data);
}

var onerror = function() {
  console.log('Error receiving data from platform');
}

function on(eventName, cb) {
  switch(eventName){
    case 'open':
      onopen = cb;
      break;
    case 'message':
      onmessage = cb;
      break;
    case 'error':
      onerror = cb;
      break;
  }
}

function currentlySelected(command)
{
  if (command && command['action'] && command['action']['sceneId'])
    return findElem(rooms, command['action']);
  return false;
}

function startEventSource(cb)
{
  var src = new EventSource(deviceConf['eventStreamUrl']);

  src.onopen = function(){
    onopen();
  };
  src.onmessage = function(e) {
    try
    {
      var parsedData = JSON.parse(e.data);

      if (parsedData['sceneTriggerData'])
        updateSceneTriggerData(parsedData);
      else if (!currentlySelected(parsedData))
        onmessage(parsedData);
    }
    catch (err)
    {
      logError(err);
    }
  };
  src.onerror = function() {
    onerror();
  };

  sendKeepAlive();

  if (typeof cb === 'function')
    cb();
}

function run(cb) {
  // Configure our HTTP server
  var server = http.createServer(function (req, res) {
    handleReq(req, res);
  });

  server.on('error', function(e) {
    logError('Unable to start local server on port ' + serverPort.toString());
    serverPort = serverPort + 1;
    run(cb);
  });

  if (serverPort < 3300) {
    console.log('Attempting to start local server on port ' + serverPort.toString());
    server.listen(serverPort, function(){
      console.log('Local http server running at ' + directUrl());
      startEventSource(cb);
    });
  }
  else {
    logError('[warning] Unable to start local server.');
    startEventSource(cb);
  }
}

function connect(deviceProperties, cb)
{
  serverPort = 3205;

  deviceProperties['directUrl'] = directUrl();

  fs.readFile('sceneData.conf', 'utf8', function (err,data) {
    if (!err) {
      sceneTriggerData = JSON.parse(data);
    }
    fs.readFile('device.conf', 'utf8', function (err,data) {
      if (err)
      {
        console.log('Creating new device.');
        request.post({url: urlToThinkAutomatic + 'devices',
                    form: deviceProperties }, function(err,httpResponse,body) {
          updateThinkDeviceConf(body, function () { run(cb); });
        });
      }
      else
      {
        deviceConf = JSON.parse(data);
        console.log('Updating device.');
        request.patch({url: urlToThinkAutomatic + 'devices/' + deviceConf['deviceId'],
                    qs: { access_token: deviceConf['deviceToken'] },
                    form: deviceProperties }, function(err,httpResponse,body) {
          updateThinkDeviceConf(body, function () { run(cb); });
        });
      }
    });
  });
}

function matchElem(elem, selector) {
  var oneMatch = false;
  var keyArray = Object.keys(selector);

  for (var i = 0; i < keyArray.length; i++) {
    var key = keyArray[i];
    if (elem[key]) {
      if (elem[key] != selector[key])
        return false;
      oneMatch = true;
    }
  };
  return oneMatch;
}

function findElem(elemArray, selector) {
  for (var i = 0; i < elemArray.length; i++) {
    if (matchElem(elemArray[i], selector))
      return elemArray[i];
  };
  return null;
}

function purgeRoomFromSceneCache(roomId) {
  var room = findElem(rooms, {roomId:roomId});
  var currSceneId = 0;

  if (room != null)
    currSceneId = room['sceneId'];

  for(var i = sceneSelectHistory.length; i--;) {
    if (sceneSelectHistory[i]["roomId"] == roomId &&
        sceneSelectHistory[i]["sceneId"] != currSceneId)
      sceneSelectHistory.splice(i, 1);
  }
}

function setRoomTimeout(roomId, sceneId) {
  var room = findElem(rooms, {roomId:roomId});
  var timeout = setTimeout(function() { purgeRoomFromSceneCache(roomId); }, 20000 );

  if (room == null) {
    rooms.push({ roomId:roomId, sceneId:sceneId, timeout:timeout });
  }
  else {
    clearTimeout(room['timeout']);
    room['timeout'] = timeout;
    room['sceneId'] = sceneId;
  }
}

function selectScene(scene) {
  if (scene) {
    if (scene['commands']) {
      scene['commands'].forEach(function (command) { onmessage(command); });
    }
    setRoomTimeout(scene.roomId, scene.sceneId);
    if (scene.level == 0)
      purgeRoomFromSceneCache(scene.roomId);
    else
      sceneSelectHistory.push({"sceneId":scene.sceneId, "roomId":scene.roomId});
  }
}

function selectCachedScene(deviceSelector, deviceProperties, cb) {
  if (sceneTriggerData['sceneTriggerData']) {
    var devices = sceneTriggerData['sceneTriggerData']['devices'];
    if (devices) {
      var device = findElem(devices, deviceSelector);
      if (device && device['actions']) {
        var action = findElem(device['actions'], deviceProperties);
        if (action && action['scenes']) {
          var scenes = action['scenes'];
          for (var i = 0; i < scenes.length; i++) {
            var scene = scenes[i];
            if (findElem(sceneSelectHistory, scene) == null) {
              var fullScene = findElem(sceneTriggerData['sceneTriggerData']['scenes'], scene);
              if (fullScene) {
                selectScene(fullScene);

                var localDirectUrls = sceneTriggerData['sceneTriggerData']['localDirectUrls'];
                if (localDirectUrls) {
                  localDirectUrls.forEach(function (directUrl) {
                    try {
                      console.log('Selecting scene ' + fullScene.sceneId.toString() + ' at: ' + directUrl.toString());
                      request.get({url: directUrl + '?sceneId=' + fullScene.sceneId.toString() }, function(error, response, body) {
                      });
                    }
                    catch (err) {
                      logError(err);
                    }
                  });
                }

                deviceProperties['sceneId'] = fullScene.sceneId;
//                deviceProperties['silent'] = 'true';
                cb(deviceProperties);
                return;
              }
            }
          };
        }
      }
    }
  }
  cb(deviceProperties);
}

function patch(deviceSelector, deviceProperties, cb) {
  // check if deviceSelector was omitted
  if ((typeof deviceProperties === 'function') ||
      (typeof deviceProperties === 'undefined')) {
    cb = deviceProperties;
    deviceProperties = deviceSelector;
    request.patch({url: urlToThinkAutomatic + 'devices/' + deviceConf['deviceId'],
                  qs: { access_token: deviceConf['deviceToken'] },
                  form: deviceProperties }, function(err,httpResponse,body) {
                    if (typeof cb === 'function')
                      cb(body);
                  });
  }
  else {
    selectCachedScene(deviceSelector, deviceProperties, function (devicePropertiesWithScene) {
      devicePropertiesWithScene['hubId'] = deviceConf['deviceId'];

      if (typeof deviceSelector === 'number') {
        request.patch({url: urlToThinkAutomatic + 'devices/' + deviceSelector.toString(),
                      qs: { access_token: deviceConf['deviceToken'] },
                      form: devicePropertiesWithScene }, function(err,httpResponse,body) {
                        if (typeof cb === 'function')
                          cb(body);
                      });
      }
      else {
        request.patch({url: urlToThinkAutomatic + 'devices',
                      qs: { access_token: deviceConf['deviceToken'], where: deviceSelector },
                      form: devicePropertiesWithScene }, function(err,httpResponse,body) {
                        if (typeof cb === 'function')
                          cb(body);
                      });
      }
    });
  }
}

module.exports =  {
  connect: connect,
  patch: patch,
  on: on
};

<|endoftext|>


<commit_before>/**
 * Copyright 2018-present Facebook.
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 * @format
 */

import {ManagedTable, Text, Panel, colors, FlexRow, DataInspector} from 'sonar';
import {SonarPlugin} from 'sonar';

const {clone} = require('lodash');

type SharedPreferencesChangeEvent = {|
  name: string,
  time: number,
  deleted: boolean,
  value: string,
|};

export type SharedPreferences = {
  [name: string]: any,
};

type SharedPreferencesState = {|
  sharedPreferences: ?SharedPreferences,
  changesList: Array<SharedPreferencesChangeEvent>,
|};

const CHANGELOG_COLUMNS = {
  event: {
    value: 'Event',
  },
  name: {
    value: 'Name',
  },
  value: {
    value: 'Value',
  },
};

const CHANGELOG_COLUMN_SIZES = {
  event: '20%',
  name: '40%',
  value: '40%',
};

const UPDATED_LABEL = <Text color={colors.lime}>Updated</Text>;
const DELETED_LABEL = <Text color={colors.cherry}>Deleted</Text>;

export default class extends SonarPlugin<SharedPreferencesState> {
  static title = 'Shared Preferences Viewer';
  static id = 'Preferences';

  reducers = {
    UpdateSharedPreferences(state: SharedPreferencesState, results: Object) {
      return {
        changesList: state.changesList,
        sharedPreferences: results.results,
      };
    },

    ChangeSharedPreferences(state: SharedPreferencesState, event: Object) {
      const sharedPreferences = {...(state.sharedPreferences || {})};
      if (event.change.deleted) {
        delete sharedPreferences[event.change.name];
      } else {
        sharedPreferences[event.change.name] = event.change.value;
      }
      return {
        changesList: [event.change, ...state.changesList],
        sharedPreferences,
      };
    },
  };

  init() {
    this.client
      .call('getSharedPreferences')
      .then((results: SharedPreferences) => {
        this.dispatchAction({results, type: 'UpdateSharedPreferences'});
      });

    this.client.subscribe(
      'sharedPreferencesChange',
      (change: SharedPreferencesChangeEvent) => {
        this.dispatchAction({change, type: 'ChangeSharedPreferences'});
      },
    );
    this.client.subscribe(
      'newSharedPreferences',
      (results: SharedPreferences) => {
        this.dispatchAction({results, type: 'UpdateSharedPreferences'});
      },
    );
  }

  constructor() {
    super();
    this.state = {
      changesList: [],
      sharedPreferences: null,
    };
  }

  onSharedPreferencesChanged = (path: Array<string>, value: any) => {
    const values = this.state.sharedPreferences;

    let newValue = value;
    if (path.length === 2 && values) {
      newValue = clone(values[path[0]]);
      newValue[path[1]] = value;
    }
    this.client
      .call('setSharedPreference', {
        preferenceName: path[0],
        preferenceValue: newValue,
      })
      .then((results: SharedPreferences) => {
        this.dispatchAction({results, type: 'UpdateSharedPreferences'});
      });
  };

  render() {
    if (this.state.sharedPreferences == null) {
      return null;
    }

    return (
      <FlexRow fill={true} scrollable={true}>
        <Panel heading="Inspector">
          <DataInspector
            data={this.state.sharedPreferences}
            setValue={this.onSharedPreferencesChanged}
          />
        </Panel>
        <Panel heading="Changelog">
          <ManagedTable
            columnSizes={CHANGELOG_COLUMN_SIZES}
            columns={CHANGELOG_COLUMNS}
            rows={this.state.changesList.map((element, index) => {
              return {
                columns: {
                  event: {
                    value: element.deleted ? DELETED_LABEL : UPDATED_LABEL,
                  },
                  name: {
                    value: element.name,
                  },
                  value: {
                    value: element.value,
                  },
                },

                key: String(index),
              };
            })}
          />
        </Panel>
      </FlexRow>
    );
  }
}
<commit_msg>fix plugin state initialization<commit_after>/**
 * Copyright 2018-present Facebook.
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 * @format
 */

import {ManagedTable, Text, Panel, colors, FlexRow, DataInspector} from 'sonar';
import {SonarPlugin} from 'sonar';

const {clone} = require('lodash');

type SharedPreferencesChangeEvent = {|
  name: string,
  time: number,
  deleted: boolean,
  value: string,
|};

export type SharedPreferences = {
  [name: string]: any,
};

type SharedPreferencesState = {|
  sharedPreferences: ?SharedPreferences,
  changesList: Array<SharedPreferencesChangeEvent>,
|};

const CHANGELOG_COLUMNS = {
  event: {
    value: 'Event',
  },
  name: {
    value: 'Name',
  },
  value: {
    value: 'Value',
  },
};

const CHANGELOG_COLUMN_SIZES = {
  event: '20%',
  name: '40%',
  value: '40%',
};

const UPDATED_LABEL = <Text color={colors.lime}>Updated</Text>;
const DELETED_LABEL = <Text color={colors.cherry}>Deleted</Text>;

export default class extends SonarPlugin<SharedPreferencesState> {
  static title = 'Shared Preferences Viewer';
  static id = 'Preferences';

  state = {
    changesList: [],
    sharedPreferences: null,
  };

  reducers = {
    UpdateSharedPreferences(state: SharedPreferencesState, results: Object) {
      return {
        changesList: state.changesList,
        sharedPreferences: results.results,
      };
    },

    ChangeSharedPreferences(state: SharedPreferencesState, event: Object) {
      const sharedPreferences = {...(state.sharedPreferences || {})};
      if (event.change.deleted) {
        delete sharedPreferences[event.change.name];
      } else {
        sharedPreferences[event.change.name] = event.change.value;
      }
      return {
        changesList: [event.change, ...state.changesList],
        sharedPreferences,
      };
    },
  };

  init() {
    this.client
      .call('getSharedPreferences')
      .then((results: SharedPreferences) => {
        this.dispatchAction({results, type: 'UpdateSharedPreferences'});
      });

    this.client.subscribe(
      'sharedPreferencesChange',
      (change: SharedPreferencesChangeEvent) => {
        this.dispatchAction({change, type: 'ChangeSharedPreferences'});
      },
    );
    this.client.subscribe(
      'newSharedPreferences',
      (results: SharedPreferences) => {
        this.dispatchAction({results, type: 'UpdateSharedPreferences'});
      },
    );
  }

  onSharedPreferencesChanged = (path: Array<string>, value: any) => {
    const values = this.state.sharedPreferences;

    let newValue = value;
    if (path.length === 2 && values) {
      newValue = clone(values[path[0]]);
      newValue[path[1]] = value;
    }
    this.client
      .call('setSharedPreference', {
        preferenceName: path[0],
        preferenceValue: newValue,
      })
      .then((results: SharedPreferences) => {
        this.dispatchAction({results, type: 'UpdateSharedPreferences'});
      });
  };

  render() {
    if (this.state.sharedPreferences == null) {
      return null;
    }

    return (
      <FlexRow fill={true} scrollable={true}>
        <Panel heading="Inspector">
          <DataInspector
            data={this.state.sharedPreferences}
            setValue={this.onSharedPreferencesChanged}
          />
        </Panel>
        <Panel heading="Changelog">
          <ManagedTable
            columnSizes={CHANGELOG_COLUMN_SIZES}
            columns={CHANGELOG_COLUMNS}
            rows={this.state.changesList.map((element, index) => {
              return {
                columns: {
                  event: {
                    value: element.deleted ? DELETED_LABEL : UPDATED_LABEL,
                  },
                  name: {
                    value: element.name,
                  },
                  value: {
                    value: element.value,
                  },
                },

                key: String(index),
              };
            })}
          />
        </Panel>
      </FlexRow>
    );
  }
}
<|endoftext|>


<commit_before>goog.provide("prism.theme.builder.ui.BuilderUI");

goog.require("goog.dom.classlist");
goog.require("prism.theme.builder.ui.AbstractUI");
goog.require("prism.theme.builder.ui.BuilderPalette");

/**
 * @constructor
 * @author Sandra https://github.com/SandraShklyaeva
 *
 * @param {goog.ui.ControlRenderer}
 *            opt_renderer
 * @param {goog.dom.DomHelper}
 *            opt_domHelper
 * @extends {prism.theme.builder.ui.AbstractUI}
 */
prism.theme.builder.ui.BuilderUI = function(builder, opt_renderer,
		opt_domHelper) {
	prism.theme.builder.ui.AbstractUI.call(this, null, opt_renderer,
			opt_domHelper);
	this.builder = builder;
	this.palette = new prism.theme.builder.ui.BuilderPalette(this.builder);
}
goog.inherits(prism.theme.builder.ui.BuilderUI,
		prism.theme.builder.ui.AbstractUI);

/**
 *
 */
prism.theme.builder.ui.BuilderUI.prototype.update = function() {
	this.palette.init();
};

/**
 *
 */
prism.theme.builder.ui.BuilderUI.prototype.render = function(element) {
	prism.theme.builder.ui.BuilderUI.base(this, 'render', element);

	var parentElement = this.getElement();

	var content = this.getDomHelper().createElement("div");
	goog.dom.classlist.add(content, "Builder-UIContent");

	this.renderCodeRegion(content);

	this.getDomHelper().appendChild(parentElement, content);

	this.palette.render(parentElement);

};

prism.theme.builder.ui.BuilderUI.prototype.renderCodeRegion = function(parent) {
	/*
	 * LEFT
	 */
	var leftDiv = goog.dom.createElement("div");
	goog.dom.classlist.set(leftDiv, "Builder-BuilderCodeRegion");
	goog.dom.appendChild(parent, leftDiv);

	/*
	 * CODE REGION
	 */
	var preRegion = goog.dom.createElement("pre");
	this.codeRegion = goog.dom.createElement("code");

	goog.dom.appendChild(preRegion, this.codeRegion);
	goog.dom.appendChild(leftDiv, preRegion);

}

/**
 *
 */
prism.theme.builder.ui.BuilderUI.prototype.fillCode = function(lang) {
	/*
	 * REMOVE CLASS FROM PARENT
	 */
	goog.dom.classlist.set(goog.dom.getParentElement(this.codeRegion), "");
	/*
	 * APPLY CLASS
	 */
	if (lang == null || lang.isGlobal()) {
		goog.dom.classlist.add(this.codeRegion, "language-javascript");
	} else {
		goog.dom.classlist.add(this.codeRegion, "language-" + lang.getName());
	}
	/*
	 * REPLACE CODE
	 */
	prism.theme.builder.LanguageCodeFactory.getCode(lang, function(code) {
		goog.dom.setTextContent(this.codeRegion, code);
		/*
		 * RERUN PRISM
		 */
		Prism.highlightAll(false, null);
	}, this)
};<commit_msg>fixed switching between languages<commit_after>goog.provide("prism.theme.builder.ui.BuilderUI");

goog.require("goog.dom.classlist");
goog.require("prism.theme.builder.ui.AbstractUI");
goog.require("prism.theme.builder.ui.BuilderPalette");

/**
 * @constructor
 * @author Sandra https://github.com/SandraShklyaeva
 *
 * @param {goog.ui.ControlRenderer}
 *            opt_renderer
 * @param {goog.dom.DomHelper}
 *            opt_domHelper
 * @extends {prism.theme.builder.ui.AbstractUI}
 */
prism.theme.builder.ui.BuilderUI = function(builder, opt_renderer,
		opt_domHelper) {
	prism.theme.builder.ui.AbstractUI.call(this, null, opt_renderer,
			opt_domHelper);
	this.builder = builder;
	this.palette = new prism.theme.builder.ui.BuilderPalette(this.builder);
}
goog.inherits(prism.theme.builder.ui.BuilderUI,
		prism.theme.builder.ui.AbstractUI);

/**
 *
 */
prism.theme.builder.ui.BuilderUI.prototype.update = function() {
	this.palette.init();
};

/**
 *
 */
prism.theme.builder.ui.BuilderUI.prototype.render = function(element) {
	prism.theme.builder.ui.BuilderUI.base(this, 'render', element);

	var parentElement = this.getElement();

	var content = this.getDomHelper().createElement("div");
	goog.dom.classlist.add(content, "Builder-UIContent");

	this.renderCodeRegion(content);

	this.getDomHelper().appendChild(parentElement, content);

	this.palette.render(parentElement);

};

prism.theme.builder.ui.BuilderUI.prototype.renderCodeRegion = function(parent) {
	/*
	 * LEFT
	 */
	var leftDiv = goog.dom.createElement("div");
	goog.dom.classlist.set(leftDiv, "Builder-BuilderCodeRegion");
	goog.dom.appendChild(parent, leftDiv);

	/*
	 * CODE REGION
	 */
	var preRegion = goog.dom.createElement("pre");
	this.codeRegion = goog.dom.createElement("code");

	goog.dom.appendChild(preRegion, this.codeRegion);
	goog.dom.appendChild(leftDiv, preRegion);

}

/**
 *
 */
prism.theme.builder.ui.BuilderUI.prototype.fillCode = function(lang) {
	/*
	 * REMOVE CLASS FROM PARENT
	 */
	goog.dom.classlist.set(goog.dom.getParentElement(this.codeRegion), "");
	/*
	 * APPLY CLASS
	 */
	if (lang == null || lang.isGlobal()) {
		goog.dom.classlist.set(this.codeRegion, "language-javascript");
	} else {
		goog.dom.classlist.set(this.codeRegion, "language-" + lang.getName());
	}
	/*
	 * REPLACE CODE
	 */
	prism.theme.builder.LanguageCodeFactory.getCode(lang, function(code) {
		goog.dom.setTextContent(this.codeRegion, code);
		/*
		 * RERUN PRISM
		 */
		Prism.highlightAll(false, null);
	}, this)
};<|endoftext|>


<commit_before>import Node from '../Node.js';
import CompileError from '../../utils/CompileError.js';

export default class VariableDeclarator extends Node {
	initialise ( transforms ) {
		this.isObjectPattern = this.id.type === 'ObjectPattern';

		// disallow compound destructuring, for now at least
		if ( /Pattern/.test( this.id.type ) ) {
			this.id[ this.isObjectPattern ? 'properties' : 'elements' ].forEach( node => {
				if ( node && /Pattern/.test( this.isObjectPattern ? node.value.type : node.type ) ) {
					throw new CompileError( node.value, 'Compound destructuring is not supported' );
				}
			});
		}

		let kind = this.parent.kind;
		if ( kind === 'let' && this.parent.parent.type === 'ForStatement' ) {
			kind = 'for.let'; // special case...
		}

		this.parent.scope.addDeclaration( this.id, kind );
		super.initialise( transforms );
	}

	transpile ( code, transforms ) {
		if ( transforms.destructuring && this.id.type !== 'Identifier' ) {
			const simple = this.init.type === 'Identifier';
			const name = simple ? this.init.name : this.findScope( true ).createIdentifier( 'ref' );

			if ( !simple ) {
				code.insert( this.start, `${name} = ` );
				code.move( this.init.start, this.init.end, this.start );
				code.insert( this.start, `, ` );
			} else {
				code.remove( this.init.start, this.init.end );
			}

			const props = this.isObjectPattern ? this.id.properties : this.id.elements;

			code.remove( this.start, props[0].start );

			let lastIndex = this.start;
			let first = true;

			props.forEach( ( property, i ) => {
				if ( property ) {
					const id = this.isObjectPattern ? property.value : property;
					const rhs = this.isObjectPattern ? `${name}.${property.key.name}` : `${name}[${i}]`;

					code.overwrite( lastIndex, id.start, `${first ? '' : ', '}` );
					code.insert( id.end, ` = ${rhs}` );

					lastIndex = property.end;
					first = false;
				}
			});

			code.remove( lastIndex, this.init.start );
		}

		super.transpile( code, transforms );
	}
}
<commit_msg>Refuctor variable deconstructing<commit_after>import Node from '../Node.js';
import CompileError from '../../utils/CompileError.js';

export default class VariableDeclarator extends Node {
	initialise ( transforms ) {
		this.isObjectPattern = this.id.type === 'ObjectPattern';

		// disallow compound destructuring, for now at least
		if ( /Pattern/.test( this.id.type ) ) {
			this.id[ this.isObjectPattern ? 'properties' : 'elements' ].forEach( node => {
				if ( node && /Pattern/.test( this.isObjectPattern ? node.value.type : node.type ) ) {
					throw new CompileError( node.value, 'Compound destructuring is not supported' );
				}
			});
		}

		let kind = this.parent.kind;
		if ( kind === 'let' && this.parent.parent.type === 'ForStatement' ) {
			kind = 'for.let'; // special case...
		}

		this.parent.scope.addDeclaration( this.id, kind );
		super.initialise( transforms );
	}

	transpile ( code, transforms ) {
		if ( transforms.destructuring && this.id.type !== 'Identifier' ) {
			const simple = this.init.type === 'Identifier';
			const name = simple ? this.init.name : this.findScope( true ).createIdentifier( 'ref' );

			const props = this.isObjectPattern ? this.id.properties : this.id.elements;

			props.forEach( ( property, i ) => {
				if ( property ) {
					const id = this.isObjectPattern ? property.value : property;
					const rhs = this.isObjectPattern ? `${name}.${property.key.name}` : `${name}[${i}]`;

					if (!simple || i !== 0) {
						code.insert( this.end, `, ` );
					}

					code.move( id.start, id.end, this.end );
					code.insert( this.end, ` = ${rhs}` );
				}
			});

			if ( !simple ) {
				code.overwrite( this.id.start, this.id.end, name );
			} else {
				code.remove( this.start, this.end );
			}
		}

		super.transpile( code, transforms );
	}
}
<|endoftext|>


<commit_before>/**
 * ASP.NET Core Starter Kit
 *
 * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

const path = require('path');
const cp = require('./lib/cp');
const task = require('./lib/task');

const isDebug = !(process.argv.includes('--production') || process.argv.includes('-p'));
const config = isDebug ? 'Debug' : 'Release';

module.exports = task('build', () => Promise.resolve()
  .then(() => require('./clean'))
  .then(() => cp.spawn('dotnet', ['publish', 'server', '-o', 'build', '-c', config, '-r', 'coreclr']))
  .then(() => require('./bundle'))
  .then(() => require('./copy'))
);
<commit_msg>Create appsettings.<env>.json files on build if they don't exist<commit_after>/**
 * ASP.NET Core Starter Kit
 *
 * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

const fs = require('fs');
const path = require('path');
const cp = require('./lib/cp');
const task = require('./lib/task');

const isDebug = !(process.argv.includes('--production') || process.argv.includes('-p'));
const config = isDebug ? 'Debug' : 'Release';

module.exports = task('build', () => Promise.resolve()
  // Clean up the output directory
  .then(() => require('./clean'))

  // Create Production/Development configuration files if they don't exist
  .then(() => new Promise(resolve => {
    let count = 2;
    const appSettings = require('../server/appsettings.json'); // use it as a template
    delete appSettings.Logging;
    ['Production', 'Development'].forEach(env => {
      const filename = path.resolve(__dirname, `../server/appsettings.${env}.json`);
      fs.writeFile(filename, JSON.stringify(appSettings, null, '  '), { flag: 'wx', }, err => {
        if (--count === 0) resolve();
      });
    })
  }))

  // Compile the ASP.NET Core app
  .then(() => cp.spawn('dotnet', ['publish', 'server', '-o', 'build', '-c', config, '-r', 'coreclr']))

  // Compile the JavaScript app
  .then(() => require('./bundle'))

  // Copy static files to the output folder
  .then(() => require('./copy'))
);
<|endoftext|>


<commit_before>'use strict';

var commander = require('commander');
var path      = require('path');
var Queue     = require('gear').Queue;
var registry  = require('./tasks');

var build, target;

commander
  .usage('[options] [<languages ...>]')
  .option('-n, --no-compress', 'Disable compression')
  .option('-t, --target <name>', 'Build for target [browser, cdn, node]',
                                 /^(browser|cdn|node)$/i, 'browser')
  .parse(process.argv);

target = './' + commander.target.toLowerCase();
build  = require(target);

global.dir       = {};
global.dir.root  = path.dirname(__dirname);
global.dir.build = path.join(dir.root, 'build');

new Queue({ registry: registry })
  .clean(dir.build)
  .log('Starting build.')
  .tasks(build(commander))
  .log('Finished build.')
  .run();
<commit_msg>Set the target to lowercase inside command object<commit_after>'use strict';

var commander = require('commander');
var path      = require('path');
var Queue     = require('gear').Queue;
var registry  = require('./tasks');

var build, target;

commander
  .usage('[options] [<languages ...>]')
  .option('-n, --no-compress', 'Disable compression')
  .option('-t, --target <name>', 'Build for target [browser, cdn, node]',
                                 /^(browser|cdn|node)$/i, 'browser')
  .parse(process.argv);

commander.target = commander.target.toLowerCase();

target = './' + commander.target;
build  = require(target);

global.dir       = {};
global.dir.root  = path.dirname(__dirname);
global.dir.build = path.join(dir.root, 'build');

new Queue({ registry: registry })
  .clean(dir.build)
  .log('Starting build.')
  .tasks(build(commander))
  .log('Finished build.')
  .run();
<|endoftext|>


<commit_before>/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import path from 'path';
import express from 'express';
import browserSync from 'browser-sync';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import createLaunchEditorMiddleware from 'react-error-overlay/middleware';
import webpackConfig from './webpack.config';
import run, { format } from './run';
import clean from './clean';

const isDebug = !process.argv.includes('--release');

// https://webpack.js.org/configuration/watch/#watchoptions
const watchOptions = {
  // Watching may not work with NFS and machines in VirtualBox
  // Uncomment next line if it is your case (use true or interval in milliseconds)
  // poll: true,
  // Decrease CPU or memory usage in some file systems
  // ignored: /node_modules/,
};

function createCompilationPromise(name, compiler, config) {
  return new Promise((resolve, reject) => {
    let timeStart = new Date();
    compiler.plugin('compile', () => {
      timeStart = new Date();
      console.info(`[${format(timeStart)}] Compiling '${name}'...`);
    });
    compiler.plugin('done', stats => {
      console.info(stats.toString(config.stats));
      const timeEnd = new Date();
      const time = timeEnd.getTime() - timeStart.getTime();
      if (stats.hasErrors()) {
        console.info(
          `[${format(timeEnd)}] Failed to compile '${name}' after ${time} ms`,
        );
        reject(new Error('Compilation failed!'));
      } else {
        console.info(
          `[${format(
            timeEnd,
          )}] Finished '${name}' compilation after ${time} ms`,
        );
        resolve(stats);
      }
    });
  });
}

let server;

/**
 * Launches a development web server with "live reload" functionality -
 * synchronizing URLs, interactions and code changes across multiple devices.
 */
async function start() {
  if (server) return server;
  server = express();
  server.use(createLaunchEditorMiddleware());
  server.use(express.static(path.resolve(__dirname, '../public')));

  // Configure client-side hot module replacement
  const clientConfig = webpackConfig.find(config => config.name === 'client');
  clientConfig.entry.client = [
    'react-error-overlay',
    'react-hot-loader/patch',
    'webpack-hot-middleware/client?name=client&reload=true',
  ]
    .concat(clientConfig.entry.client)
    .sort((a, b) => b.includes('polyfill') - a.includes('polyfill'));
  clientConfig.output.filename = clientConfig.output.filename.replace(
    'chunkhash',
    'hash',
  );
  clientConfig.output.chunkFilename = clientConfig.output.chunkFilename.replace(
    'chunkhash',
    'hash',
  );
  clientConfig.module.rules = clientConfig.module.rules.filter(
    x => x.loader !== 'null-loader',
  );
  const { options } = clientConfig.module.rules.find(
    x => x.loader === 'babel-loader',
  );
  options.plugins = ['react-hot-loader/babel'].concat(options.plugins || []);
  clientConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    new webpack.NamedModulesPlugin(),
  );

  // Configure server-side hot module replacement
  const serverConfig = webpackConfig.find(config => config.name === 'server');
  serverConfig.output.hotUpdateMainFilename = 'updates/[hash].hot-update.json';
  serverConfig.output.hotUpdateChunkFilename =
    'updates/[id].[hash].hot-update.js';
  serverConfig.module.rules = serverConfig.module.rules.filter(
    x => x.loader !== 'null-loader',
  );
  serverConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    new webpack.NamedModulesPlugin(),
  );

  // Configure compilation
  await run(clean);
  const multiCompiler = webpack(webpackConfig);
  const clientCompiler = multiCompiler.compilers.find(
    compiler => compiler.name === 'client',
  );
  const serverCompiler = multiCompiler.compilers.find(
    compiler => compiler.name === 'server',
  );
  const clientPromise = createCompilationPromise(
    'client',
    clientCompiler,
    clientConfig,
  );
  const serverPromise = createCompilationPromise(
    'server',
    serverCompiler,
    serverConfig,
  );

  // https://github.com/webpack/webpack-dev-middleware
  server.use(
    webpackDevMiddleware(clientCompiler, {
      publicPath: clientConfig.output.publicPath,
      quiet: true,
      watchOptions,
    }),
  );

  // https://github.com/glenjamin/webpack-hot-middleware
  server.use(webpackHotMiddleware(clientCompiler, { log: false }));

  let appPromise;
  let appPromiseResolve;
  let appPromiseIsResolved = true;
  serverCompiler.plugin('compile', () => {
    if (!appPromiseIsResolved) return;
    appPromiseIsResolved = false;
    appPromise = new Promise(resolve => (appPromiseResolve = resolve));
  });

  let app;
  server.use((req, res) => {
    appPromise
      .then(() => app.handle(req, res))
      .catch(error => console.error(error));
  });

  function checkForUpdate(fromUpdate) {
    const hmrPrefix = '[\x1b[35mHMR\x1b[0m] ';
    if (!app.hot) {
      throw new Error(`${hmrPrefix}Hot Module Replacement is disabled.`);
    }
    if (app.hot.status() !== 'idle') {
      return Promise.resolve();
    }
    return app.hot
      .check(true)
      .then(updatedModules => {
        if (!updatedModules) {
          if (fromUpdate) {
            console.info(`${hmrPrefix}Update applied.`);
          }
          return;
        }
        if (updatedModules.length === 0) {
          console.info(`${hmrPrefix}Nothing hot updated.`);
        } else {
          console.info(`${hmrPrefix}Updated modules:`);
          updatedModules.forEach(moduleId =>
            console.info(`${hmrPrefix} - ${moduleId}`),
          );
          checkForUpdate(true);
        }
      })
      .catch(error => {
        if (['abort', 'fail'].includes(app.hot.status())) {
          console.warn(`${hmrPrefix}Cannot apply update.`);
          delete require.cache[require.resolve('../build/server')];
          // eslint-disable-next-line global-require, import/no-unresolved
          app = require('../build/server').default;
          console.warn(`${hmrPrefix}App has been reloaded.`);
        } else {
          console.warn(
            `${hmrPrefix}Update failed: ${error.stack || error.message}`,
          );
        }
      });
  }

  serverCompiler.watch(watchOptions, (error, stats) => {
    if (app && !error && !stats.hasErrors()) {
      checkForUpdate().then(() => {
        appPromiseIsResolved = true;
        appPromiseResolve();
      });
    }
  });

  // Wait until both client-side and server-side bundles are ready
  await clientPromise;
  await serverPromise;

  const timeStart = new Date();
  console.info(`[${format(timeStart)}] Launching server...`);

  // Load compiled src/server.js as a middleware
  // eslint-disable-next-line global-require, import/no-unresolved
  app = require('../build/server').default;
  appPromiseIsResolved = true;
  appPromiseResolve();

  // Launch the development server with Browsersync and HMR
  await new Promise((resolve, reject) =>
    browserSync.create().init(
      {
        // https://www.browsersync.io/docs/options
        server: 'src/server.js',
        middleware: [server],
        open: !process.argv.includes('--silent'),
        ...(isDebug ? {} : { notify: false, ui: false }),
      },
      (error, bs) => (error ? reject(error) : resolve(bs)),
    ),
  );

  const timeEnd = new Date();
  const time = timeEnd.getTime() - timeStart.getTime();
  console.info(`[${format(timeEnd)}] Server launched after ${time} ms`);
  return server;
}

export default start;
<commit_msg>style: disable no-return-assign on line in tools/start<commit_after>/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import path from 'path';
import express from 'express';
import browserSync from 'browser-sync';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import createLaunchEditorMiddleware from 'react-error-overlay/middleware';
import webpackConfig from './webpack.config';
import run, { format } from './run';
import clean from './clean';

const isDebug = !process.argv.includes('--release');

// https://webpack.js.org/configuration/watch/#watchoptions
const watchOptions = {
  // Watching may not work with NFS and machines in VirtualBox
  // Uncomment next line if it is your case (use true or interval in milliseconds)
  // poll: true,
  // Decrease CPU or memory usage in some file systems
  // ignored: /node_modules/,
};

function createCompilationPromise(name, compiler, config) {
  return new Promise((resolve, reject) => {
    let timeStart = new Date();
    compiler.plugin('compile', () => {
      timeStart = new Date();
      console.info(`[${format(timeStart)}] Compiling '${name}'...`);
    });
    compiler.plugin('done', stats => {
      console.info(stats.toString(config.stats));
      const timeEnd = new Date();
      const time = timeEnd.getTime() - timeStart.getTime();
      if (stats.hasErrors()) {
        console.info(
          `[${format(timeEnd)}] Failed to compile '${name}' after ${time} ms`,
        );
        reject(new Error('Compilation failed!'));
      } else {
        console.info(
          `[${format(
            timeEnd,
          )}] Finished '${name}' compilation after ${time} ms`,
        );
        resolve(stats);
      }
    });
  });
}

let server;

/**
 * Launches a development web server with "live reload" functionality -
 * synchronizing URLs, interactions and code changes across multiple devices.
 */
async function start() {
  if (server) return server;
  server = express();
  server.use(createLaunchEditorMiddleware());
  server.use(express.static(path.resolve(__dirname, '../public')));

  // Configure client-side hot module replacement
  const clientConfig = webpackConfig.find(config => config.name === 'client');
  clientConfig.entry.client = [
    'react-error-overlay',
    'react-hot-loader/patch',
    'webpack-hot-middleware/client?name=client&reload=true',
  ]
    .concat(clientConfig.entry.client)
    .sort((a, b) => b.includes('polyfill') - a.includes('polyfill'));
  clientConfig.output.filename = clientConfig.output.filename.replace(
    'chunkhash',
    'hash',
  );
  clientConfig.output.chunkFilename = clientConfig.output.chunkFilename.replace(
    'chunkhash',
    'hash',
  );
  clientConfig.module.rules = clientConfig.module.rules.filter(
    x => x.loader !== 'null-loader',
  );
  const { options } = clientConfig.module.rules.find(
    x => x.loader === 'babel-loader',
  );
  options.plugins = ['react-hot-loader/babel'].concat(options.plugins || []);
  clientConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    new webpack.NamedModulesPlugin(),
  );

  // Configure server-side hot module replacement
  const serverConfig = webpackConfig.find(config => config.name === 'server');
  serverConfig.output.hotUpdateMainFilename = 'updates/[hash].hot-update.json';
  serverConfig.output.hotUpdateChunkFilename =
    'updates/[id].[hash].hot-update.js';
  serverConfig.module.rules = serverConfig.module.rules.filter(
    x => x.loader !== 'null-loader',
  );
  serverConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    new webpack.NamedModulesPlugin(),
  );

  // Configure compilation
  await run(clean);
  const multiCompiler = webpack(webpackConfig);
  const clientCompiler = multiCompiler.compilers.find(
    compiler => compiler.name === 'client',
  );
  const serverCompiler = multiCompiler.compilers.find(
    compiler => compiler.name === 'server',
  );
  const clientPromise = createCompilationPromise(
    'client',
    clientCompiler,
    clientConfig,
  );
  const serverPromise = createCompilationPromise(
    'server',
    serverCompiler,
    serverConfig,
  );

  // https://github.com/webpack/webpack-dev-middleware
  server.use(
    webpackDevMiddleware(clientCompiler, {
      publicPath: clientConfig.output.publicPath,
      quiet: true,
      watchOptions,
    }),
  );

  // https://github.com/glenjamin/webpack-hot-middleware
  server.use(webpackHotMiddleware(clientCompiler, { log: false }));

  let appPromise;
  let appPromiseResolve;
  let appPromiseIsResolved = true;
  serverCompiler.plugin('compile', () => {
    if (!appPromiseIsResolved) return;
    appPromiseIsResolved = false;
    // eslint-disable-next-line no-return-assign
    appPromise = new Promise(resolve => (appPromiseResolve = resolve));
  });

  let app;
  server.use((req, res) => {
    appPromise
      .then(() => app.handle(req, res))
      .catch(error => console.error(error));
  });

  function checkForUpdate(fromUpdate) {
    const hmrPrefix = '[\x1b[35mHMR\x1b[0m] ';
    if (!app.hot) {
      throw new Error(`${hmrPrefix}Hot Module Replacement is disabled.`);
    }
    if (app.hot.status() !== 'idle') {
      return Promise.resolve();
    }
    return app.hot
      .check(true)
      .then(updatedModules => {
        if (!updatedModules) {
          if (fromUpdate) {
            console.info(`${hmrPrefix}Update applied.`);
          }
          return;
        }
        if (updatedModules.length === 0) {
          console.info(`${hmrPrefix}Nothing hot updated.`);
        } else {
          console.info(`${hmrPrefix}Updated modules:`);
          updatedModules.forEach(moduleId =>
            console.info(`${hmrPrefix} - ${moduleId}`),
          );
          checkForUpdate(true);
        }
      })
      .catch(error => {
        if (['abort', 'fail'].includes(app.hot.status())) {
          console.warn(`${hmrPrefix}Cannot apply update.`);
          delete require.cache[require.resolve('../build/server')];
          // eslint-disable-next-line global-require, import/no-unresolved
          app = require('../build/server').default;
          console.warn(`${hmrPrefix}App has been reloaded.`);
        } else {
          console.warn(
            `${hmrPrefix}Update failed: ${error.stack || error.message}`,
          );
        }
      });
  }

  serverCompiler.watch(watchOptions, (error, stats) => {
    if (app && !error && !stats.hasErrors()) {
      checkForUpdate().then(() => {
        appPromiseIsResolved = true;
        appPromiseResolve();
      });
    }
  });

  // Wait until both client-side and server-side bundles are ready
  await clientPromise;
  await serverPromise;

  const timeStart = new Date();
  console.info(`[${format(timeStart)}] Launching server...`);

  // Load compiled src/server.js as a middleware
  // eslint-disable-next-line global-require, import/no-unresolved
  app = require('../build/server').default;
  appPromiseIsResolved = true;
  appPromiseResolve();

  // Launch the development server with Browsersync and HMR
  await new Promise((resolve, reject) =>
    browserSync.create().init(
      {
        // https://www.browsersync.io/docs/options
        server: 'src/server.js',
        middleware: [server],
        open: !process.argv.includes('--silent'),
        ...(isDebug ? {} : { notify: false, ui: false }),
      },
      (error, bs) => (error ? reject(error) : resolve(bs)),
    ),
  );

  const timeEnd = new Date();
  const time = timeEnd.getTime() - timeStart.getTime();
  console.info(`[${format(timeEnd)}] Server launched after ${time} ms`);
  return server;
}

export default start;
<|endoftext|>


<commit_before>function painterSortStable( a, b ) {

	if ( a.groupOrder !== b.groupOrder ) {

		return a.groupOrder - b.groupOrder;

	} else if ( a.renderOrder !== b.renderOrder ) {

		return a.renderOrder - b.renderOrder;

	} else if ( a.program !== b.program ) {

		return a.program.id - b.program.id;

	} else if ( a.material.id !== b.material.id ) {

		return a.material.id - b.material.id;

	} else if ( a.z !== b.z ) {

		return a.z - b.z;

	} else {

		return a.id - b.id;

	}

}

function reversePainterSortStable( a, b ) {

	if ( a.groupOrder !== b.groupOrder ) {

		return a.groupOrder - b.groupOrder;

	} else if ( a.renderOrder !== b.renderOrder ) {

		return a.renderOrder - b.renderOrder;

	} else if ( a.z !== b.z ) {

		return b.z - a.z;

	} else {

		return a.id - b.id;

	}

}

function WebGLRenderList( properties ) {

	const renderItems = [];
	let renderItemsIndex = 0;

	const opaque = [];
	const transmissive = [];
	const transparent = [];

	const defaultProgram = { id: - 1 };

	function init() {

		renderItemsIndex = 0;

		opaque.length = 0;
		transmissive.length = 0;
		transparent.length = 0;

	}

	function getNextRenderItem( object, geometry, material, groupOrder, z, group ) {

		let renderItem = renderItems[ renderItemsIndex ];
		const materialProperties = properties.get( material );

		if ( renderItem === undefined ) {

			renderItem = {
				id: object.id,
				object: object,
				geometry: geometry,
				material: material,
				program: materialProperties.program || defaultProgram,
				groupOrder: groupOrder,
				renderOrder: object.renderOrder,
				z: z,
				group: group
			};

			renderItems[ renderItemsIndex ] = renderItem;

		} else {

			renderItem.id = object.id;
			renderItem.object = object;
			renderItem.geometry = geometry;
			renderItem.material = material;
			renderItem.program = materialProperties.program || defaultProgram;
			renderItem.groupOrder = groupOrder;
			renderItem.renderOrder = object.renderOrder;
			renderItem.z = z;
			renderItem.group = group;

		}

		renderItemsIndex ++;

		return renderItem;

	}

	function push( object, geometry, material, groupOrder, z, group ) {

		const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );

		if ( material.transmission > 0.0 ) {

			transmissive.push( renderItem );

		} else if ( material.transparent === true ) {

			transparent.push( renderItem );

		} else {

			opaque.push( renderItem );

		}

	}

	function unshift( object, geometry, material, groupOrder, z, group ) {

		const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );

		if ( material.transmission > 0.0 ) {

			transmissive.unshift( renderItem );

		} else if ( material.transparent === true ) {

			transparent.unshift( renderItem );

		} else {

			opaque.unshift( renderItem );

		}

	}

	function sort( customOpaqueSort, customTransparentSort ) {

		if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable );
		if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable );
		if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable );

	}

	function finish() {

		// Clear references from inactive renderItems in the list

		for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) {

			const renderItem = renderItems[ i ];

			if ( renderItem.id === null ) break;

			renderItem.id = null;
			renderItem.object = null;
			renderItem.geometry = null;
			renderItem.material = null;
			renderItem.program = null;
			renderItem.group = null;

		}

	}

	return {

		opaque: opaque,
		transmissive: transmissive,
		transparent: transparent,

		init: init,
		push: push,
		unshift: unshift,
		finish: finish,

		sort: sort
	};

}

function WebGLRenderLists( properties ) {

	let lists = new WeakMap();

	function get( scene, renderCallDepth ) {

		let list;

		if ( lists.has( scene ) === false ) {

			list = new WebGLRenderList( properties );
			lists.set( scene, [ list ] );

		} else {

			if ( renderCallDepth >= lists.get( scene ).length ) {

				list = new WebGLRenderList( properties );
				lists.get( scene ).push( list );

			} else {

				list = lists.get( scene )[ renderCallDepth ];

			}

		}

		return list;

	}

	function dispose() {

		lists = new WeakMap();

	}

	return {
		get: get,
		dispose: dispose
	};

}

export { WebGLRenderLists, WebGLRenderList };
<commit_msg>WebGLRenderLists: Remove usage of program ref.<commit_after>function painterSortStable( a, b ) {

	if ( a.groupOrder !== b.groupOrder ) {

		return a.groupOrder - b.groupOrder;

	} else if ( a.renderOrder !== b.renderOrder ) {

		return a.renderOrder - b.renderOrder;

	} else if ( a.material.id !== b.material.id ) {

		return a.material.id - b.material.id;

	} else if ( a.z !== b.z ) {

		return a.z - b.z;

	} else {

		return a.id - b.id;

	}

}

function reversePainterSortStable( a, b ) {

	if ( a.groupOrder !== b.groupOrder ) {

		return a.groupOrder - b.groupOrder;

	} else if ( a.renderOrder !== b.renderOrder ) {

		return a.renderOrder - b.renderOrder;

	} else if ( a.z !== b.z ) {

		return b.z - a.z;

	} else {

		return a.id - b.id;

	}

}

function WebGLRenderList( properties ) {

	const renderItems = [];
	let renderItemsIndex = 0;

	const opaque = [];
	const transmissive = [];
	const transparent = [];

	function init() {

		renderItemsIndex = 0;

		opaque.length = 0;
		transmissive.length = 0;
		transparent.length = 0;

	}

	function getNextRenderItem( object, geometry, material, groupOrder, z, group ) {

		let renderItem = renderItems[ renderItemsIndex ];

		if ( renderItem === undefined ) {

			renderItem = {
				id: object.id,
				object: object,
				geometry: geometry,
				material: material,
				groupOrder: groupOrder,
				renderOrder: object.renderOrder,
				z: z,
				group: group
			};

			renderItems[ renderItemsIndex ] = renderItem;

		} else {

			renderItem.id = object.id;
			renderItem.object = object;
			renderItem.geometry = geometry;
			renderItem.material = material;
			renderItem.groupOrder = groupOrder;
			renderItem.renderOrder = object.renderOrder;
			renderItem.z = z;
			renderItem.group = group;

		}

		renderItemsIndex ++;

		return renderItem;

	}

	function push( object, geometry, material, groupOrder, z, group ) {

		const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );

		if ( material.transmission > 0.0 ) {

			transmissive.push( renderItem );

		} else if ( material.transparent === true ) {

			transparent.push( renderItem );

		} else {

			opaque.push( renderItem );

		}

	}

	function unshift( object, geometry, material, groupOrder, z, group ) {

		const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );

		if ( material.transmission > 0.0 ) {

			transmissive.unshift( renderItem );

		} else if ( material.transparent === true ) {

			transparent.unshift( renderItem );

		} else {

			opaque.unshift( renderItem );

		}

	}

	function sort( customOpaqueSort, customTransparentSort ) {

		if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable );
		if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable );
		if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable );

	}

	function finish() {

		// Clear references from inactive renderItems in the list

		for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) {

			const renderItem = renderItems[ i ];

			if ( renderItem.id === null ) break;

			renderItem.id = null;
			renderItem.object = null;
			renderItem.geometry = null;
			renderItem.material = null;
			renderItem.group = null;

		}

	}

	return {

		opaque: opaque,
		transmissive: transmissive,
		transparent: transparent,

		init: init,
		push: push,
		unshift: unshift,
		finish: finish,

		sort: sort
	};

}

function WebGLRenderLists( properties ) {

	let lists = new WeakMap();

	function get( scene, renderCallDepth ) {

		let list;

		if ( lists.has( scene ) === false ) {

			list = new WebGLRenderList( properties );
			lists.set( scene, [ list ] );

		} else {

			if ( renderCallDepth >= lists.get( scene ).length ) {

				list = new WebGLRenderList( properties );
				lists.get( scene ).push( list );

			} else {

				list = lists.get( scene )[ renderCallDepth ];

			}

		}

		return list;

	}

	function dispose() {

		lists = new WeakMap();

	}

	return {
		get: get,
		dispose: dispose
	};

}

export { WebGLRenderLists, WebGLRenderList };
<|endoftext|>


<commit_before>import setToPixelCoordinateSystem from '../setToPixelCoordinateSystem.js';
import now from '../internal/now.js';
import initializeRenderCanvas from './initializeRenderCanvas.js';
import getLut from './getLut.js';
import saveLastRendered from './saveLastRendered.js';
import doesImageNeedToBeRendered from './doesImageNeedToBeRendered.js';
import storedPixelDataToCanvasImageDataPseudocolorLUT from '../internal/storedPixelDataToCanvasImageDataPseudocolorLUT.js';
import colors from '../colors/index.js';

/**
 * Returns an appropriate canvas to render the Image. If the canvas available in the cache is appropriate
 * it is returned, otherwise adjustments are made. It also sets the color transfer functions.
 *
 * @param {Object} enabledElement The cornerstone enabled element
 * @param {Object} image The image to be rendered
 * @param {Boolean} invalidated Is pixel data valid
 * @returns {HTMLCanvasElement} An appropriate canvas for rendering the image
 * @memberof rendering
 */
function getRenderCanvas (enabledElement, image, invalidated) {
  if (!enabledElement.renderingTools.renderCanvas) {
    enabledElement.renderingTools.renderCanvas = document.createElement('canvas');
  }

  const renderCanvas = enabledElement.renderingTools.renderCanvas;

  let colormap = enabledElement.viewport.colormap || enabledElement.options.colormap;

  if (enabledElement.options.colormap) {
    console.warn('enabledElement.options.colormap is deprecated. Use enabledElement.viewport.colormap instead');
  }
  if (colormap && (typeof colormap === 'string')) {
    colormap = colors.getColormap(colormap);
  }

  if (!colormap) {
    throw new Error('renderPseudoColorImage: colormap not found.');
  }

  const colormapId = colormap.getId();

  if (doesImageNeedToBeRendered(enabledElement, image) === false && invalidated !== true &&
    enabledElement.renderingTools.colormapId === colormapId) {
    return renderCanvas;
  }

  // If our render canvas does not match the size of this image reset it
  // NOTE: This might be inefficient if we are updating multiple images of different
  // Sizes frequently.
  if (renderCanvas.width !== image.width || renderCanvas.height !== image.height) {
    initializeRenderCanvas(enabledElement, image);
  }

  // Get the lut to use
  let start = now();

  if (!enabledElement.renderingTools.colorLut || invalidated ||
    enabledElement.renderingTools.colormapId !== colormapId) {
    colormap.setNumberOfColors(256);
    enabledElement.renderingTools.colorLut = colormap.createLookupTable();
    enabledElement.renderingTools.colormapId = colormapId;
  }

  const lut = getLut(image, enabledElement.viewport, invalidated);

  image.stats = image.stats || {};
  image.stats.lastLutGenerateTime = now() - start;

  const colorLut = enabledElement.renderingTools.colorLut;
  const renderCanvasData = enabledElement.renderingTools.renderCanvasData;
  const renderCanvasContext = enabledElement.renderingTools.renderCanvasContext;

  storedPixelDataToCanvasImageDataPseudocolorLUT(image, lut, colorLut, renderCanvasData.data);

  start = now();
  renderCanvasContext.putImageData(renderCanvasData, 0, 0);
  image.stats.lastPutImageDataTime = now() - start;

  return renderCanvas;
}

/**
 * API function to draw a pseudo-color image to a given enabledElement
 *
 * @param {EnabledElement} enabledElement The Cornerstone Enabled Element to redraw
 * @param {Boolean} invalidated - true if pixel data has been invalidated and cached rendering should not be used
 * @returns {void}
 * @memberof rendering
 */
export function renderPseudoColorImage (enabledElement, invalidated) {
  if (enabledElement === undefined) {
    throw new Error('drawImage: enabledElement parameter must not be undefined');
  }

  const image = enabledElement.image;

  if (image === undefined) {
    throw new Error('drawImage: image must be loaded before it can be drawn');
  }

  // Get the canvas context and reset the transform
  const context = enabledElement.canvas.getContext('2d');

  context.setTransform(1, 0, 0, 1, 0, 0);

  // Clear the canvas
  context.fillStyle = 'black';
  context.fillRect(0, 0, enabledElement.canvas.width, enabledElement.canvas.height);

  // Turn off image smooth/interpolation if pixelReplication is set in the viewport
  context.imageSmoothingEnabled = !enabledElement.viewport.pixelReplication;
  context.mozImageSmoothingEnabled = context.imageSmoothingEnabled;

  // Save the canvas context state and apply the viewport properties
  setToPixelCoordinateSystem(enabledElement, context);

  // If no options are set we will retrieve the renderCanvas through the
  // Normal Canvas rendering path
  // TODO: Add WebGL support for pseudocolor pipeline
  const renderCanvas = getRenderCanvas(enabledElement, image, invalidated);

  const sx = enabledElement.viewport.displayedArea.tlhc.x - 1;
  const sy = enabledElement.viewport.displayedArea.tlhc.y - 1;
  const width = enabledElement.viewport.displayedArea.brhc.x - sx;
  const height = enabledElement.viewport.displayedArea.brhc.y - sy;

  context.drawImage(renderCanvas, sx, sy, width, height, 0, 0, width, height);

  enabledElement.renderingTools = saveLastRendered(enabledElement);
}

/**
 * API function to draw a pseudo-color image to a given layer
 *
 * @param {EnabledElementLayer} layer The layer that the image will be added to
 * @param {Boolean} invalidated - true if pixel data has been invalidated and cached rendering should not be used
 * @returns {void}
 */
export function addPseudoColorLayer (layer, invalidated) {
  if (layer === undefined) {
    throw new Error('addPseudoColorLayer: layer parameter must not be undefined');
  }

  const image = layer.image;

  if (image === undefined) {
    throw new Error('addPseudoColorLayer: image must be loaded before it can be drawn');
  }

  layer.canvas = getRenderCanvas(layer, image, invalidated);

  const context = layer.canvas.getContext('2d');

  // Turn off image smooth/interpolation if pixelReplication is set in the viewport
  context.imageSmoothingEnabled = !layer.viewport.pixelReplication;
  context.mozImageSmoothingEnabled = context.imageSmoothingEnabled;

  layer.renderingTools = saveLastRendered(layer);
}
<commit_msg>fix: Check if the enabledElement.options exists before checking .colormap (#412)<commit_after>import setToPixelCoordinateSystem from '../setToPixelCoordinateSystem.js';
import now from '../internal/now.js';
import initializeRenderCanvas from './initializeRenderCanvas.js';
import getLut from './getLut.js';
import saveLastRendered from './saveLastRendered.js';
import doesImageNeedToBeRendered from './doesImageNeedToBeRendered.js';
import storedPixelDataToCanvasImageDataPseudocolorLUT from '../internal/storedPixelDataToCanvasImageDataPseudocolorLUT.js';
import colors from '../colors/index.js';

/**
 * Returns an appropriate canvas to render the Image. If the canvas available in the cache is appropriate
 * it is returned, otherwise adjustments are made. It also sets the color transfer functions.
 *
 * @param {Object} enabledElement The cornerstone enabled element
 * @param {Object} image The image to be rendered
 * @param {Boolean} invalidated Is pixel data valid
 * @returns {HTMLCanvasElement} An appropriate canvas for rendering the image
 * @memberof rendering
 */
function getRenderCanvas (enabledElement, image, invalidated) {
  if (!enabledElement.renderingTools.renderCanvas) {
    enabledElement.renderingTools.renderCanvas = document.createElement('canvas');
  }

  const renderCanvas = enabledElement.renderingTools.renderCanvas;

  let colormap = enabledElement.viewport.colormap || enabledElement.options.colormap;

  if (enabledElement.options && enabledElement.options.colormap) {
    console.warn('enabledElement.options.colormap is deprecated. Use enabledElement.viewport.colormap instead');
  }
  if (colormap && (typeof colormap === 'string')) {
    colormap = colors.getColormap(colormap);
  }

  if (!colormap) {
    throw new Error('renderPseudoColorImage: colormap not found.');
  }

  const colormapId = colormap.getId();

  if (doesImageNeedToBeRendered(enabledElement, image) === false && invalidated !== true &&
    enabledElement.renderingTools.colormapId === colormapId) {
    return renderCanvas;
  }

  // If our render canvas does not match the size of this image reset it
  // NOTE: This might be inefficient if we are updating multiple images of different
  // Sizes frequently.
  if (renderCanvas.width !== image.width || renderCanvas.height !== image.height) {
    initializeRenderCanvas(enabledElement, image);
  }

  // Get the lut to use
  let start = now();

  if (!enabledElement.renderingTools.colorLut || invalidated ||
    enabledElement.renderingTools.colormapId !== colormapId) {
    colormap.setNumberOfColors(256);
    enabledElement.renderingTools.colorLut = colormap.createLookupTable();
    enabledElement.renderingTools.colormapId = colormapId;
  }

  const lut = getLut(image, enabledElement.viewport, invalidated);

  image.stats = image.stats || {};
  image.stats.lastLutGenerateTime = now() - start;

  const colorLut = enabledElement.renderingTools.colorLut;
  const renderCanvasData = enabledElement.renderingTools.renderCanvasData;
  const renderCanvasContext = enabledElement.renderingTools.renderCanvasContext;

  storedPixelDataToCanvasImageDataPseudocolorLUT(image, lut, colorLut, renderCanvasData.data);

  start = now();
  renderCanvasContext.putImageData(renderCanvasData, 0, 0);
  image.stats.lastPutImageDataTime = now() - start;

  return renderCanvas;
}

/**
 * API function to draw a pseudo-color image to a given enabledElement
 *
 * @param {EnabledElement} enabledElement The Cornerstone Enabled Element to redraw
 * @param {Boolean} invalidated - true if pixel data has been invalidated and cached rendering should not be used
 * @returns {void}
 * @memberof rendering
 */
export function renderPseudoColorImage (enabledElement, invalidated) {
  if (enabledElement === undefined) {
    throw new Error('drawImage: enabledElement parameter must not be undefined');
  }

  const image = enabledElement.image;

  if (image === undefined) {
    throw new Error('drawImage: image must be loaded before it can be drawn');
  }

  // Get the canvas context and reset the transform
  const context = enabledElement.canvas.getContext('2d');

  context.setTransform(1, 0, 0, 1, 0, 0);

  // Clear the canvas
  context.fillStyle = 'black';
  context.fillRect(0, 0, enabledElement.canvas.width, enabledElement.canvas.height);

  // Turn off image smooth/interpolation if pixelReplication is set in the viewport
  context.imageSmoothingEnabled = !enabledElement.viewport.pixelReplication;
  context.mozImageSmoothingEnabled = context.imageSmoothingEnabled;

  // Save the canvas context state and apply the viewport properties
  setToPixelCoordinateSystem(enabledElement, context);

  // If no options are set we will retrieve the renderCanvas through the
  // Normal Canvas rendering path
  // TODO: Add WebGL support for pseudocolor pipeline
  const renderCanvas = getRenderCanvas(enabledElement, image, invalidated);

  const sx = enabledElement.viewport.displayedArea.tlhc.x - 1;
  const sy = enabledElement.viewport.displayedArea.tlhc.y - 1;
  const width = enabledElement.viewport.displayedArea.brhc.x - sx;
  const height = enabledElement.viewport.displayedArea.brhc.y - sy;

  context.drawImage(renderCanvas, sx, sy, width, height, 0, 0, width, height);

  enabledElement.renderingTools = saveLastRendered(enabledElement);
}

/**
 * API function to draw a pseudo-color image to a given layer
 *
 * @param {EnabledElementLayer} layer The layer that the image will be added to
 * @param {Boolean} invalidated - true if pixel data has been invalidated and cached rendering should not be used
 * @returns {void}
 */
export function addPseudoColorLayer (layer, invalidated) {
  if (layer === undefined) {
    throw new Error('addPseudoColorLayer: layer parameter must not be undefined');
  }

  const image = layer.image;

  if (image === undefined) {
    throw new Error('addPseudoColorLayer: image must be loaded before it can be drawn');
  }

  layer.canvas = getRenderCanvas(layer, image, invalidated);

  const context = layer.canvas.getContext('2d');

  // Turn off image smooth/interpolation if pixelReplication is set in the viewport
  context.imageSmoothingEnabled = !layer.viewport.pixelReplication;
  context.mozImageSmoothingEnabled = context.imageSmoothingEnabled;

  layer.renderingTools = saveLastRendered(layer);
}
<|endoftext|>