<commit_before>import fs from 'fs'
import path from 'path'
import { readJson, writeJson } from 'fs-extra'
const R = require('ramda')
const parseCSV = require('csv-parser');
const extract = R.curry((p, ...ks) => R.pipe(
  R.match(p), R.ifElse(R.has('input'), R.tail, R.identity), R.zipObj(ks)))
const hasKeys = (...ks) => R.allPass(R.map(R.has)(ks))
const chainApplyToAll =
  (...fs) => (...args) => R.chain(f => R.apply(f)(args))(fs)

const integerFormat = {
  "maximumFractionDigits": 0
}
const currencyFormat = {
  "style": "currency",
  "currency": "AUD"
}
const sourceDetails = (filename, field) => {
  return {
    "name": "Australian Bureau of Statistics",
    "license": {
      "type": "Creative Commons Attribution 2.5 Australia",
      "url": "https://creativecommons.org/licenses/by/2.5/au/"
    },
    "notes": `from DataPack CSV: ${filename}#${field}`
  }
}

// orEmpty :: (*... → Boolean, *... → [Object]) => (*... → [Object])
const orEmpty = (predicate, action) => R.ifElse(
  predicate,
  action,
  R.always([])
)

const matchers = [
  (filename, field) => {
    const params =
      R.merge(
        extract(
          /(\d{4})Census_I04_AUST_(\w+)_long/,
          'year',
          'regionType')(filename),
        extract(
          /median_total_household_income_weekly_(.+)$/i,
          'suffix')(field));
    return orEmpty(
      hasKeys('year', 'regionType', 'suffix'),
      chainApplyToAll(
        orEmpty(
          R.pipe(R.prop('suffix'), R.test(/total$/i)),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_median_weekly_household_income_all`,
              "description": `Median weekly household income - all households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.pipe(R.prop('suffix'), R.test(/with_indigenous_persons$/i)),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_median_weekly_household_income_indigenous`,
              "description": `Median weekly household income - indigenous households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        )
      )
    )(params)
  },
  (filename, field) => {
    const params =
      R.merge(
        extract(
          /(\d{4})Census_I06_AUST_(\w+)_long/,
          'year',
          'regionType')(filename),
        extract(
          /^persons_(year_12|total)_.*(indigenous|total)$/i,
          'prefix',
          'suffix')(field));
    return orEmpty(
      hasKeys('year', 'regionType', 'prefix', 'suffix'),
      chainApplyToAll(
        orEmpty(
          R.where({
            'prefix': R.test(/^year_12$/i),
            'suffix': R.test(/^indigenous$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_year_12_educated_indigenous_persons`,
              "description": `Year 12 educated - indigenous persons (Census ${params.year})`,
              "type": "number",
              "format": integerFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'prefix': R.test(/^total$/i),
            'suffix': R.test(/^indigenous$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_total_possibly_educated_indigenous_persons`,
              "description": `Total possibly educated persons - indigenous persons (Census ${params.year})`,
              "type": "number",
              "format": integerFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'prefix': R.test(/^year_12$/i),
            'suffix': R.test(/^total$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_year_12_educated_all_persons`,
              "description": `Year 12 educated - all persons (Census ${params.year})`,
              "type": "number",
              "format": integerFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'prefix': R.test(/^total$/i),
            'suffix': R.test(/^total$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_total_possibly_educated_all_persons`,
              "description": `Total possibly educated persons - all persons (Census ${params.year})`,
              "type": "number",
              "format": integerFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        )
      )
    )(params)
  },
  (filename, field) => {
    const params =
      R.evolve(
        {
          suffix: v => /indigenous/i.test(v) ? 'indigenous' : 'all'
        },
        R.merge(
          extract(
            /(\d{4})Census_I13_AUST_(\w+)_long/,
            'year',
            'regionType')(filename),
          extract(
            /^(\d+|negative|partial|all)_(\d+|nil_income|or_more|income.*_stated)_(households_with_indigenous_persons|total_households)$/i,
            'low',
            'high',
            'suffix')(field)
        )
      )
    return orEmpty(
      hasKeys('year', 'regionType', 'low', 'high'),
      chainApplyToAll(
        orEmpty(
          R.where({
            'low': R.test(/^partial$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_partially_unstated_weekly_household_income_${params.suffix}`,
              "description": `Partially-unstated weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'low': R.test(/^all$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_completely_unstated_weekly_household_income_${params.suffix}`,
              "description": `Completely-unstated weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'high': R.test(/^\d+$/)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_${params.low}to${params.high}_weekly_household_income_${params.suffix}`,
              "description": `\$${params.low} - \$${params.high} weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'high': R.test(/more$/)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_${params.low}plus_weekly_household_income_${params.suffix}`,
              "description": `\$${params.low}+ weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'high': R.test(/^nil/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_0minus_weekly_household_income_${params.suffix}`,
              "description": `Nil or negative weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        )
      )
    )(params)
  },
  (filename, field) => {
    const params =
      R.evolve(
        {
          status: v => /indigenous/i.test(v) ? 'indigenous' : 'all'
        },
        R.merge(
          extract(
            /(\d{4})Census_I15._AUST_(\w+)_long/i,
            'year',
            'regionType')(filename),
          extract(
            /^persons_(indigenous|total)_(\d+)_(\d+)_years_(.*)$/i,
            'status',
            'low',
            'high',
            'qualification')(field)
        )
      )
    return orEmpty(
      hasKeys('year', 'regionType', 'status', 'low', 'high', 'qualification'),
      params => [{
        regionType: params.regionType.toLowerCase(),
        attribute: {
          "name": `census${params.year}_${params.status}_persons_${params.low}to${params.high}_${params.qualification}`,
          "description": `Education level of ${params.status} persons ${params.low}-${params.high} years - ${params.qualification.replace(/_/g,' ')} (Census ${params.year})`,
          "type": "number",
          "format": integerFormat,
          "source": sourceDetails(filename, field)
        }
      }]
    )(params)
  }
]

// readCSV :: String → String → Promise [Object]
const readCSV = regionColumnName => filepath => {
  return new Promise((resolve, reject) => {
    // TODO: make this more functional
    // (At least all the mutability is contained here.)
    const attributeRetreiver = field => {
      return R.apply(chainApplyToAll, matchers)(path.basename(filepath), field)
    }
    let attributeData = null
    const reader = fs.createReadStream(filepath)
    const parser = parseCSV({ columns: false })
    parser.on('readable', function() {
      let record;
      while (record = parser.read()) {
        if (!attributeData) {
          const fields = R.reject(R.equals(regionColumnName))(R.keys(record))
          attributeData = R.chain(field => {
            const attributes = attributeRetreiver(field)
            if (R.isEmpty(attributes)) {
              return []
            } else {
              return {
                field: field,
                regionAttributes: attributes,
                data: {}
              }
            }
          })(fields)
        }
        attributeData.forEach(obj => {
          var region = record[regionColumnName]
          var value = record[obj.field]
          obj.data[region] = value
        })
      }
    })
    // Catch any error
    parser.on('error', function(err) {
      reject(err)
    });
    // When we are done, test that the parsed output matched what expected
    parser.on('finish', function() {
      resolve(R.chain(obj => {
        return R.map(R.merge({ data: obj.data }))(obj.regionAttributes)
      })(attributeData))
    })
    reader.pipe(parser)
  })
}

const jsonOptions = {
  spaces: 2
}

// targetFile :: (String, String) → String
const targetFile = (regionType, filename) => path.resolve(
  __dirname, '..', 'data', 'public',
  regionType,
  filename
)

// indexTargetFile :: String → String
const indexTargetFile = regionType => targetFile(regionType, 'index.json')

// attributeDataTargetFile :: Object → String
const attributeDataTargetFile = regionAttribute => targetFile(
  regionAttribute.regionType,
  regionAttribute.attribute.name + '.json')

// writeAttributeToFile :: Object → Promise Object
const writeAttributeDataToFile = regionAttribute => {
  return writeJson(
      attributeDataTargetFile(regionAttribute),
      regionAttribute.data,
      jsonOptions
    ).then(R.always(regionAttribute))
}

// writeIndex :: (String, [Object]) → Promise [Object]
const writeIndex = (regionType, updatedAttributes) => {
  const modifyAttributes = (currentAttributes) => R.sortBy(
    R.prop('name'),
    R.unionWith(
      R.eqBy(R.prop('name')),
      updatedAttributes,
      currentAttributes)
  )
  const attrsLens = R.lensProp('attributes')
  const indexFile = indexTargetFile(regionType)
  R.pipeP(
    () => readJson(indexFile),
    R.over(attrsLens, modifyAttributes),
    R.curryN(3, writeJson)(indexFile, R.__, jsonOptions)
  )()
}

// combinedPromise :: (a → [Promise b]) → (a → Promise [b])
const combinedPromise = f => R.pipe(f, vs => Promise.all(vs))

// readCSVs :: [String] → Promise [Object]
const readCSVs =
  R.pipeP(
    combinedPromise(R.map(readCSV('region_id'))),
    R.unnest)

// writeAttributes :: [Object] → Promise [Object]
const writeAttributes = combinedPromise(R.map(writeAttributeDataToFile))

// writeIndexes :: [Object] -> Promise [Object]
const writeIndexes =
  R.pipe(
    R.groupBy(R.prop('regionType')), // [{k: v}]
    R.toPairs,                       // [[k, v]]
    R.map(
      R.over(
        R.lensIndex(1),
        R.map(R.prop('attribute'))
      )
    ),                               // [[k, v]]
    R.map(R.apply(writeIndex)),      // [Promise Object]
    combinedPromise                  // Promise [Object]
  )

// prog :: [String] → Promise [Object]
const prog =
  R.pipeP(
    readCSVs,
    writeAttributes,
    writeIndexes)

const argsIO = process.argv.slice(2)
prog(argsIO).catch(e => console.error(e))
<commit_msg>Add household size data matchers<commit_after>import fs from 'fs'
import path from 'path'
import { readJson, writeJson } from 'fs-extra'
const R = require('ramda')
const parseCSV = require('csv-parser');
const extract = R.curry((p, ...ks) => R.pipe(
  R.match(p), R.ifElse(R.has('input'), R.tail, R.identity), R.zipObj(ks)))
const hasKeys = (...ks) => R.allPass(R.map(R.has)(ks))
const chainApplyToAll =
  (...fs) => (...args) => R.chain(f => R.apply(f)(args))(fs)

const integerFormat = {
  "maximumFractionDigits": 0
}
const currencyFormat = {
  "style": "currency",
  "currency": "AUD"
}
const sourceDetails = (filename, field) => {
  return {
    "name": "Australian Bureau of Statistics",
    "license": {
      "type": "Creative Commons Attribution 2.5 Australia",
      "url": "https://creativecommons.org/licenses/by/2.5/au/"
    },
    "notes": `from DataPack CSV: ${filename}#${field}`
  }
}

// orEmpty :: (*... → Boolean, *... → [Object]) => (*... → [Object])
const orEmpty = (predicate, action) => R.ifElse(
  predicate,
  action,
  R.always([])
)

const matchers = [
  (filename, field) => {
    const params =
      R.merge(
        extract(
          /(\d{4})Census_I04_AUST_(\w+)_long/,
          'year',
          'regionType')(filename),
        extract(
          /median_total_household_income_weekly_(.+)$/i,
          'suffix')(field));
    return orEmpty(
      hasKeys('year', 'regionType', 'suffix'),
      chainApplyToAll(
        orEmpty(
          R.pipe(R.prop('suffix'), R.test(/total$/i)),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_median_weekly_household_income_all`,
              "description": `Median weekly household income - all households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.pipe(R.prop('suffix'), R.test(/with_indigenous_persons$/i)),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_median_weekly_household_income_indigenous`,
              "description": `Median weekly household income - indigenous households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        )
      )
    )(params)
  },
  (filename, field) => {
    const params =
      R.merge(
        extract(
          /(\d{4})Census_I06_AUST_(\w+)_long/,
          'year',
          'regionType')(filename),
        extract(
          /^persons_(year_12|total)_.*(indigenous|total)$/i,
          'prefix',
          'suffix')(field));
    return orEmpty(
      hasKeys('year', 'regionType', 'prefix', 'suffix'),
      chainApplyToAll(
        orEmpty(
          R.where({
            'prefix': R.test(/^year_12$/i),
            'suffix': R.test(/^indigenous$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_year_12_educated_indigenous_persons`,
              "description": `Year 12 educated - indigenous persons (Census ${params.year})`,
              "type": "number",
              "format": integerFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'prefix': R.test(/^total$/i),
            'suffix': R.test(/^indigenous$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_total_possibly_educated_indigenous_persons`,
              "description": `Total possibly educated persons - indigenous persons (Census ${params.year})`,
              "type": "number",
              "format": integerFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'prefix': R.test(/^year_12$/i),
            'suffix': R.test(/^total$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_year_12_educated_all_persons`,
              "description": `Year 12 educated - all persons (Census ${params.year})`,
              "type": "number",
              "format": integerFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'prefix': R.test(/^total$/i),
            'suffix': R.test(/^total$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_total_possibly_educated_all_persons`,
              "description": `Total possibly educated persons - all persons (Census ${params.year})`,
              "type": "number",
              "format": integerFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        )
      )
    )(params)
  },
  (filename, field) => {
    const params =
      R.evolve(
        {
          suffix: v => /indigenous/i.test(v) ? 'indigenous' : 'all'
        },
        R.merge(
          extract(
            /(\d{4})Census_I13_AUST_(\w+)_long/,
            'year',
            'regionType')(filename),
          extract(
            /^(\d+|negative|partial|all)_(\d+|nil_income|or_more|income.*_stated)_(households_with_indigenous_persons|total_households)$/i,
            'low',
            'high',
            'suffix')(field)
        )
      )
    return orEmpty(
      hasKeys('year', 'regionType', 'low', 'high'),
      chainApplyToAll(
        orEmpty(
          R.where({
            'low': R.test(/^partial$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_partially_unstated_weekly_household_income_${params.suffix}`,
              "description": `Partially-unstated weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'low': R.test(/^all$/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_completely_unstated_weekly_household_income_${params.suffix}`,
              "description": `Completely-unstated weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'high': R.test(/^\d+$/)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_${params.low}to${params.high}_weekly_household_income_${params.suffix}`,
              "description": `\$${params.low} - \$${params.high} weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'high': R.test(/more$/)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_${params.low}plus_weekly_household_income_${params.suffix}`,
              "description": `\$${params.low}+ weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        ),
        orEmpty(
          R.where({
            'high': R.test(/^nil/i)
          }),
          params => [{
            regionType: params.regionType.toLowerCase(),
            attribute: {
              "name": `census${params.year}_0minus_weekly_household_income_${params.suffix}`,
              "description": `Nil or negative weekly household income - ${params.suffix} households (Census ${params.year})`,
              "type": "number",
              "format": currencyFormat,
              "source": sourceDetails(filename, field)
            }
          }]
        )
      )
    )(params)
  },
  (filename, field) => {
    const params =
      R.evolve(
        {
          status: v => /indigenous/i.test(v) ? 'indigenous' : 'all'
        },
        R.merge(
          extract(
            /(\d{4})Census_I15._AUST_(\w+)_long/i,
            'year',
            'regionType')(filename),
          extract(
            /^persons_(indigenous|total)_(\d+)_(\d+)_years_(.*)$/i,
            'status',
            'low',
            'high',
            'qualification')(field)
        )
      )
    return orEmpty(
      hasKeys('year', 'regionType', 'status', 'low', 'high', 'qualification'),
      params => [{
        regionType: params.regionType.toLowerCase(),
        attribute: {
          "name": `census${params.year}_${params.status}_persons_${params.low}to${params.high}_${params.qualification}`.toLowerCase(),
          "description": `Education level of ${params.status} persons ${params.low}-${params.high} years - ${params.qualification.replace(/_/g,' ')} (Census ${params.year})`,
          "type": "number",
          "format": integerFormat,
          "source": sourceDetails(filename, field)
        }
      }]
    )(params)
  },
  (filename, field) => {
    const params =
      R.evolve(
        {
          status: v => /indigenous/i.test(v) ? 'indigenous' : 'all'
        },
        R.merge(
          extract(
            /(\d{4})Census_I04_AUST_(\w+)_long/i,
            'year',
            'regionType')(filename),
          extract(
            /^(average_number_of_persons_per_bedroom|average_household_size)_(indigenous|total)/i,
            'statistic',
            'status')(field)
        )
      )
    return orEmpty(
      hasKeys('year', 'regionType', 'statistic', 'status'),
      params => [{
        regionType: params.regionType.toLowerCase(),
        attribute: {
          "name": `census${params.year}_${params.statistic}_${params.status}`.toLowerCase(),
          "description": `${params.statistic.replace(/_/g,' ')} - ${params.status} (Census ${params.year})`,
          "type": "number",
          "format": {
            "maximumFractionDigits": 1
          },
          "source": sourceDetails(filename, field)
        }
      }]
    )(params)
  }
]

// readCSV :: String → String → Promise [Object]
const readCSV = regionColumnName => filepath => {
  return new Promise((resolve, reject) => {
    // TODO: make this more functional
    // (At least all the mutability is contained here.)
    const attributeRetreiver = field => {
      return R.apply(chainApplyToAll, matchers)(path.basename(filepath), field)
    }
    let attributeData = null
    const reader = fs.createReadStream(filepath)
    const parser = parseCSV({ columns: false })
    parser.on('readable', function() {
      let record;
      while (record = parser.read()) {
        if (!attributeData) {
          const fields = R.reject(R.equals(regionColumnName))(R.keys(record))
          attributeData = R.chain(field => {
            const attributes = attributeRetreiver(field)
            if (R.isEmpty(attributes)) {
              return []
            } else {
              return {
                field: field,
                regionAttributes: attributes,
                data: {}
              }
            }
          })(fields)
        }
        attributeData.forEach(obj => {
          var region = record[regionColumnName]
          var value = record[obj.field]
          obj.data[region] = value
        })
      }
    })
    // Catch any error
    parser.on('error', function(err) {
      reject(err)
    });
    // When we are done, test that the parsed output matched what expected
    parser.on('finish', function() {
      resolve(R.chain(obj => {
        return R.map(R.merge({ data: obj.data }))(obj.regionAttributes)
      })(attributeData))
    })
    reader.pipe(parser)
  })
}

const jsonOptions = {
  spaces: 2
}

// targetFile :: (String, String) → String
const targetFile = (regionType, filename) => path.resolve(
  __dirname, '..', 'data', 'public',
  regionType,
  filename
)

// indexTargetFile :: String → String
const indexTargetFile = regionType => targetFile(regionType, 'index.json')

// attributeDataTargetFile :: Object → String
const attributeDataTargetFile = regionAttribute => targetFile(
  regionAttribute.regionType,
  regionAttribute.attribute.name + '.json')

// writeAttributeToFile :: Object → Promise Object
const writeAttributeDataToFile = regionAttribute => {
  return writeJson(
      attributeDataTargetFile(regionAttribute),
      regionAttribute.data,
      jsonOptions
    ).then(R.always(regionAttribute))
}

// writeIndex :: (String, [Object]) → Promise [Object]
const writeIndex = (regionType, updatedAttributes) => {
  const modifyAttributes = (currentAttributes) => R.sortBy(
    R.prop('name'),
    R.unionWith(
      R.eqBy(R.prop('name')),
      updatedAttributes,
      currentAttributes)
  )
  const attrsLens = R.lensProp('attributes')
  const indexFile = indexTargetFile(regionType)
  R.pipeP(
    () => readJson(indexFile),
    R.over(attrsLens, modifyAttributes),
    R.curryN(3, writeJson)(indexFile, R.__, jsonOptions)
  )()
}

// combinedPromise :: (a → [Promise b]) → (a → Promise [b])
const combinedPromise = f => R.pipe(f, vs => Promise.all(vs))

// readCSVs :: [String] → Promise [Object]
const readCSVs =
  R.pipeP(
    combinedPromise(R.map(readCSV('region_id'))),
    R.unnest)

// writeAttributes :: [Object] → Promise [Object]
const writeAttributes = combinedPromise(R.map(writeAttributeDataToFile))

// writeIndexes :: [Object] -> Promise [Object]
const writeIndexes =
  R.pipe(
    R.groupBy(R.prop('regionType')), // [{k: v}]
    R.toPairs,                       // [[k, v]]
    R.map(
      R.over(
        R.lensIndex(1),
        R.map(R.prop('attribute'))
      )
    ),                               // [[k, v]]
    R.map(R.apply(writeIndex)),      // [Promise Object]
    combinedPromise                  // Promise [Object]
  )

// prog :: [String] → Promise [Object]
const prog =
  R.pipeP(
    readCSVs,
    writeAttributes,
    writeIndexes)

const argsIO = process.argv.slice(2)
prog(argsIO).catch(e => console.error(e))
<|endoftext|>


<commit_before>var cooking = require('cooking');
var path = require('path');
var md = require('markdown-it')();
var Components = require('../components.json');

cooking.set({
  entry: {
    app: './examples/entry.js',
    vendor: ['vue', 'vue-router']
  },
  dist: './examples/element-ui/',
  template: './examples/index.template.html',

  publicPath: '/element-ui/',
  hash: true,
  devServer: {
    port: 8085,
    log: false,
    publicPath: '/'
  },
  chunk: 'vendor',
  extractCSS: true,
  extends: {
    vue: true,
    lint: true,
    saladcss: {
      browser: ['ie > 8', 'last 2 version'],
      features: {
        'bem': {
          'shortcuts': {
            'component': 'b',
            'modifier': 'm',
            'descendent': 'e'
          },
          'separators': {
            'descendent': '__',
            'modifier': '--'
          }
        }
      }
    }
  }
});

cooking.add('loader.md', {
  test: /\.md$/,
  loader: 'vue-markdown-loader'
});

cooking.add('vueMarkdown', {
  use: [
    [require('markdown-it-toc-and-anchor').default, {
      anchorLinkSymbol: '',
      anchorClassName: 'anchor'
    }],
    [require('markdown-it-container'), 'code', {
      validate: function(params) {
        return params.trim().match(/^code\s+(.*)$/);
      },

      render: function (tokens, idx) {
        var m = tokens[idx].info.trim().match(/^code\s+(.*)$/);

        if (tokens[idx].nesting === 1) {
          // opening tag
          return '
' +
                    `<div class="summary">${md.utils.escapeHtml(m[1])}</div>` +
                  '
';

        } else {
          // closing tag
          return '

\n';
        }
      }
    }]
  ],
  preprocess: function (MarkdownIt, source) {
    MarkdownIt.renderer.rules.table_open = function () {
      return '
';
    };
    MarkdownIt.renderer.rules.fence = wrap(MarkdownIt.renderer.rules.fence);
    return source;
  }
});

var wrap = function (render) {
  return function () {
    return render.apply(this, arguments)
      .replace('<code class="', '<code class="hljs ')
      .replace('', '')
  };
};

cooking.add('resolve.alias', {
  'main': path.join(__dirname, '../src'),
  'packages': path.join(__dirname, '../packages'),
  'examples': path.join(__dirname, '../examples')
});

var externals = {};
Object.keys(Components).forEach(function (key) {
  externals[`packages/${key}/style.css`] = 'null';
});

// 开发模式不需要将不存在的 style.css 打包进去
cooking.add('externals', externals);

// cooking.config.vue.loaders.html = 'html?minimize=true&conservativeCollapse=false';

module.exports = cooking.resolve();
<commit_msg>update markdown config<commit_after>var cooking = require('cooking');
var path = require('path');
var md = require('markdown-it')();
var Components = require('../components.json');

cooking.set({
  entry: {
    app: './examples/entry.js',
    vendor: ['vue', 'vue-router']
  },
  dist: './examples/element-ui/',
  template: './examples/index.template.html',

  publicPath: '/element-ui/',
  hash: true,
  devServer: {
    port: 8085,
    log: false,
    publicPath: '/'
  },
  chunk: 'vendor',
  extractCSS: true,
  extends: {
    vue: true,
    lint: true,
    saladcss: {
      browser: ['ie > 8', 'last 2 version'],
      features: {
        'bem': {
          'shortcuts': {
            'component': 'b',
            'modifier': 'm',
            'descendent': 'e'
          },
          'separators': {
            'descendent': '__',
            'modifier': '--'
          }
        }
      }
    }
  }
});

cooking.add('loader.md', {
  test: /\.md$/,
  loader: 'vue-markdown-loader'
});

cooking.add('vueMarkdown', {
  use: [
    [require('markdown-it-toc-and-anchor').default, {
      anchorLinkSymbol: '',
      anchorClassName: 'anchor'
    }],
    [require('markdown-it-container'), 'demo', {
      validate: function(params) {
        return params.trim().match(/^demo\s+(.*)$/);
      },

      render: function (tokens, idx) {
        var m = tokens[idx].info.trim().match(/^demo\s+(.*)$/);
        if (tokens[idx].nesting === 1) {
          var html = tokens[idx + 1].content;
          return `

${html}

${md.utils.escapeHtml(m[1])}

`;
        }
        return '
\n';
      }
    }]
  ],
  preprocess: function (MarkdownIt, source) {
    MarkdownIt.renderer.rules.table_open = function () {
      return '
';
    };
    MarkdownIt.renderer.rules.fence = wrap(MarkdownIt.renderer.rules.fence);
    return source;
  }
});

var wrap = function (render) {
  return function () {
    return render.apply(this, arguments)
      .replace('<code class="', '<code class="hljs ')
      .replace('', '')
  };
};

cooking.add('resolve.alias', {
  'main': path.join(__dirname, '../src'),
  'packages': path.join(__dirname, '../packages'),
  'examples': path.join(__dirname, '../examples')
});

var externals = {};
Object.keys(Components).forEach(function (key) {
  externals[`packages/${key}/style.css`] = 'null';
});

// 开发模式不需要将不存在的 style.css 打包进去
cooking.add('externals', externals);

// cooking.config.vue.loaders.html = 'html?minimize=true&conservativeCollapse=false';

module.exports = cooking.resolve();
<|endoftext|>


<commit_before>/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Image,
  ListView,
  Text,
  View,
} = React;

var Swipeout = require('react-native-swipeout')

//  Button examples
var btnsDefault = [ { text: 'Button' } ]
var btnsTypes = [
  { text: 'Primary',    type: 'primary',   },
  { text: 'Secondary',  type: 'secondary', },
  { text: 'Delete',     type: 'delete',    }
]

var swipeoutExample = React.createClass({
  getInitialState: function() {
    return {
      dataSource: new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
      })
    }
  }
, componentWillMount: function() {
    this.updateDataSource([
      {
        text: "Basic Example",
        right: btnsDefault,
      }, {
        text: "onPress Callback",
        right: [
          {
            text: 'Press Me',
            onPress: function(){ alert('button pressed') },
            type: 'primary',
          }
        ],
      }, {
        text: "Button Types",
        right: btnsTypes,
      }, {
        text: "Button with custom styling",
        right: [
          {
            text: 'Button',
            backgroundColor: '#4fba8a',
            color: '#17807a',
          }
        ],
      }, {
        text: "Custom overswipe background color (drag me far)",
        right: btnsDefault,
        backgroundColor: '#006fff',
      }, {
        text: "Swipeout autoClose={true}",
        right: btnsDefault,
        autoClose: true,
      }, {
        text: "Five buttons (full-width) + autoClose={true}",
        right: [
          { text: 'One'},
          { text: 'Two'},
          { text: 'Three' },
          { text: 'Four' },
          { text: 'Five' }
        ],
        autoClose: true,
      }, {
        text: "Custom button component",
        right: [
          {
            component: <Image style={{flex: 1}} source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}/>
          }
        ],
      }, {
        text: "Swipe me right (buttons on left side)",
        left: btnsDefault,
      }, {
        text: "Buttons on both sides",
        left: btnsTypes,
        right: btnsTypes,
      }
    ])
  }
, handleScroll: function() {
  }
, updateDataSource: function(data) {
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(data)
    })
  }
, renderRow: function (item) {
    return <Swipeout
            autoClose={item.autoClose}
            backgroundColor={item.backgroundColor}
            left={item.left}
            right={item.right}>
              <View style={styles.li}>
                <Text style={styles.liText}>{item.text}</Text>
              </View>
            </Swipeout>
  }
, render: function() {
    return (
      <View style={styles.container}>
        <ListView
          dataSource={this.state.dataSource}
          onScroll={this.handleScroll}
          renderRow={this.renderRow}/>
      </View>
    );
  }
});

var Listitem = React.createClass({
  render: function() {
    return (
      <View style={styles.li}>
        <Text style={styles.liText}>{this.props.text}</Text>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    backgroundColor: '#f2f2f2',
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'center',
  },
  li: {
    backgroundColor: '#fff',
    borderBottomColor: '#eee',
    borderColor: 'transparent',
    borderWidth: 1,
    paddingLeft: 16,
    paddingTop: 14,
    paddingBottom: 16,
  },
  liContainer: {
    flex: 1,
  },
  liText: {
    color: '#333',
    fontSize: 16,
  },
});

AppRegistry.registerComponent('swipeoutExample', () => swipeoutExample);
<commit_msg>Add styling to example<commit_after>/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Image,
  ListView,
  StatusBarIOS,
  Text,
  View,
} = React;

var Swipeout = require('react-native-swipeout')

StatusBarIOS.setStyle(StatusBarIOS.Style['lightContent'])

//  Button examples
var btnsDefault = [ { text: 'Button' } ]
var btnsTypes = [
  { text: 'Primary',    type: 'primary',   },
  { text: 'Secondary',  type: 'secondary', },
  { text: 'Delete',     type: 'delete',    }
]

var swipeoutExample = React.createClass({
  getInitialState: function() {
    return {
      dataSource: new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
      })
    }
  }
, componentWillMount: function() {
    this.updateDataSource([
      {
        text: "Basic Example",
        right: btnsDefault,
      }, {
        text: "onPress Callback",
        right: [
          {
            text: 'Press Me',
            onPress: function(){ alert('button pressed') },
            type: 'primary',
          }
        ],
      }, {
        text: "Button Types",
        right: btnsTypes,
      }, {
        text: "Button with custom styling",
        right: [
          {
            text: 'Button',
            backgroundColor: '#4fba8a',
            color: '#17807a',
          }
        ],
      }, {
        text: "Overswipe background color (drag me far)",
        right: btnsDefault,
        backgroundColor: '#006fff',
      }, {
        text: "Swipeout autoClose={true}",
        right: btnsDefault,
        autoClose: true,
      }, {
        text: "Five buttons (full-width) + autoClose={true}",
        right: [
          { text: 'One'},
          { text: 'Two'},
          { text: 'Three' },
          { text: 'Four' },
          { text: 'Five' }
        ],
        autoClose: true,
      }, {
        text: "Custom button component",
        right: [
          {
            component: <Image style={{flex: 1}} source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}/>
          }
        ],
      }, {
        text: "Swipe me right (buttons on left side)",
        left: btnsDefault,
      }, {
        text: "Buttons on both sides",
        left: btnsTypes,
        right: btnsTypes,
      }
    ])
  }
, handleScroll: function() {
  }
, updateDataSource: function(data) {
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(data)
    })
  }
, renderRow: function (item) {
    return <Swipeout
            autoClose={item.autoClose}
            backgroundColor={item.backgroundColor}
            left={item.left}
            right={item.right}>
              <View style={styles.li}>
                <Text style={styles.liText}>{item.text}</Text>
              </View>
            </Swipeout>
  }
, render: function() {
    return (
      <View style={styles.container}>
        <View style={styles.statusbar}/>
        <View style={styles.navbar}><Text style={styles.navbarTitle}>Swipeout</Text></View>
        <ListView
          dataSource={this.state.dataSource}
          onScroll={this.handleScroll}
          renderRow={this.renderRow}
          style={styles.listview}/>
      </View>
    );
  }
});

var Listitem = React.createClass({
  render: function() {
    return (
      <View style={styles.li}>
        <Text style={styles.liText}>{this.props.text}</Text>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    backgroundColor: '#f2f2f2',
    flex: 1,
  },
  listview: {
    flex: 1,
  },
  li: {
    backgroundColor: '#fff',
    borderBottomColor: '#eee',
    borderColor: 'transparent',
    borderWidth: 1,
    paddingLeft: 16,
    paddingTop: 14,
    paddingBottom: 16,
  },
  liContainer: {
    flex: 2,
  },
  liText: {
    color: '#333',
    fontSize: 16,
  },
  navbar: {
    alignItems: 'center',
    backgroundColor: '#444',
    justifyContent: 'center',
    height: 44,
  },
  navbarTitle: {
    color: '#fff',
    fontSize: 16,
    fontWeight: "500",
  },
  statusbar: {
    backgroundColor: '#444',
    height: 22,
  }
});

AppRegistry.registerComponent('swipeoutExample', () => swipeoutExample);
<|endoftext|>


<commit_before>/*
 * Copyright 2009 Google Inc.
 *
 * 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.
 */
/**
 * @fileoverview Definitions for all iPhone extensions. Created from:
 * http://developer.apple.com/library/safari/navigation/
 *
 * @externs
 * @author agrieve@google.com (Andrew Grieve)
 */

/**
 * The Touch class represents a single touch on the surface. A touch is the
 * presence or movement of a finger that is part of a unique multi-touch
 * sequence.
 * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/TouchClassReference/Touch/Touch.html
 * @constructor
 */
function Touch() {}

/**
 * The x-coordinate of the touch's location relative to the window's viewport.
 * @type {number}
 */
Touch.prototype.clientX;

/**
 * The y-coordinate of the touch's location relative to the window's viewport.
 * @type {number}
 */
Touch.prototype.clientY;

/**
 * The unique identifier for this touch object.
 * @type {number}
 */
Touch.prototype.identifier;

/**
 * The x-coordinate of the touch's location in page coordinates.
 * @type {number}
 */
Touch.prototype.pageX;

/**
 * The y-coordinate of the touch's location in page coordinates.
 * @type {number}
 */
Touch.prototype.pageY;

/**
 * The x-coordinate of the touch's location in screen coordinates.
 * @type {number}
 */
Touch.prototype.screenX;

/**
 * The y-coordinate of the touch's location in screen coordinates.
 * @type {number}
 */
Touch.prototype.screenY;

/**
 * The target of this touch.
 * @type {EventTarget}
 */
Touch.prototype.target;

/**
 * The TouchList class is used to represent a collection of Touch objects.
 * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/TouchListClassReference/TouchList/TouchList.html
 * @constructor
 */
function TouchList() {}

/**
 * The number of Touch objects in this TouchList object.
 * @type {number}
 */
TouchList.prototype.length;

/**
 * Returns the Touch object at the given index.
 * @param {number} index
 * @return {!Touch}
 */
TouchList.prototype.item = function(index) {};

/**
 * The TouchEvent class encapsulates information about a touch event.
 *
 *
The system continually sends TouchEvent objects to an application as
 * fingers touch and move across a surface. A touch event provides a snapshot of
 * all touches during a multi-touch sequence, most importantly the touches that
 * are new or have changed for a particular target. A multi-touch sequence
 * begins when a finger first touches the surface. Other fingers may
 * subsequently touch the surface, and all fingers may move across the surface.
 * The sequence ends when the last of these fingers is lifted from the surface.
 * An application receives touch event objects during each phase of any touch.
 *

 *
 *
The different types of TouchEvent objects that can occur are:
 *

 *
touchstart - Sent when a finger for a given event touches the surface.
 *
touchmove - Sent when a given event moves on the surface.
 *
touchend - Sent when a given event lifts from the surface.
 *
touchcancel - Sent when the system cancels tracking for the touch.
 *

 * TouchEvent objects are combined together to form high-level GestureEvent
 * objects that are also sent during a multi-touch sequence.

 *
 * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/TouchEventClassReference/TouchEvent/TouchEvent.html
 * @extends {UIEvent}
 * @constructor
 */
function TouchEvent() {}

/**
 * A collection of Touch objects representing all touches associated with this
 * target.
 * @type {TouchList}
 */
TouchEvent.prototype.touches;

/**
 * A collection of Touch objects representing all touches associated with this
 * target.
 * @type {TouchList}
 */
TouchEvent.prototype.targetTouches;

/**
 * A collection of Touch objects representing all touches that changed in this event.
 * @type {TouchList}
 */
TouchEvent.prototype.changedTouches;

/**
 * The distance between two fingers since the start of an event as a multiplier
 * of the initial distance. The initial value is 1.0. If less than 1.0, the
 * gesture is pinch close (to zoom out). If greater than 1.0, the gesture is
 * pinch open (to zoom in).
 * @type {number}
 */
TouchEvent.prototype.scale;

/**
 * The delta rotation since the start of an event, in degrees, where clockwise
 * is positive and counter-clockwise is negative. The initial value is 0.0.
 * @type {number}
 */
TouchEvent.prototype.rotation;

/**
 * Initializes a newly created TouchEvent object.
 * @param {string} type
 * @param {boolean} canBubble
 * @param {boolean} cancelable
 * @param {Window} view
 * @param {number} detail
 * @param {number} screenX
 * @param {number} screenY
 * @param {number} clientX
 * @param {number} clientY
 * @param {boolean} ctrlKey
 * @param {boolean} altKey
 * @param {boolean} shiftKey
 * @param {boolean} metaKey
 * @param {TouchList} touches
 * @param {TouchList} targetTouches
 * @param {TouchList} changedTouches
 * @param {number} scale
 * @param {number} rotation
 */
TouchEvent.prototype.initTouchEvent = function(type, canBubble, cancelable,
    view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey,
    metaKey, touches, targetTouches, changedTouches, scale, rotation) {};

/**
 * Specifies the JavaScript method to invoke when the system cancels tracking
 * for the touch.
 * @param {Event} e
 */
Element.prototype.ontouchcancel = function(e) {};

/**
 * Specifies the JavaScript method to invoke when a given event lifts from the
 * surface.
 * @param {Event} e
 */
Element.prototype.ontouchend = function(e) {};

/**
 * Specifies the JavaScript method to invoke when a finger for a given event
 * moves on the surface.
 * @param {Event} e
 */
Element.prototype.ontouchmove = function(e) {};

/**
 * Specifies the JavaScript method to invoke when a finger for a given event
 * touches the surface.
 * @param {Event} e
 */
Element.prototype.ontouchstart = function(e) {};

/**
 * Specifies the JavaScript method to invoke when the browser device's
 * orientation changes, i.e.the device is rotated.
 * @param {Event} e
 * @see http://developer.apple.com/library/IOS/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
 */
Window.prototype.onorientationchange = function(e) {};

/**
 * Returns the orientation of the browser's device, one of [-90, 0, 90, 180].
 * @type {number}
 * @see http://developer.apple.com/library/IOS/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
 */
Window.prototype.orientation;

/**
 * @implicitCast
 * @type {boolean}
 */
HTMLInputElement.prototype.autocorrect;

/**
 * @implicitCast
 * @type {boolean}
 */
HTMLInputElement.prototype.autocapitalize;

/**
 * @implicitCast
 * @type {boolean}
 */
HTMLTextAreaElement.prototype.autocorrect;

/**
 * @implicitCast
 * @type {boolean}
 */
HTMLTextAreaElement.prototype.autocapitalize;
<commit_msg><commit_after>/*
 * Copyright 2009 Google Inc.
 *
 * 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.
 */
/**
 * @fileoverview Definitions for all iPhone extensions. Created from:
 * http://developer.apple.com/library/safari/navigation/
 *
 * @externs
 * @author agrieve@google.com (Andrew Grieve)
 */

/**
 * The Touch class represents a single touch on the surface. A touch is the
 * presence or movement of a finger that is part of a unique multi-touch
 * sequence.
 * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/TouchClassReference/Touch/Touch.html
 * @constructor
 */
function Touch() {}

/**
 * The x-coordinate of the touch's location relative to the window's viewport.
 * @type {number}
 */
Touch.prototype.clientX;

/**
 * The y-coordinate of the touch's location relative to the window's viewport.
 * @type {number}
 */
Touch.prototype.clientY;

/**
 * The unique identifier for this touch object.
 * @type {number}
 */
Touch.prototype.identifier;

/**
 * The x-coordinate of the touch's location in page coordinates.
 * @type {number}
 */
Touch.prototype.pageX;

/**
 * The y-coordinate of the touch's location in page coordinates.
 * @type {number}
 */
Touch.prototype.pageY;

/**
 * The x-coordinate of the touch's location in screen coordinates.
 * @type {number}
 */
Touch.prototype.screenX;

/**
 * The y-coordinate of the touch's location in screen coordinates.
 * @type {number}
 */
Touch.prototype.screenY;

/**
 * The target of this touch.
 * @type {EventTarget}
 */
Touch.prototype.target;

/**
 * The TouchList class is used to represent a collection of Touch objects.
 * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/TouchListClassReference/TouchList/TouchList.html
 * @constructor
 */
function TouchList() {}

/**
 * The number of Touch objects in this TouchList object.
 * @type {number}
 */
TouchList.prototype.length;

/**
 * Returns the Touch object at the given index.
 * @param {number} index
 * @return {!Touch}
 */
TouchList.prototype.item = function(index) {};

/**
 * The TouchEvent class encapsulates information about a touch event.
 *
 *
The system continually sends TouchEvent objects to an application as
 * fingers touch and move across a surface. A touch event provides a snapshot of
 * all touches during a multi-touch sequence, most importantly the touches that
 * are new or have changed for a particular target. A multi-touch sequence
 * begins when a finger first touches the surface. Other fingers may
 * subsequently touch the surface, and all fingers may move across the surface.
 * The sequence ends when the last of these fingers is lifted from the surface.
 * An application receives touch event objects during each phase of any touch.
 *

 *
 *
The different types of TouchEvent objects that can occur are:
 *

 *
touchstart - Sent when a finger for a given event touches the surface.
 *
touchmove - Sent when a given event moves on the surface.
 *
touchend - Sent when a given event lifts from the surface.
 *
touchcancel - Sent when the system cancels tracking for the touch.
 *

 * TouchEvent objects are combined together to form high-level GestureEvent
 * objects that are also sent during a multi-touch sequence.

 *
 * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/TouchEventClassReference/TouchEvent/TouchEvent.html
 * @extends {UIEvent}
 * @constructor
 */
function TouchEvent() {}

/**
 * A collection of Touch objects representing all touches associated with this
 * target.
 * @type {TouchList}
 */
TouchEvent.prototype.touches;

/**
 * A collection of Touch objects representing all touches associated with this
 * target.
 * @type {TouchList}
 */
TouchEvent.prototype.targetTouches;

/**
 * A collection of Touch objects representing all touches that changed in this event.
 * @type {TouchList}
 */
TouchEvent.prototype.changedTouches;

/**
 * The distance between two fingers since the start of an event as a multiplier
 * of the initial distance. The initial value is 1.0. If less than 1.0, the
 * gesture is pinch close (to zoom out). If greater than 1.0, the gesture is
 * pinch open (to zoom in).
 * @type {number}
 */
TouchEvent.prototype.scale;

/**
 * The delta rotation since the start of an event, in degrees, where clockwise
 * is positive and counter-clockwise is negative. The initial value is 0.0.
 * @type {number}
 */
TouchEvent.prototype.rotation;

/**
 * Initializes a newly created TouchEvent object.
 * @param {string} type
 * @param {boolean} canBubble
 * @param {boolean} cancelable
 * @param {Window} view
 * @param {number} detail
 * @param {number} screenX
 * @param {number} screenY
 * @param {number} clientX
 * @param {number} clientY
 * @param {boolean} ctrlKey
 * @param {boolean} altKey
 * @param {boolean} shiftKey
 * @param {boolean} metaKey
 * @param {TouchList} touches
 * @param {TouchList} targetTouches
 * @param {TouchList} changedTouches
 * @param {number} scale
 * @param {number} rotation
 */
TouchEvent.prototype.initTouchEvent = function(type, canBubble, cancelable,
    view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey,
    metaKey, touches, targetTouches, changedTouches, scale, rotation) {};

/**
 * The GestureEvent class encapsulates information about a multi-touch gesture.
 *
 * GestureEvent objects are high-level events that encapsulate the low-level
 * TouchEvent objects. Both GestureEvent and TouchEvent events are sent during
 * a multi-touch sequence. Gesture events contain scaling and rotation
 * information allowing gestures to be combined, if supported by the platform.
 * If not supported, one gesture ends before another starts. Listen for
 * GestureEvent events if you want to respond to gestures only, not process
 * the low-level TouchEvent objects.
 *
 * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html
 * @extends {UIEvent}
 * @constructor
 */
function GestureEvent() {}

/**
 * The distance between two fingers since the start of an event as a multiplier
 * of the initial distance. The initial value is 1.0. If less than 1.0, the
 * gesture is pinch close (to zoom out). If greater than 1.0, the gesture is
 * pinch open (to zoom in).
 * @type {number}
 */
GestureEvent.prototype.scale;

/**
 * The delta rotation since the start of an event, in degrees, where clockwise
 * is positive and counter-clockwise is negative. The initial value is 0.0.
 * @type {number}
 */
GestureEvent.prototype.rotation;

/**
 * The target of this gesture.
 * @type {EventTarget}
 */
GestureEvent.prototype.target;

/**
 * Initializes a newly created GestureEvent object.
 * @param {string} type
 * @param {boolean} canBubble
 * @param {boolean} cancelable
 * @param {Window} view
 * @param {number} detail
 * @param {number} screenX
 * @param {number} screenY
 * @param {number} clientX
 * @param {number} clientY
 * @param {boolean} ctrlKey
 * @param {boolean} altKey
 * @param {boolean} shiftKey
 * @param {boolean} metaKey
 * @param {EventTarget} target
 * @param {number} scale
 * @param {number} rotation
 */
GestureEvent.prototype.initGestureEvent = function(type, canBubble, cancelable,
    view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey,
    metaKey, target, scale, rotation) {};

/**
 * Specifies the JavaScript method to invoke when the system cancels tracking
 * for the touch.
 * @param {Event} e
 */
Element.prototype.ontouchcancel = function(e) {};

/**
 * Specifies the JavaScript method to invoke when a given event lifts from the
 * surface.
 * @param {Event} e
 */
Element.prototype.ontouchend = function(e) {};

/**
 * Specifies the JavaScript method to invoke when a finger for a given event
 * moves on the surface.
 * @param {Event} e
 */
Element.prototype.ontouchmove = function(e) {};

/**
 * Specifies the JavaScript method to invoke when a finger for a given event
 * touches the surface.
 * @param {Event} e
 */
Element.prototype.ontouchstart = function(e) {};

/**
 * Specifies the JavaScript method to invoke when a gesture is started by
 * two or more fingers touching the surface.
 * @param {Event} e
 */
Element.prototype.ongesturestart = function(e) {};

/**
 * Specifies the JavaScript method to invoke when fingers are moved during a
 * gesture.
 * @param {Event} e
 */
Element.prototype.ongesturechange = function(e) {};

/**
 * Specifies the JavaScript method to invoke when a gesture ends (when there are
 * 0 or 1 fingers touching the surface).
 * @param {Event} e
 */
Element.prototype.ongestureend = function(e) {};

/**
 * Specifies the JavaScript method to invoke when the browser device's
 * orientation changes, i.e.the device is rotated.
 * @param {Event} e
 * @see http://developer.apple.com/library/IOS/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
 */
Window.prototype.onorientationchange = function(e) {};

/**
 * Returns the orientation of the browser's device, one of [-90, 0, 90, 180].
 * @type {number}
 * @see http://developer.apple.com/library/IOS/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
 */
Window.prototype.orientation;

/**
 * @implicitCast
 * @type {boolean}
 */
HTMLInputElement.prototype.autocorrect;

/**
 * @implicitCast
 * @type {boolean}
 */
HTMLInputElement.prototype.autocapitalize;

/**
 * @implicitCast
 * @type {boolean}
 */
HTMLTextAreaElement.prototype.autocorrect;

/**
 * @implicitCast
 * @type {boolean}
 */
HTMLTextAreaElement.prototype.autocapitalize;
<|endoftext|>


<commit_before>var gulp = require('gulp'),
    gutil = require('gulp-util'),
    jscs = require('gulp-jscs'),
    jshint = require('gulp-jshint'),
    cached = require('gulp-cached'),
    coverage = require('gulp-jsx-coverage'),
    fs = require('fs'),
    React = require('react-tools'),
    through = require('through2'),
    buffer = require('vinyl-buffer'),
    source = require('vinyl-source-stream'),
    aliasify = require('aliasify'),
    browserify = require('browserify'),
    uglify = require('gulp-uglify'),
    nodemon = require('nodemon'),
    browserSync = require('browser-sync'),
    serverStarted = false,

// If you use vim and watch tasks be triggered 2 times when saving
// You can :set nowritebackup in vim to prevent this
// Reference: https://github.com/joyent/node/issues/3172
configs = {
    static_dir: 'static/',
    mainjs: require(process.cwd() + '/package.json').main,
    appjs: process.cwd() + '/fluxexapp.js',
    nodemon_restart_delay: 200,
    nodemon_delay: 2000,
    gulp_watch: {debounceDelay: 500},
    watchify: {debug: true, delay: 500},
    jshint_jsx: {quotmark: false},
    jshint_fail: false,
    jscs_fail: false,
    aliasify: {
        aliases: {
            request: 'browser-request'
        }
    },
    test_coverage: {
        default: {
            src: ['test/**/*.js', 'test/components/*.jsx', 'test/components/*.js'],
            istanbul: {
                coverageVariable: '__FLUXEX_COVERAGE__',
                exclude: /node_modules\/|test\//
            },
            coverage: {
                directory: 'coverage'
            },
            mocha: {},
            react: {
                sourceMap: true
            },
            coffee: {
                sourceMap: true
            }
        },
        console: {
            coverage: {
                reporters: ['text-summary']
            },
            mocha: {
                reporter: 'spec'
            }
        },
        report: {
            coverage: {
                reporters: ['lcov', 'json']
            },
            mocha: {
                reporter: 'tap'
            }
        }
    }
},

build_files = {
    js: ['actions/*.js', 'stores/*.js'],
    jsx: ['components/*.jsx']
},

restart_nodemon = function () {
    setTimeout(function () {
        nodemon.emit('restart');
    }, configs.nodemon_restart_delay);
},

lint_chain = function (task) {
    task = task.pipe(jshint.reporter('jshint-stylish'));

    if (configs.github) {
        task = task.pipe(require('gulp-github')(configs.github));
    }

    if (configs.jshint_fail) {
        task = task.pipe(('object' === typeof configs.jshint_fail) ? configs.jshint_fail : jshint.reporter('fail'));
    }

    return task;
},

// Stop using gulp-react because it do not keep original file name
react_compiler = function (options) {
    return through.obj(function (file, enc, callback) {
        try {
            file.contents = new Buffer(React.transform(file.contents.toString(), options));
            this.push(file);
        } catch (E) {
            gutil.log('[jsx ERROR]', gutil.colors.red(file.path));
            gutil.log('[jsx ERROR]', gutil.colors.red(E.message));
        }
        callback();
    });
},

// Do testing tasks
get_testing_task = function (options) {
    var cfg = JSON.parse(JSON.stringify(configs.test_coverage.default));

    cfg.istanbul.exclude = configs.test_coverage.default.istanbul.exclude;
    cfg.coverage.reporters = options.coverage.reporters;
    cfg.mocha = options.mocha;
    cfg.cleanup = configs.test_coverage.default.cleanup;

    return coverage.createTask(cfg);
},

handleJSCSError = function (E) {
    if (!configs.jscs_fail) {
        return;
    }

    if ('function' === typeof configs.jscs_fail) {
        return configs.jscs_fail(E);
    }

    this.emit('error', E);
},

bundleAll = function (b, noSave) {
    var B = b.bundle()
    .on('error', function (E) {
        gutil.log('[browserify ERROR]', gutil.colors.red(E));
    });

    if (!noSave) {
        B.pipe(source('main.js'))
        .pipe(gulp.dest(configs.static_dir + 'js/'))
        .on('end', restart_nodemon);
    }

    return B;
},

buildApp = function (watch, fullpath, nosave, disc) {
    var b = browserify(configs.appjs, {
        cache: {},
        packageCache: {},
        require: (disc ? process.cwd() : '.') + '/components/Html.jsx',
        standalone: 'Fluxex',
        fullPaths: fullpath ? true: false,
        debug: watch
    });

    b.transform('reactify');
    b.transform(aliasify.configure(configs.aliasify), {global: true});

    if (watch) {
        b = require('watchify')(b, configs.watchify);
        b.on('update', function (F) {
            gutil.log('[browserify] ' + F[0] + ' updated');
            bundleAll(b);
        });
    }

    return bundleAll(b, nosave);
};

gulp.task('build_app', function () {
    return buildApp(false, false, true)
        .pipe(source('main.js'))
        .pipe(buffer())
        .pipe(uglify())
        .pipe(gulp.dest(configs.static_dir + 'js/'));
});

gulp.task('disc_app', function () {
    return buildApp(false, true, true, true)
        .pipe(require('disc')())
        .pipe(fs.createWriteStream(configs.static_dir + 'disc.html'));
});

gulp.task('watch_app', function () {
    return buildApp(true, true);
});

gulp.task('watch_flux_js', ['lint_flux_js'], function () {
    gulp.watch(build_files.js, configs.gulp_watch, ['lint_flux_js']);
});

gulp.task('lint_flux_js', function () {
    return lint_chain(
        gulp.src(build_files.js)
        .pipe(cached('jshint'))
        .pipe(jscs()).on('error', handleJSCSError)
        .pipe(jshint())
    );
});

gulp.task('watch_jsx', ['lint_jsx'], function () {
    gulp.watch(build_files.jsx, configs.gulp_watch, ['lint_jsx']);
});

gulp.task('lint_jsx', function () {
    return lint_chain(
        gulp.src(build_files.jsx)
        .pipe(cached('jshint'))
        .pipe(react_compiler({sourceMap: true}))
        .pipe(jscs()).on('error', handleJSCSError)
        .pipe(jshint(configs.jshint_jsx))
    );
});

gulp.task('watch_server', ['lint_server'], function () {
    gulp.watch(configs.mainjs, configs.gulp_watch, ['lint_server'])
    .on('change', function () {
        nodemon.emit('restart');
    });
});

gulp.task('lint_server', function () {
    return gulp.src(configs.mainjs)
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('nodemon_server', ['watch_flux_js', 'watch_jsx', 'watch_app', 'watch_server'], function () {
    nodemon({
        ignore: '*',
        script: configs.mainjs,
        ext: 'do_not_watch'
    })
    .on('log', function (log) {
        gutil.log(log.colour);
    })
    .on('start', function () {
        if (serverStarted) {
            setTimeout(browserSync.reload, configs.nodemon_delay);
        } else {
            browserSync.init(null, {
                proxy: 'http://localhost:3000',
                files: [configs.static_dir + 'css/*.css'],
                port: 3001,
                online: false,
                open: false
            });

            serverStarted = true;
        }
    });
});

gulp.task('watch_tests', ['test_app'], function () {
    gulp.watch(configs.test_coverage.default.src, ['test_app']);
});
gulp.task('test_app', function () {
    return get_testing_task(configs.test_coverage.console)();
});
gulp.task('save_test_app', function () {
    return get_testing_task(configs.test_coverage.report)();
});
gulp.task('develop', ['nodemon_server']);
gulp.task('lint_all', ['lint_server', 'lint_flux_js', 'lint_jsx']);
gulp.task('buildall', ['lint_all', 'build_app']);
gulp.task('default',['buildall']);

module.exports = configs;
<commit_msg>watch more<commit_after>var gulp = require('gulp'),
    gutil = require('gulp-util'),
    jscs = require('gulp-jscs'),
    jshint = require('gulp-jshint'),
    cached = require('gulp-cached'),
    coverage = require('gulp-jsx-coverage'),
    fs = require('fs'),
    React = require('react-tools'),
    through = require('through2'),
    buffer = require('vinyl-buffer'),
    source = require('vinyl-source-stream'),
    aliasify = require('aliasify'),
    browserify = require('browserify'),
    uglify = require('gulp-uglify'),
    nodemon = require('nodemon'),
    browserSync = require('browser-sync'),
    serverStarted = false,

// If you use vim and watch tasks be triggered 2 times when saving
// You can :set nowritebackup in vim to prevent this
// Reference: https://github.com/joyent/node/issues/3172
configs = {
    static_dir: 'static/',
    mainjs: require(process.cwd() + '/package.json').main,
    appjs: process.cwd() + '/fluxexapp.js',
    nodemon_restart_delay: 200,
    nodemon_delay: 2000,
    gulp_watch: {debounceDelay: 500},
    watchify: {debug: true, delay: 500},
    jshint_jsx: {quotmark: false},
    jshint_fail: false,
    jscs_fail: false,
    aliasify: {
        aliases: {
            request: 'browser-request'
        }
    },
    test_coverage: {
        default: {
            src: ['test/**/*.js', 'test/components/*.jsx', 'test/components/*.js'],
            istanbul: {
                coverageVariable: '__FLUXEX_COVERAGE__',
                exclude: /node_modules\/|test\//
            },
            coverage: {
                directory: 'coverage'
            },
            mocha: {},
            react: {
                sourceMap: true
            },
            coffee: {
                sourceMap: true
            }
        },
        console: {
            coverage: {
                reporters: ['text-summary']
            },
            mocha: {
                reporter: 'spec'
            }
        },
        report: {
            coverage: {
                reporters: ['lcov', 'json']
            },
            mocha: {
                reporter: 'tap'
            }
        }
    }
},

build_files = {
    js: ['actions/*.js', 'stores/*.js'],
    jsx: ['components/*.jsx']
},

restart_nodemon = function () {
    setTimeout(function () {
        nodemon.emit('restart');
    }, configs.nodemon_restart_delay);
},

lint_chain = function (task) {
    task = task.pipe(jshint.reporter('jshint-stylish'));

    if (configs.github) {
        task = task.pipe(require('gulp-github')(configs.github));
    }

    if (configs.jshint_fail) {
        task = task.pipe(('object' === typeof configs.jshint_fail) ? configs.jshint_fail : jshint.reporter('fail'));
    }

    return task;
},

// Stop using gulp-react because it do not keep original file name
react_compiler = function (options) {
    return through.obj(function (file, enc, callback) {
        try {
            file.contents = new Buffer(React.transform(file.contents.toString(), options));
            this.push(file);
        } catch (E) {
            gutil.log('[jsx ERROR]', gutil.colors.red(file.path));
            gutil.log('[jsx ERROR]', gutil.colors.red(E.message));
        }
        callback();
    });
},

// Do testing tasks
get_testing_task = function (options) {
    var cfg = JSON.parse(JSON.stringify(configs.test_coverage.default));

    cfg.istanbul.exclude = configs.test_coverage.default.istanbul.exclude;
    cfg.coverage.reporters = options.coverage.reporters;
    cfg.mocha = options.mocha;
    cfg.cleanup = configs.test_coverage.default.cleanup;

    return coverage.createTask(cfg);
},

handleJSCSError = function (E) {
    if (!configs.jscs_fail) {
        return;
    }

    if ('function' === typeof configs.jscs_fail) {
        return configs.jscs_fail(E);
    }

    this.emit('error', E);
},

bundleAll = function (b, noSave) {
    var B = b.bundle()
    .on('error', function (E) {
        gutil.log('[browserify ERROR]', gutil.colors.red(E));
    });

    if (!noSave) {
        B.pipe(source('main.js'))
        .pipe(gulp.dest(configs.static_dir + 'js/'))
        .on('end', restart_nodemon);
    }

    return B;
},

buildApp = function (watch, fullpath, nosave, disc) {
    var b = browserify(configs.appjs, {
        cache: {},
        packageCache: {},
        require: (disc ? process.cwd() : '.') + '/components/Html.jsx',
        standalone: 'Fluxex',
        fullPaths: fullpath ? true: false,
        debug: watch
    });

    b.transform('reactify');
    b.transform(aliasify.configure(configs.aliasify), {global: true});

    if (watch) {
        b = require('watchify')(b, configs.watchify);
        b.on('update', function (F) {
            gutil.log('[browserify] ' + F[0] + ' updated');
            bundleAll(b);
        });
    }

    return bundleAll(b, nosave);
};

gulp.task('build_app', function () {
    return buildApp(false, false, true)
        .pipe(source('main.js'))
        .pipe(buffer())
        .pipe(uglify())
        .pipe(gulp.dest(configs.static_dir + 'js/'));
});

gulp.task('disc_app', function () {
    return buildApp(false, true, true, true)
        .pipe(require('disc')())
        .pipe(fs.createWriteStream(configs.static_dir + 'disc.html'));
});

gulp.task('watch_app', function () {
    return buildApp(true, true);
});

gulp.task('watch_flux_js', ['lint_flux_js'], function () {
    gulp.watch(build_files.js, configs.gulp_watch, ['lint_flux_js']);
});

gulp.task('lint_flux_js', function () {
    return lint_chain(
        gulp.src(build_files.js)
        .pipe(cached('jshint'))
        .pipe(jscs()).on('error', handleJSCSError)
        .pipe(jshint())
    );
});

gulp.task('watch_jsx', ['lint_jsx'], function () {
    gulp.watch(build_files.jsx, configs.gulp_watch, ['lint_jsx']);
});

gulp.task('lint_jsx', function () {
    return lint_chain(
        gulp.src(build_files.jsx)
        .pipe(cached('jshint'))
        .pipe(react_compiler({sourceMap: true}))
        .pipe(jscs()).on('error', handleJSCSError)
        .pipe(jshint(configs.jshint_jsx))
    );
});

gulp.task('watch_server', ['lint_server'], function () {
    gulp.watch(configs.mainjs, configs.gulp_watch, ['lint_server'])
    .on('change', function () {
        nodemon.emit('restart');
    });
});

gulp.task('lint_server', function () {
    return gulp.src(configs.mainjs)
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('nodemon_server', ['watch_flux_js', 'watch_jsx', 'watch_app', 'watch_server'], function () {
    nodemon({
        ignore: '*',
        script: configs.mainjs,
        ext: 'do_not_watch'
    })
    .on('log', function (log) {
        gutil.log(log.colour);
    })
    .on('start', function () {
        if (serverStarted) {
            setTimeout(browserSync.reload, configs.nodemon_delay);
        } else {
            browserSync.init(null, {
                proxy: 'http://localhost:3000',
                files: [configs.static_dir + 'css/*.css'],
                port: 3001,
                online: false,
                open: false
            });

            serverStarted = true;
        }
    });
});

gulp.task('watch_tests', ['test_app'], function () {
    gulp.watch([
        configs.test_coverage.default.src,
        build_files.js,
        build_files.jsx
    ], ['test_app']);
});
gulp.task('test_app', function () {
    return get_testing_task(configs.test_coverage.console)();
});
gulp.task('save_test_app', function () {
    return get_testing_task(configs.test_coverage.report)();
});
gulp.task('develop', ['nodemon_server']);
gulp.task('lint_all', ['lint_server', 'lint_flux_js', 'lint_jsx']);
gulp.task('buildall', ['lint_all', 'build_app']);
gulp.task('default',['buildall']);

module.exports = configs;
<|endoftext|>


<commit_before>/*jslint browser: true, newcap: true, nomen: true */
/*globals Facade, Box2D */

(function (Facade) {

    'use strict';

    var methods,

        TO_DEGREES = 180 / Math.PI,

        contactListeners = ['PreSolve', 'PostSolve', 'BeginContact', 'EndContact'],

        b2Body = Box2D.Dynamics.b2Body,
        b2BodyDef = Box2D.Dynamics.b2BodyDef,
        b2ContactListener = Box2D.Dynamics.b2ContactListener,
        b2DebugDraw = Box2D.Dynamics.b2DebugDraw,
        b2FixtureDef = Box2D.Dynamics.b2FixtureDef,
        b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape,
        b2Vec2 = Box2D.Common.Math.b2Vec2,
        b2World = Box2D.Dynamics.b2World;

    function hasBox2DEntityRef(obj, type) {

        if (!type) {

            type = b2Body;

        }

        return obj._box2d !== undefined &&
            obj._box2d.entity !== undefined &&
            obj._box2d.entity instanceof type;

    }

    function resolveContactListener(entities) {

        var a = entities.GetFixtureA().GetBody().GetUserData(),
            b = entities.GetFixtureB().GetBody().GetUserData();

        if (a && typeof a._box2d.callback[this.type] === 'function') {

            a._box2d.callback[this.type].call(a, a, b);

        }

        if (b && typeof b._box2d.callback[this.type] === 'function') {

            b._box2d.callback[this.type].call(b, b, a);

        }

    }

    methods = {

        createObject: function (world, config) {

            var body = new b2BodyDef(),
                fixture = new b2FixtureDef(),
                options = this._configOptions(this.getAllOptions()),
                metrics = this.getAllMetrics(),
                vertices = [],
                key,
                defaults = {
                    type: 'static',
                    sync: true,
                    rotate: false,
                    density: 1.0,
                    friction: 1.0,
                    restitution: 0.1,
                    scale: 30
                };

            if (config === undefined) {

                config = {};

            }

            Object.keys(defaults).forEach(function (key) {

                if (config[key] === undefined) {

                    config[key] = defaults[key];

                }

            });

            body.userData = this;

            if (config.type === 'dynamic') {

                body.type = b2Body.b2_dynamicBody;

            } else if (config.type === 'kinematic') {

                body.type = b2Body.b2_kinematicBody;

            } else {

                body.type = b2Body.b2_staticBody;

            }

            body.fixedRotation = config.rotate ? false : true;
            body.angle = options.rotate * (Math.PI / 180);

            body.position = new b2Vec2(
                metrics.x / config.scale,
                metrics.y / config.scale
            );

            for (key in options.points) {

                if (options.points[key] !== undefined) {

                    vertices.push(
                        new b2Vec2(
                            (options.points[key][0] / config.scale),
                            options.points[key][1] / config.scale
                        )
                    );

                }

            }

            fixture.density = config.density;
            fixture.friction = config.friction;
            fixture.restitution = config.restitution;

            fixture.shape = new b2PolygonShape();
            fixture.shape.SetAsArray(vertices, options.points.length);

            if (hasBox2DEntityRef(world, b2World)) {

                world._box2d.entity.CreateBody(body).CreateFixture(fixture);

                if (this._box2d !== undefined) {

                    methods.destroyObject.call(this, world._box2d.entity);

                }

                this._box2d = {
                    entity: world._box2d.entity.GetBodyList(),
                    config: config,
                    callback: {
                        BeginContact: null,
                        EndContact: null,
                        PostSolve: null,
                        PreSolve: null
                    }
                };

                if (config.sync) {

                    world._box2d.cache.sync.push(this);

                }

            } else {

                console.error('Object supplied is not a valid Box2D world object.');

            }

            return this;

        },

        createWorld: function (config) {

            var world,
                listener = new b2ContactListener(),
                debugDraw = new b2DebugDraw(),
                defaults = {
                    canvas: null,
                    gravity: [0, 20],
                    sleep: true
                };

            if (config === undefined) {

                config = {};

            }

            Object.keys(defaults).forEach(function (key) {

                if (config[key] === undefined) {

                    config[key] = defaults[key];

                }

            });

            world = new b2World(new b2Vec2(0, 0), config.sleep);

            world.userData = this;

            contactListeners.forEach(function (type) {

                listener[type] = resolveContactListener.bind({ type: type });

            });

            world.SetContactListener(listener);

            if (config.canvas) {

                if (config.canvas instanceof Facade) {

                    debugDraw.SetSprite(config.canvas.context);

                } else if (typeof config.canvas === 'object' && config.canvas.nodeType === 1) {

                    debugDraw.SetSprite(config.canvas.getContext('2d'));

                }

                debugDraw.SetDrawScale(30);
                debugDraw.SetFillAlpha(0.3);
                debugDraw.SetLineThickness(1.0);
                debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_centerOfMassBit);
                world.SetDebugDraw(debugDraw);

            }

            this._box2d = {
                entity: world,
                config: config,
                cache: {
                    sync: []
                }
            };

            methods.setGravity.call(this, config.gravity);

            return this;

        },

        drawDebug: function () {

            if (hasBox2DEntityRef(this, b2World)) {

                this._box2d.entity.DrawDebugData();

            }

        },

        destroyObject: function () {

            var self = this,
                world;

            if (hasBox2DEntityRef(this)) {

                world = self._box2d.entity.GetWorld();

                if (hasBox2DEntityRef(world.userData, b2World)) {

                    world.userData._box2d.cache.sync.splice(
                        world.userData._box2d.cache.sync.indexOf(self),
                        1
                    );

                }

                self._box2d.entity.SetUserData(null);

                setTimeout(function () {

                    world.DestroyBody(self._box2d.entity);

                    delete self._box2d;

                }, 0);

            }

        },

        getCurrentState: function () {

            if (hasBox2DEntityRef(this)) {

                return {
                    x: this._box2d.entity.GetPosition().x * this._box2d.config.scale,
                    y: this._box2d.entity.GetPosition().y * this._box2d.config.scale,
                    rotate: this._box2d.entity.GetAngle() * TO_DEGREES
                };

            }

        },

        getPosition: function () {

            if (hasBox2DEntityRef(this)) {

                var vector = this._box2d.entity.GetPosition();

                return {
                    x: vector.x,
                    y: vector.y
                };

            }

        },

        getVelocity: function () {

            if (hasBox2DEntityRef(this)) {

                return {
                    x: this._box2d.entity.m_linearVelocity.x,
                    y: this._box2d.entity.m_linearVelocity.y
                };

            }

        },

        setCallback: function (type, callback) {

            if (hasBox2DEntityRef(this)) {

                if (this._box2d.callback[type] !== undefined) {

                    this._box2d.callback[type] = callback;

                } else {

                    console.error(type + ' is not a valid callback type.');

                }

            }

        },

        setGravity: function (gravity) {

            if (hasBox2DEntityRef(this, b2World)) {

                if (!(gravity instanceof b2Vec2)) {

                    gravity = new b2Vec2(
                        gravity[0],
                        gravity[1]
                    );

                }

                this._box2d.entity.SetGravity(gravity);

            }

        },

        setPosition: function (x, y) {

            if (hasBox2DEntityRef(this)) {

                this._box2d.entity.SetPosition(new b2Vec2(x, y));

            }

        },

        setVelocity: function (x, y) {

            if (hasBox2DEntityRef(this)) {

                if (x === undefined) {

                    x = 0;

                }

                if (y === undefined) {

                    y = 0;

                }

                this._box2d.entity.SetAwake(true);

                this._box2d.entity.SetLinearVelocity(
                    new b2Vec2(x, y),
                    this._box2d.entity.GetWorldCenter()
                );

            }

        },

        step: function (callback) {

            var i,
                length,
                obj;

            if (hasBox2DEntityRef(this, b2World)) {

                this._box2d.entity.Step(1 / 60, 8, 3);

                for (i = 0, length = this._box2d.cache.sync.length; i < length; i += 1) {

                    obj = this._box2d.cache.sync[i];

                    obj.setOptions(obj.Box2D('getCurrentState'));

                }

                if (callback !== undefined) {

                    callback();

                }

            }

        }

    };

    Facade.Entity.prototype.Box2D = function (method) {

        if (!methods[method]) {

            console.error(method + ' is not a method specified in this plugin.');

        }

        return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));

    };

}(Facade));
<commit_msg>Added support for circles.<commit_after>/*jslint browser: true, newcap: true, nomen: true */
/*globals Facade, Box2D */

(function (Facade) {

    'use strict';

    var methods,

        TO_DEGREES = 180 / Math.PI,

        contactListeners = ['PreSolve', 'PostSolve', 'BeginContact', 'EndContact'],

        b2Body = Box2D.Dynamics.b2Body,
        b2BodyDef = Box2D.Dynamics.b2BodyDef,
        b2ContactListener = Box2D.Dynamics.b2ContactListener,
        b2DebugDraw = Box2D.Dynamics.b2DebugDraw,
        b2FixtureDef = Box2D.Dynamics.b2FixtureDef,
        b2CircleShape = Box2D.Collision.Shapes.b2CircleShape,
        b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape,
        b2Vec2 = Box2D.Common.Math.b2Vec2,
        b2World = Box2D.Dynamics.b2World;

    function hasBox2DEntityRef(obj, type) {

        if (!type) {

            type = b2Body;

        }

        return obj._box2d !== undefined &&
            obj._box2d.entity !== undefined &&
            obj._box2d.entity instanceof type;

    }

    function resolveContactListener(entities) {

        var a = entities.GetFixtureA().GetBody().GetUserData(),
            b = entities.GetFixtureB().GetBody().GetUserData();

        if (a && typeof a._box2d.callback[this.type] === 'function') {

            a._box2d.callback[this.type].call(a, a, b);

        }

        if (b && typeof b._box2d.callback[this.type] === 'function') {

            b._box2d.callback[this.type].call(b, b, a);

        }

    }

    methods = {

        createObject: function (world, config) {

            var body = new b2BodyDef(),
                fixture = new b2FixtureDef(),
                options = this._configOptions(this.getAllOptions()),
                metrics = this.getAllMetrics(),
                vertices = [],
                key,
                defaults = {
                    type: 'static',
                    sync: true,
                    rotate: false,
                    density: 1.0,
                    friction: 1.0,
                    restitution: 0.1,
                    scale: 30
                };

            if (config === undefined) {

                config = {};

            }

            Object.keys(defaults).forEach(function (key) {

                if (config[key] === undefined) {

                    config[key] = defaults[key];

                }

            });

            body.userData = this;

            if (config.type === 'dynamic') {

                body.type = b2Body.b2_dynamicBody;

            } else if (config.type === 'kinematic') {

                body.type = b2Body.b2_kinematicBody;

            } else {

                body.type = b2Body.b2_staticBody;

            }

            body.fixedRotation = config.rotate ? false : true;
            body.angle = options.rotate * (Math.PI / 180);

            if (this instanceof Facade.Circle) {

                fixture.shape = new b2CircleShape(options.radius / config.scale);

                body.position = new b2Vec2(
                    (metrics.x + (metrics.width / 2)) / config.scale,
                    (metrics.y + (metrics.height / 2)) / config.scale
                );

            } else {

                body.position = new b2Vec2(
                    metrics.x / config.scale,
                    metrics.y / config.scale
                );

                for (key in options.points) {

                    if (options.points[key] !== undefined) {

                        vertices.push(
                            new b2Vec2(
                                (options.points[key][0] / config.scale),
                                options.points[key][1] / config.scale
                            )
                        );

                    }

                }

                fixture.shape = new b2PolygonShape();
                fixture.shape.SetAsArray(vertices, options.points.length);

            }

            fixture.density = config.density;
            fixture.friction = config.friction;
            fixture.restitution = config.restitution;

            if (hasBox2DEntityRef(world, b2World)) {

                world._box2d.entity.CreateBody(body).CreateFixture(fixture);

                if (this._box2d !== undefined) {

                    methods.destroyObject.call(this, world._box2d.entity);

                }

                this._box2d = {
                    entity: world._box2d.entity.GetBodyList(),
                    config: config,
                    callback: {
                        BeginContact: null,
                        EndContact: null,
                        PostSolve: null,
                        PreSolve: null
                    }
                };

                if (config.sync) {

                    world._box2d.cache.sync.push(this);

                }

            } else {

                console.error('Object supplied is not a valid Box2D world object.');

            }

            return this;

        },

        createWorld: function (config) {

            var world,
                listener = new b2ContactListener(),
                debugDraw = new b2DebugDraw(),
                defaults = {
                    canvas: null,
                    gravity: [0, 20],
                    sleep: true
                };

            if (config === undefined) {

                config = {};

            }

            Object.keys(defaults).forEach(function (key) {

                if (config[key] === undefined) {

                    config[key] = defaults[key];

                }

            });

            world = new b2World(new b2Vec2(0, 0), config.sleep);

            world.userData = this;

            contactListeners.forEach(function (type) {

                listener[type] = resolveContactListener.bind({ type: type });

            });

            world.SetContactListener(listener);

            if (config.canvas) {

                if (config.canvas instanceof Facade) {

                    debugDraw.SetSprite(config.canvas.context);

                } else if (typeof config.canvas === 'object' && config.canvas.nodeType === 1) {

                    debugDraw.SetSprite(config.canvas.getContext('2d'));

                }

                debugDraw.SetDrawScale(30);
                debugDraw.SetFillAlpha(0.3);
                debugDraw.SetLineThickness(1.0);
                debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_centerOfMassBit);
                world.SetDebugDraw(debugDraw);

            }

            this._box2d = {
                entity: world,
                config: config,
                cache: {
                    sync: []
                }
            };

            methods.setGravity.call(this, config.gravity);

            return this;

        },

        drawDebug: function () {

            if (hasBox2DEntityRef(this, b2World)) {

                this._box2d.entity.DrawDebugData();

            }

        },

        destroyObject: function () {

            var self = this,
                world;

            if (hasBox2DEntityRef(this)) {

                world = self._box2d.entity.GetWorld();

                if (hasBox2DEntityRef(world.userData, b2World)) {

                    world.userData._box2d.cache.sync.splice(
                        world.userData._box2d.cache.sync.indexOf(self),
                        1
                    );

                }

                self._box2d.entity.SetUserData(null);

                setTimeout(function () {

                    world.DestroyBody(self._box2d.entity);

                    delete self._box2d;

                }, 0);

            }

        },

        getCurrentState: function () {

            if (hasBox2DEntityRef(this)) {

                return {
                    x: this._box2d.entity.GetPosition().x * this._box2d.config.scale,
                    y: this._box2d.entity.GetPosition().y * this._box2d.config.scale,
                    rotate: this._box2d.entity.GetAngle() * TO_DEGREES
                };

            }

        },

        getPosition: function () {

            if (hasBox2DEntityRef(this)) {

                var vector = this._box2d.entity.GetPosition();

                return {
                    x: vector.x,
                    y: vector.y
                };

            }

        },

        getVelocity: function () {

            if (hasBox2DEntityRef(this)) {

                return {
                    x: this._box2d.entity.m_linearVelocity.x,
                    y: this._box2d.entity.m_linearVelocity.y
                };

            }

        },

        setCallback: function (type, callback) {

            if (hasBox2DEntityRef(this)) {

                if (this._box2d.callback[type] !== undefined) {

                    this._box2d.callback[type] = callback;

                } else {

                    console.error(type + ' is not a valid callback type.');

                }

            }

        },

        setGravity: function (gravity) {

            if (hasBox2DEntityRef(this, b2World)) {

                if (!(gravity instanceof b2Vec2)) {

                    gravity = new b2Vec2(
                        gravity[0],
                        gravity[1]
                    );

                }

                this._box2d.entity.SetGravity(gravity);

            }

        },

        setPosition: function (x, y) {

            if (hasBox2DEntityRef(this)) {

                this._box2d.entity.SetPosition(new b2Vec2(x, y));

            }

        },

        setVelocity: function (x, y) {

            if (hasBox2DEntityRef(this)) {

                if (x === undefined) {

                    x = 0;

                }

                if (y === undefined) {

                    y = 0;

                }

                this._box2d.entity.SetAwake(true);

                this._box2d.entity.SetLinearVelocity(
                    new b2Vec2(x, y),
                    this._box2d.entity.GetWorldCenter()
                );

            }

        },

        step: function (callback) {

            var i,
                length,
                obj;

            if (hasBox2DEntityRef(this, b2World)) {

                this._box2d.entity.Step(1 / 60, 8, 3);

                for (i = 0, length = this._box2d.cache.sync.length; i < length; i += 1) {

                    obj = this._box2d.cache.sync[i];

                    obj.setOptions(obj.Box2D('getCurrentState'));

                }

                if (callback !== undefined) {

                    callback();

                }

            }

        }

    };

    Facade.Entity.prototype.Box2D = function (method) {

        if (!methods[method]) {

            console.error(method + ' is not a method specified in this plugin.');

        }

        return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));

    };

}(Facade));
<|endoftext|>


<commit_before>$("document").ready(function(){

    $(".multiSearch").select2({
        minimumInputLength: 2,
        ajax: {
            url: function (params){
              return window.CRM.root + "api/search/" + params.term;
            },
            dataType: 'json',
            delay: 250,
            data: "",
            processResults: function (data, params) {
              var idKey = 1;
              var results = new Array();
              $.each(data.results, function(key, resultGroupJSONText)
              {
                var rawResultGroup = JSON.parse(resultGroupJSONText);
                var groupName = Object.keys(rawResultGroup)[0];
                var ckeys = rawResultGroup[groupName];
                var resultGroup = {
                  id: key,
                  text: groupName,
                  children: []
                };
                idKey++;
                $.each(ckeys, function(ckey, cvalue)
                {
                  var childObject = {
                    id: idKey,
                    text: cvalue.displayName,
                    uri: cvalue.uri
                  };
                  idKey++;
                  resultGroup.children.push(childObject);
                });
                results.push(resultGroup);
              });
              return {results: results};
            },
            cache: true
        }
    });
    $(".multiSearch").on("select2:select",function (e) { window.location.href= e.params.data.uri;});

});
<commit_msg>added missing /<commit_after>$("document").ready(function(){

    $(".multiSearch").select2({
        minimumInputLength: 2,
        ajax: {
            url: function (params){
              return window.CRM.root + "/api/search/" + params.term;
            },
            dataType: 'json',
            delay: 250,
            data: "",
            processResults: function (data, params) {
              var idKey = 1;
              var results = new Array();
              $.each(data.results, function(key, resultGroupJSONText)
              {
                var rawResultGroup = JSON.parse(resultGroupJSONText);
                var groupName = Object.keys(rawResultGroup)[0];
                var ckeys = rawResultGroup[groupName];
                var resultGroup = {
                  id: key,
                  text: groupName,
                  children: []
                };
                idKey++;
                $.each(ckeys, function(ckey, cvalue)
                {
                  var childObject = {
                    id: idKey,
                    text: cvalue.displayName,
                    uri: cvalue.uri
                  };
                  idKey++;
                  resultGroup.children.push(childObject);
                });
                results.push(resultGroup);
              });
              return {results: results};
            },
            cache: true
        }
    });
    $(".multiSearch").on("select2:select",function (e) { window.location.href= e.params.data.uri;});

});
<|endoftext|>