<commit_before>/**
 *
 *    Copyright (c) 2020 Silicon Labs
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
const fs = require('fs')
const util = require('../util/util.js')
const dbEnum = require('../../src-shared/db-enum.js')
const env = require('../util/env')
const queryConfig = require('../db/query-config.js')
const queryPackage = require('../db/query-package.js')
const queryImpexp = require('../db/query-impexp.js')
const querySession = require('../db/query-session.js')
const zclLoader = require('../zcl/zcl-loader.js')

/**
 * Resolves with a promise that imports session key values.
 *
 * @param {*} db
 * @param {*} sessionId
 * @param {*} keyValuePairs
 */
async function importSessionKeyValues(db, sessionId, keyValuePairs) {
  let allQueries = []
  if (keyValuePairs != null) {
    env.logDebug(`Loading ${keyValuePairs.length} key value pairs.`)
    // Write key value pairs
    keyValuePairs.forEach((element) => {
      allQueries.push(
        querySession.updateSessionKeyValue(
          db,
          sessionId,
          element.key,
          element.value
        )
      )
    })
  }
  return Promise.all(allQueries).then(() => sessionId)
}

function getPkgPath(pkg, zapFilePath) {
  if ('pathRelativity' in pkg) {
    return util.createAbsolutePath(pkg.path, pkg.pathRelativity, zapFilePath)
  } else {
    return pkg.path
  }
}

// Resolves into a { packageId:, packageType:}
// object, pkg has`path`, `version`, `type`. It can ALSO have pathRelativity. If pathRelativity is missing
// path is considered absolute.
async function importSinglePackage(db, sessionId, pkg, zapFilePath) {
  let absPath = getPkgPath(pkg, zapFilePath)
  let pkgId = await queryPackage.getPackageIdByPathAndTypeAndVersion(
    db,
    absPath,
    pkg.type,
    pkg.version
  )

  if (pkgId != null) {
    // Perfect match found, return it and be done.
    return {
      packageId: pkgId,
      packageType: pkg.type,
    }
  }

  // Now we have to perform the guessing logic.
  env.logDebug(
    'Packages from the file did not match loaded packages making best bet.'
  )
  let packages = await queryPackage.getPackagesByType(db, pkg.type)

  // If there isn't any, then abort, but if there is only one, use it.
  if (packages.length == 0) {
    if (pkg.type == dbEnum.packageType.genTemplatesJson) {
      // We don't throw exception for genTemplatesJson, we can survive without.
      env.logDebug(`No packages of type ${pkg.type} found in the database.`)
      return null
    } else {
      throw new Error(`No packages of type ${pkg.type} found in the database.`)
    }
  } else if (packages.length == 1) {
    env.logDebug(
      `Only one package of given type ${pkg.type} present. Using it.`
    )
    return {
      packageId: packages[0].id,
      packageType: pkg.type,
    }
  }

  // Filter to just the ones that match the version
  packages = packages.filter((p) => p.version == pkg.version)
  // If there isn't any abort, if there is only one, use it.
  if (packages.length == 0) {
    let msg = `No packages of type ${pkg.type} that match version ${pkg.version} found in the database.`
    if (pkg.type == dbEnum.packageType.genTemplatesJson) {
      // We don't throw exception for genTemplatesJson, we can survive without.
      env.logDebug(msg)
      return null
    } else {
      throw new Error(msg)
    }
  } else if (packages.length == 1) {
    env.logDebug(
      `Only one package of given type ${pkg.type} and version ${pkg.version} present. Using it.`
    )
    return {
      packageId: packages[0].id,
      packageType: pkg.type,
    }
  }

  // We now know we have more than 1 matching package. Find best bet.
  let existingPackages = packages.filter((p) => fs.existsSync(p.path))

  if (existingPackages.length == 1) {
    // Only one exists, use that one.
    let p = existingPackages[0]
    env.logWarning(`Using only package that exists:${p.id}.`)
    return {
      packageId: p.id,
      packageType: pkg.type,
    }
  } else if (existingPackages.length > 1) {
    // More than one exists. Use the first one.
    let p = existingPackages[0]
    env.logWarning(
      `Using first package that exists out of ${existingPackages.length}: ${p.id}.`
    )
    return {
      packageId: p.id,
      packageType: pkg.type,
    }
  } else {
    // None exists, so use the first one from 'packages'.
    let p = packages[0]
    env.logWarning(
      `None of packages exist, so using first one overall: ${p.id}.`
    )
    return {
      packageId: p.id,
      packageType: pkg.type,
    }
  }
}

// Resolves an array of { packageId:, packageType:} objects into { packageId: id, otherIds: [] }
function convertPackageResult(sessionId, data) {
  let ret = {
    sessionId: sessionId,
    packageId: null,
    otherIds: [],
    optionalIds: [],
  }
  data.forEach((obj) => {
    if (obj == null) return null
    if (obj.packageType == dbEnum.packageType.zclProperties) {
      ret.packageId = obj.packageId
    } else if (obj.packageType == dbEnum.packageType.genTemplatesJson) {
      ret.otherIds.push(obj.packageId)
    } else {
      ret.optionalIds.push(obj.packageId)
    }
  })
  return ret
}

// Returns a promise that resolves into an object containing: packageId and otherIds
async function importPackages(db, sessionId, packages, zapFilePath) {
  let allQueries = []
  if (packages != null) {
    env.logDebug(`Loading ${packages.length} packages`)
    packages.forEach((p) => {
      allQueries.push(importSinglePackage(db, sessionId, p, zapFilePath))
    })
  }
  let data = await Promise.all(allQueries)
  return convertPackageResult(sessionId, data)
}

async function importEndpointTypes(
  db,
  sessionId,
  packageId,
  endpointTypes,
  endpoints,
  newPackageIds
) {
  let allQueries = []
  let sortedEndpoints = {}
  if (endpoints != null) {
    endpoints.forEach((ep) => {
      let eptIndex = ep.endpointTypeIndex
      if (sortedEndpoints[eptIndex] == null) sortedEndpoints[eptIndex] = []
      sortedEndpoints[eptIndex].push(ep)
    })
  }

  if (endpointTypes != null) {
    env.logDebug(`Loading ${endpointTypes.length} endpoint types`)
    endpointTypes.forEach((et, index) => {
      allQueries.push(
        queryImpexp
          .importEndpointType(db, sessionId, [packageId, ...newPackageIds], et)
          .then((endpointId) => {
            // Now we need to import commands, attributes and clusters.
            let promises = []
            if (sortedEndpoints[index]) {
              sortedEndpoints[index].forEach((endpoint) => {
                promises.push(
                  queryImpexp.importEndpoint(
                    db,
                    sessionId,
                    endpoint,
                    endpointId
                  )
                )
              })
            }
            // et.clusters
            et.clusters.forEach((cluster) => {
              // code, mfgCode, side
              promises.push(
                queryImpexp
                  .importClusterForEndpointType(
                    db,
                    [packageId, ...newPackageIds],
                    endpointId,
                    cluster
                  )
                  .then((endpointClusterId) => {
                    let ps = []

                    if ('commands' in cluster)
                      cluster.commands.forEach((command) => {
                        ps.push(
                          queryImpexp.importCommandForEndpointType(
                            db,
                            [packageId, ...newPackageIds],
                            endpointId,
                            endpointClusterId,
                            command
                          )
                        )
                      })

                    if ('attributes' in cluster)
                      cluster.attributes.forEach((attribute) => {
                        ps.push(
                          queryImpexp.importAttributeForEndpointType(
                            db,
                            [packageId, ...newPackageIds],
                            endpointId,
                            endpointClusterId,
                            attribute
                          )
                        )
                      })
                    return Promise.all(ps)
                  })
              )
            })
            return Promise.all(promises)
          })
      )
    })
  }
  return Promise.all(allQueries)
}

/**
 * Given a state object, this method returns a promise that resolves
 * with the succesfull writing into the database.
 *
 * @export
 * @param {*} db
 * @param {*} state
 * @param {*} sessionId If null, then new session will get
 *              created, otherwise it loads the data into an
 *              existing session. Previous session data is not deleted.
 * @returns a promise that resolves into a sessionId that was created.
 */
async function jsonDataLoader(db, state, sessionId) {
  // import any packages new to the DB
  let newPkgs = await Promise.all(
    state.package.map((pkg) =>
      queryPackage.getPackageIdByPathAndTypeAndVersion(
        db,
        getPkgPath(pkg, state.filePath),
        pkg.type,
        pkg.version
      )
    )
  )
    .then((pkgIds) =>
      pkgIds.map((pkgId, index) => {
        // packages that is new to DB will carry null value.
        if (pkgId) {
          return null
        } else {
          return state.package[index]
        }
      })
    )
    .then((newPkgs) => newPkgs.filter((x) => x))

  let newPkgIds = (
    await Promise.all(
      newPkgs.map((pkg) => {
        let filePath = getPkgPath(pkg, state.filePath)
        return zclLoader.loadIndividualFile(db, filePath, sessionId)
      })
    )
  )
    .filter((p) => p.succeeded)
    .map((p) => p.packageId)

  let data = await importPackages(db, sessionId, state.package, state.filePath)

  // data: { sessionId, packageId, otherIds, optionalIds}
  let promisesStage0 = []
  let promisesStage1 = [] // Stage 1 is endpoint types
  let promisesStage2 = [] // Stage 2 is endpoints, which require endpoint types to be loaded prior.
  if (data.optionalIds.length > 0) {
    data.optionalIds.forEach((optionalId) =>
      promisesStage0.push(
        queryPackage.insertSessionPackage(db, sessionId, optionalId)
      )
    )
  }

  if ('keyValuePairs' in state) {
    promisesStage1.push(
      importSessionKeyValues(db, data.sessionId, state.keyValuePairs)
    )
  }

  if ('endpointTypes' in state) {
    promisesStage1.push(
      importEndpointTypes(
        db,
        data.sessionId,
        data.packageId,
        state.endpointTypes,
        state.endpoints,
        newPkgIds
      )
    )
  }

  await Promise.all(promisesStage0)
  await Promise.all(promisesStage1)
  await Promise.all(promisesStage2)
  await querySession.setSessionClean(db, data.sessionId)

  // ensure the PACKAGE_REF is correct inside SESSION_PACKAGE table.
  // Issue: upon startup, initializeSessionPackage() inits everything from the commandline args.
  //        when loading an .zap configuration, it might refer to a different package than the startup package.
  if ('package' in state) {
    await Promise.all(
      state.package.map(async (pkg) => {
        let pkgFilePath = getPkgPath(pkg, state.filePath)

        let sessionPkgs = await queryPackage.getSessionPackagesByType(
          db,
          data.sessionId,
          pkg.type
        )
        let invalidSessionPkgs = sessionPkgs.filter(
          (x) => x.path !== pkgFilePath
        )
        let validSessionPkgId =
          await queryPackage.getPackageIdByPathAndTypeAndVersion(
            db,
            pkgFilePath,
            pkg.type,
            pkg.version
          )

        if (validSessionPkgId != null && invalidSessionPkgs.length) {
          await Promise.all(
            invalidSessionPkgs.map((y) => {
              env.logDebug(
                `Disabling/removing invalid session package. sessionId(${sessionId}), packageId(${y.id}), path(${y.path})`
              )
              return queryPackage.deleteSessionPackage(db, sessionId, y.id)
            })
          )

          env.logDebug(
            `Enabling session package. sessionId(${sessionId}), packageId(${validSessionPkgId})`
          )
          await queryPackage.insertSessionPackage(
            db,
            sessionId,
            validSessionPkgId
          )
        }
      })
    )
  }

  return {
    sessionId: data.sessionId,
    errors: [],
    warnings: [],
  }
}

// This function cleans up some backwards-compatible problems in zap
// files.
function cleanJsonState(state) {
  if (!('featureLevel' in state)) {
    state.featureLevel = 0
  }
  /*
  state.endpoints.forEach((ep) => {
    if (!('endpointVersion' in ep) || ep.endpointVersion == null) {
      ep.endpointVersion = 1
    }
  })
  */
}

/**
 * Parses JSON file and creates a state object out of it, which is passed further down the chain.
 *
 * @param {*} filePath
 * @param {*} data
 * @returns Promise of parsed JSON object
 */
async function readJsonData(filePath, data) {
  let state = JSON.parse(data)

  cleanJsonState(state)
  let status = util.matchFeatureLevel(state.featureLevel, filePath)

  if (status.match) {
    if (!('keyValuePairs' in state)) {
      state.keyValuePairs = []
    }
    state.filePath = filePath
    state.keyValuePairs.push({
      key: dbEnum.sessionKey.filePath,
      value: filePath,
    })
    state.loader = jsonDataLoader
    return state
  } else {
    throw new Error(status.message)
  }
}

exports.readJsonData = readJsonData
<commit_msg>fix issue with loading multiple custom zcl extension files.<commit_after>/**
 *
 *    Copyright (c) 2020 Silicon Labs
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
const fs = require('fs')
const util = require('../util/util.js')
const dbEnum = require('../../src-shared/db-enum.js')
const env = require('../util/env')
const queryConfig = require('../db/query-config.js')
const queryPackage = require('../db/query-package.js')
const queryImpexp = require('../db/query-impexp.js')
const querySession = require('../db/query-session.js')
const zclLoader = require('../zcl/zcl-loader.js')

/**
 * Resolves with a promise that imports session key values.
 *
 * @param {*} db
 * @param {*} sessionId
 * @param {*} keyValuePairs
 */
async function importSessionKeyValues(db, sessionId, keyValuePairs) {
  let allQueries = []
  if (keyValuePairs != null) {
    env.logDebug(`Loading ${keyValuePairs.length} key value pairs.`)
    // Write key value pairs
    keyValuePairs.forEach((element) => {
      allQueries.push(
        querySession.updateSessionKeyValue(
          db,
          sessionId,
          element.key,
          element.value
        )
      )
    })
  }
  return Promise.all(allQueries).then(() => sessionId)
}

function getPkgPath(pkg, zapFilePath) {
  if ('pathRelativity' in pkg) {
    return util.createAbsolutePath(pkg.path, pkg.pathRelativity, zapFilePath)
  } else {
    return pkg.path
  }
}

// Resolves into a { packageId:, packageType:}
// object, pkg has`path`, `version`, `type`. It can ALSO have pathRelativity. If pathRelativity is missing
// path is considered absolute.
async function importSinglePackage(db, sessionId, pkg, zapFilePath) {
  let absPath = getPkgPath(pkg, zapFilePath)
  let pkgId = await queryPackage.getPackageIdByPathAndTypeAndVersion(
    db,
    absPath,
    pkg.type,
    pkg.version
  )

  if (pkgId != null) {
    // Perfect match found, return it and be done.
    return {
      packageId: pkgId,
      packageType: pkg.type,
    }
  }

  // Now we have to perform the guessing logic.
  env.logDebug(
    'Packages from the file did not match loaded packages making best bet.'
  )
  let packages = await queryPackage.getPackagesByType(db, pkg.type)

  // If there isn't any, then abort, but if there is only one, use it.
  if (packages.length == 0) {
    if (pkg.type == dbEnum.packageType.genTemplatesJson) {
      // We don't throw exception for genTemplatesJson, we can survive without.
      env.logDebug(`No packages of type ${pkg.type} found in the database.`)
      return null
    } else {
      throw new Error(`No packages of type ${pkg.type} found in the database.`)
    }
  } else if (packages.length == 1) {
    env.logDebug(
      `Only one package of given type ${pkg.type} present. Using it.`
    )
    return {
      packageId: packages[0].id,
      packageType: pkg.type,
    }
  }

  // Filter to just the ones that match the version
  packages = packages.filter((p) => p.version == pkg.version)
  // If there isn't any abort, if there is only one, use it.
  if (packages.length == 0) {
    let msg = `No packages of type ${pkg.type} that match version ${pkg.version} found in the database.`
    if (pkg.type == dbEnum.packageType.genTemplatesJson) {
      // We don't throw exception for genTemplatesJson, we can survive without.
      env.logDebug(msg)
      return null
    } else {
      throw new Error(msg)
    }
  } else if (packages.length == 1) {
    env.logDebug(
      `Only one package of given type ${pkg.type} and version ${pkg.version} present. Using it.`
    )
    return {
      packageId: packages[0].id,
      packageType: pkg.type,
    }
  }

  // We now know we have more than 1 matching package. Find best bet.
  let existingPackages = packages.filter(
    (p) => fs.existsSync(p.path) && p.path === absPath
  )

  if (existingPackages.length == 1) {
    // Only one exists, use that one.
    let p = existingPackages[0]
    env.logWarning(`Using only package that exists:${p.id}.`)
    return {
      packageId: p.id,
      packageType: pkg.type,
    }
  } else if (existingPackages.length > 1) {
    // More than one exists. Use the first one.
    let p = existingPackages[0]
    env.logWarning(
      `Using first package that exists out of ${existingPackages.length}: ${p.id}.`
    )
    return {
      packageId: p.id,
      packageType: pkg.type,
    }
  } else {
    // None exists, so use the first one from 'packages'.
    let p = packages[0]
    env.logWarning(
      `None of packages exist, so using first one overall: ${p.id}.`
    )
    return {
      packageId: p.id,
      packageType: pkg.type,
    }
  }
}

// Resolves an array of { packageId:, packageType:} objects into { packageId: id, otherIds: [] }
function convertPackageResult(sessionId, data) {
  let ret = {
    sessionId: sessionId,
    packageId: null,
    otherIds: [],
    optionalIds: [],
  }
  data.forEach((obj) => {
    if (obj == null) return null
    if (obj.packageType == dbEnum.packageType.zclProperties) {
      ret.packageId = obj.packageId
    } else if (obj.packageType == dbEnum.packageType.genTemplatesJson) {
      ret.otherIds.push(obj.packageId)
    } else {
      ret.optionalIds.push(obj.packageId)
    }
  })
  return ret
}

// Returns a promise that resolves into an object containing: packageId and otherIds
async function importPackages(db, sessionId, packages, zapFilePath) {
  let allQueries = []
  if (packages != null) {
    env.logDebug(`Loading ${packages.length} packages`)
    packages.forEach((p) => {
      allQueries.push(importSinglePackage(db, sessionId, p, zapFilePath))
    })
  }
  let data = await Promise.all(allQueries)
  return convertPackageResult(sessionId, data)
}

async function importEndpointTypes(
  db,
  sessionId,
  packageId,
  endpointTypes,
  endpoints,
  newPackageIds
) {
  let allQueries = []
  let sortedEndpoints = {}
  if (endpoints != null) {
    endpoints.forEach((ep) => {
      let eptIndex = ep.endpointTypeIndex
      if (sortedEndpoints[eptIndex] == null) sortedEndpoints[eptIndex] = []
      sortedEndpoints[eptIndex].push(ep)
    })
  }

  if (endpointTypes != null) {
    env.logDebug(`Loading ${endpointTypes.length} endpoint types`)
    endpointTypes.forEach((et, index) => {
      allQueries.push(
        queryImpexp
          .importEndpointType(db, sessionId, [packageId, ...newPackageIds], et)
          .then((endpointId) => {
            // Now we need to import commands, attributes and clusters.
            let promises = []
            if (sortedEndpoints[index]) {
              sortedEndpoints[index].forEach((endpoint) => {
                promises.push(
                  queryImpexp.importEndpoint(
                    db,
                    sessionId,
                    endpoint,
                    endpointId
                  )
                )
              })
            }
            // et.clusters
            et.clusters.forEach((cluster) => {
              // code, mfgCode, side
              promises.push(
                queryImpexp
                  .importClusterForEndpointType(
                    db,
                    [packageId, ...newPackageIds],
                    endpointId,
                    cluster
                  )
                  .then((endpointClusterId) => {
                    let ps = []

                    if ('commands' in cluster)
                      cluster.commands.forEach((command) => {
                        ps.push(
                          queryImpexp.importCommandForEndpointType(
                            db,
                            [packageId, ...newPackageIds],
                            endpointId,
                            endpointClusterId,
                            command
                          )
                        )
                      })

                    if ('attributes' in cluster)
                      cluster.attributes.forEach((attribute) => {
                        ps.push(
                          queryImpexp.importAttributeForEndpointType(
                            db,
                            [packageId, ...newPackageIds],
                            endpointId,
                            endpointClusterId,
                            attribute
                          )
                        )
                      })
                    return Promise.all(ps)
                  })
              )
            })
            return Promise.all(promises)
          })
      )
    })
  }
  return Promise.all(allQueries)
}

/**
 * Given a state object, this method returns a promise that resolves
 * with the succesfull writing into the database.
 *
 * @export
 * @param {*} db
 * @param {*} state
 * @param {*} sessionId If null, then new session will get
 *              created, otherwise it loads the data into an
 *              existing session. Previous session data is not deleted.
 * @returns a promise that resolves into a sessionId that was created.
 */
async function jsonDataLoader(db, state, sessionId) {
  // import any packages new to the DB
  let newPkgs = await Promise.all(
    state.package.map((pkg) =>
      queryPackage.getPackageIdByPathAndTypeAndVersion(
        db,
        getPkgPath(pkg, state.filePath),
        pkg.type,
        pkg.version
      )
    )
  )
    .then((pkgIds) =>
      pkgIds.map((pkgId, index) => {
        // packages that is new to DB will carry null value.
        if (pkgId) {
          return null
        } else {
          return state.package[index]
        }
      })
    )
    .then((newPkgs) => newPkgs.filter((x) => x))

  let newPkgIds = (
    await Promise.all(
      newPkgs.map((pkg) => {
        let filePath = getPkgPath(pkg, state.filePath)
        return zclLoader.loadIndividualFile(db, filePath, sessionId)
      })
    )
  )
    .filter((p) => p.succeeded)
    .map((p) => p.packageId)

  let data = await importPackages(db, sessionId, state.package, state.filePath)

  // data: { sessionId, packageId, otherIds, optionalIds}
  let promisesStage0 = []
  let promisesStage1 = [] // Stage 1 is endpoint types
  let promisesStage2 = [] // Stage 2 is endpoints, which require endpoint types to be loaded prior.
  if (data.optionalIds.length > 0) {
    data.optionalIds.forEach((optionalId) =>
      promisesStage0.push(
        queryPackage.insertSessionPackage(db, sessionId, optionalId)
      )
    )
  }

  if ('keyValuePairs' in state) {
    promisesStage1.push(
      importSessionKeyValues(db, data.sessionId, state.keyValuePairs)
    )
  }

  if ('endpointTypes' in state) {
    promisesStage1.push(
      importEndpointTypes(
        db,
        data.sessionId,
        data.packageId,
        state.endpointTypes,
        state.endpoints,
        newPkgIds
      )
    )
  }

  await Promise.all(promisesStage0)
  await Promise.all(promisesStage1)
  await Promise.all(promisesStage2)
  await querySession.setSessionClean(db, data.sessionId)

  // ensure the PACKAGE_REF is correct inside SESSION_PACKAGE table.
  // Issue: upon startup, initializeSessionPackage() inits everything from the commandline args.
  //        when loading an .zap configuration, it might refer to a different package than the startup package.
  if ('package' in state) {
    await Promise.all(
      state.package.map(async (pkg) => {
        let pkgFilePath = getPkgPath(pkg, state.filePath)

        let sessionPkgs = await queryPackage.getSessionPackagesByType(
          db,
          data.sessionId,
          pkg.type
        )
        let invalidSessionPkgs = sessionPkgs.filter(
          (x) => x.path !== pkgFilePath
        )
        let validSessionPkgId =
          await queryPackage.getPackageIdByPathAndTypeAndVersion(
            db,
            pkgFilePath,
            pkg.type,
            pkg.version
          )

        if (validSessionPkgId != null && invalidSessionPkgs.length) {
          await Promise.all(
            invalidSessionPkgs.map((y) => {
              env.logDebug(
                `Disabling/removing invalid session package. sessionId(${sessionId}), packageId(${y.id}), path(${y.path})`
              )
              return queryPackage.deleteSessionPackage(db, sessionId, y.id)
            })
          )

          env.logDebug(
            `Enabling session package. sessionId(${sessionId}), packageId(${validSessionPkgId})`
          )
          await queryPackage.insertSessionPackage(
            db,
            sessionId,
            validSessionPkgId
          )
        }
      })
    )
  }

  return {
    sessionId: data.sessionId,
    errors: [],
    warnings: [],
  }
}

// This function cleans up some backwards-compatible problems in zap
// files.
function cleanJsonState(state) {
  if (!('featureLevel' in state)) {
    state.featureLevel = 0
  }
  /*
  state.endpoints.forEach((ep) => {
    if (!('endpointVersion' in ep) || ep.endpointVersion == null) {
      ep.endpointVersion = 1
    }
  })
  */
}

/**
 * Parses JSON file and creates a state object out of it, which is passed further down the chain.
 *
 * @param {*} filePath
 * @param {*} data
 * @returns Promise of parsed JSON object
 */
async function readJsonData(filePath, data) {
  let state = JSON.parse(data)

  cleanJsonState(state)
  let status = util.matchFeatureLevel(state.featureLevel, filePath)

  if (status.match) {
    if (!('keyValuePairs' in state)) {
      state.keyValuePairs = []
    }
    state.filePath = filePath
    state.keyValuePairs.push({
      key: dbEnum.sessionKey.filePath,
      value: filePath,
    })
    state.loader = jsonDataLoader
    return state
  } else {
    throw new Error(status.message)
  }
}

exports.readJsonData = readJsonData
<|endoftext|>


<commit_before>import React from 'react'
import Nav from '../Nav/component.js'
import {Box, cube, Table, TableData, TableRow, TableHead, Bullet} from '../theme.js'
import { connect } from 'react-redux';
import styled from 'styled-components';
import { Link } from 'react-router';

const LinkBullet = ({link})=>{
  let required = link.required == true ? 'Yes':'No'
  return(
    <TableRow>

    <TableData>
      <Link to={'/dd/' + link.target_type }> {link.name} </Link>
    </TableData>

    <TableData>
      {required}
    </TableData>

    <TableData>
      {link.label}
    </TableData>

    </TableRow>
 )
}

const LinkTable = ({links, node}) =>{
  let fields = ['Name', 'Required', 'Label']
  return(

    <TableHead>
      <TableRow>
      {fields.map((field, i) =>
              <TableData first_cr key={i}>{field}</TableData>)}
      </TableRow>
    </TableHead>

      {links.map( (link, i) => <LinkBullet key={i} link={link} /> )}

 )
}

const getType = ( schema ) => {
  if ('type' in schema) {
    if (typeof schema.type == 'string'){
      return schema.type;
    }
    else {
      return schema.type.join(', ');
    }
  }
  else if ('enum' in schema) {
    return schema.enum.join(', ');
  }
}

const NodeTable = ({node}) => {
  return(

    <TableRow>
      <TableData first_cr> Title </TableData>
      <TableData right>{ node['title'] }</TableData>
    </TableRow>

    <TableRow>
      <TableData first_cr> Category </TableData>
      <TableData right>{ node['category']}</TableData>
    </TableRow>

    <TableRow>
      <TableData first_cr> Description </TableData>
      <TableData right>{ node['description']}</TableData>
    </TableRow>

    <TableRow>
      <TableData first_cr> Unique Keys </TableData>
      <TableData right>{

        {node['uniqueKeys'].map( (key, i) => <Bullet key={i}>{key.join(', ')}</Bullet> )}

      }</TableData>
    </TableRow>

  )
}
const Required = styled(TableData)`
`;
const Col1 = styled(TableData)`
  width: 35%;
`;
const Col2 = styled(TableData)`
  width: 15%;
`;
const Col3 = styled(TableData)`
  width: 10%;
`;
const Col4 = styled(TableData)`
  width: 40%;
`;

const PropertyBullet = ({property_name, property, required}) =>{
  let description = 'No Description';
  if ('description' in property){
    description = property.description;
  }
  if ('term' in property){
    description = property.term.description;
  }

  let type = getType(property);
  if (type == undefined){
    if ('oneOf' in property){
      type = property['oneOf'].map((item, i) => getType(item)).join(', ');
    }
  }

  return(
    <TableRow>
      <Col1>
 { property_name }
 </Col1>
      <Col2>
{ (type.indexOf(',') == -1) ? type : type.split(', ').map((item) => <Bullet>{item}</Bullet>)}
</Col2>
      <Col3> { required ? "Yes" : "No" } </Col3>
      <Col4> { description } </Col4>
    </TableRow>
  )
}

const PropertiesTable = ({node, required, links}) =>{
  let properties_fields = ['Property','Type','Required', 'Description']
  let linknames = links.map((link, i) => link['name']);
  let properties = Object.keys(node['properties'])
  return(

      <TableHead>
        <TableRow>
          <Col1 first_cr>Property</Col1>
          <Col2 first_cr>Type</Col2>
          <Col3 first_cr>Required</Col3>
          <Col4 first_cr>Description</Col4>
        </TableRow>
      </TableHead>

        {
        properties.map( (property, i) =>
          (linknames.indexOf(property) == -1) &&
          <PropertyBullet key={i} required={property in required} property_name={property} property={node['properties'][property]}/>
         )
        }

  )
}

const DataDictionaryNodeType = ({params,submission}) =>{
  let node = params.node
  let dictionary = submission.dictionary
  let links = [];
  let required = ('required' in dictionary[node]) ? dictionary[node].required : [];
  for (var link of submission.dictionary[node].links){
    if (link.name != undefined){
        links.push(link);
    }
    else {
      links = links.concat(link['subgroup']);
    }
  }
  return (
  <Box>

 {node}

    <Link to='/dd'>{'< top level dictionary'}</Link>

 Summary

      <NodeTable node={dictionary[node]} > </NodeTable>

 Links

      <LinkTable links={links} node = {node} />

 Properties

      <PropertiesTable links={links} required={required} node ={dictionary[node]} >  </PropertiesTable>

  </Box>

 )
}

const mapStateToProps = (state)=> {return {
  'submission': state.submission,
}};

const mapDispatchToProps = (dispatch) => ({
});

const DataDictionaryNode = connect(mapStateToProps, mapDispatchToProps)(DataDictionaryNodeType);
export default DataDictionaryNode<commit_msg>fix(required): fix dictionary required property<commit_after>import React from 'react'
import Nav from '../Nav/component.js'
import {Box, cube, Table, TableData, TableRow, TableHead, Bullet} from '../theme.js'
import { connect } from 'react-redux';
import styled from 'styled-components';
import { Link } from 'react-router';

const LinkBullet = ({link})=>{
  let required = link.required == true ? 'Yes':'No'
  return(
    <TableRow>

    <TableData>
      <Link to={'/dd/' + link.target_type }> {link.name} </Link>
    </TableData>

    <TableData>
      {required}
    </TableData>

    <TableData>
      {link.label}
    </TableData>

    </TableRow>
 )
}

const LinkTable = ({links, node}) =>{
  let fields = ['Name', 'Required', 'Label']
  return(

    <TableHead>
      <TableRow>
      {fields.map((field, i) =>
              <TableData first_cr key={i}>{field}</TableData>)}
      </TableRow>
    </TableHead>

      {links.map( (link, i) => <LinkBullet key={i} link={link} /> )}

 )
}

const getType = ( schema ) => {
  if ('type' in schema) {
    if (typeof schema.type == 'string'){
      return schema.type;
    }
    else {
      return schema.type.join(', ');
    }
  }
  else if ('enum' in schema) {
    return schema.enum.join(', ');
  }
}

const NodeTable = ({node}) => {
  return(

    <TableRow>
      <TableData first_cr> Title </TableData>
      <TableData right>{ node['title'] }</TableData>
    </TableRow>

    <TableRow>
      <TableData first_cr> Category </TableData>
      <TableData right>{ node['category']}</TableData>
    </TableRow>

    <TableRow>
      <TableData first_cr> Description </TableData>
      <TableData right>{ node['description']}</TableData>
    </TableRow>

    <TableRow>
      <TableData first_cr> Unique Keys </TableData>
      <TableData right>{

        {node['uniqueKeys'].map( (key, i) => <Bullet key={i}>{key.join(', ')}</Bullet> )}

      }</TableData>
    </TableRow>

  )
}
const Required = styled(TableData)`
`;
const Col1 = styled(TableData)`
  width: 35%;
`;
const Col2 = styled(TableData)`
  width: 15%;
`;
const Col3 = styled(TableData)`
  width: 10%;
`;
const Col4 = styled(TableData)`
  width: 40%;
`;

const PropertyBullet = ({property_name, property, required}) =>{
  let description = 'No Description';
  if ('description' in property){
    description = property.description;
  }
  if ('term' in property){
    description = property.term.description;
  }

  let type = getType(property);
  if (type == undefined){
    if ('oneOf' in property){
      type = property['oneOf'].map((item, i) => getType(item)).join(', ');
    }
  }

  return(
    <TableRow>
      <Col1>
 { property_name }
 </Col1>
      <Col2>
{ (type.indexOf(',') == -1) ? type : type.split(', ').map((item) => <Bullet>{item}</Bullet>)}
</Col2>
      <Col3> { required ? "Yes" : "No" } </Col3>
      <Col4> { description } </Col4>
    </TableRow>
  )
}

const PropertiesTable = ({node, required, links}) =>{
  let properties_fields = ['Property','Type','Required', 'Description']
  let linknames = links.map((link, i) => link['name']);
  let properties = Object.keys(node['properties'])
  return(

      <TableHead>
        <TableRow>
          <Col1 first_cr>Property</Col1>
          <Col2 first_cr>Type</Col2>
          <Col3 first_cr>Required</Col3>
          <Col4 first_cr>Description</Col4>
        </TableRow>
      </TableHead>

        {
        properties.map( (property, i) =>
          (! linknames.includes(property)) &&
          <PropertyBullet key={i} required={required.includes(property)} property_name={property} property={node['properties'][property]}/>
         )
        }

  )
}

const DataDictionaryNodeType = ({params,submission}) =>{
  let node = params.node
  let dictionary = submission.dictionary
  let links = [];
  let required = ('required' in dictionary[node]) ? dictionary[node].required : [];
  for (var link of submission.dictionary[node].links){
    if (link.name != undefined){
        links.push(link);
    }
    else {
      links = links.concat(link['subgroup']);
    }
  }
  return (
  <Box>

 {node}

    <Link to='/dd'>{'< top level dictionary'}</Link>

 Summary

      <NodeTable node={dictionary[node]} > </NodeTable>

 Links

      <LinkTable links={links} node = {node} />

 Properties

      <PropertiesTable links={links} required={required} node ={dictionary[node]} >  </PropertiesTable>

  </Box>

 )
}

const mapStateToProps = (state)=> {return {
  'submission': state.submission,
}};

const mapDispatchToProps = (dispatch) => ({
});

const DataDictionaryNode = connect(mapStateToProps, mapDispatchToProps)(DataDictionaryNodeType);
export default DataDictionaryNode<|endoftext|>


<commit_before>define([
        '../../../dojo/_base/declare',
        'dojo/_base/lang',
        'dojo/_base/array',
        'dojo/Deferred',
        'JBrowse/Model/SimpleFeature',
        'JBrowse/Store/SeqFeature',
        'JBrowse/Store/DeferredStatsMixin',
        'JBrowse/Store/DeferredFeaturesMixin',
        'JBrowse/Store/TabixIndexedFile',
        'JBrowse/Store/SeqFeature/GlobalStatsEstimationMixin',
        'JBrowse/Model/XHRBlob',
        './BEDTabix/Parser',
        'JBrowse/Store/SeqFeature/BEDTabix/LazyFeature'
    ],
    function(
        declare,
        lang,
        array,
        Deferred,
        SimpleFeature,
        SeqFeatureStore,
        DeferredStatsMixin,
        DeferredFeaturesMixin,
        TabixIndexedFile,
        GlobalStatsEstimationMixin,
        XHRBlob,
        Parser,
        LazyFeature
    ) {

        return declare( [ SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, GlobalStatsEstimationMixin, Parser ],
            {

                constructor: function( args ) {
                    var thisB = this;

                    var tbiBlob = args.tbi ||
                        new XHRBlob(
                            this.resolveUrl(
                                this.getConf('tbiUrlTemplate',[]) || this.getConf('urlTemplate',[])+'.tbi'
                            )
                        );

                    var fileBlob = args.file ||
                        new XHRBlob(
                            this.resolveUrl( this.getConf('urlTemplate',[]) )
                        );

                    this.indexedData = new TabixIndexedFile(
                        {
                            tbi: tbiBlob,
                            file: fileBlob,
                            browser: this.browser,
                            chunkSizeLimit: args.chunkSizeLimit || 1000000
                        });

                    this.getHeader()
                        .then( function( header ) {
                            thisB._deferred.features.resolve({success:true});
                            thisB._estimateGlobalStats()
                                .then(
                                function( stats ) {
                                    thisB.globalStats = stats;
                                    thisB._deferred.stats.resolve( stats );
                                },
                                lang.hitch( thisB, '_failAllDeferred' )
                            );
                        },
                        lang.hitch( thisB, '_failAllDeferred' )
                    );
                },

                /**fetch and parse the Header line */

                getHeader: function() {
                    var thisB = this;
                    return this._parsedHeader || ( this._parsedHeader = function() {
                            var d = new Deferred();
                            var reject = lang.hitch( d, 'reject' );

                            thisB.indexedData.indexLoaded.then( function() {
                                    var maxFetch = thisB.indexedData.index.firstDataLine
                                        ? (thisB.indexedData.index.firstDataLine.block + thisB.indexedData.data.blockSize - 1) *2
                                        : null;

                                    thisB.indexedData.data.read(
                                        0,
                                        maxFetch,
                                        function( bytes ) {
                                            thisB.parseHeader( new Uint8Array( bytes ) );
                                            d.resolve( thisB.header );
                                        },
                                        reject
                                    );
                                },
                                reject
                            );

                            return d;
                        }.call(this));
                },
                _getFeatures: function(query, featureCallback,finishCallback,errorCallback){
                    var thisB=this;
                    thisB.getHeader().then(function(){
                        thisB.indexedData.getLines(
                            query.ref || thisB.refSeq.name,
                            query.start,
                            query.end,
                            function( line ) {
                                var f=thisB.lineToFeature(line);
//                                console.log(f);
                                featureCallback(f);
                            },
                            finishCallback,
                            errorCallback

                        );
                    },errorCallback);
                },
/*
                _getFeatures: function( query, featureCallback,finishedCallback, errorCallback ) {
                    var thisB = this;
                    var f=featureCallback;
                    console.log(thisB);
                    var parser = new Parser(
                        {
                            featureCallback: function(fs) {
                                array.forEach( fs, function( feature ) {

                                    var feat = thisB._formatFeature(feature);
                                    f(feat);
                                });
                            },
                            endCallback: function() {
                                finishedCallback();
                            }
                        });

                    thisB.getHeader().then( function() {
                        thisB.indexedData.getLines(
                            query.ref || thisB.refSeq.name,
                            query.start,
                            query.end,
                            function( line ) {
                                parser._buffer_feature( thisB.lineToFeature(line) );
                            },
                            function() {
                                parser.finish();
                            },
                            errorCallback
                        );
                    }, errorCallback );
                },

                lineToFeature: function( line ) {
                    var ref    = line.fields[0];
                    var matrix = line.matrix;
                    var strand = {'-':-1,'.':0,'+':1}[line.fields[5]];
                    var featureData = {
                        start:  line.start,
                        end:    line.end,
                        strand: strand,
                        seq_id: line.ref,
                        name: line.fields[3],
                        score: line.fields[4],
                        matrix: matrix
                    };

                    return featureData;
                }, // not so sure if keep this one
*/
                // flatten array like [ [1,2], [3,4] ] to [ 1,2,3,4 ]
                _flattenOneLevel: function( ar ) {
                    var r = [];
                    for( var i = 0; i<ar.length; i++ ) {
                        r.push.apply( r, ar[i] );
                    }
                    return r;
                },

                _featureData: function( data ) {
                    var f = lang.mixin( {}, data );
                    for( var a in data.matrix ) {
                        f[ a.toLowerCase() ] = data.attributes[a].join(',');
                    }

                    return f;
                },
                _formatFeature: function( data ) {
                    var f = new SimpleFeature({
                        data: this._featureData( data ),
                        id: data.seq_id + "_"+ data.start + "_" +data.end+ "_" + data.name
                    });
                    f._reg_seq_id = this.browser.regularizeReferenceName( data.seq_id );
                    return f;
                },

                /**
                 * Interrogate whether a store has data for a given reference
                 * sequence.  Calls the given callback with either true or false.
                 *
                 * Implemented as a binary interrogation because some stores are
                 * smart enough to regularize reference sequence names, while
                 * others are not.
                 */
                hasRefSeq: function( seqName, callback, errorCallback ) {
                    return this.indexedData.index.hasRefSeq( seqName, callback, errorCallback );
                },

                saveStore: function() {
                    return {
                        urlTemplate: this.config.file.url,
                        tbiUrlTemplate: this.config.tbi.url
                    };
                }

            });
    });
<commit_msg>src/JBrowse/Store/SeqFeature/BEDTabix.js<commit_after>define([
        '../../../dojo/_base/declare',
        'dojo/_base/lang',
        'dojo/_base/array',
        'dojo/Deferred',
        'JBrowse/Model/SimpleFeature',
        'JBrowse/Store/SeqFeature',
        'JBrowse/Store/DeferredStatsMixin',
        'JBrowse/Store/DeferredFeaturesMixin',
        'JBrowse/Store/TabixIndexedFile',
        'JBrowse/Store/SeqFeature/GlobalStatsEstimationMixin',
        'JBrowse/Model/XHRBlob',
        './BEDTabix/Parser',
        'JBrowse/Store/SeqFeature/BEDTabix/LazyFeature'
    ],
    function(
        declare,
        lang,
        array,
        Deferred,
        SimpleFeature,
        SeqFeatureStore,
        DeferredStatsMixin,
        DeferredFeaturesMixin,
        TabixIndexedFile,
        GlobalStatsEstimationMixin,
        XHRBlob,
        Parser,
        LazyFeature
    ) {

        return declare( [ SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, GlobalStatsEstimationMixin, Parser ],
            {

                constructor: function( args ) {
                    var thisB = this;

                    var tbiBlob = args.tbi ||
                        new XHRBlob(
                            this.resolveUrl(
                                this.getConf('tbiUrlTemplate',[]) || this.getConf('urlTemplate',[])+'.tbi'
                            )
                        );

                    var fileBlob = args.file ||
                        new XHRBlob(
                            this.resolveUrl( this.getConf('urlTemplate',[]) )
                        );

                    this.indexedData = new TabixIndexedFile(
                        {
                            tbi: tbiBlob,
                            file: fileBlob,
                            browser: this.browser,
                            chunkSizeLimit: args.chunkSizeLimit || 1000000
                        });

                    this.getHeader()
                        .then( function( header ) {
                            thisB._deferred.features.resolve({success:true});
                            thisB._estimateGlobalStats()
                                .then(
                                function( stats ) {
                                    thisB.globalStats = stats;
                                    thisB._deferred.stats.resolve( stats );
                                },
                                lang.hitch( thisB, '_failAllDeferred' )
                            );
                        },
                        lang.hitch( thisB, '_failAllDeferred' )
                    );
                },

                /**fetch and parse the Header line */

                getHeader: function() {
                    var thisB = this;
                    return this._parsedHeader || ( this._parsedHeader = function() {
                            var d = new Deferred();
                            var reject = lang.hitch( d, 'reject' );

                            thisB.indexedData.indexLoaded.then( function() {
                                    var maxFetch = thisB.indexedData.index.firstDataLine
                                        ? (thisB.indexedData.index.firstDataLine.block + thisB.indexedData.data.blockSize - 1) *2
                                        : null;

                                    thisB.indexedData.data.read(
                                        0,
                                        maxFetch,
                                        function( bytes ) {
                                            thisB.parseHeader( new Uint8Array( bytes ) );
                                            d.resolve( thisB.header );
                                        },
                                        reject
                                    );
                                },
                                reject
                            );

                            return d;
                        }.call(this));
                },
                _getFeatures: function(query, featureCallback,finishCallback,errorCallback){
                    var thisB=this;
                    thisB.getHeader().then(function(){
                        thisB.indexedData.getLines(
                            query.ref || thisB.refSeq.name,
                            query.start,
                            query.end,
                            function( line ) {
                                var f=thisB.lineToFeature(line);
//                                console.log(f);
                                featureCallback(f);
                            },
                            finishCallback,
                            errorCallback

                        );
                    },errorCallback);
                },

                // flatten array like [ [1,2], [3,4] ] to [ 1,2,3,4 ]
                _flattenOneLevel: function( ar ) {
                    var r = [];
                    for( var i = 0; i<ar.length; i++ ) {
                        r.push.apply( r, ar[i] );
                    }
                    return r;
                },

                _featureData: function( data ) {
                    var f = lang.mixin( {}, data );
                    for( var a in data.matrix ) {
                        f[ a.toLowerCase() ] = data.attributes[a].join(',');
                    }

                    return f;
                },
                _formatFeature: function( data ) {
                    var f = new SimpleFeature({
                        data: this._featureData( data ),
                        id: data.seq_id + "_"+ data.start + "_" +data.end+ "_" + data.name
                    });
                    f._reg_seq_id = this.browser.regularizeReferenceName( data.seq_id );
                    return f;
                },

                /**
                 * Interrogate whether a store has data for a given reference
                 * sequence.  Calls the given callback with either true or false.
                 *
                 * Implemented as a binary interrogation because some stores are
                 * smart enough to regularize reference sequence names, while
                 * others are not.
                 */
                hasRefSeq: function( seqName, callback, errorCallback ) {
                    return this.indexedData.index.hasRefSeq( seqName, callback, errorCallback );
                },

                saveStore: function() {
                    return {
                        urlTemplate: this.config.file.url,
                        tbiUrlTemplate: this.config.tbi.url
                    };
                }

            });
    });
<|endoftext|>


<commit_before>/**
 * Extend CommonJS require to allow for installing (using NPM) and reloading of code
 * without doing a hard reset of the application.
 *
 * All of this code is a mess. Layers of hackery and bodges to get the desired end result.
 *
 * Written By:
 *         Matthew Knox
 *
 * License:
 *        MIT License. All code unless otherwise specified is
 *        Copyright (c) Matthew Knox and Contributors 2016.
 */

'use strict';

const npm = require(global.rootPathJoin('core/common/npm.js')),
    path = require('path'),
    fs = require('fs'),
    nativeReloadHacksCache = {},
    nativeReloadHacks = ['deasync'],
    npmFolder = 'node_modules',
    npmDirectory = global.rootPathJoin(npmFolder),
    npmLocalDirective = 'package.json',
    coreDirectory = global.rootPathJoin('core'),
    nativeModules = Object.getOwnPropertyNames(process.binding('natives')),
    common = {},
    referenceCounts = {};

// populate common, allows for require('concierge/*') to be redirected to the common directory
fs.readdirSync(global.rootPathJoin('core/common')).forEach(f => {
    common['concierge/' + path.parse(f).name] = global.rootPathJoin('core/common', f);
});

// force add node_modules to the path, so that installed NPM modules will be found regardless of module install location
let newPath = process.env.NODE_PATH || '';
if (newPath.length > 0) {
    newPath += /^win/.test(process.platform) ? ';' : ':';
}
newPath += global.rootPathJoin('node_modules');
process.env.NODE_PATH = newPath;
require('module').Module._initPaths();

/**
 * Translate a modules alias to its actual name/path.
 * @param {string} mod input name
 * @returns {string} actual name/path
 */
const getActualName = (mod) => common[mod] || mod;

/**
 * Detect if a code module needs to be installed through NPM without using any internal node
 * require mechanisms. This needs to be done manually, because newer versions of node cache resolve/require
 * lookups, which means that after they have been installed they will not be detected by using require().
 * @param {string} request the lookup to perform.
 * @param {string} dirName relative directory to perform the lookup from.
 * @returns {boolean} if the module has already been installed.
 */
const resolve = (request, dirName) => {
    if (nativeModules.includes(request)) {
        return true;
    }
    const parsed = path.parse(request);
    try {
        if (parsed.ext !== '' && parsed.ext !== '.' && parsed.dir !== '') {
            const p = path.resolve(dirName, request),
                file = fs.statSync(p);
            return file && (file.isFile() || file.isDirectory());
        }
        else {
            const npmDirs = [];
            const modName = global.moduleNameFromPath(dirName);
            if (modName === null) {
                npmDirs.push(path.join(dirName, npmFolder, request));
            }
            else {
                npmDirs.push(path.join(global.__modulesPath, modName, npmFolder, request));
            }
            if (global.__runAsLocal || dirName.startsWith(coreDirectory)) {
                npmDirs.push(path.join(npmDirectory, request));
            }
            for (let n of npmDirs) {
                try {
                    const dir = fs.statSync(n);
                    if (dir && dir.isDirectory()) {
                        return true;
                    }
                } catch (e2) {}
            }
            throw new Error();
        }
    }
    catch (e) {
        return parsed.dir.indexOf('.') === 0;
    }
};

/**
 * Determines if an NPM install should be performed in a modules directory.
 * @param {string} dirName the directory of the module
 * @param {string} name the name of the package that is being installed
 * @returns {boolean} if NPM install should be run on the directory.
 */
const shouldInstallLocally = (dirName, name) => {
    try {
        if (!global.__runAsLocal) {
            const mock = {
                dependencies: {}
            };
            mock.dependencies[name] = '*';
            return mock;
        }
        const p = path.join(dirName, npmLocalDirective),
            d = fs.statSync(p);
        if (dirName.startsWith(global.__modulesPath) && d.isFile()) {
            return JSON.parse(fs.readFileSync(p));
        }
        return false;
    }
    catch (e) {
        return false;
    }
};

/**
 * Requires a module, installing it first from NPM if necessary.
 * @param {function()} req origional require function
 * @param {string} name name of the module to install
 * @param {string} dirName relative directory to require from
 */
const installAndRequire = (req, name, dirName) => {
    const v = resolve(name, dirName);
    if (!v) {
        const local = shouldInstallLocally(dirName, name);
        let cwd = global.__rootPath,
            npmName = name;
        if (local) {
            cwd = dirName;
            const ver = local.dependencies[name] || local.devDependencies[name];
            npmName = `${name}@${ver}`;
        }
        const t = global.$$, // translations might not have loaded
            startStr = t ? t`Installing "${npmName}" from npm.` : `Installing "${npmName}" from npm.`,
            endStr = t ? t`Installation complete.` : 'Installation complete.';

        console.info(startStr);
        npm.install([npmName], cwd);
        console.info(endStr);
    }
    else if (nativeReloadHacksCache.hasOwnProperty(name)) {
        return nativeReloadHacksCache[name];
    }
    const r = req(name);
    if (nativeReloadHacks.includes(name) || name.endsWith('.node')) {
        nativeReloadHacksCache[name] = r;
    }
    return r;
};

/**
 * A global require replacement hook. This is used to allow code-hotswapping,
 * runtime dependency installation and injection. When called, this will generate a
 * new require function that should be used within a module.
 * @param {function()} req origional require function of the module.
 * @param {string} dirName __dirname of the module
 * @param {string} fileName __filename of the module
 * @returns {function()} a replacement require function for the module.
 */
module.exports = (req, dirName, fileName) => {
    const moduleDirName = global.moduleNameFromPath(dirName) || fileName;
    if (!referenceCounts.hasOwnProperty(moduleDirName)) {
        referenceCounts[moduleDirName] = {
            self: [fileName],
            count: 'Ignore',
            refs: []
        };
    }

    const func = (mod) => {
        mod = getActualName(mod);
        const res = installAndRequire(req, mod, dirName),
            p = req.resolve(mod),
            refName = global.moduleNameFromPath(p) || p;
        if (!p.contains(npmDirectory) && !nativeModules.includes(mod)) {
            if (!referenceCounts.hasOwnProperty(refName)) {
                referenceCounts[refName] = {
                    self: [],
                    count: 0,
                    refs: []
                };
            }

            if (!referenceCounts[refName].self.includes(p)) {
                referenceCounts[refName].self.push(p);
            }

            if (refName !== moduleDirName && !referenceCounts[moduleDirName].refs.includes(refName)) {
                if (typeof (referenceCounts[refName].count) === 'string') {
                    referenceCounts[refName].count = 0;
                }
                referenceCounts[refName].count++;
                referenceCounts[moduleDirName].refs.push(refName);
            }
        }
        return res;
    };
    for (let key in req) {
        func[key] = req[key];
    }

    func.forcePackageInstall = dir => {
        const locally = shouldInstallLocally(dir);
        let stat = null;
        try {
            stat = fs.statSync(path.join(dir, npmFolder));
        }
        catch (e) {}
        if (global.__runAsLocal && !stat && locally && Object.keys(locally.dependencies).length > 0) {
            npm.install(Object.keys(locally.dependencies).map(d => `${d}@${locally.dependencies[d]}`), dir);
        }
    };

    func.searchCache = (moduleName, callback) => {
        moduleName = getActualName(moduleName);
        let mod = func.resolve(moduleName);
        if (mod && (typeof (mod = func.cache[mod]) !== 'undefined')) {
            const run = (m) => {
                m.children.forEach((child) => {
                    run(child);
                });
                callback(mod);
            };
            run(mod);
        }
    };

    func.uncache = (moduleName) => {
        moduleName = getActualName(moduleName);
        func.searchCache(moduleName, (mod) => {
            delete func.cache[mod.id];
        });

        Object.keys(module.constructor._pathCache).forEach((cacheKey) => {
            if (cacheKey.indexOf(moduleName) > 0) {
                delete module.constructor._pathCache[cacheKey];
            }
        });
    };

    func.unrequire = (mod, context) => {
        mod = global.moduleNameFromPath(mod) || mod;
        context = func.resolve(context);
        const curr = global.moduleNameFromPath(context) || context,
            refs = referenceCounts[curr].refs,
            ind = refs.indexOf(mod);
        if (ind < 0) {
            LOG.silly(`mod: ${mod}\ncontext: ${context}\ncurr: ${curr}\nrefs: ${refs}`);
            throw new Error('You never required that!');
        }
        refs.splice(ind, 1);
        const uncache = [],
            queue = [mod];
        while (queue.length > 0) {
            const top = queue.shift();
            if (!referenceCounts[top]) {
                continue;
            }
            referenceCounts[top].count--;
            if (referenceCounts[top].count === 0) {
                queue.push.apply(queue, referenceCounts[top].refs);
                uncache.push.apply(uncache, referenceCounts[top].self);
                delete referenceCounts[top];
            }
        }

        for (let m of uncache) {
            func.uncache(m);
        }
    };

    func.once = (moduleName) => {
        process.emitWarning(`'require.once("${moduleName}")' is deprecated and will be removed soon. Please use 'require("${moduleName}")' instead.`);
        return func(moduleName);
    };

    func.safe = (moduleName) => {
        process.emitWarning(`'require.safe("${moduleName}")' is deprecated and will be removed soon. Please use 'require("${moduleName}")' instead.`);
        return func(moduleName);
    };

    return func;
};
<commit_msg>Quick fix<commit_after>/**
 * Extend CommonJS require to allow for installing (using NPM) and reloading of code
 * without doing a hard reset of the application.
 *
 * All of this code is a mess. Layers of hackery and bodges to get the desired end result.
 *
 * Written By:
 *         Matthew Knox
 *
 * License:
 *        MIT License. All code unless otherwise specified is
 *        Copyright (c) Matthew Knox and Contributors 2016.
 */

'use strict';

const npm = require(global.rootPathJoin('core/common/npm.js')),
    path = require('path'),
    fs = require('fs'),
    nativeReloadHacksCache = {},
    nativeReloadHacks = ['deasync'],
    npmFolder = 'node_modules',
    npmDirectory = global.rootPathJoin(npmFolder),
    npmLocalDirective = 'package.json',
    coreDirectory = global.rootPathJoin('core'),
    nativeModules = Object.getOwnPropertyNames(process.binding('natives')),
    common = {},
    referenceCounts = {};

// populate common, allows for require('concierge/*') to be redirected to the common directory
fs.readdirSync(global.rootPathJoin('core/common')).forEach(f => {
    common['concierge/' + path.parse(f).name] = global.rootPathJoin('core/common', f);
});

// force add node_modules to the path, so that installed NPM modules will be found regardless of module install location
let newPath = process.env.NODE_PATH || '';
if (newPath.length > 0) {
    newPath += /^win/.test(process.platform) ? ';' : ':';
}
newPath += global.rootPathJoin('node_modules');
process.env.NODE_PATH = newPath;
require('module').Module._initPaths();

/**
 * Translate a modules alias to its actual name/path.
 * @param {string} mod input name
 * @returns {string} actual name/path
 */
const getActualName = (mod) => common[mod] || mod;

/**
 * Detect if a code module needs to be installed through NPM without using any internal node
 * require mechanisms. This needs to be done manually, because newer versions of node cache resolve/require
 * lookups, which means that after they have been installed they will not be detected by using require().
 * @param {string} request the lookup to perform.
 * @param {string} dirName relative directory to perform the lookup from.
 * @returns {boolean} if the module has already been installed.
 */
const resolve = (request, dirName) => {
    if (nativeModules.includes(request)) {
        return true;
    }
    const parsed = path.parse(request);
    try {
        if (parsed.ext !== '' && parsed.ext !== '.' && parsed.dir !== '') {
            const p = path.resolve(dirName, request),
                file = fs.statSync(p);
            return file && (file.isFile() || file.isDirectory());
        }
        else {
            const npmDirs = [];
            const modName = global.moduleNameFromPath(dirName);
            if (modName === null) {
                npmDirs.push(path.join(dirName, npmFolder, request));
            }
            else {
                npmDirs.push(path.join(global.__modulesPath, modName, npmFolder, request));
            }
            if (global.__runAsLocal || dirName.startsWith(coreDirectory)) {
                npmDirs.push(path.join(npmDirectory, request));
            }
            for (let n of npmDirs) {
                try {
                    const dir = fs.statSync(n);
                    if (dir && dir.isDirectory()) {
                        return true;
                    }
                } catch (e2) {}
            }
            throw new Error();
        }
    }
    catch (e) {
        return parsed.dir.indexOf('.') === 0;
    }
};

/**
 * Determines if an NPM install should be performed in a modules directory.
 * @param {string} dirName the directory of the module
 * @param {string} name the name of the package that is being installed
 * @returns {boolean} if NPM install should be run on the directory.
 */
const shouldInstallLocally = (dirName, name) => {
    try {
        if (!global.__runAsLocal) {
            const mock = {
                dependencies: {}
            };
            mock.dependencies[name] = '*';
            return mock;
        }
        const p = path.join(dirName, npmLocalDirective),
            d = fs.statSync(p);
        if (dirName.startsWith(global.__modulesPath) && d.isFile()) {
            return JSON.parse(fs.readFileSync(p));
        }
        return false;
    }
    catch (e) {
        return false;
    }
};

/**
 * Requires a module, installing it first from NPM if necessary.
 * @param {function()} req origional require function
 * @param {string} name name of the module to install
 * @param {string} dirName relative directory to require from
 */
const installAndRequire = (req, name, dirName) => {
    const v = resolve(name, dirName);
    if (!v) {
        const local = shouldInstallLocally(dirName, name);
        let cwd = global.__rootPath,
            npmName = name;
        if (local) {
            cwd = dirName;
            const ver = local.dependencies[name] || local.devDependencies[name];
            npmName = `${name}@${ver}`;
        }
        const t = global.$$, // translations might not have loaded
            startStr = t ? t`Installing "${npmName}" from npm.` : `Installing "${npmName}" from npm.`,
            endStr = t ? t`Installation complete.` : 'Installation complete.';

        console.info(startStr);
        npm.install([npmName], cwd);
        console.info(endStr);
    }
    else if (nativeReloadHacksCache.hasOwnProperty(name)) {
        return nativeReloadHacksCache[name];
    }
    const r = req(name);
    if (nativeReloadHacks.includes(name) || name.endsWith('.node')) {
        nativeReloadHacksCache[name] = r;
    }
    return r;
};

/**
 * A global require replacement hook. This is used to allow code-hotswapping,
 * runtime dependency installation and injection. When called, this will generate a
 * new require function that should be used within a module.
 * @param {function()} req origional require function of the module.
 * @param {string} dirName __dirname of the module
 * @param {string} fileName __filename of the module
 * @returns {function()} a replacement require function for the module.
 */
module.exports = (req, dirName, fileName) => {
    const moduleDirName = global.moduleNameFromPath(dirName) || fileName;
    if (!referenceCounts.hasOwnProperty(moduleDirName)) {
        referenceCounts[moduleDirName] = {
            self: [fileName],
            count: 'Ignore',
            refs: []
        };
    }

    const func = (mod) => {
        mod = getActualName(mod);
        const res = installAndRequire(req, mod, dirName),
            p = req.resolve(mod),
            refName = global.moduleNameFromPath(p) || p;
        if (!p.contains(npmDirectory) && !nativeModules.includes(mod)) {
            if (!referenceCounts.hasOwnProperty(refName)) {
                referenceCounts[refName] = {
                    self: [],
                    count: 0,
                    refs: []
                };
            }

            if (!referenceCounts[refName].self.includes(p)) {
                referenceCounts[refName].self.push(p);
            }

            if (refName !== moduleDirName && !referenceCounts[moduleDirName].refs.includes(refName)) {
                if (typeof (referenceCounts[refName].count) === 'string') {
                    referenceCounts[refName].count = 0;
                }
                referenceCounts[refName].count++;
                referenceCounts[moduleDirName].refs.push(refName);
            }
        }
        return res;
    };
    for (let key in req) {
        func[key] = req[key];
    }

    func.forcePackageInstall = dir => {
        const locally = shouldInstallLocally(dir);
        let stat = null;
        try {
            stat = fs.statSync(path.join(dir, npmFolder));
        }
        catch (e) {}
        try {
            if (global.__runAsLocal && !stat && locally && Object.keys(locally.dependencies).length > 0) {
                npm.install(Object.keys(locally.dependencies).map(d => `${d}@${locally.dependencies[d]}`), dir);
            }
        }
        catch (e) {} // ignoring is an ugly solution, but this code *must not fail*
    };

    func.searchCache = (moduleName, callback) => {
        moduleName = getActualName(moduleName);
        let mod = func.resolve(moduleName);
        if (mod && (typeof (mod = func.cache[mod]) !== 'undefined')) {
            const run = (m) => {
                m.children.forEach((child) => {
                    run(child);
                });
                callback(mod);
            };
            run(mod);
        }
    };

    func.uncache = (moduleName) => {
        moduleName = getActualName(moduleName);
        func.searchCache(moduleName, (mod) => {
            delete func.cache[mod.id];
        });

        Object.keys(module.constructor._pathCache).forEach((cacheKey) => {
            if (cacheKey.indexOf(moduleName) > 0) {
                delete module.constructor._pathCache[cacheKey];
            }
        });
    };

    func.unrequire = (mod, context) => {
        mod = global.moduleNameFromPath(mod) || mod;
        context = func.resolve(context);
        const curr = global.moduleNameFromPath(context) || context,
            refs = referenceCounts[curr].refs,
            ind = refs.indexOf(mod);
        if (ind < 0) {
            LOG.silly(`mod: ${mod}\ncontext: ${context}\ncurr: ${curr}\nrefs: ${refs}`);
            throw new Error('You never required that!');
        }
        refs.splice(ind, 1);
        const uncache = [],
            queue = [mod];
        while (queue.length > 0) {
            const top = queue.shift();
            if (!referenceCounts[top]) {
                continue;
            }
            referenceCounts[top].count--;
            if (referenceCounts[top].count === 0) {
                queue.push.apply(queue, referenceCounts[top].refs);
                uncache.push.apply(uncache, referenceCounts[top].self);
                delete referenceCounts[top];
            }
        }

        for (let m of uncache) {
            func.uncache(m);
        }
    };

    func.once = (moduleName) => {
        process.emitWarning(`'require.once("${moduleName}")' is deprecated and will be removed soon. Please use 'require("${moduleName}")' instead.`);
        return func(moduleName);
    };

    func.safe = (moduleName) => {
        process.emitWarning(`'require.safe("${moduleName}")' is deprecated and will be removed soon. Please use 'require("${moduleName}")' instead.`);
        return func(moduleName);
    };

    return func;
};
<|endoftext|>


<commit_before>//     keymaster.js
//     (c) 2011 Thomas Fuchs
//     keymaster.js may be freely distributed under the MIT license.

;(function(global){
  var _handlers = {},
    _mods = { 16: false, 18: false, 17: false, 91: false },
    _scope = 'all',
    _MODIFIERS = {
      shift: 16, option: 18, '⌥': 18, alt: 18, ctrl: 17, control: 17, command: 91, '⌘': 91 },
    _MAP = {
      backspace: 8, tab: 9,
      enter: 13, 'return': 13,
      escape: 27, space: 32,
      left: 37, up: 38,
      right: 39, down: 40 };

  function dispatch(event){
    var key, tagName;
    tagName = event.srcElement.tagName;
    key = event.keyCode.toString();
    if(key in _mods) return _mods[key] = true;
    if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') return;
    if (!(key in _handlers)) return;
    _handlers[key].forEach(function(handler){
      if(handler.scope == _scope || handler.scope == 'all')
        if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) ||
          (handler.mods.length > 0 && handler.mods.every(function(mod){ return _mods[mod] })))
          handler.method(event, handler.key, handler.scope);
    });
  }

  function clearModifier(event){
    var key = event.keyCode.toString();
    if(key in _mods) _mods[key] = false;
  }

  function assignKey(key, scope, method){
    var keys, mods;
    if (method === undefined) {
      method = scope;
      scope = 'all';
    }
    key = key.replace(/\s/g,'');
    keys = key.split(',');
    keys.forEach(function(key){
      mods = [];
      key = key.split('+');
      if(key.length > 1){
        console.log(key);
        mods = key.slice(0,key.length-1).map(function(mod){ return _MODIFIERS[mod] });
        key = [key[key.length-1]];
        console.log(mods,key);
      }
      key = key[0]
      key = key.length > 1 ? _MAP[key] : key.toUpperCase().charCodeAt(0);
      if (!(key in _handlers)) _handlers[key] = [];
      _handlers[key].push({ scope: scope, method: method, mods: mods });
    });
  }

  function setScope(scope){ _scope = scope || 'all' }

  document.body.addEventListener('keydown', dispatch);
  document.body.addEventListener('keyup', clearModifier);
  global.key = assignKey;
  global.keyScope = setScope;
})(this);
<commit_msg>remove console.log calls<commit_after>//     keymaster.js
//     (c) 2011 Thomas Fuchs
//     keymaster.js may be freely distributed under the MIT license.

;(function(global){
  var _handlers = {},
    _mods = { 16: false, 18: false, 17: false, 91: false },
    _scope = 'all',
    _MODIFIERS = {
      shift: 16, option: 18, '⌥': 18, alt: 18, ctrl: 17, control: 17, command: 91, '⌘': 91 },
    _MAP = {
      backspace: 8, tab: 9,
      enter: 13, 'return': 13,
      escape: 27, space: 32,
      left: 37, up: 38,
      right: 39, down: 40 };

  function dispatch(event){
    var key, tagName;
    tagName = event.srcElement.tagName;
    key = event.keyCode.toString();
    if(key in _mods) return _mods[key] = true;
    if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') return;
    if (!(key in _handlers)) return;
    _handlers[key].forEach(function(handler){
      if(handler.scope == _scope || handler.scope == 'all')
        if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) ||
          (handler.mods.length > 0 && handler.mods.every(function(mod){ return _mods[mod] })))
          handler.method(event, handler.key, handler.scope);
    });
  }

  function clearModifier(event){
    var key = event.keyCode.toString();
    if(key in _mods) _mods[key] = false;
  }

  function assignKey(key, scope, method){
    var keys, mods;
    if (method === undefined) {
      method = scope;
      scope = 'all';
    }
    key = key.replace(/\s/g,'');
    keys = key.split(',');
    keys.forEach(function(key){
      mods = [];
      key = key.split('+');
      if(key.length > 1){
        mods = key.slice(0,key.length-1).map(function(mod){ return _MODIFIERS[mod] });
        key = [key[key.length-1]];
      }
      key = key[0]
      key = key.length > 1 ? _MAP[key] : key.toUpperCase().charCodeAt(0);
      if (!(key in _handlers)) _handlers[key] = [];
      _handlers[key].push({ scope: scope, method: method, mods: mods });
    });
  }

  function setScope(scope){ _scope = scope || 'all' }

  document.body.addEventListener('keydown', dispatch);
  document.body.addEventListener('keyup', clearModifier);
  global.key = assignKey;
  global.keyScope = setScope;
})(this);
<|endoftext|>


<commit_before>//app.js
App({
  onLaunch: function () {
    //调用API从本地缓存中获取数据
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)
  },
  getUserInfo:function(cb){
    var that = this
    if(this.globalData.userInfo){
      typeof cb == "function" && cb(this.globalData.userInfo)
    }else{
      //调用登录接口
wx.login({
  success: function (res) {
    //换取openid & session_key
    wx.request({
      url: 'https://sdk.weixin.senparc.com/WxOpen/OnLogin',
      method: 'POST',
      data: {
        code: res.code
      }
    })

    wx.getUserInfo({
      success: function (res) {
        console.log('get userinfo',res);
        that.globalData.userInfo = res.userInfo
        typeof cb == "function" && cb(that.globalData.userInfo)
      }
    })
  }
})
    }
  },
  globalData:{
    userInfo:null
  }
})<commit_msg>update demo<commit_after>//app.js
App({
  onLaunch: function () {
    //调用API从本地缓存中获取数据
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)
  },
  getUserInfo:function(cb){
    var that = this
    if(this.globalData.userInfo){
      typeof cb == "function" && cb(this.globalData.userInfo)
    }else{
      //调用登录接口
wx.login({
  success: function (res) {
    //换取openid & session_key
    wx.request({
      url: 'https://sdk.weixin.senparc.com/WxOpen/OnLogin',
      method: 'POST',
      data: {
        code: res.code
      },
      success:function(json){
        var result = json.data;
        if(result.success)
        {
          wx.setStorageSync('sessionId', result.sessionId);
          console.log('sessionId:',wx.getStorageSync('sessionId'));
        }else{
          console.log('储存session失败！',json);
        }
      }
    })

    wx.getUserInfo({
      success: function (res) {
        console.log('get userinfo',res);
        that.globalData.userInfo = res.userInfo
        typeof cb == "function" && cb(that.globalData.userInfo)
      }
    })
  }
})
    }
  },
  globalData:{
    userInfo:null
  }
})<|endoftext|>


<commit_before>/*jslint node: true*/
var googleSearch = function () {
    "use strict";
    this.title = function () {
        return browser.driver.getTitle();
    };
    this.searchTextBox = function (text) {
        $("input[name='q']").sendKeys(text);
    };
    this.searchButton = $("button[name='btnG']");
    this.clearText = function () {
        $("input[name='q']").clear();
    };
};
module.exports = googleSearch;<commit_msg>testing travis build<commit_after>/*jslint node: true*/
var googleSearch = function () {
    "use strict";
    this.title = function () {
        return browser.driver.getTitle();
    };
    this.searchTextBox = function (text) {
        $("input[name='q']").sendKeys(text);
    };
    this.searchButton = $("button[name='btnG']");
    this.clearText = function () {
        $("input[name='q1']").clear();
    };
};
module.exports = googleSearch;<|endoftext|>


<commit_before>window.TypeTester = {
    trimArray: function (array) {
        var out = [];
        var val;
        for (var i = 0; i < array.length; i++) {
            val = array[i].trim();
            if (typeof val != "string" || val.length > 0) {
                out.push(val);
            }
        }
        return out;
    },
    isOptional: function (argument) {
        return argument.indexOf("[optional]") == 0;
    },
    extractParameter: function (parameter) {
        var name, isOptional = false, type = "any", subtype = "any";

        if (parameter && parameter.indexOf("/*") == 0) {
            name = parameter.split("*/")[1].trim();
            type = parameter.split("/*")[1].split("*/")[0].trim();
            isOptional = this.isOptional(type);
            if (isOptional) {
                type = type.replace("[optional]", "").trim();
            }

            if (type.indexOf("array<") == 0) {
                subtype = type.split("<")[1].split(">")[0].trim();
                type = type.split("<")[0].trim();
            }
        } else {
            name = parameter;
        }

        return {
            name: name,
            optional: isOptional,
            type: type,
            subtype: subtype
        };
    },

    _consoleOut_: function (message) {
        console.warn(message);
    },
    _errorOut_: function (message) {
        throw new TypeError(message);
    },

    checkArgument: function (parameter, passed) {
        var actual = typeof(passed);

        var isCorrect = true;
        switch (parameter.type) {
            case "any":
                //Nothing to check
                break;
            case "int":
                isCorrect = actual == "number" && passed.toString().match(/^[0-9]+$/) !== null;
                break;
            case "float":
                isCorrect = actual == "number" && passed.toString().match(/^[0-9]+(\.[0-9])?$/) !== null;
                break;
            case "dom":
                isCorrect = actual == "object" && passed instanceof Node;
                break;
            case "array":
                isCorrect = passed instanceof Array || (/*required for node collections*/ passed && passed.length !== undefined);
                if (isCorrect) {
                    //Test type of elements in the array
                    for (var i = 0; i < passed.length; i++) {
                        this.checkArgument(
                            {
                                name: parameter.name,
                                optional: parameter.optional,
                                type: parameter.subtype,
                                subtype: "any"
                            },
                            passed[i]
                        );
                    }
                }
                break;
                case "unit":
					try{
						isCorrect = (""+this.size)===(""+parseInt(this.size));
					}catch(e){
						isCorrect = false;
					}
				break;
            default:
                isCorrect = parameter.type == actual;
        }
        if (!isCorrect) {
            this.fail("Wrong type given to parameter '" + parameter.name + "' in function '" + functionName + "'.\nExpected a '" + parameter.type + "' but got type " + actual + ":" + JSON.stringify(parentArguments[i], null, 4));
        }
    },
    _check_: function () {
        var fullFunction = arguments.callee.caller.toString();
        var functionName = fullFunction.replace(/\r*\n*/, "").split("(")[0].split("function")[1].trim();
        if (functionName == "") {
            //Anonymous function
            functionName = fullFunction.replace(/\r*\n*/, "").substring(0, 40);
        }

        var parameterPart = fullFunction.replace(/\r*\n*/, "").split("(")[1].split(")")[0];
        var parameters = parameterPart.split(",");
        parameters = this.trimArray(parameters);

        var parentArguments = arguments.callee.caller.arguments;

        if (parentArguments.length > parameters.length) {
            this.fail("Too many arguments passed to the function '" + functionName + "'.\nExpected " + parameters.length + " but got " + parentArguments.length)
        }

        var i, parameter;
        for (i = 0; i < parentArguments.length; i++) {
            parameter = this.extractParameter(parameters[i]);

            if (parentArguments[i] == undefined || parentArguments[i] == null) {
                if (!parameter.optional) {
                    this.fail("Missing required parameter '" + parameter.name + "' in function '" + functionName + "' at index " + i + " but got " + parentArguments[i]);
                }
            } else {
                this.checkArgument(parameter, parentArguments[i]);
            }
        }

        for (i = parentArguments.length; i < parameters.length; i++) {
            parameter = this.extractParameter(parameters[i]);
            if (!parameter.optional) {
                this.fail("Missing required parameter '" + parameter.name + "' in function '" + functionName + "' at index " + i);
            }
        }
    },

    //Enable type check
    on: function () {
        this.check = this._check_;
    },
    //Disable type check
    off: function () {
        this.check = function () {
        };
    },

    //Use console.warn() to output type errors
    warn: function () {
        this.fail = this._consoleOut_;
    },
    //Throw exception on type error
    except: function () {
        this.fail = this._errorOut_;
    }
};
window.TypeTester.on();
window.TypeTester.warn();
<commit_msg>Add a way to describe the function being tested<commit_after>window.TypeTester = {
    trimArray: function (array) {
        var out = [];
        var val;
        for (var i = 0; i < array.length; i++) {
            val = array[i].trim();
            if (typeof val != "string" || val.length > 0) {
                out.push(val);
            }
        }
        return out;
    },
    isOptional: function (argument) {
        return argument.indexOf("[optional]") == 0;
    },
    extractParameter: function (parameter) {
        var name, isOptional = false, type = "any", subtype = "any";

        if (parameter && parameter.indexOf("/*") == 0) {
            name = parameter.split("*/")[1].trim();
            type = parameter.split("/*")[1].split("*/")[0].trim();
            isOptional = this.isOptional(type);
            if (isOptional) {
                type = type.replace("[optional]", "").trim();
            }

            if (type.indexOf("array<") == 0) {
                subtype = type.split("<")[1].split(">")[0].trim();
                type = type.split("<")[0].trim();
            }
        } else {
            name = parameter;
        }

        return {
            name: name,
            optional: isOptional,
            type: type,
            subtype: subtype
        };
    },

    _consoleOut_: function (message) {
        console.warn(message);
    },
    _errorOut_: function (message) {
        throw new TypeError(message);
    },

    checkArgument: function (parameter, passed) {
        var actual = typeof(passed);

        var isCorrect = true;
        switch (parameter.type) {
            case "any":
                //Nothing to check
                break;
            case "int":
                isCorrect = actual == "number" && passed.toString().match(/^[0-9]+$/) !== null;
                break;
            case "float":
                isCorrect = actual == "number" && passed.toString().match(/^[0-9]+(\.[0-9])?$/) !== null;
                break;
            case "dom":
                isCorrect = actual == "object" && passed instanceof Node;
                break;
            case "array":
                isCorrect = passed instanceof Array || (/*required for node collections*/ passed && passed.length !== undefined);
                if (isCorrect) {
                    //Test type of elements in the array
                    for (var i = 0; i < passed.length; i++) {
                        this.checkArgument(
                            {
                                name: parameter.name,
                                optional: parameter.optional,
                                type: parameter.subtype,
                                subtype: "any"
                            },
                            passed[i]
                        );
                    }
                }
                break;
            case "unit":
                try {
                    isCorrect = ("" + this.size) === ("" + parseInt(this.size));
                } catch (e) {
                    isCorrect = false;
                }
                break;
            default:
                isCorrect = parameter.type == actual;
        }
        if (!isCorrect) {
            this.fail("Wrong type given to parameter '" + parameter.name + "' in function '" + functionName + "'.\nExpected a '" + parameter.type + "' but got type " + actual + ":" + JSON.stringify(parentArguments[i], null, 4));
        }
    },
    _check_: function (functionInfo) {
        var fullFunction = arguments.callee.caller.toString();
        var functionName = fullFunction.replace(/\r*\n*/, "").split("(")[0].split("function")[1].trim();
        if (functionName == "") {
            //Anonymous function
            functionName = fullFunction.replace(/\r*\n*/, "").substring(0, 40);
        }

        if (functionInfo !== undefined) {
            functionName = functionInfo + "--" + functionName;
        }

        var parameterPart = fullFunction.replace(/\r*\n*/, "").split("(")[1].split(")")[0];
        var parameters = parameterPart.split(",");
        parameters = this.trimArray(parameters);

        var parentArguments = arguments.callee.caller.arguments;

        if (parentArguments.length > parameters.length) {
            this.fail("Too many arguments passed to the function '" + functionName + "'.\nExpected " + parameters.length + " but got " + parentArguments.length)
        }

        var i, parameter;
        for (i = 0; i < parentArguments.length; i++) {
            parameter = this.extractParameter(parameters[i]);

            if (parentArguments[i] == undefined || parentArguments[i] == null) {
                if (!parameter.optional) {
                    this.fail("Missing required parameter '" + parameter.name + "' in function '" + functionName + "' at index " + i + " but got " + parentArguments[i]);
                }
            } else {
                this.checkArgument(parameter, parentArguments[i]);
            }
        }

        for (i = parentArguments.length; i < parameters.length; i++) {
            parameter = this.extractParameter(parameters[i]);
            if (!parameter.optional) {
                this.fail("Missing required parameter '" + parameter.name + "' in function '" + functionName + "' at index " + i);
            }
        }
    },

    //Enable type check
    on: function () {
        this.check = this._check_;
    },
    //Disable type check
    off: function () {
        this.check = function () {
        };
    },

    //Use console.warn() to output type errors
    warn: function () {
        this.fail = this._consoleOut_;
    },
    //Throw exception on type error
    except: function () {
        this.fail = this._errorOut_;
    }
};
window.TypeTester.on();
window.TypeTester.warn();
<|endoftext|>