<commit_before>var sys = require('sys'),
	util = require('./util'),
	q = util.q,
	debug = util.debug

exports.parse = util.intercept('ParseError', doParse)

var L_PAREN = '(',
	R_PAREN = ')',
	L_CURLY = '{',
	R_CURLY = '}',
	L_ARRAY = '[',
	R_ARRAY = ']'

var gToken, gIndex, gTokens, gState, gAST

var ParseError = function(file, msg) {
	this.name = 'ParseError';
	this.message = ['on line', gToken.line + ',', 'column', gToken.column, 'of', '"'+file+'":', msg].join(' ')
}
ParseError.prototype = Error.protoype

function doParse(tokens) {
	gTokens = tokens
	gIndex = -1
	gToken = null
	gAST = []

	while (true) {
		if (gIndex + 1 == gTokens.length) { break }
		advance()
		gAST.push(parseStatement())
	}

	return gAST
}

var parseStatement = function() {
	switch(gToken.type) {
		case 'string':
		case 'number':
			return getStaticValue()
		case 'symbol':
			switch(gToken.value) {
				case '<': return parseXML()
				case '=': halt('Unexpected symbol "=". Did you forget a "let" at the beginning of the line?')
				default: halt('Unexpected symbol "'+gToken.value+'"')
			}
		case 'name':
			return parseAliasOrInvocation()
		case 'keyword':
			switch (gToken.value) {
				case 'import': return parseImport()
				case 'let': return parseDeclaration()
				case 'for': return parseForLoop()
				case 'if': return parseIfStatement()
				default: halt('Unexpected keyword "'+gToken.value+'" at the beginning of a statement')
			}
		default:
			halt('Unknown parse statement token: ' + gToken.type)
	}
}

function parseBlock(statementType) {
	advance('symbol', L_CURLY, 'beginning of the '+statementType+'\'s block')
	var block = []
	while(!isAhead('symbol', R_CURLY)) {
		advance()
		block.push(parseStatement())
	}
	advance('symbol', R_CURLY, 'end of the '+statementType+' statement\'s block')
	return block
}

/*******************
 * Utility methods *
 *******************/
var assert = function(ok, msg) { if (!ok) halt(msg) }
var halt = function(msg) {
	sys.puts(util.grabLine(gToken.file, gToken.line, gToken.column, gToken.span));
	throw new ParseError(gToken.file, msg)
}
var advance = function(type, value, expressionType) {
	var nextToken = gTokens[++gIndex]
	if (!nextToken) { halt('Unexpected end of file') }
	gToken = nextToken
	function check(v1, v2) {
		assert(v1 == v2,
			['Expected a', q(type),
				value ? 'of value ' + q(value) : '',
				expressionType ? 'for the ' + expressionType : '',
				'but found a', q(gToken.type),
				'of value', q(gToken.value)].join(' ')
	)}
	if (type) { check(findInArray(type, gToken.type), gToken.type) }
	if (value) { check(findInArray(value, gToken.value), gToken.value) }
}
var isAhead = function(type, value, steps) {
	var token = gTokens[gIndex + (steps || 1)]
	if (!token) { return false }
	if (type && findInArray(type, token.type) != token.type) { return false }
	if (value && findInArray(value, token.value) != token.value) { return false }
	return true
}
// Find an item in an array and return it
//  if target is in array, return target
//  if target is not in array, return array
//  if array is not an array, return array
var findInArray = function(array, target) {
	if (!(array instanceof Array)) { return array }
	for (var i=0, item; item = array[i]; i++) {
		if (item == target) { return item }
	}
	return array
}

function astGenerator(generatorFn) {
	return function() {
		var startToken = gToken,
			ast = generatorFn(),
			endToken = gToken

		ast.file = startToken.file
		ast.line = startToken.line
		ast.column = startToken.column
		ast.lineEnd = endToken.line
		ast.columnEnd = endToken.column
		if (ast.line == ast.lineEnd) {
			ast.span = endToken.column - startToken.column + endToken.span
		} else {
			ast.span = startToken.span
		}

		return ast
	}
}

/***********
 * Imports *
 ***********/
var parseImport = astGenerator(function() {
	debug('parseImport')
	advance(['string','name'])
	if (gToken.type == 'string') {
		return { type: 'IMPORT_FILE', path: gToken.value }
	} else {
		return { type: 'IMPORT_MODULE', name: gToken.value }
	}
})

/*****************************
 * Items, aliases and values *
 *****************************/
function parseValueOrAlias() {
	debug('parseValueOrAlias')
	advance()
	switch(gToken.type) {
		case 'name':
		    return parseAliasOrInvocation()
		case 'string':
		case 'number':
			return getStaticValue()
		case 'symbol':
			return parseValueLiteral();
		case 'keyword':
			return parseKeyword();
		default:
			halt('Unexpected value or alias token: ' + gToken.type + ' ' + gToken.value)
	}
}

function parseValueLiteral() {
	switch(gToken.value) {
		case '<':
			return parseXML()
		case L_CURLY:
		case L_ARRAY:
			return parseJSON()
		case '@':
			return parseItem()
		default:
			halt('Unexpected symbol "'+gToken.value+'". Expected XML or JSON')
	}
}

var parseItem = astGenerator(function() {
	debug('parseItem')
	assert(gToken.type == 'symbol' && gToken.value == '@')
	advance(['name', 'number'])
	var itemID = gToken.value
	if (isAhead('symbol', '.')) {
		// TODO parse property
	}
	return { type:'ITEM', id: itemID }
})

function parseKeywordValue() {
	switch(gToken.value) {
		case 'import': return parseImport()
		case 'template': return parseTemplate()
		case 'handler': return parseHandler()
		default:
			halt('Unexpected keyword "'+gToken.value+'"')
	}
}

var parseAliasOrInvocation = astGenerator(function() {
	debug('parseAliasOrInvocation')
	var namespace = parseNamespace()
	if (isAhead('symbol', L_PAREN)) {
		advance('symbol', L_PAREN)
		var args = parseValueList(R_PAREN)
		advance('symbol', R_PAREN)
		return { type:'INVOCATION', namespace:namespace, args:args }
	} else {
		return { type:'ALIAS', namespace:namespace }
	}
})

var getStaticValue = astGenerator(function() {
	debug('getStaticValue')
	assert(gToken.type == 'string' || gToken.type == 'number')
	return { type:'STATIC_VALUE', valueType:gToken.type, value:gToken.value }
})

function parseNamespace() {
	var namespace = []
	while(true) {
		assert(gToken.type == 'name')
		namespace.push(gToken.value)
		if (!isAhead('symbol', '.')) { break }
		advance('symbol', '.')
		advance('name')
	}
	return namespace;
}

/*******
 * XML *
 *******/
var parseXML = astGenerator(function() {
	debug('parseXML')
	advance('name', null, 'XML tag')
	var tagName = gToken.value,
		attributes = parseXMLAttributes()

	advance('symbol', ['>', '/'], 'end of XML tag')
	if (gToken.value == '/') {
		advance('symbol', '>', 'self-closing XML tag')
		return { type:'XML', tag:tagName, attributes:attributes, content:[] }
	} else {
		var statements = []
		while(true) {
			if (isAhead('symbol', '<') && isAhead('symbol', '/', 2)) { break }
			advance()
			statements.push(parseStatement())
		}

		advance('symbol', '<')
		advance('symbol', '/')
		advance('name', tagName, 'matching XML tags')
		// allow for attributes on closing tag, e.g. <button>"Click"</button onClick=handler(){ ... }>
		attributes = attributes.concat(parseXMLAttributes())
		advance('symbol', '>')

		return { type:'XML', tag:tagName, attributes:attributes, block:statements }
	}
})
var parseXMLAttributes = function() {
	debug('parseXMLAttributes')

	var XMLAttributes = []
	while (isAhead('name')) {
		var assignment = parseAssignment('XML_attribute')
		XMLAttributes.push({ namespace:assignment[0], value:assignment[1] })
	}

	return XMLAttributes
}

function parseAssignment(acceptDotNotation, msg) {
	debug('parseAssignment')
	var namespace
	advance('name', null, msg)
	if (acceptDotNotation) { namespace = parseNamespace() }
	else { namespace = gToken.value }
	advance('symbol', '=', msg)
	var value = parseValueOrAlias()
	return [namespace, value]
}

/****************
 * Declarations *
 ****************/
var parseDeclaration = astGenerator(function() {
	debug('parseDeclaration')
	var assignment = parseAssignment('declaration')
	return { type:'DECLARATION', namespace:assignment[0], value:assignment[1] }
})

/********
 * JSON *
 ********/
function parseJSON() {
	if (gToken.value == L_CURLY) { return parseAliasLiteral() }
	else { return parseListLiteral() }
}
var parseAliasLiteral = astGenerator(function() {
	debug('parseObjectLiteral')
	assert(gToken.type == 'symbol' && gToken.value == L_CURLY)
	var content = []
	while (true) {
		if (isAhead('symbol', R_CURLY)) { break }
		var nameValuePair = {}
		advance(['name','string'])
		nameValuePair.name = gToken.value
		advance('symbol', ':')
		nameValuePair.value = parseValueOrAlias()
		content.push(nameValuePair)
		if (!isAhead('symbol', ',')) { break }
		advance('symbol',',')
	}
	advance('symbol', R_CURLY, 'right curly at the end of the JSON object')
	return { type:'NESTED_ALIAS', content:content }
})
var parseArrayLiteral = astGenerator(function() {
	debug('parseListLiteral')
	assert(gToken.type == 'symbol' && gToken.value == L_ARRAY)
	var content = parseValueList(R_ARRAY)
	advance('symbol', R_ARRAY, 'right bracket at the end of the JSON array')
	return { type:'LIST', content:content }
})
function parseValueList(breakSymbol) {
	var list = []
	while (true) {
		if (isAhead('symbol', breakSymbol)) { break }
		list.push(parseValueOrAlias())
		if (!isAhead('symbol', ',')) { break }
		advance('symbol', ',')
	}
	return list
}

/*************
* For loops *
*************/
var parseForLoop = astGenerator(function() {
	debug('parseForLoop')

	// parse "(item in Global.items)"
	advance('symbol', L_PAREN, 'beginning of for_loop\'s iterator statement')
	advance('name', null, 'for_loop\'s iterator')
	var iterator = gToken.value
	advance('keyword', 'in', 'for_loop\'s "in" keyword')
	advance('name', null, 'for_loop\'s iterable value')
	var iterable = gToken.value
	advance('symbol', R_PAREN, 'end of for_loop\'s iterator statement')

	// parse "{ ... for loop statements ... }"
	var block = parseBlock('for_loop')

	return { type:'FOR_LOOP', iterable:iterable, iterator:iterator, block:block }
})

/****************
 * If statement *
 ****************/
var parseIfStatement = astGenerator(function() {
	debug('parseIfStatement')

	advance('symbol', L_PAREN, 'beginning of the if statement\'s conditional')
	var condition = parseCondition()
	advance('symbol', R_PAREN, 'end of the if statement\'s conditional')

	var ifBlock = parseBlock('if statement')

	var elseBlock = null
	if (isAhead('keyword', 'else')) {
		advance('keyword', 'else')
		elseBlock = parseBlock('else statement')
	}

	return { type:'IF_STATEMENT', condition:condition, ifBlock:ifBlock, elseBlock:elseBlock }
})
function parseCondition() {
	debug('parseCondition')
	// TODO Parse compond statements, e.g. if (age < 30 && (income > 10e6 || looks=='awesome'))
	var type = gToken.type,
		value = gToken.value

	// Only strings, numbers, and aliases allowed
	advance(['string', 'number', 'name'])
	var left = parseStatement()

	var comparison, right
	if (isAhead('symbol', ['<','<=','>','>=','=='])) {
		advance('symbol')
		comparison = gToken.value
		advance(['string', 'number', 'name'])
		var right = parseStatement()
	}

	return { left:left, comparison:comparison, right:right }
}

/************************
 * Templates & Handlers *
 ************************/
var parseTemplate = astGenerator(function() {
	debug('parseTemplate')
	var callable = parseCallable('template')
	return { type:'TEMPLATE', args:callable[0], block:callable[1] }
})

var parseHandler = astGenerator(function() {
	debug('parseHandler')
	var callable = parseCallable('handler')
	return { type:'HANDLER', args:callable[0], block:callable[1] }
})

function parseCallable(msg) {
	advance('symbol', L_PAREN)
	var args = parseArgumentList()
	advance('symbol', R_PAREN)
	var block = parseBlock(msg)
	return [args, block]
}

function parseArgumentList() {
	debug('parseArgumentList')
	var args = []
	while (true) {
		if (isAhead('symbol', R_PAREN)) { break }
		advance('name')
		args.push(gToken.value)
		if (!isAhead('symbol', ',')) { break }
		advance('symbol', ',')
	}
	return args
}
<commit_msg>Don't advance until the assert has passed - this way an error line number and column number corresponds to the token before the missing token<commit_after>var sys = require('sys'),
	util = require('./util'),
	q = util.q,
	debug = util.debug

exports.parse = util.intercept('ParseError', doParse)

var L_PAREN = '(',
	R_PAREN = ')',
	L_CURLY = '{',
	R_CURLY = '}',
	L_ARRAY = '[',
	R_ARRAY = ']'

var gToken, gIndex, gTokens, gState, gAST

var ParseError = function(file, msg) {
	this.name = 'ParseError';
	this.message = ['on line', gToken.line + ',', 'column', gToken.column, 'of', '"'+file+'":', msg].join(' ')
}
ParseError.prototype = Error.protoype

function doParse(tokens) {
	gTokens = tokens
	gIndex = -1
	gToken = null
	gAST = []

	while (true) {
		if (gIndex + 1 == gTokens.length) { break }
		advance()
		gAST.push(parseStatement())
	}

	return gAST
}

var parseStatement = function() {
	switch(gToken.type) {
		case 'string':
		case 'number':
			return getStaticValue()
		case 'symbol':
			switch(gToken.value) {
				case '<': return parseXML()
				case '=': halt('Unexpected symbol "=". Did you forget a "let" at the beginning of the line?')
				default: halt('Unexpected symbol "'+gToken.value+'"')
			}
		case 'name':
			return parseAliasOrInvocation()
		case 'keyword':
			switch (gToken.value) {
				case 'import': return parseImport()
				case 'let': return parseDeclaration()
				case 'for': return parseForLoop()
				case 'if': return parseIfStatement()
				default: halt('Unexpected keyword "'+gToken.value+'" at the beginning of a statement')
			}
		default:
			halt('Unknown parse statement token: ' + gToken.type)
	}
}

function parseBlock(statementType) {
	advance('symbol', L_CURLY, 'beginning of the '+statementType+'\'s block')
	var block = []
	while(!isAhead('symbol', R_CURLY)) {
		advance()
		block.push(parseStatement())
	}
	advance('symbol', R_CURLY, 'end of the '+statementType+' statement\'s block')
	return block
}

/*******************
 * Utility methods *
 *******************/
var assert = function(ok, msg) { if (!ok) halt(msg) }
var halt = function(msg) {
	sys.puts(util.grabLine(gToken.file, gToken.line, gToken.column, gToken.span));
	throw new ParseError(gToken.file, msg)
}
var advance = function(type, value, expressionType) {
	var nextToken = gTokens[++gIndex]
	if (!nextToken) { halt('Unexpected end of file') }
	function check(v1, v2) {
		assert(v1 == v2,
			['Expected a', q(type),
				value ? 'of value ' + q(value) : '',
				expressionType ? 'for the ' + expressionType : '',
				'but found a', q(nextToken.type),
				'of value', q(nextToken.value)].join(' ')
	)}
	if (type) { check(findInArray(type, nextToken.type), nextToken.type) }
	if (value) { check(findInArray(value, nextToken.value), nextToken.value) }
	gToken = nextToken
}
var isAhead = function(type, value, steps) {
	var token = gTokens[gIndex + (steps || 1)]
	if (!token) { return false }
	if (type && findInArray(type, token.type) != token.type) { return false }
	if (value && findInArray(value, token.value) != token.value) { return false }
	return true
}
// Find an item in an array and return it
//  if target is in array, return target
//  if target is not in array, return array
//  if array is not an array, return array
var findInArray = function(array, target) {
	if (!(array instanceof Array)) { return array }
	for (var i=0, item; item = array[i]; i++) {
		if (item == target) { return item }
	}
	return array
}

function astGenerator(generatorFn) {
	return function() {
		var startToken = gToken,
			ast = generatorFn(),
			endToken = gToken

		ast.file = startToken.file
		ast.line = startToken.line
		ast.column = startToken.column
		ast.lineEnd = endToken.line
		ast.columnEnd = endToken.column
		if (ast.line == ast.lineEnd) {
			ast.span = endToken.column - startToken.column + endToken.span
		} else {
			ast.span = startToken.span
		}

		return ast
	}
}

/***********
 * Imports *
 ***********/
var parseImport = astGenerator(function() {
	debug('parseImport')
	advance(['string','name'])
	if (gToken.type == 'string') {
		return { type: 'IMPORT_FILE', path: gToken.value }
	} else {
		return { type: 'IMPORT_MODULE', name: gToken.value }
	}
})

/*****************************
 * Items, aliases and values *
 *****************************/
function parseValueOrAlias() {
	debug('parseValueOrAlias')
	advance()
	switch(gToken.type) {
		case 'name':
		    return parseAliasOrInvocation()
		case 'string':
		case 'number':
			return getStaticValue()
		case 'symbol':
			return parseValueLiteral();
		case 'keyword':
			return parseKeyword();
		default:
			halt('Unexpected value or alias token: ' + gToken.type + ' ' + gToken.value)
	}
}

function parseValueLiteral() {
	switch(gToken.value) {
		case '<':
			return parseXML()
		case L_CURLY:
		case L_ARRAY:
			return parseJSON()
		case '@':
			return parseItem()
		default:
			halt('Unexpected symbol "'+gToken.value+'". Expected XML or JSON')
	}
}

var parseItem = astGenerator(function() {
	debug('parseItem')
	assert(gToken.type == 'symbol' && gToken.value == '@')
	advance(['name', 'number'])
	var itemID = gToken.value
	if (isAhead('symbol', '.')) {
		// TODO parse property
	}
	return { type:'ITEM', id: itemID }
})

function parseKeywordValue() {
	switch(gToken.value) {
		case 'import': return parseImport()
		case 'template': return parseTemplate()
		case 'handler': return parseHandler()
		default:
			halt('Unexpected keyword "'+gToken.value+'"')
	}
}

var parseAliasOrInvocation = astGenerator(function() {
	debug('parseAliasOrInvocation')
	var namespace = parseNamespace()
	if (isAhead('symbol', L_PAREN)) {
		advance('symbol', L_PAREN)
		var args = parseValueList(R_PAREN)
		advance('symbol', R_PAREN)
		return { type:'INVOCATION', namespace:namespace, args:args }
	} else {
		return { type:'ALIAS', namespace:namespace }
	}
})

var getStaticValue = astGenerator(function() {
	debug('getStaticValue')
	assert(gToken.type == 'string' || gToken.type == 'number')
	return { type:'STATIC_VALUE', valueType:gToken.type, value:gToken.value }
})

function parseNamespace() {
	var namespace = []
	while(true) {
		assert(gToken.type == 'name')
		namespace.push(gToken.value)
		if (!isAhead('symbol', '.')) { break }
		advance('symbol', '.')
		advance('name')
	}
	return namespace;
}

/*******
 * XML *
 *******/
var parseXML = astGenerator(function() {
	debug('parseXML')
	advance('name', null, 'XML tag')
	var tagName = gToken.value,
		attributes = parseXMLAttributes()

	advance('symbol', ['>', '/'], 'end of XML tag')
	if (gToken.value == '/') {
		advance('symbol', '>', 'self-closing XML tag')
		return { type:'XML', tag:tagName, attributes:attributes, content:[] }
	} else {
		var statements = []
		while(true) {
			if (isAhead('symbol', '<') && isAhead('symbol', '/', 2)) { break }
			advance()
			statements.push(parseStatement())
		}

		advance('symbol', '<')
		advance('symbol', '/')
		advance('name', tagName, 'matching XML tags')
		// allow for attributes on closing tag, e.g. <button>"Click"</button onClick=handler(){ ... }>
		attributes = attributes.concat(parseXMLAttributes())
		advance('symbol', '>')

		return { type:'XML', tag:tagName, attributes:attributes, block:statements }
	}
})
var parseXMLAttributes = function() {
	debug('parseXMLAttributes')

	var XMLAttributes = []
	while (isAhead('name')) {
		var assignment = parseAssignment('XML_attribute')
		XMLAttributes.push({ namespace:assignment[0], value:assignment[1] })
	}

	return XMLAttributes
}

function parseAssignment(acceptDotNotation, msg) {
	debug('parseAssignment')
	var namespace
	advance('name', null, msg)
	if (acceptDotNotation) { namespace = parseNamespace() }
	else { namespace = gToken.value }
	advance('symbol', '=', msg)
	var value = parseValueOrAlias()
	return [namespace, value]
}

/****************
 * Declarations *
 ****************/
var parseDeclaration = astGenerator(function() {
	debug('parseDeclaration')
	var assignment = parseAssignment('declaration')
	return { type:'DECLARATION', namespace:assignment[0], value:assignment[1] }
})

/********
 * JSON *
 ********/
function parseJSON() {
	if (gToken.value == L_CURLY) { return parseAliasLiteral() }
	else { return parseListLiteral() }
}
var parseAliasLiteral = astGenerator(function() {
	debug('parseObjectLiteral')
	assert(gToken.type == 'symbol' && gToken.value == L_CURLY)
	var content = []
	while (true) {
		if (isAhead('symbol', R_CURLY)) { break }
		var nameValuePair = {}
		advance(['name','string'])
		nameValuePair.name = gToken.value
		advance('symbol', ':')
		nameValuePair.value = parseValueOrAlias()
		content.push(nameValuePair)
		if (!isAhead('symbol', ',')) { break }
		advance('symbol',',')
	}
	advance('symbol', R_CURLY, 'right curly at the end of the JSON object')
	return { type:'NESTED_ALIAS', content:content }
})
var parseArrayLiteral = astGenerator(function() {
	debug('parseListLiteral')
	assert(gToken.type == 'symbol' && gToken.value == L_ARRAY)
	var content = parseValueList(R_ARRAY)
	advance('symbol', R_ARRAY, 'right bracket at the end of the JSON array')
	return { type:'LIST', content:content }
})
function parseValueList(breakSymbol) {
	var list = []
	while (true) {
		if (isAhead('symbol', breakSymbol)) { break }
		list.push(parseValueOrAlias())
		if (!isAhead('symbol', ',')) { break }
		advance('symbol', ',')
	}
	return list
}

/*************
* For loops *
*************/
var parseForLoop = astGenerator(function() {
	debug('parseForLoop')

	// parse "(item in Global.items)"
	advance('symbol', L_PAREN, 'beginning of for_loop\'s iterator statement')
	advance('name', null, 'for_loop\'s iterator')
	var iterator = gToken.value
	advance('keyword', 'in', 'for_loop\'s "in" keyword')
	advance('name', null, 'for_loop\'s iterable value')
	var iterable = gToken.value
	advance('symbol', R_PAREN, 'end of for_loop\'s iterator statement')

	// parse "{ ... for loop statements ... }"
	var block = parseBlock('for_loop')

	return { type:'FOR_LOOP', iterable:iterable, iterator:iterator, block:block }
})

/****************
 * If statement *
 ****************/
var parseIfStatement = astGenerator(function() {
	debug('parseIfStatement')

	advance('symbol', L_PAREN, 'beginning of the if statement\'s conditional')
	var condition = parseCondition()
	advance('symbol', R_PAREN, 'end of the if statement\'s conditional')

	var ifBlock = parseBlock('if statement')

	var elseBlock = null
	if (isAhead('keyword', 'else')) {
		advance('keyword', 'else')
		elseBlock = parseBlock('else statement')
	}

	return { type:'IF_STATEMENT', condition:condition, ifBlock:ifBlock, elseBlock:elseBlock }
})
function parseCondition() {
	debug('parseCondition')
	// TODO Parse compond statements, e.g. if (age < 30 && (income > 10e6 || looks=='awesome'))
	var type = gToken.type,
		value = gToken.value

	// Only strings, numbers, and aliases allowed
	advance(['string', 'number', 'name'])
	var left = parseStatement()

	var comparison, right
	if (isAhead('symbol', ['<','<=','>','>=','=='])) {
		advance('symbol')
		comparison = gToken.value
		advance(['string', 'number', 'name'])
		var right = parseStatement()
	}

	return { left:left, comparison:comparison, right:right }
}

/************************
 * Templates & Handlers *
 ************************/
var parseTemplate = astGenerator(function() {
	debug('parseTemplate')
	var callable = parseCallable('template')
	return { type:'TEMPLATE', args:callable[0], block:callable[1] }
})

var parseHandler = astGenerator(function() {
	debug('parseHandler')
	var callable = parseCallable('handler')
	return { type:'HANDLER', args:callable[0], block:callable[1] }
})

function parseCallable(msg) {
	advance('symbol', L_PAREN)
	var args = parseArgumentList()
	advance('symbol', R_PAREN)
	var block = parseBlock(msg)
	return [args, block]
}

function parseArgumentList() {
	debug('parseArgumentList')
	var args = []
	while (true) {
		if (isAhead('symbol', R_PAREN)) { break }
		advance('name')
		args.push(gToken.value)
		if (!isAhead('symbol', ',')) { break }
		advance('symbol', ',')
	}
	return args
}
<|endoftext|>


<commit_before>import Component from 'can-component';
import DefineMap from 'can-define/map/';
import './payouts.less';
import template from './payouts.stache';
import ContributionMonth from 'bitcentive/models/contribution-month/';

export const ViewModel = DefineMap.extend({
  // Passed properties
  contributionMonth: {
    Type: ContributionMonth
  },
  get contributionMonthsPromise() {
    return ContributionMonth.getList({"date": {"$lte": this.contributionMonth.date}});
  },
  contributionMonths: {
    get: function(initial, resolve){
      this.contributionMonthsPromise.then(res => {
        return resolve(res);
      });
    }
  },
  getOSProjectPayoutTotal: function(monthlyOSProject, contributor) {
    var total = 0;
    if(this.contributionMonths) {
      var contributorsMap = this.contributionMonths.OSProjectContributionsMap;

      if(contributorsMap[monthlyOSProject.osProjectRef._id] && contributorsMap[monthlyOSProject.osProjectRef._id].contributors[contributor.contributorRef._id] ) {
        var contributorData = contributorsMap[monthlyOSProject.osProjectRef._id].contributors[contributor.contributorRef._id];
        var points = contributorData.points;
        var totalPoints = contributorsMap[monthlyOSProject.osProjectRef._id].totalPoints;
        var totalAmountForOSProject = this.contributionMonth.calculations.osProjects[monthlyOSProject.osProjectRef._id];

        total = (points / totalPoints) * totalAmountForOSProject;
      }
    }
    return total;
  },
  getTotalForAllPayoutsForContributor: function(contributorRef) {
    var total = 0;

    if(this.contributionMonths) {
      var contributorsMap = this.contributionMonths.OSProjectContributionsMap;
      for(var osProjectID in contributorsMap) {
        var projectContributors = contributorsMap[osProjectID].contributors;

        if(projectContributors[contributorRef._id]) {
          var contributorData = contributorsMap[osProjectID].contributors[contributorRef._id];
          var points = contributorData.points;
          var totalPoints = contributorsMap[osProjectID].totalPoints;
          var totalAmountForOSProject = this.contributionMonth.calculations.osProjects[osProjectID];

          total = total + ( (points / totalPoints) * totalAmountForOSProject );
        }
      }
    }

    return total;
  },
  get payouts() {
    var map = {};
    if(this.contributionMonth) {

      var monthlyContributorsMap = this.contributionMonth.monthlyContributions.contributorsMap;
      var monthlyOSProjects = this.contributionMonth.monthlyOSProjects;

      for(var contributorID in monthlyContributorsMap) {

        var contributor = monthlyContributorsMap[contributorID];
        var contributorRef = contributor.contributorRef;

        if(!map[contributorRef._id]) {
          map[contributorRef._id] = {
            contributorRef: contributorRef,
            monthlyOSProjects: []
          };
        }

        monthlyOSProjects.forEach(monthlyOSProject => {
          var contributorTotal = this.getOSProjectPayoutTotal(monthlyOSProject, contributor);
          map[contributorRef._id].monthlyOSProjects.push({
            osProjectRef: monthlyOSProject.osProjectRef,
            total: contributorTotal
          });
        });
      }
    }
    return map;
  },
  formatDollarAmount: function(value) {
    return value.toFixed(2);
  }
});

export default Component.extend({
  tag: 'bit-payouts',
  ViewModel: ViewModel,
  template
});
<commit_msg>refactor(bit-payouts):use ES6 syntax<commit_after>import Component from 'can-component';
import DefineMap from 'can-define/map/';
import './payouts.less';
import template from './payouts.stache';
import ContributionMonth from 'bitcentive/models/contribution-month/';

export const ViewModel = DefineMap.extend({
  // Passed properties
  contributionMonth: {
    Type: ContributionMonth
  },
  get contributionMonthsPromise() {
    return ContributionMonth.getList({"date": {"$lte": this.contributionMonth.date}});
  },
  contributionMonths: {
    get(initial, resolve){
      this.contributionMonthsPromise.then(resolve);
    }
  },
  getOSProjectPayoutTotal(monthlyOSProject, contributor) {
    let total = 0;
    if(this.contributionMonths) {
      const contributorsMap = this.contributionMonths.OSProjectContributionsMap;

      if(contributorsMap[monthlyOSProject.osProjectRef._id] && contributorsMap[monthlyOSProject.osProjectRef._id].contributors[contributor.contributorRef._id] ) {
        const contributorData = contributorsMap[monthlyOSProject.osProjectRef._id].contributors[contributor.contributorRef._id];
        const points = contributorData.points;
        const totalPoints = contributorsMap[monthlyOSProject.osProjectRef._id].totalPoints;
        const totalAmountForOSProject = this.contributionMonth.calculations.osProjects[monthlyOSProject.osProjectRef._id];

        total = (points / totalPoints) * totalAmountForOSProject;
      }
    }
    return total;
  },
  getTotalForAllPayoutsForContributor(contributorRef) {
    let total = 0;

    if(this.contributionMonths) {
      const contributorsMap = this.contributionMonths.OSProjectContributionsMap;
      for (const osProjectID in contributorsMap) {
        const projectContributors = contributorsMap[osProjectID].contributors;

        if(projectContributors[contributorRef._id]) {
          const contributorData = contributorsMap[osProjectID].contributors[contributorRef._id];
          const points = contributorData.points;
          const totalPoints = contributorsMap[osProjectID].totalPoints;
          const totalAmountForOSProject = this.contributionMonth.calculations.osProjects[osProjectID];

          total = total + ( (points / totalPoints) * totalAmountForOSProject );
        }
      }
    }

    return total;
  },
  get payouts() {
    const map = {};
    if(this.contributionMonth) {

      const monthlyContributorsMap = this.contributionMonth.monthlyContributions.contributorsMap;
      const monthlyOSProjects = this.contributionMonth.monthlyOSProjects;

      for (const contributorID in monthlyContributorsMap) {

        const contributor = monthlyContributorsMap[contributorID];
        const contributorRef = contributor.contributorRef;

        if(!map[contributorRef._id]) {
          map[contributorRef._id] = {
            contributorRef: contributorRef,
            monthlyOSProjects: []
          };
        }

        monthlyOSProjects.forEach(monthlyOSProject => {
          const contributorTotal = this.getOSProjectPayoutTotal(monthlyOSProject, contributor);
          map[contributorRef._id].monthlyOSProjects.push({
            osProjectRef: monthlyOSProject.osProjectRef,
            total: contributorTotal
          });
        });
      }
    }
    return map;
  },
  formatDollarAmount(value) {
    return value.toFixed(2);
  }
});

export default Component.extend({
  tag: 'bit-payouts',
  ViewModel: ViewModel,
  template
});
<|endoftext|>


<commit_before>
import React, {View, Text, Linking} from 'react-native'

import {logout} from '../api/Account'
import Styles from '../styles/Styles'
import ControlPanelItem from '../components/ControlPanelItem'
import Settings from '../settings'

export default React.createClass({
  render() {
    return (
      <View style={Styles.controlPanel.container}>
        <ControlPanelItem
          onPress={()=>{
            this.props.closeDrawer();
            this.props.logout();
          }}
          text="Logout"
        />
        <ControlPanelItem
          onPress={()=>{
            this.props.navigator.push({name: 'surveylist'})
            this.props.closeDrawer()
          }}
          text="Surveys"
        />
        <ControlPanelItem
          text="Notifications"
        />
        <ControlPanelItem
          text="Profile"
        />
        <ControlPanelItem
          text="Account"
        />
        <ControlPanelItem
          onPress={()=>{
            // mailto links do not work in the ios simulator.
            // https://github.com/facebook/react-native/issues/916
            // TODO: Set up support mailing list
            Linking.openURL("mailto:goodquestion@ecohealthalliance.org?subject=Support")
              .catch(err => console.error('An error occurred', err))
            this.props.closeDrawer()
          }}
          text="Support"
        />
        <View style={Styles.controlPanel.item}>
            <Text>Version: {Settings.version || "None"}</Text>
        </View>
      </View>
    )
  }
})
<commit_msg>Send title to navigator on control panel item press<commit_after>
import React, {View, Text, Linking} from 'react-native'

import {logout} from '../api/Account'
import Styles from '../styles/Styles'
import ControlPanelItem from '../components/ControlPanelItem'
import Settings from '../settings'

export default React.createClass({
  render() {
    return (
      <View style={Styles.controlPanel.container}>
        <ControlPanelItem
          onPress={()=>{
            this.props.closeDrawer();
            this.props.logout();
          }}
          text="Logout"
        />
        <ControlPanelItem
          onPress={()=>{
            this.props.navigator.push({name: 'surveylist', title: "Surveys"})
            this.props.closeDrawer()
          }}
          text="Surveys"
        />
        <ControlPanelItem
          text="Notifications"
        />
        <ControlPanelItem
          text="Profile"
        />
        <ControlPanelItem
          text="Account"
        />
        <ControlPanelItem
          onPress={()=>{
            // mailto links do not work in the ios simulator.
            // https://github.com/facebook/react-native/issues/916
            // TODO: Set up support mailing list
            Linking.openURL("mailto:goodquestion@ecohealthalliance.org?subject=Support")
              .catch(err => console.error('An error occurred', err))
            this.props.closeDrawer()
          }}
          text="Support"
        />
        <View style={Styles.controlPanel.item}>
            <Text>Version: {Settings.version || "None"}</Text>
        </View>
      </View>
    )
  }
})
<|endoftext|>


<commit_before>/**
 * @fileOverview Issue all directive
 * @author Chris James
 */
define(['jquery', 'webfontloader'], function ($, WebFont) {
    'use strict';

    /**
     * Issue All directive
     */
    var IssueAllDirective = function () {
        return {
            restrict: 'A',
            scope: true,
            controller: IssueAllCtrl,
            link: IssueAllLinkFn,
            templateUrl: '/app/lorax/directives/issue-all.tpl.html'
        };
    };

    /**
     * Controller for issue all directive
     * @constructor
     */
    var IssueAllCtrl = function (
        $scope,
        $timeout,
        dataService,
        windowService,
        scrollService,
        pubSubService,
        experienceService
    ) {

        this._$scope = $scope;
        this._$timeout = $timeout;
        this._dataService = dataService;
        this._windowService = windowService;
        this._scrollService = scrollService;
        this._pubSubService = pubSubService;
        this._experienceService = experienceService;

        this._$scope.detail = {
            currentIssue : '',
            scrollTo: this.scrollToIssue.bind(this),
            nextIssue: this.nextIssue.bind(this)
        };

        this._downArrowOpacity = 1.0;

        this._issueOffset = {
            'small': 60,
            'medium': 100,
            'large': 125,
            'xlarge': 138
        };

        this._onScrollBind = this.onScroll.bind(this);

        // get model
        this._dataService.getMain().then(this.onModelLoaded.bind(this));

        this._pubSubService.subscribe('detail.scrollToIssue', this.scrollToIssue.bind(this));
        this._pubSubService.subscribe('detail.open', this.open.bind(this));
        this._pubSubService.subscribe('detail.close', this.close.bind(this));
    };

    IssueAllCtrl.$inject = [
        '$scope',
        '$timeout',
        'dataService',
        'windowService',
        'scrollService',
        'pubSubService',
        'experienceService'
    ];

    IssueAllCtrl.prototype.init = function () {
        WebFont.load({
            active: this.onFontsLoaded.bind(this),
            custom: {families: ['Fira Sans:n2,n3,n4,n5,n6,n7,n8,n9']}
        });
    };

    IssueAllCtrl.prototype.open = function () {
        $(window).on('scroll', this._onScrollBind);
    };

    IssueAllCtrl.prototype.close = function () {
        $(window).off('scroll', this._onScrollBind);
    };

    IssueAllCtrl.prototype.onFontsLoaded = function () {
        this.fontsLoaded = true;

        if (this._$scope.detail.model) {
            this.setOffsets();
        }
    };

    IssueAllCtrl.prototype.onModelLoaded = function (model) {
        this._$scope.detail.model = model;

        if (this.fontsLoaded) {
            this.setOffsets();
        }
    };

    IssueAllCtrl.prototype.setOffsets = function () {
        var issues = this._$scope.detail.model.getIssues();
        var issue;
        var issueElm;
        var issueTitle;

        // hack
        var detailIsHidden = $('#detail').hasClass('ng-hide');
        $('#detail').removeClass('ng-hide');

        // don't do it unless needed
        var lastIssue = issues[issues.length - 1];
        if (lastIssue.offset && lastIssue.offset.top === $('#' + lastIssue.getId()).offset().top) {
            return;
        }

        // set offset property of all issues
        for (var i = 0; i < issues.length; i ++) {
            issue = issues[i];
            issueElm = $('#' + issue.getId());
            issueTitle = $('.detail-header-section', issueElm);
            issue.offset = issueElm.offset();
            issue.titleOffset = issueTitle.offset();
        }

        this._experienceService.setMenuPositions();

        // redirect svg fill so it works on ff
        var fill = $('rect', '.banner-nav-bg');
        var fillValue = fill.attr('fill');
        fill.attr('fill', '');
        fill.attr('fill', fillValue);

        if (detailIsHidden) {
            $('#detail').addClass('ng-hide');
        }

        this.initialized = true;

        if (this.issue || this.topic) {
            this._doScrollToIssue();
        }
    };

    IssueAllCtrl.prototype.scrollToIssue = function (issue, topic) {
        this.issue = issue;
        this.topic = topic;

        if ($('.detail').length === 0) {
            return;
        }

        var scrollTop = $(window).scrollTop();

        var currentElm = $('.detail')[0];
        $('.detail').each(function (index, elm) {
            if (elm.offsetTop - 100 >= scrollTop) {
                return false;
            }
            currentElm = elm;
        });

        var status = currentElm.attributes['data-issue-status'].value;

        if (status !== this._currentStatus) {
            this._windowService.setBgMode(status);
            this._currentStatus = status;
        }

        if (this.initialized) {
            this._doScrollToIssue();
        }
    };

    IssueAllCtrl.prototype._doScrollToIssue = function () {
        this.setOffsets(); // just to be sure

        this._currentStatus = null;

        // get first issue from topic
        if (this.topic && !this.issue) {
            this.issue = this._$scope.detail.model.getTopicById(this.topic).getIssues()[0].getId();
        }

        // find issue offset
        var offset = 0;
        if (this.issue) {
            offset = this._$scope.detail.model.getIssueById(this.issue).offset.top - this._issueOffset[this._windowService.breakpoint()];
        }

        offset = Math.max(1, offset);

        // scroll to offset
        if (!this._scrolled) {
            this._scrollService.go('top', {offset: offset, animate: false});
        }
        this._scrolled = false;
        this._$scope.detail.currentIssue = this.issue;
    };

    IssueAllCtrl.prototype.nextIssue = function () {
        this._$scope.detail.currentIssue =
            $('#' + this._$scope.detail.currentIssue).next().attr('id');

        if (this._$scope.detail.currentIssue) {
            this._$scope.detail.scrollTo(this._$scope.detail.currentIssue);
        }
    };

    IssueAllCtrl.prototype.onScroll = function () {
        var scrollTop = $(window).scrollTop();
        this._experienceService.onScroll(scrollTop);

        var currentElm = $('.detail')[0];
        $('.detail').each(function (index, elm) {
            if (elm.offsetTop - 100 >= scrollTop) {
                return false;
            }
            currentElm = elm;
        });

        var data = this._$scope.detail.model.getIssueById(currentElm.id);
        var status = currentElm.attributes['data-issue-status'].value;

        // if issue changed and it's not right after a scrollToIssue
        if (currentElm.id !== this.issue && this._currentStatus !== null) {
            this._scrolled = true;
            this.issue = currentElm.id;
            this._windowService.setIssue(data);
        }

        if (status !== this._currentStatus) {
            this._windowService.setBgMode(status);
            this._currentStatus = status;
        }

        var topic = data.getParentId();
        if (topic !== this._currentTopic) {
            this._windowService.setTopic(topic);
            this._currentTopic = topic;
        }

        // Fade out the down arrow as we scroll
        this._downArrowOpacity -= 0.005;
        $('.down-arrow-nav').css('opacity', this._downArrowOpacity);
    };

    /**
     * Link function for Issue All directive
     * @param {object} scope      Angular scope.
     * @param {JQuery} iElem      Detail wrapper jQuery element.
     */
    var IssueAllLinkFn = function (scope, iElem, iAttrs, controller) {

        // wait for everything to be rendered
        // controller._$timeout(function () {
        //     console.log($('.main--detail').children());
        //     controller.init();
        // }, 500);
        var checkRenderedInterval = setInterval(function () {
            controller._$timeout(function () {
                var children = $('.main--detail').children();
                if(children.length && $(children[0]).attr('data-issue-name')) {
                    clearInterval(checkRenderedInterval);
                    controller.init();
                }
            });
        }, 100);
    };

    return IssueAllDirective;
});
<commit_msg>make sure we refresh the header gradient on ff<commit_after>/**
 * @fileOverview Issue all directive
 * @author Chris James
 */
define(['jquery', 'webfontloader'], function ($, WebFont) {
    'use strict';

    /**
     * Issue All directive
     */
    var IssueAllDirective = function () {
        return {
            restrict: 'A',
            scope: true,
            controller: IssueAllCtrl,
            link: IssueAllLinkFn,
            templateUrl: '/app/lorax/directives/issue-all.tpl.html'
        };
    };

    /**
     * Controller for issue all directive
     * @constructor
     */
    var IssueAllCtrl = function (
        $scope,
        $timeout,
        dataService,
        windowService,
        scrollService,
        pubSubService,
        experienceService
    ) {

        this._$scope = $scope;
        this._$timeout = $timeout;
        this._dataService = dataService;
        this._windowService = windowService;
        this._scrollService = scrollService;
        this._pubSubService = pubSubService;
        this._experienceService = experienceService;

        this._$scope.detail = {
            currentIssue : '',
            scrollTo: this.scrollToIssue.bind(this),
            nextIssue: this.nextIssue.bind(this)
        };

        this._downArrowOpacity = 1.0;

        this._issueOffset = {
            'small': 60,
            'medium': 100,
            'large': 125,
            'xlarge': 138
        };

        this._onScrollBind = this.onScroll.bind(this);

        // get model
        this._dataService.getMain().then(this.onModelLoaded.bind(this));

        this._pubSubService.subscribe('detail.scrollToIssue', this.scrollToIssue.bind(this));
        this._pubSubService.subscribe('detail.open', this.open.bind(this));
        this._pubSubService.subscribe('detail.close', this.close.bind(this));
    };

    IssueAllCtrl.$inject = [
        '$scope',
        '$timeout',
        'dataService',
        'windowService',
        'scrollService',
        'pubSubService',
        'experienceService'
    ];

    IssueAllCtrl.prototype.init = function () {
        WebFont.load({
            active: this.onFontsLoaded.bind(this),
            custom: {families: ['Fira Sans:n2,n3,n4,n5,n6,n7,n8,n9']}
        });
    };

    IssueAllCtrl.prototype.open = function () {
        $(window).on('scroll', this._onScrollBind);
    };

    IssueAllCtrl.prototype.close = function () {
        $(window).off('scroll', this._onScrollBind);
    };

    IssueAllCtrl.prototype.onFontsLoaded = function () {
        this.fontsLoaded = true;

        if (this._$scope.detail.model) {
            this.setOffsets();
        }
    };

    IssueAllCtrl.prototype.onModelLoaded = function (model) {
        this._$scope.detail.model = model;

        if (this.fontsLoaded) {
            this.setOffsets();
        }
    };

    IssueAllCtrl.prototype.setOffsets = function () {
        var issues = this._$scope.detail.model.getIssues();
        var issue;
        var issueElm;
        var issueTitle;

        // hack
        var detailIsHidden = $('#detail').hasClass('ng-hide');
        $('#detail').removeClass('ng-hide');

        // redirect svg fill so it works on ff
        var fill = $('rect', '.banner-nav-bg');
        var fillValue = fill.attr('fill');
        fill.attr('fill', '');
        fill.attr('fill', fillValue);

        // don't do it unless needed
        var lastIssue = issues[issues.length - 1];
        if (lastIssue.offset && lastIssue.offset.top === $('#' + lastIssue.getId()).offset().top) {
            return;
        }

        // set offset property of all issues
        for (var i = 0; i < issues.length; i ++) {
            issue = issues[i];
            issueElm = $('#' + issue.getId());
            issueTitle = $('.detail-header-section', issueElm);
            issue.offset = issueElm.offset();
            issue.titleOffset = issueTitle.offset();
        }

        this._experienceService.setMenuPositions();

        if (detailIsHidden) {
            $('#detail').addClass('ng-hide');
        }

        this.initialized = true;

        if (this.issue || this.topic) {
            this._doScrollToIssue();
        }
    };

    IssueAllCtrl.prototype.scrollToIssue = function (issue, topic) {
        this.issue = issue;
        this.topic = topic;

        if ($('.detail').length === 0) {
            return;
        }

        var scrollTop = $(window).scrollTop();

        var currentElm = $('.detail')[0];
        $('.detail').each(function (index, elm) {
            if (elm.offsetTop - 100 >= scrollTop) {
                return false;
            }
            currentElm = elm;
        });

        var status = currentElm.attributes['data-issue-status'].value;

        if (status !== this._currentStatus) {
            this._windowService.setBgMode(status);
            this._currentStatus = status;
        }

        if (this.initialized) {
            this._doScrollToIssue();
        }
    };

    IssueAllCtrl.prototype._doScrollToIssue = function () {
        this.setOffsets(); // just to be sure

        this._currentStatus = null;

        // get first issue from topic
        if (this.topic && !this.issue) {
            this.issue = this._$scope.detail.model.getTopicById(this.topic).getIssues()[0].getId();
        }

        // find issue offset
        var offset = 0;
        if (this.issue) {
            offset = this._$scope.detail.model.getIssueById(this.issue).offset.top - this._issueOffset[this._windowService.breakpoint()];
        }

        offset = Math.max(1, offset);

        // scroll to offset
        if (!this._scrolled) {
            this._scrollService.go('top', {offset: offset, animate: false});
        }
        this._scrolled = false;
        this._$scope.detail.currentIssue = this.issue;
    };

    IssueAllCtrl.prototype.nextIssue = function () {
        this._$scope.detail.currentIssue =
            $('#' + this._$scope.detail.currentIssue).next().attr('id');

        if (this._$scope.detail.currentIssue) {
            this._$scope.detail.scrollTo(this._$scope.detail.currentIssue);
        }
    };

    IssueAllCtrl.prototype.onScroll = function () {
        var scrollTop = $(window).scrollTop();
        this._experienceService.onScroll(scrollTop);

        var currentElm = $('.detail')[0];
        $('.detail').each(function (index, elm) {
            if (elm.offsetTop - 100 >= scrollTop) {
                return false;
            }
            currentElm = elm;
        });

        var data = this._$scope.detail.model.getIssueById(currentElm.id);
        var status = currentElm.attributes['data-issue-status'].value;

        // if issue changed and it's not right after a scrollToIssue
        if (currentElm.id !== this.issue && this._currentStatus !== null) {
            this._scrolled = true;
            this.issue = currentElm.id;
            this._windowService.setIssue(data);
        }

        if (status !== this._currentStatus) {
            this._windowService.setBgMode(status);
            this._currentStatus = status;
        }

        var topic = data.getParentId();
        if (topic !== this._currentTopic) {
            this._windowService.setTopic(topic);
            this._currentTopic = topic;
        }

        // Fade out the down arrow as we scroll
        this._downArrowOpacity -= 0.005;
        $('.down-arrow-nav').css('opacity', this._downArrowOpacity);
    };

    /**
     * Link function for Issue All directive
     * @param {object} scope      Angular scope.
     * @param {JQuery} iElem      Detail wrapper jQuery element.
     */
    var IssueAllLinkFn = function (scope, iElem, iAttrs, controller) {

        // wait for everything to be rendered
        // controller._$timeout(function () {
        //     console.log($('.main--detail').children());
        //     controller.init();
        // }, 500);
        var checkRenderedInterval = setInterval(function () {
            controller._$timeout(function () {
                var children = $('.main--detail').children();
                if(children.length && $(children[0]).attr('data-issue-name')) {
                    clearInterval(checkRenderedInterval);
                    controller.init();
                }
            });
        }, 100);
    };

    return IssueAllDirective;
});
<|endoftext|>


<commit_before>/* global Marionette, Handlebars */

/**
 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
 *
 * @license GNU AGPL version 3 or any later version
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

(function(OCA, Marionette, Handlebars) {
	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};
	OCA.SpreedMe.Views = OCA.SpreedMe.Views || {};

	var ITEM_TEMPLATE = '{{name}} {{count}}';

	var RoomItenView = Marionette.View.extend({
		tagName: 'li',
		modelEvents: {
			change: 'render'
		},
		template: Handlebars.compile(ITEM_TEMPLATE)
	});

	var RoomListView = Marionette.CollectionView.extend({
		tagName: 'ul',
		childView: RoomItenView
	});

	OCA.SpreedMe.Views.RoomListView = RoomListView;

})(OCA, Marionette, Handlebars);
<commit_msg>Added more actions button and menu to roomlistview template<commit_after>/* global Marionette, Handlebars */

/**
 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
 *
 * @license GNU AGPL version 3 or any later version
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

(function(OCA, Marionette, Handlebars) {
	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};
	OCA.SpreedMe.Views = OCA.SpreedMe.Views || {};

	var ITEM_TEMPLATE = '{{name}}'+
						''+
									'{{count}}'+
									''+
								''+
								'
'+
									'
'+
										'
<button>'+
												''+
												'Add person'+
											'</button>'+
										'
'+
										'
<button>'+
												''+
												'Share group'+
											'</button>'+
										'
'+
										'
<button>'+
												''+
												'Leave group'+
											'</button>'+
										'
'+
									'
'+
								'
';

	var RoomItenView = Marionette.View.extend({
		tagName: 'li',
		modelEvents: {
			change: 'render'
		},
		template: Handlebars.compile(ITEM_TEMPLATE)
	});

	var RoomListView = Marionette.CollectionView.extend({
		tagName: 'ul',
		childView: RoomItenView
	});

	OCA.SpreedMe.Views.RoomListView = RoomListView;

})(OCA, Marionette, Handlebars);
<|endoftext|>


<commit_before>canvasWidth = (window.innerWidth / 100) * 70;
canvasHeight = (window.innerHeight / 100) * 80;

players = [];
playerPositions = [];
playerNames = [];
playerNumbers = [];
playerColours = [];
velocity = [2,2];

startX = 0;
startY = 0;

$(document).ready(function() {

  stage = new Kinetic.Stage({
    container: "game-container",
    width: canvasWidth,
    height: canvasHeight
  });
  layer = new Kinetic.Layer();

  // Super shiny header
  herps();

  conn = new WebSocket('ws://109.109.137.94:8080');

  conn.onopen = function(e) {
    console.log("Connection established!");
    var gridRequest = '{"operation":"GAME","type":"create","message":"crystalmaze","size":"21","sender":"steph","id":388542958}';
    conn.send(gridRequest);
  };

  conn.onclose = function(e) {
    console.log("Connection closed!");
  };

  conn.onmessage = function(e) {
    var json = JSON.parse(e.data);
    console.log(json);

    var playerIndex = -1;
    for (var k = 0; k < playerNumbers.length; k++) {
      if (playerNumbers[k] === json.player) {
        playerIndex = k;
      }
    }
    if (json.operation === "PLAYER" && json.type === "move") {

      if (playerIndex > -1) {
        movePlayer(playerIndex, parseInt(json.x), parseInt(json.y));
      } else {
        console.log("Couldn't find player " + json.player);
      }
      $("#console").append("
" + colouredName(playerIndex) + " moved to (" + json.x + "," + json.y + ")
");
      // $("#console").scrollTo('100%');
    } else if (json.operation === "PLAYER" && json.type === "join") {
      playerNames.push(json.name);
      playerNumbers.push(json.player);
      addPlayer();
      $("#console").append("
" + colouredName(playerNumbers.length - 1) + " joined
");
      // $("#console").scrollTo('100%');
    } else if (json.operation === "GAME" && json.type === "new") {
      grid = json.grid;
      gridWidth = grid[0].length;
      gridHeight = grid.length;

      mazeBlockWidth = Math.round(canvasWidth / gridWidth);
      mazeBlockHeight = Math.round(canvasHeight / gridHeight);

      buildMaze();

      // Add the layer to the stage
      stage.add(layer);

    } else if (json.operation === "PLAYER" && json.type === "win") {

      if (playerIndex > -1) {
        movePlayer(playerIndex, parseInt(json.x), parseInt(json.y));
      } else {
        console.log("Couldn't find player " + json.player);
      }
      $("#console").append("
" + colouredName(playerIndex) + " moved to (" + json.x + "," + json.y + ")
");

      $("#win-box").append("
" + playerNames[playerIndex] + " are win!!!
");
      setInterval(function() {
        $("#win-box h2").css({ color: getRandomColor() });
      }, 100);
      $("#win-box").css({
        position: "fixed",
        display: "block"
      });
    }
  };

});

  function buildMaze() {
    for (var i = 0; i < grid.length; i++) {
      for (var j = 0; j < grid[i].length; j++) {
        buildMazeBlock(i,j);
      };
    };
  }

  function buildMazeBlock(i,j) {
    var blockFill = ""
    if (grid[i][j] === " ") {
       return;
    } else if (grid[i][j] === "#") {
      blockFill = "rgb(100,100,100)";
    } else if (grid[i][j] === "s") {
      startX = i;
      startY = j;
      return;
    }

    var block = new Kinetic.Rect({
      x: i * mazeBlockWidth,
      y: j * mazeBlockHeight,
      width: mazeBlockWidth,
      height: mazeBlockHeight,
      fill: blockFill
    });

    if (grid[j][i] === "e") {
      setInterval(function() {
        block.setFill(getRandomColor());
        layer.draw();
      },100);
    }

    layer.add(block);
    layer.draw();
  }

  function addPlayer() {
    var colour = getRandomColor();
    playerColours.push(colour);

    var newPlayer = new Kinetic.Rect({
      x: startX * mazeBlockWidth,
      y: startY * mazeBlockHeight,
      width: mazeBlockWidth,
      height: mazeBlockHeight,
      fill: colour
    });

    players.push(newPlayer);
    playerPositions.push([startX,startY]);

    // add the shape to the layer
    layer.add(newPlayer);
    stackPlayers(startX,startY);
    layer.draw();
  }

  function stackPlayers(i,j) {
    var stackedPlayers = [];
    for (var k = 0; k < players.length; k++) {
      if (playerPositions[k][0] === i && playerPositions[k][1] === j) {
        stackedPlayers.push(players[k]);
      }
    }

    var stackRows = Math.ceil(stackedPlayers.length / 2);
    for (var k = 0; k < stackedPlayers.length; k++) {
      var row = Math.floor(k / 2);
      var column = k % 2;

      // Adjust x and y
      stackedPlayers[k].setPosition(
        i * mazeBlockWidth + column * Math.round(mazeBlockWidth / 2),
        j * mazeBlockHeight + row * Math.round(mazeBlockHeight / stackRows)
      );

      // Adjust width and height
      if (stackedPlayers.length % 2 === 0 || row != (stackRows - 1)) {
        stackedPlayers[k].setWidth(Math.round(mazeBlockWidth / 2));
      } else {
        stackedPlayers[k].setWidth(mazeBlockWidth);
      }

      stackedPlayers[k].setHeight(Math.ceil(mazeBlockHeight / stackRows));
    }

    layer.draw();

  }

  function movePlayer(pIndex, i, j) {
    players[pIndex].setWidth(mazeBlockWidth);
    players[pIndex].setHeight(mazeBlockHeight);

    var oldX = playerPositions[pIndex][0];
    var oldY = playerPositions[pIndex][1];

    playerPositions[pIndex] = [i,j];
    stackPlayers(oldX,oldY);

    var tween = new Kinetic.Tween({
      node: players[pIndex],
      duration: 1,
      x: i * mazeBlockWidth,
      y: j * mazeBlockHeight,
      opacity: 1,
      onFinish: function() {
        stackPlayers(playerPositions[pIndex][0],playerPositions[pIndex][1]);
      }
    });

    tween.play();
  }

  function whatIsWallWhatisNot() {
    $(".kineticjs-content").css({ background: "rgb(100,100,100)" });
    setTimeout(function() {
      $(".kineticjs-content").css({ background: "rgb(220,220,220)" });
    },2000);
  }

  function getRandomColor() {
      var letters = '0123456789ABCDEF'.split('');
      var color = '#';
      for (var i = 0; i < 6; i++ ) {
          color += letters[Math.round(Math.random() * 15)];
      }
      return color;
  }

  function herps() {

    var interval;
    $("header").mouseover(function() {
      interval = setInterval(function() {
        $("h1").css({ color: getRandomColor() });
      }, 100);
    });
    $("header").mouseout(function() {
      clearInterval(interval);
      $("h1").css({ color: "rgb(80,80,80)" });
    });
  }

  function colouredName(plIndex) {
    return "" + playerNames[plIndex] + "";
  }<commit_msg>AHAHAHHAHAHSFJKBSKDJFBS<commit_after>canvasWidth = (window.innerWidth / 100) * 70;
canvasHeight = (window.innerHeight / 100) * 80;

players = [];
playerPositions = [];
playerNames = [];
playerNumbers = [];
playerColours = [];
velocity = [2,2];

startX = 0;
startY = 0;

endX = 0;
endY = 0;

$(document).ready(function() {

  stage = new Kinetic.Stage({
    container: "game-container",
    width: canvasWidth,
    height: canvasHeight
  });
  layer = new Kinetic.Layer();

  // Super shiny header
  herps();

  conn = new WebSocket('ws://109.109.137.94:8080');

  conn.onopen = function(e) {
    console.log("Connection established!");
    var gridRequest = '{"operation":"GAME","type":"create","message":"crystalmaze","size":"21","sender":"steph","id":388542958}';
    conn.send(gridRequest);
  };

  conn.onclose = function(e) {
    console.log("Connection closed!");
  };

  conn.onmessage = function(e) {
    var json = JSON.parse(e.data);
    console.log(json);

    var playerIndex = -1;
    for (var k = 0; k < playerNumbers.length; k++) {
      if (playerNumbers[k] === json.player) {
        playerIndex = k;
      }
    }
    if (json.operation === "PLAYER" && json.type === "move") {

      if (playerIndex > -1) {
        movePlayer(playerIndex, parseInt(json.x), parseInt(json.y));
      } else {
        console.log("Couldn't find player " + json.player);
      }
      $("#console").append("
" + colouredName(playerIndex) + " moved to (" + json.x + "," + json.y + ")
");
      // $("#console").scrollTo('100%');
    } else if (json.operation === "PLAYER" && json.type === "join") {
      playerNames.push(json.name);
      playerNumbers.push(json.player);
      addPlayer();
      $("#console").append("
" + colouredName(playerNumbers.length - 1) + " joined
");
      // $("#console").scrollTo('100%');
    } else if (json.operation === "GAME" && json.type === "new") {
      grid = json.grid;
      gridWidth = grid[0].length;
      gridHeight = grid.length;

      mazeBlockWidth = Math.round(canvasWidth / gridWidth);
      mazeBlockHeight = Math.round(canvasHeight / gridHeight);

      buildMaze();

      // Add the layer to the stage
      stage.add(layer);

    } else if (json.operation === "PLAYER" && json.type === "win") {

      if (playerIndex > -1) {
        movePlayer(playerIndex, parseInt(json.x), parseInt(json.y));
      } else {
        console.log("Couldn't find player " + json.player);
      }
    }
  };

});

  function win(playerIndex) {
      $("#win-box").append("
" + playerNames[playerIndex] + " are win!!!
");
      setInterval(function() {
        $("#win-box h2").css({ color: getRandomColor() });
      }, 100);
      $("#win-box").css({
        position: "fixed",
        display: "block"
      });
  }

  function buildMaze() {
    for (var i = 0; i < grid.length; i++) {
      for (var j = 0; j < grid[i].length; j++) {
        buildMazeBlock(i,j);
      };
    };
  }

  function buildMazeBlock(i,j) {
    var blockFill = ""
    if (grid[i][j] === " ") {
       return;
    } else if (grid[i][j] === "#") {
      blockFill = "rgb(100,100,100)";
    } else if (grid[i][j] === "s") {
      startX = i;
      startY = j;
      return;
    } else if (grid[i][j] === "e") {
      endX = i;
      endY = j;
    }

    var block = new Kinetic.Rect({
      x: i * mazeBlockWidth,
      y: j * mazeBlockHeight,
      width: mazeBlockWidth,
      height: mazeBlockHeight,
      fill: blockFill
    });

    if (grid[j][i] === "e") {
      setInterval(function() {
        block.setFill(getRandomColor());
        layer.draw();
      },100);
    }

    layer.add(block);
    layer.draw();
  }

  function addPlayer() {
    var colour = getRandomColor();
    playerColours.push(colour);

    var newPlayer = new Kinetic.Rect({
      x: startX * mazeBlockWidth,
      y: startY * mazeBlockHeight,
      width: mazeBlockWidth,
      height: mazeBlockHeight,
      fill: colour
    });

    players.push(newPlayer);
    playerPositions.push([startX,startY]);

    // add the shape to the layer
    layer.add(newPlayer);
    stackPlayers(startX,startY);
    layer.draw();
  }

  function stackPlayers(i,j) {
    var stackedPlayers = [];
    for (var k = 0; k < players.length; k++) {
      if (playerPositions[k][0] === i && playerPositions[k][1] === j) {
        stackedPlayers.push(players[k]);
      }
    }

    var stackRows = Math.ceil(stackedPlayers.length / 2);
    for (var k = 0; k < stackedPlayers.length; k++) {
      var row = Math.floor(k / 2);
      var column = k % 2;

      // Adjust x and y
      stackedPlayers[k].setPosition(
        i * mazeBlockWidth + column * Math.round(mazeBlockWidth / 2),
        j * mazeBlockHeight + row * Math.round(mazeBlockHeight / stackRows)
      );

      // Adjust width and height
      if (stackedPlayers.length % 2 === 0 || row != (stackRows - 1)) {
        stackedPlayers[k].setWidth(Math.round(mazeBlockWidth / 2));
      } else {
        stackedPlayers[k].setWidth(mazeBlockWidth);
      }

      stackedPlayers[k].setHeight(Math.ceil(mazeBlockHeight / stackRows));
    }

    layer.draw();

  }

  function movePlayer(pIndex, i, j) {
    players[pIndex].setWidth(mazeBlockWidth);
    players[pIndex].setHeight(mazeBlockHeight);

    var oldX = playerPositions[pIndex][0];
    var oldY = playerPositions[pIndex][1];

    playerPositions[pIndex] = [i,j];
    stackPlayers(oldX,oldY);

    var posX = i * mazeBlockWidth;
    var posY = j * mazeBlockHeight

    var tween = new Kinetic.Tween({
      node: players[pIndex],
      duration: 1,
      x: posX,
      y: posY,
      opacity: 1,
      onFinish: function() {
        stackPlayers(playerPositions[pIndex][0],playerPositions[pIndex][1]);
        if (posX === endX && posY === endY) {
          win(pIndex);
        }
      }
    });

    tween.play();
  }

  function whatIsWallWhatisNot() {
    $(".kineticjs-content").css({ background: "rgb(100,100,100)" });
    setTimeout(function() {
      $(".kineticjs-content").css({ background: "rgb(220,220,220)" });
    },2000);
  }

  function getRandomColor() {
      var letters = '0123456789ABCDEF'.split('');
      var color = '#';
      for (var i = 0; i < 6; i++ ) {
          color += letters[Math.round(Math.random() * 15)];
      }
      return color;
  }

  function herps() {

    var interval;
    $("header").mouseover(function() {
      interval = setInterval(function() {
        $("h1").css({ color: getRandomColor() });
      }, 100);
    });
    $("header").mouseout(function() {
      clearInterval(interval);
      $("h1").css({ color: "rgb(80,80,80)" });
    });
  }

  function colouredName(plIndex) {
    return "" + playerNames[plIndex] + "";
  }<|endoftext|>


<commit_before>var Analyst = Analyst || {};

(function(A, $) {

  A.analysis.AnalysisMultiPointLayout = Backbone.Marionette.Layout.extend({

    template: Handlebars.getTemplate('analysis', 'analysis-multi-point'),

    events: {
      'click #createQuery': 'createQuery',
      'click #cancelQuery': 'cancelQuery',
      'click #newQuery': 'newQuery',
      'change #shapefile': 'updateAttributes'
    },

    regions: {
      main: "#main"
    },

    initialize: function(options) {
      _.bindAll(this, 'createQuery', 'cancelQuery', 'updateAttributes');
    },

    onRender: function() {
      $('#scenario2-controls').hide();
    },

    onShow: function() {

      var _this = this;

      this.scenarios = new A.models.Scenarios();
      this.queries = new A.models.Queries();
      this.shapefiles = new A.models.Shapefiles();

      this.scenarios.fetch({
        reset: true,
        data: {
          projectId: A.app.selectedProject
        },
        success: function(collection, response, options) {

          _this.$(".scenario-list").empty();

          for (var i in _this.scenarios.models) {
            if (_this.scenarios.models[i].get("id") == "default")
              _this.$(".scenario-list").append('<option selected value="' + _this.scenarios.models[i].get(
                "id") + '">' + _this.scenarios.models[i].get("name") + '</option>');
            else
              _this.$(".scenario-list").append('<option value="' + _this.scenarios.models[i].get("id") +
                '">' + _this.scenarios.models[i].get("name") + '</option>');

          }

        }
      });

      this.queries.fetch({
        reset: true,
        data: {
          projectId: A.app.selectedProject
        },
        success: function(collection, response, options) {

        }
      });

      this.shapefiles.fetch({data: {projectId: A.app.selectedProject}})
        .done(function () {
          _this.shapefiles.each(function (shp) {
            $('<option>')
              .attr('value', shp.id)
              .text(shp.get('name'))
              .appendTo(this.$('#shapefile'));
          });

          _this.updateAttributes();
        });

      this.mode = "WALK,TRANSIT";

      this.$('input[name=mode1]:radio').on('change', function(event) {
        _this.mode = _this.$('input:radio[name=mode1]:checked').val();
      });

      this.$("#createQueryForm").hide();

      var queryListLayout = new A.analysis.QueryList({
        collection: this.queries
      });

      this.main.show(queryListLayout);
    },

    /**
     * Update the attributes select to show the attributes of the current shapefile
     */
    updateAttributes: function () {
      var shpId = this.$('#shapefile').val();
      var shp = this.shapefiles.get(shpId);
      var _this = this;

      this.$('#shapefileColumn').empty();

      shp.getNumericAttributes().forEach(function (attr) {
        var atName = A.models.Shapefile.attributeName(attr);

        $('<option>')
          .attr('value', attr.fieldName)
          .text(atName)
          .appendTo(_this.$('#shapefileColumn'));
      });
    },

    createQuery: function(evt) {

      var _this = this;

      var data = {
        name: this.$("#name").val(),
        mode: this.mode,
        shapefileId: this.$('#shapefile').val(),
        attributeName: this.$('#shapefileColumn').val(),
        scenarioId: this.$('#scenario1').val(),
        projectId: A.app.selectedProject
      };

      var query = new A.models.Query();
      query.save(data, {
        success: function() {
          _this.queries.fetch({
            reset: true,
            data: {
              projectId: A.app.selectedProject
            },
            success: function(collection, response, options) {

            }
          });
        }
      });

      this.$("#createQueryForm").hide();
    },

    cancelQuery: function(evt) {
      this.$("#createQueryForm").hide();
    },

    newQuery: function(evt) {
      this.$("#createQueryForm").show();
    }

  });

  A.analysis.QueryListItem = Backbone.Marionette.ItemView.extend({
    tagName: 'li',
    className: 'list-group-item',

    template: Handlebars.getTemplate('analysis', 'query-list-item'),

    events: {

      'click #deleteItem': 'deleteItem',
      'click #queryCheckbox': 'clickItem',
      'click #normalizeCheckbox': 'normalizeBy',
      'click #compareCheckbox': 'compareTo',
      'click #exportShape': 'exportShape',
      'click #updateMap': 'updateMap',
      'change #weightByShapefile': 'updateAttributes'
    },

    modelEvents: {
      'change': 'fieldsChanged'
    },

    initialize: function() {
      var _this = this;
      this.updateInterval = setInterval(function() {
        if (!_this.isComplete()) {
          _this.model.fetch();
        } else {
          // don't keep polling
          clearInterval(_this.updateInterval);
        }
      }, 1000);
    },

    onClose: function() {
      clearInterval(this.updateInterval);

      if (this.queryOverlay && A.map.hasLayer(this.queryOverlay))
        A.map.removeLayer(this.queryOverlay);

    },

    fieldsChanged: function() {
      this.render();
    },

    serializeData: function() {

      var data = this.model.toJSON();

      if (this.isStarting())
        data['starting'] = true;
      else if (this.isComplete())
        data['complete'] = true;

      data['pointSetName'] = this.model.pointSetName();

      return data;

    },

    isStarting: function() {
      return this.model.get("totalPoints") === null || this.model.get('totalPoints') == -1;
    },

    isComplete: function() {
      var tp = this.model.get("totalPoints");
      var cp = this.model.get("completePoints");
      return tp !== null && cp !== null && tp == cp;
    },

    clickItem: function(evt) {

      this.refreshMap();

    },

    exportShape: function(evt) {

      this.normalizeBy();
      this.compareTo();

      var timeLimit = this.timeSlider.getValue() * 60;

      var url = '/gis/query?queryId=' + this.model.id + '&timeLimit=' + timeLimit;

      if (this.groupById)
        url = url + "&groupBy=" + this.groupById;

      if (this.weightByShapefile)
        url = url + "&weightByShapefile=" + this.weightByShapefile + '&weightByAttribute=' + this.weightByAttribute;

      url += '&which=' + this.which;

      if (this.compareToId)
        url += '&compareTo=' + this.compareToId;

      window.open(url);

    },

    updateMap: function(evt) {
      this.refreshMap();
    },

    normalizeBy: function(evt) {

      if (this.$("#normalizeCheckbox").prop('checked')) {
        this.$("#weightByShapefile").prop("disabled", false);
        this.$("#weightByAttribute").prop("disabled", false);
        this.$("#groupBy").prop("disabled", false);
        this.$('#aggregation-controls').slideDown();

        this.weightByShapefile = this.$("#weightByShapefile").val();
        this.weightByName = this.$('#weightByShapefile :selected').text() + ' ' +
          this.$('#weightByAttribute :selected').text();

        this.weightByAttribute = this.$("#weightByAttribute").val();

        this.groupById = this.$('#groupBy').val();
        this.groupByName = this.$('#groupBy :selected').text();
      } else {
        this.$("#weightByShapefile").prop("disabled", true);
        this.$("#weightByAttribute").prop("disabled", true);

        this.weightByShapefile = false;
        this.weightByName = '';
        this.weightByAttribute = false;

        this.groupById = false;
        this.groupByName = '';
        this.$("#groupBy").prop("disabled", true);

        this.$('#aggregation-controls').slideUp();
      }
    },

    compareTo: function () {
      if (this.$('#compareCheckbox').is(':checked')) {
        this.compareToId = this.$('#compareTo').val();
        this.$('#compareControls').removeClass('hidden');
      } else {
        this.compareToId = false;
        this.$('#compareControls').addClass('hidden');
      }
    },

    deleteItem: function(evt) {
      this.model.destroy();
    },

    refreshMap: function() {
      var target = this.$("#queryCheckbox");
      var _this = this;

      this.normalizeBy();
      this.compareTo();

      this.which = this.$('.whichMulti input:checked').val();

      var legendTitle;
      if (this.groupById) {
        legendTitle = Messages('analysis.aggregated-title',
          this.model.pointSetName(),
          this.groupByName,
          this.weightByName
        );
      } else {
        legendTitle = Messages('analysis.accessibility-to', this.model.pointSetName());
      }

      // add the suffix, e.g. (best case)
      legendTitle += ' ';

      if (this.which == 'POINT_ESTIMATE')
        legendTitle += window.Messages('analysis.point-estimate-suffix');

      else if (this.which == 'WORST_CASE')
        legendTitle += window.Messages('analysis.worst-case-suffix');

      else if (this.which == 'BEST_CASE')
        legendTitle += window.Messages('analysis.best-case-suffix');

      else if (this.which == 'SPREAD')
        legendTitle += window.Messages('analysis.spread-suffix');

      if (target.prop("checked")) {
        if (A.map.hasLayer(this.queryOverlay))
          A.map.removeLayer(this.queryOverlay);

        var timeLimit = this.timeSlider.getValue() * 60;

        var url = 'queryId=' + this.model.id + '&timeLimit=' + timeLimit;

        if (this.groupById)
          url = url + "&groupBy=" + this.groupById;

        if (this.weightByShapefile)
          url = url + "&weightByShapefile=" + this.weightByShapefile + '&weightByAttribute=' + this.weightByAttribute;

        url += '&which=' + this.which;

        if (this.compareToId)
          url = url + '&compareTo=' + this.compareToId;

        this.$(".legendTitle").text(legendTitle);

        var legendItemTemplate = Handlebars.getTemplate('analysis', 'query-legend-item')

        this.$("#legendData").empty();
        this.$("#updatingMap").show();

        var _map = A.map;

        $.getJSON('/api/queryBins?' + url, function(data) {

          _this.queryOverlay = L.tileLayer('/tile/query?z={z}&x={x}&y={y}&' + url).addTo(_map);

          _this.$("#updatingMap").hide();

          for (var i in data) {

            var lower = _this.numberWithCommas(parseFloat(data[i].lower).toFixed(2));
            var upper = _this.numberWithCommas(parseFloat(data[i].upper).toFixed(2));
            var legendItem = {
              color: data[i].hexColor,
              label: lower + " - " + upper
            };

            _this.$("#legendData").append(legendItemTemplate(legendItem));

          }
        });

        this.$("#legend").show();

      } else {
        if (A.map.hasLayer(this.queryOverlay))
          A.map.removeLayer(this.queryOverlay);

        this.$("#legend").hide();
      }
    },

    /** Update available shapefile attributes for weighting */
    updateAttributes: function () {
      this.$('#weightByAttribute').empty();

      var _this = this;

      var shp = this.shapefiles.get(this.$('#weightByShapefile').val());

      shp.getNumericAttributes().forEach(function (attr) {
        var atName = A.models.Shapefile.attributeName(attr);

        $('<option>')
          .attr('value', attr.fieldName)
          .text(atName)
          .appendTo(_this.$('#weightByAttribute'));
      });

    },

    onRender: function() {

      var _this = this;

      if (this.isComplete()) {
        if (this.model.get('transit')) {
          // we have transit modes, so it's a profile request
          this.$('.whichMulti input[value="POINT_ESTIMATE"]').parent().remove();
          this.$('.whichMulti input[value="SPREAD"]').parent().remove();
          this.$('.whichMulti input[value="WORST_CASE"]').prop('checked', true).parent().addClass('active');
        } else {
          // it's a stock/vanilla request
          this.$('.whichMulti input[value="WORST_CASE"]').parent().remove();
          this.$('.whichMulti input[value="BEST_CASE"]').parent().remove();
          this.$('.whichMulti input[value="SPREAD"]').parent().remove();
          this.$('.whichMulti input[value="POINT_ESTIMATE"]').prop('checked', true).parent().addClass('active');
        }

        // Set up weight and group by select boxes
        // we weight and group by shapefiles. for weighting we also specify an attribute.
        this.shapefiles = new A.models.Shapefiles();
        this.shapefiles.fetch({
            data: {
              projectId: this.model.get("projectId")
            }
          })
          .done(function() {
            _this.$('#groupBy').empty();

            _this.shapefiles.each(function(shapefile) {
              $('<option>')
                .attr('value', shapefile.id)
                .text(shapefile.get('name'))
                .appendTo(_this.$('#weightByShapefile'));

                $('<option>')
                .attr('value', shapefile.id)
                .text(shapefile.get('name'))
                .appendTo(_this.$('#groupBy'));
            });

            _this.updateAttributes();
          });

        // set up the comparison select boxes
        this.comparisonQueries = new A.models.Queries();
        this.comparisonQueries.fetch({
          data: {
            pointSetId: this.model.get('pointSetId')
          }
        }).done(function () {
          _this.comparisonQueries.each(function (query) {
            $('<option>')
            .attr('value', query.id)
            .text(query.get('name'))
            .appendTo(_this.$('#compareTo'));
          });
        });

        this.$("#weightBy").prop("disabled", true);
        this.$("#groupBy").prop("disabled", true);

        this.$("#settings").show();
      } else
        this.$("#settings").hide();

      this.timeSlider = this.$('#timeSlider').slider({
        formater: function(value) {
          return value + " minutes";
        }
      }).on('slideStop', function(evt) {
        _this.$('#timeLimitValue').html(evt.value + " mins");
      }).data('slider');

    },
    numberWithCommas: function(x) {
      var parts = x.toString().split(".");
      parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
      return parts.join(".");
    }

  });

  A.analysis.QueryList = Backbone.Marionette.CompositeView.extend({

    template: Handlebars.getTemplate('analysis', 'query-list'),
    itemView: A.analysis.QueryListItem,

    initialize: function() {
      this.queryOverlay = {};

    },

    modelEvents: {
      'change': 'render'
    },

    onShow: function() {

    },

    appendHtml: function(collectionView, itemView) {
      collectionView.$("#queryList").append(itemView.el);
    }

  });

})(Analyst, jQuery);
<commit_msg>profile result reconfig<commit_after>var Analyst = Analyst || {};

(function(A, $) {

  A.analysis.AnalysisMultiPointLayout = Backbone.Marionette.Layout.extend({

    template: Handlebars.getTemplate('analysis', 'analysis-multi-point'),

    events: {
      'click #createQuery': 'createQuery',
      'click #cancelQuery': 'cancelQuery',
      'click #newQuery': 'newQuery',
      'change #shapefile': 'updateAttributes'
    },

    regions: {
      main: "#main"
    },

    initialize: function(options) {
      _.bindAll(this, 'createQuery', 'cancelQuery', 'updateAttributes');
    },

    onRender: function() {
      $('#scenario2-controls').hide();
    },

    onShow: function() {

      var _this = this;

      this.scenarios = new A.models.Scenarios();
      this.queries = new A.models.Queries();
      this.shapefiles = new A.models.Shapefiles();

      this.scenarios.fetch({
        reset: true,
        data: {
          projectId: A.app.selectedProject
        },
        success: function(collection, response, options) {

          _this.$(".scenario-list").empty();

          for (var i in _this.scenarios.models) {
            if (_this.scenarios.models[i].get("id") == "default")
              _this.$(".scenario-list").append('<option selected value="' + _this.scenarios.models[i].get(
                "id") + '">' + _this.scenarios.models[i].get("name") + '</option>');
            else
              _this.$(".scenario-list").append('<option value="' + _this.scenarios.models[i].get("id") +
                '">' + _this.scenarios.models[i].get("name") + '</option>');

          }

        }
      });

      this.queries.fetch({
        reset: true,
        data: {
          projectId: A.app.selectedProject
        },
        success: function(collection, response, options) {

        }
      });

      this.shapefiles.fetch({data: {projectId: A.app.selectedProject}})
        .done(function () {
          _this.shapefiles.each(function (shp) {
            $('<option>')
              .attr('value', shp.id)
              .text(shp.get('name'))
              .appendTo(this.$('#shapefile'));
          });

          _this.updateAttributes();
        });

      this.mode = "WALK,TRANSIT";

      this.$('input[name=mode1]:radio').on('change', function(event) {
        _this.mode = _this.$('input:radio[name=mode1]:checked').val();
      });

      this.$("#createQueryForm").hide();

      var queryListLayout = new A.analysis.QueryList({
        collection: this.queries
      });

      this.main.show(queryListLayout);
    },

    /**
     * Update the attributes select to show the attributes of the current shapefile
     */
    updateAttributes: function () {
      var shpId = this.$('#shapefile').val();
      var shp = this.shapefiles.get(shpId);
      var _this = this;

      this.$('#shapefileColumn').empty();

      shp.getNumericAttributes().forEach(function (attr) {
        var atName = A.models.Shapefile.attributeName(attr);

        $('<option>')
          .attr('value', attr.fieldName)
          .text(atName)
          .appendTo(_this.$('#shapefileColumn'));
      });
    },

    createQuery: function(evt) {

      var _this = this;

      var data = {
        name: this.$("#name").val(),
        mode: this.mode,
        shapefileId: this.$('#shapefile').val(),
        attributeName: this.$('#shapefileColumn').val(),
        scenarioId: this.$('#scenario1').val(),
        projectId: A.app.selectedProject
      };

      var query = new A.models.Query();
      query.save(data, {
        success: function() {
          _this.queries.fetch({
            reset: true,
            data: {
              projectId: A.app.selectedProject
            },
            success: function(collection, response, options) {

            }
          });
        }
      });

      this.$("#createQueryForm").hide();
    },

    cancelQuery: function(evt) {
      this.$("#createQueryForm").hide();
    },

    newQuery: function(evt) {
      this.$("#createQueryForm").show();
    }

  });

  A.analysis.QueryListItem = Backbone.Marionette.ItemView.extend({
    tagName: 'li',
    className: 'list-group-item',

    template: Handlebars.getTemplate('analysis', 'query-list-item'),

    events: {

      'click #deleteItem': 'deleteItem',
      'click #queryCheckbox': 'clickItem',
      'click #normalizeCheckbox': 'normalizeBy',
      'click #compareCheckbox': 'compareTo',
      'click #exportShape': 'exportShape',
      'click #updateMap': 'updateMap',
      'change #weightByShapefile': 'updateAttributes'
    },

    modelEvents: {
      'change': 'fieldsChanged'
    },

    initialize: function() {
      var _this = this;
      this.updateInterval = setInterval(function() {
        if (!_this.isComplete()) {
          _this.model.fetch();
        } else {
          // don't keep polling
          clearInterval(_this.updateInterval);
        }
      }, 1000);
    },

    onClose: function() {
      clearInterval(this.updateInterval);

      if (this.queryOverlay && A.map.hasLayer(this.queryOverlay))
        A.map.removeLayer(this.queryOverlay);

    },

    fieldsChanged: function() {
      this.render();
    },

    serializeData: function() {

      var data = this.model.toJSON();

      if (this.isStarting())
        data['starting'] = true;
      else if (this.isComplete())
        data['complete'] = true;

      data['pointSetName'] = this.model.pointSetName();

      return data;

    },

    isStarting: function() {
      return this.model.get("totalPoints") === null || this.model.get('totalPoints') == -1;
    },

    isComplete: function() {
      var tp = this.model.get("totalPoints");
      var cp = this.model.get("completePoints");
      return tp !== null && cp !== null && tp == cp;
    },

    clickItem: function(evt) {

      this.refreshMap();

    },

    exportShape: function(evt) {

      this.normalizeBy();
      this.compareTo();

      var timeLimit = this.timeSlider.getValue() * 60;

      var url = '/gis/query?queryId=' + this.model.id + '&timeLimit=' + timeLimit;

      if (this.groupById)
        url = url + "&groupBy=" + this.groupById;

      if (this.weightByShapefile)
        url = url + "&weightByShapefile=" + this.weightByShapefile + '&weightByAttribute=' + this.weightByAttribute;

      url += '&which=' + this.which;

      if (this.compareToId)
        url += '&compareTo=' + this.compareToId;

      window.open(url);

    },

    updateMap: function(evt) {
      this.refreshMap();
    },

    normalizeBy: function(evt) {

      if (this.$("#normalizeCheckbox").prop('checked')) {
        this.$("#weightByShapefile").prop("disabled", false);
        this.$("#weightByAttribute").prop("disabled", false);
        this.$("#groupBy").prop("disabled", false);
        this.$('#aggregation-controls').slideDown();

        this.weightByShapefile = this.$("#weightByShapefile").val();
        this.weightByName = this.$('#weightByShapefile :selected').text() + ' ' +
          this.$('#weightByAttribute :selected').text();

        this.weightByAttribute = this.$("#weightByAttribute").val();

        this.groupById = this.$('#groupBy').val();
        this.groupByName = this.$('#groupBy :selected').text();
      } else {
        this.$("#weightByShapefile").prop("disabled", true);
        this.$("#weightByAttribute").prop("disabled", true);

        this.weightByShapefile = false;
        this.weightByName = '';
        this.weightByAttribute = false;

        this.groupById = false;
        this.groupByName = '';
        this.$("#groupBy").prop("disabled", true);

        this.$('#aggregation-controls').slideUp();
      }
    },

    compareTo: function () {
      if (this.$('#compareCheckbox').is(':checked')) {
        this.compareToId = this.$('#compareTo').val();
        this.$('#compareControls').removeClass('hidden');
      } else {
        this.compareToId = false;
        this.$('#compareControls').addClass('hidden');
      }
    },

    deleteItem: function(evt) {
      this.model.destroy();
    },

    refreshMap: function() {
      var target = this.$("#queryCheckbox");
      var _this = this;

      this.normalizeBy();
      this.compareTo();

      this.which = this.$('.whichMulti input:checked').val();

      var legendTitle;
      if (this.groupById) {
        legendTitle = Messages('analysis.aggregated-title',
          this.model.pointSetName(),
          this.groupByName,
          this.weightByName
        );
      } else {
        legendTitle = Messages('analysis.accessibility-to', this.model.pointSetName());
      }

      // add the suffix, e.g. (best case)
      legendTitle += ' ';

      if (this.which == 'POINT_ESTIMATE')
        legendTitle += window.Messages('analysis.point-estimate-suffix');

      else if (this.which == 'WORST_CASE')
        legendTitle += window.Messages('analysis.worst-case-suffix');

      else if (this.which == 'BEST_CASE')
        legendTitle += window.Messages('analysis.best-case-suffix');

      else if (this.which == 'SPREAD')
        legendTitle += window.Messages('analysis.spread-suffix');

      if (target.prop("checked")) {
        if (A.map.hasLayer(this.queryOverlay))
          A.map.removeLayer(this.queryOverlay);

        var timeLimit = this.timeSlider.getValue() * 60;

        var url = 'queryId=' + this.model.id + '&timeLimit=' + timeLimit;

        if (this.groupById)
          url = url + "&groupBy=" + this.groupById;

        if (this.weightByShapefile)
          url = url + "&weightByShapefile=" + this.weightByShapefile + '&weightByAttribute=' + this.weightByAttribute;

        url += '&which=' + this.which;

        if (this.compareToId)
          url = url + '&compareTo=' + this.compareToId;

        this.$(".legendTitle").text(legendTitle);

        var legendItemTemplate = Handlebars.getTemplate('analysis', 'query-legend-item')

        this.$("#legendData").empty();
        this.$("#updatingMap").show();

        var _map = A.map;

        $.getJSON('/api/queryBins?' + url, function(data) {

          _this.queryOverlay = L.tileLayer('/tile/query?z={z}&x={x}&y={y}&' + url).addTo(_map);

          _this.$("#updatingMap").hide();

          for (var i in data) {

            var lower = _this.numberWithCommas(parseFloat(data[i].lower).toFixed(2));
            var upper = _this.numberWithCommas(parseFloat(data[i].upper).toFixed(2));
            var legendItem = {
              color: data[i].hexColor,
              label: lower + " - " + upper
            };

            _this.$("#legendData").append(legendItemTemplate(legendItem));

          }
        });

        this.$("#legend").show();

      } else {
        if (A.map.hasLayer(this.queryOverlay))
          A.map.removeLayer(this.queryOverlay);

        this.$("#legend").hide();
      }
    },

    /** Update available shapefile attributes for weighting */
    updateAttributes: function () {
      this.$('#weightByAttribute').empty();

      var _this = this;

      var shp = this.shapefiles.get(this.$('#weightByShapefile').val());

      shp.getNumericAttributes().forEach(function (attr) {
        var atName = A.models.Shapefile.attributeName(attr);

        $('<option>')
          .attr('value', attr.fieldName)
          .text(atName)
          .appendTo(_this.$('#weightByAttribute'));
      });

    },

    onRender: function() {

      var _this = this;

      if (this.isComplete()) {
        if (this.model.get('transit') && false) {
          // we have transit modes, so it's a profile request
          this.$('.whichMulti input[value="POINT_ESTIMATE"]').parent().remove();
          this.$('.whichMulti input[value="SPREAD"]').parent().remove();
          this.$('.whichMulti input[value="WORST_CASE"]').prop('checked', true).parent().addClass('active');
        } else {
          // it's a stock/vanilla request
          this.$('.whichMulti input[value="WORST_CASE"]').parent().remove();
          this.$('.whichMulti input[value="BEST_CASE"]').parent().remove();
          this.$('.whichMulti input[value="SPREAD"]').parent().remove();
          this.$('.whichMulti input[value="POINT_ESTIMATE"]').prop('checked', true).parent().addClass('active');
        }

        // Set up weight and group by select boxes
        // we weight and group by shapefiles. for weighting we also specify an attribute.
        this.shapefiles = new A.models.Shapefiles();
        this.shapefiles.fetch({
            data: {
              projectId: this.model.get("projectId")
            }
          })
          .done(function() {
            _this.$('#groupBy').empty();

            _this.shapefiles.each(function(shapefile) {
              $('<option>')
                .attr('value', shapefile.id)
                .text(shapefile.get('name'))
                .appendTo(_this.$('#weightByShapefile'));

                $('<option>')
                .attr('value', shapefile.id)
                .text(shapefile.get('name'))
                .appendTo(_this.$('#groupBy'));
            });

            _this.updateAttributes();
          });

        // set up the comparison select boxes
        this.comparisonQueries = new A.models.Queries();
        this.comparisonQueries.fetch({
          data: {
            pointSetId: this.model.get('pointSetId')
          }
        }).done(function () {
          _this.comparisonQueries.each(function (query) {
            $('<option>')
            .attr('value', query.id)
            .text(query.get('name'))
            .appendTo(_this.$('#compareTo'));
          });
        });

        this.$("#weightBy").prop("disabled", true);
        this.$("#groupBy").prop("disabled", true);

        this.$("#settings").show();
      } else
        this.$("#settings").hide();

      this.timeSlider = this.$('#timeSlider').slider({
        formater: function(value) {
          return value + " minutes";
        }
      }).on('slideStop', function(evt) {
        _this.$('#timeLimitValue').html(evt.value + " mins");
      }).data('slider');

    },
    numberWithCommas: function(x) {
      var parts = x.toString().split(".");
      parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
      return parts.join(".");
    }

  });

  A.analysis.QueryList = Backbone.Marionette.CompositeView.extend({

    template: Handlebars.getTemplate('analysis', 'query-list'),
    itemView: A.analysis.QueryListItem,

    initialize: function() {
      this.queryOverlay = {};

    },

    modelEvents: {
      'change': 'render'
    },

    onShow: function() {

    },

    appendHtml: function(collectionView, itemView) {
      collectionView.$("#queryList").append(itemView.el);
    }

  });

})(Analyst, jQuery);
<|endoftext|>


<commit_before>const Article = require('./lib/article')
const Build = require('./lib/source')

module.exports = {
    Article: Article,
    Build: Build
}<commit_msg>Fixed bug<commit_after><|endoftext|>


<commit_before>import $ from 'jquery';

/**
 * Property Reference.
 */
class Reference {

  /**
   * Initialize Property Reference.
   */
  static init() {
    new Reference().binds();
  }

  /**
   * Bind elements with functions.
   */
  binds() {
    $(document).on('click', '.papi-property-reference .handlediv', this.toggle);
  }

  /**
   * Toggle page type div.
   *
   * @parma {object} e
   */
  toggle(e) {
    e.preventDefault();

    $(this).parent()
      .toggleClass('closed')
      .next()
      .toggle();
  }
}

export default Reference;
<commit_msg>Use `bind` and `e.currentTarget` for reference property<commit_after>import $ from 'jquery';

/**
 * Property Reference.
 */
class Reference {

  /**
   * Initialize Property Reference.
   */
  static init() {
    new Reference().binds();
  }

  /**
   * Bind elements with functions.
   */
  binds() {
    $(document).on('click', '.papi-property-reference .handlediv', this.toggle.bind(this));
  }

  /**
   * Toggle page type div.
   *
   * @parma {object} e
   */
  toggle(e) {
    e.preventDefault();

    $(e.currentTarget)
      .parent()
      .toggleClass('closed')
      .next()
      .toggle();
  }
}

export default Reference;
<|endoftext|>