<commit_before>/**
 * @module util
 */

'use strict'

const child = require('child_process')
const fs = require('fs')
const path = require('path')

const Promise = require('bluebird')

const debug = require('debug')

/**
 * This callback is used across signing and flattening.
 * @callback RequestCallback
 * @param {?Error} err
 */

/** @function */
const debuglog = module.exports.debuglog = debug('electron-osx-sign')
debuglog.log = console.log.bind(console)

/** @function */
const debugwarn = module.exports.debugwarn = debug('electron-osx-sign:warn')
debugwarn.log = console.warn.bind(console)

/** @function */
const debugerror = module.exports.debugerror = debug('electron-osx-sign:error')
debugerror.log = console.error.bind(console)

/** @function */
const isBinaryFileAsync = module.exports.isBinaryFileAsync = Promise.promisify(require('isbinaryfile'))

function removePassword (input) {
  return input.replace(/(-P |pass:|\/p|-pass )([^ ]+)/, function (match, p1, p2) {
    return `${p1}***`
  })
}

/** @function */
module.exports.execFileAsync = function (file, args, options) {
  if (debuglog.enabled) {
    debuglog(`Executing ${file} ${args == null ? '' : removePassword(args.join(' '))}`)
  }

  return new Promise((resolve, reject) => {
    child.execFile(file, args, options, function (error, stdout, stderr) {
      if (error == null) {
        if (debuglog.enabled) {
          if (stderr.length !== 0) {
            debuglog(stderr)
          }
          if (stdout.length !== 0) {
            debuglog(stdout)
          }
        }
        resolve(stdout)
      } else {
        let message = removePassword(`Exit code: ${error.code}. ${error.message}`)
        if (stdout.length !== 0) {
          message += `\n${stdout}`
        }
        if (stderr.length !== 0) {
          message += `\n${stderr}`
        }

        reject(new Error(message))
      }
    })
  })
}

/** @function */
const lstatAsync = module.exports.lstatAsync = Promise.promisify(fs.lstat)

/** @function */
const readdirAsync = module.exports.readdirAsync = Promise.promisify(fs.readdir)

/** @function */
module.exports.readFileAsync = Promise.promisify(fs.readFile)

/** @function */
module.exports.writeFileAsync = Promise.promisify(fs.writeFile)

/**
 * This function returns a flattened list of elements from an array of lists.
 * @function
 * @param {*} list - List.
 * @returns Flattened list.
 */
var flatList = module.exports.flatList = function (list) {
  function populateResult (list) {
    if (!Array.isArray(list)) {
      result.push(list)
    } else if (list.length > 0) {
      for (let item of list) if (item) populateResult(item)
    }
  }

  var result = []
  populateResult(list)
  return result
}

/**
 * This function returns the path to app contents.
 * @function
 * @param {Object} opts - Options.
 * @returns {string} App contents path.
 */
var getAppContentsPath = module.exports.getAppContentsPath = function (opts) {
  return path.join(opts.app, 'Contents')
}

/**
 * This function returns the path to app frameworks within contents.
 * @function
 * @param {Object} opts - Options.
 * @returns {string} App frameworks path.
 */
var getAppFrameworksPath = module.exports.getAppFrameworksPath = function (opts) {
  return path.join(getAppContentsPath(opts), 'Frameworks')
}

/**
 * This function returns a promise copying a file from the source to the target.
 * @function
 * @param {string} source - Source path.
 * @param {string} target - Target path.
 * @returns {Promise} Promise.
 */
module.exports.copyFileAsync = function (source, target) {
  debuglog('Copying file...', '\n',
    '> Source:', source, '\n',
    '> Target:', target)
  return new Promise(function (resolve, reject) {
    var readStream = fs.createReadStream(source)
    readStream.on('error', reject)
    var writeStream = fs.createWriteStream(target)
    writeStream.on('error', reject)
    writeStream.on('close', resolve)
    readStream.pipe(writeStream)
  })
}

/**
 * This function returns a promise with platform resolved.
 * @function
 * @param {Object} opts - Options.
 * @returns {Promise} Promise resolving platform.
 */
var detectElectronPlatformAsync = module.exports.detectElectronPlatformAsync = function (opts) {
  return new Promise(function (resolve) {
    var appFrameworksPath = getAppFrameworksPath(opts)
    // The presence of Squirrel.framework identifies a Mac App Store build as used in https://github.com/atom/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md
    return lstatAsync(path.join(appFrameworksPath, 'Squirrel.framework'))
      .then(function () {
        resolve('darwin')
      })
      .catch(function () {
        resolve('mas')
      })
  })
}

/**
 * This function returns a promise resolving the file path if file binary.
 * @function
 * @param {string} filePath - Path to file.
 * @returns {Promise} Promise resolving file path or undefined.
 */
var getFilePathIfBinaryAsync = module.exports.getFilePathIfBinaryAsync = function (filePath) {
  return isBinaryFileAsync(filePath)
    .then(function (isBinary) {
      return isBinary ? filePath : undefined
    })
}

/**
 * This function returns a promise validating opts.app, the application to be signed or flattened.
 * @function
 * @param {Object} opts - Options.
 * @returns {Promise} Promise.
 */
module.exports.validateOptsAppAsync = function (opts) {
  return new Promise(function (resolve, reject) {
    if (!opts.app) {
      reject(new Error('Path to aplication must be specified.'))
      return
    }
    if (path.extname(opts.app) !== '.app') {
      reject(new Error('Extension of application must be `.app`.'))
      return
    }
    return lstatAsync(opts.app)
      .then(function () {
        resolve(undefined)
      })
      .catch(function (err) {
        debugerror(err)
        reject(new Error('Application not found. See details in debug log. (electron-osx-sign:error)'))
      })
  })
}

/**
 * This function returns a promise validating opts.platform, the platform of Electron build. It allows auto-discovery if no opts.platform is specified.
 * @function
 * @param {Object} opts - Options.
 * @returns {Promise} Promise.
 */
module.exports.validateOptsPlatformAsync = function (opts) {
  return new Promise(function (resolve, reject) {
    if (opts.platform) {
      if (opts.platform === 'mas' || opts.platform === 'darwin') {
        resolve()
        return
      } else {
        debugwarn('`platform` passed in arguments not supported, checking Electron platform...')
      }
    } else {
      debugwarn('No `platform` passed in arguments, checking Electron platform...')
    }
    return detectElectronPlatformAsync(opts)
      .then(function (platform) {
        opts.platform = platform
        resolve()
      })
      .catch(function (err) {
        // NB: This should logically not happen as detectElectronPlatformAsync should not give any rejections. However, it is put here just in case.
        debugerror(err)
        reject(new Error('Unable to decide Electron platform. See details in debug log. (electron-osx-sign:error)'))
      })
  })
}

/**
 * This function returns a promise resolving all child paths within the directory specified.
 * @function
 * @param {string} dirPath - Path to directory.
 * @returns {Promise} Promise resolving child paths needing signing in order.
 */
module.exports.walkAsync = function (dirPath) {
  debuglog('Walking... ' + dirPath)

  var unlinkAsync = Promise.promisify(fs.unlink)

  function _walkAsync (dirPath) {
    return readdirAsync(dirPath)
      .then(function (names) {
        return Promise.map(names, function (name) {
          var filePath = path.join(dirPath, name)
          return lstatAsync(filePath)
            .then(function (stat) {
              if (stat.isFile()) {
                switch (path.extname(filePath)) {
                  case '': // Binary
                    if (path.basename(filePath)[0] !== '.') {
                      return getFilePathIfBinaryAsync(filePath)
                    } // Else reject hidden file
                    break
                  case '.dylib': // Dynamic library
                  case '.node': // Native node addon
                    return filePath
                  case '.cstemp': // Temporary file generated from past codesign
                    debuglog('Removing... ' + filePath)
                    return unlinkAsync(filePath)
                      .thenReturn(undefined)
                  default:
                    if (path.extname(filePath).indexOf(' ') >= 0) {
                      // Still consider the file as binary if extension seems invalid
                      return getFilePathIfBinaryAsync(filePath)
                    }
                }
              } else if (stat.isDirectory() && !stat.isSymbolicLink()) {
                return _walkAsync(filePath)
                  .then(function (result) {
                    switch (path.extname(filePath)) {
                      case '.app': // Application
                      case '.framework': // Framework
                        result.push(filePath)
                    }
                    return result
                  })
              }
              return undefined
            })
        })
      })
  }

  return _walkAsync(dirPath)
    .then(flatList)
}
<commit_msg>Update validateOptsAppAsync<commit_after>/**
 * @module util
 */

'use strict'

const child = require('child_process')
const fs = require('fs')
const path = require('path')

const Promise = require('bluebird')

const debug = require('debug')

/**
 * This callback is used across signing and flattening.
 * @callback RequestCallback
 * @param {?Error} err
 */

/** @function */
const debuglog = module.exports.debuglog = debug('electron-osx-sign')
debuglog.log = console.log.bind(console)

/** @function */
const debugwarn = module.exports.debugwarn = debug('electron-osx-sign:warn')
debugwarn.log = console.warn.bind(console)

/** @function */
const debugerror = module.exports.debugerror = debug('electron-osx-sign:error')
debugerror.log = console.error.bind(console)

/** @function */
const isBinaryFileAsync = module.exports.isBinaryFileAsync = Promise.promisify(require('isbinaryfile'))

function removePassword (input) {
  return input.replace(/(-P |pass:|\/p|-pass )([^ ]+)/, function (match, p1, p2) {
    return `${p1}***`
  })
}

/** @function */
module.exports.execFileAsync = function (file, args, options) {
  if (debuglog.enabled) {
    debuglog(`Executing ${file} ${args == null ? '' : removePassword(args.join(' '))}`)
  }

  return new Promise((resolve, reject) => {
    child.execFile(file, args, options, function (error, stdout, stderr) {
      if (error == null) {
        if (debuglog.enabled) {
          if (stderr.length !== 0) {
            debuglog(stderr)
          }
          if (stdout.length !== 0) {
            debuglog(stdout)
          }
        }
        resolve(stdout)
      } else {
        let message = removePassword(`Exit code: ${error.code}. ${error.message}`)
        if (stdout.length !== 0) {
          message += `\n${stdout}`
        }
        if (stderr.length !== 0) {
          message += `\n${stderr}`
        }

        reject(new Error(message))
      }
    })
  })
}

/** @function */
const lstatAsync = module.exports.lstatAsync = Promise.promisify(fs.lstat)

/** @function */
const readdirAsync = module.exports.readdirAsync = Promise.promisify(fs.readdir)

/** @function */
module.exports.readFileAsync = Promise.promisify(fs.readFile)

/** @function */
module.exports.writeFileAsync = Promise.promisify(fs.writeFile)

/**
 * This function returns a flattened list of elements from an array of lists.
 * @function
 * @param {*} list - List.
 * @returns Flattened list.
 */
var flatList = module.exports.flatList = function (list) {
  function populateResult (list) {
    if (!Array.isArray(list)) {
      result.push(list)
    } else if (list.length > 0) {
      for (let item of list) if (item) populateResult(item)
    }
  }

  var result = []
  populateResult(list)
  return result
}

/**
 * This function returns the path to app contents.
 * @function
 * @param {Object} opts - Options.
 * @returns {string} App contents path.
 */
var getAppContentsPath = module.exports.getAppContentsPath = function (opts) {
  return path.join(opts.app, 'Contents')
}

/**
 * This function returns the path to app frameworks within contents.
 * @function
 * @param {Object} opts - Options.
 * @returns {string} App frameworks path.
 */
var getAppFrameworksPath = module.exports.getAppFrameworksPath = function (opts) {
  return path.join(getAppContentsPath(opts), 'Frameworks')
}

/**
 * This function returns a promise copying a file from the source to the target.
 * @function
 * @param {string} source - Source path.
 * @param {string} target - Target path.
 * @returns {Promise} Promise.
 */
module.exports.copyFileAsync = function (source, target) {
  debuglog('Copying file...', '\n',
    '> Source:', source, '\n',
    '> Target:', target)
  return new Promise(function (resolve, reject) {
    var readStream = fs.createReadStream(source)
    readStream.on('error', reject)
    var writeStream = fs.createWriteStream(target)
    writeStream.on('error', reject)
    writeStream.on('close', resolve)
    readStream.pipe(writeStream)
  })
}

/**
 * This function returns a promise with platform resolved.
 * @function
 * @param {Object} opts - Options.
 * @returns {Promise} Promise resolving platform.
 */
var detectElectronPlatformAsync = module.exports.detectElectronPlatformAsync = function (opts) {
  return new Promise(function (resolve) {
    var appFrameworksPath = getAppFrameworksPath(opts)
    // The presence of Squirrel.framework identifies a Mac App Store build as used in https://github.com/atom/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md
    return lstatAsync(path.join(appFrameworksPath, 'Squirrel.framework'))
      .then(function () {
        resolve('darwin')
      })
      .catch(function () {
        resolve('mas')
      })
  })
}

/**
 * This function returns a promise resolving the file path if file binary.
 * @function
 * @param {string} filePath - Path to file.
 * @returns {Promise} Promise resolving file path or undefined.
 */
var getFilePathIfBinaryAsync = module.exports.getFilePathIfBinaryAsync = function (filePath) {
  return isBinaryFileAsync(filePath)
    .then(function (isBinary) {
      return isBinary ? filePath : undefined
    })
}

/**
 * This function returns a promise validating opts.app, the application to be signed or flattened.
 * @function
 * @param {Object} opts - Options.
 * @returns {Promise} Promise.
 */
module.exports.validateOptsAppAsync = function (opts) {
  if (!opts.app) {
    return Promise.reject(new Error('Path to aplication must be specified.'))
  }
  if (path.extname(opts.app) !== '.app') {
    return Promise.reject(new Error('Extension of application must be `.app`.'))
  }
  return lstatAsync(opts.app)
    .thenReturn()
}

/**
 * This function returns a promise validating opts.platform, the platform of Electron build. It allows auto-discovery if no opts.platform is specified.
 * @function
 * @param {Object} opts - Options.
 * @returns {Promise} Promise.
 */
module.exports.validateOptsPlatformAsync = function (opts) {
  return new Promise(function (resolve, reject) {
    if (opts.platform) {
      if (opts.platform === 'mas' || opts.platform === 'darwin') {
        resolve()
        return
      } else {
        debugwarn('`platform` passed in arguments not supported, checking Electron platform...')
      }
    } else {
      debugwarn('No `platform` passed in arguments, checking Electron platform...')
    }
    return detectElectronPlatformAsync(opts)
      .then(function (platform) {
        opts.platform = platform
        resolve()
      })
      .catch(function (err) {
        // NB: This should logically not happen as detectElectronPlatformAsync should not give any rejections. However, it is put here just in case.
        debugerror(err)
        reject(new Error('Unable to decide Electron platform. See details in debug log. (electron-osx-sign:error)'))
      })
  })
}

/**
 * This function returns a promise resolving all child paths within the directory specified.
 * @function
 * @param {string} dirPath - Path to directory.
 * @returns {Promise} Promise resolving child paths needing signing in order.
 */
module.exports.walkAsync = function (dirPath) {
  debuglog('Walking... ' + dirPath)

  var unlinkAsync = Promise.promisify(fs.unlink)

  function _walkAsync (dirPath) {
    return readdirAsync(dirPath)
      .then(function (names) {
        return Promise.map(names, function (name) {
          var filePath = path.join(dirPath, name)
          return lstatAsync(filePath)
            .then(function (stat) {
              if (stat.isFile()) {
                switch (path.extname(filePath)) {
                  case '': // Binary
                    if (path.basename(filePath)[0] !== '.') {
                      return getFilePathIfBinaryAsync(filePath)
                    } // Else reject hidden file
                    break
                  case '.dylib': // Dynamic library
                  case '.node': // Native node addon
                    return filePath
                  case '.cstemp': // Temporary file generated from past codesign
                    debuglog('Removing... ' + filePath)
                    return unlinkAsync(filePath)
                      .thenReturn(undefined)
                  default:
                    if (path.extname(filePath).indexOf(' ') >= 0) {
                      // Still consider the file as binary if extension seems invalid
                      return getFilePathIfBinaryAsync(filePath)
                    }
                }
              } else if (stat.isDirectory() && !stat.isSymbolicLink()) {
                return _walkAsync(filePath)
                  .then(function (result) {
                    switch (path.extname(filePath)) {
                      case '.app': // Application
                      case '.framework': // Framework
                        result.push(filePath)
                    }
                    return result
                  })
              }
              return undefined
            })
        })
      })
  }

  return _walkAsync(dirPath)
    .then(flatList)
}
<|endoftext|>


<commit_before>import {InputRule} from "./inputrules"
import {canWrap, joinable} from "../transform"

// :: (RegExp, string, NodeType, ?union<Object, ([string]) → ?Object>, ?([string], Node) → bool) → InputRule

// Build an input rule for automatically wrapping a textblock when a
// given string is typed. The `regexp` and `filter` arguments are
// directly passed through to the `InputRule` constructor. You'll
// probably want the regexp to start with `^`, so that the pattern can
// only occur at the start of a textblock.
//
// `nodeType` is the type of node to wrap in. If it needs attributes,
// you can either pass them directly, or pass a function that will
// compute them from the regular expression match.
//
// By default, if there's a node with the same type above the newly
// wrapped node, the rule will try to [join](#Transform.join) those
// two nodes. You can pass a join predicate, which takes a regular
// expression match and the node before the wrapped node, and can
// return a boolean to indicate whether a join should happen.
export function wrappingInputRule(regexp, filter, nodeType, getAttrs, joinPredicate) {
  return new InputRule(regexp, filter, (pm, match, pos) => {
    let start = pos - match[0].length
    let attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs
    if (!canWrap(pm.doc, pos, pos, nodeType, attrs)) return
    let tr = pm.tr.delete(start, pos).wrap(start, start, nodeType, attrs)
    let before = tr.doc.resolve(start - 1).nodeBefore
    if (before && before.type == nodeType && joinable(tr.doc, start - 1) &&
        (!joinPredicate || joinPredicate(match, before)))
      tr.join(start - 1)
    tr.apply()
  })
}

// :: (RegExp, string, NodeType, ?union<Object, ([string]) → ?Object>) → InputRule
// Build an input rule that changes the type of a textblock when the
// matched text is typed into it. You'll usually want to start your
// regexp with `^` to that it is only matched at the start of a
// textblock. The optional `getAttrs` parameter can be used to compute
// the new node's attributes, and works the same as in the
// `wrappingInputRule` function.
export function textblockTypeInputRule(regexp, filter, nodeType, getAttrs) {
  return new InputRule(regexp, filter, (pm, match, pos) => {
    let $pos = pm.doc.resolve(pos), start = pos - match[0].length
    let attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs
    if (!$pos.node(-1).canReplaceWith($pos.index(-1), $pos.indexAfter(-1), nodeType, attrs)) return
    return pm.tr
      .delete(start, pos)
      .setBlockType(start, start, nodeType, attrs)
      .apply()
  })
}
<commit_msg>Make Transform.wrap take precise, explicit arguments<commit_after>import {InputRule} from "./inputrules"
import {findWrapping, joinable} from "../transform"

// :: (RegExp, string, NodeType, ?union<Object, ([string]) → ?Object>, ?([string], Node) → bool) → InputRule

// Build an input rule for automatically wrapping a textblock when a
// given string is typed. The `regexp` and `filter` arguments are
// directly passed through to the `InputRule` constructor. You'll
// probably want the regexp to start with `^`, so that the pattern can
// only occur at the start of a textblock.
//
// `nodeType` is the type of node to wrap in. If it needs attributes,
// you can either pass them directly, or pass a function that will
// compute them from the regular expression match.
//
// By default, if there's a node with the same type above the newly
// wrapped node, the rule will try to [join](#Transform.join) those
// two nodes. You can pass a join predicate, which takes a regular
// expression match and the node before the wrapped node, and can
// return a boolean to indicate whether a join should happen.
export function wrappingInputRule(regexp, filter, nodeType, getAttrs, joinPredicate) {
  return new InputRule(regexp, filter, (pm, match, pos) => {
    let start = pos - match[0].length
    let attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs
    let $pos = pm.doc.resolve(pos), range = $pos.blockRange(), wrapping = range && findWrapping(range, nodeType, attrs)
    if (!wrapping) return
    let tr = pm.tr.delete(start, pos).wrap(range, wrapping)
    let before = tr.doc.resolve(start - 1).nodeBefore
    if (before && before.type == nodeType && joinable(tr.doc, start - 1) &&
        (!joinPredicate || joinPredicate(match, before)))
      tr.join(start - 1)
    tr.apply()
  })
}

// :: (RegExp, string, NodeType, ?union<Object, ([string]) → ?Object>) → InputRule
// Build an input rule that changes the type of a textblock when the
// matched text is typed into it. You'll usually want to start your
// regexp with `^` to that it is only matched at the start of a
// textblock. The optional `getAttrs` parameter can be used to compute
// the new node's attributes, and works the same as in the
// `wrappingInputRule` function.
export function textblockTypeInputRule(regexp, filter, nodeType, getAttrs) {
  return new InputRule(regexp, filter, (pm, match, pos) => {
    let $pos = pm.doc.resolve(pos), start = pos - match[0].length
    let attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs
    if (!$pos.node(-1).canReplaceWith($pos.index(-1), $pos.indexAfter(-1), nodeType, attrs)) return
    return pm.tr
      .delete(start, pos)
      .setBlockType(start, start, nodeType, attrs)
      .apply()
  })
}
<|endoftext|>


<commit_before>// DarkFO
// Copyright (c) 2014 darkf
// Licensed under the terms of the zlib license

function parseIni(text) {
	// Parse a .ini-style categorized key-value format
	var lines = text.split('\n')
	var category = null
	var ini = {}

	for(var i = 0; i < lines.length; i++) {
		var line = lines[i].replace(/\s*;.*/, "") // replace comments
		if(line.trim() === '') { }
		else if(line[0] === '[')
			category = line.trim().slice(1, -1)
		else {
			// key=value
			var kv = line.match(/(.+?)=(.+)/)
			if(kv === null) { // MAPS.TXT has one of these, so it's not an exception
				console.log("warning: parseIni: not a key=value line: " + line)
				continue
			}
			if(category === null) throw "parseIni: key=value not in category: " + line

			if(ini[category] === undefined) ini[category] = {}
			ini[category][kv[1]] = kv[2]
		}
	}

	return ini
}

function getFileText(path) {
	var r = null
	$.get(path, function(text) { r = text }, "text").fail(function() {
		throw "getFileText: getting path " + path + " failed"
	})
	return r
}<commit_msg>added utility function for d100 based skillchecks.<commit_after>// DarkFO
// Copyright (c) 2014 darkf
// Licensed under the terms of the zlib license

function parseIni(text) {
	// Parse a .ini-style categorized key-value format
	var lines = text.split('\n')
	var category = null
	var ini = {}

	for(var i = 0; i < lines.length; i++) {
		var line = lines[i].replace(/\s*;.*/, "") // replace comments
		if(line.trim() === '') { }
		else if(line[0] === '[')
			category = line.trim().slice(1, -1)
		else {
			// key=value
			var kv = line.match(/(.+?)=(.+)/)
			if(kv === null) { // MAPS.TXT has one of these, so it's not an exception
				console.log("warning: parseIni: not a key=value line: " + line)
				continue
			}
			if(category === null) throw "parseIni: key=value not in category: " + line

			if(ini[category] === undefined) ini[category] = {}
			ini[category][kv[1]] = kv[2]
		}
	}

	return ini
}

function getFileText(path) {
	var r = null
	$.get(path, function(text) { r = text }, "text").fail(function() {
		throw "getFileText: getting path " + path + " failed"
	})
	return r
}

function rollSkillCheck(skill, modifier, isBounded)
{
	var tempskill = skill+modifier
	if(isBounded === true)
	{
		if(tempskill < 0) tempskill = 0
		if(tempskill > 95) tempskill = 95
	}
	var roll = combat.getRandomInt(0,100)
	if(roll < skill)
	{
		return true
	}else{
		return false
	}
}<|endoftext|>


<commit_before>ig.module(
	'plugins.lattice.wall'
).requires(
	'impact.entity',
	'impact.game'
).defines(function () {
window.lat = window.lat || {};

// Wall
lat.Wall = ig.Entity.extend({
	impassable: true,
	snapping: true,

	init: function (x, y, settings) {
		this.parent(x, y, settings);

		this.addAnim( '0', 1, [ 0]); // 0000 - NONE
		this.addAnim('10', 1, [ 1]); // 1010 - above/below
		this.addAnim( '8', 1, [ 2]); // 1000 - above
		this.addAnim( '2', 1, [ 3]); // 0010 - below
		this.addAnim( '5', 1, [ 4]); // 0101 - left/right
		this.addAnim( '1', 1, [ 5]); // 0001 - left
		this.addAnim( '4', 1, [ 6]); // 0100 - right
		this.addAnim( '3', 1, [ 7]); // 0011 - below/left
		this.addAnim( '9', 1, [ 8]); // 1001 - above/left
		this.addAnim('12', 1, [ 9]); // 1100 - above/right
		this.addAnim( '6', 1, [10]); // 0110 - right/below
		this.addAnim('13', 1, [11]); // 1101 - above/right/left
		this.addAnim('14', 1, [12]); // 1011 - above/below/left
		this.addAnim( '7', 1, [13]); // 0111 - right/below/left
		this.addAnim('11', 1, [14]); // 1110 - above/right/below
		this.addAnim('15', 1, [15]); // 1111 - above/right/below/left

		this._initWallProperties();
		ig.game._loaded && this.orient(false);
	},

	ready: function () {
		this.parent();
		this.orient(false);
	},

	_initWallProperties: function () {
		var bit = { N: 8, E: 4, S: 2, W: 1 }, dir = 0;
		Object.defineProperties(this, {
			dir: {
				get: function () {
					return dir;
				}.bind(this),
				set: function (v) {
					this.currentAnim = this.anims[dir = v];
				}.bind(this)
			},
			north: {
				get: function () {
					return !!(this.dir & bit.N);
				}.bind(this),
				set: function (v) {
					this.dir = v ? (this.dir|bit.N) : (this.dir&~bit.N);
				}.bind(this)
			},
			east: {
				get: function () {
					return !!(this.dir & bit.E);
				}.bind(this),
				set: function (v) {
					this.dir = v ? (this.dir|bit.E) : (this.dir&~bit.E);
				}.bind(this)
			},
			south: {
				get: function () {
					return !!(this.dir & bit.S);
				}.bind(this),
				set: function (v) {
					this.dir = v ? (this.dir|bit.S) : (this.dir&~bit.S);
				}.bind(this)
			},
			west: {
				get: function () {
					return !!(this.dir & bit.W);
				}.bind(this),
				set: function (v) {
					this.dir = v ? (this.dir|bit.W) : (this.dir&~bit.W);
				}.bind(this)
			}
		});
	},

	orient: function (erase) {
		var nbr = this.neighbors();
		this.north = this.south = this.east = this.west = false;

		var st = ig.game.grid.singleTenant;
		if (this.north = ((st && nbr.north) || (!st && nbr.north[0]))) {
			(st ? [nbr.north] : nbr.north).forEach(function (n) {
				n.south = !erase;
			});
		}
		if (this.east = ((st && nbr.east) || (!st && nbr.east[0]))) {
			(st ? [nbr.east] : nbr.east).forEach(function (n) {
				n.west = !erase;
			});
		}
		if (this.south = ((st && nbr.south) || (!st && nbr.south[0]))) {
			(st ? [nbr.south] : nbr.south).forEach(function (n) {
				n.north = !erase;
			});
		}
		if (this.west = ((st && nbr.west) || (!st && nbr.west[0]))) {
			(st ? [nbr.west] : nbr.west).forEach(function (n) {
				n.east = !erase;
			});
		}
	}
});

ig.Game.inject({
	removeEntity: function (ent) {
		if (ent instanceof lat.Wall) {
			ent.orient(true);
		}
		this.parent.apply(this, arguments);
	}
})

});<commit_msg>Code tidying<commit_after>ig.module(
	'plugins.lattice.wall'
).requires(
	'impact.entity',
	'impact.game'
).defines(function () {
window.lat = window.lat || {};

// Wall
lat.Wall = ig.Entity.extend({
	impassable: true,
	snapping: true,

	init: function (x, y, settings) {
		this.parent(x, y, settings);

		this.addAnim( '0', 1, [ 0]); // 0000 - NONE
		this.addAnim('10', 1, [ 1]); // 1010 - above/below
		this.addAnim( '8', 1, [ 2]); // 1000 - above
		this.addAnim( '2', 1, [ 3]); // 0010 - below
		this.addAnim( '5', 1, [ 4]); // 0101 - left/right
		this.addAnim( '1', 1, [ 5]); // 0001 - left
		this.addAnim( '4', 1, [ 6]); // 0100 - right
		this.addAnim( '3', 1, [ 7]); // 0011 - below/left
		this.addAnim( '9', 1, [ 8]); // 1001 - above/left
		this.addAnim('12', 1, [ 9]); // 1100 - above/right
		this.addAnim( '6', 1, [10]); // 0110 - right/below
		this.addAnim('13', 1, [11]); // 1101 - above/right/left
		this.addAnim('14', 1, [12]); // 1011 - above/below/left
		this.addAnim( '7', 1, [13]); // 0111 - right/below/left
		this.addAnim('11', 1, [14]); // 1110 - above/right/below
		this.addAnim('15', 1, [15]); // 1111 - above/right/below/left

		this._initWallProperties();
		ig.game._loaded && this.orient(false);
	},

	ready: function () {
		this.parent();
		this.orient(false);
	},

	_initWallProperties: function () {
		var bit = { N: 8, E: 4, S: 2, W: 1 }, dir = 0;
		Object.defineProperties(this, {
			dir: {
				get: function () {
					return dir;
				}.bind(this),
				set: function (v) {
					this.currentAnim = this.anims[dir = v];
				}.bind(this)
			},
			north: {
				get: function () {
					return !!(this.dir & bit.N);
				}.bind(this),
				set: function (v) {
					this.dir = v ? (this.dir|bit.N) : (this.dir&~bit.N);
				}.bind(this)
			},
			east: {
				get: function () {
					return !!(this.dir & bit.E);
				}.bind(this),
				set: function (v) {
					this.dir = v ? (this.dir|bit.E) : (this.dir&~bit.E);
				}.bind(this)
			},
			south: {
				get: function () {
					return !!(this.dir & bit.S);
				}.bind(this),
				set: function (v) {
					this.dir = v ? (this.dir|bit.S) : (this.dir&~bit.S);
				}.bind(this)
			},
			west: {
				get: function () {
					return !!(this.dir & bit.W);
				}.bind(this),
				set: function (v) {
					this.dir = v ? (this.dir|bit.W) : (this.dir&~bit.W);
				}.bind(this)
			}
		});
	},

	orient: function (erase) {
		var nbr = this.neighbors(), st = ig.game.grid.singleTenant;
		this.north = this.south = this.east = this.west = false;

		if (this.north = ((st && nbr.north) || (!st && nbr.north[0]))) {
			(st ? [nbr.north] : nbr.north).forEach(function (n) {
				n.south = !erase;
			});
		}
		if (this.east = ((st && nbr.east) || (!st && nbr.east[0]))) {
			(st ? [nbr.east] : nbr.east).forEach(function (n) {
				n.west = !erase;
			});
		}
		if (this.south = ((st && nbr.south) || (!st && nbr.south[0]))) {
			(st ? [nbr.south] : nbr.south).forEach(function (n) {
				n.north = !erase;
			});
		}
		if (this.west = ((st && nbr.west) || (!st && nbr.west[0]))) {
			(st ? [nbr.west] : nbr.west).forEach(function (n) {
				n.east = !erase;
			});
		}
	}
});

ig.Game.inject({
	removeEntity: function (ent) {
		if (ent instanceof lat.Wall) {
			ent.orient(true);
		}
		this.parent.apply(this, arguments);
	}
})

});<|endoftext|>


<commit_before>{
  "configurationSettings":[
    {
      "category":"Content Options",
      "fields":[
        {
          "type":"group",
          "label":"Select a group"
        },
        {
          "type":"string",
          "fieldName":"siteTitle",
          "label":"Website banner title:",
          "tooltip":"The title of this template's website.",
          "placeHolder":"My Maps"
        },
        {
          "type":"paragraph",
          "value":"The text above will be displayed in the top banner if no banner image is set. Uses group title by default."
        },
        {
          "type":"string",
          "fieldName":"siteBannerImage",
          "tooltip":"URL for the navigation header logo image. If empty, Site Title text is used.",
          "placeHolder":"http://www.mysite.com/logo.png",
          "label":"Banner logo:"
        },
        {
          "type":"paragraph",
          "value":"Image to display in the top banner. Recommended height of 75 pixels."
        },
        {
          "type":"string",
          "fieldName":"addThisProfileId",
          "label":"AddThis.com profile ID:",
          "tooltip":"Account ID for AddThis.com.",
          "placeHolder":"xa-4f3bf72958320e9e"
        },
        {
          "type":"paragraph",
          "value":"Register an AddThis.com<\/a> account for social media button analytics."
        },
        {
          "type":"string",
          "fieldName":"sourceCountry",
          "label":"Locator Source Country",
          "tooltip":"Locator Source Country",
          "placeHolder":""
        },
        {
          "type":"paragraph",
          "value":"A value representing the country. Providing this value increases geocoding speed."
        }
      ]
    },
    {
      "category":"Home page Settings",
      "fields":[
        {
          "type":"string",
          "fieldName":"homeHeading",
          "label":"Homepage heading:",
          "tooltip":"Heading displayed within the main panel on the index page.",
          "placeHolder":""
        },
        {
          "type":"string",
          "fieldName":"homeSideHeading",
          "label":"Homepage side heading:",
          "tooltip":"Heading displayed within the side panel on the index page.",
          "placeHolder":""
        },
        {
          "type":"string",
          "fieldName":"homeSnippet",
          "label":"Homepage snippet:",
          "tooltip":"Brief text displayed on the index page.",
          "placeHolder":""
        },
        {
          "type":"string",
          "fieldName":"homeSideContent",
          "label":"Homepage side content:",
          "tooltip":"Content displayed within the side panel on the index page.",
          "placeHolder":""
        }
      ]
    },
    {
      "category":"Footer Settings",
      "fields":[
        {
          "type":"string",
          "fieldName":"footerHeading",
          "label":"Footer heading:",
          "tooltip":"Heading displayed in the footer.",
          "placeHolder":""
        },
        {
          "type":"string",
          "fieldName":"footerDescription",
          "label":"Footer content:",
          "tooltip":"Content description displayed in the footer.",
          "stringFieldOption":"textarea",
          "placeHolder":"Footer Content"
        },
        {
          "type":"string",
          "fieldName":"footerLogo",
          "tooltip":"Image to display in the footer. If empty, the group's thumbnail image is used.",
          "placeHolder":"http://www.mysite.com/logo.png",
          "label":"Footer logo:"
        },
        {
          "type":"paragraph",
          "value":"Image to display in the footer. If empty, the group's thumbnail image is used."
        },
        {
          "type":"string",
          "fieldName":"footerLogoUrl",
          "tooltip":"Link for the image in the footer. If empty, link to the group on ArcGIS Online is used.",
          "placeHolder":"http://www.mysite.com/",
          "label":"Footer logo link:"
        },
        {
          "type":"paragraph",
          "value":"Link for the image in the footer. If empty, link to the group on ArcGIS Online is used."
        }
      ]
    },
    {
      "category":"General Settings",
      "fields":[
        {
          "type":"string",
          "fieldName":"theme",
          "tooltip":"Color theme to use.",
          "label":"Color theme:",
          "options":[
            {
              "label":"Blue",
              "value":"blueTheme"
            },
            {
              "label":"Red",
              "value":"redTheme"
            },
            {
              "label":"Green",
              "value":"greenTheme"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"defaultLayout",
          "tooltip":"Whether to use grid view or list view as the default layout.",
          "label":"Default layout:",
          "options":[
            {
              "label":"Grid",
              "value":"grid"
            },
            {
              "label":"List",
              "value":"list"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"sortField",
          "tooltip":"Field to sort the group items by.",
          "label":"Group sort field:",
          "options":[
            {
              "label":"Modified Date",
              "value":"modified"
            },
            {
              "label":"Title",
              "value":"title"
            },
            {
              "label":"Type",
              "value":"type"
            },
            {
              "label":"Owner",
              "value":"owner"
            },
            {
              "label":"Average rating",
              "value":"avgRating"
            },
            {
              "label":"Number of ratings",
              "value":"numRatings"
            },
            {
              "label":"Number of comments",
              "value":"numComments"
            },
            {
              "label":"Number of views",
              "value":"numViews"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"sortOrder",
          "tooltip":"Order to sort the group field.",
          "label":"Group sort order:",
          "options":[
            {
              "label":"Descending",
              "value":"desc"
            },
            {
              "label":"Ascending",
              "value":"asc"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"filterType",
          "tooltip":"Only display this type of items",
          "label":"Only display this type of items:",
          "options":[
            {
              "label":"All",
              "value":""
            },
            {
              "label":"Maps",
              "value":"Maps"
            },
            {
              "label":"Layers",
              "value":"Layers"
            },
            {
              "label":"Applications",
              "value":"Applications"
            },
            {
              "label":"Tools",
              "value":"Tools"
            },
            {
              "label":"Datafiles",
              "value":"Datafiles"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"mapViewer",
          "tooltip":"Open maps with this viewer.",
          "label":"Open maps with:",
          "options":[
            {
              "label":"Simple Viewer",
              "value":"simple"
            },
            {
              "label":"Map Viewer",
              "value":"arcgis"
            },
            {
              "label":"ArcGIS Item Page",
              "value":"item_page"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"galleryItemsPerPage",
          "tooltip":"Gallery items to show per page.",
          "label":"Items per page:",
          "options":[
            {
              "label":"3",
              "value":"3"
            },
            {
              "label":"6",
              "value":"6"
            },
            {
              "label":"9",
              "value":"9"
            },
            {
              "label":"12",
              "value":"12"
            },
            {
              "label":"15",
              "value":"15"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"searchString",
          "tooltip":"Default search string for group query.",
          "placeHolder":"",
          "label":"Search string:"
        },
        {
          "type":"paragraph",
          "value":"Default search string for group query."
        }
      ]
    },
    {
      "category":"Options",
      "fields":[
        {
          "type":"boolean",
          "fieldName":"openGalleryItemsNewWindow",
          "label":"Open items in new window.",
          "tooltip":"Open group items in a new window."
        },
        {
          "type":"paragraph",
          "value":"Open group items in a new window."
        },
        {
          "type":"boolean",
          "fieldName":"showProfileUrl",
          "label":"Show profile link.",
          "tooltip":"Links usernames to ArcGIS Online profiles."
        },
        {
          "type":"paragraph",
          "value":"Links usernames to ArcGIS Online profiles."
        },
        {
          "type":"boolean",
          "fieldName":"showSocialButtons",
          "label":"Show social media buttons.",
          "tooltip":""
        },
        {
          "type":"paragraph",
          "value":"Show social media buttons."
        },
        {
          "type":"boolean",
          "fieldName":"showFooter",
          "label":"Show footer.",
          "tooltip":"Show footer on all pages."
        },
        {
          "type":"paragraph",
          "value":"Show footer on all pages."
        },
        {
          "type":"boolean",
          "fieldName":"showArcGISOnlineButton",
          "label":"Show Map Viewer button.",
          "tooltip":"Displays the button to open the webmap in ArcGIS Online Map Viewer."
        },
        {
          "type":"paragraph",
          "value":"Displays the button to open the webmap in ArcGIS Online Map Viewer."
        },
        {
          "type":"boolean",
          "fieldName":"showMobileButtons",
          "label":"Show mobile buttons.",
          "tooltip":"Displays a mobile button to open the webmap in ArcGIS mobile app and a button to download the app."
        },
        {
          "type":"paragraph",
          "value":"Displays a mobile button to open the webmap in ArcGIS mobile app and a button to download the app."
        },
        {
          "type":"boolean",
          "fieldName":"showAttribution",
          "label":"Show attribution.",
          "tooltip":"Displays credits on the map page."
        },
        {
          "type":"paragraph",
          "value":"Displays credits on the map page."
        },
        {
          "type":"boolean",
          "fieldName":"showLicenseInfo",
          "label":"Show constraints.",
          "tooltip":"Displays constraints on the map page."
        },
        {
          "type":"paragraph",
          "value":"Displays constraints on the map page."
        },
        {
          "type":"boolean",
          "fieldName":"showMoreInfo",
          "label":"Show more info links.",
          "tooltip":"Displays More information links to the ArcGIS Online item page."
        },
        {
          "type":"paragraph",
          "value":"Displays More information links to the ArcGIS Online item page."
        },
        {
          "type":"boolean",
          "fieldName":"showGroupSort",
          "label":"Show group sort bar",
          "tooltip":"Displays group sort bar on the home page."
        },
        {
          "type":"paragraph",
          "value":"Displays group sort bar on the home page."
        },
        {
          "type":"boolean",
          "fieldName":"showViews",
          "label":"Show Views",
          "tooltip":"Displays view counts for maps"
        },
        {
          "type":"paragraph",
          "value":"Displays view counts for maps."
        },
        {
          "type":"boolean",
          "fieldName":"showComments",
          "label":"Show comments",
          "tooltip":"Shows comments for items and allows items to be commented on"
        },
        {
          "type":"paragraph",
          "value":"Shows comments for items and allows items to be commented on."
        },
        {
          "type":"boolean",
          "fieldName":"showRatings",
          "label":"Show ratings",
          "tooltip":"Shows ratings for items and allows items to be rated"
        },
        {
          "type":"paragraph",
          "value":"Shows ratings for items and allows items to be rated."
        },
        {
          "type":"boolean",
          "fieldName":"showFilterType",
          "label":"Show Filter By Type",
          "tooltip":"Shows a list allowing users to filter items by type."
        },
        {
          "type":"paragraph",
          "value":"Shows a list allowing users to filter items by type."
        },
        {
          "type":"boolean",
          "fieldName":"openLayersInViewer",
          "label":"Open Layers in ArcGIS Map Viewer",
          "tooltip":"Opens layers in the map viewer instead of the layer's REST URL"
        },
        {
          "type":"paragraph",
          "value":"Opens layers in the map viewer instead of the layer's REST URL"
        }
      ]
    }
  ],
  "values":{
    "theme":"blueTheme",
    "siteTitle":"",
    "searchString":"",
    "homeHeading":"",
    "homeSideHeading":"",
    "homeSideContent":"",
    "homeSnippet":"",
    "addThisProfileId":"xa-4f3bf72958320e9e",
    "defaultLayout":"grid",
    "sortField":"modified",
    "sortOrder":"desc",
    "filterType":"",
    "mapViewer":"simple",
    "footerHeading":"",
    "footerDescription":"",
    "footerLogo":"",
    "footerLogoUrl":"",
    "openLayersInViewer":true,
    "showProfileUrl":true,
    "showSocialButtons":true,
    "showFooter":true,
    "showMoreInfo":false,
    "showComments":false,
    "showRatings":false,
    "showViews":false,
    "showArcGISOnlineButton":false,
    "showMobileButtons":false,
    "openGalleryItemsNewWindow":false,
    "showAttribution":true,
    "showLicenseInfo":true,
    "showGroupSort":false,
    "showFilterType":false,
    "galleryItemsPerPage":"9",
    "sourceCountry":"USA"
  }
}<commit_msg>Public Gallery son<commit_after>{
  "configurationSettings":[
    {
      "category":"Content Options",
      "fields":[
        {
          "type":"group",
          "label":"Select a group"
        },
        {
          "type":"string",
          "fieldName":"siteTitle",
          "label":"Website banner title:",
          "tooltip":"The title of this template's website.",
          "placeHolder":"My Maps"
        },
        {
          "type":"paragraph",
          "value":"The text above will be displayed in the top banner if no banner image is set. Uses group title by default."
        },
        {
          "type":"string",
          "fieldName":"siteBannerImage",
          "tooltip":"URL for the navigation header logo image. If empty, Site Title text is used.",
          "placeHolder":"http://www.mysite.com/logo.png",
          "label":"Banner logo:"
        },
        {
          "type":"paragraph",
          "value":"Image to display in the top banner. Recommended height of 75 pixels."
        },
        {
          "type":"string",
          "fieldName":"addThisProfileId",
          "label":"AddThis.com profile ID:",
          "tooltip":"Account ID for AddThis.com.",
          "placeHolder":"xa-4f3bf72958320e9e"
        },
        {
          "type":"paragraph",
          "value":"Register an AddThis.com<\/a> account for social media button analytics."
        },
        {
          "type":"string",
          "fieldName":"sourceCountry",
          "label":"Locator Source Country",
          "tooltip":"Locator Source Country",
          "placeHolder":""
        },
        {
          "type":"paragraph",
          "value":"A value representing the country. Providing this value increases geocoding speed."
        }
      ]
    },
    {
      "category":"Home page Settings",
      "fields":[
        {
          "type":"string",
          "fieldName":"homeHeading",
          "label":"Homepage heading:",
          "tooltip":"Heading displayed within the main panel on the index page.",
          "placeHolder":""
        },
        {
          "type":"string",
          "fieldName":"homeSideHeading",
          "label":"Homepage side heading:",
          "tooltip":"Heading displayed within the side panel on the index page.",
          "placeHolder":""
        },
        {
          "type":"string",
          "fieldName":"homeSnippet",
          "label":"Homepage snippet:",
          "tooltip":"Brief text displayed on the index page.",
          "placeHolder":""
        },
        {
          "type":"string",
          "fieldName":"homeSideContent",
          "label":"Homepage side content:",
          "tooltip":"Content displayed within the side panel on the index page.",
          "placeHolder":""
        }
      ]
    },
    {
      "category":"Footer Settings",
      "fields":[
        {
          "type":"string",
          "fieldName":"footerHeading",
          "label":"Footer heading:",
          "tooltip":"Heading displayed in the footer.",
          "placeHolder":""
        },
        {
          "type":"string",
          "fieldName":"footerDescription",
          "label":"Footer content:",
          "tooltip":"Content description displayed in the footer.",
          "stringFieldOption":"textarea",
          "placeHolder":"Footer Content"
        },
        {
          "type":"string",
          "fieldName":"footerLogo",
          "tooltip":"Image to display in the footer. If empty, the group's thumbnail image is used.",
          "placeHolder":"http://www.mysite.com/logo.png",
          "label":"Footer logo:"
        },
        {
          "type":"paragraph",
          "value":"Image to display in the footer. If empty, the group's thumbnail image is used."
        },
        {
          "type":"string",
          "fieldName":"footerLogoUrl",
          "tooltip":"Link for the image in the footer. If empty, link to the group on ArcGIS Online is used.",
          "placeHolder":"http://www.mysite.com/",
          "label":"Footer logo link:"
        },
        {
          "type":"paragraph",
          "value":"Link for the image in the footer. If empty, link to the group on ArcGIS Online is used."
        }
      ]
    },
    {
      "category":"General Settings",
      "fields":[
        {
          "type":"string",
          "fieldName":"theme",
          "tooltip":"Color theme to use.",
          "label":"Color theme:",
          "options":[
            {
              "label":"Blue",
              "value":"blueTheme"
            },
            {
              "label":"Red",
              "value":"redTheme"
            },
            {
              "label":"Green",
              "value":"greenTheme"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"defaultLayout",
          "tooltip":"Whether to use grid view or list view as the default layout.",
          "label":"Default layout:",
          "options":[
            {
              "label":"Grid",
              "value":"grid"
            },
            {
              "label":"List",
              "value":"list"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"sortField",
          "tooltip":"Field to sort the group items by.",
          "label":"Group sort field:",
          "options":[
            {
              "label":"Modified Date",
              "value":"modified"
            },
            {
              "label":"Title",
              "value":"title"
            },
            {
              "label":"Type",
              "value":"type"
            },
            {
              "label":"Owner",
              "value":"owner"
            },
            {
              "label":"Average rating",
              "value":"avgRating"
            },
            {
              "label":"Number of ratings",
              "value":"numRatings"
            },
            {
              "label":"Number of comments",
              "value":"numComments"
            },
            {
              "label":"Number of views",
              "value":"numViews"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"sortOrder",
          "tooltip":"Order to sort the group field.",
          "label":"Group sort order:",
          "options":[
            {
              "label":"Descending",
              "value":"desc"
            },
            {
              "label":"Ascending",
              "value":"asc"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"filterType",
          "tooltip":"Only display items of this type",
          "label":"Only display items of this type:",
          "options":[
            {
              "label":"All",
              "value":""
            },
            {
              "label":"Maps",
              "value":"Maps"
            },
            {
              "label":"Layers",
              "value":"Layers"
            },
            {
              "label":"Applications",
              "value":"Applications"
            },
            {
              "label":"Tools",
              "value":"Tools"
            },
            {
              "label":"Datafiles",
              "value":"Datafiles"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"mapViewer",
          "tooltip":"Open maps with this viewer.",
          "label":"Open maps with:",
          "options":[
            {
              "label":"Simple Viewer",
              "value":"simple"
            },
            {
              "label":"Map Viewer",
              "value":"arcgis"
            },
            {
              "label":"ArcGIS Item Page",
              "value":"item_page"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"galleryItemsPerPage",
          "tooltip":"Gallery items to show per page.",
          "label":"Items per page:",
          "options":[
            {
              "label":"3",
              "value":"3"
            },
            {
              "label":"6",
              "value":"6"
            },
            {
              "label":"9",
              "value":"9"
            },
            {
              "label":"12",
              "value":"12"
            },
            {
              "label":"15",
              "value":"15"
            }
          ]
        },
        {
          "type":"string",
          "fieldName":"searchString",
          "tooltip":"Default search string for group query.",
          "placeHolder":"",
          "label":"Search string:"
        },
        {
          "type":"paragraph",
          "value":"Default search string for group query."
        }
      ]
    },
    {
      "category":"Options",
      "fields":[
        {
          "type":"boolean",
          "fieldName":"openGalleryItemsNewWindow",
          "label":"Open items in new window.",
          "tooltip":"Open group items in a new window."
        },
        {
          "type":"paragraph",
          "value":"Open group items in a new window."
        },
        {
          "type":"boolean",
          "fieldName":"showProfileUrl",
          "label":"Show profile link.",
          "tooltip":"Links usernames to ArcGIS Online profiles."
        },
        {
          "type":"paragraph",
          "value":"Links usernames to ArcGIS Online profiles."
        },
        {
          "type":"boolean",
          "fieldName":"showSocialButtons",
          "label":"Show social media buttons.",
          "tooltip":""
        },
        {
          "type":"paragraph",
          "value":"Show social media buttons."
        },
        {
          "type":"boolean",
          "fieldName":"showFooter",
          "label":"Show footer.",
          "tooltip":"Show footer on all pages."
        },
        {
          "type":"paragraph",
          "value":"Show footer on all pages."
        },
        {
          "type":"boolean",
          "fieldName":"showArcGISOnlineButton",
          "label":"Show Map Viewer button.",
          "tooltip":"Displays the button to open the webmap in ArcGIS Online Map Viewer."
        },
        {
          "type":"paragraph",
          "value":"Displays the button to open the webmap in ArcGIS Online Map Viewer."
        },
        {
          "type":"boolean",
          "fieldName":"showMobileButtons",
          "label":"Show mobile buttons.",
          "tooltip":"Displays a mobile button to open the webmap in ArcGIS mobile app and a button to download the app."
        },
        {
          "type":"paragraph",
          "value":"Displays a mobile button to open the webmap in ArcGIS mobile app and a button to download the app."
        },
        {
          "type":"boolean",
          "fieldName":"showAttribution",
          "label":"Show attribution.",
          "tooltip":"Displays credits on the map page."
        },
        {
          "type":"paragraph",
          "value":"Displays credits on the map page."
        },
        {
          "type":"boolean",
          "fieldName":"showLicenseInfo",
          "label":"Show constraints.",
          "tooltip":"Displays constraints on the map page."
        },
        {
          "type":"paragraph",
          "value":"Displays constraints on the map page."
        },
        {
          "type":"boolean",
          "fieldName":"showMoreInfo",
          "label":"Show more info links.",
          "tooltip":"Displays More information links to the ArcGIS Online item page."
        },
        {
          "type":"paragraph",
          "value":"Displays More information links to the ArcGIS Online item page."
        },
        {
          "type":"boolean",
          "fieldName":"showGroupSort",
          "label":"Show group sort bar",
          "tooltip":"Displays group sort bar on the home page."
        },
        {
          "type":"paragraph",
          "value":"Displays group sort bar on the home page."
        },
        {
          "type":"boolean",
          "fieldName":"showViews",
          "label":"Show Views",
          "tooltip":"Displays view counts for maps"
        },
        {
          "type":"paragraph",
          "value":"Displays view counts for maps."
        },
        {
          "type":"boolean",
          "fieldName":"showComments",
          "label":"Show comments",
          "tooltip":"Shows comments for items and allows items to be commented on"
        },
        {
          "type":"paragraph",
          "value":"Shows comments for items and allows items to be commented on."
        },
        {
          "type":"boolean",
          "fieldName":"showRatings",
          "label":"Show ratings",
          "tooltip":"Shows ratings for items and allows items to be rated"
        },
        {
          "type":"paragraph",
          "value":"Shows ratings for items and allows items to be rated."
        },
        {
          "type":"boolean",
          "fieldName":"showFilterType",
          "label":"Show Filter By Type",
          "tooltip":"Shows a list allowing users to filter items by type."
        },
        {
          "type":"paragraph",
          "value":"Shows a list allowing users to filter items by type, if 'Only display items of this type' does not equal 'All' it will control the default item type selected in the filter."
        },
        {
          "type":"boolean",
          "fieldName":"openLayersInViewer",
          "label":"Open Layers in ArcGIS Map Viewer",
          "tooltip":"Opens layers in the map viewer instead of the layer's REST URL"
        },
        {
          "type":"paragraph",
          "value":"Opens layers in the map viewer instead of the layer's REST URL"
        }
      ]
    }
  ],
  "values":{
    "theme":"blueTheme",
    "siteTitle":"",
    "searchString":"",
    "homeHeading":"",
    "homeSideHeading":"",
    "homeSideContent":"",
    "homeSnippet":"",
    "addThisProfileId":"xa-4f3bf72958320e9e",
    "defaultLayout":"grid",
    "sortField":"modified",
    "sortOrder":"desc",
    "filterType":"",
    "mapViewer":"simple",
    "footerHeading":"",
    "footerDescription":"",
    "footerLogo":"",
    "footerLogoUrl":"",
    "openLayersInViewer":true,
    "showProfileUrl":true,
    "showSocialButtons":true,
    "showFooter":true,
    "showMoreInfo":false,
    "showComments":false,
    "showRatings":false,
    "showViews":false,
    "showArcGISOnlineButton":false,
    "showMobileButtons":false,
    "openGalleryItemsNewWindow":false,
    "showAttribution":true,
    "showLicenseInfo":true,
    "showGroupSort":false,
    "showFilterType":false,
    "galleryItemsPerPage":"9",
    "sourceCountry":"USA"
  }
}<|endoftext|>


<commit_before>/**
 * Code for ascii-game
 * @author KeyboardFire
 * @license MIT
 */

/* TODO list
 * eliminate physics magic numbers (constants instead)
 * abstract into multiple files
 * scrolling!
 * a menu (low priority)
 * dynamic jump height (holding down W makes you jump higher)
 */

/* BUGS list
 * When you walk off an edge, you float for a bit before you fall
 */

/* List of events
 * frame(self, deltaTime): called every frame
 * collision(self, other, direction): called when an object tries to move into another object
 */

/* Physics properties
 * positionX, positionY
 * velocityX, velocityY
 * gravity
 */

var ASCIIGame = {
	init: function(el, options) {
		if (!options) options = {};

		// game is the object that stores all the game-related information (data, internal metadata, player, etc.)
		var game = {
				// DOM
				el: el, elData: [], info: null,
				// internals
				data: [], eventData: {frame: []}, physicsData: [],
				w: options.w || options.width || 80,
				h: options.h || options.height || 24,
				player: null,
				lastFrame: 0,
				mainLoop: function() {
					var currentFrame = new Date();
					var deltaTime = Math.min(currentFrame - game.lastFrame, 500);
					game.lastFrame = currentFrame;
					// old was fixed 50ms per frame

					// handle player input
					game.player.physics.velocityX *= deltaTime * 0.01;
					if (tools.keysDown[65]) { // A (go left)
						game.player.physics.velocityX = Math.max(game.player.physics.velocityX - (deltaTime * 0.001), -1);
					}
					if (tools.keysDown[68]) { // D (go right)
						game.player.physics.velocityX = Math.min(game.player.physics.velocityX + (deltaTime * 0.001), 1);
					}
					if (tools.keysDown[87]) { // W (jump)
						// can't jump in midair
						if (data.get(game.player.x, game.player.y + 1).id !== 'empty') {
							game.player.physics.velocityY = -0.02;
						}
					}

					// handle "each frame" events
					for (var i = 0; i < game.eventData.frame.length; ++i) {
						var c = tools.unpack(game.eventData.frame[i]), x = c[0], y = c[1];
						data.get(x, y).events.frame(data.get(x, y), deltaTime);
					}

					// handle physics
					for (var i = 0; i < game.physicsData.length; ++i) {
						var c = tools.unpack(game.physicsData[i]), x = c[0], y = c[1], d = data.get(x, y);
						d.physics.positionX += deltaTime * d.physics.velocityX;
						d.physics.positionY += deltaTime * d.physics.velocityY;
						if (d.physics.gravity) {
							if (data.get(x, y + 1).id === 'empty') d.physics.velocityY += deltaTime * d.physics.gravity;
							else {
								d.physics.velocityY = Math.min(d.physics.velocityY, 0);
							}
						}

						// update "real" coords if changed
						var newX = Math.round(d.physics.positionX), newY = Math.round(d.physics.positionY);
						if (x !== newX || y !== newY) {
							if (data.moveWithCollision(x, y, newX, newY)) {
								d.physics.positionX = d.x;
								d.physics.positionY = d.y;
							}
						}
						// this is really hacky
						if (d.physics.gravity && data.get(d.x, d.y + 1) !== 'empty') {
							data.collide(d, data.get(d.x, d.y + 1)); // force of gravity when standing on the ground
						}

						data.render();
					}

					tools.reqAnimFrame(game.mainLoop);
				}
			// tools stores all the stuff not directly related to the game (DOM tools, I/O, shims, algorithms, etc.)
			}, tools = {
				// simple requestAnimationFrame shim
				reqAnimFrame: function(f){window.requestAnimationFrame(f);} ||
					function(f){window.webkitRequestAnimationFrame(f);} ||
					function(f){window.mozRequestAnimationFrame(f);} ||
					function(f){window.msRequestAnimationFrame(f);} ||
					function(f){window.oRequestAnimationFrame(f);} ||
					function(f){setTimeout(f, 5);},
				keysDown: {},
				// for packing X/Y coords into a single number
				// for both values, 0 <= v < pow(2, 16)
				pack: function(a, b) {
					return (a << 16) | b;
				},
				unpack: function(n) {
					return [(n >> 16) & 0xFFFF, n & 0xFFFF];
				},
				// helper constants
				direction: {
					RIGHT: 1, LEFT: -1, UP: 2, DOWN: -2
				},
				// Bresenham's line algorithm (okay, fine, I don't understand a single bit of this)
				line: function(x1, y1, x2, y2) {
					var dx = Math.abs(x2 - x1), dy = Math.abs(y2 - y1);
					var sx = (x1 < x2) ? 1 : -1, sy = (y1 < y2) ? 1 : -1;
					var err = dx - dy;
					var points = [tools.pack(x1, y1)];

					while (x1 != x2 || y1 != y2) {
						var e2 = err * 2;
						if (e2 > -dy) {
							err -= dy;
							x1 += sx;
						}
						if (e2 < dx) {
							err += dx;
							y1 += sy;
						}
						points.push(tools.pack(x1, y1));
					}

					return points;
				}
			// data is a helper object to manipulate game.data and game.elData
			}, data = {
				// types of squares / tiles
				tile: function(type) {
					var o = ({
						empty: {color: '#000', chr: '.'},
						block: {color: '#F00', chr: '#'},
						// this is where the one and only player is initialize from, so special player attributes go here
						player: {color: '#00F', chr: '@', events: {
							// foo bar
						}, physics: {
							gravity: 0.00002
						}},
						goomba: {color: '#B73', chr: 'O', events: {
							collision: function(self, other, direction) {
								if (direction == tools.direction.UP) {
									data.set(self.x, self.y, data.tile('empty'));
								}
							}
						}, physics: {
							velocityX: -0.001,
							gravity: 0.00002
						}, counter: 0}
					})[type] || {};
					o.id = type;
					if (!o.events) o.events = {};
					return o;
				},
				get: function(x, y) {
					if (x < 0 || y < 0 || x >= game.data[0].length || y >= game.data.length) {
						return {id: 'outofbounds', events: {}};
					}
					return game.data[y][x];
				},
				set: function(x, y, val) {
					val.x = x;
					val.y = y;
					for (evtType in game.eventData) {
						if (game.data[y][x].events[evtType]) {
							game.eventData[evtType].splice(game.eventData[evtType].indexOf(tools.pack(x, y)), 1);
						}
						if (val.events[evtType]) {
							game.eventData[evtType].push(tools.pack(x, y));
						}

						if (game.data[y][x].physics) {
							game.physicsData.splice(game.physicsData.indexOf(tools.pack(x, y)), 1);
						}
						if (val.physics) {
							if (!val.physics.positionX) val.physics.positionX = x;
							if (!val.physics.positionY) val.physics.positionY = y;
							if (!val.physics.velocityX) val.physics.velocityX = 0;
							if (!val.physics.velocityY) val.physics.velocityY = 0;
							game.physicsData.push(tools.pack(x, y));
						}
					}
					game.data[y][x] = val;
					data.modified.push(tools.pack(x, y));
				},
				move: function(x1, y1, x2, y2) {
					var old = data.get(x1, y1);
					data.set(x1, y1, data.tile('empty'));
					data.set(x2, y2, old);
				},
				collide: function(d1, d2) {
					var dx = d2.x - d1.x, dy = d2.y - d1.y,
						dir = Math.abs(dy) >= Math.abs(dx) ?
						(dy > 0 ? tools.direction.DOWN : tools.direction.UP) :
						(dx > 0 ? tools.direction.RIGHT : tools.direction.LEFT);

					if (d1.events.collision) {
						d1.events.collision(d1, d2, dir);
					}
					if (d2.events.collision) {
						d2.events.collision(d2, d1, -dir);
					}
				},
				// uses tools#line to stop on collision
				moveWithCollision: function(x1, y1, x2, y2) {
					var line = tools.line(x1, y1, x2, y2);
					var c = tools.unpack(line.shift()); // origin (x1, y1)
					x2 = c[0]; y2 = c[1]; // will be changed to x1/y1 in loop below

					while (line.length > 0) {
						c = tools.unpack(line.shift());
						x1 = x2; y1 = y2;
						x2 = c[0]; y2 = c[1];

						var d1 = data.get(x1, y1), d2 = data.get(x2, y2);
						if (d2.id === 'empty') {
							data.move(x1, y1, x2, y2);
						} else {
							data.collide(d1, d2);
							return true; // collided
						}
					}

					return false; // did not collide
				},
				// render and modified exist so that the whole DOM doesn't have to be updated each frame
				render: function() {
					for (var i = 0; i < data.modified.length; ++i) {
						var c = tools.unpack(data.modified[i]), x = c[0], y = c[1];
						game.elData[y][x].style.color = game.data[y][x].color;
						game.elData[y][x].firstChild.nodeValue = game.data[y][x].chr;
					}
					data.modified = [];
				},
				modified: []
			};

		// this is the only object from init that will be exposed to outsiders
		var asciiGame = {
			play: function() {
				// clear loading message, etc.
				while (game.el.firstChild) game.el.removeChild(game.el.firstChild);

				// set up / initialize internal data and DOM
				for (var y = 0; y < game.h; ++y) {
					var row = [];

					var elRow = [];

					for (var x = 0; x < game.w; ++x) {
						row.push(data.tile('empty'));

						var node = document.createElement('span');
						node.style.color = data.tile('empty').color;
						node.appendChild(document.createTextNode(data.tile('empty').chr));
						elRow.push(node);
						game.el.appendChild(node);
					}

					game.data.push(row);

					game.elData.push(elRow);
					game.el.appendChild(document.createTextNode('\n'));
				}

				// set up info display
				game.info = document.createElement('div');
				game.info.appendChild(document.createTextNode('info goes here'));
				game.el.appendChild(document.createTextNode('\n'));
				game.el.appendChild(game.info);

				// set up event listeners
				var _firsttime = true; // for finding charcodes quickly (should be removed later); exists due to laziness
				window.onkeydown = function(e) { if (_firsttime) console.log(e.which, e.keyCode); _firsttime = false; tools.keysDown[e.which || e.keyCode] = true; };
				window.onkeyup = function(e) { tools.keysDown[e.which || e.keyCode] = false; };

				// you!
				game.player = data.tile('player');
				data.set(1, game.h - 1, game.player);

				// a random wall (for testing)
				var wx = (Math.random() * (game.w-3) | 0) + 3;
				for (var i = 1; i < 6; ++i) data.set(wx, game.h - i, data.tile('block'));

				// a random goomba (also for testing)
				var gx = (Math.random() * (game.w-9) | 0) + 9;
				if (wx === gx) gx += (gx > game.w/2 ? 1 : -1);
				data.set(gx, game.h - 4, data.tile('goomba'));

				// start playing!
				data.render();
				game.mainLoop();
			}
		};
		return asciiGame;
	}
};

window.onload = function() {
	var asciiGame = ASCIIGame.init(document.getElementById('ascii'));
	asciiGame.play();
};
<commit_msg>dynamic jump height<commit_after>/**
 * Code for ascii-game
 * @author KeyboardFire
 * @license MIT
 */

/* TODO list
 * eliminate physics magic numbers (constants instead)
 * abstract into multiple files
 * scrolling!
 * a menu (low priority)
 */

/* BUGS list
 * When you walk off an edge, you float for a bit before you fall
 */

/* List of events
 * frame(self, deltaTime): called every frame
 * collision(self, other, direction): called when an object tries to move into another object
 */

/* Physics properties
 * positionX, positionY
 * velocityX, velocityY
 * gravity
 */

var ASCIIGame = {
	init: function(el, options) {
		if (!options) options = {};

		// game is the object that stores all the game-related information (data, internal metadata, player, etc.)
		var game = {
				// DOM
				el: el, elData: [], info: null,
				// internals
				data: [], eventData: {frame: []}, physicsData: [],
				w: options.w || options.width || 80,
				h: options.h || options.height || 24,
				player: null,
				lastFrame: 0,
				mainLoop: function() {
					var currentFrame = new Date();
					var deltaTime = Math.min(currentFrame - game.lastFrame, 500);
					game.lastFrame = currentFrame;
					// old was fixed 50ms per frame

					// handle player input
					game.player.physics.velocityX *= deltaTime * 0.01;
					if (tools.keysDown[65]) { // A (go left)
						game.player.physics.velocityX = Math.max(game.player.physics.velocityX - (deltaTime * 0.001), -1);
					}
					if (tools.keysDown[68]) { // D (go right)
						game.player.physics.velocityX = Math.min(game.player.physics.velocityX + (deltaTime * 0.001), 1);
					}
					if (tools.keysDown[87]) { // W (jump)
						// can't jump in midair
						if (data.get(game.player.x, game.player.y + 1).id !== 'empty') {
							game.player.physics.velocityY = -0.02;
							game.player.holdingJump = true;
						}
					} else if (game.player.holdingJump) {
						game.player.holdingJump = false;
						game.player.physics.velocityY = Math.max(game.player.physics.velocityY, -0.005);
					}

					// handle "each frame" events
					for (var i = 0; i < game.eventData.frame.length; ++i) {
						var c = tools.unpack(game.eventData.frame[i]), x = c[0], y = c[1];
						data.get(x, y).events.frame(data.get(x, y), deltaTime);
					}

					// handle physics
					for (var i = 0; i < game.physicsData.length; ++i) {
						var c = tools.unpack(game.physicsData[i]), x = c[0], y = c[1], d = data.get(x, y);
						d.physics.positionX += deltaTime * d.physics.velocityX;
						d.physics.positionY += deltaTime * d.physics.velocityY;
						if (d.physics.gravity) {
							if (data.get(x, y + 1).id === 'empty') d.physics.velocityY += deltaTime * d.physics.gravity;
							else {
								d.physics.velocityY = Math.min(d.physics.velocityY, 0);
							}
						}

						// update "real" coords if changed
						var newX = Math.round(d.physics.positionX), newY = Math.round(d.physics.positionY);
						if (x !== newX || y !== newY) {
							if (data.moveWithCollision(x, y, newX, newY)) {
								d.physics.positionX = d.x;
								d.physics.positionY = d.y;
							}
						}
						// this is really hacky
						if (d.physics.gravity && data.get(d.x, d.y + 1) !== 'empty') {
							data.collide(d, data.get(d.x, d.y + 1)); // force of gravity when standing on the ground
						}

						data.render();
					}

					tools.reqAnimFrame(game.mainLoop);
				}
			// tools stores all the stuff not directly related to the game (DOM tools, I/O, shims, algorithms, etc.)
			}, tools = {
				// simple requestAnimationFrame shim
				reqAnimFrame: function(f){window.requestAnimationFrame(f);} ||
					function(f){window.webkitRequestAnimationFrame(f);} ||
					function(f){window.mozRequestAnimationFrame(f);} ||
					function(f){window.msRequestAnimationFrame(f);} ||
					function(f){window.oRequestAnimationFrame(f);} ||
					function(f){setTimeout(f, 5);},
				keysDown: {},
				// for packing X/Y coords into a single number
				// for both values, 0 <= v < pow(2, 16)
				pack: function(a, b) {
					return (a << 16) | b;
				},
				unpack: function(n) {
					return [(n >> 16) & 0xFFFF, n & 0xFFFF];
				},
				// helper constants
				direction: {
					RIGHT: 1, LEFT: -1, UP: 2, DOWN: -2
				},
				// Bresenham's line algorithm (okay, fine, I don't understand a single bit of this)
				line: function(x1, y1, x2, y2) {
					var dx = Math.abs(x2 - x1), dy = Math.abs(y2 - y1);
					var sx = (x1 < x2) ? 1 : -1, sy = (y1 < y2) ? 1 : -1;
					var err = dx - dy;
					var points = [tools.pack(x1, y1)];

					while (x1 != x2 || y1 != y2) {
						var e2 = err * 2;
						if (e2 > -dy) {
							err -= dy;
							x1 += sx;
						}
						if (e2 < dx) {
							err += dx;
							y1 += sy;
						}
						points.push(tools.pack(x1, y1));
					}

					return points;
				}
			// data is a helper object to manipulate game.data and game.elData
			}, data = {
				// types of squares / tiles
				tile: function(type) {
					var o = ({
						empty: {color: '#000', chr: '.'},
						block: {color: '#F00', chr: '#'},
						// this is where the one and only player is initialize from, so special player attributes go here
						player: {color: '#00F', chr: '@', events: {
							// foo bar
						}, physics: {
							gravity: 0.00002
						}},
						goomba: {color: '#B73', chr: 'O', events: {
							collision: function(self, other, direction) {
								if (direction == tools.direction.UP) {
									data.set(self.x, self.y, data.tile('empty'));
								}
							}
						}, physics: {
							velocityX: -0.001,
							gravity: 0.00002
						}, counter: 0}
					})[type] || {};
					o.id = type;
					if (!o.events) o.events = {};
					return o;
				},
				get: function(x, y) {
					if (x < 0 || y < 0 || x >= game.data[0].length || y >= game.data.length) {
						return {id: 'outofbounds', events: {}};
					}
					return game.data[y][x];
				},
				set: function(x, y, val) {
					val.x = x;
					val.y = y;
					for (evtType in game.eventData) {
						if (game.data[y][x].events[evtType]) {
							game.eventData[evtType].splice(game.eventData[evtType].indexOf(tools.pack(x, y)), 1);
						}
						if (val.events[evtType]) {
							game.eventData[evtType].push(tools.pack(x, y));
						}

						if (game.data[y][x].physics) {
							game.physicsData.splice(game.physicsData.indexOf(tools.pack(x, y)), 1);
						}
						if (val.physics) {
							if (!val.physics.positionX) val.physics.positionX = x;
							if (!val.physics.positionY) val.physics.positionY = y;
							if (!val.physics.velocityX) val.physics.velocityX = 0;
							if (!val.physics.velocityY) val.physics.velocityY = 0;
							game.physicsData.push(tools.pack(x, y));
						}
					}
					game.data[y][x] = val;
					data.modified.push(tools.pack(x, y));
				},
				move: function(x1, y1, x2, y2) {
					var old = data.get(x1, y1);
					data.set(x1, y1, data.tile('empty'));
					data.set(x2, y2, old);
				},
				collide: function(d1, d2) {
					var dx = d2.x - d1.x, dy = d2.y - d1.y,
						dir = Math.abs(dy) >= Math.abs(dx) ?
						(dy > 0 ? tools.direction.DOWN : tools.direction.UP) :
						(dx > 0 ? tools.direction.RIGHT : tools.direction.LEFT);

					if (d1.events.collision) {
						d1.events.collision(d1, d2, dir);
					}
					if (d2.events.collision) {
						d2.events.collision(d2, d1, -dir);
					}
				},
				// uses tools#line to stop on collision
				moveWithCollision: function(x1, y1, x2, y2) {
					var line = tools.line(x1, y1, x2, y2);
					var c = tools.unpack(line.shift()); // origin (x1, y1)
					x2 = c[0]; y2 = c[1]; // will be changed to x1/y1 in loop below

					while (line.length > 0) {
						c = tools.unpack(line.shift());
						x1 = x2; y1 = y2;
						x2 = c[0]; y2 = c[1];

						var d1 = data.get(x1, y1), d2 = data.get(x2, y2);
						if (d2.id === 'empty') {
							data.move(x1, y1, x2, y2);
						} else {
							data.collide(d1, d2);
							return true; // collided
						}
					}

					return false; // did not collide
				},
				// render and modified exist so that the whole DOM doesn't have to be updated each frame
				render: function() {
					for (var i = 0; i < data.modified.length; ++i) {
						var c = tools.unpack(data.modified[i]), x = c[0], y = c[1];
						game.elData[y][x].style.color = game.data[y][x].color;
						game.elData[y][x].firstChild.nodeValue = game.data[y][x].chr;
					}
					data.modified = [];
				},
				modified: []
			};

		// this is the only object from init that will be exposed to outsiders
		var asciiGame = {
			play: function() {
				// clear loading message, etc.
				while (game.el.firstChild) game.el.removeChild(game.el.firstChild);

				// set up / initialize internal data and DOM
				for (var y = 0; y < game.h; ++y) {
					var row = [];

					var elRow = [];

					for (var x = 0; x < game.w; ++x) {
						row.push(data.tile('empty'));

						var node = document.createElement('span');
						node.style.color = data.tile('empty').color;
						node.appendChild(document.createTextNode(data.tile('empty').chr));
						elRow.push(node);
						game.el.appendChild(node);
					}

					game.data.push(row);

					game.elData.push(elRow);
					game.el.appendChild(document.createTextNode('\n'));
				}

				// set up info display
				game.info = document.createElement('div');
				game.info.appendChild(document.createTextNode('info goes here'));
				game.el.appendChild(document.createTextNode('\n'));
				game.el.appendChild(game.info);

				// set up event listeners
				var _firsttime = true; // for finding charcodes quickly (should be removed later); exists due to laziness
				window.onkeydown = function(e) { if (_firsttime) console.log(e.which, e.keyCode); _firsttime = false; tools.keysDown[e.which || e.keyCode] = true; };
				window.onkeyup = function(e) { tools.keysDown[e.which || e.keyCode] = false; };

				// you!
				game.player = data.tile('player');
				data.set(1, game.h - 1, game.player);

				// a random wall (for testing)
				var wx = (Math.random() * (game.w-3) | 0) + 3;
				for (var i = 1; i < 6; ++i) data.set(wx, game.h - i, data.tile('block'));

				// a random goomba (also for testing)
				var gx = (Math.random() * (game.w-9) | 0) + 9;
				if (wx === gx) gx += (gx > game.w/2 ? 1 : -1);
				data.set(gx, game.h - 4, data.tile('goomba'));

				// start playing!
				data.render();
				game.mainLoop();
			}
		};
		return asciiGame;
	}
};

window.onload = function() {
	var asciiGame = ASCIIGame.init(document.getElementById('ascii'));
	asciiGame.play();
};
<|endoftext|>


<commit_before>/*
---

name: Form.AutoGrow

description: Automatically resizes textareas based on their content.

authors: Christoph Pojer (@cpojer)

credits: Based on a script by Gary Glass (www.bookballoon.com)

license: MIT-style license.

requires: [Core/Class.Extras, Core/Element, Core/Element.Event, Core/Element.Style, Core/Element.Dimensions]

provides: Form.AutoGrow

...
*/

(function(){

var wrapper = new Element('div').setStyles({
	overflowX: 'hidden',
	position: 'absolute',
	top: 0,
	left: -9999
});

var escapeHTML = function(string){
	return string.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
};

if (!this.Form) this.Form = {};

var AutoGrow = this.Form.AutoGrow = new Class({

	Implements: [Options, Class.Singleton, Class.Binds],

	options: {
		minHeightFactor: 2,
		margin: 0
	},

	initialize: function(element, options){
		this.setOptions(options);
		element = this.element = document.id(element);

		return this.check(element) || this.setup();
	},

	setup: function(){
		this.attach().focus().resize();
	},

	toElement: function(){
		return this.element;
	},

	attach: function(){
		this.element.addEvents({
			focus: this.bound('focus'),
			keydown: this.bound('keydown'),
			scroll: this.bound('scroll')
		});

		return this;
	},

	detach: function(){
		this.element.removeEvents({
			focus: this.bound('focus'),
			keydown: this.bound('keydown'),
			scroll: this.bound('scroll')
		});

		return this;
	},

	focus: function(){
		wrapper.setStyles(this.element.getStyles('fontSize', 'fontFamily', 'width', 'lineHeight', 'padding')).inject(document.body);

		this.minHeight = (wrapper.set('html', 'A').getHeight() + this.options.margin) * this.options.minHeightFactor;

		return this;
	},

	keydown: function(){
		this.resize.delay(15, this);
	},

	resize: function(){
		var element = this.element,
			html = escapeHTML(element.get('value')).replace(/\n|\r\n/g, '
A');

		if (wrapper.get('html') == html) return this;

		wrapper.set('html', html);
		var height = wrapper.getHeight() + this.options.margin;
		if (element.getHeight() != height){
			element.setStyle('height', this.minHeight.max(height));

			AutoGrow.triggerEvent('resize', [this]);
		}

		return this;
	},

	scroll: function(){
		this.element.scrollTo(0, 0);
	}

});

AutoGrow.extend(new Events);

})();
<commit_msg>* Adding Class-Extras to requires<commit_after>/*
---

name: Form.AutoGrow

description: Automatically resizes textareas based on their content.

authors: Christoph Pojer (@cpojer)

credits: Based on a script by Gary Glass (www.bookballoon.com)

license: MIT-style license.

requires: [Core/Class.Extras, Core/Element, Core/Element.Event, Core/Element.Style, Core/Element.Dimensions, Class-Extras/Class.Binds, Class-Extras/Class.Singleton]

provides: Form.AutoGrow

...
*/

(function(){

var wrapper = new Element('div').setStyles({
	overflowX: 'hidden',
	position: 'absolute',
	top: 0,
	left: -9999
});

var escapeHTML = function(string){
	return string.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
};

if (!this.Form) this.Form = {};

var AutoGrow = this.Form.AutoGrow = new Class({

	Implements: [Options, Class.Singleton, Class.Binds],

	options: {
		minHeightFactor: 2,
		margin: 0
	},

	initialize: function(element, options){
		this.setOptions(options);
		element = this.element = document.id(element);

		return this.check(element) || this.setup();
	},

	setup: function(){
		this.attach().focus().resize();
	},

	toElement: function(){
		return this.element;
	},

	attach: function(){
		this.element.addEvents({
			focus: this.bound('focus'),
			keydown: this.bound('keydown'),
			scroll: this.bound('scroll')
		});

		return this;
	},

	detach: function(){
		this.element.removeEvents({
			focus: this.bound('focus'),
			keydown: this.bound('keydown'),
			scroll: this.bound('scroll')
		});

		return this;
	},

	focus: function(){
		wrapper.setStyles(this.element.getStyles('fontSize', 'fontFamily', 'width', 'lineHeight', 'padding')).inject(document.body);

		this.minHeight = (wrapper.set('html', 'A').getHeight() + this.options.margin) * this.options.minHeightFactor;

		return this;
	},

	keydown: function(){
		this.resize.delay(15, this);
	},

	resize: function(){
		var element = this.element,
			html = escapeHTML(element.get('value')).replace(/\n|\r\n/g, '
A');

		if (wrapper.get('html') == html) return this;

		wrapper.set('html', html);
		var height = wrapper.getHeight() + this.options.margin;
		if (element.getHeight() != height){
			element.setStyle('height', this.minHeight.max(height));

			AutoGrow.triggerEvent('resize', [this]);
		}

		return this;
	},

	scroll: function(){
		this.element.scrollTo(0, 0);
	}

});

AutoGrow.extend(new Events);

})();
<|endoftext|>