<commit_before>'use strict';

const CIPObject = require('./CIPObject');

class MessageRouter extends CIPObject {

  // tagname
  // tagname.member
  // tagname[element]
  // tagname[element].member
  // tagname[idx1,idx2]

  // EIP-CIP V1 Appendix C-1.4.5.2, pg.
  static ANSIExtSymbolSegment(address) {
    let offset = 0;
    let buffer = Buffer.alloc(256);

    let items = address.split('.');

    for (let i = 0; i < items.length; i++) {
      let item = items[i];
      let items2 = item.split('[');

      let tagname = items2[0];
      let tagnameLength = tagname.length;

      buffer.writeUInt8(0x91, offset); offset += 1;
      buffer.writeUInt8(tagnameLength, offset); offset += 1;

      for (let j = 0; j < tagnameLength; j++) {
        buffer.writeUInt8(tagname.charCodeAt(j), offset); offset += 1;
      }

      offset += tagnameLength % 2 === 1 ? 1 : 0;

      if (items2.length > 1) {
        let elements = items2[1];
        elements = elements.substring(0, elements.length - 1);
        elements = elements.split(',');

        for (let j = 0; j < elements.length; j++) {
          let element = parseInt(elements[j], 16);

          if (!isNaN(element)) {
            if (element <= 0xFF) {
              buffer.writeUInt8(0x28, offset); offset += 1;
              buffer.writeUInt8(element, offset); offset += 1;
            } else if (element <= 0xFFFF) {
              buffer.writeUInt8(0x29, offset); offset += 2;
              buffer.writeUInt16LE(element, offset); offset += 2;
            } else {
              buffer.writeUInt8(0x30, offset); offset += 2;
              buffer.writeUInt32LE(element, offset); offset += 4;
            }
          } else {
            throw new Error('Element is not an integer: ' + address);
          }
        }
      }
    }

    return buffer.slice(0, offset);
  }

  constructor(service, path) {
    this._service = service;
    this._path = path;
  }

  request(service, path, data) {
    let offset = 0;
    // let buffer = Buffer.alloc(256);
    let buffer = Buffer.alloc(2 + path.length + data.length);
    buffer.writeUInt8(service, offset); offset += 1;
    buffer.writeUInt8(path.length / 2, offset); offset += 1;
    path.copy(buffer, offset); offset += path.length;
    data.copy(buffer, offset); offset + data.length;

    return buffer;
  }

  fromBuffer(buffer) {
    let offset = 0;
    let response = {};
    response.buffer = Buffer.from(buffer);
    response.service = buffer.readUInt8(offset); offset += 1;
    offset += 1; // reserved
    response.status = buffer.readUInt8(offset); offset += 1;

    let sizeOfAdditionStatus = buffer.readUInt8(offset); offset += 1; // number of 16 bit words
    if (sizeOfAdditionStatus > 0) {
      response.additionalStatus = buffer.slice(offset, offset + 2 * sizeOfAdditionStatus);
      offset += 2 * sizeOfAdditionStatus;
    }

    response.data = buffer.slice(offset);
    return response;
  }

  static Request(service, path, data) {
    let offset = 0;
    // let buffer = Buffer.alloc(256);
    let buffer = Buffer.alloc(2 + path.length + data.length);
    buffer.writeUInt8(service, offset); offset += 1;
    buffer.writeUInt8(path.length / 2, offset); offset += 1;
    path.copy(buffer, offset); offset += path.length;
    data.copy(buffer, offset); offset + data.length;

    return buffer;
  }

  static Reply(buffer) {
    let offset = 0;
    let res = {};
    res.buffer = Buffer.from(buffer);
    res.service = buffer.readUInt8(offset); offset += 1;
    offset += 1; // reserved
    res.statusCode = buffer.readUInt8(offset); offset += 1;

    if (res.statusCode !== 0) {
      res.statusDescription = CIPGeneralStatusCodeNames[res.statusCode];
    } else {
      res.statusDescription = '';
    }

    let additionalStatusSize = buffer.readUInt8(offset); offset += 1; // number of 16 bit words
    if (additionalStatusSize > 0) {
      res.additionalStatus = buffer.slice(offset, offset + 2 * additionalStatusSize);
      offset += 2 * additionalStatusSize;
    }

    res.data = buffer.slice(offset);
    return res;
  }

  static Segments(buffer) {
    let offset = 0;
    let length = buffer.length;
    let segments = [];

    let count = 0;

    while (offset < length) {
      count++;
      if (count > 100) throw new Error('Inifinit loop');

      let segmentType = utils.getBits(buffer[offset], 5, 8);
      let segment = null;
      switch (segmentType) {
        case 0:
          offset += PortSegment(buffer, offset, segments);
          break;
        case 1:
          offset += LogicalSegment(buffer, offset, segments);
          break;
        case 2:
          offset += NetworkSegment(buffer, offset, segments);
          break;
        case 3:
          offset += SymbolicSegment(buffer, offset, segments);
          break;
        case 4:
          offset += DataSegment(buffer, offset, segments);
          break;
        case 5:
          // DataType constructed (EIP-CIP V1 Appendix C-2.2)
          break;
        case 6:
          // DataType elementary (EIP-CIP V1 Appendix C-2.1)
          break;
        default:
          throw new Error('Segment type not recognized');
      }
      segments.push(segment);
    }
    return segments;
  }
}

module.exports = MessageRouter;

MessageRouter.Code = 0x02;

// Data Segment - 0b100XXXXX
//    Simple Data Segment, 0x80, 0b10000000
//    ASNI Extended Symbol Segment, 0x91, 0b10010001

const Segments = {
  LogicalSegmentMemberID: 0x28,
  LogicalSegmentMemberLong: 0x29
};

function PortSegment(buffer, offset, segments) {
  // used for routing from one subnet to another
  let originalOffset = offset;
  // let length = 1;
  let extendedLinkAddressSize = utils.getBit(buffer[offset], 4);
  let portIdentifier = utils.getBits(buffer[offset], 0, 4);
  let linkAddress = null;
  let linkAddressSize = 0;
  offset += 1;

  if (extendedLinkAddressSize) {
    linkAddressSize = buffer[offset]; offset += 1;
  } else {
    linkAddressSize = 1;
  }

  if (portIdentifier === 15) {
    portIdentifier = buffer.readUInt16LE(offset); offset += 2;
  }

  linkAddress = buffer.slice(offset, offset + linkAddressSize);

  offset += linkAddressSize;

  offset = (offset - originalOffset) % 2 === 0 ? offset : offset + 1;

  segments.push({
    Type: CIPSegmentType.PortSegment,
    Port: portIdentifier,
    Address: linkAddress
  });

  return offset - originalOffset;
}

function LogicalSegment(buffer, offset, segments) {
  // logical reference information (such as class/instance/attribute IDs)
  let originalOffset = 0;
  let logicalType = utils.getBits(buffer[offset], 2, 5);
  let logicalFormat = utils.getBits(buffer[offset], 0, 2);

  // 8bit - all
  // 16bit - ClassID, InstanceID, MemberID, ConnectionPoint
  // 32bit - not allowed, reserved for future use

  return offset - originalOffset;
}

function NetworkSegment(buffer, offset, segments) {

}

function SymbolicSegment(buffer, offset, segments) {
  // symbolic name
}

function DataSegment(buffer, offset, segments) {
  // embedded data (such as configuration data)
}

// CIP-V1-1.0 Appendix B-1. General status codes
const CIPGeneralStatusCodeNames = {
  0x01: 'Connection failure',
  0x02: 'Resource unavailable',
  0x03: 'Invalid parameter value',
  0x04: 'Path segment error',
  0x05: 'Path destination unknown',
  0x06: 'Partial transfer',
  0x07: 'Connection lost',
  0x08: 'Service not supported',
  0x09: 'Invalid attribute value',
  0x0A: 'Attribute list error',
  0x0B: 'Already in requested mode/state',
  0x0C: 'Object state conflict',
  0x0D: 'Object already exists',
  0x0E: 'Attribute not settable',
  0x0F: 'Privilege violation',
  0x10: 'Device state conflict',
  0x11: 'Reply data too large',
  0x12: 'Fragmentation of a primitive value',
  0x13: 'Not enough data',
  0x14: 'Attribute not supported',
  0x15: 'Too much data',
  0x16: 'Objet does not exist',
  0x17: 'Service fragmentation sequence not in progress',
  0x18: 'No stored attribute data',
  0x19: 'Store operation failure',
  0x1A: 'Routing failure, request packet too large',
  0x1B: 'Routing failure, response packet too large',
  0x1C: 'Missing attribute list entry data',
  0x1D: 'Invalid attribute value list',
  0x1E: 'Embedded service error',
  0x1F: 'Vendor specific error',
  0x20: 'Invalid parameter',
  0x21: 'Write-once value or medium already written',
  0x22: 'Invalid Replay Received',
  0x25: 'Key Failure in path',
  0x26: 'Path Size Invalid',
  0x27: 'Unexpected attribute in list',
  0x28: 'Invalid member ID',
  0x29: 'Member not settable',
  0x2A: 'Group 2 only server general failure'
};

// CIP-V1-1.0 Appendix B-1. General status codes
const CIPGeneralStatusCodeDescriptions = {
  0x01: 'A connection related service failed along the connection path.',
  0x02: 'Resources needed for the object to perform the requested service were unavailable.',
  0x03: 'See Status Code 0x20, which is the preferred value to use for this condition.',
  0x04: 'The path segment identifier or the segment syntax was not understood by the processing node. Path processing shall stop when a path segment error is encountered.',
  0x05: 'The path is referencing an object class, instance or structure element that is not known or is not contained in the processing node. Path processing shall stop when a path destination unknown error is encountered.',
  0x06: 'Only part of the expected data was transferred.',
  0x07: 'The message connection was lost.',
  0x08: 'The requested service was not implemented or was not defined for this Object Class/Instance.',
  0x09: 'Invalid attribute data detected.',
  0x0A: 'An attribute in the Get_Attribute_List or Set_Attribute_List response has a non-zero status.',
  0x0B: 'The object is already in the mode/state being requested by the service.',
  0x0C: 'The object cannot perform the requested service in its current mode/state.',
  0x0D: 'The requested instance of object to be created already exists.',
  0x0E: 'A request to modify a non-modifiable attribute was received.',
  0x0F: 'A permission/privilege check failed.',
  0x10: 'The device’s current mode/state prohibits the execution of the requested service.',
  0x11: 'The data to be transmitted in the response buffer is larger than the allocated response buffer.',
  0x12: 'The service specified an operation that is going to fragment a primitive data value, i.e. half a REAL data type.',
  0x13: 'The service did not supply enough data to perform the specified operation.',
  0x14: 'The attribute specified in the request is not supported.',
  0x15: 'The service supplied more data than was expected.',
  0x16: 'The object specified does not exist in the device.',
  0x17: 'The fragmentation sequence for this service is not currently active for this data.',
  0x18: 'The attribute data of this object was not saved prior to the requested service.',
  0x19: 'The attribute data of this object was not saved due to a failure during the attempt.',
  0x1A: 'The service request packet was too large for transmission on a network in the path to the destination. The routing device was forced to abort the service.',
  0x1B: 'The service response packet was too large for transmission on a network in the path from the destination. The routing device was forced to abort the service.',
  0x1C: 'The service did not supply an attribute in a list of attributes that was needed by the service to perform the requested behaviour.',
  0x1D: 'The service is returning the list of attributes supplied with status information for those attributes that were invalid.',
  0x1E: 'An embedded service resulted in an error.',
  0x1F: 'A vendor specific error has been encountered. The Additional Code Field of the Error Response defines the particular error encountered. Use of this General Error Code should only be performed when none of the Error Codes presented in this table or within an Object Class definition accurately reflect the error.',
  0x20: 'A parameter associated with the request was invalid. This code is used when a parameter does not meet the requirements of this specification and/or the requirements defined in an Application Object Specification.',
  0x21: 'An attempt was made to write to a write-once medium (e.g. WORM drive, PROM) that has already been written, or to modify a value that cannot be changed once established.',
  0x22: 'An invalid reply is received (e.g. reply service code does not match the request service code, or reply message is shorter than the minimum expected reply size). This status code can serve for other causes of invalid replies.',
  0x25: 'The Key Segment that was included as the first segment in the path does not match the destination module. The object specific status shall indicate which part of the key check failed.',
  0x26: 'The size of the path which was sent with the Service Request is either not large enough to allow the Request to be routed to an object or too much routing data was included.',
  0x27: 'An attempt was made to set an attribute that is not able to be set at this time.',
  0x28: 'The Member ID specified in the request does not exist in the specified Class/Instance/Attribute.',
  0x29: 'A request to modify a non-modifiable member was received.',
  0x2A: 'This error code may only be reported by DeviceNet group 2 only servers with 4K or less code space and only in place of Service not supported, Attribute not supported and Attribute not settable.'
};

MessageRouter.StatusDescriptions = CIPGeneralStatusCodeNames;
<commit_msg>Removed instance methods<commit_after>'use strict';

class MessageRouter {
  static Request(service, path, data) {
    let offset = 0;
    // let buffer = Buffer.alloc(256);
    let buffer = Buffer.alloc(2 + path.length + data.length);
    buffer.writeUInt8(service, offset); offset += 1;
    buffer.writeUInt8(path.length / 2, offset); offset += 1;
    path.copy(buffer, offset); offset += path.length;
    data.copy(buffer, offset); offset + data.length;

    return buffer;
  }

  static Reply(buffer) {
    let offset = 0;
    let res = {};
    res.buffer = Buffer.from(buffer);
    res.service = buffer.readUInt8(offset); offset += 1;
    offset += 1; // reserved
    res.statusCode = buffer.readUInt8(offset); offset += 1;

    if (res.statusCode !== 0) {
      res.statusDescription = CIPGeneralStatusCodeNames[res.statusCode];
    } else {
      res.statusDescription = '';
    }

    let additionalStatusSize = buffer.readUInt8(offset); offset += 1; // number of 16 bit words
    if (additionalStatusSize > 0) {
      res.additionalStatus = buffer.slice(offset, offset + 2 * additionalStatusSize);
      offset += 2 * additionalStatusSize;
    }

    res.data = buffer.slice(offset);
    return res;
  }

  // tagname
  // tagname.member
  // tagname[element]
  // tagname[element].member
  // tagname[idx1,idx2]

  // EIP-CIP V1 Appendix C-1.4.5.2, pg.
  static ANSIExtSymbolSegment(address) {
    let offset = 0;
    let buffer = Buffer.alloc(256);

    let items = address.split('.');

    for (let i = 0; i < items.length; i++) {
      let item = items[i];
      let items2 = item.split('[');

      let tagname = items2[0];
      let tagnameLength = tagname.length;

      buffer.writeUInt8(0x91, offset); offset += 1;
      buffer.writeUInt8(tagnameLength, offset); offset += 1;

      for (let j = 0; j < tagnameLength; j++) {
        buffer.writeUInt8(tagname.charCodeAt(j), offset); offset += 1;
      }

      offset += tagnameLength % 2 === 1 ? 1 : 0;

      if (items2.length > 1) {
        let elements = items2[1];
        elements = elements.substring(0, elements.length - 1);
        elements = elements.split(',');

        for (let j = 0; j < elements.length; j++) {
          let element = parseInt(elements[j], 16);

          if (!isNaN(element)) {
            if (element <= 0xFF) {
              buffer.writeUInt8(0x28, offset); offset += 1;
              buffer.writeUInt8(element, offset); offset += 1;
            } else if (element <= 0xFFFF) {
              buffer.writeUInt8(0x29, offset); offset += 2;
              buffer.writeUInt16LE(element, offset); offset += 2;
            } else {
              buffer.writeUInt8(0x30, offset); offset += 2;
              buffer.writeUInt32LE(element, offset); offset += 4;
            }
          } else {
            throw new Error('Element is not an integer: ' + address);
          }
        }
      }
    }

    return buffer.slice(0, offset);
  }

  static Segments(buffer) {
    let offset = 0;
    let length = buffer.length;
    let segments = [];

    let count = 0;

    while (offset < length) {
      count++;
      if (count > 100) throw new Error('Inifinit loop');

      let segmentType = utils.getBits(buffer[offset], 5, 8);
      let segment = null;
      switch (segmentType) {
        case 0:
          offset += PortSegment(buffer, offset, segments);
          break;
        case 1:
          offset += LogicalSegment(buffer, offset, segments);
          break;
        case 2:
          offset += NetworkSegment(buffer, offset, segments);
          break;
        case 3:
          offset += SymbolicSegment(buffer, offset, segments);
          break;
        case 4:
          offset += DataSegment(buffer, offset, segments);
          break;
        case 5:
          // DataType constructed (EIP-CIP V1 Appendix C-2.2)
          break;
        case 6:
          // DataType elementary (EIP-CIP V1 Appendix C-2.1)
          break;
        default:
          throw new Error('Segment type not recognized');
      }
      segments.push(segment);
    }
    return segments;
  }
}

module.exports = MessageRouter;

MessageRouter.Code = 0x02;

// Data Segment - 0b100XXXXX
//    Simple Data Segment, 0x80, 0b10000000
//    ASNI Extended Symbol Segment, 0x91, 0b10010001

const Segments = {
  LogicalSegmentMemberID: 0x28,
  LogicalSegmentMemberLong: 0x29
};

function PortSegment(buffer, offset, segments) {
  // used for routing from one subnet to another
  let originalOffset = offset;
  // let length = 1;
  let extendedLinkAddressSize = utils.getBit(buffer[offset], 4);
  let portIdentifier = utils.getBits(buffer[offset], 0, 4);
  let linkAddress = null;
  let linkAddressSize = 0;
  offset += 1;

  if (extendedLinkAddressSize) {
    linkAddressSize = buffer[offset]; offset += 1;
  } else {
    linkAddressSize = 1;
  }

  if (portIdentifier === 15) {
    portIdentifier = buffer.readUInt16LE(offset); offset += 2;
  }

  linkAddress = buffer.slice(offset, offset + linkAddressSize);

  offset += linkAddressSize;

  offset = (offset - originalOffset) % 2 === 0 ? offset : offset + 1;

  segments.push({
    Type: CIPSegmentType.PortSegment,
    Port: portIdentifier,
    Address: linkAddress
  });

  return offset - originalOffset;
}

function LogicalSegment(buffer, offset, segments) {
  // logical reference information (such as class/instance/attribute IDs)
  let originalOffset = 0;
  let logicalType = utils.getBits(buffer[offset], 2, 5);
  let logicalFormat = utils.getBits(buffer[offset], 0, 2);

  // 8bit - all
  // 16bit - ClassID, InstanceID, MemberID, ConnectionPoint
  // 32bit - not allowed, reserved for future use

  return offset - originalOffset;
}

function NetworkSegment(buffer, offset, segments) {

}

function SymbolicSegment(buffer, offset, segments) {
  // symbolic name
}

function DataSegment(buffer, offset, segments) {
  // embedded data (such as configuration data)
}

// CIP-V1-1.0 Appendix B-1. General status codes
const CIPGeneralStatusCodeNames = {
  0x01: 'Connection failure',
  0x02: 'Resource unavailable',
  0x03: 'Invalid parameter value',
  0x04: 'Path segment error',
  0x05: 'Path destination unknown',
  0x06: 'Partial transfer',
  0x07: 'Connection lost',
  0x08: 'Service not supported',
  0x09: 'Invalid attribute value',
  0x0A: 'Attribute list error',
  0x0B: 'Already in requested mode/state',
  0x0C: 'Object state conflict',
  0x0D: 'Object already exists',
  0x0E: 'Attribute not settable',
  0x0F: 'Privilege violation',
  0x10: 'Device state conflict',
  0x11: 'Reply data too large',
  0x12: 'Fragmentation of a primitive value',
  0x13: 'Not enough data',
  0x14: 'Attribute not supported',
  0x15: 'Too much data',
  0x16: 'Objet does not exist',
  0x17: 'Service fragmentation sequence not in progress',
  0x18: 'No stored attribute data',
  0x19: 'Store operation failure',
  0x1A: 'Routing failure, request packet too large',
  0x1B: 'Routing failure, response packet too large',
  0x1C: 'Missing attribute list entry data',
  0x1D: 'Invalid attribute value list',
  0x1E: 'Embedded service error',
  0x1F: 'Vendor specific error',
  0x20: 'Invalid parameter',
  0x21: 'Write-once value or medium already written',
  0x22: 'Invalid Replay Received',
  0x25: 'Key Failure in path',
  0x26: 'Path Size Invalid',
  0x27: 'Unexpected attribute in list',
  0x28: 'Invalid member ID',
  0x29: 'Member not settable',
  0x2A: 'Group 2 only server general failure'
};

// CIP-V1-1.0 Appendix B-1. General status codes
const CIPGeneralStatusCodeDescriptions = {
  0x01: 'A connection related service failed along the connection path.',
  0x02: 'Resources needed for the object to perform the requested service were unavailable.',
  0x03: 'See Status Code 0x20, which is the preferred value to use for this condition.',
  0x04: 'The path segment identifier or the segment syntax was not understood by the processing node. Path processing shall stop when a path segment error is encountered.',
  0x05: 'The path is referencing an object class, instance or structure element that is not known or is not contained in the processing node. Path processing shall stop when a path destination unknown error is encountered.',
  0x06: 'Only part of the expected data was transferred.',
  0x07: 'The message connection was lost.',
  0x08: 'The requested service was not implemented or was not defined for this Object Class/Instance.',
  0x09: 'Invalid attribute data detected.',
  0x0A: 'An attribute in the Get_Attribute_List or Set_Attribute_List response has a non-zero status.',
  0x0B: 'The object is already in the mode/state being requested by the service.',
  0x0C: 'The object cannot perform the requested service in its current mode/state.',
  0x0D: 'The requested instance of object to be created already exists.',
  0x0E: 'A request to modify a non-modifiable attribute was received.',
  0x0F: 'A permission/privilege check failed.',
  0x10: 'The device’s current mode/state prohibits the execution of the requested service.',
  0x11: 'The data to be transmitted in the response buffer is larger than the allocated response buffer.',
  0x12: 'The service specified an operation that is going to fragment a primitive data value, i.e. half a REAL data type.',
  0x13: 'The service did not supply enough data to perform the specified operation.',
  0x14: 'The attribute specified in the request is not supported.',
  0x15: 'The service supplied more data than was expected.',
  0x16: 'The object specified does not exist in the device.',
  0x17: 'The fragmentation sequence for this service is not currently active for this data.',
  0x18: 'The attribute data of this object was not saved prior to the requested service.',
  0x19: 'The attribute data of this object was not saved due to a failure during the attempt.',
  0x1A: 'The service request packet was too large for transmission on a network in the path to the destination. The routing device was forced to abort the service.',
  0x1B: 'The service response packet was too large for transmission on a network in the path from the destination. The routing device was forced to abort the service.',
  0x1C: 'The service did not supply an attribute in a list of attributes that was needed by the service to perform the requested behaviour.',
  0x1D: 'The service is returning the list of attributes supplied with status information for those attributes that were invalid.',
  0x1E: 'An embedded service resulted in an error.',
  0x1F: 'A vendor specific error has been encountered. The Additional Code Field of the Error Response defines the particular error encountered. Use of this General Error Code should only be performed when none of the Error Codes presented in this table or within an Object Class definition accurately reflect the error.',
  0x20: 'A parameter associated with the request was invalid. This code is used when a parameter does not meet the requirements of this specification and/or the requirements defined in an Application Object Specification.',
  0x21: 'An attempt was made to write to a write-once medium (e.g. WORM drive, PROM) that has already been written, or to modify a value that cannot be changed once established.',
  0x22: 'An invalid reply is received (e.g. reply service code does not match the request service code, or reply message is shorter than the minimum expected reply size). This status code can serve for other causes of invalid replies.',
  0x25: 'The Key Segment that was included as the first segment in the path does not match the destination module. The object specific status shall indicate which part of the key check failed.',
  0x26: 'The size of the path which was sent with the Service Request is either not large enough to allow the Request to be routed to an object or too much routing data was included.',
  0x27: 'An attempt was made to set an attribute that is not able to be set at this time.',
  0x28: 'The Member ID specified in the request does not exist in the specified Class/Instance/Attribute.',
  0x29: 'A request to modify a non-modifiable member was received.',
  0x2A: 'This error code may only be reported by DeviceNet group 2 only servers with 4K or less code space and only in place of Service not supported, Attribute not supported and Attribute not settable.'
};

MessageRouter.StatusDescriptions = CIPGeneralStatusCodeNames;
<|endoftext|>


<commit_before>/* G. Hemingway Copyright @2016
 * Process shell geometry data from wire format to internal
 */

"use strict";

function unindexValues(data, values, buffers) {
    let numValues = data.pointsIndex.length;
    for (let i = 0; i < numValues; i++) {
        buffers.position[i] = values[data.pointsIndex[i]];
        if (buffers.position[i]===undefined|| isNaN(buffers.position[i])) buffers.position[i]=0;
        buffers.normal[i] = values[data.normalsIndex[i]];
        if (buffers.normal[i]===undefined ||isNaN(buffers.normal[i])) buffers.normal[i]=0;
    }
}

function uncompressColorsA(data, colorsBuffer) {
    let index = 0;
    let numBlocks = data.colorsData.length;
    for (let i = 0; i < numBlocks; i++) {
        let block = data.colorsData[i];
        let r, g, b;
        // Be robust to crappy color data
        try {
            r = block.data[0];
            g = block.data[1];
            b = block.data[2];
        } catch(ex) {
            r = 1.0;
            g = 1.0;
            b = 1.0;
        }
        for (let j = 0; j < block.duration; j++) {
            colorsBuffer[index++] = r;
            colorsBuffer[index++] = g;
            colorsBuffer[index++] = b;
        }
    }
}

/* Revised color compression method - requires a "faces" field in the JSON */
function uncompressColorsB(data, colorsBuffer) {
    let index = 0;
    let numBlocks = data.faces.length;
    for (let i = 0; i < numBlocks; i++) {
        let block = data.faces[i];
        let r, g, b;
        // Be robust to crappy color data
        try {
            r = block.color[0];
            g = block.color[1];
            b = block.color[2];
        } catch(ex) {
            r = 1.0;
            g = 1.0;
            b = 1.0;
        }
        for (let j = 0; j < block.count; j++) {
            colorsBuffer[index++] = r;
            colorsBuffer[index++] = g;
            colorsBuffer[index++] = b;
        }
    }
}

export function makeGeometry(buffers) {
    // Calculate number of triangles
    let size = buffers.position.length / 9;
    // Create the geometry to hold the data
    let geometry = new THREE.BufferGeometry();
    geometry.addAttribute('position', new THREE.BufferAttribute(size * 3, 3));
    geometry.addAttribute('normal',   new THREE.BufferAttribute(size * 3, 3), true);
    geometry.addAttribute('color',    new THREE.BufferAttribute(size * 3, 3));
    geometry.addAttribute('values',   new THREE.BufferAttribute(buffers.values, 1));

    // Now load the rest of the data
    geometry.attributes.position.array = buffers.position;
    geometry.attributes.normal.array = buffers.normal;
    geometry.attributes.color.array = buffers.color;

    // Compute bbox
    geometry.computeBoundingBox();
    return geometry;
}

export function processKeyframe(dataJSON) {
    // Correct precision
    if (dataJSON.values) {
        let precision = dataJSON.precision || 2;
        let factor = Math.pow(10, precision);
        let length = dataJSON.values.length;
        for (let i = 0; i < length; i++) {
            dataJSON.values[i] /= factor;
        }
    }
    // Just copy the data into arrays
    var buffers = {
        position:   new Float32Array(dataJSON.pointsIndex.length),
        normal:    new Float32Array(dataJSON.pointsIndex.length),
        color:     new Float32Array(dataJSON.pointsIndex.length),
        values:     new Float32Array(dataJSON.values)
    };
    unindexValues(dataJSON, dataJSON.values, buffers);
    // Handle colors
    if (dataJSON.colorsData) {
        uncompressColorsA(dataJSON, buffers.color);
    } else if (dataJSON.faces) {
        uncompressColorsB(dataJSON, buffers.color);
    }
    return buffers;
}

export function processDelta(dataJSON, obj) {
    let oldGeom = obj.model.getGeometry();
    let oldValues = oldGeom.getAttribute('values').array;
    // Correct precision
    if (dataJSON.values) {
        let precision = dataJSON.precision || obj.precision || 2;
        let factor = Math.pow(10, precision);
        let length = dataJSON.values.length;
        for (let i = 0; i < length; i++) {
            dataJSON.values[i] /= factor;
        }
    }
    let deltaBuffers = {
        position:   new Float32Array(dataJSON.pointsIndex.length),
        normal:    new Float32Array(dataJSON.pointsIndex.length),
        color:     new Float32Array(dataJSON.pointsIndex.length),
    };
    //console.log(dataJSON.pointsIndex.length);

    // Allocate the new buffers
    let oldPositions = oldGeom.getAttribute('position').array;
    let oldNormals = oldGeom.getAttribute('normal').array;
    let oldColors = oldGeom.getAttribute('color').array;
    let offset = oldPositions.length;
    let newLength = offset + deltaBuffers.position.length;
    let buffers = {
        position:   new Float32Array(newLength),
        normal:     new Float32Array(newLength),
        color:      new Float32Array(newLength),
        values:     new Float32Array(oldValues.length + dataJSON.values.length)
    };

    // Concatenate values and dataJSON.values into new values array
    buffers.values.set(oldValues);
    buffers.values.set(dataJSON.values, oldValues.length);
    // Now we can use the full values array to un-index the delta data
    unindexValues(dataJSON, buffers.values, deltaBuffers);
    // Get the color data out too (choose right version)
    if (dataJSON.colorsData) {
        uncompressColorsA(dataJSON, deltaBuffers.color);
    } else if (dataJSON.faces) {
        // Different approach here
        uncompressColorsB(dataJSON, deltaBuffers.color);
    }

    let nullNonet = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    // Trim out the remove values
    for (let i = 0; i < dataJSON.remove.length; ++i) {
        let pos = dataJSON.remove[i] * 9;
        oldPositions.set(nullNonet, pos);
        oldNormals.set(nullNonet, pos);
        oldColors.set(nullNonet, pos);
    }

    // Concatenate together the new position, normal and color values
    buffers.position.set(oldPositions);
    buffers.position.set(deltaBuffers.position, offset);
    buffers.normal.set(oldNormals);
    buffers.normal.set(deltaBuffers.normal, offset);
    buffers.color.set(oldColors);
    buffers.color.set(deltaBuffers.color, offset);
    // And, we are done
    return buffers;
}<commit_msg>re-arranged the order of removal processing<commit_after>/* G. Hemingway Copyright @2016
 * Process shell geometry data from wire format to internal
 */

"use strict";

function unindexValues(data, values, buffers) {
    let numValues = data.pointsIndex.length;
    for (let i = 0; i < numValues; i++) {
        buffers.position[i] = values[data.pointsIndex[i]];
        if (buffers.position[i]===undefined|| isNaN(buffers.position[i])) buffers.position[i]=0;
        buffers.normal[i] = values[data.normalsIndex[i]];
        if (buffers.normal[i]===undefined ||isNaN(buffers.normal[i])) buffers.normal[i]=0;
    }
}

function uncompressColorsA(data, colorsBuffer) {
    let index = 0;
    let numBlocks = data.colorsData.length;
    for (let i = 0; i < numBlocks; i++) {
        let block = data.colorsData[i];
        let r, g, b;
        // Be robust to crappy color data
        try {
            r = block.data[0];
            g = block.data[1];
            b = block.data[2];
        } catch(ex) {
            r = 1.0;
            g = 1.0;
            b = 1.0;
        }
        for (let j = 0; j < block.duration; j++) {
            colorsBuffer[index++] = r;
            colorsBuffer[index++] = g;
            colorsBuffer[index++] = b;
        }
    }
}

/* Revised color compression method - requires a "faces" field in the JSON */
function uncompressColorsB(data, colorsBuffer) {
    let index = 0;
    let numBlocks = data.faces.length;
    for (let i = 0; i < numBlocks; i++) {
        let block = data.faces[i];
        let r, g, b;
        // Be robust to crappy color data
        try {
            r = block.color[0];
            g = block.color[1];
            b = block.color[2];
        } catch(ex) {
            r = 1.0;
            g = 1.0;
            b = 1.0;
        }
        for (let j = 0; j < block.count; j++) {
            colorsBuffer[index++] = r;
            colorsBuffer[index++] = g;
            colorsBuffer[index++] = b;
        }
    }
}

export function makeGeometry(buffers) {
    // Calculate number of triangles
    let size = buffers.position.length / 9;
    // Create the geometry to hold the data
    let geometry = new THREE.BufferGeometry();
    geometry.addAttribute('position', new THREE.BufferAttribute(size * 3, 3));
    geometry.addAttribute('normal',   new THREE.BufferAttribute(size * 3, 3), true);
    geometry.addAttribute('color',    new THREE.BufferAttribute(size * 3, 3));
    geometry.addAttribute('values',   new THREE.BufferAttribute(buffers.values, 1));

    // Now load the rest of the data
    geometry.attributes.position.array = buffers.position;
    geometry.attributes.normal.array = buffers.normal;
    geometry.attributes.color.array = buffers.color;

    // Compute bbox
    geometry.computeBoundingBox();
    return geometry;
}

export function processKeyframe(dataJSON) {
    // Correct precision
    if (dataJSON.values) {
        let precision = dataJSON.precision || 2;
        let factor = Math.pow(10, precision);
        let length = dataJSON.values.length;
        for (let i = 0; i < length; i++) {
            dataJSON.values[i] /= factor;
        }
    }
    // Just copy the data into arrays
    var buffers = {
        position:   new Float32Array(dataJSON.pointsIndex.length),
        normal:    new Float32Array(dataJSON.pointsIndex.length),
        color:     new Float32Array(dataJSON.pointsIndex.length),
        values:     new Float32Array(dataJSON.values)
    };
    unindexValues(dataJSON, dataJSON.values, buffers);
    // Handle colors
    if (dataJSON.colorsData) {
        uncompressColorsA(dataJSON, buffers.color);
    } else if (dataJSON.faces) {
        uncompressColorsB(dataJSON, buffers.color);
    }
    return buffers;
}

export function processDelta(dataJSON, obj) {
    let oldGeom = obj.model.getGeometry();
    let oldValues = oldGeom.getAttribute('values').array;
    // Correct precision
    if (dataJSON.values) {
        let precision = dataJSON.precision || obj.precision || 2;
        let factor = Math.pow(10, precision);
        let length = dataJSON.values.length;
        for (let i = 0; i < length; i++) {
            dataJSON.values[i] /= factor;
        }
    }
    let deltaBuffers = {
        position:   new Float32Array(dataJSON.pointsIndex.length),
        normal:    new Float32Array(dataJSON.pointsIndex.length),
        color:     new Float32Array(dataJSON.pointsIndex.length),
    };
    //console.log(dataJSON.pointsIndex.length);

    // Allocate the new buffers
    let oldPositions = oldGeom.getAttribute('position').array;
    let oldNormals = oldGeom.getAttribute('normal').array;
    let oldColors = oldGeom.getAttribute('color').array;
    let offset = oldPositions.length;
    let newLength = offset + deltaBuffers.position.length;
    let buffers = {
        position:   new Float32Array(newLength),
        normal:     new Float32Array(newLength),
        color:      new Float32Array(newLength),
        values:     new Float32Array(oldValues.length + dataJSON.values.length)
    };

    // Concatenate values and dataJSON.values into new values array
    buffers.values.set(oldValues);
    buffers.values.set(dataJSON.values, oldValues.length);
    // Now we can use the full values array to un-index the delta data
    unindexValues(dataJSON, buffers.values, deltaBuffers);
    // Get the color data out too (choose right version)
    if (dataJSON.colorsData) {
        uncompressColorsA(dataJSON, deltaBuffers.color);
    } else if (dataJSON.faces) {
        // Different approach here
        uncompressColorsB(dataJSON, deltaBuffers.color);
    }
    // Concatenate together the new position, normal and color values
    buffers.position.set(oldPositions);
    buffers.position.set(deltaBuffers.position, offset);
    buffers.normal.set(oldNormals);
    buffers.normal.set(deltaBuffers.normal, offset);
    buffers.color.set(oldColors);
    buffers.color.set(deltaBuffers.color, offset);

    let nullNonet = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    // Trim out the remove values
    for (let i = 0; i < dataJSON.remove.length; ++i) {
        let pos = dataJSON.remove[i] * 9;
        buffers.position.set(nullNonet, pos);
        buffers.normal.set(nullNonet, pos);
        buffers.color.set(nullNonet, pos);
    }

   // And, we are done
    return buffers;
}<|endoftext|>


<commit_before>describe('subsemitter-emitloop', function() {

	var Event
	var Emitter
	var SubsEmitter
	var Observable

	it.should('require', function(){
		Event = require( '../lib/event' )
		Emitter = require( '../lib/emitter' )
		SubsEmitter = require( '../lib/observable/subscribe/emitter' )
		Observable = require( '../lib/observable' )
	})

	it.skip('emits on SubsEmitter are meta-postponed ', function() {

		var obs = new Observable()

		var subsemitter = new SubsEmitter({
			listener: function( ) {
				console.log('subscription handler fired!')
			}
		})

		var emitterA = new Emitter({
			$change: {
				$val: function(event) {
					console.log('emitterA is fired')
				}
			}
		})

		var emitterB = new Emitter({
			$change: {
				$val: function(event) {
					console.log('emitterB is fired')
					subsemitter.$emit(event, obs)
					emitterA.$emit(event, obs)
					console.log('check out dat event!', event.$postponed)
				}
			}
		})

		var e = new Event()

		emitterB.$emit(e, obs)

	})

})
<commit_msg>correct require paths<commit_after>describe('subsemitter-emitloop', function() {

	var Event
	var Emitter
	var SubsEmitter
	var Observable

	it('requires', function(){
		Event = require( '../../../../lib/event' )
		Emitter = require( '../../../../lib/emitter' )
		SubsEmitter = require( '../../../../lib/observable/subscribe/emitter' )
		Observable = require( '../../../../lib/observable' )
	})

	it('emits on SubsEmitter are meta-postponed ', function() {

		var obs = new Observable()

		var subsemitter = new SubsEmitter({
			listener: function( ) {
				console.log('subscription handler fired!')
			}
		})

		var emitterA = new Emitter({
			$change: {
				$val: function(event) {
					console.log('emitterA is fired')
				}
			}
		})

		var emitterB = new Emitter({
			$change: {
				$val: function(event) {
					console.log('emitterB is fired')
					subsemitter.$emit(event, obs)
					emitterA.$emit(event, obs)
					console.log('check out dat event!', event.$postponed)
				}
			}
		})

		var e = new Event()

		emitterB.$emit(e, obs)

	})

})
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2017 o2r project
 *
 * 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.
 *
 */
const debug = require('debug')('muncher:bagit');
const Bag = require('bagit');
const path = require('path');
const fs = require('fs');
const config = require('../config/config');
const cleanMessage = require('../lib/error-message');

/*
 *  Load the associated bag, check if it's valid.
 *
 *  The provided object must have the fields id and bagpath.
 */
function validateBag(passon) {
    return new Promise((fulfill, reject) => {
        debug('[%s] Validate bag', passon.id);

        let bag = new Bag(passon.bagpath);
        bag
            .validate(config.bagit.validateFast)
            .then(res => {
                debug('[%s] bag is valid: %s', passon.id, res);
                fulfill(passon);
            }).catch((err) => {
                debug('[%s] bag is _IN_valid [fail? %s]: %s', passon.id,
                    config.bagit.failOnValidationError.upload, err);

                if (config.bagit.failOnValidationError.upload) {
                    err.status = 400;
                    err.msg = 'bag ist invalid: ' + cleanMessage(err.message);
                    reject(err);
                } else {
                    fulfill(passon);
                }
            });
    });
}

compendiumIsBag = function (compendium_id) {
    let detectionFile = path.join(config.fs.compendium, compendium_id, config.bagit.detectionFileName);

    try {
        fs.accessSync(detectionFile);
        debug('[%s] Found %s - compendium is a bag!', compendium_id, detectionFile);
        return true;
    } catch (err) {
        debug('[%s] Could not find bag detection file for compendium, NOT a bag: %s', compendium_id, err);
        return false;
    }
}

jobIsBag = function (job_id) {
    let detectionFile = path.join(config.fs.job, job_id, config.bagit.detectionFileName);

    try {
        fs.accessSync(detectionFile);
        debug('[%s] Found %s - job is a bag!', job_id, detectionFile);
        return true;
    } catch (err) {
        debug('[%s] Could not find bag detection file for job, NOT a bag: %s', job_id, err);
        return false;
    }
}

module.exports.validateBag = validateBag;
module.exports.compendiumIsBag = compendiumIsBag;
module.exports.jobIsBag = jobIsBag;<commit_msg>no stacktrace on expected error<commit_after>/*
 * (C) Copyright 2017 o2r project
 *
 * 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.
 *
 */
const debug = require('debug')('muncher:bagit');
const Bag = require('bagit');
const path = require('path');
const fs = require('fs');
const config = require('../config/config');
const cleanMessage = require('../lib/error-message');

/*
 *  Load the associated bag, check if it's valid.
 *
 *  The provided object must have the fields id and bagpath.
 */
function validateBag(passon) {
    return new Promise((fulfill, reject) => {
        debug('[%s] Validate bag', passon.id);

        let bag = new Bag(passon.bagpath);
        bag
            .validate(config.bagit.validateFast)
            .then(res => {
                debug('[%s] bag is valid: %s', passon.id, res);
                fulfill(passon);
            }).catch((err) => {
                debug('[%s] bag is _IN_valid [fail? %s]: %s', passon.id,
                    config.bagit.failOnValidationError.upload, err);

                if (config.bagit.failOnValidationError.upload) {
                    err.status = 400;
                    err.msg = 'bag ist invalid: ' + cleanMessage(err.message);
                    reject(err);
                } else {
                    fulfill(passon);
                }
            });
    });
}

compendiumIsBag = function (compendium_id) {
    let detectionFile = path.join(config.fs.compendium, compendium_id, config.bagit.detectionFileName);

    try {
        fs.accessSync(detectionFile);
        debug('[%s] Found %s - compendium is a bag!', compendium_id, detectionFile);
        return true;
    } catch (err) {
        debug('[%s] Could not find bag detection file for compendium, NOT a bag: %s', compendium_id, err.message);
        return false;
    }
}

jobIsBag = function (job_id) {
    let detectionFile = path.join(config.fs.job, job_id, config.bagit.detectionFileName);

    try {
        fs.accessSync(detectionFile);
        debug('[%s] Found %s - job is a bag!', job_id, detectionFile);
        return true;
    } catch (err) {
        debug('[%s] Could not find bag detection file for job, NOT a bag: %s', job_id, err);
        return false;
    }
}

module.exports.validateBag = validateBag;
module.exports.compendiumIsBag = compendiumIsBag;
module.exports.jobIsBag = jobIsBag;<|endoftext|>


<commit_before>import storePretender from 'helpers/store-pretender';
import fixturePretender from 'helpers/fixture-pretender';

function parsePostData(query) {
  const result = {};
  query.split("&").forEach(function(part) {
    const item = part.split("=");
    const firstSeg = decodeURIComponent(item[0]);
    const m = /^([^\[]+)\[([^\]]+)\]/.exec(firstSeg);

    const val = decodeURIComponent(item[1]).replace(/\+/g, ' ');
    if (m) {
      result[m[1]] = result[m[1]] || {};
      result[m[1]][m[2]] = val;
    } else {
      result[firstSeg] = val;
    }

  });
  return result;
}

function response(code, obj) {
  if (typeof code === "object") {
    obj = code;
    code = 200;
  }
  return [code, {"Content-Type": "application/json"}, obj];
}

const success = () => response({ success: true });
const loggedIn = () => !!Discourse.User.current();

const helpers = { response, success, parsePostData };

export default function() {

  const server = new Pretender(function() {
    storePretender.call(this, helpers);
    const fixturesByUrl = fixturePretender.call(this, helpers);

    this.get('/admin/plugins', () => response({ plugins: [] }));

    this.get('/composer_messages', () => response({ composer_messages: [] }));

    this.get("/latest.json", () => {
      const json = fixturesByUrl['/latest.json'];

      if (loggedIn()) {
        // Stuff to let us post
        json.topic_list.can_create_topic = true;
        json.topic_list.draft_key = "new_topic";
        json.topic_list.draft_sequence = 1;
      }
      return response(json);
    });

    this.get('/tags', () => {
      return response({ tags: [{
        id: 'eviltrout',
        count: 1
      }] });
    });

    this.get(`/users/eviltrout/emails.json`, () => {
      return response({ email: 'eviltrout@example.com' });
    });

    this.get('/users/eviltrout.json', () => {
      const json = fixturesByUrl['/users/eviltrout.json'];
      json.user.can_edit = loggedIn();
      return response(json);
    });

    this.get('/users/eviltrout/summary.json', () => {
      return response({
        user_summary: {
          topics: [],
          topic_ids: [],
          replies: [],
          links: []
        },
        topics: [],
      });
    });

    this.get('/users/eviltrout/invited_count.json', () => {
      return response({
        "counts": { "pending": 1, "redeemed": 0, "total": 0 }
      });
    });

    this.get('/users/eviltrout/invited.json', () => {
      return response({ "invites": [ {id: 1} ] });
    });

    this.get('/topics/private-messages/eviltrout.json', () => {
      return response({ topic_list: { topics: [] } });
    });

    this.get('/clicks/track', success);

    this.get('/search', request => {
      if (request.queryParams.q === 'posts') {
        return response({
          posts: [{
            id: 1234
          }]
        });
      }

      return response({});
    });

    this.put('/users/eviltrout', () => response({ user: {} }));

    this.get("/t/280.json", () => response(fixturesByUrl['/t/280/1.json']));
    this.get("/t/28830.json", () => response(fixturesByUrl['/t/28830/1.json']));
    this.get("/t/9.json", () => response(fixturesByUrl['/t/9/1.json']));

    this.get("/t/id_for/:slug", () => {
      return response({id: 280, slug: "internationalization-localization", url: "/t/internationalization-localization/280"});
    });

    this.delete('/t/:id', success);
    this.put('/t/:id/recover', success);

    this.get("/404-body", () => {
      return [200, {"Content-Type": "text/html"}, "
not found
"];
    });

    this.delete('/draft.json', success);
    this.post('/draft.json', success);

    this.get('/users/:username/staff-info.json', () => response({}));

    this.get('/post_action_users', () => {
      return response({
        post_action_users: [
           {id: 1, username: 'eviltrout', avatar_template: '/user_avatar/default/eviltrout/{size}/1.png', username_lower: 'eviltrout' }
         ]
      });
    });

    this.get('/post_replies', () => {
      return response({ post_replies: [{ id: 1234, cooked: 'wat' }] });
    });

    this.get('/post_reply_histories', () => {
      return response({ post_reply_histories: [{ id: 1234, cooked: 'wat' }] });
    });

    this.get('/category_hashtags/check', () => {
      return response({ valid: [{ slug: "bug", url: '/c/bugs' }] });
    });

    this.get("/categories_and_latest", () => response(fixturesByUrl["/categories_and_latest.json"]));

    this.put('/categories/:category_id', request => {

      const category = parsePostData(request.requestBody);

      if (category.email_in === "duplicate@example.com") {
        return response(422, {"errors": ['duplicate email']});
      }

      return response({category});
    });

    this.get('/draft.json', () => response({}));

    this.put('/queued_posts/:queued_post_id', function(request) {
      return response({ queued_post: {id: request.params.queued_post_id } });
    });

    this.get('/queued_posts', function() {
      return response({
        queued_posts: [{id: 1, raw: 'queued post text', can_delete_user: true}]
      });
    });

    this.post('/session', function(request) {
      const data = parsePostData(request.requestBody);

      if (data.password === 'correct') {
        return response({username: 'eviltrout'});
      }

      if (data.password === 'not-activated') {
        return response({ error: "not active",
                          reason: "not_activated",
                          sent_to_email: 'eviltrout@example.com',
                          current_email: 'current@example.com' });
      }

      return response(400, {error: 'invalid login'});
    });

    this.post('/users/action/send_activation_email', success);

    this.get('/users/hp.json', function() {
      return response({"value":"32faff1b1ef1ac3","challenge":"61a3de0ccf086fb9604b76e884d75801"});
    });

    this.get('/session/csrf', function() {
      return response({"csrf":"mgk906YLagHo2gOgM1ddYjAN4hQolBdJCqlY6jYzAYs="});
    });

    this.get('/users/check_username', function(request) {
      if (request.queryParams.username === 'taken') {
        return response({available: false, suggestion: 'nottaken'});
      }
      return response({available: true});
    });

    this.post('/users', () => response({success: true}));

    this.get('/login.html', () => [200, {}, 'LOGIN PAGE']);

    this.delete('/posts/:post_id', success);
    this.put('/posts/:post_id/recover', success);
    this.get('/posts/:post_id/expand-embed', success);

    this.put('/posts/:post_id', request => {
      const data = parsePostData(request.requestBody);
      data.post.id = request.params.post_id;
      data.post.version = 2;
      return response(200, data.post);
    });

    this.get('/t/403.json', () => response(403, {}));
    this.get('/t/404.json', () => response(404, "not found"));
    this.get('/t/500.json', () => response(502, {}));

    this.put('/t/:slug/:id', request => {
      const data = parsePostData(request.requestBody);

      return response(200, { basic_topic: {id: request.params.id,
                                           title: data.title,
                                           fancy_title: data.title,
                                           slug: request.params.slug } });
    });

    this.get("groups", () => {
      return response(200, fixturesByUrl['/groups.json']);
    });

    this.get("/groups/discourse/topics.json", () => {
      return response(200, fixturesByUrl['/groups/discourse/posts.json']);
    });

    this.get("/groups/discourse/mentions.json", () => {
      return response(200, fixturesByUrl['/groups/discourse/posts.json']);
    });

    this.get("/groups/discourse/messages.json", () => {
      return response(200, fixturesByUrl['/groups/discourse/posts.json']);
    });

    this.get('/t/:topic_id/posts.json', request => {
      const postIds = request.queryParams.post_ids;
      const posts = postIds.map(p => ({id: parseInt(p), post_number: parseInt(p) }));
      return response(200, { post_stream: { posts } });
    });

    this.get('/posts/:post_id/reply-history.json', () => {
      return response(200, [ { id: 2222, post_number: 2222 } ]);
    });

    this.post('/user_badges', () => response(200, fixturesByUrl['/user_badges']));
    this.delete('/user_badges/:badge_id', success);

    this.post('/posts', function(request) {
      const data = parsePostData(request.requestBody);

      if (data.title === "this title triggers an error") {
        return response(422, {errors: ['That title has already been taken']});
      }

      if (data.raw === "enqueue this content please") {
        return response(200, { success: true, action: 'enqueued' });
      }

      return response(200, {
        success: true,
        action: 'create_post',
        post: {id: 12345, topic_id: 280, topic_slug: 'internationalization-localization'}
      });
    });

    this.post('/topics/timings', () => response(200, {}));

    const siteText = {id: 'site.test', value: 'Test McTest'};
    const overridden = {id: 'site.overridden', value: 'Overridden', overridden: true };

    this.get('/admin/users/list/active.json', () => {
      return response(200, [
        {id: 1, username: 'eviltrout', email: 'eviltrout@example.com'}
      ]);
    });

    this.get('/admin/customize/site_texts', request => {

      if (request.queryParams.overridden) {
        return response(200, {site_texts: [overridden] });
      } else {
        return response(200, {site_texts: [siteText, overridden] });
      }
    });

    this.get('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText }));
    this.delete('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText }));

    this.put('/admin/customize/site_texts/:key', request => {
      const result = parsePostData(request.requestBody);
      result.id = request.params.key;
      result.can_revert = true;
      return response(200, {site_text: result});
    });

    this.get('/tag_groups', () => response(200, {tag_groups: []}));
    this.post('/admin/users/:user_id/generate_api_key', success);
    this.delete('/admin/users/:user_id/revoke_api_key', success);
    this.post('/admin/badges', success);
    this.delete('/admin/badges/:id', success);

    this.get('/onebox', request => {
      if (request.queryParams.url === 'http://www.example.com/has-title.html') {
        return [
          200,
          {"Content-Type": "application/html"},
          '

An interesting article

'
        ];
      }

      if (request.queryParams.url === 'http://www.example.com/no-title.html') {
        return [
          200,
          {"Content-Type": "application/html"},
          '

No title

'
        ];
      }

      return [404, {"Content-Type": "application/html"}, ''];;
    });
  });

  server.prepareBody = function(body){
    if (body && typeof body === "object") {
      return JSON.stringify(body);
    }
    return body;
  };

  server.unhandledRequest = function(verb, path) {
    const error = 'Unhandled request in test environment: ' + path + ' (' + verb + ')';
    window.console.error(error);
    throw error;
  };

  server.checkPassthrough = request => request.requestHeaders['Discourse-Script'];
  return server;
}
<commit_msg>Fix JS tests.<commit_after>import storePretender from 'helpers/store-pretender';
import fixturePretender from 'helpers/fixture-pretender';

function parsePostData(query) {
  const result = {};
  query.split("&").forEach(function(part) {
    const item = part.split("=");
    const firstSeg = decodeURIComponent(item[0]);
    const m = /^([^\[]+)\[([^\]]+)\]/.exec(firstSeg);

    const val = decodeURIComponent(item[1]).replace(/\+/g, ' ');
    if (m) {
      result[m[1]] = result[m[1]] || {};
      result[m[1]][m[2]] = val;
    } else {
      result[firstSeg] = val;
    }

  });
  return result;
}

function response(code, obj) {
  if (typeof code === "object") {
    obj = code;
    code = 200;
  }
  return [code, {"Content-Type": "application/json"}, obj];
}

const success = () => response({ success: true });
const loggedIn = () => !!Discourse.User.current();

const helpers = { response, success, parsePostData };

export default function() {

  const server = new Pretender(function() {
    storePretender.call(this, helpers);
    const fixturesByUrl = fixturePretender.call(this, helpers);

    this.get('/admin/plugins', () => response({ plugins: [] }));

    this.get('/composer_messages', () => response({ composer_messages: [] }));

    this.get("/latest.json", () => {
      const json = fixturesByUrl['/latest.json'];

      if (loggedIn()) {
        // Stuff to let us post
        json.topic_list.can_create_topic = true;
        json.topic_list.draft_key = "new_topic";
        json.topic_list.draft_sequence = 1;
      }
      return response(json);
    });

    this.get('/tags', () => {
      return response({ tags: [{
        id: 'eviltrout',
        count: 1
      }] });
    });

    this.get(`/users/eviltrout/emails.json`, () => {
      return response({ email: 'eviltrout@example.com' });
    });

    this.get('/users/eviltrout.json', () => {
      const json = fixturesByUrl['/users/eviltrout.json'];
      json.user.can_edit = loggedIn();
      return response(json);
    });

    this.get('/users/eviltrout/summary.json', () => {
      return response({
        user_summary: {
          topics: [],
          topic_ids: [],
          replies: [],
          links: []
        },
        topics: [],
      });
    });

    this.get('/users/eviltrout/invited_count.json', () => {
      return response({
        "counts": { "pending": 1, "redeemed": 0, "total": 0 }
      });
    });

    this.get('/users/eviltrout/invited.json', () => {
      return response({ "invites": [ {id: 1} ] });
    });

    this.get('/topics/private-messages/eviltrout.json', () => {
      return response({ topic_list: { topics: [] } });
    });

    this.get('/clicks/track', success);

    this.get('/search', request => {
      if (request.queryParams.q === 'posts') {
        return response({
          posts: [{
            id: 1234
          }]
        });
      }

      return response({});
    });

    this.put('/users/eviltrout.json', () => response({ user: {} }));

    this.get("/t/280.json", () => response(fixturesByUrl['/t/280/1.json']));
    this.get("/t/28830.json", () => response(fixturesByUrl['/t/28830/1.json']));
    this.get("/t/9.json", () => response(fixturesByUrl['/t/9/1.json']));

    this.get("/t/id_for/:slug", () => {
      return response({id: 280, slug: "internationalization-localization", url: "/t/internationalization-localization/280"});
    });

    this.delete('/t/:id', success);
    this.put('/t/:id/recover', success);

    this.get("/404-body", () => {
      return [200, {"Content-Type": "text/html"}, "
not found
"];
    });

    this.delete('/draft.json', success);
    this.post('/draft.json', success);

    this.get('/users/:username/staff-info.json', () => response({}));

    this.get('/post_action_users', () => {
      return response({
        post_action_users: [
           {id: 1, username: 'eviltrout', avatar_template: '/user_avatar/default/eviltrout/{size}/1.png', username_lower: 'eviltrout' }
         ]
      });
    });

    this.get('/post_replies', () => {
      return response({ post_replies: [{ id: 1234, cooked: 'wat' }] });
    });

    this.get('/post_reply_histories', () => {
      return response({ post_reply_histories: [{ id: 1234, cooked: 'wat' }] });
    });

    this.get('/category_hashtags/check', () => {
      return response({ valid: [{ slug: "bug", url: '/c/bugs' }] });
    });

    this.get("/categories_and_latest", () => response(fixturesByUrl["/categories_and_latest.json"]));

    this.put('/categories/:category_id', request => {

      const category = parsePostData(request.requestBody);

      if (category.email_in === "duplicate@example.com") {
        return response(422, {"errors": ['duplicate email']});
      }

      return response({category});
    });

    this.get('/draft.json', () => response({}));

    this.put('/queued_posts/:queued_post_id', function(request) {
      return response({ queued_post: {id: request.params.queued_post_id } });
    });

    this.get('/queued_posts', function() {
      return response({
        queued_posts: [{id: 1, raw: 'queued post text', can_delete_user: true}]
      });
    });

    this.post('/session', function(request) {
      const data = parsePostData(request.requestBody);

      if (data.password === 'correct') {
        return response({username: 'eviltrout'});
      }

      if (data.password === 'not-activated') {
        return response({ error: "not active",
                          reason: "not_activated",
                          sent_to_email: 'eviltrout@example.com',
                          current_email: 'current@example.com' });
      }

      return response(400, {error: 'invalid login'});
    });

    this.post('/users/action/send_activation_email', success);

    this.get('/users/hp.json', function() {
      return response({"value":"32faff1b1ef1ac3","challenge":"61a3de0ccf086fb9604b76e884d75801"});
    });

    this.get('/session/csrf', function() {
      return response({"csrf":"mgk906YLagHo2gOgM1ddYjAN4hQolBdJCqlY6jYzAYs="});
    });

    this.get('/users/check_username', function(request) {
      if (request.queryParams.username === 'taken') {
        return response({available: false, suggestion: 'nottaken'});
      }
      return response({available: true});
    });

    this.post('/users', () => response({success: true}));

    this.get('/login.html', () => [200, {}, 'LOGIN PAGE']);

    this.delete('/posts/:post_id', success);
    this.put('/posts/:post_id/recover', success);
    this.get('/posts/:post_id/expand-embed', success);

    this.put('/posts/:post_id', request => {
      const data = parsePostData(request.requestBody);
      data.post.id = request.params.post_id;
      data.post.version = 2;
      return response(200, data.post);
    });

    this.get('/t/403.json', () => response(403, {}));
    this.get('/t/404.json', () => response(404, "not found"));
    this.get('/t/500.json', () => response(502, {}));

    this.put('/t/:slug/:id', request => {
      const data = parsePostData(request.requestBody);

      return response(200, { basic_topic: {id: request.params.id,
                                           title: data.title,
                                           fancy_title: data.title,
                                           slug: request.params.slug } });
    });

    this.get("groups", () => {
      return response(200, fixturesByUrl['/groups.json']);
    });

    this.get("/groups/discourse/topics.json", () => {
      return response(200, fixturesByUrl['/groups/discourse/posts.json']);
    });

    this.get("/groups/discourse/mentions.json", () => {
      return response(200, fixturesByUrl['/groups/discourse/posts.json']);
    });

    this.get("/groups/discourse/messages.json", () => {
      return response(200, fixturesByUrl['/groups/discourse/posts.json']);
    });

    this.get('/t/:topic_id/posts.json', request => {
      const postIds = request.queryParams.post_ids;
      const posts = postIds.map(p => ({id: parseInt(p), post_number: parseInt(p) }));
      return response(200, { post_stream: { posts } });
    });

    this.get('/posts/:post_id/reply-history.json', () => {
      return response(200, [ { id: 2222, post_number: 2222 } ]);
    });

    this.post('/user_badges', () => response(200, fixturesByUrl['/user_badges']));
    this.delete('/user_badges/:badge_id', success);

    this.post('/posts', function(request) {
      const data = parsePostData(request.requestBody);

      if (data.title === "this title triggers an error") {
        return response(422, {errors: ['That title has already been taken']});
      }

      if (data.raw === "enqueue this content please") {
        return response(200, { success: true, action: 'enqueued' });
      }

      return response(200, {
        success: true,
        action: 'create_post',
        post: {id: 12345, topic_id: 280, topic_slug: 'internationalization-localization'}
      });
    });

    this.post('/topics/timings', () => response(200, {}));

    const siteText = {id: 'site.test', value: 'Test McTest'};
    const overridden = {id: 'site.overridden', value: 'Overridden', overridden: true };

    this.get('/admin/users/list/active.json', () => {
      return response(200, [
        {id: 1, username: 'eviltrout', email: 'eviltrout@example.com'}
      ]);
    });

    this.get('/admin/customize/site_texts', request => {

      if (request.queryParams.overridden) {
        return response(200, {site_texts: [overridden] });
      } else {
        return response(200, {site_texts: [siteText, overridden] });
      }
    });

    this.get('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText }));
    this.delete('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText }));

    this.put('/admin/customize/site_texts/:key', request => {
      const result = parsePostData(request.requestBody);
      result.id = request.params.key;
      result.can_revert = true;
      return response(200, {site_text: result});
    });

    this.get('/tag_groups', () => response(200, {tag_groups: []}));
    this.post('/admin/users/:user_id/generate_api_key', success);
    this.delete('/admin/users/:user_id/revoke_api_key', success);
    this.post('/admin/badges', success);
    this.delete('/admin/badges/:id', success);

    this.get('/onebox', request => {
      if (request.queryParams.url === 'http://www.example.com/has-title.html') {
        return [
          200,
          {"Content-Type": "application/html"},
          '

An interesting article

'
        ];
      }

      if (request.queryParams.url === 'http://www.example.com/no-title.html') {
        return [
          200,
          {"Content-Type": "application/html"},
          '

No title

'
        ];
      }

      return [404, {"Content-Type": "application/html"}, ''];;
    });
  });

  server.prepareBody = function(body){
    if (body && typeof body === "object") {
      return JSON.stringify(body);
    }
    return body;
  };

  server.unhandledRequest = function(verb, path) {
    const error = 'Unhandled request in test environment: ' + path + ' (' + verb + ')';
    window.console.error(error);
    throw error;
  };

  server.checkPassthrough = request => request.requestHeaders['Discourse-Script'];
  return server;
}
<|endoftext|>


<commit_before>import {Avatar} from './avatar'
import {wrap, toRadians} from '../turns'
import {clamp} from '../scalars'
import {makeInterpoland} from '../interpolands'

export class PieAvatar extends Avatar {
    constructor(state, container) {
        super(container)
        this.x = makeInterpoland(state, 0)
        this.y = makeInterpoland(state, 0)
        this.w = makeInterpoland(state, 0)
        this.h = makeInterpoland(state, 0)
        // Distance of second jaw from first jaw. Positive is clockwise.
        this.breadth = makeInterpoland(state, 0)
        // Distance of first jaw from north. Positive is clockwise.
        this.startAngle = makeInterpoland(state, 0)
        this.innerRadiusRel = makeInterpoland(state, 0)
        this.fillStyle = undefined
        this.strokeStyle = undefined
        this.lineWidth = undefined
    }

    draw(context) {
        if(this.breadth.curr === 0)
            return

        context.save()
        if(this.x.curr || this.y.curr)
            context.translate(this.x.curr, this.y.curr)
        if(this.w.curr !== 1 || this.h.curr !== 1)
            context.scale(this.w.curr, this.h.curr)

        context.beginPath()

        if(this.breadth.curr >= 1) {
            context.arc(0, 0, .5, 0, Math.PI * 2, false)
        }
        else {
            var startAngle = this.startAngle.curr
            var endAngle = startAngle + this.breadth.curr

            startAngle = toRadians(startAngle - .25)
            endAngle = toRadians(endAngle - .25)

            context.arc(0, 0, .5 * this.innerRadiusRel.curr, endAngle, startAngle,
                        this.breadth.curr >= 0)
            context.arc(0, 0, .5, startAngle, endAngle, this.breadth.curr < 0)
        }

        context.closePath()

        if(this.fillStyle) {
            context.fillStyle = this.fillStyle
            context.fill()
        }
        if(this.strokeStyle && this.lineWidth) {
            context.strokeStyle = this.strokeStyle
            context.lineWidth = this.lineWidth
            context.stroke()
        }

        context.restore()
    }

    subremove() {
        this.x.remove()
        this.y.remove()
        this.w.remove()
        this.h.remove()
        this.breadth.remove()
        this.startAngle.remove()
        this.innerRadiusRel.remove()
    }
}
<commit_msg>Fix for pie strip turning into circle when using complete angle<commit_after>import {Avatar} from './avatar'
import {wrap, toRadians} from '../turns'
import {clamp} from '../scalars'
import {makeInterpoland} from '../interpolands'

export class PieAvatar extends Avatar {
    constructor(state, container) {
        super(container)
        this.x = makeInterpoland(state, 0)
        this.y = makeInterpoland(state, 0)
        this.w = makeInterpoland(state, 0)
        this.h = makeInterpoland(state, 0)
        // Distance of second jaw from first jaw. Positive is clockwise.
        this.breadth = makeInterpoland(state, 0)
        // Distance of first jaw from north. Positive is clockwise.
        this.startAngle = makeInterpoland(state, 0)
        this.innerRadiusRel = makeInterpoland(state, 0)
        this.fillStyle = undefined
        this.strokeStyle = undefined
        this.lineWidth = undefined
    }

    draw(context) {
        if(this.breadth.curr === 0)
            return

        context.save()
        if(this.x.curr || this.y.curr)
            context.translate(this.x.curr, this.y.curr)
        if(this.w.curr !== 1 || this.h.curr !== 1)
            context.scale(this.w.curr, this.h.curr)

        context.beginPath()

        if(this.breadth.curr >= 1 && this.innerRadiusRel.curr >= 1) {
            context.arc(0, 0, .5, 0, Math.PI * 2, false)
        }
        else {
            var startAngle = this.startAngle.curr
            var endAngle = startAngle + this.breadth.curr

            startAngle = toRadians(startAngle - .25)
            endAngle = toRadians(endAngle - .25)

            context.arc(0, 0, .5 * this.innerRadiusRel.curr, endAngle, startAngle,
                        this.breadth.curr >= 0)
            context.arc(0, 0, .5, startAngle, endAngle, this.breadth.curr < 0)
        }

        context.closePath()

        if(this.fillStyle) {
            context.fillStyle = this.fillStyle
            context.fill()
        }
        if(this.strokeStyle && this.lineWidth) {
            context.strokeStyle = this.strokeStyle
            context.lineWidth = this.lineWidth
            context.stroke()
        }

        context.restore()
    }

    subremove() {
        this.x.remove()
        this.y.remove()
        this.w.remove()
        this.h.remove()
        this.breadth.remove()
        this.startAngle.remove()
        this.innerRadiusRel.remove()
    }
}
<|endoftext|>


<commit_before>var assert = require('assert');
var common = require('../common');
var formidable = require('../../lib/index');
var http = require('http');

var server = http.createServer(function(req, res) {
  var form = new formidable.IncomingForm();
  form.uploadDir = common.dir.tmp;
  form.on('end', function () {
    throw new Error('Unexpected "end" event');
  });
  form.on('error', function (e) {
    res.writeHead(500);
    res.end(e.message);
  });
  form.parse(req);
});

server.listen(0, function() {
  var body =
    '--foo\r\n' +
    'Content-Disposition: form-data; name="file1"; filename="file1"\r\n' +
    'Content-Type: application/octet-stream\r\n' +
    '\r\nThis is the first file\r\n' +
    '--foo\r\n' +
    'Content-Type: application/octet-stream\r\n' +
    'Content-Disposition: form-data; name="file2"; filename="file2"\r\n' +
    'Content-Transfer-Encoding: unknown\r\n' +
    '\r\nThis is the second file\r\n' +
    '--foo--\r\n';

  var req = http.request({
    method: 'POST',
    port: server.address().port,
    headers: {
      'Content-Length': body.length,
      'Content-Type': 'multipart/form-data; boundary=foo'
    }
  });
  req.on('response', function (res) {
    assert.equal(res.statusCode, 500);
    server.close();
  });
  req.end(body);
});
<commit_msg>adjust test case to work with node 0.9.x<commit_after>var assert = require('assert');
var common = require('../common');
var formidable = require('../../lib/index');
var http = require('http');

var server = http.createServer(function(req, res) {
  var form = new formidable.IncomingForm();
  form.uploadDir = common.dir.tmp;
  form.on('end', function () {
    throw new Error('Unexpected "end" event');
  });
  form.on('error', function (e) {
    res.writeHead(500);
    res.end(e.message);
  });
  form.parse(req);
});

server.listen(0, function() {
  var body =
    '--foo\r\n' +
    'Content-Disposition: form-data; name="file1"; filename="file1"\r\n' +
    'Content-Type: application/octet-stream\r\n' +
    '\r\nThis is the first file\r\n' +
    '--foo\r\n' +
    'Content-Type: application/octet-stream\r\n' +
    'Content-Disposition: form-data; name="file2"; filename="file2"\r\n' +
    'Content-Transfer-Encoding: unknown\r\n' +
    '\r\nThis is the second file\r\n' +
    '--foo--\r\n';

  var req = http.request({
    method: 'POST',
    port: server.address().port,
    headers: {
      'Content-Length': body.length,
      'Content-Type': 'multipart/form-data; boundary=foo'
    }
  });
  req.on('response', function (res) {
    assert.equal(res.statusCode, 500);
    res.on('data', function () {});
    res.on('end', function () {
      server.close();
    });
  });
  req.end(body);
});
<|endoftext|>


<commit_before>/**
 * @class Ds.createjs.GalleryLooper
 * @classdesc:环形作品画廊 基于Ds.gemo.GalleryAnnularLoopManager进行逻辑计算
 * @param {[Object]} opts     [初始化参数]
 * opts.box   [容器 createjs.Container]
 * 显示容器 定位显示对象命名规范 mc0-mc3
 * 消失显示对象命名规范 box.leftOut box.rightOut
 * opts.list  [填充数据显示对象 createjs.DisplayObject]
 * opts.showNum [显示个数 Number]
 * opts.autoInit 是否初始化构建，Boolean
 * opts.filters 是否支持滤镜效果附加，Boolean
 * 某种情况下需要手动控制开始构建初始化，
 * 如：一开始场景需要提前渲染，然后回执行setTimeout对box进行删除所有的内容，这时候如果自动今天构建，那就会发生先添加元素到box内，然后setTimeout对box进行删除所有的内容错误
 *
 * 事件:upData movieEnd
 * @extends
 * @example: 举例
 *
 //webpack 进行 require
 // require('ds/createjs/GalleryLooper');
 var _GalleryLooper = new Ds.createjs.GalleryLooper({
     box:_Panel,//动画显示与参考容器
     list:_ItemList,//选项内容
     showNum:3,//显示项目个数
     autoInit:false,//是否自动构建
     filters:true,//是否动画需要做滤镜支持，滤镜是消耗性能的
 });
 //如果创建参数里面autoInit值为false,没有进行自动构建，可以在实例需要构建初始化时候执行
_GalleryLooper.Init();
 //监听事件
 _GalleryLooper.on('upData', function(e){});
 //上一页
 _GalleryLooper.Previous();
 //下一页
 _GalleryLooper.Next();
 //选择项队列里面的1
 _GalleryLooper.Select(1);
//设置锁  不会拖动
_GalleryLooper.Lock=true;

 * @author: maksim email:maksim.lin@foxmail.com
 * @copyright:  我发起Ds库目的，简化方便工作项目开发。里面代码大部分理念来至曾经flash 前端时代，尽力减小类之间耦合，通过webpack按需request使用。Ds库里代码很多也都来源至或参考网络开源开放代码，所以这个库也开源开放。更多希望团队成员把积累工作中常用的代码，加快自己开发效率。
 * @constructor
 **/
(function (factory) {
    var root = (typeof self == 'object' && self.self == self && self) ||
        (typeof global == 'object' && global.global == global && global);

    if (typeof define === 'function' && define.amd) {
        define(['exports'], function (exports) {
          require('ds/EventDispatcher');
          require('ds/gemo/GalleryAnnularLoopManager');
          module.exports= factory(root, exports);
        });
    } else if (typeof exports !== 'undefined') {
        module.exports=factory(root, exports);
    } else {
         factory(root, {});
    }
}(function (root, modelObj) {
  root.Ds = root.Ds || {};
  root.Ds.createjs=window.Ds.createjs ||{};
  root.Ds.createjs.GalleryLooper = GalleryLooper;

  function GalleryLooper(opts) {
      if(!opts.box){
        console.warn('显示容器不能为空');
        return;
      }
      Ds.Extend(this, new Ds.EventDispatcher());
      var _Self = this;
      var _Box = opts.box;
      var _showNum=opts.showNum!==undefined?opts.showNum:3;
      //是否有滤镜效果
      var _filters=opts.filters!==undefined?opts.filters:false;
      //数据
      var _DataArr = opts.list;
      //触摸事件的dom元素
      var _touchDom=opts.touchDom!==undefined?$(opts.touchDom)[0]:$('body')[0];
      //清空显示容器
      _Box.removeAllChildren();
      //是否锁定
      this.Lock = false;
      //是否运动中
      this.MovieIng = false;
      //触摸
      touch.on(_touchDom, 'swipeleft', function() {
          _Self.Next();
      });
      touch.on(_touchDom, 'swiperight', function() {
          _Self.Previous();
      });
      //Reference参考对象
      //左消失
      var _BeforeOut = _Box.beforeOut;
      //右消失
      var _AfterOut = _Box.afterOut;
      //参考排列
      var _ReferenceArr = [];
      for (var i = 0; i < _showNum; i++) {
          var _rf = _Box['mc' + i];
          _ReferenceArr.push(_rf);
          // console.log(_rf.filters);
      }
      //画廊滚动管理对象
      var _GalleryLoopManager = new Ds.gemo.GalleryAnnularLoopManager(null, _showNum);
      this.GalleryLoopManager = _GalleryLoopManager;
      var _AutoInit=opts.autoInit!==undefined?opts.autoInit:true;
      var _InitBool=false;
      /**
       * 选择索引
       * @type {[type]}
       */
      Object.defineProperty(this, "SelectNum", {
          get: function() {return _GalleryLoopManager.SelectNum;},
      });
      /**
       * 选择的对象
       * @type {[type]}
       */
      Object.defineProperty(this, "SelectObj", {
          get: function() {return _GalleryLoopManager.SelectObj;},
      });

      /**
       * 初始化
       */
      this.Init=function(){
        if(_InitBool)return;
        _InitBool=true;
        //监听事件
        _GalleryLoopManager.on(_GalleryLoopManager.event.UP_DATA, UpData);
        _GalleryLoopManager.InitData(_DataArr);
      };

      /**
       * 上一个
       */
      this.Previous = function() {
          if (this.Lock) return;
          if (this.MovieIng) return;
          _GalleryLoopManager.Previous();
      };
      /**
       * 下一个
       */
      this.Next = function() {
          if (this.Lock) return;
          if (this.MovieIng) return;
          _GalleryLoopManager.Next();
      };
      /**
       * 选择显示对象
       * @param {[Number]} value []
       */
      this.Select = function(value) {
          if (this.Lock) return;
          if (this.MovieIng) return;
          _GalleryLoopManager.Select(value);
      };
      /**
       * [UpData 数据更新]
       * @param {[type]} e [事件对象]
       * {
          type:_Self.event.UP_DATA,				//更新数据
          old:_OldShowObjects,    				//旧数据组
          now:_NowShowObjects,    				//新数据组
          oldNums:_OldNumArr,    					//旧编号数据组
          nowNums:_NowNumArr,    					//新编号数据组
          seletNum:_SelectNum,					//当前选择数字
          seletObj:_SelectObj,					//当前选择对象
          isInit:(_OldSelectNum==-1)?true:false,	//是否初始化数据
          direction:_Direction,					//方向
        }
       */
      function UpData(e) {
          if (_Self.MovieIng) return;
          _Self.MovieIng = true;
          var i,_mc,_rf,_nowArr,_oldArr;
          //判断是否初始化
          if (e.isInit) {
              // console.log('isInit');
              _Box.removeAllChildren();
              _nowArr = e.now;
              for (i = 0; i < _nowArr.length; i++) {
                  _mc = _nowArr[i];
                  _rf = _ReferenceArr[i];
                  _mc.scaleX = _mc.scaleY = _rf.scaleX;
                  _mc.x = _rf.x;
                  _mc.y = _rf.y;
                  //是否更新滤镜
                  if(_filters)UpFilters(_mc,_rf);
                  _Box.addChild(_mc);
              }
              _Self.MovieIng = false;
          }
          else {
              _nowArr = e.now;
              _oldArr = e.old;
              var _time = 0.5;
              var _outMc = e.direction ? _BeforeOut : _AfterOut;
              var _inMc = e.direction ? _AfterOut : _BeforeOut;
              for ( i = 0; i < _oldArr.length; i++) {
                   _mc = _oldArr[i];
                  if (_nowArr.indexOf(_mc) == -1) {
                      JT.to(_mc, _time, {
                          x: _outMc.x,
                          y: _outMc.y,
                          alpha: _outMc.alpha,
                          scaleX: _outMc.scaleX,
                          scaleY: _outMc.scaleY,
                          onEnd: function(temp) {
                              if (temp.parent) temp.parent.removeChild(temp);
                          },
                          onEndParams: [_mc],
                      });
                  }
              }
              for ( i = 0; i < _nowArr.length; i++) {
                  _mc = _nowArr[i];
                  _rf = _ReferenceArr[i];
                  //是否更新滤镜
                  if(_filters)UpFilters(_mc,_rf);
                  if (_oldArr.indexOf(_mc) == -1) {
                      JT.set(_mc, {
                          x: _inMc.x,
                          y: _inMc.y,
                          alpha: _inMc.alpha,
                          scaleX: _inMc.scaleX,
                          scaleY: _inMc.scaleY
                      });
                      _Box.addChild(_mc);
                  }
                  JT.to(_mc, _time, {
                      x: _rf.x,
                      y: _rf.y,
                      alpha: _rf.alpha,
                      scaleX: _rf.scaleX,
                      scaleY: _rf.scaleY,
                      onEnd:MovieEnd,
                      onEndParams: [i],
                  });
              }
          }
          _Self.ds(e);
      }
      //更新滤镜
      function UpFilters(mc,rf){
        if(rf.filters&&rf.filters.length>=1){
          mc.filters=rf.filters;
          if(mc.cacheCanvas){
            mc.updateCache();
          }else{
            var _rect=rf.getBounds();
            mc.cache(_rect.x,_rect.y,_rect.width,_rect.height);
          }
        }else{
          mc.filters=[];
          mc.uncache();
        }
      }
      //动画播放完成
      function MovieEnd(value){
        //动画是否播放完成
        if (value == 1){
          _Self.MovieIng = false;
          _Self.ds('movieEnd');
        }
      }
      if(_AutoInit)_Self.Init();
  }

  return root.Ds.createjs.GalleryLooper;
}));
<commit_msg>滑动方向初始化参数<commit_after>/**
 * @class Ds.createjs.GalleryLooper
 * @classdesc:环形作品画廊 基于Ds.gemo.GalleryAnnularLoopManager进行逻辑计算
 * @param {[Object]} opts     [初始化参数]
 * opts.box   [容器 createjs.Container]
 * 显示容器 定位显示对象命名规范 mc0-mc3
 * 消失显示对象命名规范 box.leftOut box.rightOut
 * opts.list  [填充数据显示对象 createjs.DisplayObject]
 * opts.showNum [显示个数 Number]
 * opts.autoInit 是否初始化构建，Boolean
 * opts.filters 是否支持滤镜效果附加，Boolean
 * opts.touchDom 滑动触发的dom元素，默认是body
 * opts.touchDirection 滑动触发方向，默认true 是左右滑动
 *
 * 某种情况下需要手动控制开始构建初始化，
 * 如：一开始场景需要提前渲染，然后回执行setTimeout对box进行删除所有的内容，这时候如果自动今天构建，那就会发生先添加元素到box内，然后setTimeout对box进行删除所有的内容错误
 *
 * 事件:upData movieEnd
 * @extends
 * @example: 举例
 *
 //webpack 进行 require
 // require('ds/createjs/GalleryLooper');
 var _GalleryLooper = new Ds.createjs.GalleryLooper({

     box:_Panel,//动画显示与参考容器
     list:_ItemList,//选项内容
     showNum:3,//显示项目个数
     autoInit:false,//是否自动构建
     filters:true,//是否动画需要做滤镜支持，滤镜是消耗性能的
 });
 //如果创建参数里面autoInit值为false,没有进行自动构建，可以在实例需要构建初始化时候执行
_GalleryLooper.Init();
 //监听事件
 _GalleryLooper.on('upData', function(e){});
 //上一页
 _GalleryLooper.Previous();
 //下一页
 _GalleryLooper.Next();
 //选择项队列里面的1
 _GalleryLooper.Select(1);
//设置锁  不会拖动
_GalleryLooper.Lock=true;

 * @author: maksim email:maksim.lin@foxmail.com
 * @copyright:  我发起Ds库目的，简化方便工作项目开发。里面代码大部分理念来至曾经flash 前端时代，尽力减小类之间耦合，通过webpack按需request使用。Ds库里代码很多也都来源至或参考网络开源开放代码，所以这个库也开源开放。更多希望团队成员把积累工作中常用的代码，加快自己开发效率。
 * @constructor
 **/
(function (factory) {
    var root = (typeof self == 'object' && self.self == self && self) ||
        (typeof global == 'object' && global.global == global && global);

    if (typeof define === 'function' && define.amd) {
        define(['exports'], function (exports) {
          require('ds/EventDispatcher');
          require('ds/gemo/GalleryAnnularLoopManager');
          module.exports= factory(root, exports);
        });
    } else if (typeof exports !== 'undefined') {
        module.exports=factory(root, exports);
    } else {
         factory(root, {});
    }
}(function (root, modelObj) {
  root.Ds = root.Ds || {};
  root.Ds.createjs=window.Ds.createjs ||{};
  root.Ds.createjs.GalleryLooper = GalleryLooper;

  function GalleryLooper(opts) {
      if(!opts.box){
        console.warn('显示容器不能为空');
        return;
      }
      Ds.Extend(this, new Ds.EventDispatcher());
      var _Self = this;
      var _Box = opts.box;
      var _showNum=opts.showNum!==undefined?opts.showNum:3;
      //是否有滤镜效果
      var _filters=opts.filters!==undefined?opts.filters:false;
      //数据
      var _DataArr = opts.list;
      //触摸事件的dom元素
      var _touchDom=opts.touchDom!==undefined?$(opts.touchDom)[0]:$('body')[0];
      //滑动方向
      var _touchDirection=opts.touchDirection!==undefined?opts.touchDirection:true;
      //清空显示容器
      _Box.removeAllChildren();
      //是否锁定
      this.Lock = false;
      //是否运动中
      this.MovieIng = false;
      //触摸
      touch.on(_touchDom, 'swipeleft', function() {
          if(_touchDirection)_Self.Next();
      });
      touch.on(_touchDom, 'swiperight', function() {
          if(_touchDirection)_Self.Previous();
      });
      touch.on(_touchDom, 'swipedown', function() {
        if(!_touchDirection)_Self.Previous();
      });
      touch.on(_touchDom, 'swipeup', function() {
        if(!_touchDirection)_Self.Next();
      });
      //Reference参考对象
      //左消失
      var _BeforeOut = _Box.beforeOut;
      //右消失
      var _AfterOut = _Box.afterOut;
      //参考排列
      var _ReferenceArr = [];
      for (var i = 0; i < _showNum; i++) {
          var _rf = _Box['mc' + i];
          _ReferenceArr.push(_rf);
          // console.log(_rf.filters);
      }
      //画廊滚动管理对象
      var _GalleryLoopManager = new Ds.gemo.GalleryAnnularLoopManager(null, _showNum);
      this.GalleryLoopManager = _GalleryLoopManager;
      var _AutoInit=opts.autoInit!==undefined?opts.autoInit:true;
      var _InitBool=false;
      /**
       * 选择索引
       * @type {[type]}
       */
      Object.defineProperty(this, "SelectNum", {
          get: function() {return _GalleryLoopManager.SelectNum;},
      });
      /**
       * 选择的对象
       * @type {[type]}
       */
      Object.defineProperty(this, "SelectObj", {
          get: function() {return _GalleryLoopManager.SelectObj;},
      });

      /**
       * 初始化
       */
      this.Init=function(){
        if(_InitBool)return;
        _InitBool=true;
        //监听事件
        _GalleryLoopManager.on(_GalleryLoopManager.event.UP_DATA, UpData);
        _GalleryLoopManager.InitData(_DataArr);
      };

      /**
       * 上一个
       */
      this.Previous = function() {
          if (this.Lock) return;
          if (this.MovieIng) return;
          _GalleryLoopManager.Previous();
      };
      /**
       * 下一个
       */
      this.Next = function() {
          if (this.Lock) return;
          if (this.MovieIng) return;
          _GalleryLoopManager.Next();
      };
      /**
       * 选择显示对象
       * @param {[Number]} value []
       */
      this.Select = function(value) {
          if (this.Lock) return;
          if (this.MovieIng) return;
          _GalleryLoopManager.Select(value);
      };
      /**
       * [UpData 数据更新]
       * @param {[type]} e [事件对象]
       * {
          type:_Self.event.UP_DATA,				//更新数据
          old:_OldShowObjects,    				//旧数据组
          now:_NowShowObjects,    				//新数据组
          oldNums:_OldNumArr,    					//旧编号数据组
          nowNums:_NowNumArr,    					//新编号数据组
          seletNum:_SelectNum,					//当前选择数字
          seletObj:_SelectObj,					//当前选择对象
          isInit:(_OldSelectNum==-1)?true:false,	//是否初始化数据
          direction:_Direction,					//方向
        }
       */
      function UpData(e) {
          if (_Self.MovieIng) return;
          _Self.MovieIng = true;
          var i,_mc,_rf,_nowArr,_oldArr;
          //判断是否初始化
          if (e.isInit) {
              // console.log('isInit');
              _Box.removeAllChildren();
              _nowArr = e.now;
              for (i = 0; i < _nowArr.length; i++) {
                  _mc = _nowArr[i];
                  _rf = _ReferenceArr[i];
                  _mc.scaleX = _mc.scaleY = _rf.scaleX;
                  _mc.x = _rf.x;
                  _mc.y = _rf.y;
                  //是否更新滤镜
                  if(_filters)UpFilters(_mc,_rf);
                  _Box.addChild(_mc);
              }
              _Self.MovieIng = false;
          }
          else {
              _nowArr = e.now;
              _oldArr = e.old;
              var _time = 0.5;
              var _outMc = e.direction ? _BeforeOut : _AfterOut;
              var _inMc = e.direction ? _AfterOut : _BeforeOut;
              for ( i = 0; i < _oldArr.length; i++) {
                   _mc = _oldArr[i];
                  if (_nowArr.indexOf(_mc) == -1) {
                      JT.to(_mc, _time, {
                          x: _outMc.x,
                          y: _outMc.y,
                          alpha: _outMc.alpha,
                          scaleX: _outMc.scaleX,
                          scaleY: _outMc.scaleY,
                          onEnd: function(temp) {
                              if (temp.parent) temp.parent.removeChild(temp);
                          },
                          onEndParams: [_mc],
                      });
                  }
              }
              for ( i = 0; i < _nowArr.length; i++) {
                  _mc = _nowArr[i];
                  _rf = _ReferenceArr[i];
                  //是否更新滤镜
                  if(_filters)UpFilters(_mc,_rf);
                  if (_oldArr.indexOf(_mc) == -1) {
                      JT.set(_mc, {
                          x: _inMc.x,
                          y: _inMc.y,
                          alpha: _inMc.alpha,
                          scaleX: _inMc.scaleX,
                          scaleY: _inMc.scaleY
                      });
                      _Box.addChild(_mc);
                  }
                  JT.to(_mc, _time, {
                      x: _rf.x,
                      y: _rf.y,
                      alpha: _rf.alpha,
                      scaleX: _rf.scaleX,
                      scaleY: _rf.scaleY,
                      onEnd:MovieEnd,
                      onEndParams: [i],
                  });
              }
          }
          _Self.ds(e);
      }
      //更新滤镜
      function UpFilters(mc,rf){
        if(rf.filters&&rf.filters.length>=1){
          mc.filters=rf.filters;
          if(mc.cacheCanvas){
            mc.updateCache();
          }else{
            var _rect=rf.getBounds();
            mc.cache(_rect.x,_rect.y,_rect.width,_rect.height);
          }
        }else{
          mc.filters=[];
          mc.uncache();
        }
      }
      //动画播放完成
      function MovieEnd(value){
        //动画是否播放完成
        if (value == 1){
          _Self.MovieIng = false;
          _Self.ds('movieEnd');
        }
      }
      if(_AutoInit)_Self.Init();
  }

  return root.Ds.createjs.GalleryLooper;
}));
<|endoftext|>


<commit_before>import Immutable from 'seamless-immutable';
import {
  DECK_DELETION_REQUEST,
  DECK_DELETION_REQUEST_FAILURE,
  DECK_DELETION_REQUEST_SUCCESS,
  REQUEST_DECK_LIST,
  REQUEST_DECK_LIST_FAILURE,
  REQUEST_DECK_LIST_SUCCESS,
} from 'actions/deckManagementActions';

const initialState = Immutable({
  listOfDecks: [],
  sentRequestForDecksList: false,
  receivedList: false,
  listErrorMessage: '',
  sentDeletionRequestFor: null,
  deletionErrorMessage: '',
});

function feedReducer(state = initialState, action) {
  switch (action.type) {
    case REQUEST_DECK_LIST:
      return Immutable.merge(state, {
        sentRequestForDecksList: true,
        receivedList: false,
        sendDeletionRequest: false,
      });
    case REQUEST_DECK_LIST_SUCCESS:
      return Immutable.merge(state, {
        sentRequestForDeckList: false,
        receivedList: true,
        listErrorMessage: '',
        listOfDecks: state.listOfDecks.concat(action.payload.listOfDecks),
      });
    case REQUEST_DECK_LIST_FAILURE:
      return Immutable.merge(state, {
        sentRequestForDecksList: false,
        receivedList: false,
        listErrorMessage: action.payload,
      });
    case DECK_DELETION_REQUEST:
      return Immutable.merge(state, {
        sentDeletionRequestFor: action.payload,
      });
    case DECK_DELETION_REQUEST_FAILURE:
      return Immutable.merge(state, {
        sentDeletionRequestFor: null,
        deletionErrorMessage: action.payload,
      });
    case DECK_DELETION_REQUEST_SUCCESS:
      return Immutable.merge(state, {
        listOfDecks: state.listOfDecks
          .concat()
          .filter(deck => deck.deckId !== state.sentDeletionRequestFor),
        sentDeletionRequestFor: null,
        deletionErrorMessage: '',
      });
    default:
      return state;
  }
}

export default feedReducer;
<commit_msg>fixed esport name of deck management reducer<commit_after>import Immutable from 'seamless-immutable';
import {
  DECK_DELETION_REQUEST,
  DECK_DELETION_REQUEST_FAILURE,
  DECK_DELETION_REQUEST_SUCCESS,
  REQUEST_DECK_LIST,
  REQUEST_DECK_LIST_FAILURE,
  REQUEST_DECK_LIST_SUCCESS,
} from 'actions/deckManagementActions';

const initialState = Immutable({
  listOfDecks: [],
  sentRequestForDecksList: false,
  receivedList: false,
  listErrorMessage: '',
  sentDeletionRequestFor: null,
  deletionErrorMessage: '',
});

function feedReducer(state = initialState, action) {
  switch (action.type) {
    case REQUEST_DECK_LIST:
      return Immutable.merge(state, {
        sentRequestForDecksList: true,
        receivedList: false,
        sendDeletionRequest: false,
      });
    case REQUEST_DECK_LIST_SUCCESS:
      return Immutable.merge(state, {
        sentRequestForDeckList: false,
        receivedList: true,
        listErrorMessage: '',
        listOfDecks: state.listOfDecks.concat(action.payload.listOfDecks),
      });
    case REQUEST_DECK_LIST_FAILURE:
      return Immutable.merge(state, {
        sentRequestForDecksList: false,
        receivedList: false,
        listErrorMessage: action.payload,
      });
    case DECK_DELETION_REQUEST:
      return Immutable.merge(state, {
        sentDeletionRequestFor: action.payload,
      });
    case DECK_DELETION_REQUEST_FAILURE:
      return Immutable.merge(state, {
        sentDeletionRequestFor: null,
        deletionErrorMessage: action.payload,
      });
    case DECK_DELETION_REQUEST_SUCCESS:
      return Immutable.merge(state, {
        listOfDecks: state.listOfDecks
          .concat()
          .filter(deck => deck.deckId !== state.sentDeletionRequestFor),
        sentDeletionRequestFor: null,
        deletionErrorMessage: '',
      });
    default:
      return state;
  }
}

export default deckManagementReducer;
<|endoftext|>