<commit_before>'use strict'

const co = require('co-utils')

class Superchain {
  constructor (conf) {
    conf = conf || {}
    this.clear()
    this.debug = false
    this.name = conf.name || ''
  }

  add (link) {
    if (typeof link !== 'function') {
      throw new TypeError(`Unsupported chain-link type. Only functions are allowed. Input value was ${this.getLinkType(link)}`)
    }

    if (link.constructor.name === 'GeneratorFunction') {
      return this.__chain.push(co.wrap(link))
    }

    if (this.debug) {
      console.log('[Superchain] Add link to chain', this.name, link.name)
    }

    this.__chain.push(link)
  }

  final (link) {
    if (typeof link !== 'function') {
      throw new TypeError(`Unsupported chain-link type. Only functions are allowed. Input value was ${this.getLinkType(link)}`)
    }

    if (link.constructor.name === 'GeneratorFunction') {
      return this.__final.push(co.wrap(link))
    }

    if (this.debug) {
      console.log('[Superchain] Add final link to chain', this.name, link.name)
    }

    this.__final.push(link)
  }

  when (condition) {
    if (this.__subChains.has(condition)) {
      return this.__subChains.get(condition)
    }

    const subchain = new Superchain()
    let thisContext

    this.__subChains.set(condition, subchain)

    const conditionFn = function conditionFn () {
      const args = Array.prototype.slice.call(arguments, 0, -2)
      const cont = condition.apply(this, args)
      const next = arguments[arguments.length - 2]
      thisContext = this

      if (!cont) {
        return next()
      }

      const subCall = subchain.runWith.apply(subchain, [thisContext].concat(args))
      subCall.then((data) => {
        next()
      }).catch((err) => {
        thisContext.cancelChain(err)
        next()
      })
    }

    conditionFn.chain = subchain

    this.add(conditionFn)

    return subchain
  }

  run (ctx) {
    const args = Array.prototype.slice.call(arguments)
    return this.runWith.apply(this, [{}].concat(args))
  }

  runWith (thisContext, ctx) {
    const args = Array.prototype.slice.call(arguments, 1)

    thisContext.__exitChain = false
    let __chainErr = null
    thisContext.cancelChain = function cancelChain (err) {
      this.__chainErr = err
    }

    const chain = [function initialLink () {
      return arguments[args.length]()
    }].concat(this.__chain, this.__final)

    let i = 0
    const self = this
    const next = () => {
      return new Promise((resolve, reject) => {
        if (__chainErr) {
          return reject(__chainErr)
        }

        if (thisContext.__exitChain) {
          if (this.debug) {
            console.log('[Superchain] Exit chain', this.name)
          }

          return resolve(thisContext)
        }

        const fn = chain[i]

        if (!fn) {
          return resolve(thisContext)
        }

        if (this.debug) {
          console.log('[Superchain] Run chain link', this.name, fn.name)
        }

        i += 1
        try {
          const nextFn = function nextFunc () {
            return next().then((res) => {
              resolve(res)
              return res
            }).catch((err) => {
              reject(err)
            })
          }

          const exitFn = function exitFn () {
            if (self.debug) {
              console.log('[Superchain] Chain exit called', self.name)
            }

            self.__exitChain = true
            return resolve(thisContext)
          }

          fn.apply(thisContext, args.concat([nextFn, exitFn]))
        } catch (err) {
          return reject(err)
        }
      })
    }

    if (this.debug) {
      console.log('[Superchain] Run chain', this.name)
    }

    const p = next().then((res) => {
      return res
    })

    return {
      then (fn) {
        return p.then(fn)
      },
      catch (fn) {
        return p.catch(fn)
      }
    }
  }

  getLinkType (link) {
    const type = typeof link
    if (Array.isArray(link)) {
      return 'array'
    } else if (type === 'object' && link === null) {
      return 'null'
    }

    return type
  }

  clear () {
    if (this.debug) {
      console.log('[Superchain] Run chain', this.name)
    }

    this.__chain = []
    this.__final = []
    this.__subChains = new Map()
  }
}

module.exports = Superchain
<commit_msg>Call promise if link fn is one<commit_after>'use strict'

const co = require('co-utils')

class Superchain {
  constructor (conf) {
    conf = conf || {}
    this.clear()
    this.debug = false
    this.name = conf.name || ''
  }

  add (link) {
    if (typeof link !== 'function') {
      throw new TypeError(`Unsupported chain-link type. Only functions are allowed. Input value was ${this.getLinkType(link)}`)
    }

    if (link.constructor.name === 'GeneratorFunction') {
      return this.__chain.push(co.wrap(link))
    }

    if (this.debug) {
      console.log('[Superchain] Add link to chain', this.name, link.name)
    }

    this.__chain.push(link)
  }

  final (link) {
    if (typeof link !== 'function') {
      throw new TypeError(`Unsupported chain-link type. Only functions are allowed. Input value was ${this.getLinkType(link)}`)
    }

    if (link.constructor.name === 'GeneratorFunction') {
      return this.__final.push(co.wrap(link))
    }

    if (this.debug) {
      console.log('[Superchain] Add final link to chain', this.name, link.name)
    }

    this.__final.push(link)
  }

  when (condition) {
    if (this.__subChains.has(condition)) {
      return this.__subChains.get(condition)
    }

    const subchain = new Superchain()
    let thisContext

    this.__subChains.set(condition, subchain)

    const conditionFn = function conditionFn () {
      const args = Array.prototype.slice.call(arguments, 0, -2)
      const cont = condition.apply(this, args)
      const next = arguments[arguments.length - 2]
      thisContext = this

      if (!cont) {
        return next()
      }

      const subCall = subchain.runWith.apply(subchain, [thisContext].concat(args))
      subCall.then((data) => {
        next()
      }).catch((err) => {
        thisContext.cancelChain(err)
        next()
      })
    }

    conditionFn.chain = subchain

    this.add(conditionFn)

    return subchain
  }

  run (ctx) {
    const args = Array.prototype.slice.call(arguments)
    return this.runWith.apply(this, [{}].concat(args))
  }

  runWith (thisContext, ctx) {
    const args = Array.prototype.slice.call(arguments, 1)

    thisContext.__exitChain = false
    let __chainErr = null
    thisContext.cancelChain = function cancelChain (err) {
      this.__chainErr = err
    }

    const chain = [function initialLink () {
      return arguments[args.length]()
    }].concat(this.__chain, this.__final)

    let i = 0
    const self = this
    const next = () => {
      return new Promise((resolve, reject) => {
        if (__chainErr) {
          return reject(__chainErr)
        }

        if (thisContext.__exitChain) {
          if (this.debug) {
            console.log('[Superchain] Exit chain', this.name)
          }

          return resolve(thisContext)
        }

        const fn = chain[i]

        if (!fn) {
          return resolve(thisContext)
        }

        if (this.debug) {
          console.log('[Superchain] Run chain link', this.name, fn.name)
        }

        i += 1
        try {
          const nextFn = function nextFunc () {
            return next().then((res) => {
              resolve(res)
              return res
            }).catch((err) => {
              reject(err)
            })
          }

          const exitFn = function exitFn () {
            if (self.debug) {
              console.log('[Superchain] Chain exit called', self.name)
            }

            self.__exitChain = true
            return resolve(thisContext)
          }

          const p = fn.apply(thisContext, args.concat([nextFn, exitFn]))
          if (p && p.then) {
            p.then(() => {}).catch((err) => reject(err))
          }
        } catch (err) {
          return reject(err)
        }
      })
    }

    if (this.debug) {
      console.log('[Superchain] Run chain', this.name)
    }

    const p = next().then((res) => {
      return res
    })

    return {
      then (fn) {
        return p.then(fn)
      },
      catch (fn) {
        return p.catch(fn)
      }
    }
  }

  getLinkType (link) {
    const type = typeof link
    if (Array.isArray(link)) {
      return 'array'
    } else if (type === 'object' && link === null) {
      return 'null'
    }

    return type
  }

  clear () {
    if (this.debug) {
      console.log('[Superchain] Run chain', this.name)
    }

    this.__chain = []
    this.__final = []
    this.__subChains = new Map()
  }
}

module.exports = Superchain
<|endoftext|>


<commit_before>const wiki = require('./wiki')
const { EventEmitter } = require('events')

let $id = 0
const HINT_TIMEOUT = 40 // seconds
const BACKLINKS_TIMEOUT = 90 // seconds

module.exports = WikiBattle

function WikiBattle (io, origin, goal) {
  if (!(this instanceof WikiBattle)) return new WikiBattle(io, origin, goal)
  EventEmitter.call(this)
  $id %= 2e15 // should be enough for anyone!
  this.id = `game:${++$id}`
  this.players = []
  this.origin = origin
  this.goal = goal
  this.io = io
}

WikiBattle.prototype.emitSocket = function (...args) {
  return this.players.forEach(player => {
    const sock = player.sock
    if (sock) {
      sock.emit(...args)
    }
  })
}

WikiBattle.prototype.connect = function (connectingPlayer) {
  this.players.forEach(player => {
    player.notifyConnect(connectingPlayer)
    connectingPlayer.notifyConnect(player)
  })
  this.players.push(connectingPlayer)
  return this
}

WikiBattle.prototype.disconnect = function (disconnectingPlayer) {
  disconnectingPlayer.disconnect()
  this.navigate(disconnectingPlayer, null)

  var connected = this.players.filter(p => p.connected)
  connected.forEach(p => {
    p.notifyDisconnect(disconnectingPlayer)
  })
  if (connected.length === 1) {
    connected[0].win()
    this.end()
  }
}

WikiBattle.prototype.checkWin = function () {
  const hasWon = p => p.current() === this.goal
  if (this.players.some(hasWon)) {
    this.players.forEach(p => {
      hasWon(p) ? p.win() : p.lose()
    })
    this.end()
  }
}

WikiBattle.prototype.navigate = function (player, to) {
  wiki.get(player.current(), (e, page) => {
    if(!e && page.linksTo(to)){
      player.navigateTo(to)
      this.emitSocket('navigated', player.id, to)
      this.checkWin()
    } else {
      //Maybe put some fancy "Do not cheat" page here?
    }
  })
}

WikiBattle.prototype.sendHint = function () {
  wiki.get(this.goal, (e, page) => {
    this.emitSocket('hint', e || page.getHint())
  })
}

WikiBattle.prototype.sendBacklinks = function () {
  wiki.get(this.goal, (e, page) => {
    if (e) return this.emitSocket('backlinks', e)
    page.getBacklinks((e, back) => {
      this.emitSocket('backlinks', e, back)
    })
  })
}

WikiBattle.prototype.sendPaths = function () {
  const paths = this.players.reduce((paths, p) => {
    paths[p.id] = p.path
    return paths
  }, {})
  this.emitSocket('paths', paths)
}

WikiBattle.prototype.start = function () {
  this.emitSocket('start', this.origin, this.goal)

  this.players.forEach(p => {
    this.navigate(p, this.origin)
  })

  this.hintTimeout = setTimeout(this.sendHint.bind(this), HINT_TIMEOUT * 1000)
  this.backlinksTimeout = setTimeout(this.sendBacklinks.bind(this), BACKLINKS_TIMEOUT * 1000)
}

WikiBattle.prototype.end = function () {
  this.sendPaths()
  clearTimeout(this.hintTimeout)
  clearTimeout(this.backlinksTimeout)
}

WikiBattle.prototype.notifyScroll = function (scroller, top, width) {
  this.players.forEach(player => {
    if (scroller !== player) {
      player.notifyScroll(scroller, top, width)
    }
  })
}
<commit_msg>fix linksTo() check when on the first page<commit_after>const wiki = require('./wiki')
const { EventEmitter } = require('events')

let $id = 0
const HINT_TIMEOUT = 40 // seconds
const BACKLINKS_TIMEOUT = 90 // seconds

module.exports = WikiBattle

function WikiBattle (io, origin, goal) {
  if (!(this instanceof WikiBattle)) return new WikiBattle(io, origin, goal)
  EventEmitter.call(this)
  $id %= 2e15 // should be enough for anyone!
  this.id = `game:${++$id}`
  this.players = []
  this.origin = origin
  this.goal = goal
  this.io = io
}

WikiBattle.prototype.emitSocket = function (...args) {
  return this.players.forEach(player => {
    const sock = player.sock
    if (sock) {
      sock.emit(...args)
    }
  })
}

WikiBattle.prototype.connect = function (connectingPlayer) {
  this.players.forEach(player => {
    player.notifyConnect(connectingPlayer)
    connectingPlayer.notifyConnect(player)
  })
  this.players.push(connectingPlayer)
  return this
}

WikiBattle.prototype.disconnect = function (disconnectingPlayer) {
  disconnectingPlayer.disconnect()
  this.navigate(disconnectingPlayer, null)

  var connected = this.players.filter(p => p.connected)
  connected.forEach(p => {
    p.notifyDisconnect(disconnectingPlayer)
  })
  if (connected.length === 1) {
    connected[0].win()
    this.end()
  }
}

WikiBattle.prototype.checkWin = function () {
  const hasWon = p => p.current() === this.goal
  if (this.players.some(hasWon)) {
    this.players.forEach(p => {
      hasWon(p) ? p.win() : p.lose()
    })
    this.end()
  }
}

WikiBattle.prototype.navigateInner = function (player, to) {
  player.navigateTo(to)
  this.emitSocket('navigated', player.id, to)
  this.checkWin()
}

WikiBattle.prototype.navigate = function (player, to) {
  if (!player.current()) {
    this.navigateInner(player, to)
  }
  wiki.get(player.current(), (e, page) => {
    if(!e && page.linksTo(to)){
      this.navigateInner(player, to)
    } else {
      //Maybe put some fancy "Do not cheat" page here?
    }
  })
}

WikiBattle.prototype.sendHint = function () {
  wiki.get(this.goal, (e, page) => {
    this.emitSocket('hint', e || page.getHint())
  })
}

WikiBattle.prototype.sendBacklinks = function () {
  wiki.get(this.goal, (e, page) => {
    if (e) return this.emitSocket('backlinks', e)
    page.getBacklinks((e, back) => {
      this.emitSocket('backlinks', e, back)
    })
  })
}

WikiBattle.prototype.sendPaths = function () {
  const paths = this.players.reduce((paths, p) => {
    paths[p.id] = p.path
    return paths
  }, {})
  this.emitSocket('paths', paths)
}

WikiBattle.prototype.start = function () {
  this.emitSocket('start', this.origin, this.goal)

  this.players.forEach(p => {
    this.navigate(p, this.origin)
  })

  this.hintTimeout = setTimeout(this.sendHint.bind(this), HINT_TIMEOUT * 1000)
  this.backlinksTimeout = setTimeout(this.sendBacklinks.bind(this), BACKLINKS_TIMEOUT * 1000)
}

WikiBattle.prototype.end = function () {
  this.sendPaths()
  clearTimeout(this.hintTimeout)
  clearTimeout(this.backlinksTimeout)
}

WikiBattle.prototype.notifyScroll = function (scroller, top, width) {
  this.players.forEach(player => {
    if (scroller !== player) {
      player.notifyScroll(scroller, top, width)
    }
  })
}
<|endoftext|>


<commit_before>const makeOperator = (logic) => {
  return (...predicates) => {
    const fn = [];
    const js = `!!(${predicates.map((p) => (
      `fn[${fn.push(p) - 1}](v)`
    )).join(logic)})`;
    return v => eval(js);
  }
}

export const and = makeOperator('&&');
export const or = makeOperator('||');
export const not = predicate => v => !predicate(v);<commit_msg>removing unneded parens<commit_after>const makeOperator = (logic) => {
  return (...predicates) => {
    const fn = [];
    const js = `!!(${predicates.map(p => (
      `fn[${fn.push(p) - 1}](v)`
    )).join(logic)})`;
    return v => eval(js);
  }
}

export const and = makeOperator('&&');
export const or = makeOperator('||');
export const not = predicate => v => !predicate(v);<|endoftext|>


<commit_before>import {Animations} from 'chart.js';
import {clipArea, unclipArea, isFinite, merge, valueOrDefault} from 'chart.js/helpers';
import handleEvent from './events';
import BoxAnnotation from './types/box';
import LineAnnotation from './types/line';

const chartElements = new Map();

const annotationTypes = {
	box: BoxAnnotation,
	line: LineAnnotation
};

export default {
	id: 'annotation',

	beforeUpdate(chart, options) {
		bindAfterDataLimits(chart, options);
	},

	afterUpdate(chart, options) {
		updateElements(chart, options);
	},

	resize(chart, options) {
		updateElements(chart, options, 'resize');
	},

	beforeDatasetDraw(chart, options) {
		draw(chart, options, 'beforeDatasetsDraw');
	},

	afterDatasetsDraw(chart, options) {
		draw(chart, options, 'afterDatasetsDraw');
	},

	afterDraw(chart, options) {
		draw(chart, options, 'afterDraw');
	},

	afterEvent(chart, event, _replay, options) {
		const events = options.events || [];
		if (events.indexOf(event.type) !== -1) {
			handleEvent(event, chartElements.get(chart));
		}
	},

	destroy(chart) {
		chartElements.remove(chart);
	},

	defaults: {
		drawTime: 'afterDatasetsDraw',
		dblClickSpeed: 350, // ms
		events: [],
		annotations: [],
		animation: {
			numbers: {
				properties: ['x', 'y', 'x2', 'y2', 'width', 'height'],
				type: 'number'
			},
		}
	},
};

function updateElements(chart, options, mode) {
	const chartAnims = chart.options.animation;
	const animOpts = chartAnims && merge({}, [chartAnims, options.animation]);
	const animations = new Animations(chart, animOpts, mode);

	const elements = chartElements.get(chart) || (chartElements.set(chart, []).get(chart));
	const annotations = options.annotations || [];
	const count = annotations.length;
	const start = elements.length;

	if (start < count) {
		const add = count - start;
		elements.splice(start, 0, ...new Array(add));
	} else if (start > count) {
		elements.splice(count, start - count);
	}
	for (let i = 0; i < annotations.length; i++) {
		const annotation = annotations[i];
		let el = elements[i];
		const elType = annotationTypes[annotation.type] || annotationTypes.line;
		if (!el || !(el instanceof elType)) {
			el = elements[i] = new elType();
		}
		const properties = calculateElementProperties(chart, annotation, elType.defaults);
		animations.update(el, properties);
	}
}

const scaleValue = (scale, value, fallback) => isFinite(value) ? scale.getPixelForValue(value) : fallback;

function calculateElementProperties(chart, options, defaults) {
	const scale = chart.scales[options.scaleID];

	let {top: y, left: x, bottom: y2, right: x2} = chart.chartArea;
	let min, max;

	if (scale) {
		min = scaleValue(scale, options.value, NaN);
		max = scaleValue(scale, options.endValue, min);
		if (scale.isHorizontal()) {
			x = Math.min(min, max);
			x2 = Math.max(min, max);
		} else {
			y = Math.min(min, max);
			y2 = Math.max(min, max);
		}
	} else {
		const xScale = chart.scales[options.xScaleID];
		const yScale = chart.scales[options.yScaleID];

		if (xScale) {
			min = scaleValue(xScale, options.xMin, x);
			max = scaleValue(xScale, options.xMax, x2);
			x = Math.min(min, max);
			x2 = Math.max(min, max);
		}

		if (yScale) {
			min = scaleValue(yScale, options.yMin, y2);
			max = scaleValue(yScale, options.yMax, y);
			y = Math.min(min, max);
			y2 = Math.max(min, max);
		}
	}

	return {
		chartInstance: chart,
		x,
		y,
		x2,
		y2,
		width: x2 - x,
		height: y2 - y,
		options: merge(Object.create(null), [defaults, options])
	};
}

function draw(chart, options, caller) {
	const {ctx, chartArea} = chart;
	const elements = chartElements.get(chart);

	clipArea(ctx, chartArea);
	for (let i = 0; i < elements.length; i++) {
		const el = elements[i];
		if ((el.options.drawTime || options.drawTime || caller) === caller) {
			el.draw(ctx);
		}
	}
	unclipArea(ctx);
}

const binds = new WeakSet();
function bindAfterDataLimits(chart, options) {
	const scales = chart.scales || {};
	Object.keys(scales).forEach(id => {
		const scale = chart.scales[id];
		if (binds.has(scale)) {
			return;
		}
		const originalHook = scale.afterDataLimits;
		scale.afterDataLimits = function(...args) {
			if (originalHook) {
				originalHook.apply(scale, [...args]);
			}
			adjustScaleRange(scale, options);
		};
		binds.add(scale);
	});
}

function getAnnotationOptions(chart, options) {
	const elems = chartElements.get(chart);
	if (elems) {
		return elems.map(el => el.options);
	}
	return options.annotations || [];
}

function adjustScaleRange(scale, options) {
	const annotations = getAnnotationOptions(scale.chart, options);
	const range = getScaleLimits(scale, annotations);
	let changed = false;
	if (isFinite(range.min) &&
		typeof scale.options.min === 'undefined' &&
		typeof scale.options.suggestedMin === 'undefined') {
		scale.min = range.min;
		changed = true;
	}
	if (isFinite(range.max) &&
		typeof scale.options.max === 'undefined' &&
		typeof scale.options.suggestedMax === 'undefined') {
		scale.max = range.max;
		changed = true;
	}
	if (changed && typeof scale.handleTickRangeOptions === 'function') {
		scale.handleTickRangeOptions();
	}
}

function getScaleLimits(scale, annotations) {
	const axis = scale.axis;
	const scaleID = scale.id;
	const scaleIDOption = scale.axis + 'ScaleID';
	const scaleAnnotations = annotations.filter(annotation => annotation[scaleIDOption] === scaleID || annotation.scaleID === scaleID);
	let min = valueOrDefault(scale.min, Number.NEGATIVE_INFINITY);
	let max = valueOrDefault(scale.max, Number.POSITIVE_INFINITY);
	scaleAnnotations.forEach(annotation => {
		['value', 'endValue', axis + 'Min', axis + 'Max'].forEach(prop => {
			if (prop in annotation) {
				const value = annotation[prop];
				min = Math.min(min, value);
				max = Math.max(max, value);
			}
		});
	});
	return {min, max};
}

<commit_msg>Reflect IPlugin changes from Chart.js (#238)<commit_after>import {Animations} from 'chart.js';
import {clipArea, unclipArea, isFinite, merge, valueOrDefault} from 'chart.js/helpers';
import handleEvent from './events';
import BoxAnnotation from './types/box';
import LineAnnotation from './types/line';

const chartElements = new Map();

const annotationTypes = {
	box: BoxAnnotation,
	line: LineAnnotation
};

export default {
	id: 'annotation',

	beforeUpdate(chart, args, options) {
		if (!args.mode) {
			bindAfterDataLimits(chart, options);
		}
	},

	afterUpdate(chart, args, options) {
		updateElements(chart, options, args.mode);
	},

	beforeDatasetDraw(chart, options) {
		draw(chart, options, 'beforeDatasetsDraw');
	},

	afterDatasetsDraw(chart, options) {
		draw(chart, options, 'afterDatasetsDraw');
	},

	afterDraw(chart, options) {
		draw(chart, options, 'afterDraw');
	},

	afterEvent(chart, event, _replay, options) {
		const events = options.events || [];
		if (events.indexOf(event.type) !== -1) {
			handleEvent(event, chartElements.get(chart));
		}
	},

	destroy(chart) {
		chartElements.remove(chart);
	},

	defaults: {
		drawTime: 'afterDatasetsDraw',
		dblClickSpeed: 350, // ms
		events: [],
		annotations: [],
		animation: {
			numbers: {
				properties: ['x', 'y', 'x2', 'y2', 'width', 'height'],
				type: 'number'
			},
		}
	},
};

function updateElements(chart, options, mode) {
	const chartAnims = chart.options.animation;
	const animOpts = chartAnims && merge({}, [chartAnims, options.animation]);
	const animations = new Animations(chart, animOpts, mode);

	const elements = chartElements.get(chart) || (chartElements.set(chart, []).get(chart));
	const annotations = options.annotations || [];
	const count = annotations.length;
	const start = elements.length;

	if (start < count) {
		const add = count - start;
		elements.splice(start, 0, ...new Array(add));
	} else if (start > count) {
		elements.splice(count, start - count);
	}
	for (let i = 0; i < annotations.length; i++) {
		const annotation = annotations[i];
		let el = elements[i];
		const elType = annotationTypes[annotation.type] || annotationTypes.line;
		if (!el || !(el instanceof elType)) {
			el = elements[i] = new elType();
		}
		const properties = calculateElementProperties(chart, annotation, elType.defaults);
		animations.update(el, properties);
	}
}

const scaleValue = (scale, value, fallback) => isFinite(value) ? scale.getPixelForValue(value) : fallback;

function calculateElementProperties(chart, options, defaults) {
	const scale = chart.scales[options.scaleID];

	let {top: y, left: x, bottom: y2, right: x2} = chart.chartArea;
	let min, max;

	if (scale) {
		min = scaleValue(scale, options.value, NaN);
		max = scaleValue(scale, options.endValue, min);
		if (scale.isHorizontal()) {
			x = Math.min(min, max);
			x2 = Math.max(min, max);
		} else {
			y = Math.min(min, max);
			y2 = Math.max(min, max);
		}
	} else {
		const xScale = chart.scales[options.xScaleID];
		const yScale = chart.scales[options.yScaleID];

		if (xScale) {
			min = scaleValue(xScale, options.xMin, x);
			max = scaleValue(xScale, options.xMax, x2);
			x = Math.min(min, max);
			x2 = Math.max(min, max);
		}

		if (yScale) {
			min = scaleValue(yScale, options.yMin, y2);
			max = scaleValue(yScale, options.yMax, y);
			y = Math.min(min, max);
			y2 = Math.max(min, max);
		}
	}

	return {
		chartInstance: chart,
		x,
		y,
		x2,
		y2,
		width: x2 - x,
		height: y2 - y,
		options: merge(Object.create(null), [defaults, options])
	};
}

function draw(chart, options, caller) {
	const {ctx, chartArea} = chart;
	const elements = chartElements.get(chart);

	clipArea(ctx, chartArea);
	for (let i = 0; i < elements.length; i++) {
		const el = elements[i];
		if ((el.options.drawTime || options.drawTime || caller) === caller) {
			el.draw(ctx);
		}
	}
	unclipArea(ctx);
}

const binds = new WeakSet();
function bindAfterDataLimits(chart, options) {
	const scales = chart.scales || {};
	Object.keys(scales).forEach(id => {
		const scale = chart.scales[id];
		if (binds.has(scale)) {
			return;
		}
		const originalHook = scale.afterDataLimits;
		scale.afterDataLimits = function(...args) {
			if (originalHook) {
				originalHook.apply(scale, [...args]);
			}
			adjustScaleRange(scale, options);
		};
		binds.add(scale);
	});
}

function getAnnotationOptions(chart, options) {
	const elems = chartElements.get(chart);
	if (elems && elems.length) {
		return elems.map(el => el.options);
	}
	return options.annotations || [];
}

function adjustScaleRange(scale, options) {
	const annotations = getAnnotationOptions(scale.chart, options);
	const range = getScaleLimits(scale, annotations);
	let changed = false;
	if (isFinite(range.min) &&
		typeof scale.options.min === 'undefined' &&
		typeof scale.options.suggestedMin === 'undefined') {
		scale.min = range.min;
		changed = true;
	}
	if (isFinite(range.max) &&
		typeof scale.options.max === 'undefined' &&
		typeof scale.options.suggestedMax === 'undefined') {
		scale.max = range.max;
		changed = true;
	}
	if (changed && typeof scale.handleTickRangeOptions === 'function') {
		scale.handleTickRangeOptions();
	}
}

function getScaleLimits(scale, annotations) {
	const axis = scale.axis;
	const scaleID = scale.id;
	const scaleIDOption = scale.axis + 'ScaleID';
	const scaleAnnotations = annotations.filter(annotation => annotation[scaleIDOption] === scaleID || annotation.scaleID === scaleID);
	let min = valueOrDefault(scale.min, Number.NEGATIVE_INFINITY);
	let max = valueOrDefault(scale.max, Number.POSITIVE_INFINITY);
	scaleAnnotations.forEach(annotation => {
		['value', 'endValue', axis + 'Min', axis + 'Max'].forEach(prop => {
			if (prop in annotation) {
				const value = annotation[prop];
				min = Math.min(min, value);
				max = Math.max(max, value);
			}
		});
	});
	return {min, max};
}

<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import { Route } from 'react-router';

import App from './App';
import { Counter } from './counter';
import { CRUD } from './crud';
import { REST } from './REST';
import { GraphQL } from './graphql';

const Routes = () => (

    <Route exact path="/" component={App} />
    <Route path="/counter" component={Counter} />
    <Route path="/crud" component={CRUD} />
    <Route path="/rest" component={REST} />
    <Route path="/graphql" component={GraphQL} />

);

export default connect()(Routes);
<commit_msg>81st Commit<commit_after>import React from 'react';
import { connect } from 'react-redux';
import { Route } from 'react-router';

import App from './App';
import { Counter } from './counter';
import { CRUD } from './crud';
import { REST } from './rest';
import { GraphQL } from './graphql';

const Routes = () => (

    <Route exact path="/" component={App} />
    <Route path="/counter" component={Counter} />
    <Route path="/crud" component={CRUD} />
    <Route path="/rest" component={REST} />
    <Route path="/graphql" component={GraphQL} />

);

export default connect()(Routes);
<|endoftext|>


<commit_before>/* jshint maxlen:false */
define(['dojo/has', 'esri/config'], function (has, esriConfig) {
    // force api to use CORS on mapserv thus removing the test request on app load
    // e.g. http://mapserv.utah.gov/ArcGIS/rest/info?f=json
    esriConfig.defaults.io.corsEnabledServers.push('mapserv.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('basemaps.utah.gov');

    window.AGRC = {
        // errorLogger: ijit.modules.ErrorLogger
        errorLogger: null,

        // app: app.App
        //      global reference to App
        app: null,

        // version.: String
        //      The version number.
        version: '0.1.0#router',

        // apiKey: String
        //      The api key used for services on api.mapserv.utah.gov
        apiKey: '', // acquire at developer.mapserv.utah.gov

        urls: {
            mapService: '/arcgis/rest/services/WRI/MapService/MapServer'
        },

        layerIndices: {
            point: 0,
            line: 1,
            poly: 2
        },

        fieldNames: {
            Project_ID: 'Project_ID'
        },

        topics: {
            projectIdsChanged: 'wri/projectIdsChanged'
        }
    };

    if (has('agrc-build') === 'prod') {
        // mapserv.utah.gov
        window.AGRC.apiKey = 'AGRC-A94B063C533889';
    } else if (has('agrc-build') === 'stage') {
        // test.mapserv.utah.gov
        window.AGRC.apiKey = 'AGRC-AC122FA9671436';
    } else {
        // localhost
        window.AGRC.apiKey = 'AGRC-E5B94F99865799';
    }

    return window.AGRC;
});
<commit_msg>use has for url creation.<commit_after>/* jshint maxlen:false */
define(['dojo/has', 'esri/config'], function (has, esriConfig) {
    // force api to use CORS on mapserv thus removing the test request on app load
    // e.g. http://mapserv.utah.gov/ArcGIS/rest/info?f=json
    esriConfig.defaults.io.corsEnabledServers.push('mapserv.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('basemaps.utah.gov');

    window.AGRC = {
        // errorLogger: ijit.modules.ErrorLogger
        errorLogger: null,

        // app: app.App
        //      global reference to App
        app: null,

        // version.: String
        //      The version number.
        version: '0.1.0#router',

        // apiKey: String
        //      The api key used for services on api.mapserv.utah.gov
        apiKey: '', // acquire at developer.mapserv.utah.gov

        urls: {
            mapService: '/arcgis/rest/services/WRI/MapService/MapServer'
        },

        layerIndices: {
            point: 0,
            line: 1,
            poly: 2
        },

        fieldNames: {
            Project_ID: 'Project_ID'
        },

        topics: {
            projectIdsChanged: 'wri/projectIdsChanged'
        }
    };

    if (has('agrc-build') === 'prod') {
        // mapserv.utah.gov
        window.AGRC.apiKey = 'AGRC-A94B063C533889';
    } else if (has('agrc-build') === 'stage') {
        // test.mapserv.utah.gov
        window.AGRC.apiKey = 'AGRC-AC122FA9671436';

        var fragment = window.AGRC.urls.mapService;
        window.AGRC.urls.MapService = 'https://wrimaps.at.utah.gov' + fragment;

        esriConfig.defaults.io.corsEnabledServers.push('wrimaps.at.utah.gov');
    } else {
        // localhost
        window.AGRC.apiKey = 'AGRC-E5B94F99865799';
    }

    return window.AGRC;
});
<|endoftext|>


<commit_before>define([
    'dojo/has',
    'dojo/request/xhr',

    'esri/config'
], function (
    has,
    xhr,

    esriConfig
) {
    esriConfig.defaults.io.corsEnabledServers.push('mapserv.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('basemaps.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('api.mapserv.utah.gov');

    window.AGRC = {
        // app: app.App
        //      global reference to App
        app: null,

        // version.: String
        //      The version number.
        version: '1.4.2',

        //apiKey: String
        apiKey: null,

        appName: 'addressediting',

        // cache level 13
        labelsMinScale: 2257,

        urls: {
            basemap: '//mapserv.utah.gov/ArcGIS/rest/services/BaseMaps/Vector/MapServer',
            editLayer: '/arcgis/rest/services/AddressEditor/Editing/FeatureServer/',
            viewLayer: '/arcgis/rest/services/AddressEditor/Viewing/FeatureServer/',
            geometryService: '/arcgis/rest/services/Geometry/GeometryServer',
            downloadGp: '/arcgis/rest/services/AddressEditor/DownloadTool/GPServer/Download%20Address%20Points',
            redline: null
        },

        fieldNames: {
            Editor: 'Editor',
            ModifyDate: 'ModifyDate',
            FullAddress: 'FullAdd'
        }
    };

    if (has('agrc-api-key') === 'prod') {
        // addressediting.utah.gov
        window.AGRC.apiKey = 'AGRC-7D48DD3D449390';
        window.AGRC.urls.redline = 'http://mapserv.utah.gov/chalkdust';
    } else if (has('agrc-api-key') === 'stage') {
        // test.mapserv.utah.gov
        window.AGRC.apiKey = 'AGRC-FFCDAD6B933051';
        window.AGRC.urls.redline = 'http://test.mapserv.utah.gov/chalkdust';
        window.AGRC.quadWord = 'opera-event-little-pinball';
    } else {
        // localhost
        xhr(require.baseUrl + 'secrets.json', {
            handleAs: 'json',
            sync: true
        }).then(function (secrets) {
            window.AGRC.quadWord = secrets.quadWord;
            window.AGRC.apiKey = secrets.apiKey;
        }, function () {
            throw 'Error getting secrets!';
        });
    }

    return window.AGRC;
});
<commit_msg>and production quad word<commit_after>define([
    'dojo/has',
    'dojo/request/xhr',

    'esri/config'
], function (
    has,
    xhr,

    esriConfig
) {
    esriConfig.defaults.io.corsEnabledServers.push('mapserv.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('basemaps.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('api.mapserv.utah.gov');

    window.AGRC = {
        // app: app.App
        //      global reference to App
        app: null,

        // version.: String
        //      The version number.
        version: '1.4.2',

        //apiKey: String
        apiKey: null,

        appName: 'addressediting',

        // cache level 13
        labelsMinScale: 2257,

        urls: {
            basemap: '//mapserv.utah.gov/ArcGIS/rest/services/BaseMaps/Vector/MapServer',
            editLayer: '/arcgis/rest/services/AddressEditor/Editing/FeatureServer/',
            viewLayer: '/arcgis/rest/services/AddressEditor/Viewing/FeatureServer/',
            geometryService: '/arcgis/rest/services/Geometry/GeometryServer',
            downloadGp: '/arcgis/rest/services/AddressEditor/DownloadTool/GPServer/Download%20Address%20Points',
            redline: null
        },

        fieldNames: {
            Editor: 'Editor',
            ModifyDate: 'ModifyDate',
            FullAddress: 'FullAdd'
        }
    };

    if (has('agrc-api-key') === 'prod') {
        // addressediting.utah.gov
        window.AGRC.apiKey = 'AGRC-7D48DD3D449390';
        window.AGRC.urls.redline = 'http://mapserv.utah.gov/chalkdust';
        window.AGRC.quadWord = 'panther-avatar-neutral-grille'
    } else if (has('agrc-api-key') === 'stage') {
        // test.mapserv.utah.gov
        window.AGRC.apiKey = 'AGRC-FFCDAD6B933051';
        window.AGRC.urls.redline = 'http://test.mapserv.utah.gov/chalkdust';
        window.AGRC.quadWord = 'opera-event-little-pinball';
    } else {
        // localhost
        xhr(require.baseUrl + 'secrets.json', {
            handleAs: 'json',
            sync: true
        }).then(function (secrets) {
            window.AGRC.quadWord = secrets.quadWord;
            window.AGRC.apiKey = secrets.apiKey;
        }, function () {
            throw 'Error getting secrets!';
        });
    }

    return window.AGRC;
});
<|endoftext|>


<commit_before>let React = require('react'),
  ReactRedux = require('react-redux'),
  actions = require('./../actions'),
  MenuFeed = require('./Menu/MenuFeed'),
  MenuUser = require('./Menu/MenuUser'),
  MenuAbout = require('./Menu/MenuAbout'),
  MenuSteem = require('./Menu/MenuSteem'),
  Link = require('react-router').Link;

const Header = React.createClass({
  render() {
    const menu = this.props.menu || this.props.header.menu;
    const username = this.props.account || this.props.auth.user.name;
    const category = (this.props.category) ? `/${this.props.category}` : '';
    return (

          {!this.props.app.sidebarIsVisible &&  this.props.showSidebar()}>menu}
          {this.props.app.sidebarIsVisible &&  this.props.hideSidebar()}>keyboard_backspace}

            <Link to="/" onlyActiveOnIndex activeClassName="active"></Link>

          notifications

        {menu === 'primary' && <MenuFeed category={category} />}
        {menu === 'secondary' && <MenuUser username={username} />}
        {menu === 'about' && <MenuAbout />}
        {menu === 'steem' && <MenuSteem />}
        {this.props.children &&
{this.props.children}
}

    );
  }
});

const mapStateToProps = function (state) {
  return {
    app: state.app,
    auth: state.auth,
    header: state.header
  };
};

const mapDispatchToProps = function (dispatch) {
  return {
    showSidebar() { dispatch(actions.showSidebar()); },
    hideSidebar() { dispatch(actions.hideSidebar()); },
    setMenu(menu) { dispatch(actions.setMenu(menu)); }
  };
};

module.exports = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(Header);
<commit_msg>Remove sub menu from header<commit_after>let React = require('react'),
  ReactRedux = require('react-redux'),
  actions = require('./../actions'),
  Link = require('react-router').Link;

const Header = React.createClass({
  render() {
    return (

          {!this.props.app.sidebarIsVisible &&  this.props.showSidebar()}>menu}
          {this.props.app.sidebarIsVisible &&  this.props.hideSidebar()}>keyboard_backspace}

            <Link to="/" onlyActiveOnIndex activeClassName="active"></Link>

          notifications

        {this.props.children &&
{this.props.children}
}

    );
  }
});

const mapStateToProps = (state) => {
  return {};
};

const mapDispatchToProps = (dispatch) => {
  return {
    showSidebar() { dispatch(actions.showSidebar()); },
    hideSidebar() { dispatch(actions.hideSidebar()); },
  };
};

module.exports = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(Header);
<|endoftext|>


<commit_before>const { readFileSync } = require('fs');
const { join, isAbsolute } = require('path');

const LAYOUTS_DIR = join(__dirname, '..', 'layouts');
const THEMES_DIR = join(__dirname, '..', 'themes');

module.exports = function (options) {
  const { layout, theme, css } = options;
  const pathPrefix = isAbsolute(css) ? '' : process.cwd();
  const layoutCSS = readFileSync(join(LAYOUTS_DIR, layout + '.css'));
  const themeCSS = readFileSync(join(THEMES_DIR, theme + '.css'));
  const customCSS = css ? readFileSync(join(pathPrefix, css)) : '';

  return [layoutCSS, themeCSS, customCSS].join('\n');
}
<commit_msg>Check for optional CSS path before inspecting it<commit_after>const { readFileSync } = require('fs');
const { join, isAbsolute } = require('path');

const LAYOUTS_DIR = join(__dirname, '..', 'layouts');
const THEMES_DIR = join(__dirname, '..', 'themes');

module.exports = function (options) {
  const { layout, theme, css } = options;
  const pathPrefix = css && isAbsolute(css) ? '' : process.cwd();
  const layoutCSS = readFileSync(join(LAYOUTS_DIR, layout + '.css'));
  const themeCSS = readFileSync(join(THEMES_DIR, theme + '.css'));
  const customCSS = css ? readFileSync(join(pathPrefix, css)) : '';

  return [layoutCSS, themeCSS, customCSS].join('\n');
}
<|endoftext|>


<commit_before>/*#ifndef(UMD)*/
"use strict";
/*global _GpfClassDefinition*/ // GPF class definition
/*global _gpfDefine*/ // Shortcut for gpf.define
/*global _gpfFunc*/ // Create a new function using the source
/*global _gpfGenDefHandler*/ // Class handler for class types (interfaces...)
/*global _gpfGetClassDefinition*/ // Get GPF class definition for a constructor
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _gpfObjectForEach*/
/*exported _gpfA*/
/*exported _gpfAAdd*/
/*exported _gpfDefAttr*/
/*#endif*/

// @property {gpf.attributes.Map|null} Attributes of this class (null if none)
_GpfClassDefinition.prototype._attributes = null;

var
    /**
     * gpf.attributes shortcut
     *
     * @type {Object}
     * @private
     */
    _gpfA = gpf.attributes = {},

    /**
     * Used for empty members
     *
     * @type {gpf.attributes.Array}
     * @private
     */
    _gpfEmptyMemberArray = 0,

    /**
     * Generates a factory capable of creating a new instance of a class
     *
     * @param {Function} objectClass Object constructor
     * @param {String} name Alias name (will be prefixed by $)
     * @private
     * @closure
     */
    _gpfAlias = function (objectClass, name) {
        name = "$" + name;
        gpf[name] = (function(){
            var Proxy = (_gpfFunc("return function " + name + "(args) {" +
                "this.constructor.apply(this, args);" +
            "};"))();
            Proxy.prototype = objectClass.prototype;
            return function() {
                return new Proxy(arguments);
            };
        }());
    },

    /**
     * gpf.define handler for attributes
     *
     * @type {Function}
     * @private
     */
    _gpfDefAttrBase = _gpfGenDefHandler("gpf.attributes", "Attribute"),

    /**
     * gpf.define for attributes
     *
     * @param {String} name Attribute name. If it contains a dot, it is
     * treated as absolute contextual. Otherwise, it is relative to
     * "gpf.attributes". If starting with $ (and no dot), the contextual name
     * will be the "gpf.attributes." + name(without $) + "Attribute" and an
     * alias is automatically created (otherwise, use $Alias attribute on class)
     * @param {Function|string} [base=undefined] base Base attribute
     * (or contextual name)
     * @param {Object} [definition=undefined] definition Attribute definition
     * @return {Function}
     * @private
     */
    _gpfDefAttr = function (name, base, definition) {
        var
            isAlias = name.charAt(0) === "$",
            fullName,
            result;
        if (isAlias) {
            name = name.substr(1);
            fullName = name + "Attribute";
        } else {
            fullName = name;
        }
        result = _gpfDefAttrBase(fullName, base, definition);
        if (isAlias) {
            _gpfAlias(result, name);
        }
        return result;
    },

    /**
     * Shortcut for gpf.attributes.add
     */
    _gpfAAdd;

/**
 * Base class for any attribute
 *
 * @class gpf.attributes.Attribute
 */
_gpfDefAttr("Attribute", {

    protected: {

        /**
         * Name of the member the attribute is associated to
         *
         * @type {String}
         * @protected
         * @friend {gpf.attributes.add}
         */
        _member: "",

        /**
         * This method is the implementation of the attribute: it receives the prototype to alter
         * NOTE: this is called *after* all declared members are set
         *
         * @param {Object} objPrototype Class prototype
         * @protected
         * @friend {gpf.attributes.add}
         */
        _alterPrototype: function /*abstract*/ (objPrototype) {
            _gpfIgnore(objPrototype);
        }

    },

    public: {

        /**
         * Get the member name
         *
         * @return {String}
         */
        member: function () {
            return this._member;
        }

    }

});

/**
 * Generates an alias for the attribute. An alias is a factory function that
 * allocates an attribute instance (parameters are forwarded).
 * As a result, instead of using:
 * "[Class]" : [new gpf.attributes.AliasAttribute("Name")]
 * It is reduced to:
 * "[Class]" : [$Alias("Name")]
 *
 * @param {String} name Name of the alias to build below gpf
 *
 * @class gpf.attributes.AliasAttribute
 * @extends gpf.attributes.Attribute
 * @alias gpf.$Alias
 */
_gpfDefAttr("$Alias", {

    private: {

        /**
         * Name of the alias to create
         *
         * @type {String}
         * @private
         */
        _name: ""

    },

    protected: {

        /**
         * @inheritdoc gpf.attributes.Attribute:_alterPrototype
         */
        _alterPrototype: function (objPrototype) {
            _gpfAlias(objPrototype.constructor, this._name);
        }

    },

    public: {

        /**
         * Defines an alias
         *
         * @param {String} name Name of the alias to create
         * @constructor
         */
        constructor: function (name) {
            this._name = name;
        }

    }

});

/**
 * Attribute array, generally used to list attributes on a class member
 *
 * @class gpf.attributes.Array
 */
_gpfDefine("gpf.attributes.Array", {

    private: {

        /**
         * @type {gpf.attributes.Attribute[]}
         * @private
         */
        _array: []

    },

    public: {

        /**
         * @constructor
         */
        constructor: function () {
            this._array = []; // Create a new instance of the array
        },

        /**
         * @inheritdoc gpf.interfaces.IReadOnlyArray#getItem
         *
         * NOTE: this implementation will be replaced with the one coming
         * from IReadOnlyArray (once i_array.js is loaded)
         */
        getItemsCount: function () {
            return this._array.length;
        },

        /**
         * @inheritdoc gpf.interfaces.IReadOnlyArray#getItem
         *
         * NOTE: this implementation will be replaced with the one coming
         * from IReadOnlyArray (once i_array.js is loaded)
         */
        getItem: function (index) {
            return this._array[index];
        },

        /**
         * Return the first occurrence of the expected class
         *
         * @param {gpf.attributes.Attribute} expectedClass the class to
         * match
         * @return {gpf.attributes.Attribute}
         */
        has: function (expectedClass) {
            gpf.ASSERT("function" === typeof expectedClass,
                "Expected a class parameter");
            gpf.ASSERT(
                expectedClass.prototype instanceof _gpfA.Attribute,
                "Expected an Attribute-like class parameter");
            var
                idx,
                array = this._array,
                len = array.length,
                item;
            for (idx = 0; idx < len; ++idx) {
                item = array[idx];
                if (item instanceof expectedClass) {
                    return item;
                }
            }
            return null;
        },

        /**
         * Returns a new array with all attributes matching the expected
         * class
         *
         * @param {Function} expectedClass the class to match
         * @return {gpf.attributes.Array}
         */
        filter: function (expectedClass) {
            gpf.ASSERT("function" === typeof expectedClass,
                "Expected a class parameter");
            gpf.ASSERT(
                expectedClass.prototype instanceof _gpfA.Attribute,
                "Expected an Attribute-like class parameter");
            var
                idx,
                array = this._array,
                len = array.length,
                attribute,
                result = new _gpfA.Array(),
                resultArray = result._array;
            for (idx = 0; idx < len; ++idx) {
                attribute = array[idx];
                if (attribute instanceof expectedClass) {
                    resultArray.push(attribute);
                }
            }
            return result;
        },

        /**
         * The forEach() method executes a provided function once per array element
         *
         * @param {Function} callback function to execute for each element, taking three arguments
         * - {gpf.attributes.Attribute} attribute
         * - {Number} index
         * - {gpf.attributes.Attribute[]} attribute array
         * @param {Object} [thisArg=undefined] thisArg value to use as this when executing callback
         */
        forEach: function (callback, thisArg) {
            // TODO decide if the array should be read-only (_gpfArraySlice might be used)
            this._array.forEach(callback, thisArg);
        }
    }

});

/**
 * Attribute map, generally used to list attributes of a class
 *
 * @class gpf.attributes.Map
 */
_gpfDefine("gpf.attributes.Map", {

    private: {

        /**
         * @type {Object} Map(String,gpf.attributes.Array)
         * @private
         */
        _members: {},

        /**
         * @type {Number}
         * @private
         */
        _count: 0,

        /**
         * Copy the content of this map to a new one
         *
         * @param {gpf.attributes.Map} attributesMap recipient of the copy
         * @param {Function} [callback=undefined] callback callback function
         * to test if the mapping should be added
         * @param {*} [param=undefined] param additional parameter for the
         * callback
         * @private
         */
        _copyTo: function (attributesMap, callback, param) {
            var
                members = this._members,
                member,
                array,
                idx,
                attribute;
            if (this._count) {
                for (member in members) {
                    if (members.hasOwnProperty(member)) {
                        array = members[member]._array;
                        for (idx = 0; idx < array.length; ++idx) {
                            attribute = array[ idx ];
                            if (!callback || callback(member, attribute, param)) {
                                attributesMap.add(member, attribute);
                            }
                        }
                    }
                }
            }
        },

        /**
         * Callback for _copyTo, test if attribute is of a given class
         *
         * @param {String} member
         * @param {gpf.attributes.Attribute} attribute
         * @param {Function} expectedClass
         * @return {Boolean}
         * @private
         */
        _filterCallback: function (member, attribute, expectedClass) {
            _gpfIgnore(member);
            return attribute instanceof expectedClass;
        }

    },

    public: {

        /**
         * @param {Object|Function} [object=undefined] object Object or
         * constructor to read attributes from
         *
         * @constructor
         */
        constructor: function (object) {
            var classDef;
            this._members = {}; // Creates a new dictionary
            this._count = 0;
            if (object instanceof Function) {
                classDef = _gpfGetClassDefinition(object);
                this.fillFromClassDef(classDef);
            } else if (undefined !== object) {
                this.fillFromObject(object);
            }
        },

        /**
         * Gives the total number of attributes enclosed in the map
         *
         * @return {Number}
         */
        count: function () {
            return this._count;
        },

        /**
         * Associate an attribute to a member
         *
         * @param {String} member member name
         * @param {gpf.attributes.Attribute} attribute attribute to map
         */
        add: function (member, attribute) {
            var array = this._members[member];
            if (undefined === array) {
                array = this._members[member] = new _gpfA.Array();
            }
            array._array.push(attribute);
            ++this._count;
        },

        /**
         * Fill the map using object's attributes
         *
         * @param {Object} object object to get attributes from
         * @return {Number} number of attributes in the resulting map
         */
        fillFromObject: function (object) {
            var classDef = _gpfGetClassDefinition(object.constructor);
            return this.fillFromClassDef(classDef);
        },

        /**
         * Fill the map using class definition object
         *
         * @param {gpf.classDef} classDef class definition
         * @return {Number} number of attributes in the resulting map
         */
        fillFromClassDef: function (classDef) {
            var
                attributes,
                Super;
            while (classDef) { // !undefined && !null
                attributes = classDef._attributes;
                if (attributes) {
                    attributes._copyTo(this);
                }
                Super = classDef.Super();
                if (Super !== Object) { // Can't go upper
                    classDef = _gpfGetClassDefinition(Super);
                } else {
                    break;
                }
            }
            return this._count;
        },

        /**
         * Creates a new map that contains only instances of the given
         * attribute class
         *
         * @param {Function} expectedClass
         * @return {gpf.attributes.Map}
         */
        filter: function (expectedClass) {
            gpf.ASSERT("function" === typeof expectedClass,
                "Expected a class parameter");
            gpf.ASSERT( expectedClass.prototype instanceof _gpfA.Attribute,
                "Expected an Attribute-like class parameter");
            var result = new _gpfA.Map();
            this._copyTo(result, this._filterCallback, expectedClass);
            return result;
        },

        /**
         * Returns the array of map associated to a member
         *
         * @param {String} name
         * @return {gpf.attributes.Array}
         */
        member: function (name) {
            /**
             * When member is a known Object member (i.e. constructor),
             * this generates weird results. Filter out by testing the
             * result type.
             */
            var result = this._members[name];
            if (undefined === result
                || !(result instanceof _gpfA.Array)) {
                if (0 === _gpfEmptyMemberArray) {
                    _gpfEmptyMemberArray = new _gpfA.Array();
                }
                result = _gpfEmptyMemberArray;
            }
            return result;
        },

        /**
         * Returns the list of members stored in this map
         *
         * @perf_warn Result is computed on each call
         * @return {String[]}
         */
        members: function () {
            var
                members = this._members,
                result = [],
                member;
            for (member in members) {
                if (members.hasOwnProperty(member)) {
                    result.push(member);
                }
            }
            return result;
        },

        /**
         * The forEach() method executes a provided function once per object property
         *
         * @param {Function} callback function to execute for each element, taking three arguments
         * - {gpf.attributes.Array} array
         * - {String} property
         * - {Object} dictionary
         * @param {Object} [thisArg=undefined] thisArg value to use as this when executing callback
         */
        forEach: function (callback, thisArg) {
            _gpfObjectForEach(this._members, callback, thisArg);
        },

        /**
         * Add the attributes contained in the map to the given prototype
         *
         * @param {Function} objectClass
         */
        addTo: function (objectClass) {
            var
                members = this._members,
                member;
            for (member in members) {
                if (members.hasOwnProperty(member)) {
                    _gpfAAdd(objectClass, member, members[member]);
                }
            }
        }

    }

});

/**
 * Add the attribute list to the given prototype associated with the
 * provided member name
 *
 * @param {Function} objectClass class constructor
 * @param {String} name member name
 * @param {gpf.attributes.Array
 *        |gpf.attributes.Attribute
 *        |gpf.attributes.Attribute[]} attributes
 */
_gpfAAdd = _gpfA.add = function (objectClass, name, attributes) {
    // Check attributes parameter
    if (attributes instanceof _gpfA.Array) {
        attributes = attributes._array;
    } else if (!(attributes instanceof Array)) {
        attributes = [attributes];
    }
    var
        classDef = _gpfGetClassDefinition(objectClass),
        objectClassOwnAttributes = classDef._attributes,
        len,
        idx,
        attribute;
    if (!objectClassOwnAttributes) {
        objectClassOwnAttributes = classDef._attributes = new _gpfA.Map();
    }
    len = attributes.length;
    for (idx = 0; idx < len; ++idx) {
        attribute = attributes[idx];
        gpf.ASSERT(attribute instanceof _gpfA.Attribute, "Expected attribute");
        attribute._member = name; // Assign member name
        objectClassOwnAttributes.add(name, attribute);
        attribute._alterPrototype(objectClass.prototype);
    }
};<commit_msg>Refactoring (WIP)<commit_after>/*#ifndef(UMD)*/
"use strict";
/*global _GpfClassDefinition*/ // GPF class definition
/*global _gpfDefine*/ // Shortcut for gpf.define
/*global _gpfEmptyFunc*/ // An empty function
/*global _gpfFunc*/ // Create a new function using the source
/*global _gpfGenDefHandler*/ // Class handler for class types (interfaces...)
/*global _gpfGetClassDefinition*/ // Get GPF class definition for a constructor
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _gpfObjectForEach*/
/*exported _gpfA*/
/*exported _gpfAttribute*/
/*exported _gpfAttributesAdd*/
/*exported _gpfDefAttr*/
/*#endif*/

// @property {gpf.attributes.Map|null} Attributes of this class (null if none)
_GpfClassDefinition.prototype._attributes = null;

var
    // gpf.attributes shortcut
    _gpfA = gpf.attributes = {},

    /**
     * Used for empty members
     *
     * @type {gpf.attributes.Array}
     */
    _gpfEmptyMemberArray = 0;

/**
 * Generates a factory capable of creating a new instance of a class
 *
 * @param {Function} objectClass Object constructor
 * @param {String} name Alias name (will be prefixed by $)
 * @closure
 */
function _gpfAlias (objectClass, name) {
    name = "$" + name;
    gpf[name] = (function(){
        var Proxy = (_gpfFunc("return function " + name + "(args) {this.constructor.apply(this, args);};"))();
        Proxy.prototype = objectClass.prototype;
        return function() {
            return new Proxy(arguments);
        };
    }());
}

/**
 * gpf.define handler for attributes
 *
 * @type {Function}
 */
var _gpfDefAttrBase = _gpfGenDefHandler("gpf.attributes", "Attribute");

/**
 * gpf.define for attributes
 *
 * @param {String} name Attribute name. If it contains a dot, it is treated as absolute contextual.
 * Otherwise, it is relative to "gpf.attributes". If starting with $ (and no dot), the contextual name will be the
 * "gpf.attributes." + name(without $) + "Attribute" and an alias is automatically created
 * @param {Function|string} [base=undefined] base Base attribute (or contextual name)
 * @param {Object} [definition=undefined] definition Attribute definition
 * @return {Function}
 */
function _gpfDefAttr (name, base, definition) {
    var
        isAlias = name.charAt(0) === "$",
        fullName,
        result;
    if (isAlias) {
        name = name.substr(1);
        fullName = name + "Attribute";
    } else {
        fullName = name;
    }
    result = _gpfDefAttrBase(fullName, base, definition);
    if (isAlias) {
        _gpfAlias(result, name);
    }
    return result;
}

/**
 * Base class for any attribute
 *
 * @class gpf.attributes.Attribute
 */
var _gpfAttribute = _gpfDefAttr("Attribute", {
    protected: {

        // Name of the member the attribute is associated to
        _member: "",

        /**
         * This method is the implementation of the attribute: it receives the prototype to alter
         * NOTE: this is called *after* all declared members are set
         *
         * @param {Object} objPrototype Class prototype
         */
        _alterPrototype: function (objPrototype) {
            _gpfIgnore(objPrototype);
            throw gpf.Error.Abstract();
        }

    },
    public: {

        getMemberName: function () {
            return this._member;
        }

    }
});

/*#ifdef(DEBUG)*/

// DEBUG specifics

gpf.ASSERT_ATTRIBUTE_ONLY = function (value) {
    gpf.ASSERT("function" === typeof value, "Expected a class parameter");
    gpf.ASSERT(value.prototype instanceof _gpfAttribute, "Expected an Attribute-like class parameter");
};

if (!gpf.ASSERT) {

/*#else*/

    /*gpf:nop*/ gpf.ASSERT_ATTRIBUTE_ONLY = _gpfEmptyFunc;

/*#endif*/

/*#ifdef(DEBUG)*/

}

/**
 * Attribute array, generally used to list attributes on a class member
 *
 * @class gpf.attributes.Array
 */
_gpfDefine("gpf.attributes.Array", {
    private: {

        // @property {gpf.attributes.Attribute[]}
        _array: []

    },
    public: {

        constructor: function () {
            this._array = []; // Create a new instance of the array
        },

        /**
         * @inheritdoc gpf.interfaces.IReadOnlyArray#getItem
         *
         * NOTE: will be replaced with IReadOnlyArray implementation (once i_array.js is loaded)
         */
        getItemsCount: function () {
            return this._array.length;
        },

        /**
         * @inheritdoc gpf.interfaces.IReadOnlyArray#getItem
         *
         * NOTE: will be replaced with IReadOnlyArray implementation (once i_array.js is loaded)
         */
        getItem: function (index) {
            return this._array[index];
        },

        /**
         * Return the first occurrence of the expected class
         *
         * @param {gpf.attributes.Attribute} expectedClass the class to match
         * @return {Boolean}
         */
        has: function (expectedClass) {
            gpf.ASSERT_ATTRIBUTE_ONLY(expectedClass);
            /*gpf:inline(array)*/ return !this._array.every(function (attribute) {
                return !(attribute instanceof expectedClass);
            });
        },

        /**
         * Returns a new array with all attributes matching the expected class
         *
         * @param {Function} expectedClass the class to match
         * @return {gpf.attributes.Array}
         */
        filter: function (expectedClass) {
            gpf.ASSERT_ATTRIBUTE_ONLY(expectedClass);
            /*gpf:inline(array)*/ return this._array.filter(function (attribute) {
                return !(attribute instanceof expectedClass);
            });
        },

        /**
         * The forEach() method executes a provided function once per array element
         *
         * @param {Function} callback function to execute for each element, taking three arguments
         * - {gpf.attributes.Attribute} attribute
         * - {Number} index
         * - {gpf.attributes.Attribute[]} attribute array
         * @param {Object} [thisArg=undefined] thisArg value to use as this when executing callback
         */
        forEach: function (callback, thisArg) {
            /*gpf:inline(array)*/ this._array.forEach(callback, thisArg);
        }
    }

});

/**
 * Attribute map, generally used to list attributes of a class
 *
 * @class gpf.attributes.Map
 */
_gpfDefine("gpf.attributes.Map", {

    private: {

        /**
         * @type {Object} Map(String,gpf.attributes.Array)
         * @private
         */
        _members: {},

        /**
         * @type {Number}
         * @private
         */
        _count: 0,

        /**
         * Copy the content of this map to a new one
         *
         * @param {gpf.attributes.Map} attributesMap recipient of the copy
         * @param {Function} [callback=undefined] callback callback function
         * to test if the mapping should be added
         * @param {*} [param=undefined] param additional parameter for the
         * callback
         * @private
         */
        _copyTo: function (attributesMap, callback, param) {
            var
                members = this._members,
                member,
                array,
                idx,
                attribute;
            if (this._count) {
                for (member in members) {
                    if (members.hasOwnProperty(member)) {
                        array = members[member]._array;
                        for (idx = 0; idx < array.length; ++idx) {
                            attribute = array[ idx ];
                            if (!callback || callback(member, attribute, param)) {
                                attributesMap.add(member, attribute);
                            }
                        }
                    }
                }
            }
        },

        /**
         * Callback for _copyTo, test if attribute is of a given class
         *
         * @param {String} member
         * @param {gpf.attributes.Attribute} attribute
         * @param {Function} expectedClass
         * @return {Boolean}
         * @private
         */
        _filterCallback: function (member, attribute, expectedClass) {
            _gpfIgnore(member);
            return attribute instanceof expectedClass;
        }

    },

    public: {

        /**
         * @param {Object|Function} [object=undefined] object Object or
         * constructor to read attributes from
         *
         * @constructor
         */
        constructor: function (object) {
            var classDef;
            this._members = {}; // Creates a new dictionary
            this._count = 0;
            if (object instanceof Function) {
                classDef = _gpfGetClassDefinition(object);
                this.fillFromClassDef(classDef);
            } else if (undefined !== object) {
                this.fillFromObject(object);
            }
        },

        /**
         * Gives the total number of attributes enclosed in the map
         *
         * @return {Number}
         */
        count: function () {
            return this._count;
        },

        /**
         * Associate an attribute to a member
         *
         * @param {String} member member name
         * @param {gpf.attributes.Attribute} attribute attribute to map
         */
        add: function (member, attribute) {
            var array = this._members[member];
            if (undefined === array) {
                array = this._members[member] = new _gpfA.Array();
            }
            array._array.push(attribute);
            ++this._count;
        },

        /**
         * Fill the map using object's attributes
         *
         * @param {Object} object object to get attributes from
         * @return {Number} number of attributes in the resulting map
         */
        fillFromObject: function (object) {
            var classDef = _gpfGetClassDefinition(object.constructor);
            return this.fillFromClassDef(classDef);
        },

        /**
         * Fill the map using class definition object
         *
         * @param {gpf.classDef} classDef class definition
         * @return {Number} number of attributes in the resulting map
         */
        fillFromClassDef: function (classDef) {
            var
                attributes,
                Super;
            while (classDef) { // !undefined && !null
                attributes = classDef._attributes;
                if (attributes) {
                    attributes._copyTo(this);
                }
                Super = classDef.Super();
                if (Super !== Object) { // Can't go upper
                    classDef = _gpfGetClassDefinition(Super);
                } else {
                    break;
                }
            }
            return this._count;
        },

        /**
         * Creates a new map that contains only instances of the given
         * attribute class
         *
         * @param {Function} expectedClass
         * @return {gpf.attributes.Map}
         */
        filter: function (expectedClass) {
            gpf.ASSERT("function" === typeof expectedClass,
                "Expected a class parameter");
            gpf.ASSERT( expectedClass.prototype instanceof _gpfAttribute,
                "Expected an Attribute-like class parameter");
            var result = new _gpfA.Map();
            this._copyTo(result, this._filterCallback, expectedClass);
            return result;
        },

        /**
         * Returns the array of map associated to a member
         *
         * @param {String} name
         * @return {gpf.attributes.Array}
         */
        member: function (name) {
            /**
             * When member is a known Object member (i.e. constructor),
             * this generates weird results. Filter out by testing the
             * result type.
             */
            var result = this._members[name];
            if (undefined === result
                || !(result instanceof _gpfA.Array)) {
                if (0 === _gpfEmptyMemberArray) {
                    _gpfEmptyMemberArray = new _gpfA.Array();
                }
                result = _gpfEmptyMemberArray;
            }
            return result;
        },

        /**
         * Returns the list of members stored in this map
         *
         * @perf_warn Result is computed on each call
         * @return {String[]}
         */
        members: function () {
            var
                members = this._members,
                result = [],
                member;
            for (member in members) {
                if (members.hasOwnProperty(member)) {
                    result.push(member);
                }
            }
            return result;
        },

        /**
         * The forEach() method executes a provided function once per object property
         *
         * @param {Function} callback function to execute for each element, taking three arguments
         * - {gpf.attributes.Array} array
         * - {String} property
         * - {Object} dictionary
         * @param {Object} [thisArg=undefined] thisArg value to use as this when executing callback
         */
        forEach: function (callback, thisArg) {
            /*gpf:inline(object)*/ _gpfObjectForEach(this._members, callback, thisArg);
        },

        /**
         * Add the attributes contained in the map to the given prototype
         *
         * @param {Function} objectClass
         */
        addTo: function (objectClass) {
            var
                members = this._members,
                member;
            for (member in members) {
                if (members.hasOwnProperty(member)) {
                    // TODO Remove this dependency to _gpfAttributesAdd
                    _gpfAttributesAdd(objectClass, member, members[member]);
                }
            }
        }

    }

});

/**
 * Add the attribute list to the given prototype associated with the
 * provided member name
 *
 * @param {Function} objectClass class constructor
 * @param {String} name member name
 * @param {gpf.attributes.Array
 *        |gpf.attributes.Attribute
 *        |gpf.attributes.Attribute[]} attributes
 */
var _gpfAttributesAdd = _gpfA.add = function (objectClass, name, attributes) {
    // Check attributes parameter
    if (attributes instanceof _gpfA.Array) {
        attributes = attributes._array;
    } else if (!(attributes instanceof Array)) {
        attributes = [attributes];
    }
    var
        classDef = _gpfGetClassDefinition(objectClass),
        objectClassOwnAttributes = classDef._attributes,
        len,
        idx,
        attribute;
    if (!objectClassOwnAttributes) {
        objectClassOwnAttributes = classDef._attributes = new _gpfA.Map();
    }
    len = attributes.length;
    for (idx = 0; idx < len; ++idx) {
        attribute = attributes[idx];
        gpf.ASSERT(attribute instanceof _gpfAttribute, "Expected attribute");
        attribute._member = name; // Assign member name
        objectClassOwnAttributes.add(name, attribute);
        attribute._alterPrototype(objectClass.prototype);
    }
};
<|endoftext|>


<commit_before>/**
 *  Startup or transition into Handshake game
 *  o Start ambient sound
 *   Handshake interaction:
 *  o play handshake play
 *  o stop ambient sound
 *   Mole Game:
 *  We have 3 sounds
 *  1) Light activated: Panel comes on with 'neutral' color using ACTIVE_PANEL_INTENSITY
 *  2) Success interaction: panel turn active and state == TrackedPanels.STATE_IGNORED
 *  3) Fail interaction: panel turn active and state == TrackedPanels.STATE_OFF
 *  Q: Sound on light deactivated? (similar to 1) but opposite)
 *   Success animation
 *  o Play Success sound (missing)
 *   Transition animation to Disk Game
 *  o For each light on event, play lighteffect sound (4 events)
 *  o FIXME: Some sort of demo/helper to clarify interaction
 *   Disk Game
 *  o Start ambient sound (FIXME: Fading vs. loop, new sound from Alain?)
 *  o Start loop sound (loop)
 *  o Start distance sound (loop)
 *  o Calculate distance from success (positive and negative distance)
 *    -> use distance to modulate pitch of distance sound
 *  Level success animation
 *  o On success of level 1 and 2, play success sound
 *  o On perimeter light on event, play lighteffect sound
 *   Success animation
 *  o On success of level 3, play show sound
 *  o For each light off event, play lighteffect sound (3 events)
 *   Transition animation to Simon Game
 *  o No sounds
 *   Simon Game
 *  o On animation playback, play panel sounds
 *  o On correct local interaction, play panel sounds
 *  o On free play, play panel sounds
 *  o On fail local interaction, play failure sound
 *  o On level success, play success sound
 *   Light show and transition back to Start State
 *  o play show sound
 */

const _ = require('lodash');

const SculptureStore = require('@anyware/game-logic/lib/sculpture-store');
const GAMES = require('@anyware/game-logic/lib/constants/games');
const {TrackedPanels} = require('@anyware/game-logic/lib/utils/panel-group');
import {Sound, VCFSound} from './audio-api';

export default class AudioView {
  constructor(store, config, dispatcher) {
    this.store = store;
    this.config = config;
  }

  reset() {
  }

  /**
   * Loads all sounds, calls callback([err]) when done
   */
  load(callback) {
    // Maps logical sound identifiers to filenames. We'll load these sounds next.
    this.sounds = {
      alone: {
        ambient: new VCFSound({url: 'sounds/Alone_Mode/Pulse_Amb_Loop.wav', fadeIn: 3}),
        handshake: 'sounds/Alone_Mode/Hand_Shake_01.wav'
      },
      mole: {
        success: 'sounds/Game_01/G01_Success_01.wav',
        failure: 'sounds/Game_01/G01_Negative_01.wav',
        panels: [0,1,2].map(stripId => _.range(10).map(panelId => `sounds/Game_01/G01_LED_${("0"+(stripId*10+panelId+1)).slice(-2)}.wav`))
      },
      disk: {
        ambient: new Sound({url: 'sounds/Game_02/G02_Amb_Breath_Loop_01.wav', loop: true}),
        loop: new Sound({url: 'sounds/Game_02/G02_Disk_Loop_Ref_01.wav', loop: true, rate: 2, gain: 0.3, fadeIn: 10}),
        distance: new Sound({url: 'sounds/Game_02/G02_Disk_Loop_01.wav', loop: true, rate: 2, gain: 0.5, fadeIn: 10}),
        lighteffect: 'sounds/Game_02/G02_Lights_01.wav',
        success: 'sounds/Game_02/G02_Success_01.wav',
        show: 'sounds/Game_02/G02_Success_final_01.wav'
      },
      simon: {
        panels: [0,1,2].map(stripId => _.range(10).map(panelId => `sounds/Game_03/G03_LED_${("0"+(stripId*10+panelId+1)).slice(-2)}.wav`)),
        success: 'sounds/Game_03/G03_Success_01.wav',
        failure: 'sounds/Game_03/G03_Negative_01.wav',
        show: 'sounds/Game_03/G03_Light_Show_01.wav'
      }
    };

    // Traverse this.sounds and replace the filenames with valid sound objects.
    this._promises = [];
    this._traverse(this.sounds, this._promises);

    // _traverse() will create promises. We call the callback once all promises resolve
    console.log(`${this._promises.length} promises`);
    Promise.all(this._promises)
      // Don't listen to events until we've loaded all sounds
      .then(() => this.store.on(SculptureStore.EVENT_CHANGE, this._handleChanges.bind(this)))
      .then(() => callback(null))
      .catch(callback.bind(null));
  }

  /**
   * Traverses sound config objects and replaces nodes with valid, loaded, sounds
   * populates the given promises array with promises of loaded sounds
   */
  _traverse(node, promises) {
    for (let key in node) {
      const value = node[key];
      let sound;
      if (typeof value === 'string') sound = node[key] = new Sound({url: value});
      else if (value instanceof Sound) sound = value;
      if (sound) promises.push(sound.load());
      else this._traverse(value, promises);
    }
  }

  _handleChanges(changes) {
    if (this.store.isPlayingHandshakeGame) this._handleHandshakeGame(changes);
    if (this.store.isPlayingMoleGame) this._handleMoleGame(changes);
    if (this.store.isPlayingDiskGame) this._handleDiskGame(changes);
    if (this.store.isPlayingSimonGame) this._handleSimonGame(changes);
  }

  _handleHandshakeGame(changes) {
    // On startup, or when Start State becomes active, play ambient sound
    if (changes.currentGame === GAMES.HANDSHAKE) this.sounds.alone.ambient.play();

    if (changes.handshakes) {
      // Did someone shake my hand?
      if (changes.handshakes[this.config.username]) {
        this.sounds.alone.ambient.stop();
        this.sounds.alone.handshake.play();
      }
      // FIXME: Did someone else shake hands? -> dimmed sound?
    }
  }

  _handleMoleGame(changes) {
    const lightChanges = changes.lights;
    if (!lightChanges || !this.store.isReady) return;

    /*
      Mole game sounds:
      o If a panel got activated (changes.lights.<stripId>.panels.<panelId>.active === true)
      changes.mole.panels.<id> == STATE_IGNORED: Just turned -> success?
      state.mole.panels.<id> == STATE_OFF: failure
    */
    // If a panel got activated (changes.lights.<stripId>.panels.<panelId>.active === true)
    for (let stripId in lightChanges) for (let panelId in lightChanges[stripId].panels) {
      const panelChange = lightChanges[stripId].panels[panelId];
      if (panelChange.active === true) {
        const panelkey = `${stripId},${panelId}`;
        if (changes.mole && changes.mole.panels) {
          const state = changes.mole.panels[panelkey];
          if (state == TrackedPanels.STATE_IGNORED) { // Panel was turned -> success
            this.sounds.mole.success.play();
          }
        }
        else {
          const state = this.store.data.get('mole').get('panels').get(panelkey);
          if (!state || state === TrackedPanels.STATE_OFF) {
            this.sounds.mole.failure.play();
          }
        }
      }
      else if (panelChange.intensity > 90) {
        this.sounds.mole.panels[stripId][panelId].play();
      }
    }
  }

  _handleDiskGame(changes) {
    if (changes.status === SculptureStore.STATUS_SUCCESS) {
      this.sounds.disk.loop.stop();
      this.sounds.disk.distance.stop();

      if (this.store.data.get('disk').get('level') >= this.config.DISK_GAME.TARGET_POSITIONS_LEVELS.length) {
        this.sounds.disk.show.play();
      }
      else {
        this.sounds.disk.success.play();
      }
    }

    // On start of disk game
    // FIXME: Is transition part of the disk game?
    if (changes.currentGame === GAMES.DISK) {
      this.sounds.disk.ambient.play();
      this.sounds.disk.loop.play();
      this.sounds.disk.distance.play();
    }

    // On start of level
    if (changes.hasOwnProperty('disk') &&
        changes.disk.hasOwnProperty('level') &&
        changes.disk.level < this.config.DISK_GAME.TARGET_POSITIONS_LEVELS.length) {
      this.sounds.disk.loop.play();
      this.sounds.disk.distance.play();
    }

    if (changes.disks) {
      const disks = this.store.data.get('disks');
      const diskgame = this.store.currentGameLogic;
      const score = diskgame.getScore(disks);
      console.log(`score: ${score}`);
      // score = -540 -> 540
      const factor = Math.abs(score) / 540 * 3 + 1;
      const rate = Math.sign(score) < 0 ? 1/factor : factor;

      // FIXME: Set our rate property instead and deal with audio node stuff internally
      this.sounds.disk.distance.source.playbackRate.value = 2*rate;
    }
    // FIXME level success and final success
  }

  _handleSimonGame(changes) {
    if (changes.status === SculptureStore.STATUS_SUCCESS) this.sounds.simon.success.play();
    if (changes.status === SculptureStore.STATUS_FAILURE) this.sounds.simon.failure.play();

    const lightChanges = changes.lights;
    if (!lightChanges || !this.store.isReady) return;

    for (let stripId in lightChanges) for (let panelId in lightChanges[stripId].panels) {
      const panelChange = lightChanges[stripId].panels[panelId];
      if (panelChange.active || panelChange.intensity > 90) {
        this.sounds.simon.panels[stripId][panelId].play();
      }
    }
  }
}
<commit_msg>Disk game audio updates. Work in progress.<commit_after>/**
 *  Startup or transition into Handshake game
 *  o Start ambient sound
 *   Handshake interaction:
 *  o play handshake play
 *  o stop ambient sound
 *   Mole Game:
 *  We have 3 sounds
 *  1) Light activated: Panel comes on with 'neutral' color using ACTIVE_PANEL_INTENSITY
 *  2) Success interaction: panel turn active and state == TrackedPanels.STATE_IGNORED
 *  3) Fail interaction: panel turn active and state == TrackedPanels.STATE_OFF
 *  Q: Sound on light deactivated? (similar to 1) but opposite)
 *   Success animation
 *  o Play Success sound (missing)
 *   Transition animation to Disk Game
 *  o For each light on event, play lighteffect sound (4 events)
 *  o FIXME: Some sort of demo/helper to clarify interaction
 *   Disk Game
 *  o Start ambient sound (FIXME: Fading vs. loop, new sound from Alain?)
 *  o Start loop sound (loop)
 *  o Start distance sound (loop)
 *  o Calculate distance from success (positive and negative distance)
 *    -> use distance to modulate pitch of distance sound
 *  Level success animation
 *  o On success of level 1 and 2, play success sound
 *  o On perimeter light on event, play lighteffect sound
 *   Success animation
 *  o On success of level 3, play show sound
 *  o For each light off event, play lighteffect sound (3 events)
 *   Transition animation to Simon Game
 *  o No sounds
 *   Simon Game
 *  o On animation playback, play panel sounds
 *  o On correct local interaction, play panel sounds
 *  o On free play, play panel sounds
 *  o On fail local interaction, play failure sound
 *  o On level success, play success sound
 *   Light show and transition back to Start State
 *  o play show sound
 */

const _ = require('lodash');

const SculptureStore = require('@anyware/game-logic/lib/sculpture-store');
const GAMES = require('@anyware/game-logic/lib/constants/games');
const {TrackedPanels} = require('@anyware/game-logic/lib/utils/panel-group');
const Disk = require('@anyware/game-logic/lib/utils/disk');
import {Sound, VCFSound} from './audio-api';

export default class AudioView {
  constructor(store, config, dispatcher) {
    this.store = store;
    this.config = config;
  }

  reset() {
  }

  /**
   * Loads all sounds, calls callback([err]) when done
   */
  load(callback) {
    // Maps logical sound identifiers to filenames. We'll load these sounds next.
    this.sounds = {
      alone: {
        ambient: new VCFSound({url: 'sounds/Alone_Mode/Pulse_Amb_Loop.wav', fadeIn: 3}),
        handshake: 'sounds/Alone_Mode/Hand_Shake_01.wav'
      },
      mole: {
        success: 'sounds/Game_01/G01_Success_01.wav',
        failure: 'sounds/Game_01/G01_Negative_01.wav',
        panels: [0,1,2].map(stripId => _.range(10).map(panelId => `sounds/Game_01/G01_LED_${("0"+(stripId*10+panelId+1)).slice(-2)}.wav`))
      },
      disk: {
        ambient: new Sound({url: 'sounds/Game_02/G02_Amb_Breath_Loop_01.wav', loop: true}),
        disk2: new Sound({url: 'sounds/Game_02/G02_Disk_Loop_C2.wav', loop: true, rate: 1, loopFreq: 2, gain: 0.5, fadeIn: 2}),
        disk1: new Sound({url: 'sounds/Game_02/G02_Disk_Loop_Eb2.wav', loop: true, rate: 1, loopFreq: 1, gain: 0.5, fadeIn: 2}),
        disk0: new Sound({url: 'sounds/Game_02/G02_Disk_Loop_G2.wav', loop: true, rate: 1, loopFreq: 1, gain: 0.5, fadeIn: 2}),
        lighteffect: 'sounds/Game_02/G02_Lights_01.wav',
        success: 'sounds/Game_02/G02_Success_01.wav',
        show: 'sounds/Game_02/G02_Success_final_01.wav'
      },
      simon: {
        panels: [0,1,2].map(stripId => _.range(10).map(panelId => `sounds/Game_03/G03_LED_${("0"+(stripId*10+panelId+1)).slice(-2)}.wav`)),
        success: 'sounds/Game_03/G03_Success_01.wav',
        failure: 'sounds/Game_03/G03_Negative_01.wav',
        show: 'sounds/Game_03/G03_Light_Show_01.wav'
      }
    };

    // Traverse this.sounds and replace the filenames with valid sound objects.
    this._promises = [];
    this._traverse(this.sounds, this._promises);

    // _traverse() will create promises. We call the callback once all promises resolve
    console.log(`${this._promises.length} promises`);
    Promise.all(this._promises)
      // Don't listen to events until we've loaded all sounds
      .then(() => this.store.on(SculptureStore.EVENT_CHANGE, this._handleChanges.bind(this)))
      .then(() => callback(null))
      .catch(callback.bind(null));
  }

  /**
   * Traverses sound config objects and replaces nodes with valid, loaded, sounds
   * populates the given promises array with promises of loaded sounds
   */
  _traverse(node, promises) {
    for (let key in node) {
      const value = node[key];
      let sound;
      if (typeof value === 'string') sound = node[key] = new Sound({url: value});
      else if (value instanceof Sound) sound = value;
      if (sound) promises.push(sound.load());
      else this._traverse(value, promises);
    }
  }

  _handleChanges(changes) {
    if (this.store.isPlayingHandshakeGame) this._handleHandshakeGame(changes);
    if (this.store.isPlayingMoleGame) this._handleMoleGame(changes);
    if (this.store.isPlayingDiskGame) this._handleDiskGame(changes);
    if (this.store.isPlayingSimonGame) this._handleSimonGame(changes);
  }

  _handleHandshakeGame(changes) {
    // On startup, or when Start State becomes active, play ambient sound
    if (changes.currentGame === GAMES.HANDSHAKE) this.sounds.alone.ambient.play();

    if (changes.handshakes) {
      // Did someone shake my hand?
      if (changes.handshakes[this.config.username]) {
        this.sounds.alone.ambient.stop();
        this.sounds.alone.handshake.play();
      }
      // FIXME: Did someone else shake hands? -> dimmed sound?
    }
  }

  _handleMoleGame(changes) {
    const lightChanges = changes.lights;
    if (!lightChanges || !this.store.isReady) return;

    /*
      Mole game sounds:
      o If a panel got activated (changes.lights.<stripId>.panels.<panelId>.active === true)
      changes.mole.panels.<id> == STATE_IGNORED: Just turned -> success?
      state.mole.panels.<id> == STATE_OFF: failure
    */
    // If a panel got activated (changes.lights.<stripId>.panels.<panelId>.active === true)
    for (let stripId in lightChanges) for (let panelId in lightChanges[stripId].panels) {
      const panelChange = lightChanges[stripId].panels[panelId];
      if (panelChange.active === true) {
        const panelkey = `${stripId},${panelId}`;
        if (changes.mole && changes.mole.panels) {
          const state = changes.mole.panels[panelkey];
          if (state == TrackedPanels.STATE_IGNORED) { // Panel was turned -> success
            this.sounds.mole.success.play();
          }
        }
        else {
          const state = this.store.data.get('mole').get('panels').get(panelkey);
          if (!state || state === TrackedPanels.STATE_OFF) {
            this.sounds.mole.failure.play();
          }
        }
      }
      else if (panelChange.intensity > 90) {
        this.sounds.mole.panels[stripId][panelId].play();
      }
    }
  }

  _handleDiskGame(changes) {
    if (changes.status === SculptureStore.STATUS_SUCCESS) {
      // End of game
      if (this.store.data.get('disk').get('level') >= this.config.DISK_GAME.LEVELS.length) {
        this.sounds.disk.disk0.stop();
        this.sounds.disk.disk1.stop();
        this.sounds.disk.disk2.stop();
        this.sounds.disk.show.play();
      }
      // End of level
      else {
        this.sounds.disk.success.play();
      }
    }

    // On start of disk game
    // FIXME: Is transition part of the disk game?
    if (changes.currentGame === GAMES.DISK) {
      // Start all sounds in silent mode
      this.sounds.disk.disk0.play({gain: 0});
      this.sounds.disk.disk1.play({gain: 0});
      this.sounds.disk.disk2.play({gain: 0});
    }

    // On start of level
    if (changes.hasOwnProperty('disk') &&
        changes.disk.hasOwnProperty('level') &&
        changes.disk.level < this.config.DISK_GAME.LEVELS.length) {
    }

    if (changes.disks) {
      const disks = this.store.data.get('disks');

      for (let disk of ['disk0', 'disk1', 'disk2']) {
        if (changes.disks.hasOwnProperty(disk) &&
            changes.disks[disk].hasOwnProperty('direction') &&
            disks.get(disk).get('state') === Disk.STATE_READY) {
          if (changes.disks[disk].direction === Disk.STOPPED) this.sounds.disk[disk].fadeOut({delay: 1});
          else this.sounds.disk[disk].fadeIn();
        }
      }

      const diskgame = this.store.currentGameLogic;
      const score = diskgame.getScore(disks);
      //console.log(`score: ${score}`);
      // score = 0 -> 540
      const factor = score / 540 * 3 + 1;
      const rate = 1/factor;
      const pulseFreq = this._calcFreq(score);
      // FIXME: Set our loopFreq property instead and deal with audio node stuff internally
      //console.log(`pulseFreq: ${pulseFreq}`);
      if (this.sounds.disk.disk0.source) this.sounds.disk.disk0.source.loopEnd = (pulseFreq === 0 ? 0 : 1/pulseFreq);
      if (this.sounds.disk.disk1.source) this.sounds.disk.disk1.source.loopEnd = (pulseFreq === 0 ? 0 : 1/pulseFreq);
      if (this.sounds.disk.disk2.source) this.sounds.disk.disk2.source.loopEnd = (pulseFreq === 0 ? 0 : 1/pulseFreq);
    }
    // FIXME level success and final success
  }

  _calcFreq(score) {
    function map(value, in_min, in_max, out_min, out_max) {
      return (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }
    if (score > 200) {
      return map(score, 540, 200, 0.5, 1);
    }
    else if (score > 100) {
      return map(score, 200, 100, 1, 4);
    }
    else if (score > 50) {
      return map(score, 100, 50, 4, 10);
    }
    else {
      return map(score, 50, 0, 10, 60);
    }
  }

  _handleSimonGame(changes) {
    if (changes.status === SculptureStore.STATUS_SUCCESS) this.sounds.simon.success.play();
    if (changes.status === SculptureStore.STATUS_FAILURE) this.sounds.simon.failure.play();

    const lightChanges = changes.lights;
    if (!lightChanges || !this.store.isReady) return;

    for (let stripId in lightChanges) for (let panelId in lightChanges[stripId].panels) {
      const panelChange = lightChanges[stripId].panels[panelId];
      if (panelChange.active || panelChange.intensity > 90) {
        this.sounds.simon.panels[stripId][panelId].play();
      }
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose');
var GenericAdapter = require('../../node_modules/@back4app/back4app-entity').adapters.databaseAdapter;
var Schema = require('../../src/back/models/schema');

module.exports = new MongoAdapter();

function MongoAdapter() {
  var mongo = new GenericAdapter();

  mongo.db = null;
  mongo.entitySchema = null;

  mongo.registerEntity = registerEntity;

  function registerEntity(entity) {
    var promise = openConnection().then(function (db) {
      mongo.db = db;
      mongo.entitySchema = Schema.buildModel(entity);
    }, function (db) {
      console.log('error error error');
    }).then(function() {
      closeConnection();
      return mongo.entitySchema;
    });
    return promise;
  }

  function openConnection(){
    return new Promise(function(resolve, reject) {
      mongoose.connect('mongodb://localhost/test');
      var db = mongoose.connection;
      db.on('open', function() {
        resolve(db);
      });
      db.on('error', function() {
        reject(db);
      });
    });
  }

  function closeConnection(){
    mongo.db.disconnect(function() {
      mongo.db = null;
    });
  }

  return mongo;
}
<commit_msg>Fixes the promise logic.<commit_after>'use strict';

var mongoose = require('mongoose');
var GenericAdapter = require('../../node_modules/@back4app/back4app-entity').adapters.databaseAdapter;
var Schema = require('../../src/back/models/schema');

module.exports = new MongoAdapter();

function MongoAdapter() {
  var mongo = new GenericAdapter();

  mongo.db = null;
  mongo.entitySchema = null;

  mongo.registerEntity = registerEntity;

  function registerEntity(entity) {
    var promise = openConnection().then(function (db) {
      mongo.db = db;
      mongo.entitySchema = Schema.buildModel(entity);
      return mongo.entitySchema;
    }, function (db) {
      console.log('error error error');
      return db;
    }).then(function() {
      closeConnection();
      return mongo.entitySchema;
    });
    return promise;
  }

  function openConnection(){
    return new Promise(function(resolve, reject) {
      mongoose.connect('mongodb://localhost/test');
      var db = mongoose.connection;
      db.once('open', function(callback) {
        resolve(db);
        return db;
      });
      db.on('error', function() {
        reject(db);
      });

    });
  }

  function closeConnection(){
    mongoose.disconnect(function() {
      mongo.db = null;
    });
  }

  return mongo;
}
<|endoftext|>