<commit_before>var viewer;

// Many thanks
function initialize() {

  // Get our access token from the internal web-service API

  $.get("http://" + window.location.host + '/api/token',
    function (accessToken) {

      var options = {};
      options.env = "AutodeskProduction";
      options.accessToken = accessToken;
      options.document =
        "urn:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6c3RlYW1idWNrL1NwTTNXNy5mM2Q=";

      // Create and initialize our 3D viewer

      var elem = document.getElementById('viewer3d');
      viewer = new Autodesk.Viewing.Viewer3D(elem, {});
      viewer.initialize();

      // Go with the "Riverbank" lighting and background effect

      viewer.impl.setLightPreset(8);

      // We have a heavy model, so let's save some work during
      // navigation

      viewer.setOptimizeNavigation(true);

      // Let's zoom in and out of the pivot - the screen
      // real estate is fairly limited - and reverse the
      // zoom direction

      viewer.navigation.setZoomTowardsPivot(true);
      viewer.navigation.setReverseZoomDirection(true);

      Autodesk.Viewing.Initializer(options, function () {
        loadDocument(viewer, options.document);
      });
    }
  );

  // Set up some UI elements for the Steampunk UI

  $("#ui").find("div").attr("id", "over");
  $("#window").wrapInner("
");

  // Store positions

  var overPositions =
        {
          entirety: 0, engine: 75, body: 152,
          interior: 230, wheels: 310
        },
      cogPositions =
        {
          entirety: 5, engine: 80, body: 154,
          interior: 235, wheels: 310
        },
      previousCogPosition = 0;

  // Animation function

  function animator(pointer, callback) {

    // Move cog

    $("#cog").animate({
      "translateY": parseInt(cogPositions[pointer]),
      "rotate":
        (parseInt(cogPositions[pointer]) < previousCogPosition) ?
        "-=365" : "+=365"
    }, function () {
      previousCogPosition = cogPositions[pointer];
    });

    // Move over

    $("#over").animate({
      "translateY": parseInt(overPositions[pointer])
    });

    // Add a delay so the camera changes after the cog has stopped
    // whirring

    if (callback) {
      setTimeout(function () { callback(); }, 400);
    }
  }

  // Is there a hash?

  if (window.location.hash) {

    // Store the hash

    var hash = window.location.hash.split("#")[1];

    // Position over

    animator(hash);
  }

  // Add transitions

  $("#ui a").click(function (e) {
    e.preventDefault();

    // Store new pointer

    var pointer = $(this).attr("href").split("#")[1];

    // Call animation function

    animator(pointer, function () {
      if (pointer === "entirety") {
        zoomEntirety();
      } else if (pointer === "engine") {
        zoomEngine();
      } else if (pointer === "body") {
        zoomBody();
      } else if (pointer === "interior") {
        zoomInterior();
      } else if (pointer === "wheels") {
        zoomWheels();
      }
    });
  });
}

// Helper functions to zoom into a specific part of the model

function zoomEntirety() {
  zoom(-48722.5, -54872, 44704.8, 10467.3, 1751.8, 1462.8);
}
function zoomEngine() {
  zoom(-17484, -364, 4568, 12927, 173, 1952);
}
function zoomBody() {
  zoom(53143, -7200, 5824, 12870, -327.5, 1674);
}
function zoomInterior() {
  zoom(20459, -19227, 19172.5, 13845, 1228.6, 2906);
}
function zoomWheels() {
  zoom(260.3, 26327, 954, 371.5, 134, 2242.7);
}

// Set the camera based on a position and target location

function zoom(px, py, pz, tx, ty, tz) {

  // Make sure our up vector is correct for this model

  viewer.navigation.setWorldUpVector(new THREE.Vector3(0, 0, 1));

  // This performs a smooth view transition (we might also use
  // setView() to get there more directly)

  viewer.navigation.setRequestTransition(
    true,
    new THREE.Vector3(px, py, pz), new THREE.Vector3(tx, ty, tz),
    viewer.getFOV()
  );
}

// Progress listener to set the view once the data has started
// loading properly (we get a 5% notification early on that we
// need to ignore - it comes too soon)

function progressListener(param) {

  if (param.percent > 0.1 && param.percent < 5) {

    // Remove the listener once called - one-time operation

    viewer.removeEventListener("progress", progressListener);

    // Iterate the materials to change any red ones to grey

    for (var p in viewer.impl.materials) {
      var m = viewer.impl.materials[p];
      if (m.color.r >= 0.5 && m.color.g == 0 && m.color.b == 0) {
        m.color.r = m.color.g = m.color.b = 0.5;
        m.needsUpdate = true;
      }
    }

    // Zoom to the overal view initially

    zoomEntirety();
  }
}

function loadDocument(viewer, docId) {

  var path = VIEWING_URL + '/' + docId.substr(4);

  Autodesk.Viewing.Document.load(path,
    function (document) {
      var geometryItems = [];

      if (geometryItems.length == 0) {
        geometryItems =
          Autodesk.Viewing.Document.getSubItemsWithProperties(
            document.getRootItem(),
            { 'type': 'geometry', 'role': '3d' },
            true
          );
      }
      if (geometryItems.length > 0) {
        viewer.load(document.getViewablePath(geometryItems[0]));
      }

      viewer.addEventListener("progress", progressListener);
    },
    function (errorMsg, httpErrorCode) {
      var container = document.getElementById('viewer3d');
      if (container) {
        alert("Load error " + errorMsg);
      }
    }
  );
}
<commit_msg>Attributed/credited source of UI code/artwork<commit_after>var viewer;

// Many thanks to Dan Wellman (@danwellman). Not only did he write
// the excellent post that formed the basis for this application's
// Steampunk UI, he provided the artwork to help build a custom
// version...
// http://www.dmxzone.com/go/18220/an-image-viewer-with-the-dmxzone-universal-css-transforms-library/

function initialize() {

  // Get our access token from the internal web-service API

  $.get("http://" + window.location.host + '/api/token',
    function (accessToken) {

      var options = {};
      options.env = "AutodeskProduction";
      options.accessToken = accessToken;
      options.document =
        "urn:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6c3RlYW1idWNrL1NwTTNXNy5mM2Q=";

      // Create and initialize our 3D viewer

      var elem = document.getElementById('viewer3d');
      viewer = new Autodesk.Viewing.Viewer3D(elem, {});
      viewer.initialize();

      // Go with the "Riverbank" lighting and background effect

      viewer.impl.setLightPreset(8);

      // We have a heavy model, so let's save some work during
      // navigation

      viewer.setOptimizeNavigation(true);

      // Let's zoom in and out of the pivot - the screen
      // real estate is fairly limited - and reverse the
      // zoom direction

      viewer.navigation.setZoomTowardsPivot(true);
      viewer.navigation.setReverseZoomDirection(true);

      Autodesk.Viewing.Initializer(options, function () {
        loadDocument(viewer, options.document);
      });
    }
  );

  // Set up some UI elements for the Steampunk UI

  $("#ui").find("div").attr("id", "over");
  $("#window").wrapInner("
");

  // Store positions

  var overPositions =
        {
          entirety: 0, engine: 75, body: 152,
          interior: 230, wheels: 310
        },
      cogPositions =
        {
          entirety: 5, engine: 80, body: 154,
          interior: 235, wheels: 310
        },
      previousCogPosition = 0;

  // Animation function

  function animator(pointer, callback) {

    // Move cog

    $("#cog").animate({
      "translateY": parseInt(cogPositions[pointer]),
      "rotate":
        (parseInt(cogPositions[pointer]) < previousCogPosition) ?
        "-=365" : "+=365"
    }, function () {
      previousCogPosition = cogPositions[pointer];
    });

    // Move over

    $("#over").animate({
      "translateY": parseInt(overPositions[pointer])
    });

    // Add a delay so the camera changes after the cog has stopped
    // whirring

    if (callback) {
      setTimeout(function () { callback(); }, 400);
    }
  }

  // Is there a hash?

  if (window.location.hash) {

    // Store the hash

    var hash = window.location.hash.split("#")[1];

    // Position over

    animator(hash);
  }

  // Add transitions

  $("#ui a").click(function (e) {
    e.preventDefault();

    // Store new pointer

    var pointer = $(this).attr("href").split("#")[1];

    // Call animation function

    animator(pointer, function () {
      if (pointer === "entirety") {
        zoomEntirety();
      } else if (pointer === "engine") {
        zoomEngine();
      } else if (pointer === "body") {
        zoomBody();
      } else if (pointer === "interior") {
        zoomInterior();
      } else if (pointer === "wheels") {
        zoomWheels();
      }
    });
  });
}

// Helper functions to zoom into a specific part of the model

function zoomEntirety() {
  zoom(-48722.5, -54872, 44704.8, 10467.3, 1751.8, 1462.8);
}
function zoomEngine() {
  zoom(-17484, -364, 4568, 12927, 173, 1952);
}
function zoomBody() {
  zoom(53143, -7200, 5824, 12870, -327.5, 1674);
}
function zoomInterior() {
  zoom(20459, -19227, 19172.5, 13845, 1228.6, 2906);
}
function zoomWheels() {
  zoom(260.3, 26327, 954, 371.5, 134, 2242.7);
}

// Set the camera based on a position and target location

function zoom(px, py, pz, tx, ty, tz) {

  // Make sure our up vector is correct for this model

  viewer.navigation.setWorldUpVector(new THREE.Vector3(0, 0, 1));

  // This performs a smooth view transition (we might also use
  // setView() to get there more directly)

  viewer.navigation.setRequestTransition(
    true,
    new THREE.Vector3(px, py, pz), new THREE.Vector3(tx, ty, tz),
    viewer.getFOV()
  );
}

// Progress listener to set the view once the data has started
// loading properly (we get a 5% notification early on that we
// need to ignore - it comes too soon)

function progressListener(param) {

  if (param.percent > 0.1 && param.percent < 5) {

    // Remove the listener once called - one-time operation

    viewer.removeEventListener("progress", progressListener);

    // Iterate the materials to change any red ones to grey

    for (var p in viewer.impl.materials) {
      var m = viewer.impl.materials[p];
      if (m.color.r >= 0.5 && m.color.g == 0 && m.color.b == 0) {
        m.color.r = m.color.g = m.color.b = 0.5;
        m.needsUpdate = true;
      }
    }

    // Zoom to the overal view initially

    zoomEntirety();
  }
}

function loadDocument(viewer, docId) {

  var path = VIEWING_URL + '/' + docId.substr(4);

  Autodesk.Viewing.Document.load(path,
    function (document) {
      var geometryItems = [];

      if (geometryItems.length == 0) {
        geometryItems =
          Autodesk.Viewing.Document.getSubItemsWithProperties(
            document.getRootItem(),
            { 'type': 'geometry', 'role': '3d' },
            true
          );
      }
      if (geometryItems.length > 0) {
        viewer.load(document.getViewablePath(geometryItems[0]));
      }

      viewer.addEventListener("progress", progressListener);
    },
    function (errorMsg, httpErrorCode) {
      var container = document.getElementById('viewer3d');
      if (container) {
        alert("Load error " + errorMsg);
      }
    }
  );
}
<|endoftext|>


<commit_before>var	socket = io();

io.emit('getGroups', function(){
	// puts initial groups down
});

io.emit('getAnnouncements', function(){
	// puts initial announcements down
});

socket.on('groupMessage', function(user, group, message){
	// put down group message
});

socket.on('announcement', function(user, announcement){
	// put down annoucement
});

socket.on('newGroup', function(newGroup){
	// put down new group
});

//sends message to server
function sendMessage(user, group, messages){
	io.emit('groupMessage', user, group, messsage);
}

//sends announcement to server
function sendAnnouncement(user, announcement){
	io.emit('announcement', user, announcement);
}

function getNewUser(user, pass){
	return io.emit('newUser', user, pass, function(user){
		return user;
	});javascript:;
}

function createNewGroup(user, groupName){
	io.emit('newGroup', user, groupName);
}

function getUsers(){
	io.emit('getUsers', function(userList){
		return userList;
	});
}
<commit_msg>Fixed code magically appearing into a file<commit_after>var	socket = io();

io.emit('getGroups', function(){
	// puts initial groups down
});

io.emit('getAnnouncements', function(){
	// puts initial announcements down
});

socket.on('groupMessage', function(user, group, message){
	// put down group message
});

socket.on('announcement', function(user, announcement){
	// put down annoucement
});

socket.on('newGroup', function(newGroup){
	// put down new group
});

//sends message to server
function sendMessage(user, group, messages){
	io.emit('groupMessage', user, group, messsage);
}

//sends announcement to server
function sendAnnouncement(user, announcement){
	io.emit('announcement', user, announcement);
}

function getNewUser(user, pass){
	return io.emit('newUser', user, pass, function(user){
		return user;
	});
}

function createNewGroup(user, groupName){
	io.emit('newGroup', user, groupName);
}

function getUsers(){
	io.emit('getUsers', function(userList){
		return userList;
	});
}
<|endoftext|>


<commit_before>var Arrow = require('arrow'),
    _ = require('lodash');

var Query = require('../utils/Query').Query,
    collection = require('../utils/Collection').Collection;
/**
 * Creates models from your schema (see "fetchSchema" for more information on the schema).
 */
exports.createModelsFromSchema = function () {
    var self = this,
        models = {};
    // TODO: Iterate through the models in your schema.
    Object.keys(self.schema).forEach(function (modelName) {
        var object = self.schema[modelName],
            fields = {};
        object.fields.forEach(function (fieldName) {
            var field = fieldName;//object.fields[fieldName];
            if (fieldName !== 'id') {
                fields[fieldName] = {
                    type: String, //Should do some sort of filed type check
                    required: false //should do some sort of field check, FALSE - since the DB is schema less
                };
            }
        });

        models[self.name + '/' + modelName] = Arrow.Model.extend(self.name + '/' + modelName, {
            name: self.name + '/' + modelName,
            autogen: !!self.config.modelAutogen, // Controls if APIs are automatically created for this model.
            fields: fields,
            connector: self,
            generated: true,
            visible: true
        });
    });
    self.models = _.defaults(self.models || {}, models);
};
<commit_msg>[ARRSOF-18] appc.loki.js generated models overlap with user defined ones<commit_after>var Arrow = require('arrow'),
    _ = require('lodash'),
    fs = require('fs');

var Query = require('../utils/Query').Query,
    collection = require('../utils/Collection').Collection;
/**
 * Creates models from your schema (see "fetchSchema" for more information on the schema).
 */
exports.createModelsFromSchema = function () {
    var self = this,
        models = {},
        processPath = process.cwd(),
        modelsDir = processPath + '/models';
    var predefinedModels = [];

    try {
        (fs.readdirSync(modelsDir).map((fileName) => {
            let __model = require(modelsDir + '/' + fileName);
            if (__model) {
                //register models name
                predefinedModels.push(__model.name);
            }
        }));
    } catch (error) {
        //... Nothing here
    }
    Object.keys(self.schema).forEach(function (modelName) {
        var object = self.schema[modelName],
            fields = {};
        object.fields.forEach(function (fieldName) {
            var field = fieldName;//object.fields[fieldName];
            if (fieldName !== 'id') {
                fields[fieldName] = {
                    type: String, //Should do some sort of filed type check
                    required: false //should do some sort of field check, FALSE - since the DB is schema less
                };
            }
        });
        if (_.indexOf(predefinedModels, self.name + '/' + modelName) === -1) {
            models[self.name + '/' + modelName] = Arrow.Model.extend(self.name + '/' + modelName, {
                name: self.name + '/' + modelName,
                autogen: !!self.config.modelAutogen, // Controls if APIs are automatically created for this model.
                fields: fields,
                connector: self,
                generated: true,
                visible: true
            });
        }
    });
    self.models = _.defaults(self.models || {}, models);
};
<|endoftext|>


<commit_before>/*
 * Copyright 2017 resin.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * This work is heavily based on https://github.com/raspberrypi/usbboot
 * Copyright 2016 Raspberry Pi Foundation
 */

'use strict'

const _ = require('lodash')
const Bluebird = require('bluebird')
const usb = require('./usb')

// The equivalent of a NULL buffer, given that node-usb complains
// if the data argument is not an instance of Buffer
const NULL_BUFFER_SIZE = 0
const NULL_BUFFER = Buffer.alloc(NULL_BUFFER_SIZE)

/**
 * @summary The size of the boot message bootcode length section
 * @type {Number}
 * @constant
 */
const BOOT_MESSAGE_BOOTCODE_LENGTH_SIZE = 4

/**
 * @summary The offset of the boot message bootcode length section
 * @type {Number}
 * @constant
 */
const BOOT_MESSAGE_BOOTCODE_LENGTH_OFFSET = 0

/**
 * @summary The size of the boot message signature section
 * @type {Number}
 * @constant
 */
const BOOT_MESSAGE_SIGNATURE_SIZE = 20

/**
 * @summary The offset of the file message command section
 * @type {Number}
 * @constant
 */
const FILE_MESSAGE_COMMAND_OFFSET = 0

/**
 * @summary The size of the file message command section
 * @type {Number}
 * @constant
 */
const FILE_MESSAGE_COMMAND_SIZE = 4

/**
 * @summary The offset of the file message file name section
 * @type {Number}
 * @constant
 */
const FILE_MESSAGE_FILE_NAME_OFFSET = FILE_MESSAGE_COMMAND_SIZE

/**
 * @summary The size of the file message file name section
 * @type {Number}
 * @constant
 */
const FILE_MESSAGE_FILE_NAME_SIZE = 256

/**
 * @summary The GET_STATUS usb control transfer request code
 * @type {Number}
 * @constant
 * @description
 * See http://www.jungo.com/st/support/documentation/windriver/811/wdusb_man_mhtml/node55.html#usb_standard_dev_req_codes
 */
const USB_REQUEST_CODE_GET_STATUS = 0

/**
 * @summary The maximum buffer length of a usbboot message
 * @type {Number}
 * @constant
 */
const USBBOOT_MESSAGE_MAX_BUFFER_LENGTH = 0xffff

/**
 * @summary The delay to wait between each USB read/write operation
 * @type {Number}
 * @constant
 * @description
 * The USB bus seems to hang if we execute many operations at
 * the same time.
 */
const USB_REQUEST_DELAY_MS = 1000

/**
 * @summary The timeout for USB bulk transfers, in milliseconds
 * @type {Number}
 * @constant
 */
// In node-usb, 0 means "infinite" timeout
const USB_BULK_TRANSFER_TIMEOUT_MS = 0

/**
 * @summary The amount of bits to shift to the right on a control transfer index
 * @type {Number}
 * @constant
 */
const CONTROL_TRANSFER_INDEX_RIGHT_BIT_SHIFT = 16

/**
 * @summary The size of the usbboot file message
 * @type {Number}
 * @constant
 */
exports.FILE_MESSAGE_SIZE = FILE_MESSAGE_COMMAND_SIZE + FILE_MESSAGE_FILE_NAME_SIZE

/**
 * @summary File message command display names
 * @namespace FILE_MESSAGE_COMMANDS
 * @public
 */
exports.FILE_MESSAGE_COMMANDS = {

  /**
   * @property {String}
   * @memberof FILE_MESSAGE_COMMANDS
   *
   * @description
   * The "get file size" file message command name.
   */
  GET_FILE_SIZE: 'GetFileSize',

  /**
   * @property {String}
   * @memberof FILE_MESSAGE_COMMANDS
   *
   * @description
   * The "read file" file message command name.
   */
  READ_FILE: 'ReadFile',

  /**
   * @property {String}
   * @memberof FILE_MESSAGE_COMMANDS
   *
   * @description
   * The "done" file message command name.
   */
  DONE: 'Done'
}

/**
 * @summary The usbboot return code that represents success
 * @type {Number}
 * @constant
 */
exports.RETURN_CODE_SUCCESS = 0

/**
 * @summary The buffer length of the return code message
 * @type {Number}
 * @constant
 */
exports.RETURN_CODE_LENGTH = 4

/**
 * @summary Send a buffer size to a device as a control transfer
 * @function
 * @public
 *
 * @param {Object} device - node-usb device
 * @param {Number} size - buffer size
 * @returns {Promise}
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 *
 * protocol.sendBufferSize(device, 512).then(() => {
 *   console.log('Done!')
 * })
 */
exports.sendBufferSize = (device, size) => {
  return usb.performControlTransfer(device, {
    bmRequestType: usb.LIBUSB_REQUEST_TYPE_VENDOR,
    bRequest: USB_REQUEST_CODE_GET_STATUS,
    data: NULL_BUFFER,

    /* eslint-disable no-bitwise */
    wValue: size & USBBOOT_MESSAGE_MAX_BUFFER_LENGTH,
    wIndex: size >> CONTROL_TRANSFER_INDEX_RIGHT_BIT_SHIFT
    /* eslint-enable no-bitwise */
  })
}

/**
 * @summary Write a buffer to an OUT endpoint
 * @function
 * @private
 *
 * @param {Object} device - device
 * @param {Object} endpoint - endpoint
 * @param {Buffer} buffer - buffer
 * @returns {Promise}
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 * return protocol.write(device, device.interface(0).endpoint(1), Buffer.alloc(1)).then(() => {
 *   console.log('Done!')
 * })
 */
exports.write = (device, endpoint, buffer) => {
  return exports.sendBufferSize(device, buffer.length)

    // We get LIBUSB_TRANSFER_STALL sometimes
    // in future bulk transfers without this
    .delay(USB_REQUEST_DELAY_MS)

    .then(() => {
      return Bluebird.fromCallback((callback) => {
        endpoint.timeout = USB_BULK_TRANSFER_TIMEOUT_MS
        endpoint.transfer(buffer, callback)
      })
    })
}

/**
 * @summary Send an error signal to a device
 * @function
 * @public
 *
 * @param {Object} device - node-usb device
 * @returns {Promise}
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 *
 * protocol.sendErrorSignal(device).then(() => {
 *   console.log('Done!')
 * })
 */
exports.sendErrorSignal = (device) => {
  return exports.sendBufferSize(device, NULL_BUFFER_SIZE)
}

/**
 * @summary Read a buffer from a device
 * @function
 * @private
 *
 * @param {Object} device - device
 * @param {Number} bytesToRead - bytes to read
 * @fulfil {Buffer} - data
 * @returns {Promise}
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 * protocol.read(device, 4).then((data) => {
 *   console.log(data.readInt32BE())
 * })
 */
exports.read = (device, bytesToRead) => {
  return usb.performControlTransfer(device, {
    /* eslint-disable no-bitwise */
    bmRequestType: usb.LIBUSB_REQUEST_TYPE_VENDOR | usb.LIBUSB_ENDPOINT_IN,
    wValue: bytesToRead & USBBOOT_MESSAGE_MAX_BUFFER_LENGTH,
    wIndex: bytesToRead >> CONTROL_TRANSFER_INDEX_RIGHT_BIT_SHIFT,
    /* eslint-enable no-bitwise */

    bRequest: USB_REQUEST_CODE_GET_STATUS,
    length: bytesToRead
  })
}

/**
 * @summary Create a boot message buffer
 * @function
 * @private
 *
 * @description
 * This is based on the following data structure:
 *
 * typedef struct MESSAGE_S {
 *   int length;
 *   unsigned char signature[20];
 * } boot_message_t;
 *
 * This needs to be sent to the out endpoint of the USB device
 * as a 24 bytes big-endian buffer where:
 *
 * - The first 4 bytes contain the size of the bootcode.bin buffer
 * - The remaining 20 bytes contain the boot signature, which
 *   we don't make use of in this implementation
 *
 * @param {Number} bootCodeBufferLength - bootcode.bin buffer length
 * @returns {Buffer} boot message buffer
 *
 * @example
 * const bootMessageBuffer = protocol.createBootMessageBuffer(50216)
 */
exports.createBootMessageBuffer = (bootCodeBufferLength) => {
  const bootMessageBufferSize = BOOT_MESSAGE_BOOTCODE_LENGTH_SIZE + BOOT_MESSAGE_SIGNATURE_SIZE

  // Buffers are automatically filled with zero bytes
  const bootMessageBuffer = Buffer.alloc(bootMessageBufferSize)

  // The bootcode length should be stored in 4 big-endian bytes
  bootMessageBuffer.writeInt32BE(bootCodeBufferLength, BOOT_MESSAGE_BOOTCODE_LENGTH_OFFSET)

  return bootMessageBuffer
}

/**
 * @summary Parse a file message buffer from a device
 * @function
 * @public
 *
 * @param {Buffer} fileMessageBuffer - file message buffer
 * @returns {Object} parsed file message
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 *
 * return protocol.read(device, protocol.FILE_MESSAGE_SIZE).then((fileMessageBuffer) => {
 *   return protocol.parseFileMessageBuffer(fileMessageBuffer)
 * }).then((fileMessage) => {
 *   console.log(fileMessage.command)
 *   console.log(fileMessage.fileName)
 * })
 */
exports.parseFileMessageBuffer = (fileMessageBuffer) => {
  const commandCode = fileMessageBuffer.readInt32LE(FILE_MESSAGE_COMMAND_OFFSET)
  const command = _.nth([
    exports.FILE_MESSAGE_COMMANDS.GET_FILE_SIZE,
    exports.FILE_MESSAGE_COMMANDS.READ_FILE,
    exports.FILE_MESSAGE_COMMANDS.DONE
  ], commandCode)

  if (_.isNil(command)) {
    throw new Error(`Invalid file message command code: ${commandCode}`)
  }

  const fileName = _.chain(fileMessageBuffer.toString('ascii', FILE_MESSAGE_FILE_NAME_OFFSET))

    // The parsed string will likely contain tons of trailing
    // null bytes that we should get rid of for convenience
    // See https://github.com/nodejs/node/issues/4775
    .takeWhile((character) => {
      return character !== '\0'
    })
    .join('')
    .value()

  // A blank file name can also mean "done"
  if (_.isEmpty(fileName)) {
    return {
      command: exports.FILE_MESSAGE_COMMANDS.DONE
    }
  }

  return {
    command,
    fileName
  }
}
<commit_msg>fix(usbboot): Limit usbboot transfers to 1MiB<commit_after>/*
 * Copyright 2017 resin.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * This work is heavily based on https://github.com/raspberrypi/usbboot
 * Copyright 2016 Raspberry Pi Foundation
 */

'use strict'

const _ = require('lodash')
const Bluebird = require('bluebird')
const usb = require('./usb')

// The equivalent of a NULL buffer, given that node-usb complains
// if the data argument is not an instance of Buffer
const NULL_BUFFER_SIZE = 0
const NULL_BUFFER = Buffer.alloc(NULL_BUFFER_SIZE)

const ONE_MEGABYTE = 1048576

/**
 * @summary The size of the boot message bootcode length section
 * @type {Number}
 * @constant
 */
const BOOT_MESSAGE_BOOTCODE_LENGTH_SIZE = 4

/**
 * @summary The offset of the boot message bootcode length section
 * @type {Number}
 * @constant
 */
const BOOT_MESSAGE_BOOTCODE_LENGTH_OFFSET = 0

/**
 * @summary The size of the boot message signature section
 * @type {Number}
 * @constant
 */
const BOOT_MESSAGE_SIGNATURE_SIZE = 20

/**
 * @summary The offset of the file message command section
 * @type {Number}
 * @constant
 */
const FILE_MESSAGE_COMMAND_OFFSET = 0

/**
 * @summary The size of the file message command section
 * @type {Number}
 * @constant
 */
const FILE_MESSAGE_COMMAND_SIZE = 4

/**
 * @summary The offset of the file message file name section
 * @type {Number}
 * @constant
 */
const FILE_MESSAGE_FILE_NAME_OFFSET = FILE_MESSAGE_COMMAND_SIZE

/**
 * @summary The size of the file message file name section
 * @type {Number}
 * @constant
 */
const FILE_MESSAGE_FILE_NAME_SIZE = 256

/**
 * @summary The GET_STATUS usb control transfer request code
 * @type {Number}
 * @constant
 * @description
 * See http://www.jungo.com/st/support/documentation/windriver/811/wdusb_man_mhtml/node55.html#usb_standard_dev_req_codes
 */
const USB_REQUEST_CODE_GET_STATUS = 0

/**
 * @summary The maximum buffer length of a usbboot message
 * @type {Number}
 * @constant
 */
const USBBOOT_MESSAGE_MAX_BUFFER_LENGTH = 0xffff

/**
 * @summary The delay to wait between each USB read/write operation
 * @type {Number}
 * @constant
 * @description
 * The USB bus seems to hang if we execute many operations at
 * the same time.
 */
const USB_REQUEST_DELAY_MS = 1000

/**
 * @summary The timeout for USB bulk transfers, in milliseconds
 * @type {Number}
 * @constant
 */
// In node-usb, 0 means "infinite" timeout
const USB_BULK_TRANSFER_TIMEOUT_MS = 0

/**
 * @summary The amount of bits to shift to the right on a control transfer index
 * @type {Number}
 * @constant
 */
const CONTROL_TRANSFER_INDEX_RIGHT_BIT_SHIFT = 16

/**
 * @summary The size of the usbboot file message
 * @type {Number}
 * @constant
 */
exports.FILE_MESSAGE_SIZE = FILE_MESSAGE_COMMAND_SIZE + FILE_MESSAGE_FILE_NAME_SIZE

/**
 * @summary File message command display names
 * @namespace FILE_MESSAGE_COMMANDS
 * @public
 */
exports.FILE_MESSAGE_COMMANDS = {

  /**
   * @property {String}
   * @memberof FILE_MESSAGE_COMMANDS
   *
   * @description
   * The "get file size" file message command name.
   */
  GET_FILE_SIZE: 'GetFileSize',

  /**
   * @property {String}
   * @memberof FILE_MESSAGE_COMMANDS
   *
   * @description
   * The "read file" file message command name.
   */
  READ_FILE: 'ReadFile',

  /**
   * @property {String}
   * @memberof FILE_MESSAGE_COMMANDS
   *
   * @description
   * The "done" file message command name.
   */
  DONE: 'Done'
}

/**
 * @summary The usbboot return code that represents success
 * @type {Number}
 * @constant
 */
exports.RETURN_CODE_SUCCESS = 0

/**
 * @summary The buffer length of the return code message
 * @type {Number}
 * @constant
 */
exports.RETURN_CODE_LENGTH = 4

/**
 * @summary Send a buffer size to a device as a control transfer
 * @function
 * @public
 *
 * @param {Object} device - node-usb device
 * @param {Number} size - buffer size
 * @returns {Promise}
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 *
 * protocol.sendBufferSize(device, 512).then(() => {
 *   console.log('Done!')
 * })
 */
exports.sendBufferSize = (device, size) => {
  return usb.performControlTransfer(device, {
    bmRequestType: usb.LIBUSB_REQUEST_TYPE_VENDOR,
    bRequest: USB_REQUEST_CODE_GET_STATUS,
    data: NULL_BUFFER,

    /* eslint-disable no-bitwise */
    wValue: size & USBBOOT_MESSAGE_MAX_BUFFER_LENGTH,
    wIndex: size >> CONTROL_TRANSFER_INDEX_RIGHT_BIT_SHIFT
    /* eslint-enable no-bitwise */
  })
}

const chunks = function *(buffer, size) {
  for (let start = 0; start < buffer.length; start += size) {
    yield buffer.slice(start, start + size)
  }
}

/**
 * @summary Write a buffer to an OUT endpoint
 * @function
 * @private
 *
 * @param {Object} device - device
 * @param {Object} endpoint - endpoint
 * @param {Buffer} buffer - buffer
 * @returns {Promise}
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 * return protocol.write(device, device.interface(0).endpoint(1), Buffer.alloc(1)).then(() => {
 *   console.log('Done!')
 * })
 */
exports.write = (device, endpoint, buffer) => {
  return exports.sendBufferSize(device, buffer.length)

    // We get LIBUSB_TRANSFER_STALL sometimes
    // in future bulk transfers without this
    .delay(USB_REQUEST_DELAY_MS)

    .then(() => {
      endpoint.timeout = USB_BULK_TRANSFER_TIMEOUT_MS
      return Bluebird.each(chunks(buffer, ONE_MEGABYTE), (chunk) => {
        return Bluebird.fromCallback((callback) => {
          endpoint.transfer(chunk, callback)
        })
      })
    })
}

/**
 * @summary Send an error signal to a device
 * @function
 * @public
 *
 * @param {Object} device - node-usb device
 * @returns {Promise}
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 *
 * protocol.sendErrorSignal(device).then(() => {
 *   console.log('Done!')
 * })
 */
exports.sendErrorSignal = (device) => {
  return exports.sendBufferSize(device, NULL_BUFFER_SIZE)
}

/**
 * @summary Read a buffer from a device
 * @function
 * @private
 *
 * @param {Object} device - device
 * @param {Number} bytesToRead - bytes to read
 * @fulfil {Buffer} - data
 * @returns {Promise}
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 * protocol.read(device, 4).then((data) => {
 *   console.log(data.readInt32BE())
 * })
 */
exports.read = (device, bytesToRead) => {
  return usb.performControlTransfer(device, {
    /* eslint-disable no-bitwise */
    bmRequestType: usb.LIBUSB_REQUEST_TYPE_VENDOR | usb.LIBUSB_ENDPOINT_IN,
    wValue: bytesToRead & USBBOOT_MESSAGE_MAX_BUFFER_LENGTH,
    wIndex: bytesToRead >> CONTROL_TRANSFER_INDEX_RIGHT_BIT_SHIFT,
    /* eslint-enable no-bitwise */

    bRequest: USB_REQUEST_CODE_GET_STATUS,
    length: bytesToRead
  })
}

/**
 * @summary Create a boot message buffer
 * @function
 * @private
 *
 * @description
 * This is based on the following data structure:
 *
 * typedef struct MESSAGE_S {
 *   int length;
 *   unsigned char signature[20];
 * } boot_message_t;
 *
 * This needs to be sent to the out endpoint of the USB device
 * as a 24 bytes big-endian buffer where:
 *
 * - The first 4 bytes contain the size of the bootcode.bin buffer
 * - The remaining 20 bytes contain the boot signature, which
 *   we don't make use of in this implementation
 *
 * @param {Number} bootCodeBufferLength - bootcode.bin buffer length
 * @returns {Buffer} boot message buffer
 *
 * @example
 * const bootMessageBuffer = protocol.createBootMessageBuffer(50216)
 */
exports.createBootMessageBuffer = (bootCodeBufferLength) => {
  const bootMessageBufferSize = BOOT_MESSAGE_BOOTCODE_LENGTH_SIZE + BOOT_MESSAGE_SIGNATURE_SIZE

  // Buffers are automatically filled with zero bytes
  const bootMessageBuffer = Buffer.alloc(bootMessageBufferSize)

  // The bootcode length should be stored in 4 big-endian bytes
  bootMessageBuffer.writeInt32BE(bootCodeBufferLength, BOOT_MESSAGE_BOOTCODE_LENGTH_OFFSET)

  return bootMessageBuffer
}

/**
 * @summary Parse a file message buffer from a device
 * @function
 * @public
 *
 * @param {Buffer} fileMessageBuffer - file message buffer
 * @returns {Object} parsed file message
 *
 * @example
 * const usb = require('usb')
 * const device = usb.findByIds(0x0a5c, 0x2763)
 *
 * return protocol.read(device, protocol.FILE_MESSAGE_SIZE).then((fileMessageBuffer) => {
 *   return protocol.parseFileMessageBuffer(fileMessageBuffer)
 * }).then((fileMessage) => {
 *   console.log(fileMessage.command)
 *   console.log(fileMessage.fileName)
 * })
 */
exports.parseFileMessageBuffer = (fileMessageBuffer) => {
  const commandCode = fileMessageBuffer.readInt32LE(FILE_MESSAGE_COMMAND_OFFSET)
  const command = _.nth([
    exports.FILE_MESSAGE_COMMANDS.GET_FILE_SIZE,
    exports.FILE_MESSAGE_COMMANDS.READ_FILE,
    exports.FILE_MESSAGE_COMMANDS.DONE
  ], commandCode)

  if (_.isNil(command)) {
    throw new Error(`Invalid file message command code: ${commandCode}`)
  }

  const fileName = _.chain(fileMessageBuffer.toString('ascii', FILE_MESSAGE_FILE_NAME_OFFSET))

    // The parsed string will likely contain tons of trailing
    // null bytes that we should get rid of for convenience
    // See https://github.com/nodejs/node/issues/4775
    .takeWhile((character) => {
      return character !== '\0'
    })
    .join('')
    .value()

  // A blank file name can also mean "done"
  if (_.isEmpty(fileName)) {
    return {
      command: exports.FILE_MESSAGE_COMMANDS.DONE
    }
  }

  return {
    command,
    fileName
  }
}
<|endoftext|>


<commit_before>/**
 * 资源注入到html
 *
 * webpack.config.js:
 * html: {
 *     injectAsset: function(assetFile) {
 *         return
 *     }
 * }
 *
 * output files:
 *  dist/
 *
 *
 * hbs tpl:
 * <html>
 *     <head>
 *         {{{assets.entryA.css.toComboTag}}}
 *     </head>
 *     <body>
 *         {{{assets.entryA.js.toComboTag}}}
 *         {{#each assets.entryA.js}}
 *         <script src="{{url}}"></script>
 *         {{/each}}
 *     </body>
 * </html>
 */

'use strict';

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlTags = require('html-webpack-plugin/lib/html-tags');
const TypeOf = require('./typeof');

const includeAssetsHtmlPluginName = 'IncludeAssetsHtmlPlugin';

function injectAsset(file) {
    return file;
}

class IncludeAssetsHtmlPlugin {
    constructor(options = {}) {
        const userOptions = options || {};

        const defaultOptions = {
            inject: injectAsset
        };

        this.options = Object.assign(defaultOptions, userOptions);
    }

    templateParametersGenerator(compilation, assets, assetTags, options) {
        let self = this;
        let publicPath = assets.publicPath || '';
        if (publicPath !== '' && !publicPath.endsWith('/')) {
            publicPath += '/';
        }

        const xhtml = options.xhtml;
        const inject = self.options.inject;
        const crossOriginLoading = compilation.options.output.crossOriginLoading;

        let newAssetFiles = {};

        compilation.entrypoints.forEach((EntryPoint) => {
            let entryPointFiles = {js: [], css: []};

            EntryPoint.chunks.forEach((chunk) => {
                let chunkName = chunk.name;
                chunk.files.forEach((file) => {
                    let extName = path.extname(file).replace('.', '');
                    let tag = {};

                    let url = `${publicPath}${file}`;
                    let source = compilation.assets[file].source();
                    if (extName === 'js') {
                        tag = {
                            tagName: 'script',
                            voidTag: false
                        };
                        if (TypeOf.isNumber(inject) && source.length < inject) {
                            tag.innerHTML = source;
                        } else {
                            tag.attributes = {
                                src: url
                            };
                            if (crossOriginLoading !== false) {
                                tag.attributes.crossorigin = crossOriginLoading;
                            }
                        }
                    } else if (extName === 'css') {
                        if (TypeOf.isNumber(inject) && source.length < inject) {
                            tag = {
                                tagName: 'style',
                                voidTag: false,
                                innerHTML: source
                            };
                        } else {
                            tag = {
                                tagName: 'link',
                                voidTag: true,
                                attributes: {
                                    rel: 'stylesheet',
                                    href: url
                                }
                            };
                        }
                    }

                    if (tag.tagName) {
                        let file = {
                            chunkName, url, source,
                            tag: htmlTags.htmlTagObjectToString(tag, xhtml)
                        };
                        if (TypeOf.isFunction(inject)) {
                            file = inject(file);
                        }
                        entryPointFiles[extName].push(file);
                    }
                });
            });

            entryPointFiles.js.toComboTag = entryPointFiles.js.map((assetObj) => {
                return assetObj.tag;
            }).join('\n');
            entryPointFiles.css.toComboTag = entryPointFiles.css.map((assetObj) => {
                return assetObj.tag;
            }).join('\n');

            newAssetFiles[EntryPoint.name] = entryPointFiles;
        });

        return {
            assets: newAssetFiles
        }
    }

    apply(compiler) {
        let self = this;

        compiler.hooks.compilation.tap(includeAssetsHtmlPluginName, (compilation) => {
            const hooks = HtmlWebpackPlugin.getHooks(compilation);

            hooks.beforeAssetTagGeneration.tapAsync(
                includeAssetsHtmlPluginName, (data, cb) => {
                    data.plugin.options.templateParameters = (
                        compilation, assets, assetTags, options
                    ) => {
                        return self.templateParametersGenerator(
                            compilation, assets, assetTags, options
                        );
                    };
                    return cb(null, data);
                });
        });
    }
}

module.exports = IncludeAssetsHtmlPlugin;
<commit_msg>html注入扩展注释<commit_after>/**
 * 资源注入到html
 *
 * webpack.config.js:
 * html: {
 *     injectAsset: function(assetFile) {
 *         return
 *     }
 * }
 *
 * output files:
 *  dist/
 *    css/
 *      commons~pageA~pageB~pageD-chunk-327fe913.css
 *    js/
 *      commons~pageA~pageB~pageC~pageD-chunk-2b8836d9.js
 *      commons~pageA~pageB~pageD-chunk-3e461ae1.js
 *      pageA-0476b1b3.js
 *      vendor-chunk-66e793b2.js
 *
 * hbs tpl:
 * <html>
 *     <head>
 *         {{{assets.entryA.css.toComboTag}}}
 *     </head>
 *     <body>
 *         {{{assets.entryA.js.toComboTag}}}
 *         {{#each assets.entryA.js}}
 *         <script src="{{url}}"></script>
 *         {{/each}}
 *     </body>
 * </html>
 *
 * output html:
 * <html>
 *     <head>
 *         <link rel="stylesheet" href="../css/commons~pageA~pageB~pageD-chunk-327fe913.css">
 *     </head>
 *     <body>
 *         <script src="../js/vendor-chunk-66e793b2.js"></script>
 *         <script src="../js/commons~pageA~pageB~pageC~pageD-chunk-2b8836d9.js"></script>
 *         <script src="../js/commons~pageA~pageB~pageD-chunk-3e461ae1.js"></script>
 *         <script src="../js/pageA-0476b1b3.js"></script>
 *     </body>
 * </html>
 *
 */

'use strict';

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlTags = require('html-webpack-plugin/lib/html-tags');
const TypeOf = require('./typeof');

const includeAssetsHtmlPluginName = 'IncludeAssetsHtmlPlugin';

function injectAsset(file) {
    return file;
}

class IncludeAssetsHtmlPlugin {
    constructor(options = {}) {
        const userOptions = options || {};

        const defaultOptions = {
            inject: injectAsset
        };

        this.options = Object.assign(defaultOptions, userOptions);
    }

    templateParametersGenerator(compilation, assets, assetTags, options) {
        let self = this;
        let publicPath = assets.publicPath || '';
        if (publicPath !== '' && !publicPath.endsWith('/')) {
            publicPath += '/';
        }

        const xhtml = options.xhtml;
        const inject = self.options.inject;
        const crossOriginLoading = compilation.options.output.crossOriginLoading;

        let newAssetFiles = {};

        compilation.entrypoints.forEach((EntryPoint) => {
            let entryPointFiles = {js: [], css: []};

            EntryPoint.chunks.forEach((chunk) => {
                let chunkName = chunk.name;
                chunk.files.forEach((file) => {
                    let extName = path.extname(file).replace('.', '');
                    let tag = {};

                    let url = `${publicPath}${file}`;
                    let source = compilation.assets[file].source();
                    if (extName === 'js') {
                        tag = {
                            tagName: 'script',
                            voidTag: false
                        };
                        if (TypeOf.isNumber(inject) && source.length < inject) {
                            tag.innerHTML = source;
                        } else {
                            tag.attributes = {
                                src: url
                            };
                            if (crossOriginLoading !== false) {
                                tag.attributes.crossorigin = crossOriginLoading;
                            }
                        }
                    } else if (extName === 'css') {
                        if (TypeOf.isNumber(inject) && source.length < inject) {
                            tag = {
                                tagName: 'style',
                                voidTag: false,
                                innerHTML: source
                            };
                        } else {
                            tag = {
                                tagName: 'link',
                                voidTag: true,
                                attributes: {
                                    rel: 'stylesheet',
                                    href: url
                                }
                            };
                        }
                    }

                    if (tag.tagName) {
                        let file = {
                            chunkName, url, source,
                            tag: htmlTags.htmlTagObjectToString(tag, xhtml)
                        };
                        if (TypeOf.isFunction(inject)) {
                            file = inject(file);
                        }
                        entryPointFiles[extName].push(file);
                    }
                });
            });

            entryPointFiles.js.toComboTag = entryPointFiles.js.map((assetObj) => {
                return assetObj.tag;
            }).join('\n');
            entryPointFiles.css.toComboTag = entryPointFiles.css.map((assetObj) => {
                return assetObj.tag;
            }).join('\n');

            newAssetFiles[EntryPoint.name] = entryPointFiles;
        });

        return {
            assets: newAssetFiles
        }
    }

    apply(compiler) {
        let self = this;

        compiler.hooks.compilation.tap(includeAssetsHtmlPluginName, (compilation) => {
            const hooks = HtmlWebpackPlugin.getHooks(compilation);

            hooks.beforeAssetTagGeneration.tapAsync(
                includeAssetsHtmlPluginName, (data, cb) => {
                    data.plugin.options.templateParameters = (
                        compilation, assets, assetTags, options
                    ) => {
                        return self.templateParametersGenerator(
                            compilation, assets, assetTags, options
                        );
                    };
                    return cb(null, data);
                });
        });
    }
}

module.exports = IncludeAssetsHtmlPlugin;
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */
'use strict';

/* This test tries calling getUserMedia() with each resolution from the list
 * below. Each gUM() call triggers a success or a fail callback; we report
 * ok/nok and schedule another gUM() with the next resolution until the list
 * is exhausted. Some resolutions are mandatory and make the test fail if not
 * supported.
 *
 * In generic cameras using Chrome rescaler, all resolutions should be supported
 * up to a given one and none beyond there. Special cameras, such as digitizers,
 * might support only one resolution.
 */

addTest('Camera', 'Supported resolutions', function() {
  var test = new CamResolutionsTest();
  test.run();
});

function CamResolutionsTest() {
  // Each resolution has width, height and 'mandatory' fields.
  this.resolutions = [ [ 160, 120, false],
                       [ 320, 180, false],
                       [ 320, 240,  true],  // QVGA
                       [ 640, 360, false],
                       [ 640, 480,  true],  // VGA
                       [ 768, 576, false],  // PAL
                       [1024, 576, false],
                       [1280, 720,  true],  // HD
                       [1280, 768, false],
                       [1280, 800, false],
                       [1920,1080, false],  // Full HD
                       [1920,1200, false],
                       [3840,2160, false],  // 4K
                       [4096,2160, false] ];
  this.mandatoryUnsupportedResolutions = 0;
  this.numResolutions = this.resolutions.length;
  this.counter = 0;
  this.supportedResolutions = 0;
  this.unsupportedResolutions = 0;
  this.currentResolutionForCheckEncodeTime = null;
}

CamResolutionsTest.prototype = {
  run: function() {
    this.triggerGetUserMedia_(this.resolutions[0]);
  },

  triggerGetUserMedia_: function(resolution) {
    var constraints = {
      audio: false,
      video: {
        mandatory: {
          minWidth:  resolution[0],
          minHeight: resolution[1],
          maxWidth:  resolution[0],
          maxHeight: resolution[1]
        }
      }
    };
    try {
      doGetUserMedia(constraints, this.successFunc_.bind(this),
          this.failFunc_.bind(this));
    } catch (e) {
      reportFatal('GetUserMedia failed.');
    }
  },

  successFunc_: function(stream) {
    this.supportedResolutions++;
    var theResolution = this.resolutions[this.counter++];
    // Check stats for mandatory resolutions only.
    if (theResolution[2]) {
      this.currentResolutionForCheckEncodeTime = theResolution;
      this.collectAndAnlyzeStats_(stream);
      return;
    }
    reportInfo('Supported ' + theResolution[0] + 'x' + theResolution[1]);
    stream.getVideoTracks()[0].stop();
    this.finishTestOrRetrigger_();
    return;
  },

  collectAndAnlyzeStats_: function(stream) {
    var call = new Call();
    call.pc1.addStream(stream);
    call.establishConnection();
    call.gatherStats(call.pc1, this.analyzeStats_.bind(this), 100);
    setTimeoutWithProgressBar( function() {
      call.close();
      stream.getVideoTracks()[0].stop();
      this.finishTestOrRetrigger_();
    }.bind(this), 5000);
  },

  analyzeStats_: function(stats) {
    var currentRes = this.currentResolutionForCheckEncodeTime;
    var googAvgEncodeTime = [];

    for (var index = 0; index < stats.length - 1; index++) {
      if (stats[index].type === 'ssrc') {
        // Make sure to only capture stats after the encoder is setup.
        // TODO(jansson) expand to cover audio as well.
        if (stats[index].stat('googFrameRateInput') > 0) {
          googAvgEncodeTime.push(parseInt(stats[index].stat('googAvgEncodeMs')));
        }
      }
    }

    function average_(statArray) {
      Math.average = function() {
        var cnt, tot, i;
        cnt = arguments.length;
        tot = i = 0;
        while (i < cnt) {
          tot += arguments[i++];
        }
        return tot / cnt;
      };
      return Math.floor(Math.average.apply(Math, statArray));
    }
    function max_(statArray) {
      return Math.max.apply(Math, statArray);
    }
    function min_(statArray) {
      return Math.min.apply(Math, statArray);
    }

    var userMessage = 'Supported ' + currentRes[0] + 'x' + currentRes[1];
    var avgMinMaxStats = ' Average: ' + average_(googAvgEncodeTime) +
                         ' Max: ' + max_(googAvgEncodeTime) +
                         ' Min: ' + min_(googAvgEncodeTime)
    if (googAvgEncodeTime.length === 0) {
      reportError(userMessage + ' but no stats collected, check your chamera.');
    } else {
      reportInfo(userMessage + avgMinMaxStats + ' encode time (ms)');
    }
  },

  failFunc_: function() {
    this.unsupportedResolutions++;
    var theResolution = this.resolutions[this.counter++];
    if (theResolution[2]) {
      this.mandatoryUnsupportedResolutions++;
      reportError('Camera does not support a mandatory resolution: ' +
                  theResolution[0] + 'x' + theResolution[1]);
    } else {
      reportInfo('NOT supported ' + theResolution[0] + 'x' +
                 theResolution[1]);
    }
    this.finishTestOrRetrigger_();
  },

  finishTestOrRetrigger_: function() {
    if (this.counter === this.numResolutions) {
      if (this.mandatoryUnsupportedResolutions === 0) {
        if (this.supportedResolutions) {
          reportSuccess(this.supportedResolutions + '/' + this.numResolutions +
                        ' resolutions supported.');
        } else {
          reportError('No camera resolutions supported, most likely the ' +
                      'camera is not accessible or dead.');
        }
      }
      testFinished();
    } else {
      this.triggerGetUserMedia_(this.resolutions[this.counter]);
    }
  }

};
<commit_msg>forgot semicolon as usual....<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */
'use strict';

/* This test tries calling getUserMedia() with each resolution from the list
 * below. Each gUM() call triggers a success or a fail callback; we report
 * ok/nok and schedule another gUM() with the next resolution until the list
 * is exhausted. Some resolutions are mandatory and make the test fail if not
 * supported.
 *
 * In generic cameras using Chrome rescaler, all resolutions should be supported
 * up to a given one and none beyond there. Special cameras, such as digitizers,
 * might support only one resolution.
 */

addTest('Camera', 'Supported resolutions', function() {
  var test = new CamResolutionsTest();
  test.run();
});

function CamResolutionsTest() {
  // Each resolution has width, height and 'mandatory' fields.
  this.resolutions = [ [ 160, 120, false],
                       [ 320, 180, false],
                       [ 320, 240,  true],  // QVGA
                       [ 640, 360, false],
                       [ 640, 480,  true],  // VGA
                       [ 768, 576, false],  // PAL
                       [1024, 576, false],
                       [1280, 720,  true],  // HD
                       [1280, 768, false],
                       [1280, 800, false],
                       [1920,1080, false],  // Full HD
                       [1920,1200, false],
                       [3840,2160, false],  // 4K
                       [4096,2160, false] ];
  this.mandatoryUnsupportedResolutions = 0;
  this.numResolutions = this.resolutions.length;
  this.counter = 0;
  this.supportedResolutions = 0;
  this.unsupportedResolutions = 0;
  this.currentResolutionForCheckEncodeTime = null;
}

CamResolutionsTest.prototype = {
  run: function() {
    this.triggerGetUserMedia_(this.resolutions[0]);
  },

  triggerGetUserMedia_: function(resolution) {
    var constraints = {
      audio: false,
      video: {
        mandatory: {
          minWidth:  resolution[0],
          minHeight: resolution[1],
          maxWidth:  resolution[0],
          maxHeight: resolution[1]
        }
      }
    };
    try {
      doGetUserMedia(constraints, this.successFunc_.bind(this),
          this.failFunc_.bind(this));
    } catch (e) {
      reportFatal('GetUserMedia failed.');
    }
  },

  successFunc_: function(stream) {
    this.supportedResolutions++;
    var theResolution = this.resolutions[this.counter++];
    // Check stats for mandatory resolutions only.
    if (theResolution[2]) {
      this.currentResolutionForCheckEncodeTime = theResolution;
      this.collectAndAnlyzeStats_(stream);
      return;
    }
    reportInfo('Supported ' + theResolution[0] + 'x' + theResolution[1]);
    stream.getVideoTracks()[0].stop();
    this.finishTestOrRetrigger_();
    return;
  },

  collectAndAnlyzeStats_: function(stream) {
    var call = new Call();
    call.pc1.addStream(stream);
    call.establishConnection();
    call.gatherStats(call.pc1, this.analyzeStats_.bind(this), 100);
    setTimeoutWithProgressBar( function() {
      call.close();
      stream.getVideoTracks()[0].stop();
      this.finishTestOrRetrigger_();
    }.bind(this), 5000);
  },

  analyzeStats_: function(stats) {
    var currentRes = this.currentResolutionForCheckEncodeTime;
    var googAvgEncodeTime = [];

    for (var index = 0; index < stats.length - 1; index++) {
      if (stats[index].type === 'ssrc') {
        // Make sure to only capture stats after the encoder is setup.
        // TODO(jansson) expand to cover audio as well.
        if (stats[index].stat('googFrameRateInput') > 0) {
          googAvgEncodeTime.push(parseInt(stats[index].stat('googAvgEncodeMs')));
        }
      }
    }

    function average_(statArray) {
      Math.average = function() {
        var cnt, tot, i;
        cnt = arguments.length;
        tot = i = 0;
        while (i < cnt) {
          tot += arguments[i++];
        }
        return tot / cnt;
      };
      return Math.floor(Math.average.apply(Math, statArray));
    }
    function max_(statArray) {
      return Math.max.apply(Math, statArray);
    }
    function min_(statArray) {
      return Math.min.apply(Math, statArray);
    }

    var userMessage = 'Supported ' + currentRes[0] + 'x' + currentRes[1];
    var avgMinMaxStats = ' Average: ' + average_(googAvgEncodeTime) +
                         ' Max: ' + max_(googAvgEncodeTime) +
                         ' Min: ' + min_(googAvgEncodeTime);
    if (googAvgEncodeTime.length === 0) {
      reportError(userMessage + ' but no stats collected, check your chamera.');
    } else {
      reportInfo(userMessage + avgMinMaxStats + ' encode time (ms)');
    }
  },

  failFunc_: function() {
    this.unsupportedResolutions++;
    var theResolution = this.resolutions[this.counter++];
    if (theResolution[2]) {
      this.mandatoryUnsupportedResolutions++;
      reportError('Camera does not support a mandatory resolution: ' +
                  theResolution[0] + 'x' + theResolution[1]);
    } else {
      reportInfo('NOT supported ' + theResolution[0] + 'x' +
                 theResolution[1]);
    }
    this.finishTestOrRetrigger_();
  },

  finishTestOrRetrigger_: function() {
    if (this.counter === this.numResolutions) {
      if (this.mandatoryUnsupportedResolutions === 0) {
        if (this.supportedResolutions) {
          reportSuccess(this.supportedResolutions + '/' + this.numResolutions +
                        ' resolutions supported.');
        } else {
          reportError('No camera resolutions supported, most likely the ' +
                      'camera is not accessible or dead.');
        }
      }
      testFinished();
    } else {
      this.triggerGetUserMedia_(this.resolutions[this.counter]);
    }
  }

};
<|endoftext|>


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

const commander = require('commander');
const webpack = require('webpack');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const path = require('path');
const StripesConfigPlugin = require('./webpack/stripes-config-plugin');
const devConfig = require('./webpack.config.cli.dev');

const cwd = path.resolve();
const cwdModules = path.join(cwd, 'node_modules');
const coreModules = path.join(__dirname, 'node_modules');

const packageJSON = require ('./package.json');
commander.version(packageJSON.version);

const cachePlugin = new HardSourceWebpackPlugin({
  cacheDirectory: path.join(cwd, 'webpackcache'),
  recordsPath: path.join(cwd, 'webpackcache/records.json'),
  configHash: function(webpackConfig) {
    // Build a string value used by HardSource to determine which cache to
    // use if [confighash] is in cacheDirectory or if the cache should be
    // replaced if [confighash] does not appear in cacheDirectory.
    return require('node-object-hash')().hash(devConfig);
  },
});

// Display webpack output to the console
function processStats(err, stats) {
  if (err) {
    console.error(err);
  }
  console.log(stats.toString({
    chunks: false,
    colors: true
  }));
  if(err || stats.hasErrors()){
    process.exit(1);
  }
}

commander
  .command('dev')
  .option('--port [port]', 'Port')
  .option('--host [host]', 'Host')
  .option('--cache', 'Use HardSourceWebpackPlugin cache')
  .option('--devtool [devtool]', 'Use another value for devtool instead of "inline-source-map"')
  .arguments('<config>')
  .description('Launch a webpack-dev-server')
  .action(function (stripesConfigFile, options) {
    const express = require('express');
    const app = express();

    const config = Object.assign({}, devConfig);
    const stripesConfig = require(path.resolve(stripesConfigFile));
    config.plugins.push(new StripesConfigPlugin(stripesConfig));
    // Look for modules in node_modules, then the platform, then stripes-core
    config.resolve.modules = ['node_modules', cwdModules, coreModules];
    config.resolveLoader = { modules: ['node_modules', cwdModules, coreModules] };
    if (options.cache) config.plugins.push(cachePlugin);
    if (options.devtool) config.devtool = options.devtool;
    const compiler = webpack(config);

    const port = options.port || process.env.STRIPES_PORT || 3000;
    const host = options.host || process.env.STRIPES_HOST || 'localhost';

    app.use(express.static(__dirname + '/public'));

    app.use(require('webpack-dev-middleware')(compiler, {
      noInfo: true,
      publicPath: config.output.publicPath
    }));

    app.use(require('webpack-hot-middleware')(compiler));

    app.get('/favicon.ico', function(req, res) {
      res.sendFile(path.join(__dirname, 'favicon.ico'));
    });

    app.get('*', function(req, res) {
      res.sendFile(path.join(__dirname, 'index.html'));
    });

    app.listen(port, host, function(err) {
      if (err) {
        console.log(err);
        return;
      }

      console.log('Listening at http://' + host + ':' + port);
    });
  });

commander
  .command('build')
  .option('--publicPath [publicPath]', 'publicPath')
  .arguments('<config> <output>')
  .description('Build a tenant bundle')
  .action(function (stripesConfigFile, outputPath, options) {
    const config = require('./webpack.config.cli.prod');
    const stripesConfig = require(path.resolve(stripesConfigFile));
    config.plugins.push(new StripesConfigPlugin(stripesConfig));
    config.resolve.modules = ['node_modules', cwdModules];
    config.resolveLoader = { modules: ['node_modules', cwdModules] };
    config.output.path = path.resolve(outputPath);
    if (options.publicPath) {
      config.output.publicPath = options.publicPath;
    }
    const compiler = webpack(config);
    compiler.run(function (err, stats) {
      processStats(err, stats);
    });
  });

commander.parse(process.argv);

// output help if no command specified
if (!process.argv.slice(2).length) {
  commander.outputHelp();
}
<commit_msg>add comments<commit_after>#!/usr/bin/env node

const commander = require('commander');
const webpack = require('webpack');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const path = require('path');
const StripesConfigPlugin = require('./webpack/stripes-config-plugin');
const devConfig = require('./webpack.config.cli.dev');

const cwd = path.resolve();
const cwdModules = path.join(cwd, 'node_modules');
const coreModules = path.join(__dirname, 'node_modules');

const packageJSON = require ('./package.json');
commander.version(packageJSON.version);

const cachePlugin = new HardSourceWebpackPlugin({
  cacheDirectory: path.join(cwd, 'webpackcache'),
  recordsPath: path.join(cwd, 'webpackcache/records.json'),
  configHash: function(webpackConfig) {
    // Build a string value used by HardSource to determine which cache to
    // use if [confighash] is in cacheDirectory or if the cache should be
    // replaced if [confighash] does not appear in cacheDirectory.
    return require('node-object-hash')().hash(devConfig);
  },
});

// Display webpack output to the console
function processStats(err, stats) {
  if (err) {
    console.error(err);
  }
  console.log(stats.toString({
    chunks: false,
    colors: true
  }));
  // Check for webpack compile errors and exit
  if(err || stats.hasErrors()){
    process.exit(1);
  }
}

commander
  .command('dev')
  .option('--port [port]', 'Port')
  .option('--host [host]', 'Host')
  .option('--cache', 'Use HardSourceWebpackPlugin cache')
  .option('--devtool [devtool]', 'Use another value for devtool instead of "inline-source-map"')
  .arguments('<config>')
  .description('Launch a webpack-dev-server')
  .action(function (stripesConfigFile, options) {
    const express = require('express');
    const app = express();

    const config = Object.assign({}, devConfig);
    const stripesConfig = require(path.resolve(stripesConfigFile));
    config.plugins.push(new StripesConfigPlugin(stripesConfig));
    // Look for modules in node_modules, then the platform, then stripes-core
    config.resolve.modules = ['node_modules', cwdModules, coreModules];
    config.resolveLoader = { modules: ['node_modules', cwdModules, coreModules] };
    if (options.cache) config.plugins.push(cachePlugin);
    if (options.devtool) config.devtool = options.devtool;
    const compiler = webpack(config);

    const port = options.port || process.env.STRIPES_PORT || 3000;
    const host = options.host || process.env.STRIPES_HOST || 'localhost';

    app.use(express.static(__dirname + '/public'));

    app.use(require('webpack-dev-middleware')(compiler, {
      noInfo: true,
      publicPath: config.output.publicPath
    }));

    app.use(require('webpack-hot-middleware')(compiler));

    app.get('/favicon.ico', function(req, res) {
      res.sendFile(path.join(__dirname, 'favicon.ico'));
    });

    app.get('*', function(req, res) {
      res.sendFile(path.join(__dirname, 'index.html'));
    });

    app.listen(port, host, function(err) {
      if (err) {
        console.log(err);
        return;
      }

      console.log('Listening at http://' + host + ':' + port);
    });
  });

commander
  .command('build')
  .option('--publicPath [publicPath]', 'publicPath')
  .arguments('<config> <output>')
  .description('Build a tenant bundle')
  .action(function (stripesConfigFile, outputPath, options) {
    const config = require('./webpack.config.cli.prod');
    const stripesConfig = require(path.resolve(stripesConfigFile));
    config.plugins.push(new StripesConfigPlugin(stripesConfig));
    config.resolve.modules = ['node_modules', cwdModules];
    config.resolveLoader = { modules: ['node_modules', cwdModules] };
    config.output.path = path.resolve(outputPath);
    if (options.publicPath) {
      config.output.publicPath = options.publicPath;
    }
    const compiler = webpack(config);
    compiler.run(function (err, stats) {
      processStats(err, stats);
    });
  });

commander.parse(process.argv);

// output help if no command specified
if (!process.argv.slice(2).length) {
  commander.outputHelp();
}
<|endoftext|>


<commit_before>var test = require('tape');
var sinon = require('sinon');

test('qotd service should make an XHR call', function (t) {
  var requests = [];
  var quote = require('../src/qotdService.js');
  var cb = sinon.spy();

  var server = sinon.fakeServer.create();

  quote(cb);

  t.plan(4);
  t.equals(server.requests.length, 1);
  t.ok(cb.notCalled);

  server.requests[0].respond(200, {}, 'foo');

  t.ok(cb.called);
  t.ok(cb.calledWith(sinon.match.string));
});
<commit_msg>correct example to some extent<commit_after>var test = require('tape');
var sinon = require('sinon');

test('qotd service should make an XHR call', function (t) {
  var requests = [];
  var quote = require('../src/qotdService.js');
  var cb = sinon.spy();

  var server = sinon.fakeServer.create();

  quote(cb);

  t.plan(4);
  t.equals(server.requests.length, 1);
  t.ok(cb.notCalled);

  server.requests[0].respond(200, {}, {
    value: { joke: 'The cake is a lie.' }
  });

  t.ok(cb.called);
  t.ok(cb.calledWith(null, sinon.match.string));
});
<|endoftext|>


<commit_before>import React from 'react';
import ShowThumbnail from './ShowThumbnail';
import sessionStore from '../stores/Session';
import { observer } from 'mobx-react';
import rootStore from '../stores/RootStore';
import { Redirect } from 'react-router'
import {FormGroup,
        Button,
        Col,
        Alert,
        Form,
        FormControl,
        ControlLabel} from 'react-bootstrap';

const request = require('superagent');
const FontAwesome = require('react-fontawesome');

class AddBook extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loadingBookData: false,
      errorMsg: '',
      infoMsg: '',
      room_id: '',
      isbn: '',
      tag: '',
      title: '',
      authors: '',
      description: '',
      format: '',
      pages: '',
      publication_date: '',
      thumbnail: ''
    };
  }

  onFormInput(e) {
    this.setState({[e.target.id]: e.target.value});
  }
  onIsbnChange(e) {
    this.setState({[e.target.id]: e.target.value});
    let isbn = e.target.value;
    if (isbn.length === 13) {
      this.getBookData(isbn);
    }
  }
  getBookData(isbn) {
    let url = `${window.__appUrl}/api/books/goodreads/${isbn}`;
    this.setState({loadingBookData: true});
    request
      .get(url)
      .type('application/json')
      .on('error', (err) => {
        this.setState({
          errorMsg: `Unable to fetch book info for ${isbn}`,
          loadingBookData: false
        });
      })
      .end((err, res) => {
        if (err) {
          return;
        }
        this.setState({loadingBookData: false});
        this.setState({errorMsg: null});
        let state = Object.assign({}, res.body);
        state.pages = state.num_pages;
        state.isbn = isbn;
        delete state.num_pages;
        delete state.errorMsg;
        this.setState(state);
        document.getElementById("tag").focus();
      });
  }

  reset(e) {
    this.setState({
        loadingBookData: false,
        thumbnail: null,
        isbn: '',
        title: '',
        tag: '',
        authors: '',
        description: '',
        format: '',
        pages: '',
        publication_date: ''
     });
  }

  submitBook() {
    let url = `${window.__appUrl}/api/books/${this.state.tag}`;
    request
      .put(url)
      .send(this.state)
      .type('application/json')
      .on('error', (err) => {
        this.setState({errorMsg: `Unable to post new book: ${err.response.text}`});
      })
      .end((err, res) => {
        if (!err) {
          this.setState({
              errorMsg: null,
              infoMsg: `New book with tag ${res.body.id} added!`,
          });
          let isbn = document.getElementById("isbn");
          isbn.focus();
          isbn.scrollIntoView();
          this.reset();
        }
      });
  }

  onRoomChange(e) {
    this.setState({room_id: e.target.value});
    rootStore.selectRoom(e.target.value);
  }

  render() {
    const ErrAlert = this.state.errorMsg ? <Alert bsStyle="danger">{this.state.errorMsg}</Alert> : null;
    const InfoAlert = this.state.infoMsg ? <Alert bsStyle="success">{this.state.infoMsg}</Alert> : null;
    let rooms = []
    this.props.sites.map((site) =>
      site.rooms.map((room) =>
        rooms.push({name: `${site.name}-${room.name}`, id: room.id})
      )
    );
    if (!sessionStore.loggedIn) {
      return <Redirect to="/login" push={false} />
    }
    return (

Add book

          {ErrAlert}
          {InfoAlert}
          <Form horizontal>
            <FormGroup controlId="isbn">
              <Col componentClass={ControlLabel} sm={2}>
                ISBN-13
              </Col>
              <Col sm={5}>
                <FormControl
                  value={this.state.isbn}
                  onChange={this.onIsbnChange.bind(this)}
                  type="text"
                  autoComplete="off"
                  autoFocus="true"
                  placeholder=""/>
              </Col>
              <Col sm={1}>
                {this.state.loadingBookData ? <FontAwesome name='spinner' size='2x'spin/> : null}
              </Col>
            </FormGroup>
              <FormGroup controlId="tag">
              <Col componentClass={ControlLabel} sm={2}>
                TAG
              </Col>
              <Col sm={5}>
                <FormControl
                  value={this.state.tag}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="title">
              <Col componentClass={ControlLabel} sm={2}>
                Title
              </Col>
              <Col sm={7}>
                <FormControl
                  value={this.state.title}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="room_id">
              <Col componentClass={ControlLabel} sm={2}>
                Room
              </Col>
              <Col sm={7}>
                <FormControl
                  componentClass="select"
		              defaultValue={rootStore.selectedRoom}
                  onChange={this.onRoomChange.bind(this)}
                  placeholder="select">
                    <option />
                    {rooms.map((room) => {
                        return <option
                                 key={room.id}
                                 value={room.id}>{room.name}</option>
                      })
                    }
                </FormControl>
              </Col>
            </FormGroup>
            <FormGroup controlId="authors">
              <Col componentClass={ControlLabel} sm={2}>
                Authors
              </Col>
              <Col sm={7}>
                <FormControl
                  value={this.state.authors}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="description">
              <Col componentClass={ControlLabel} sm={2}>
                Description
              </Col>
              <Col sm={10}>
                <FormControl
                  value={this.state.description}
                  onChange={this.onFormInput.bind(this)}
                  style={{height: '150px'}}
                  componentClass="textarea"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="format">
              <Col componentClass={ControlLabel} sm={2}>
                Format
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.format}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>
              </Col>
              <Col componentClass={ControlLabel} sm={2}>
                Pages
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.pages}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>
              </Col>
              <Col componentClass={ControlLabel} sm={2}>
                Publication date
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.publication_date}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>

              </Col>
            </FormGroup>
            <FormGroup controlId="publication_date">
              <Col componentClass={ControlLabel} sm={2}>
                Thumbnail
              </Col>
              <Col sm={6}>
                <ShowThumbnail thumbnailUrl={this.state.thumbnail}/>
              </Col>
            </FormGroup>
            <FormGroup>
              <Col smOffset={2} sm={1}>
                <Button
                  disabled={!this.state.isbn || !this.state.tag}
                  onClick={this.submitBook.bind(this)}>
                  Submit book
                </Button>
              </Col>
              <Col smOffset={2} sm={1}>
                <Button
                  onClick={this.reset.bind(this)}>
                  Reset
                </Button>
              </Col>
            </FormGroup>
          </Form>

    );
  }
}

export default observer(AddBook);
<commit_msg>Don't add existing books in add book form<commit_after>import React from 'react';
import ShowThumbnail from './ShowThumbnail';
import sessionStore from '../stores/Session';
import { observer } from 'mobx-react';
import rootStore from '../stores/RootStore';
import { Redirect } from 'react-router'
import {FormGroup,
        Button,
        Col,
        Alert,
        Form,
        FormControl,
        ControlLabel} from 'react-bootstrap';

const request = require('superagent');
const FontAwesome = require('react-fontawesome');

class AddBook extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loadingBookData: false,
      errorMsg: '',
      infoMsg: '',
      room_id: '',
      isbn: '',
      tag: '',
      title: '',
      authors: '',
      description: '',
      format: '',
      pages: '',
      publication_date: '',
      thumbnail: ''
    };
  }

  onFormInput(e) {
    this.setState({[e.target.id]: e.target.value});
  }
  onIsbnChange(e) {
    this.setState({[e.target.id]: e.target.value});
    let isbn = e.target.value;
    if (isbn.length === 13) {
      this.getBookData(isbn);
    }
  }
  getBookData(isbn) {
    let url = `${window.__appUrl}/api/books/goodreads/${isbn}`;
    this.setState({loadingBookData: true});
    request
      .get(url)
      .type('application/json')
      .on('error', (err) => {
        this.setState({
          errorMsg: `Unable to fetch book info for ${isbn}`,
          loadingBookData: false
        });
      })
      .end((err, res) => {
        if (err) {
          return;
        }
        this.setState({loadingBookData: false});
        this.setState({errorMsg: null});
        let state = Object.assign({}, res.body);
        state.pages = state.num_pages;
        state.isbn = isbn;
        delete state.num_pages;
        delete state.errorMsg;
        this.setState(state);
        document.getElementById("tag").focus();
      });
  }

  reset(e) {
    this.setState({
        loadingBookData: false,
        thumbnail: null,
        isbn: '',
        title: '',
        tag: '',
        authors: '',
        description: '',
        format: '',
        pages: '',
        publication_date: ''
     });
  }

  submitBook() {
    let url = `${window.__appUrl}/api/books/${this.state.tag}`;
    request
      .get(url)
      .then((res) => {
          // HTTP 200 response, book exists
          this.setState({
            infoMsg: null,
            errorMsg: `Book ${this.state.tag} already exists`
          });
        },
        (res) => {
          // Error response
          if (res.status == 404) {
            this.addBook();
          }
          else {
            this.setState({errorMsg: `Unable to fetch data for book: ${res.response.text}`});
          }
        }
      );
  }

  addBook() {
    let url = `${window.__appUrl}/api/books/${this.state.tag}`;
    request
      .put(url)
      .send(this.state)
      .type('application/json')
      .on('error', (err) => {
        this.setState({errorMsg: `Unable to post new book: ${err.response.text}`});
      })
      .end((err, res) => {
        if (!err) {
          this.setState({
              errorMsg: null,
              infoMsg: `New book with tag ${res.body.id} added!`,
          });
          let isbn = document.getElementById("isbn");
          isbn.focus();
          isbn.scrollIntoView();
          this.reset();
        }
      });
  }

  onRoomChange(e) {
    this.setState({room_id: e.target.value});
    rootStore.selectRoom(e.target.value);
  }

  render() {
    const ErrAlert = this.state.errorMsg ? <Alert bsStyle="danger">{this.state.errorMsg}</Alert> : null;
    const InfoAlert = this.state.infoMsg ? <Alert bsStyle="success">{this.state.infoMsg}</Alert> : null;
    let rooms = []
    this.props.sites.map((site) =>
      site.rooms.map((room) =>
        rooms.push({name: `${site.name}-${room.name}`, id: room.id})
      )
    );
    if (!sessionStore.loggedIn) {
      return <Redirect to="/login" push={false} />
    }
    return (

Add book

          {ErrAlert}
          {InfoAlert}
          <Form horizontal>
            <FormGroup controlId="isbn">
              <Col componentClass={ControlLabel} sm={2}>
                ISBN-13
              </Col>
              <Col sm={5}>
                <FormControl
                  value={this.state.isbn}
                  onChange={this.onIsbnChange.bind(this)}
                  type="text"
                  autoComplete="off"
                  autoFocus="true"
                  placeholder=""/>
              </Col>
              <Col sm={1}>
                {this.state.loadingBookData ? <FontAwesome name='spinner' size='2x'spin/> : null}
              </Col>
            </FormGroup>
              <FormGroup controlId="tag">
              <Col componentClass={ControlLabel} sm={2}>
                TAG
              </Col>
              <Col sm={5}>
                <FormControl
                  value={this.state.tag}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="title">
              <Col componentClass={ControlLabel} sm={2}>
                Title
              </Col>
              <Col sm={7}>
                <FormControl
                  value={this.state.title}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="room_id">
              <Col componentClass={ControlLabel} sm={2}>
                Room
              </Col>
              <Col sm={7}>
                <FormControl
                  componentClass="select"
		              defaultValue={rootStore.selectedRoom}
                  onChange={this.onRoomChange.bind(this)}
                  placeholder="select">
                    <option />
                    {rooms.map((room) => {
                        return <option
                                 key={room.id}
                                 value={room.id}>{room.name}</option>
                      })
                    }
                </FormControl>
              </Col>
            </FormGroup>
            <FormGroup controlId="authors">
              <Col componentClass={ControlLabel} sm={2}>
                Authors
              </Col>
              <Col sm={7}>
                <FormControl
                  value={this.state.authors}
                  onChange={this.onFormInput.bind(this)}
                  type="text"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="description">
              <Col componentClass={ControlLabel} sm={2}>
                Description
              </Col>
              <Col sm={10}>
                <FormControl
                  value={this.state.description}
                  onChange={this.onFormInput.bind(this)}
                  style={{height: '150px'}}
                  componentClass="textarea"
                  autoComplete="off"
                  placeholder=""/>
              </Col>
            </FormGroup>
            <FormGroup controlId="format">
              <Col componentClass={ControlLabel} sm={2}>
                Format
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.format}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>
              </Col>
              <Col componentClass={ControlLabel} sm={2}>
                Pages
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.pages}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>
              </Col>
              <Col componentClass={ControlLabel} sm={2}>
                Publication date
              </Col>
              <Col sm={2}>
                <FormControl
                  value={this.state.publication_date}
                  onChange={this.onFormInput.bind(this)}
                  autoComplete="off"
                  placeholder=""/>

              </Col>
            </FormGroup>
            <FormGroup controlId="publication_date">
              <Col componentClass={ControlLabel} sm={2}>
                Thumbnail
              </Col>
              <Col sm={6}>
                <ShowThumbnail thumbnailUrl={this.state.thumbnail}/>
              </Col>
            </FormGroup>
            <FormGroup>
              <Col smOffset={2} sm={1}>
                <Button
                  disabled={!this.state.isbn || !this.state.tag}
                  onClick={this.submitBook.bind(this)}>
                  Submit book
                </Button>
              </Col>
              <Col smOffset={2} sm={1}>
                <Button
                  onClick={this.reset.bind(this)}>
                  Reset
                </Button>
              </Col>
            </FormGroup>
          </Form>

    );
  }
}

export default observer(AddBook);
<|endoftext|>