<commit_before>/**
 * Package dependencies
 */
const crypto = require('crypto')
const base64url = require('base64url')
const {TextEncoder, TextDecoder} = require('text-encoding')

/**
 * Local dependencies
 */
const KeyAlgorithm = require('../dictionaries/KeyAlgorithm')
const AesKeyAlgorithm = require('../dictionaries/AesKeyAlgorithm')
const Algorithm = require ('../algorithms/Algorithm')
const CryptoKey = require('../keys/CryptoKey')
const JsonWebKey = require('../keys/JsonWebKey')

/**
 * Errors
 */
const {
  DataError,
  OperationError,
  InvalidAccessError,
  KeyFormatNotSupportedError,
  CurrentlyNotSupportedError
} = require('../errors')

/**
 * AES-GCM
 */
class AES_GCM extends Algorithm {

    /**
     * dictionaries
     */
    static get dictionaries () {
      return [
        KeyAlgorithm,
        AesKeyAlgorithm
      ]
    }

    /**
     * members
     */
    static get members () {
      return {
        name: String,
        modulusLength: Number,
        publicExponent: 'BufferSource'
      }
    }

    /**
     * encrypt
     *
     * @description
     * Encrypts an AES-GCM digital signature
     *
     * @param {AesKeyAlgorithm} algorithm
     * @param {CryptoKey} key
     * @param {BufferSource} data
     *
     * @returns {Array}
     */
    encrypt (algorithm, key, data) {
      // 1. Ensure correct data length
      if (data.length === undefined || data.length > 549755813632) { // 2^39-256
        throw new OperationError('Data must have a length less than 549755813632.')
      }

      // 2. Ensure correct iv length
      if (algorithm.iv.byteLength === undefined || algorithm.iv.byteLength > 18446744073709551615) { // 2^64-1
        throw new OperationError('IV Length must be less than 18446744073709551615 in length.')
      }

      // 3. Ensure correct additionalData
      if (algorithm.additionalData !== undefined
          && (algorithm.additionalData.length === undefined
          || algorithm.additionalData.length > 18446744073709551615)) { // 2^64-1
        throw new OperationError('AdditionalData must be less than 18446744073709551615 in length.')
      }

      // 4. Verify tagLength
      // Note: node only support tag length up to 128, so there is some discrepancy between
      // this, and the spec outline: https://www.w3.org/TR/WebCryptoAPI/#aes-gcm
      let tagLength
      if (algorithm.tagLength === undefined){
        tagLength = 128
      } else if ([32,64,96,104,112,120].includes(algorithm.tagLength)) {
        throw new CurrentlyNotSupportedError('Node currently only supports 128 tagLength.', '128')
      } else if (algorithm.tagLength !== 128) {
        throw new OperationError('TagLength is an invalid size.')
      } else {
        tagLength = algorithm.tagLength
      }

      // 5. Assign additionalData
      let additionalData
      if (algorithm.additionalData !== undefined){
        additionalData = Buffer.from(algorithm.additionalData)
      } else {
        additionalData = Buffer.from('')
      }

      // 6. Do the encryption
      let cipherName
      if (key.algorithm.name === 'AES-GCM' && [128,192,256].includes(key.algorithm.length)){
        cipherName = 'aes-' + key.algorithm.length + '-gcm'
      } else {
        throw new DataError('Invalid AES-GCM and length pair.')
      }
      let cipher = crypto.createCipheriv(cipherName,key.handle,Buffer.from(algorithm.iv))
      cipher.setAAD(additionalData)
      let ciphertext = cipher.update(Buffer.from(data))
      ciphertext = Buffer.concat([ciphertext,cipher.final()])

      // 7. Concat C and T
      let authTag = cipher.getAuthTag()
      ciphertext = Buffer.concat([ciphertext,authTag])

      // 8. Return result
      return Uint8Array.from(ciphertext).buffer
    }

    /**
     * decrypt
     *
     * @description
     * Decrypts an AES-GCM digital signature
     *
     * @param {AesKeyAlgorithm} algorithm
     * @param {CryptoKey} key
     * @param {BufferSource} data
     *
     * @returns {Array}
     */
    decrypt (algorithm, key, data) {
      // 1. Verify tagLength
      let tagLength
      if (algorithm.tagLength === undefined){
        tagLength = 128
      } else if ([32,64,96,104,112,120].includes(algorithm.tagLength)) {
        throw new CurrentlyNotSupportedError('Node currently only supports 128 tagLength.', '128')
      } else if (algorithm.tagLength !== 128) {
        throw new OperationError('TagLength is an invalid size.')
      } else {
        tagLength = algorithm.tagLength
      }

      // 2. Verify data length
      if (algorithm.tagLength === undefined || (data.length * 8) < algorithm.tagLength){
        throw new OperationError('Data length cannot be less than tagLength.')
      }

      // 3. Ensure correct iv length
      if (algorithm.iv.byteLength === undefined || algorithm.iv.byteLength > 18446744073709551615) { // 2^64-1
        throw new OperationError('IV Length must be less than 18446744073709551615 in length.')
      }

      // 4 & 7. Ensure correct additionalData
      let additionalData
      if (algorithm.additionalData !== undefined){
           if (algorithm.additionalData.length === undefined
              || algorithm.additionalData.length > 18446744073709551615) { // 2^64-1
            throw new OperationError('AdditionalData must be less than 18446744073709551615 in length.')
          } else {
            additionalData = Buffer.from(algorithm.additionalData)
          }
      } else{
        additionalData = Buffer.from('')
      }

      // 5. Get the AuthTag
      data = Buffer.from(data)
      let tagLengthBytes = tagLength/8
      let tag = data.slice(-tagLengthBytes)

      // 6. Get the actualCiphertext
      let actualCiphertext = data.slice(0,-tagLengthBytes)

      // 8. Perform the decryption
      let cipherName
      if (key.algorithm.name === 'AES-GCM' && [128,192,256].includes(key.algorithm.length)){
        cipherName = 'aes-' + key.algorithm.length + '-gcm'
      } else {
        throw new DataError('Invalid AES-GCM and length pair.')
      }
      let decipher = crypto.createDecipheriv(cipherName,key.handle,Buffer.from(algorithm.iv))
      decipher.setAAD(additionalData)
      decipher.setAuthTag(tag)
      let plaintext = decipher.update(Buffer.from(actualCiphertext))
      plaintext = Buffer.concat([plaintext,decipher.final()])

      // 9. Return resulting ArrayBuffer
      return Uint8Array.from(plaintext).buffer
    }

    /**
     * generateKey
     *
     * @description
     * Generate an AES-GCM key pair
     *
     * @param {AesGcmParams} params
     * @returns {CryptoKeyPair}
     */
    generateKey (params, extractable, usages) {
      // 1. Validate usages
      usages.forEach(usage => {
        if (usage !== 'encrypt' && usage !== 'decrypt' && usage !== 'wrapKey' && usage !== 'unwrapKey') {
          throw new SyntaxError('Key usages can only include "encrypt", "decrypt", "wrapKey" or "unwrapKey"')
        }
      })

      // 2. Validate length
      if (![128,192,256].includes(params.length)) {
          throw new OperationError('Member length must be 128, 192, or 256.')
      }

      // 3. Generate AES Key
      let symmetricKey
      try {
        symmetricKey = crypto.randomBytes(params.length/8)
      // 4. Validate key generation
      } catch (error) {
        throw new OperationError(error.message)
      }

      // 6. Set new AesKeyAlgorithm
      let algorithm = new AES_GCM(params)

      // 5. Define new CryptoKey names key
      let key = new CryptoKey({
        type: 'secret',
        algorithm,
        extractable,
        usages,
        handle: symmetricKey
      })

      // 12. Return Key
      return key
  }

    /**
     * importKey
     *
     * @description
     *
     * @param {string} format
     * @param {string|JsonWebKey} keyData
     * @param {KeyAlgorithm} algorithm
     * @param {Boolean} extractable
     * @param {Array} keyUsages
     *
     * @returns {CryptoKey}
     */
    importKey (format, keyData, algorithm, extractable, keyUsages) {
      let data, jwk

      // 1. Validate keyUsages
      keyUsages.forEach(usage => {
        if (usage !== 'encrypt'
         && usage !== 'decrypt'
         && usage !== 'wrapKey'
         && usage !== 'unwrapKey') {
           throw new SyntaxError('Key usages can only include "encrypt", "decrypt", "wrapKey" or "unwrapKey"')
        }
      })

      // 2.1 "raw" format
      if (format === 'raw'){
          // 2.1.1 Let data be the octet string contained in keyData
          data = Buffer.from(keyData)

          // 2.1.2 Ensure data length is 128, 192 or 256
          if (![16,24,32].includes(data.length)){
            throw new DataError('Length of data bits must be 128, 192 or 256.')
          }
      }

      // 2.2 "jwk" format
      else if (format === 'jwk'){
        // 2.2.1 Ensure data is JsonWebKey dictionary
        if (typeof keyData === 'object' && !Array.isArray(keyData)){
          jwk = new JsonWebKey(keyData)
        } else {
          throw new DataError('Invalid jwk format')
        }

        // 2.2.2 Validate "kty" field heuristic
        if (jwk.kty !== "oct"){
          throw new DataError('kty property must be "oct"')
        }

        // 2.2.3 Ensure jwk meets these requirements:
        // https://tools.ietf.org/html/rfc7518#section-6.4
        if (!jwk.k){
          throw new DataError('k property must not be empty')
        }

        // 2.2.4 Assign data
        data = base64url.toBuffer(jwk.k)

        // 2.2.5 Validate data lengths
        if (data.length === 16) {
          if (jwk.alg && jwk.alg !== 'A128GCM'){
            throw new DataError('Algorithm "A128GCM" must be 128 bits in length')
          }
        } else if (data.length === 24) {
          if (jwk.alg && jwk.alg !== 'A192GCM'){
            throw new DataError('Algorithm "A192GCM" must be 192 bits in length')
          }
        } else if (data.length === 32) {
          if (jwk.alg && jwk.alg !== 'A256GCM'){
            throw new DataError('Algorithm "A256GCM" must be 256 bits in length')
          }
        } else {
          throw new DataError('Algorithm and data length mismatch')
        }

        // 2.2.6 Validate "use" field
        if (keyUsages && jwk.use && jwk.use !== 'enc'){
          throw new DataError('Key use must be "enc"')
        }

        // 2.2.7 Validate "key_ops" field
        if (jwk.key_ops){
          jwk.key_ops.forEach(op => {
            if (op !== 'encrypt'
             && op !== 'decrypt'
             && op !== 'wrapKey'
             && op !== 'unwrapKey') {
              throw new DataError('Key operation can only include "encrypt", "decrypt", "wrapKey" or "unwrapKey"')
            }
          })
        }

        // 2.2.8 validate "ext" field
        if (jwk.ext === false && extractable === true){
          throw new DataError('Cannot be extractable when "ext" is set to false')
        }
      }

      // 2.3 Otherwise...
      else {
        throw new KeyFormatNotSupportedError(format)
      }

      // 3. Generate new key
      let key = new CryptoKey({
            type: 'secret',
            extractable,
            usages: keyUsages,
            handle: data
        })

      // 4-6. Generate algorithm
      let aesAlgorithm = new AES_GCM(
        { name: 'AES-GCM',
          length: data.length * 8
        })

      // 7. Set algorithm to internal algorithm property of key
      key.algorithm = aesAlgorithm

      // 8. Return key
      return key
  }

  /**
   * exportKey
   *
   * @description
   *
   * @param {string} format
   * @param {CryptoKey} key
   *
   * @returns {*}
   */
  exportKey (format, key) {
      let result, data

      // 1. Validate handle slot
      if (!key.handle) {
        throw new OperationError('Missing key material')
      }

      // 2.1 "raw" format
      if (format === 'raw'){
          // 2.1.1 Let data be the raw octets of the key
          data = key.handle
          // 2.1.2 Let result be containing data
          result = Buffer.from(data)
      }

      // 2.2 "jwk" format
      else if (format === 'jwk'){
        // 2.2.1 Validate JsonWebKey
        let jwk = new JsonWebKey()

        // 2.2.2 Set kty property
        jwk.kty = 'oct'

        // 2.2.3 Set k property
        jwk.k = base64url(key.handle)
        data = key.handle

        // 2.2.4 Validate length
        if (data.length === 16) {
            jwk.alg = 'A128GCM'
        } else if (data.length === 24) {
            jwk.alg = 'A192GCM'
        } else if (data.length === 32) {
            jwk.alg = 'A256GCM'
        }
        // 2.2.5 Set keyops property
        jwk.key_ops = key.usages

        // 2.2.6 Set ext property
        jwk.ext = key.extractable

        // 2.2.7 Set result to the result of converting jwk to an ECMAScript object
        result = jwk
      }

      // 2.3 Otherwise...
      else {
        throw new KeyFormatNotSupportedError(format)
      }

      // 3. Return result
      return result
  }
}

/**
 * Export
 */
module.exports = AES_GCM<commit_msg>fix(aes-gcm): removed tagLength mismatch<commit_after>/**
 * Package dependencies
 */
const crypto = require('crypto')
const base64url = require('base64url')
const {TextEncoder, TextDecoder} = require('text-encoding')

/**
 * Local dependencies
 */
const KeyAlgorithm = require('../dictionaries/KeyAlgorithm')
const AesKeyAlgorithm = require('../dictionaries/AesKeyAlgorithm')
const Algorithm = require ('../algorithms/Algorithm')
const CryptoKey = require('../keys/CryptoKey')
const JsonWebKey = require('../keys/JsonWebKey')

/**
 * Errors
 */
const {
  DataError,
  OperationError,
  InvalidAccessError,
  KeyFormatNotSupportedError,
  CurrentlyNotSupportedError
} = require('../errors')

/**
 * AES-GCM
 */
class AES_GCM extends Algorithm {

    /**
     * dictionaries
     */
    static get dictionaries () {
      return [
        KeyAlgorithm,
        AesKeyAlgorithm
      ]
    }

    /**
     * members
     */
    static get members () {
      return {
        name: String,
        modulusLength: Number,
        publicExponent: 'BufferSource'
      }
    }

    /**
     * encrypt
     *
     * @description
     * Encrypts an AES-GCM digital signature
     *
     * @param {AesKeyAlgorithm} algorithm
     * @param {CryptoKey} key
     * @param {BufferSource} data
     *
     * @returns {Array}
     */
    encrypt (algorithm, key, data) {
      // 1. Ensure correct data length
      if (data.length === undefined || data.length > 549755813632) { // 2^39-256
        throw new OperationError('Data must have a length less than 549755813632.')
      }

      // 2. Ensure correct iv length
      if (algorithm.iv.byteLength === undefined || algorithm.iv.byteLength > 18446744073709551615) { // 2^64-1
        throw new OperationError('IV Length must be less than 18446744073709551615 in length.')
      }

      // 3. Ensure correct additionalData
      if (algorithm.additionalData !== undefined
          && (algorithm.additionalData.length === undefined
          || algorithm.additionalData.length > 18446744073709551615)) { // 2^64-1
        throw new OperationError('AdditionalData must be less than 18446744073709551615 in length.')
      }

      // 4. Verify tagLength
      // Note: node only support tag length up to 128, so there is some discrepancy between
      // this, and the spec outline: https://www.w3.org/TR/WebCryptoAPI/#aes-gcm
      let tagLength
      if (algorithm.tagLength === undefined){
        tagLength = 128
      } else if ([32,64,96,104,112,120].includes(algorithm.tagLength)) {
        throw new CurrentlyNotSupportedError('Node currently only supports 128 tagLength.', '128')
      } else if (algorithm.tagLength !== 128) {
        throw new OperationError('TagLength is an invalid size.')
      } else {
        tagLength = algorithm.tagLength
      }

      // 5. Assign additionalData
      let additionalData
      if (algorithm.additionalData !== undefined){
        additionalData = Buffer.from(algorithm.additionalData)
      } else {
        additionalData = Buffer.from('')
      }

      // 6. Do the encryption
      let cipherName
      if (key.algorithm.name === 'AES-GCM' && [128,192,256].includes(key.algorithm.length)){
        cipherName = 'aes-' + key.algorithm.length + '-gcm'
      } else {
        throw new DataError('Invalid AES-GCM and length pair.')
      }
      let cipher = crypto.createCipheriv(cipherName,key.handle,Buffer.from(algorithm.iv))
      cipher.setAAD(additionalData)
      let ciphertext = cipher.update(Buffer.from(data))
      ciphertext = Buffer.concat([ciphertext,cipher.final()])

      // 7. Concat C and T
      let authTag = cipher.getAuthTag()
      ciphertext = Buffer.concat([ciphertext,authTag])

      // 8. Return result
      return Uint8Array.from(ciphertext).buffer
    }

    /**
     * decrypt
     *
     * @description
     * Decrypts an AES-GCM digital signature
     *
     * @param {AesKeyAlgorithm} algorithm
     * @param {CryptoKey} key
     * @param {BufferSource} data
     *
     * @returns {Array}
     */
    decrypt (algorithm, key, data) {
      // 1. Verify tagLength
      let tagLength
      if (algorithm.tagLength === undefined){
        tagLength = 128
      } else if ([32,64,96,104,112,120].includes(algorithm.tagLength)) {
        throw new CurrentlyNotSupportedError('Node currently only supports 128 tagLength.', '128')
      } else if (algorithm.tagLength !== 128) {
        throw new OperationError('TagLength is an invalid size.')
      } else {
        tagLength = algorithm.tagLength
      }

      // 2. Verify data length
      if ((data.length * 8) < tagLength){
        throw new OperationError('Data length cannot be less than tagLength.')
      }

      // 3. Ensure correct iv length
      if (algorithm.iv.byteLength === undefined || algorithm.iv.byteLength > 18446744073709551615) { // 2^64-1
        throw new OperationError('IV Length must be less than 18446744073709551615 in length.')
      }

      // 4 & 7. Ensure correct additionalData
      let additionalData
      if (algorithm.additionalData !== undefined){
           if (algorithm.additionalData.length === undefined
              || algorithm.additionalData.length > 18446744073709551615) { // 2^64-1
            throw new OperationError('AdditionalData must be less than 18446744073709551615 in length.')
          } else {
            additionalData = Buffer.from(algorithm.additionalData)
          }
      } else{
        additionalData = Buffer.from('')
      }

      // 5. Get the AuthTag
      data = Buffer.from(data)
      let tagLengthBytes = tagLength/8
      let tag = data.slice(-tagLengthBytes)

      // 6. Get the actualCiphertext
      let actualCiphertext = data.slice(0,-tagLengthBytes)

      // 8. Perform the decryption
      let cipherName
      if (key.algorithm.name === 'AES-GCM' && [128,192,256].includes(key.algorithm.length)){
        cipherName = 'aes-' + key.algorithm.length + '-gcm'
      } else {
        throw new DataError('Invalid AES-GCM and length pair.')
      }
      let decipher = crypto.createDecipheriv(cipherName,key.handle,Buffer.from(algorithm.iv))
      decipher.setAAD(additionalData)
      decipher.setAuthTag(tag)
      let plaintext = decipher.update(Buffer.from(actualCiphertext))
      plaintext = Buffer.concat([plaintext,decipher.final()])

      // 9. Return resulting ArrayBuffer
      return Uint8Array.from(plaintext).buffer
    }

    /**
     * generateKey
     *
     * @description
     * Generate an AES-GCM key pair
     *
     * @param {AesGcmParams} params
     * @returns {CryptoKeyPair}
     */
    generateKey (params, extractable, usages) {
      // 1. Validate usages
      usages.forEach(usage => {
        if (usage !== 'encrypt' && usage !== 'decrypt' && usage !== 'wrapKey' && usage !== 'unwrapKey') {
          throw new SyntaxError('Key usages can only include "encrypt", "decrypt", "wrapKey" or "unwrapKey"')
        }
      })

      // 2. Validate length
      if (![128,192,256].includes(params.length)) {
          throw new OperationError('Member length must be 128, 192, or 256.')
      }

      // 3. Generate AES Key
      let symmetricKey
      try {
        symmetricKey = crypto.randomBytes(params.length/8)
      // 4. Validate key generation
      } catch (error) {
        throw new OperationError(error.message)
      }

      // 6. Set new AesKeyAlgorithm
      let algorithm = new AES_GCM(params)

      // 5. Define new CryptoKey names key
      let key = new CryptoKey({
        type: 'secret',
        algorithm,
        extractable,
        usages,
        handle: symmetricKey
      })

      // 12. Return Key
      return key
  }

    /**
     * importKey
     *
     * @description
     *
     * @param {string} format
     * @param {string|JsonWebKey} keyData
     * @param {KeyAlgorithm} algorithm
     * @param {Boolean} extractable
     * @param {Array} keyUsages
     *
     * @returns {CryptoKey}
     */
    importKey (format, keyData, algorithm, extractable, keyUsages) {
      let data, jwk

      // 1. Validate keyUsages
      keyUsages.forEach(usage => {
        if (usage !== 'encrypt'
         && usage !== 'decrypt'
         && usage !== 'wrapKey'
         && usage !== 'unwrapKey') {
           throw new SyntaxError('Key usages can only include "encrypt", "decrypt", "wrapKey" or "unwrapKey"')
        }
      })

      // 2.1 "raw" format
      if (format === 'raw'){
          // 2.1.1 Let data be the octet string contained in keyData
          data = Buffer.from(keyData)

          // 2.1.2 Ensure data length is 128, 192 or 256
          if (![16,24,32].includes(data.length)){
            throw new DataError('Length of data bits must be 128, 192 or 256.')
          }
      }

      // 2.2 "jwk" format
      else if (format === 'jwk'){
        // 2.2.1 Ensure data is JsonWebKey dictionary
        if (typeof keyData === 'object' && !Array.isArray(keyData)){
          jwk = new JsonWebKey(keyData)
        } else {
          throw new DataError('Invalid jwk format')
        }

        // 2.2.2 Validate "kty" field heuristic
        if (jwk.kty !== "oct"){
          throw new DataError('kty property must be "oct"')
        }

        // 2.2.3 Ensure jwk meets these requirements:
        // https://tools.ietf.org/html/rfc7518#section-6.4
        if (!jwk.k){
          throw new DataError('k property must not be empty')
        }

        // 2.2.4 Assign data
        data = base64url.toBuffer(jwk.k)

        // 2.2.5 Validate data lengths
        if (data.length === 16) {
          if (jwk.alg && jwk.alg !== 'A128GCM'){
            throw new DataError('Algorithm "A128GCM" must be 128 bits in length')
          }
        } else if (data.length === 24) {
          if (jwk.alg && jwk.alg !== 'A192GCM'){
            throw new DataError('Algorithm "A192GCM" must be 192 bits in length')
          }
        } else if (data.length === 32) {
          if (jwk.alg && jwk.alg !== 'A256GCM'){
            throw new DataError('Algorithm "A256GCM" must be 256 bits in length')
          }
        } else {
          throw new DataError('Algorithm and data length mismatch')
        }

        // 2.2.6 Validate "use" field
        if (keyUsages && jwk.use && jwk.use !== 'enc'){
          throw new DataError('Key use must be "enc"')
        }

        // 2.2.7 Validate "key_ops" field
        if (jwk.key_ops){
          jwk.key_ops.forEach(op => {
            if (op !== 'encrypt'
             && op !== 'decrypt'
             && op !== 'wrapKey'
             && op !== 'unwrapKey') {
              throw new DataError('Key operation can only include "encrypt", "decrypt", "wrapKey" or "unwrapKey"')
            }
          })
        }

        // 2.2.8 validate "ext" field
        if (jwk.ext === false && extractable === true){
          throw new DataError('Cannot be extractable when "ext" is set to false')
        }
      }

      // 2.3 Otherwise...
      else {
        throw new KeyFormatNotSupportedError(format)
      }

      // 3. Generate new key
      let key = new CryptoKey({
            type: 'secret',
            extractable,
            usages: keyUsages,
            handle: data
        })

      // 4-6. Generate algorithm
      let aesAlgorithm = new AES_GCM(
        { name: 'AES-GCM',
          length: data.length * 8
        })

      // 7. Set algorithm to internal algorithm property of key
      key.algorithm = aesAlgorithm

      // 8. Return key
      return key
  }

  /**
   * exportKey
   *
   * @description
   *
   * @param {string} format
   * @param {CryptoKey} key
   *
   * @returns {*}
   */
  exportKey (format, key) {
      let result, data

      // 1. Validate handle slot
      if (!key.handle) {
        throw new OperationError('Missing key material')
      }

      // 2.1 "raw" format
      if (format === 'raw'){
          // 2.1.1 Let data be the raw octets of the key
          data = key.handle
          // 2.1.2 Let result be containing data
          result = Buffer.from(data)
      }

      // 2.2 "jwk" format
      else if (format === 'jwk'){
        // 2.2.1 Validate JsonWebKey
        let jwk = new JsonWebKey()

        // 2.2.2 Set kty property
        jwk.kty = 'oct'

        // 2.2.3 Set k property
        jwk.k = base64url(key.handle)
        data = key.handle

        // 2.2.4 Validate length
        if (data.length === 16) {
            jwk.alg = 'A128GCM'
        } else if (data.length === 24) {
            jwk.alg = 'A192GCM'
        } else if (data.length === 32) {
            jwk.alg = 'A256GCM'
        }
        // 2.2.5 Set keyops property
        jwk.key_ops = key.usages

        // 2.2.6 Set ext property
        jwk.ext = key.extractable

        // 2.2.7 Set result to the result of converting jwk to an ECMAScript object
        result = jwk
      }

      // 2.3 Otherwise...
      else {
        throw new KeyFormatNotSupportedError(format)
      }

      // 3. Return result
      return result
  }
}

/**
 * Export
 */
module.exports = AES_GCM
<|endoftext|>


<commit_before>jQuery(function($) {
    //caches a jQuery object containing the header element
    var scroll = $(window).scrollTop();
    var header = $(".header-position");
    var contentWrap = $(".content-wrap");

    $(document).ready(function() {
      if(header.hasClass("resize-true") && $(window).width() >= 775)
      {
        header.height(Math.max(64, Math.min(256-scroll, 256)));
        if(header.height() <= 65) {
          header.removeClass("normal").addClass("small");
          contentWrap.removeClass("normal").addClass("small");
        } else {
          header.removeClass("small").addClass("normal");
          contentWrap.removeClass("small").addClass("normal");
        }

        if(scroll >= 192)
          header.removeClass("fade-in").addClass("fade-out");
        else
          header.removeClass("fade-out").addClass("fade-in");
      }
      else
      {
        header.height(64);
        header.removeClass("resize-").addClass("resize-false");
        header.removeClass("normal").addClass("small");
        contentWrap.addClass("small");

        if(scroll >= 64)
          header.removeClass("fade-in").addClass("fade-out");
        else
          header.removeClass("fade-out").addClass("fade-in");
      }
    });

    $(window).scroll(function() {
      scroll = $(window).scrollTop();
      if(header.hasClass("resize-true") && $(window).width() >= 775) {
        header.height(Math.max(64, Math.min(256-scroll, 256)));
        if(header.height() <= 65) {
          header.removeClass("normal").addClass("small");
          contentWrap.removeClass("normal").addClass("small");
        } else {
          header.removeClass("small").addClass("normal");
          contentWrap.removeClass("small").addClass("normal");
        }

        if(scroll >= 192)
          header.removeClass("fade-in").addClass("fade-out");
        else
          header.removeClass("fade-out").addClass("fade-in");
      } else {
        if(scroll >= 64)
          header.removeClass("fade-in").addClass("fade-out");
        else
          header.removeClass("fade-out").addClass("fade-in");
      }
    });

  // handle links with @href started with '#' only
  $(document).on('click', 'a[href^="#"]', function(e) {
    // target element id
    var id = $(this).attr('href');

    // target element
    var $id = $(id);
    if ($id.length === 0) {
        return;
    }

    // prevent standard hash navigation (avoid blinking in IE)
    e.preventDefault();

    // top position relative to the document
    var pos = $(id).offset().top - 64;

    // animated top scrolling
    $('body, html').animate({scrollTop: pos});
  });
});
<commit_msg>Fixed scroll bug<commit_after>jQuery(function($) {
    //caches a jQuery object containing the header element
    var scroll = $(window).scrollTop();
    var header = $(".header-position");
    var contentWrap = $(".content-wrap");

    $(document).ready(function() {
      if(header.hasClass("resize-true") && $(window).width() >= 775)
      {
        header.height(Math.max(64, Math.min(256-scroll, 256)));
        if(header.height() <= 65) {
          header.removeClass("normal").addClass("small");
          contentWrap.removeClass("normal").addClass("small");
        } else {
          header.removeClass("small").addClass("normal");
          contentWrap.removeClass("small").addClass("normal");
        }

        if(scroll >= 192)
          header.removeClass("fade-in").addClass("fade-out");
        else
          header.removeClass("fade-out").addClass("fade-in");
      }
      else
      {
        header.height(64);
        header.removeClass("resize-").addClass("resize-false");
        header.removeClass("normal").addClass("small");

        contentWrap.removeClass("resize-").addClass("resize-false");
        contentWrap.addClass("small");

        if(scroll >= 64)
          header.removeClass("fade-in").addClass("fade-out");
        else
          header.removeClass("fade-out").addClass("fade-in");
      }
    });

    $(window).scroll(function() {
      scroll = $(window).scrollTop();
      if(header.hasClass("resize-true") && $(window).width() >= 775) {
        header.height(Math.max(64, Math.min(256-scroll, 256)));
        if(header.height() <= 65) {
          header.removeClass("normal").addClass("small");
          contentWrap.removeClass("normal").addClass("small");
        } else {
          header.removeClass("small").addClass("normal");
          contentWrap.removeClass("small").addClass("normal");
        }

        if(scroll >= 192)
          header.removeClass("fade-in").addClass("fade-out");
        else
          header.removeClass("fade-out").addClass("fade-in");
      } else {
        if(scroll >= 64)
          header.removeClass("fade-in").addClass("fade-out");
        else
          header.removeClass("fade-out").addClass("fade-in");
      }
    });

  // handle links with @href started with '#' only
  $(document).on('click', 'a[href^="#"]', function(e) {
    // target element id
    var id = $(this).attr('href');

    // target element
    var $id = $(id);
    if ($id.length === 0) {
        return;
    }

    // prevent standard hash navigation (avoid blinking in IE)
    e.preventDefault();

    // top position relative to the document
    var pos = $(id).offset().top - 64;

    // animated top scrolling
    $('body, html').animate({scrollTop: pos});
  });
});
<|endoftext|>


<commit_before>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

import $C from '../deps/collection';
import Snakeskin from '../core';
import { ws } from '../helpers/string';
import { symbols, w } from '../consts/regs';
import { LEFT_BLOCK, RIGHT_BLOCK, ADV_LEFT_BLOCK } from '../consts/literals';

const
	callBlockNameRgxp = new RegExp(`^[^${symbols}_$][^${w}$]*|[^${w}$]+`, 'i');

Snakeskin.addDirective(
	'block',

	{
		block: true,
		deferInit: true,
		group: ['template', 'inherit', 'define', 'blockInherit'],
		logic: true,
		notEmpty: true
	},

	function (command, commandLength) {
		let
			{tplName} = this,
			name = this.getFnName(command);

		if (!name) {
			return this.error(`invalid "${this.name}" name`);
		}

		const
			parts = name.split('->');

		if (parts[1]) {
			name = parts[1].trim();

			if (!tplName) {
				if (this.structure.parent) {
					this.error(`the directive "outer block" can be used only within the global space`);
					return;
				}

				try {
					tplName = this.tplName = this.prepareNameDecl(parts[0]);

				} catch (err) {
					return this.error(err.message);
				}

				if (tplName in extMap) {
					delete extMap[tplName];
					clearScopeCache(tplName);
				}

				const desc = this.preDefs[tplName] = this.preDefs[tplName] || {
					text: ''
				};

				desc.startLine = this.info.line;
				desc.i = this.i + 1;

				this.outerLink = name;
			}

		} else if (!this.outerLink && !this.tplName) {
			return this.error(`the directive "${this.name}" can be used only within a ${groupsList['template'].join(', ')}`);
		}

		if (!name || !tplName || callBlockNameRgxp.test(name)) {
			return this.error(`invalid "${this.name}" declaration`);
		}

		const
			scope = scopeCache[this.name][tplName] = scopeCache[this.name][tplName] || {},
			parentTplName = extMap[tplName];

		let
			current = scope[name],
			parentScope;

		if (parentTplName) {
			parentScope =
				scopeCache[this.name][parentTplName] = scopeCache[this.name][parentTplName] || {};
		}

		if (!scope[name]) {
			current = scope[name] = {
				children: {},
				id: this.environment.id
			};
		}

		if (!this.outerLink && !current.root) {
			const
				parent = parentScope && parentScope[name];

			current.parent = parent;
			current.overridden = Boolean(parentTplName && this.parentTplName);
			current.root = parent ? parent.root : scope[name];

			if (parent) {
				parent.children[tplName] = scope[name];
			}
		}

		const
			start = this.i - this.startTemplateI;

		this.startDir(null, {
			from: this.outerLink ? this.i - this.getDiff(commandLength) : start + 1,
			name
		});

		const
			{structure} = this,
			dir = String(this.name);

		let
			params,
			output;

		if (name !== command) {
			const
				outputCache = this.getBlockOutput(dir);

			if (outputCache) {
				output = command.split('=>')[1];
				params = outputCache[name];

				if (output != null) {
					params =
						outputCache[name] = output;
				}
			}
		}

		if (this.isAdvTest()) {
			if (blockCache[tplName][name]) {
				return this.error(`the block "${name}" is already defined`);
			}

			const args = this.prepareArgs(
				command,
				dir,
				{
					fName: name,
					parentTplName: this.parentTplName
				}
			);

			if (args.params && callBlockNameRgxp.test(name)) {
				return this.error(`invalid "${this.name}" declaration`);
			}

			this.structure.params.args = args.params;
			blockCache[tplName][name] = {
				args,
				from: start - this.getDiff(commandLength),
				needPrfx: this.needPrfx,
				output
			};

			if (args.scope) {
				this.scope.push(args.scope);
				structure.params._scope = true;
			}
		}

		if (this.isSimpleOutput()) {
			const
				{args} = blockCache[tplName][name];

			if (args.params) {
				const
					fnDecl = structure.params.fn = `__BLOCKS__.${name}`;

				this.save(ws`
					if (!${fnDecl}) {
						${fnDecl} = function (${args.str}) {
							var __RESULT__ = ${this.getReturnDecl()};

							${this.declArguments()}

							function getTplResult(opt_clear) {
								var res = ${this.getReturnResultDecl()};

								if (opt_clear) {
									__RESULT__ = ${this.getReturnDecl()};
								}

								return res;
							}

							function clearTplResult() {
								__RESULT__ = ${this.getReturnDecl()};
							}

							${args.defParams}
				`);

				if (params != null) {
					const
						{vars} = structure;

					structure.vars = structure.parent.vars;
					params = this.getFnArgs(`(${params})`);

					structure.vars = vars;
					structure.params.params = $C(params)
						.reduce((res, el) => res += `${this.out(el, {unsafe: true})},`, '')
						.slice(0, -1);
				}
			}
		}
	},

	function (command, commandLength) {
		const
			{params} = this.structure,
			diff = this.getDiff(commandLength);

		const
			s = (this.needPrfx ? ADV_LEFT_BLOCK : '') + LEFT_BLOCK,
			e = RIGHT_BLOCK;

		if (this.outerLink === params.name) {
			const
				obj = this.preDefs[this.tplName],
				i = Number(obj.i);

			obj.text += ws`
				${this.eol}${this.source.slice(params.from, i)}
				${s}__cutLine__${e}

					${s}__switchLine__ ${obj.startLine}${e}
						${this.source.slice(i, this.i - diff)}
					${s}__end__${e}

				${this.eol}${this.source.slice(this.i - diff, this.i + 1)}
				${s}__cutLine__${e}
			`;

			this.outerLink = null;
			this.tplName = null;

			return;
		}

		const
			block = blockCache[this.tplName][params.name];

		if (this.isSimpleOutput() && params.fn) {
			this.save(ws`
						return ${this.getReturnResultDecl()};
					};
				}

				${params.params != null ? this.wrap(`${params.fn}(${params.params})`) : ''}
			`);
		}

		if (this.isAdvTest()) {
			if (!block) {
				return this.error('invalid "block" declaration');
			}

			const
				start = this.i - this.startTemplateI;

			block.to = start + 1;
			block.content = this.source.slice(this.startTemplateI).slice(params.from, start - diff);
		}
	}
);
<commit_msg>:wrench:<commit_after>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

import $C from '../deps/collection';
import Snakeskin from '../core';
import { ws } from '../helpers/string';
import { symbols, w } from '../consts/regs';
import { $scope, $extMap, $blocks } from '../consts/cache';
import { LEFT_BLOCK, RIGHT_BLOCK, ADV_LEFT_BLOCK } from '../consts/literals';

const
	callBlockNameRgxp = new RegExp(`^[^${symbols}_$][^${w}$]*|[^${w}$]+`, 'i');

Snakeskin.addDirective(
	'block',

	{
		block: true,
		deferInit: true,
		group: ['template', 'inherit', 'define', 'blockInherit'],
		logic: true,
		notEmpty: true
	},

	function (command, commandLength) {
		let
			{tplName} = this,
			name = this.getFnName(command);

		if (!name) {
			return this.error(`invalid "${this.name}" name`);
		}

		const
			parts = name.split('->');

		if (parts[1]) {
			name = parts[1].trim();

			if (!tplName) {
				if (this.structure.parent) {
					this.error(`the directive "outer block" can be used only within the global space`);
					return;
				}

				try {
					tplName = this.tplName = this.prepareNameDecl(parts[0]);

				} catch (err) {
					return this.error(err.message);
				}

				if (tplName in $extMap) {
					delete $extMap[tplName];
					this.clearScopeCache(tplName);
				}

				const desc = this.preDefs[tplName] = this.preDefs[tplName] || {
					text: ''
				};

				desc.startLine = this.info.line;
				desc.i = this.i + 1;

				this.outerLink = name;
			}

		} else if (!this.outerLink && !this.tplName) {
			return this.error(`the directive "${this.name}" can be used only within a ${groupsList['template'].join(', ')}`);
		}

		if (!name || !tplName || callBlockNameRgxp.test(name)) {
			return this.error(`invalid "${this.name}" declaration`);
		}

		const
			scope = $scope[this.name][tplName] = $scope[this.name][tplName] || {},
			parentTplName = $extMap[tplName];

		let
			current = scope[name],
			parentScope;

		if (parentTplName) {
			parentScope =
				$scope[this.name][parentTplName] = $scope[this.name][parentTplName] || {};
		}

		if (!scope[name]) {
			current = scope[name] = {
				children: {},
				id: this.environment.id
			};
		}

		if (!this.outerLink && !current.root) {
			const
				parent = parentScope && parentScope[name];

			current.parent = parent;
			current.overridden = Boolean(parentTplName && this.parentTplName);
			current.root = parent ? parent.root : scope[name];

			if (parent) {
				parent.children[tplName] = scope[name];
			}
		}

		const
			start = this.i - this.startTemplateI;

		this.startDir(null, {
			from: this.outerLink ? this.i - this.getDiff(commandLength) : start + 1,
			name
		});

		const
			{structure} = this,
			dir = String(this.name);

		let
			params,
			output;

		if (name !== command) {
			const
				outputCache = this.getBlockOutput(dir);

			if (outputCache) {
				output = command.split('=>')[1];
				params = outputCache[name];

				if (output != null) {
					params =
						outputCache[name] = output;
				}
			}
		}

		if (this.isAdvTest()) {
			if ($blocks[tplName][name]) {
				return this.error(`the block "${name}" is already defined`);
			}

			const args = this.prepareArgs(
				command,
				dir,
				{
					fName: name,
					parentTplName: this.parentTplName
				}
			);

			if (args.params && callBlockNameRgxp.test(name)) {
				return this.error(`invalid "${this.name}" declaration`);
			}

			this.structure.params.args = args.params;
			$blocks[tplName][name] = {
				args,
				from: start - this.getDiff(commandLength),
				needPrfx: this.needPrfx,
				output
			};

			if (args.scope) {
				this.scope.push(args.scope);
				structure.params._scope = true;
			}
		}

		if (this.isSimpleOutput()) {
			const
				{args} = $blocks[tplName][name];

			if (args.params) {
				const
					fnDecl = structure.params.fn = `__BLOCKS__.${name}`;

				this.save(ws`
					if (!${fnDecl}) {
						${fnDecl} = function (${args.str}) {
							var __RESULT__ = ${this.getReturnDecl()};

							${this.declArguments()}

							function getTplResult(opt_clear) {
								var res = ${this.getReturnResultDecl()};

								if (opt_clear) {
									__RESULT__ = ${this.getReturnDecl()};
								}

								return res;
							}

							function clearTplResult() {
								__RESULT__ = ${this.getReturnDecl()};
							}

							${args.defParams}
				`);

				if (params != null) {
					const
						{vars} = structure;

					structure.vars = structure.parent.vars;
					params = this.getFnArgs(`(${params})`);

					structure.vars = vars;
					structure.params.params = $C(params)
						.reduce((res, el) => res += `${this.out(el, {unsafe: true})},`, '')
						.slice(0, -1);
				}
			}
		}
	},

	function (command, commandLength) {
		const
			{params} = this.structure,
			diff = this.getDiff(commandLength);

		const
			s = (this.needPrfx ? ADV_LEFT_BLOCK : '') + LEFT_BLOCK,
			e = RIGHT_BLOCK;

		if (this.outerLink === params.name) {
			const
				obj = this.preDefs[this.tplName],
				i = Number(obj.i);

			obj.text += ws`
				${this.eol}${this.source.slice(params.from, i)}
				${s}__cutLine__${e}

					${s}__switchLine__ ${obj.startLine}${e}
						${this.source.slice(i, this.i - diff)}
					${s}__end__${e}

				${this.eol}${this.source.slice(this.i - diff, this.i + 1)}
				${s}__cutLine__${e}
			`;

			this.outerLink = null;
			this.tplName = null;

			return;
		}

		const
			block = $blocks[this.tplName][params.name];

		if (this.isSimpleOutput() && params.fn) {
			this.save(ws`
						return ${this.getReturnResultDecl()};
					};
				}

				${params.params != null ? this.wrap(`${params.fn}(${params.params})`) : ''}
			`);
		}

		if (this.isAdvTest()) {
			if (!block) {
				return this.error('invalid "block" declaration');
			}

			const
				start = this.i - this.startTemplateI;

			block.to = start + 1;
			block.content = this.source.slice(this.startTemplateI).slice(params.from, start - diff);
		}
	}
);
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name angulargm.directive:gmMap
 * @element ANY
 *
 * @description
 * A directive for embedding google maps into your app.
 *
 * `gm-map-id` is required. The `gm-center`, `gm-zoom`, `gm-bounds`, and
 * `gm-map-type-id` variables do not have to exist in the current scope--they
 * will be created if necessary. All three have bi-directional association,
 * i.e.  drag or zoom the map and they will update, update them and the map
 * will change.  However, any initial state of these three variables will be
 * ignored.
 *
 * If you need to get a handle on the google.maps.Map object, see
 * [angulargmContainer]
 *
 * @param {expression} gm-map-id angular expression that evaluates to a unique
 * string id for the map, e.g. `'map_canvas'` or `myMapId` where myMapId is a
 * variable in the current scope. This allows you to have multiple
 * maps/instances of the directive.
 *
 *
 * @param {expression} gm-center center variable in the current scope.  The
 * value will be a google.maps.LatLng object.
 *
 *
 * @param {expression} gm-zoom zoom variable in the current scope.  Value will
 * be an integer.
 *
 *
 * @param {expression} gm-bounds bounds variable in the current scope.  Value
 * will be a google.maps.LatLngBounds object.
 *
 *
 * @param {expression} gm-map-type-id mapTypeId variable in the current scope.
 * Value will be a string.
 *
 *
 * @param {expression} gm-map-options object in the current scope that is a
 * google.maps.MapOptions object. If unspecified, will use the values in
 * angulargmDefaults.mapOptions. [angulargmDefaults] is a service, so it is
 * both injectable and overrideable (using $provide.decorator).
 *
 */

/**
 * @ngdoc event
 * @name angulargm.directive:gmMap#gmMapResize
 * @eventOf angulargm.directive:gmMap
 * @eventType listen on current gmMap scope
 * @param {string} mapId Required. The id of your map.
 * @example
 * ```js
 * $scope.$broadcast('gmMapResize', 'myMapId')
 * ```
 */

(function () {
  angular.module('AngularGM').

  directive('gmMap', ['$timeout', function ($timeout) {

    /** link function **/

    function link(scope, element, attrs, controller) {
      // initialize scope
      if (!angular.isDefined(scope.gmCenter)) {
        scope.center = {};
      }
      if (!angular.isDefined(scope.gmBounds)) {
        scope.bounds = {};
      }

      // Make sure gmMapId is defined
      // Note: redundant check in MapController. Can't hurt.
      if (!angular.isDefined(scope.gmMapId)) {
        throw 'angulargm must have non-empty gmMapId attribute';
      }

      // Check what's defined in attrs
      // Note: this is also redundant since angular will throw an exception if
      // these attributes are not set. I may make these optional in the future
      // (pending angular support).
      var hasCenter = false;
      var hasZoom = false;
      var hasBounds = false;
      var hasMapTypeId = false;

      if (attrs.hasOwnProperty('gmCenter')) {
        hasCenter = true;
      }
      if (attrs.hasOwnProperty('gmZoom')) {
        hasZoom = true;
      }
      if (attrs.hasOwnProperty('gmBounds')) {
        hasBounds = true;
      }
      if (attrs.hasOwnProperty('gmMapTypeId')) {
        hasMapTypeId = true;
      }

      var updateScope = function() {
        $timeout(function () {
          if (hasCenter || hasZoom || hasBounds || hasMapTypeId) {
            scope.$apply(function (s) {
              if (hasCenter) {
                scope.gmCenter = controller.center;
              }
              if (hasZoom) {
                scope.gmZoom = controller.zoom;
              }
              if (hasBounds) {
                var b = controller.bounds;
                if (b) {
                  scope.gmBounds = b;
                }
              }
              if (hasMapTypeId) {
                scope.gmMapTypeId = controller.mapTypeId;
              }
            });
          }
        });
      };

      controller.addMapListener('drag', updateScope);
      controller.addMapListener('zoom_changed', updateScope);
      controller.addMapListener('center_changed', updateScope);
      controller.addMapListener('bounds_changed', updateScope);
      controller.addMapListener('maptypeid_changed', updateScope);
      controller.addMapListener('resize', updateScope);

      if (hasCenter) {
        scope.$watch('gmCenter', function (newValue, oldValue) {
          var changed = (newValue !== oldValue);
          if (changed && !controller.dragging) {
            var latLng = newValue;
            if (latLng)
              controller.center = latLng;
          }
        }, true);
      }

      if (hasZoom) {
        scope.$watch('gmZoom', function (newValue, oldValue) {
          var ok = (newValue != null && !isNaN(newValue));
          if (ok && newValue !== oldValue) {
            controller.zoom = newValue;
          }
        });
      }

      if (hasBounds) {
        scope.$watch('gmBounds', function(newValue, oldValue) {
          var changed = (newValue !== oldValue);
          if (changed && !controller.dragging) {
            var bounds = newValue;
            if (bounds)
              controller.bounds = bounds;
          }
        });
      }

      if (hasMapTypeId) {
        scope.$watch('gmMapTypeId', function(newValue, oldValue) {
          var changed = (newValue !== oldValue);
          if (changed && newValue) {
            controller.mapTypeId = newValue;
          }
        });
      }

      scope.$on('gmMapResize', function(event, gmMapId) {
        if (scope.gmMapId() === gmMapId) {
          controller.mapTrigger('resize');
        }
      });
    }

    return {
      restrict: 'AE',
      priority: 100,
      template: '
' +
                  '

' +
                  '

' +
                '
',
      transclude: true,
      replace: true,
      scope: {
        gmCenter: '=',
        gmZoom: '=',
        gmBounds: '=',
        gmMapTypeId: '=',
        gmMapOptions: '&',
        gmMapId: '&'
      },
      controller: 'angulargmMapController',
      link: link
    };
  }]);
})();
<commit_msg>fix links to angulargmContainer and angulargmDefaults.<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name angulargm.directive:gmMap
 * @element ANY
 *
 * @description
 * A directive for embedding google maps into your app.
 *
 * `gm-map-id` is required. The `gm-center`, `gm-zoom`, `gm-bounds`, and
 * `gm-map-type-id` variables do not have to exist in the current scope--they
 * will be created if necessary. All three have bi-directional association,
 * i.e.  drag or zoom the map and they will update, update them and the map
 * will change.  However, any initial state of these three variables will be
 * ignored.
 *
 * If you need to get a handle on the google.maps.Map object, see
 * {@link angulargm.service:angulargmContainer angulargmContainer}
 *
 * @param {expression} gm-map-id angular expression that evaluates to a unique
 * string id for the map, e.g. `'map_canvas'` or `myMapId` where myMapId is a
 * variable in the current scope. This allows you to have multiple
 * maps/instances of the directive.
 *
 *
 * @param {expression} gm-center center variable in the current scope.  The
 * value will be a google.maps.LatLng object.
 *
 *
 * @param {expression} gm-zoom zoom variable in the current scope.  Value will
 * be an integer.
 *
 *
 * @param {expression} gm-bounds bounds variable in the current scope.  Value
 * will be a google.maps.LatLngBounds object.
 *
 *
 * @param {expression} gm-map-type-id mapTypeId variable in the current scope.
 * Value will be a string.
 *
 *
 * @param {expression} gm-map-options object in the current scope that is a
 * google.maps.MapOptions object. If unspecified, will use the values in
 * angulargmDefaults.mapOptions. {@link angulargm.service:angulargmDefaults angulargmDefaults} is a service, so it is
 * both injectable and overrideable (using $provide.decorator).
 *
 */

/**
 * @ngdoc event
 * @name angulargm.directive:gmMap#gmMapResize
 * @eventOf angulargm.directive:gmMap
 * @eventType listen on current gmMap scope
 * @param {string} mapId Required. The id of your map.
 * @example
 * ```js
 * $scope.$broadcast('gmMapResize', 'myMapId')
 * ```
 */

(function () {
  angular.module('AngularGM').

  directive('gmMap', ['$timeout', function ($timeout) {

    /** link function **/

    function link(scope, element, attrs, controller) {
      // initialize scope
      if (!angular.isDefined(scope.gmCenter)) {
        scope.center = {};
      }
      if (!angular.isDefined(scope.gmBounds)) {
        scope.bounds = {};
      }

      // Make sure gmMapId is defined
      // Note: redundant check in MapController. Can't hurt.
      if (!angular.isDefined(scope.gmMapId)) {
        throw 'angulargm must have non-empty gmMapId attribute';
      }

      // Check what's defined in attrs
      // Note: this is also redundant since angular will throw an exception if
      // these attributes are not set. I may make these optional in the future
      // (pending angular support).
      var hasCenter = false;
      var hasZoom = false;
      var hasBounds = false;
      var hasMapTypeId = false;

      if (attrs.hasOwnProperty('gmCenter')) {
        hasCenter = true;
      }
      if (attrs.hasOwnProperty('gmZoom')) {
        hasZoom = true;
      }
      if (attrs.hasOwnProperty('gmBounds')) {
        hasBounds = true;
      }
      if (attrs.hasOwnProperty('gmMapTypeId')) {
        hasMapTypeId = true;
      }

      var updateScope = function() {
        $timeout(function () {
          if (hasCenter || hasZoom || hasBounds || hasMapTypeId) {
            scope.$apply(function (s) {
              if (hasCenter) {
                scope.gmCenter = controller.center;
              }
              if (hasZoom) {
                scope.gmZoom = controller.zoom;
              }
              if (hasBounds) {
                var b = controller.bounds;
                if (b) {
                  scope.gmBounds = b;
                }
              }
              if (hasMapTypeId) {
                scope.gmMapTypeId = controller.mapTypeId;
              }
            });
          }
        });
      };

      controller.addMapListener('drag', updateScope);
      controller.addMapListener('zoom_changed', updateScope);
      controller.addMapListener('center_changed', updateScope);
      controller.addMapListener('bounds_changed', updateScope);
      controller.addMapListener('maptypeid_changed', updateScope);
      controller.addMapListener('resize', updateScope);

      if (hasCenter) {
        scope.$watch('gmCenter', function (newValue, oldValue) {
          var changed = (newValue !== oldValue);
          if (changed && !controller.dragging) {
            var latLng = newValue;
            if (latLng)
              controller.center = latLng;
          }
        }, true);
      }

      if (hasZoom) {
        scope.$watch('gmZoom', function (newValue, oldValue) {
          var ok = (newValue != null && !isNaN(newValue));
          if (ok && newValue !== oldValue) {
            controller.zoom = newValue;
          }
        });
      }

      if (hasBounds) {
        scope.$watch('gmBounds', function(newValue, oldValue) {
          var changed = (newValue !== oldValue);
          if (changed && !controller.dragging) {
            var bounds = newValue;
            if (bounds)
              controller.bounds = bounds;
          }
        });
      }

      if (hasMapTypeId) {
        scope.$watch('gmMapTypeId', function(newValue, oldValue) {
          var changed = (newValue !== oldValue);
          if (changed && newValue) {
            controller.mapTypeId = newValue;
          }
        });
      }

      scope.$on('gmMapResize', function(event, gmMapId) {
        if (scope.gmMapId() === gmMapId) {
          controller.mapTrigger('resize');
        }
      });
    }

    return {
      restrict: 'AE',
      priority: 100,
      template: '
' +
                  '

' +
                  '

' +
                '
',
      transclude: true,
      replace: true,
      scope: {
        gmCenter: '=',
        gmZoom: '=',
        gmBounds: '=',
        gmMapTypeId: '=',
        gmMapOptions: '&',
        gmMapId: '&'
      },
      controller: 'angulargmMapController',
      link: link
    };
  }]);
})();
<|endoftext|>


<commit_before>/**
 * author: Di (微信小程序开发工程师)
 * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
 *               垂直微信小程序开发交流社区
 *
 * github地址: https://github.com/icindy/wxParse
 *
 * for: 微信小程序富文本解析
 * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
 */

/**
 * utils函数引入
 **/
import showdown from './showdown.js';
import HtmlToJson from './html2json.js';
/**
 * 配置及公有属性
 **/
/**
 * 主函数入口区
 **/
function wxParse(bindName = 'wxParseData', type='html', data='
数据不能为空
', target,imagePadding) {
  var that = target;
  var transData = {};//存放转化后的数据
  if (type == 'html') {
    transData = HtmlToJson.html2json(data, bindName);
    console.log(JSON.stringify(transData, ' ', ' '));
  } else if (type == 'md' || type == 'markdown') {
    var converter = new showdown.Converter();
    var html = converter.makeHtml(data);
    transData = HtmlToJson.html2json(html, bindName);
    console.log(JSON.stringify(transData, ' ', ' '));
  }
  transData.view = {};
  transData.view.imagePadding = 0;
  if(typeof(imagePadding) != 'undefined'){
    transData.view.imagePadding = imagePadding
  }
  var bindData = {};
  bindData[bindName] = transData;
  that.setData(bindData)
  that.wxParseImgLoad = wxParseImgLoad;
  that.wxParseImgTap = wxParseImgTap;
}
// 图片点击事件
function wxParseImgTap(e) {
  var that = this;
  var nowImgUrl = e.target.dataset.src;
  var tagFrom = e.target.dataset.from;
  if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) {
    wx.previewImage({
      current: nowImgUrl, // 当前显示图片的http链接
      urls: that.data[tagFrom].imageUrls // 需要预览的图片http链接列表
    })
  }
}

/**
 * 图片视觉宽高计算函数区
 **/
function wxParseImgLoad(e) {
  var that = this;
  var tagFrom = e.target.dataset.from;
  var idx = e.target.dataset.idx;
  if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) {
    calMoreImageInfo(e, idx, that, tagFrom)
  }
}
// 假循环获取计算图片视觉最佳宽高
function calMoreImageInfo(e, idx, that, bindName) {
  var temData = that.data[bindName];
  if (temData.images.length == 0) {
    return;
  }
  var temImages = temData.images;
  //因为无法获取view宽度 需要自定义padding进行计算，稍后处理
  var recal = wxAutoImageCal(e.detail.width, e.detail.height,that,bindName);
  temImages[idx].width = recal.imageWidth;
  temImages[idx].height = recal.imageheight;
  temData.images = temImages;
  var bindData = {};
  bindData[bindName] = temData;
  that.setData(bindData);
}

// 计算视觉优先的图片宽高
function wxAutoImageCal(originalWidth, originalHeight,that,bindName) {
  //获取图片的原始长宽
  var windowWidth = 0, windowHeight = 0;
  var autoWidth = 0, autoHeight = 0;
  var results = {};
  wx.getSystemInfo({
    success: function (res) {
      var padding = that.data[bindName].view.imagePadding;
      windowWidth = res.windowWidth-2*padding;
      windowHeight = res.windowHeight;
      //判断按照那种方式进行缩放
      console.log("windowWidth" + windowWidth);
      if (originalWidth > windowWidth) {//在图片width大于手机屏幕width时候
        autoWidth = windowWidth;
        console.log("autoWidth" + autoWidth);
        autoHeight = (autoWidth * originalHeight) / originalWidth;
        console.log("autoHeight" + autoHeight);
        results.imageWidth = autoWidth;
        results.imageheight = autoHeight;
      } else {//否则展示原来的数据
        results.imageWidth = originalWidth;
        results.imageheight = originalHeight;
      }
    }
  })
  return results;
}

function wxParseTemArray(temArrayName,bindNameReg,total,that){
  var array = [];
  var temData = that.data;
  var obj = null;
  for(var i = 0; i < total; i++){
    var simArr = temData[bindNameReg+i].nodes;
    array.push(simArr);
  }

  temArrayName = temArrayName || 'wxParseTemArray';
  obj = JSON.parse('{"'+ temArrayName +'":""}');
  obj[temArrayName] = array;
  that.setData(obj);
}

/**
 * 配置emojis
 *
 */

function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){
   HtmlToJson.emojisInit(reg,baseSrc,emojis);
}

module.exports = {
  wxParse: wxParse,
  wxParseTemArray:wxParseTemArray,
  emojisInit:emojisInit
}

<commit_msg>解决 calMoreImageInfo函数报错问题<commit_after>/**
 * author: Di (微信小程序开发工程师)
 * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
 *               垂直微信小程序开发交流社区
 *
 * github地址: https://github.com/icindy/wxParse
 *
 * for: 微信小程序富文本解析
 * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
 */

/**
 * utils函数引入
 **/
import showdown from './showdown.js';
import HtmlToJson from './html2json.js';
/**
 * 配置及公有属性
 **/
/**
 * 主函数入口区
 **/
function wxParse(bindName = 'wxParseData', type='html', data='
数据不能为空
', target,imagePadding) {
  var that = target;
  var transData = {};//存放转化后的数据
  if (type == 'html') {
    transData = HtmlToJson.html2json(data, bindName);
    console.log(JSON.stringify(transData, ' ', ' '));
  } else if (type == 'md' || type == 'markdown') {
    var converter = new showdown.Converter();
    var html = converter.makeHtml(data);
    transData = HtmlToJson.html2json(html, bindName);
    console.log(JSON.stringify(transData, ' ', ' '));
  }
  transData.view = {};
  transData.view.imagePadding = 0;
  if(typeof(imagePadding) != 'undefined'){
    transData.view.imagePadding = imagePadding
  }
  var bindData = {};
  bindData[bindName] = transData;
  that.setData(bindData)
  that.wxParseImgLoad = wxParseImgLoad;
  that.wxParseImgTap = wxParseImgTap;
}
// 图片点击事件
function wxParseImgTap(e) {
  var that = this;
  var nowImgUrl = e.target.dataset.src;
  var tagFrom = e.target.dataset.from;
  if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) {
    wx.previewImage({
      current: nowImgUrl, // 当前显示图片的http链接
      urls: that.data[tagFrom].imageUrls // 需要预览的图片http链接列表
    })
  }
}

/**
 * 图片视觉宽高计算函数区
 **/
function wxParseImgLoad(e) {
  var that = this;
  var tagFrom = e.target.dataset.from;
  var idx = e.target.dataset.idx;
  if (typeof (tagFrom) != 'undefined' && tagFrom.length > 0) {
    calMoreImageInfo(e, idx, that, tagFrom)
  }
}
// 假循环获取计算图片视觉最佳宽高
function calMoreImageInfo(e, idx, that, bindName) {
  var temData = that.data[bindName];
  if (!temData || temData.images.length == 0) {
    return;
  }
  var temImages = temData.images;
  //因为无法获取view宽度 需要自定义padding进行计算，稍后处理
  var recal = wxAutoImageCal(e.detail.width, e.detail.height,that,bindName);
  temImages[idx].width = recal.imageWidth;
  temImages[idx].height = recal.imageheight;
  temData.images = temImages;
  var bindData = {};
  bindData[bindName] = temData;
  that.setData(bindData);
}

// 计算视觉优先的图片宽高
function wxAutoImageCal(originalWidth, originalHeight,that,bindName) {
  //获取图片的原始长宽
  var windowWidth = 0, windowHeight = 0;
  var autoWidth = 0, autoHeight = 0;
  var results = {};
  wx.getSystemInfo({
    success: function (res) {
      var padding = that.data[bindName].view.imagePadding;
      windowWidth = res.windowWidth-2*padding;
      windowHeight = res.windowHeight;
      //判断按照那种方式进行缩放
      console.log("windowWidth" + windowWidth);
      if (originalWidth > windowWidth) {//在图片width大于手机屏幕width时候
        autoWidth = windowWidth;
        console.log("autoWidth" + autoWidth);
        autoHeight = (autoWidth * originalHeight) / originalWidth;
        console.log("autoHeight" + autoHeight);
        results.imageWidth = autoWidth;
        results.imageheight = autoHeight;
      } else {//否则展示原来的数据
        results.imageWidth = originalWidth;
        results.imageheight = originalHeight;
      }
    }
  })
  return results;
}

function wxParseTemArray(temArrayName,bindNameReg,total,that){
  var array = [];
  var temData = that.data;
  var obj = null;
  for(var i = 0; i < total; i++){
    var simArr = temData[bindNameReg+i].nodes;
    array.push(simArr);
  }

  temArrayName = temArrayName || 'wxParseTemArray';
  obj = JSON.parse('{"'+ temArrayName +'":""}');
  obj[temArrayName] = array;
  that.setData(obj);
}

/**
 * 配置emojis
 *
 */

function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){
   HtmlToJson.emojisInit(reg,baseSrc,emojis);
}

module.exports = {
  wxParse: wxParse,
  wxParseTemArray:wxParseTemArray,
  emojisInit:emojisInit
}

<|endoftext|>


<commit_before>import * as I from 'immutable';

import * as cosets      from '../fpgroups/cosets';
import * as delaney     from './delaney';
import * as properties  from './properties';
import * as delaney2d   from './delaney2d';
import * as delaney3d   from './delaney3d';
import * as fundamental from './fundamental';
import * as covers      from './covers';
import * as periodic    from '../pgraphs/periodic';

import { floatMatrices } from '../arithmetic/types';
const ops = floatMatrices;

const _remainingIndices = (ds, i) => ds.indices().filter(j => j != i);

const _edgeTranslations = function _edgeTranslations(cov) {
  const fg  = fundamental.fundamentalGroup(cov);
  const n   = fg.nrGenerators;
  const nul = ops.nullSpace(cosets.relatorMatrix(n, fg.relators).toJS());

  return fg.edge2word.map(function(a) {
    return a.map(function(b) {
      const v = cosets.relatorAsVector(b, n).toJS();
      return ops.times(v, nul);
    });
  });
};

const _cornerShifts = function _cornerShifts(cov, e2t) {
  const dim = delaney.dim(cov);
  const zero = ops.vector(dim);

  return I.Map().withMutations(function(m) {
    cov.indices().forEach(function(i) {
      const idcs = _remainingIndices(cov, i);

      properties.traversal(cov, idcs, cov.elements()).forEach(function(e) {
        const Dk = e[0];
        const k  = e[1];
        const D  = e[2];

        if (k == properties.traversal.root)
          m.setIn([D, i], zero);
        else
          m.setIn([D, i],
                  ops.minus(m.getIn([Dk, i]), e2t.getIn([Dk, k]) || zero));
      });
    });
  });
};

const _skeleton = function _skeleton(cov, e2t, c2s) {
  const dim = delaney.dim(cov);
  const zero = ops.vector(dim);
  const chambers = cov.elements();
  const idcs0 = _remainingIndices(cov, 0);
  const nodeReps = properties.orbitReps(cov, idcs0, chambers);
  const node2chamber = I.Map(I.Range().zip(nodeReps));
  const chamber2node = I.Map(
    nodeReps
      .zip(I.Range())
      .flatMap(p => properties.orbit(cov, idcs0, p[0]).zip(I.Repeat(p[1]))));

  const edges = properties.orbitReps(cov, _remainingIndices(cov, 1), chambers)
    .map(function(D) {
      const E = cov.s(0, D);
      const v = chamber2node.get(D);
      const w = chamber2node.get(E);
      const t = e2t.getIn([D, 0]) || zero;
      const sD = c2s.getIn([D, 0]);
      const sE = c2s.getIn([E, 0]);
      const s = ops.minus(ops.plus(t, sE), sD);

      return [v, w, s];
    });

  return {
    graph: periodic.make(edges),
    node2chamber: node2chamber,
    chamber2node: chamber2node
  };
};

const _chamberPositions = function _chamberPositions(cov, e2t, c2s, skel, pos) {
  const dim = delaney.dim(cov);
  let result = I.Map();

  cov.elements().forEach(function(D) {
    const p = pos.get(skel.chamber2node.get(D)).toJS();
    const t = c2s.getIn([D, 0]);
    result = result.setIn([D, 0], ops.plus(p, t));
  });

  I.Range(1, dim+1).forEach(function(i) {
    const idcs = I.Range(0, i);
    properties.orbitReps(cov, idcs, cov.elements()).forEach(function(D) {
      const orb = properties.orbit(cov, idcs, D);
      let s = ops.vector(dim);
      orb.forEach(function(E) {
        const p = result.getIn([E, 0]);
        const t = c2s.getIn([E, i]);
        s = ops.plus(s, ops.minus(p, t));
      });
      s = ops.times(ops.div(1, orb.size), s);
      orb.forEach(function(E) {
        const t = c2s.getIn([E, i]);
        result = result.setIn([E, i], ops.plus(s, t));
      });
   });
  });

  return result;
};

const _chamberBasis = function _chamberBasis(pos, D) {
  const t = pos.get(D).valueSeq().toArray();
  return t.slice(1).map(v => ops.minus(v, t[0]));
};

const _symmetries = function _symmetries(ds, cov, pos) {
  const n = delaney.size(ds);
  const m = delaney.size(cov) / n;

  const D = ds.elements()
    .find(D => ops.sgn(ops.determinant(_chamberBasis(pos, D))) != 0);
  const A = ops.inverse(_chamberBasis(pos, D));

  return I.Range(0, m).map(i => ops.times(A, _chamberBasis(pos, D + i*n)));
};

const _resymmetrizedGramMatrix = function _resymmetrizedGramMatrix(G, syms) {
  let A = ops.times(0, G);

  syms.forEach(S => {
    A = ops.plus(A, ops.times(S, ops.times(G, ops.transposed(S))));
  });

  A = ops.times(ops.div(1, syms.size), A);

  return A;
};

const _scalarProduct = (v, w, G) => ops.times(ops.times(v, G), w);

const _orthonormalBasis = function _orthonormalBasis(G) {
  const [n, m] = ops.shape(G);
  let e = ops.identityMatrix(n);

  I.Range(0, n).forEach(function(i) {
    let v = e[i];
    I.Range(0, i).forEach(function(j) {
      const w = e[j];
      const f = _scalarProduct(v, w, G);
      v = ops.minus(v, ops.times(f, w));
    });
    const d = _scalarProduct(v, v, G);
    v = ops.times(1/Math.sqrt(d), v);
    e[i] = v;
  });

  return e;
};

const makeCover = ds =>
  delaney.dim(ds) == 3 ?
  delaney3d.pseudoToroidalCover(ds) :
  delaney2d.toroidalCover(ds);

export default function tiling(ds, cover) {
  const cov  = cover || makeCover(ds);
  const e2t  = _edgeTranslations(cov);
  const c2s  = _cornerShifts(cov, e2t);
  const skel = _skeleton(cov, e2t, c2s);
  const vpos = periodic.barycentricPlacementAsFloat(skel.graph);
  const pos  = _chamberPositions(cov, e2t, c2s, skel, vpos);
  const syms = _symmetries(ds, cov, pos);

  const G = _resymmetrizedGramMatrix(ops.identityMatrix(delaney.dim(ds)), syms);
  const basis = ops.inverse(_orthonormalBasis(G));

  return {
    cover       : cov,
    graph       : skel.graph,
    node2chamber: skel.node2chamber,
    chamber2node: skel.chamber2node,
    positions   : pos,
    symmetries  : syms,
    gramMatrix  : G,
    basis       : basis
  };
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const test = function test(ds) {
    console.log('ds = '+ds);
    console.log(tiling(ds));
    console.log();
  }

  const timer = require('../common/util').timer();

  test(delaney.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>'));
  test(delaney.parse('<1.1:1:1,1,1:6,3>'));
  test(delaney.parse('<1.1:8:2 4 6 8,8 3 5 7,6 5 8 7:4,4>'));
  test(delaney.parse('<1.1:8:2 4 6 8,8 3 5 7,5 6 8 7:4,4>'));
  test(delaney.parse('<1.1:1 3:1,1,1,1:4,3,4>'));
  test(delaney.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
  test(delaney.parse('<1.1:2 3:1 2,1 2,1 2,2:3 3,3 4,4>'));

  console.log();
  console.log(`Computation time: ${timer()} msec`);
}
<commit_msg>better formatting of tiling details<commit_after>import * as I from 'immutable';

import * as cosets      from '../fpgroups/cosets';
import * as delaney     from './delaney';
import * as properties  from './properties';
import * as delaney2d   from './delaney2d';
import * as delaney3d   from './delaney3d';
import * as fundamental from './fundamental';
import * as covers      from './covers';
import * as periodic    from '../pgraphs/periodic';

import { floatMatrices } from '../arithmetic/types';
const ops = floatMatrices;

const _remainingIndices = (ds, i) => ds.indices().filter(j => j != i);

const _edgeTranslations = function _edgeTranslations(cov) {
  const fg  = fundamental.fundamentalGroup(cov);
  const n   = fg.nrGenerators;
  const nul = ops.nullSpace(cosets.relatorMatrix(n, fg.relators).toJS());

  return fg.edge2word.map(function(a) {
    return a.map(function(b) {
      const v = cosets.relatorAsVector(b, n).toJS();
      return ops.times(v, nul);
    });
  });
};

const _cornerShifts = function _cornerShifts(cov, e2t) {
  const dim = delaney.dim(cov);
  const zero = ops.vector(dim);

  return I.Map().withMutations(function(m) {
    cov.indices().forEach(function(i) {
      const idcs = _remainingIndices(cov, i);

      properties.traversal(cov, idcs, cov.elements()).forEach(function(e) {
        const Dk = e[0];
        const k  = e[1];
        const D  = e[2];

        if (k == properties.traversal.root)
          m.setIn([D, i], zero);
        else
          m.setIn([D, i],
                  ops.minus(m.getIn([Dk, i]), e2t.getIn([Dk, k]) || zero));
      });
    });
  });
};

const _skeleton = function _skeleton(cov, e2t, c2s) {
  const dim = delaney.dim(cov);
  const zero = ops.vector(dim);
  const chambers = cov.elements();
  const idcs0 = _remainingIndices(cov, 0);
  const nodeReps = properties.orbitReps(cov, idcs0, chambers);
  const node2chamber = I.Map(I.Range().zip(nodeReps));
  const chamber2node = I.Map(
    nodeReps
      .zip(I.Range())
      .flatMap(p => properties.orbit(cov, idcs0, p[0]).zip(I.Repeat(p[1]))));

  const edges = properties.orbitReps(cov, _remainingIndices(cov, 1), chambers)
    .map(function(D) {
      const E = cov.s(0, D);
      const v = chamber2node.get(D);
      const w = chamber2node.get(E);
      const t = e2t.getIn([D, 0]) || zero;
      const sD = c2s.getIn([D, 0]);
      const sE = c2s.getIn([E, 0]);
      const s = ops.minus(ops.plus(t, sE), sD);

      return [v, w, s];
    });

  return {
    graph: periodic.make(edges),
    node2chamber: node2chamber,
    chamber2node: chamber2node
  };
};

const _chamberPositions = function _chamberPositions(cov, e2t, c2s, skel, pos) {
  const dim = delaney.dim(cov);
  let result = I.Map();

  cov.elements().forEach(function(D) {
    const p = pos.get(skel.chamber2node.get(D)).toJS();
    const t = c2s.getIn([D, 0]);
    result = result.setIn([D, 0], ops.plus(p, t));
  });

  I.Range(1, dim+1).forEach(function(i) {
    const idcs = I.Range(0, i);
    properties.orbitReps(cov, idcs, cov.elements()).forEach(function(D) {
      const orb = properties.orbit(cov, idcs, D);
      let s = ops.vector(dim);
      orb.forEach(function(E) {
        const p = result.getIn([E, 0]);
        const t = c2s.getIn([E, i]);
        s = ops.plus(s, ops.minus(p, t));
      });
      s = ops.times(ops.div(1, orb.size), s);
      orb.forEach(function(E) {
        const t = c2s.getIn([E, i]);
        result = result.setIn([E, i], ops.plus(s, t));
      });
   });
  });

  return result;
};

const _chamberBasis = function _chamberBasis(pos, D) {
  const t = pos.get(D).valueSeq().toArray();
  return t.slice(1).map(v => ops.minus(v, t[0]));
};

const _symmetries = function _symmetries(ds, cov, pos) {
  const n = delaney.size(ds);
  const m = delaney.size(cov) / n;

  const D = ds.elements()
    .find(D => ops.sgn(ops.determinant(_chamberBasis(pos, D))) != 0);
  const A = ops.inverse(_chamberBasis(pos, D));

  return I.Range(0, m).map(i => ops.times(A, _chamberBasis(pos, D + i*n)));
};

const _resymmetrizedGramMatrix = function _resymmetrizedGramMatrix(G, syms) {
  let A = ops.times(0, G);

  syms.forEach(S => {
    A = ops.plus(A, ops.times(S, ops.times(G, ops.transposed(S))));
  });

  A = ops.times(ops.div(1, syms.size), A);

  return A;
};

const _scalarProduct = (v, w, G) => ops.times(ops.times(v, G), w);

const _orthonormalBasis = function _orthonormalBasis(G) {
  const [n, m] = ops.shape(G);
  let e = ops.identityMatrix(n);

  I.Range(0, n).forEach(function(i) {
    let v = e[i];
    I.Range(0, i).forEach(function(j) {
      const w = e[j];
      const f = _scalarProduct(v, w, G);
      v = ops.minus(v, ops.times(f, w));
    });
    const d = _scalarProduct(v, v, G);
    v = ops.times(1/Math.sqrt(d), v);
    e[i] = v;
  });

  return e;
};

const makeCover = ds =>
  delaney.dim(ds) == 3 ?
  delaney3d.pseudoToroidalCover(ds) :
  delaney2d.toroidalCover(ds);

export default function tiling(ds, cover) {
  const cov  = cover || makeCover(ds);
  const e2t  = _edgeTranslations(cov);
  const c2s  = _cornerShifts(cov, e2t);
  const skel = _skeleton(cov, e2t, c2s);
  const vpos = periodic.barycentricPlacementAsFloat(skel.graph);
  const pos  = _chamberPositions(cov, e2t, c2s, skel, vpos);
  const syms = _symmetries(ds, cov, pos);

  const G = _resymmetrizedGramMatrix(ops.identityMatrix(delaney.dim(ds)), syms);
  const basis = ops.inverse(_orthonormalBasis(G));

  return {
    cover       : cov,
    graph       : skel.graph,
    node2chamber: skel.node2chamber,
    chamber2node: skel.chamber2node,
    positions   : pos,
    symmetries  : syms,
    gramMatrix  : G,
    basis       : basis
  };
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x.toString()).join(', ') + ' ]';
  };

  const test = function test(ds) {
    console.log('ds = '+ds);
    const til = tiling(ds);
    Object.keys(til).forEach(k => console.log(`${k}: ${til[k]}`));
    console.log();
  }

  const timer = require('../common/util').timer();

  test(delaney.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>'));
  test(delaney.parse('<1.1:1:1,1,1:6,3>'));
  test(delaney.parse('<1.1:8:2 4 6 8,8 3 5 7,6 5 8 7:4,4>'));
  test(delaney.parse('<1.1:8:2 4 6 8,8 3 5 7,5 6 8 7:4,4>'));
  test(delaney.parse('<1.1:1 3:1,1,1,1:4,3,4>'));
  test(delaney.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
  test(delaney.parse('<1.1:2 3:1 2,1 2,1 2,2:3 3,3 4,4>'));

  console.log();
  console.log(`Computation time: ${timer()} msec`);
}
<|endoftext|>


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

/*
* Abstract Element class
*/
module.exports = (function() {
  // Graph Elements are currently Vertex or Edge
  function Element(gremlin) {
    this._id = null;

    Object.defineProperty(this, "gremlin", {
      value: gremlin,
      enumerable: false,
      writable: true
    });
  }

  // Keep track of a temporary transaction id for each element
  Object.defineProperty(Element.prototype, "identifier", {
    value: null,
    enumerable: false,
    writable: true
  });

  Element.prototype.getProperties = function() {
    var o = {};

    _.each(this, function(property, propertyName) {
      o[propertyName] = this[propertyName];
    }, this);

    return o;
  };

  Element.prototype.setProperty = function (k, v) {
    this[k] = v;
    return this;
  };

  Element.prototype.setProperties = function (properties) {
    for (var key in properties) {
      this.setProperty(key, properties[key]);
    }
    return this;
  };

  /**
   * Titan specific method
   *
   * Use this instead of setProperty() when setting the value of an indexed
   * property.
   */
  Element.prototype.addProperty = function(k, v) {
    this[k] = v;
    return this;
  };

  /**
   * Titan specific method
   *
   * Use this instead of setProperties() when setting the values of indexed
   * properties.
   */
  Element.prototype.addProperties = function (properties) {
    for (var key in properties) {
      this.addProperty(key, properties[key]);
    }
    return this;
  };

  Element.prototype.remove = function() {
    var line = this.identifier +'.remove()';
    this.gremlin.addLine(line);
  };

  return Element;

})();
<commit_msg>Make gremlin property of Element no longer writable<commit_after>var _ = require('lodash');

/*
* Abstract Element class
*/
module.exports = (function() {
  // Graph Elements are currently Vertex or Edge
  function Element(gremlin) {
    this._id = null;

    Object.defineProperty(this, "gremlin", {
      value: gremlin,
      enumerable: false,
      writable: false
    });
  }

  // Keep track of a temporary transaction id for each element
  Object.defineProperty(Element.prototype, "identifier", {
    value: null,
    enumerable: false,
    writable: true
  });

  Element.prototype.getProperties = function() {
    var o = {};

    _.each(this, function(property, propertyName) {
      o[propertyName] = this[propertyName];
    }, this);

    return o;
  };

  Element.prototype.setProperty = function (k, v) {
    this[k] = v;
    return this;
  };

  Element.prototype.setProperties = function (properties) {
    for (var key in properties) {
      this.setProperty(key, properties[key]);
    }
    return this;
  };

  /**
   * Titan specific method
   *
   * Use this instead of setProperty() when setting the value of an indexed
   * property.
   */
  Element.prototype.addProperty = function(k, v) {
    this[k] = v;
    return this;
  };

  /**
   * Titan specific method
   *
   * Use this instead of setProperties() when setting the values of indexed
   * properties.
   */
  Element.prototype.addProperties = function (properties) {
    for (var key in properties) {
      this.addProperty(key, properties[key]);
    }
    return this;
  };

  Element.prototype.remove = function() {
    var line = this.identifier +'.remove()';
    this.gremlin.addLine(line);
  };

  return Element;

})();
<|endoftext|>


<commit_before>function toggleView(container_id, map, alt_up, alt_down)
{
    var div = $(container_id + '_section_container');
    var img = $('toggle_'+container_id);

    if (!div.visible())
    {
        img.src = '/static/images/picto/close.png';
        img.alt = alt_up;
        img.title = img.alt;
        div.style.height = '';
        if (map && !map_initialized) {
            div.style.display = 'block';
            initialize_map();
        }
        else new Effect.BlindDown(div, {duration:0.6});
    }
    else
    {
        img.src = '/static/images/picto/open.png';
        img.alt = alt_down;
        img.title = img.alt;
        new Effect.BlindUp(div, {duration:0.6});
    }
}<commit_msg>(vdb) added map loading indicator<commit_after>function toggleView(container_id, map, alt_up, alt_down)
{
    var div = $(container_id + '_section_container');
    var img = $('toggle_'+container_id);

    if (!div.visible())
    {
        img.src = '/static/images/picto/close.png';
        img.alt = alt_up;
        img.title = img.alt;
        div.style.height = '';
        if (map && !map_initialized) {
            div.style.display = 'block';
            Element.show('indicator');
            initialize_map();
            Element.hide('indicator');
        }
        else new Effect.BlindDown(div, {duration:0.6});
    }
    else
    {
        img.src = '/static/images/picto/open.png';
        img.alt = alt_down;
        img.title = img.alt;
        new Effect.BlindUp(div, {duration:0.6});
    }
}<|endoftext|>


<commit_before>
export function findable(Repository) {
  decorate(Repository, 'find', function () {
    return find
      .call(this, ...arguments)
      .then(data => this.create(data))
  })
  decorate(Repository, 'loadFind', loadFind)
}

export function listable(Repository) {
  decorate(Repository, 'list', function () {
    return list
      .call(this, ...arguments)
      .then(data => this.create(data))
  })
  decorate(Repository, 'loadList', loadList)
}

export function searchable(Repository) {
  decorate(Repository, 'search', function () {
    return search
      .call(this, ...arguments)
      .then(data => this.create(data))
  })
  decorate(Repository, 'loadSearch', loadSearch)
}

export function saveable(Repository) {
  decorate(Repository, 'save', function () {
    return save
      .call(this, ...arguments)
      .then(data => this.create(data))
  })
}

export function destroyable(Repository) {
  decorate(Repository, 'destroy', destroy)
}

function find(id, params = null, { cache = false } = {}) {
  const action = 'read'
  const path = buildPath.call(this, id)
  return this.sync(action, path, null, params, { cache })
}

function loadFind(...args) {
  return load.call(this, 'find', ...args)
}

function list(where, params = {}, { cache = false } = {}) {
  const action = 'read'
  const path = buildPath.call(this)
  params.$where = where
  return this.sync(action, path, null, params, { cache })
}

function loadList(...args) {
  return load.call(this, 'list', ...args)
}

function search(text, params = {}, { cache = false } = {}) {
  const action = 'read'
  const path = buildPath.call(this)
  params.$search = text
  return this.sync(action, path, null, params, { cache })
}

function loadSearch(...args) {
  return load.call(this, 'search', ...args)
}

function save(model, params = null, { replace = false, cache = false } = {}) {
  const isCollection = Array.isArray(model)
  let action, payload, id
  if (isCollection) {
    action = replace ? 'replace' : 'update'
    payload = model.map(instance => this.serialize(instance))
  }
  else {
    id = model.id
    action = id ? replace ? 'replace' : 'update' : 'create'
    payload = this.serialize(model)
  }
  const path = buildPath.call(this, model.id)
  return this.sync(action, path, payload, params, { cache })
}

function destroy(model, params = {}) {
  const isCollection = Array.isArray(model)
  const models = isCollection ? model : [model]
  const toSync = models.filter(model => !!model.id)
  let promise
  if (!toSync.length) {
    this.clear()
    promise = Promise.resolve()
  }
  else if (isCollection) {
    params.$destroy = true
    promise = save.call(this, toSync, params)
  }
  else {
    const action = 'destroy'
    const path = buildPath.call(this, model.id)
    promise = this.sync(action, path, null, params)
  }
  return promise.then(() => models.forEach(model => model._data = null))
}

function decorate(Class, prop, fn) {
  Object.defineProperty(Class.prototype, prop, { value: fn })
}

function buildPath(id) {
  let path = this.collection || ''
  if (id) path += ('/' + id)
  return path
}

function load(query, criteria, params) {
  return this[query](criteria, params, { cache: true }).then(() => this)
}
<commit_msg>Update fetch-decorators.js<commit_after>
export function findable(Repository) {
  decorate(Repository, 'find', function () {
    return find
      .call(this, ...arguments)
      .then(data => this.create(data))
  })
  decorate(Repository, 'loadFind', loadFind)
}

export function listable(Repository) {
  decorate(Repository, 'list', function () {
    return list
      .call(this, ...arguments)
      .then(data => this.create(data))
  })
  decorate(Repository, 'loadList', loadList)
}

export function searchable(Repository) {
  decorate(Repository, 'search', function () {
    return search
      .call(this, ...arguments)
      .then(data => this.create(data))
  })
  decorate(Repository, 'loadSearch', loadSearch)
}

export function saveable(Repository) {
  decorate(Repository, 'save', function () {
    return save
      .call(this, ...arguments)
      .then(data => this.create(data))
  })
}

export function destroyable(Repository) {
  decorate(Repository, 'destroy', destroy)
}

function find(id, params = null, { cache = false } = {}) {
  const action = 'read'
  const path = buildPath.call(this, id)
  return this.sync(action, path, null, params, { cache })
}

function loadFind(...args) {
  return load.call(this, 'find', ...args)
}

function list(where, params = {}, { cache = false } = {}) {
  const action = 'read'
  const path = buildPath.call(this)
  params.$where = where
  return this.sync(action, path, null, params, { cache })
}

function loadList(...args) {
  return load.call(this, 'list', ...args)
}

function search(text, params = {}, { cache = false } = {}) {
  const action = 'read'
  const path = buildPath.call(this)
  params.$search = text
  return this.sync(action, path, null, params, { cache })
}

function loadSearch(...args) {
  return load.call(this, 'search', ...args)
}

function save(model, params = null, { replace = false, cache = false } = {}) {
  const isCollection = Array.isArray(model)
  let action, payload, id
  if (isCollection) {
    action = replace ? 'replace' : 'update'
    payload = model.map(instance => this.serialize(instance))
  }
  else {
    id = model.id
    action = id ? replace ? 'replace' : 'update' : 'create'
    payload = this.serialize(model)
  }
  const path = buildPath.call(this, model.id)
  return this.sync(action, path, payload, params, { cache })
}

function destroy(model, params = {}) {
  const isCollection = Array.isArray(model)
  const models = isCollection ? model : [model]
  const toSync = models.filter(model => !!model.id)
  let promise
  if (!toSync.length) {
    this.clear()
    promise = Promise.resolve()
  }
  else if (isCollection) {
    params.$destroy = true
    promise = save.call(this, toSync, params)
  }
  else {
    const action = 'destroy'
    const path = buildPath.call(this, model.id)
    promise = this.sync(action, path, null, params)
  }
  return promise.then(() => models.forEach(model => model._data = null))
}

function decorate(Class, prop, fn) {
  Object.defineProperty(Class.prototype, prop, { value: fn })
}

function buildPath(id) {
  const path = this.collection || ''
  return id ? path + '/' + id : path
}

function load(query, criteria, params) {
  return this[query](criteria, params, { cache: true }).then(() => this)
}
<|endoftext|>


<commit_before><commit_msg>Add layers not display in layerSwitcher always on top<commit_after><|endoftext|>


<commit_before><commit_msg>Drop deps _, versioncompare, rename global to Compat.<commit_after><|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 * @author szimek / https://github.com/szimek/
 */

THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {

	Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } );

	this.uuid = THREE.Math.generateUUID();

	this.name = '';
	this.sourceFile = '';

	this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE;
	this.mipmaps = [];

	this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING;

	this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping;
	this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;

	this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
	this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter;

	this.anisotropy = anisotropy !== undefined ? anisotropy : 1;

	this.format = format !== undefined ? format : THREE.RGBAFormat;
	this.type = type !== undefined ? type : THREE.UnsignedByteType;

	this.offset = new THREE.Vector2( 0, 0 );
	this.repeat = new THREE.Vector2( 1, 1 );

	this.generateMipmaps = true;
	this.premultiplyAlpha = false;
	this.flipY = true;
	this.unpackAlignment = 4;	// valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
	this.encoding = THREE.LinearEncoding;	// Values !== THREE.LinearEncoding only supported on map, envMap and emissiveMap (as these maps regularly have unbounded intensity values, i.e. via an *.hdr or *.exr image.)

	this.version = 0;
	this.onUpdate = null;

};

THREE.Texture.DEFAULT_IMAGE = undefined;
THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping;

THREE.Texture.prototype = {

	constructor: THREE.Texture,

	set needsUpdate ( value ) {

		if ( value === true ) this.version ++;

	},

	clone: function () {

		return new this.constructor().copy( this );

	},

	copy: function ( source ) {

		this.image = source.image;
		this.mipmaps = source.mipmaps.slice( 0 );

		this.mapping = source.mapping;

		this.wrapS = source.wrapS;
		this.wrapT = source.wrapT;

		this.magFilter = source.magFilter;
		this.minFilter = source.minFilter;

		this.anisotropy = source.anisotropy;

		this.format = source.format;
		this.type = source.type;

		this.offset.copy( source.offset );
		this.repeat.copy( source.repeat );

		this.generateMipmaps = source.generateMipmaps;
		this.premultiplyAlpha = source.premultiplyAlpha;
		this.flipY = source.flipY;
		this.unpackAlignment = source.unpackAlignment;
		this.encoding = source.encoding;

		return this;

	},

	toJSON: function ( meta ) {

		if ( meta.textures[ this.uuid ] !== undefined ) {

			return meta.textures[ this.uuid ];

		}

		function getDataURL( image ) {

			var canvas;

			if ( image.toDataURL !== undefined ) {

				canvas = image;

			} else {

				canvas = document.createElement( 'canvas' );
				canvas.width = image.width;
				canvas.height = image.height;

				canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height );

			}

			if ( canvas.width > 2048 || canvas.height > 2048 ) {

				return canvas.toDataURL( 'image/jpeg', 0.6 );

			} else {

				return canvas.toDataURL( 'image/png' );

			}

		}

		var output = {
			metadata: {
				version: 4.4,
				type: 'Texture',
				generator: 'Texture.toJSON'
			},

			uuid: this.uuid,
			name: this.name,

			mapping: this.mapping,

			repeat: [ this.repeat.x, this.repeat.y ],
			offset: [ this.offset.x, this.offset.y ],
			wrap: [ this.wrapS, this.wrapT ],

			minFilter: this.minFilter,
			magFilter: this.magFilter,
			anisotropy: this.anisotropy
		};

		if ( this.image !== undefined ) {

			// TODO: Move to THREE.Image

			var image = this.image;

			if ( image.uuid === undefined ) {

				image.uuid = THREE.Math.generateUUID(); // UGH

			}

			if ( meta.images[ image.uuid ] === undefined ) {

				meta.images[ image.uuid ] = {
					uuid: image.uuid,
					url: getDataURL( image )
				};

			}

			output.image = image.uuid;

		}

		meta.textures[ this.uuid ] = output;

		return output;

	},

	dispose: function () {

		this.dispatchEvent( { type: 'dispose' } );

	},

	transformUv: function ( uv ) {

		if ( this.mapping !== THREE.UVMapping )  return;

		uv.multiply( this.repeat );
		uv.add( this.offset );

		if ( uv.x < 0 || uv.x > 1 ) {

			switch ( this.wrapS ) {

				case THREE.RepeatWrapping:

					uv.x = uv.x - Math.floor( uv.x );
					break;

				case THREE.ClampToEdgeWrapping:

					uv.x = uv.x < 0 ? 0 : 1;
					break;

				case THREE.MirroredRepeatWrapping:

					if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {

						uv.x = Math.ceil( uv.x ) - uv.x;

					} else {

						uv.x = uv.x - Math.floor( uv.x );

					}
					break;

			}

		}

		if ( uv.y < 0 || uv.y > 1 ) {

			switch ( this.wrapT ) {

				case THREE.RepeatWrapping:

					uv.y = uv.y - Math.floor( uv.y );
					break;

				case THREE.ClampToEdgeWrapping:

					uv.y = uv.y < 0 ? 0 : 1;
					break;

				case THREE.MirroredRepeatWrapping:

					if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {

						uv.y = Math.ceil( uv.y ) - uv.y;

					} else {

						uv.y = uv.y - Math.floor( uv.y );

					}
					break;

			}

		}

		if ( this.flipY ) {

			uv.y = 1 - uv.y;

		}

	}

};

THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype );

THREE.TextureIdCount = 0;
<commit_msg>add better documentation that MAterial.needsUpdate needs to be set if one changes Texture.encoding.<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 * @author szimek / https://github.com/szimek/
 */

THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {

	Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } );

	this.uuid = THREE.Math.generateUUID();

	this.name = '';
	this.sourceFile = '';

	this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE;
	this.mipmaps = [];

	this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING;

	this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping;
	this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;

	this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
	this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter;

	this.anisotropy = anisotropy !== undefined ? anisotropy : 1;

	this.format = format !== undefined ? format : THREE.RGBAFormat;
	this.type = type !== undefined ? type : THREE.UnsignedByteType;

	this.offset = new THREE.Vector2( 0, 0 );
	this.repeat = new THREE.Vector2( 1, 1 );

	this.generateMipmaps = true;
	this.premultiplyAlpha = false;
	this.flipY = true;
	this.unpackAlignment = 4;	// valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)

	// Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
	//
	// Also changing the encoding after already used by a Material will not automatically make the Material
	// update.  You need to explicitly call Material.needsUpdate to trigger it to recompile.
	this.encoding = THREE.LinearEncoding;

	this.version = 0;
	this.onUpdate = null;

};

THREE.Texture.DEFAULT_IMAGE = undefined;
THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping;

THREE.Texture.prototype = {

	constructor: THREE.Texture,

	set needsUpdate ( value ) {

		if ( value === true ) this.version ++;

	},

	clone: function () {

		return new this.constructor().copy( this );

	},

	copy: function ( source ) {

		this.image = source.image;
		this.mipmaps = source.mipmaps.slice( 0 );

		this.mapping = source.mapping;

		this.wrapS = source.wrapS;
		this.wrapT = source.wrapT;

		this.magFilter = source.magFilter;
		this.minFilter = source.minFilter;

		this.anisotropy = source.anisotropy;

		this.format = source.format;
		this.type = source.type;

		this.offset.copy( source.offset );
		this.repeat.copy( source.repeat );

		this.generateMipmaps = source.generateMipmaps;
		this.premultiplyAlpha = source.premultiplyAlpha;
		this.flipY = source.flipY;
		this.unpackAlignment = source.unpackAlignment;
		this.encoding = source.encoding;

		return this;

	},

	toJSON: function ( meta ) {

		if ( meta.textures[ this.uuid ] !== undefined ) {

			return meta.textures[ this.uuid ];

		}

		function getDataURL( image ) {

			var canvas;

			if ( image.toDataURL !== undefined ) {

				canvas = image;

			} else {

				canvas = document.createElement( 'canvas' );
				canvas.width = image.width;
				canvas.height = image.height;

				canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height );

			}

			if ( canvas.width > 2048 || canvas.height > 2048 ) {

				return canvas.toDataURL( 'image/jpeg', 0.6 );

			} else {

				return canvas.toDataURL( 'image/png' );

			}

		}

		var output = {
			metadata: {
				version: 4.4,
				type: 'Texture',
				generator: 'Texture.toJSON'
			},

			uuid: this.uuid,
			name: this.name,

			mapping: this.mapping,

			repeat: [ this.repeat.x, this.repeat.y ],
			offset: [ this.offset.x, this.offset.y ],
			wrap: [ this.wrapS, this.wrapT ],

			minFilter: this.minFilter,
			magFilter: this.magFilter,
			anisotropy: this.anisotropy
		};

		if ( this.image !== undefined ) {

			// TODO: Move to THREE.Image

			var image = this.image;

			if ( image.uuid === undefined ) {

				image.uuid = THREE.Math.generateUUID(); // UGH

			}

			if ( meta.images[ image.uuid ] === undefined ) {

				meta.images[ image.uuid ] = {
					uuid: image.uuid,
					url: getDataURL( image )
				};

			}

			output.image = image.uuid;

		}

		meta.textures[ this.uuid ] = output;

		return output;

	},

	dispose: function () {

		this.dispatchEvent( { type: 'dispose' } );

	},

	transformUv: function ( uv ) {

		if ( this.mapping !== THREE.UVMapping )  return;

		uv.multiply( this.repeat );
		uv.add( this.offset );

		if ( uv.x < 0 || uv.x > 1 ) {

			switch ( this.wrapS ) {

				case THREE.RepeatWrapping:

					uv.x = uv.x - Math.floor( uv.x );
					break;

				case THREE.ClampToEdgeWrapping:

					uv.x = uv.x < 0 ? 0 : 1;
					break;

				case THREE.MirroredRepeatWrapping:

					if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {

						uv.x = Math.ceil( uv.x ) - uv.x;

					} else {

						uv.x = uv.x - Math.floor( uv.x );

					}
					break;

			}

		}

		if ( uv.y < 0 || uv.y > 1 ) {

			switch ( this.wrapT ) {

				case THREE.RepeatWrapping:

					uv.y = uv.y - Math.floor( uv.y );
					break;

				case THREE.ClampToEdgeWrapping:

					uv.y = uv.y < 0 ? 0 : 1;
					break;

				case THREE.MirroredRepeatWrapping:

					if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {

						uv.y = Math.ceil( uv.y ) - uv.y;

					} else {

						uv.y = uv.y - Math.floor( uv.y );

					}
					break;

			}

		}

		if ( this.flipY ) {

			uv.y = 1 - uv.y;

		}

	}

};

THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype );

THREE.TextureIdCount = 0;
<|endoftext|>