<commit_before>/* The resolver primarily does three things:
 * 1) Resolve import statements by injecting declarations from
 *    the imported module into the main context's alias table
 * 2) Resolve aliases to balues
 * 3) Infer the type of all values
 * The resolver returns an AST with no import statements and
 * no aliases, in which all values have been annotated with a type
 *****************************************************************/

var fs = require('fs'),
	sys = require('sys'),
	path = require('path')

var tokenizer = require('./tokenizer'),
	parser = require('./parser')

var util = require('./util'),
	bind = util.bind,
	map = util.map,
	name = util.name,
	shallowCopy = util.shallowCopy

// TODO Read types from types
// TODO read tags from tags
var Tags = require('./Tags'),
	Types = require('./Types')

exports.resolve = util.intercept('ResolveError', function(ast, context) {
	if (!context) {
		context = { modules:{}, declarations:[], fileDependencies:[], aliases: {} }
	}
	var ast = resolve(context, ast)
	return {
		ast: ast,
		modules: context.modules,
		declarations: context.declarations,
		dependencies: context.dependencies
	}
})

/************************
 * Top level statements *
 ************************/
var resolve = function(context, ast) {
	if (!ast) {
		return null
	} else if (ast instanceof Array) {
		return map(ast, bind(this, resolveStatement, context))
	} else {
		return resolveStatement(context, ast)
	}
}

var resolveStatement = function(context, ast) {
	switch(ast.type) {
		case 'IMPORT_MODULE':        handleModuleImport(context, ast)      ;break
		case 'IMPORT_FILE':          handleFileImport(context, ast)        ;break
		case 'DECLARATION':          handleDeclaration(context, ast)       ;break

		case 'XML':                  return resolveXML(context, ast)
		case 'IF_STATEMENT':         return resolveIfStatement(context, ast)
		case 'FOR_LOOP':             return resolveForLoop(context, ast)
		case 'INVOCATION':           return resolveInvocation(context, ast)

		case 'MUTATION':             return resolveMutation(context, ast)
		case 'MUTATION_DECLARATION': handleDeclaration(context, ast)       ;break

		case 'ALIAS':                return lookup(context, ast)

		case 'RUNTIME_ITERATOR':     return resolveRuntimeIterator(context, ast)
		case 'ITEM_PROPERTY':        return resolveItemProperty(context, ast)
		case 'STATIC_VALUE':         return resolveStaticValue(context, ast)
		case 'COMPOSITE':            return resolveCompositeStatement(context, ast)

		// Inline handler - will be compiled inline. Fall through to debugger to then return the AST
		case 'HANDLER':              resolve(createScope(context), ast.block)
		case 'DEBUGGER':             return ast

		default:                     console.log(ast); UNKNOWN_AST_TYPE
	}
}

/******************
 * Lookup aliases *
 ******************/
var lookup = function(context, aliasOrValue, allowMiss) {
	if (aliasOrValue.type == 'OBJECT_LITERAL') {
		for (var i=0, prop; prop = aliasOrValue.content[i]; i++) {
			prop.value = lookup(context, prop.value, allowMiss)
		}
	}
	if (aliasOrValue.type != 'ALIAS') { return aliasOrValue }
	else { return _lookupAlias(context, aliasOrValue, allowMiss) }
}
var _lookupAlias = function(context, ast, allowMiss) {
	var lookupNamespace = [],
		namespace = ast.namespace,
		aliases = context.aliases

	for (var i=0; i < namespace.length; i++) {
		lookupNamespace.push(namespace[i])
		var namespaceKey = lookupNamespace.join('.'),
			value = aliases[namespaceKey]

		if (!value) { continue }

		switch(value.type) {
			case 'RUNTIME_ITERATOR':
				return util.shallowCopy(value, { iteratorProperty: namespace.slice(i+1).join('.') })
			case 'ITEM':
				return util.shallowCopy(ast, { type: 'ITEM_PROPERTY', item:value, property:namespace.slice(i+1) })
			case 'JAVASCRIPT_BRIDGE':
				return value
			case 'ALIAS':
				return _lookupAlias(context, value)
			default:
				return value
		}
	}

	if (allowMiss) { return }
	else { halt(ast, 'Lookup of undeclared alias "'+namespace.join('.')+'"') }
}

/*******************
 * Item Properties *
 *******************/
var resolveItemProperty = function(context, ast) {
	// TODO Can we infer the type of item properties?
	return Types.infer(ast, [])
}

/*****************
 * Static values *
 *****************/
var resolveStaticValue = function(context, ast) {
	switch(ast.valueType) {
		case 'string': return Types.infer(ast, [Types.byName.Text])
		case 'number': return Types.infer(ast, [Types.byName.Number])
		default:       halt(ast, 'Unknown static value type "'+ast.valueType+'"')
	}
}

/************************
 * Composite statements *
 ************************/
var resolveCompositeStatement = function(context, ast) {
	ast.left = resolveStatement(context, ast.left)
	ast.right = resolveStatement(context, ast.right)
	return ast
}

/*******
 * XML *
 *******/
var resolveXML = function(context, ast) {
	_resolveXMLAttributes(context, ast.attributes)
	ast.block = resolve(context, ast.block)
	return ast
}

var _resolveXMLAttributes = function(context, attributes) {
	var i = 0, attrAST,
		dataAttr, dataTypeAttr
	while (attrAST = attributes[i]) {
		var valueType = attrAST.value.type,
			attributeName = attrAST.namespace.join('.')
		switch(attrAST.value.type) {
			case 'OBJECT_LITERAL':
				var nestedAttrs = map(attrAST.value.content, function(kvp) {
					return { namespace:attrAST.namespace.concat(kvp.name), value:kvp.value }
				})
				// splice out the nestedAST rather than increment i
				attributes.splice.apply(attributes, [i, 1].concat(nestedAttrs))
				break
			case 'ALIAS':
				attrAST.value = lookup(context, attrAST.value)
				break
			default:
				i++
				break
		}

		if (attributeName == 'dataType') { dataTypeAttr = attrAST }
		else if (attributeName == 'data') { dataAttr = attrAST }
		else if (attributeName.match(/on\w+/)) { resolve(context, attrAST.value) } // resolve the code in the handler
	}
	if (dataTypeAttr) {
		assert(dataTypeAttr, !!dataAttr, 'Found dataType attribute but no data attribute')
		var value = dataTypeAttr.value
		assert(dataTypeAttr, value.type == 'STATIC_VALUE' && value.valueType == 'string', 'dataType should be a static string, like "number"')
		var valueStr = value.value.toLowerCase()
		Types.infer(dataAttr.value, [valueStr])
		dataAttr.dataType = valueStr
	}
}

/*******************************
 * Imports (modules and files) *
 *******************************/
var handleModuleImport = function(context, ast) {
	if (context.modules[ast.name]) { return }
	var module = context.modules[ast.name] = { name: ast.name, path: __dirname + '/Modules/' + ast.name + '/' }
	assert(ast, fs.statSync(module.path).isDirectory(), 'Could not find the module at ' + module.path)

	// TODO Read a package/manifest.json file in the module directory, describing name/version/which files to load, etc
	var funFile = module.path + module.name + '.fun'
	if (fs.statSync(funFile).isFile()) { _importFile(funFile, context) }

	var jsFile = module.path + module.name + '.js'
	if (path.existsSync(jsFile)) { module.jsCode = fs.readFileSync(jsFile) }
	else { module.jsCode = '// No JS code for ' + module.name }
}

var handleFileImport = function(context, ast) {
	var filePath = __dirname + '/' + ast.path + '.fun'
	assert(ast, path.existsSync(filePath), 'Could not find file for import: "'+filePath+'"')
	context.fileDependencies.push(filePath)
	_importFile(filePath, context, true)
}

var _importFile = function(path, context, a) {
	var tokens = tokenizer.tokenize(path)
	var newAST = parser.parse(tokens)
	resolve(context, newAST)
}

/***************
 * Invocations *
 ***************/
var resolveInvocation = function(context, ast) {
	if (ast.alias) { ast.invocable = lookup(context, ast.alias) }
	assert(ast, ast.invocable, 'Found an invocation without a reference to a invocable')
	return ast
}

/*************
 * Mutations *
 *************/
var resolveMutation = function(context, ast) {
	ast.args = map(ast.args, bind(this, lookup, context))
	ast.value = lookup(context, ast.alias, true)

	// HACK For Javascript bridges, the method ("connect" in Facebook.connect()),
	//  gets popped off of the namespace in _parseInvocation. Try to look up
	//  ast.alias + ast.method and check to see if it maps to a javascript bridge.
	//  If it does, then go with that. Possibly, _parseInvocation could not pop off
	//  the last part of the namespace and interpret it as the method. However, that
	//  would require the resolver or the compiler to detect item property mutations,
	//  and pop off the last part of the namespace for method then. This works for now.
	if (!ast.value) {
		ast.alias.namespace.push(ast.method)
		var lookForJSBridge = lookup(context, ast.alias)
		if (lookForJSBridge.type == 'JAVASCRIPT_BRIDGE') {
			ast.value = lookForJSBridge
		}
	}

	switch(ast.value.type) {
		case 'ITEM_PROPERTY':
			Types.inferByMethod(ast.value, ast.method)
			break
		case 'JAVASCRIPT_BRIDGE':
			break // do nothing
	}

	delete ast.alias

	return ast
}

/*************
 * For loops *
 *************/
var resolveForLoop = function(context, ast) {
	ast.iteratorRuntimeName = ast.iterator.value.runtimeName = name('RUNTIME_ITERATOR_NAME')
	ast.iterable = lookup(context, ast.iterable)
	ast.iterator.value.iterable = ast.iterable
	Types.infer(ast.iterable, [Types.byName.List])
	var loopContext = createScope(context)
	handleDeclaration(loopContext, ast.iterator)
	ast.block = resolve(loopContext, ast.block)
	return ast
}

var resolveRuntimeIterator = function(context, ast) {
	// TODO give types to runtime iterators, so that you can have complex items in lists
	// TODO Infer type of iterator from the iterable
	return Types.infer(ast, [Types.Text])
}

/*****************
 * If statements *
 *****************/
var resolveIfStatement = function(context, ast) {
	ast.condition.left = lookup(context, ast.condition.left)
	if (ast.condition.right) {
		ast.condition.right = lookup(context, ast.condition.right)
	}
	ast.ifBlock = resolve(createScope(context), ast.ifBlock)
	if (ast.elseBlock) {
		ast.elseBlock = resolve(createScope(context), ast.elseBlock)
	}
	return ast
}

/****************
 * Declarations *
 ****************/
var handleDeclaration = function(context, ast) {
	var value = ast.value
	switch(value.type) {
		case 'TEMPLATE':
		case 'HANDLER':
			context.declarations.push(value)
			resolve(createScope(context), value.block)
			break
		case 'MUTATION_ITEM_CREATION':
			util.each(value.properties.content, function(prop) {
				prop.value = lookup(context, prop.value)
			})
			break
		case 'JAVASCRIPT_BRIDGE':
			// do nothing
			break
		default:
			// do nothing
	}
	_declareAlias(context, ast)
}
var _declareAlias = function(context, ast) {
	var aliases = context.aliases,
		namespace = ast.namespace,
		valueAST = ast.value

	if (valueAST.type == 'OBJECT_LITERAL') {
		var baseNamespace = ast.namespace
		for (var i=0, kvp; kvp = valueAST.content[i]; i++) {
			var nestedDeclarationAST = util.create(ast)
			nestedDeclarationAST.namespace = namespace.concat(kvp.name)
			nestedDeclarationAST.value = kvp.value
			handleDeclaration(context, nestedDeclarationAST)
		}
	} else {
		var namespaceKey = ast.namespace.join('.')
		assert(ast, !aliases[namespaceKey], 'Repeat declaration of "'+namespaceKey+'"')
		aliases[namespaceKey] = valueAST
	}
}

/***********
 * Utility *
 ***********/
var ResolveError = function(ast, msg) {
	var info = ast.info || {}
	this.name = "ResolveError"
	this.message = ['on line', info.line + ',', 'column', info.column, 'of', '"'+info.file+'":', msg].join(' ')
}
ResolveError.prototype = Error.prototype

var createScope = function(context) {
	// Creates a scope by prototypically inheriting from the current context.
	// Reads will propegate up the prototype chain, while writes won't.
	// However, writes *will* shadow values up the prototype chain
	context = util.create(context)
	context.aliases = shallowCopy(context.aliases)
	return context
}

var assert = function(ast, ok, msg) { if (!ok) halt(ast, msg) }
var halt = function(ast, msg) {
	var info = ast.info || {}
	if (info.file) { sys.puts(util.grabLine(info.file, info.line, info.column, info.span)) }
	throw new ResolveError(ast, msg)
}
<commit_msg>Collect the dynamic-value child ASTs for every composite statement - they will be used in order for a statement to wait for all dynamic values that the statement depends on before it evaluates<commit_after>/* The resolver primarily does three things:
 * 1) Resolve import statements by injecting declarations from
 *    the imported module into the main context's alias table
 * 2) Resolve aliases to balues
 * 3) Infer the type of all values
 * The resolver returns an AST with no import statements and
 * no aliases, in which all values have been annotated with a type
 *****************************************************************/

var fs = require('fs'),
	sys = require('sys'),
	path = require('path')

var tokenizer = require('./tokenizer'),
	parser = require('./parser')

var util = require('./util'),
	bind = util.bind,
	map = util.map,
	name = util.name,
	shallowCopy = util.shallowCopy

// TODO Read types from types
// TODO read tags from tags
var Tags = require('./Tags'),
	Types = require('./Types')

exports.resolve = util.intercept('ResolveError', function(ast, context) {
	if (!context) {
		context = { modules:{}, declarations:[], fileDependencies:[], aliases: {} }
	}
	var ast = resolve(context, ast)
	return {
		ast: ast,
		modules: context.modules,
		declarations: context.declarations,
		dependencies: context.dependencies
	}
})

/************************
 * Top level statements *
 ************************/
var resolve = function(context, ast) {
	if (!ast) {
		return null
	} else if (ast instanceof Array) {
		return map(ast, bind(this, resolveStatement, context))
	} else {
		return resolveStatement(context, ast)
	}
}

var resolveStatement = function(context, ast) {
	switch(ast.type) {
		case 'IMPORT_MODULE':        handleModuleImport(context, ast)      ;break
		case 'IMPORT_FILE':          handleFileImport(context, ast)        ;break
		case 'DECLARATION':          handleDeclaration(context, ast)       ;break

		case 'XML':                  return resolveXML(context, ast)
		case 'IF_STATEMENT':         return resolveIfStatement(context, ast)
		case 'FOR_LOOP':             return resolveForLoop(context, ast)
		case 'INVOCATION':           return resolveInvocation(context, ast)

		case 'MUTATION':             return resolveMutation(context, ast)
		case 'MUTATION_DECLARATION': handleDeclaration(context, ast)       ;break

		case 'ALIAS':                return lookup(context, ast)

		case 'RUNTIME_ITERATOR':     return resolveRuntimeIterator(context, ast)
		case 'ITEM_PROPERTY':        return resolveItemProperty(context, ast)
		case 'STATIC_VALUE':         return resolveStaticValue(context, ast)
		case 'COMPOSITE':            return resolveCompositeStatement(context, ast)

		// Inline handler - will be compiled inline. Fall through to debugger to then return the AST
		case 'HANDLER':              resolve(createScope(context), ast.block)
		case 'DEBUGGER':             return ast

		default:                     console.log(ast); UNKNOWN_AST_TYPE
	}
}

/******************
 * Lookup aliases *
 ******************/
var lookup = function(context, aliasOrValue, allowMiss) {
	if (aliasOrValue.type == 'OBJECT_LITERAL') {
		for (var i=0, prop; prop = aliasOrValue.content[i]; i++) {
			prop.value = lookup(context, prop.value, allowMiss)
		}
	}
	if (aliasOrValue.type != 'ALIAS') { return aliasOrValue }
	else { return _lookupAlias(context, aliasOrValue, allowMiss) }
}
var _lookupAlias = function(context, ast, allowMiss) {
	var lookupNamespace = [],
		namespace = ast.namespace,
		aliases = context.aliases

	for (var i=0; i < namespace.length; i++) {
		lookupNamespace.push(namespace[i])
		var namespaceKey = lookupNamespace.join('.'),
			value = aliases[namespaceKey]

		if (!value) { continue }

		switch(value.type) {
			case 'RUNTIME_ITERATOR':
				return util.shallowCopy(value, { iteratorProperty: namespace.slice(i+1).join('.') })
			case 'ITEM':
				return util.shallowCopy(ast, { type: 'ITEM_PROPERTY', item:value, property:namespace.slice(i+1) })
			case 'JAVASCRIPT_BRIDGE':
				return value
			case 'ALIAS':
				return _lookupAlias(context, value)
			default:
				return value
		}
	}

	if (allowMiss) { return }
	else { halt(ast, 'Lookup of undeclared alias "'+namespace.join('.')+'"') }
}

/*******************
 * Item Properties *
 *******************/
var resolveItemProperty = function(context, ast) {
	// TODO Can we infer the type of item properties?
	return Types.infer(ast, [])
}

/*****************
 * Static values *
 *****************/
var resolveStaticValue = function(context, ast) {
	switch(ast.valueType) {
		case 'string': return Types.infer(ast, [Types.byName.Text])
		case 'number': return Types.infer(ast, [Types.byName.Number])
		default:       halt(ast, 'Unknown static value type "'+ast.valueType+'"')
	}
}

/************************
 * Composite statements *
 ************************/
var resolveCompositeStatement = function(context, ast) {
	ast.left = resolveStatement(context, ast.left)
	ast.right = resolveStatement(context, ast.right)
	ast.dynamicASTs = _collectDynamicASTs(ast)
	return ast
}

var _collectDynamicASTs = function(ast) {
	switch(ast.type) {
		case 'COMPOSITE':
			return _collectDynamicASTs(ast.left).concat(_collectDynamicASTs(ast.right))
		case 'ITEM_PROPERTY':
		case 'RUNTIME_ITERATOR':
			return [ast]
		case 'STATIC_VALUE':
			return []
		default:
			console.log(ast); UNKNOWN_AST_TYPE
	}
}

/*******
 * XML *
 *******/
var resolveXML = function(context, ast) {
	_resolveXMLAttributes(context, ast.attributes)
	ast.block = resolve(context, ast.block)
	return ast
}

var _resolveXMLAttributes = function(context, attributes) {
	var i = 0, attrAST,
		dataAttr, dataTypeAttr
	while (attrAST = attributes[i]) {
		var valueType = attrAST.value.type,
			attributeName = attrAST.namespace.join('.')
		switch(attrAST.value.type) {
			case 'OBJECT_LITERAL':
				var nestedAttrs = map(attrAST.value.content, function(kvp) {
					return { namespace:attrAST.namespace.concat(kvp.name), value:kvp.value }
				})
				// splice out the nestedAST rather than increment i
				attributes.splice.apply(attributes, [i, 1].concat(nestedAttrs))
				break
			case 'ALIAS':
				attrAST.value = lookup(context, attrAST.value)
				break
			default:
				i++
				break
		}

		if (attributeName == 'dataType') { dataTypeAttr = attrAST }
		else if (attributeName == 'data') { dataAttr = attrAST }
		else if (attributeName.match(/on\w+/)) { resolve(context, attrAST.value) } // resolve the code in the handler
	}
	if (dataTypeAttr) {
		assert(dataTypeAttr, !!dataAttr, 'Found dataType attribute but no data attribute')
		var value = dataTypeAttr.value
		assert(dataTypeAttr, value.type == 'STATIC_VALUE' && value.valueType == 'string', 'dataType should be a static string, like "number"')
		var valueStr = value.value.toLowerCase()
		Types.infer(dataAttr.value, [valueStr])
		dataAttr.dataType = valueStr
	}
}

/*******************************
 * Imports (modules and files) *
 *******************************/
var handleModuleImport = function(context, ast) {
	if (context.modules[ast.name]) { return }
	var module = context.modules[ast.name] = { name: ast.name, path: __dirname + '/Modules/' + ast.name + '/' }
	assert(ast, fs.statSync(module.path).isDirectory(), 'Could not find the module at ' + module.path)

	// TODO Read a package/manifest.json file in the module directory, describing name/version/which files to load, etc
	var funFile = module.path + module.name + '.fun'
	if (fs.statSync(funFile).isFile()) { _importFile(funFile, context) }

	var jsFile = module.path + module.name + '.js'
	if (path.existsSync(jsFile)) { module.jsCode = fs.readFileSync(jsFile) }
	else { module.jsCode = '// No JS code for ' + module.name }
}

var handleFileImport = function(context, ast) {
	var filePath = __dirname + '/' + ast.path + '.fun'
	assert(ast, path.existsSync(filePath), 'Could not find file for import: "'+filePath+'"')
	context.fileDependencies.push(filePath)
	_importFile(filePath, context, true)
}

var _importFile = function(path, context, a) {
	var tokens = tokenizer.tokenize(path)
	var newAST = parser.parse(tokens)
	resolve(context, newAST)
}

/***************
 * Invocations *
 ***************/
var resolveInvocation = function(context, ast) {
	if (ast.alias) { ast.invocable = lookup(context, ast.alias) }
	assert(ast, ast.invocable, 'Found an invocation without a reference to a invocable')
	return ast
}

/*************
 * Mutations *
 *************/
var resolveMutation = function(context, ast) {
	ast.args = map(ast.args, bind(this, lookup, context))
	ast.value = lookup(context, ast.alias, true)

	// HACK For Javascript bridges, the method ("connect" in Facebook.connect()),
	//  gets popped off of the namespace in _parseInvocation. Try to look up
	//  ast.alias + ast.method and check to see if it maps to a javascript bridge.
	//  If it does, then go with that. Possibly, _parseInvocation could not pop off
	//  the last part of the namespace and interpret it as the method. However, that
	//  would require the resolver or the compiler to detect item property mutations,
	//  and pop off the last part of the namespace for method then. This works for now.
	if (!ast.value) {
		ast.alias.namespace.push(ast.method)
		var lookForJSBridge = lookup(context, ast.alias)
		if (lookForJSBridge.type == 'JAVASCRIPT_BRIDGE') {
			ast.value = lookForJSBridge
		}
	}

	switch(ast.value.type) {
		case 'ITEM_PROPERTY':
			Types.inferByMethod(ast.value, ast.method)
			break
		case 'JAVASCRIPT_BRIDGE':
			break // do nothing
	}

	delete ast.alias

	return ast
}

/*************
 * For loops *
 *************/
var resolveForLoop = function(context, ast) {
	ast.iteratorRuntimeName = ast.iterator.value.runtimeName = name('RUNTIME_ITERATOR_NAME')
	ast.iterable = lookup(context, ast.iterable)
	ast.iterator.value.iterable = ast.iterable
	Types.infer(ast.iterable, [Types.byName.List])
	var loopContext = createScope(context)
	handleDeclaration(loopContext, ast.iterator)
	ast.block = resolve(loopContext, ast.block)
	return ast
}

var resolveRuntimeIterator = function(context, ast) {
	// TODO give types to runtime iterators, so that you can have complex items in lists
	// TODO Infer type of iterator from the iterable
	return Types.infer(ast, [Types.Text])
}

/*****************
 * If statements *
 *****************/
var resolveIfStatement = function(context, ast) {
	ast.condition.left = lookup(context, ast.condition.left)
	if (ast.condition.right) {
		ast.condition.right = lookup(context, ast.condition.right)
	}
	ast.ifBlock = resolve(createScope(context), ast.ifBlock)
	if (ast.elseBlock) {
		ast.elseBlock = resolve(createScope(context), ast.elseBlock)
	}
	return ast
}

/****************
 * Declarations *
 ****************/
var handleDeclaration = function(context, ast) {
	var value = ast.value
	switch(value.type) {
		case 'TEMPLATE':
		case 'HANDLER':
			context.declarations.push(value)
			resolve(createScope(context), value.block)
			break
		case 'MUTATION_ITEM_CREATION':
			util.each(value.properties.content, function(prop) {
				prop.value = lookup(context, prop.value)
			})
			break
		case 'JAVASCRIPT_BRIDGE':
			// do nothing
			break
		default:
			// do nothing
	}
	_declareAlias(context, ast)
}
var _declareAlias = function(context, ast) {
	var aliases = context.aliases,
		namespace = ast.namespace,
		valueAST = ast.value

	if (valueAST.type == 'OBJECT_LITERAL') {
		var baseNamespace = ast.namespace
		for (var i=0, kvp; kvp = valueAST.content[i]; i++) {
			var nestedDeclarationAST = util.create(ast)
			nestedDeclarationAST.namespace = namespace.concat(kvp.name)
			nestedDeclarationAST.value = kvp.value
			handleDeclaration(context, nestedDeclarationAST)
		}
	} else {
		var namespaceKey = ast.namespace.join('.')
		assert(ast, !aliases[namespaceKey], 'Repeat declaration of "'+namespaceKey+'"')
		aliases[namespaceKey] = valueAST
	}
}

/***********
 * Utility *
 ***********/
var ResolveError = function(ast, msg) {
	var info = ast.info || {}
	this.name = "ResolveError"
	this.message = ['on line', info.line + ',', 'column', info.column, 'of', '"'+info.file+'":', msg].join(' ')
}
ResolveError.prototype = Error.prototype

var createScope = function(context) {
	// Creates a scope by prototypically inheriting from the current context.
	// Reads will propegate up the prototype chain, while writes won't.
	// However, writes *will* shadow values up the prototype chain
	context = util.create(context)
	context.aliases = shallowCopy(context.aliases)
	return context
}

var assert = function(ast, ok, msg) { if (!ok) halt(ast, msg) }
var halt = function(ast, msg) {
	var info = ast.info || {}
	if (info.file) { sys.puts(util.grabLine(info.file, info.line, info.column, info.span)) }
	throw new ResolveError(ast, msg)
}
<|endoftext|>


<commit_before>
// +----------------------------------------------------------------------
// | Think.Admin
// +----------------------------------------------------------------------
// | 版权所有 2014~2017 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// +----------------------------------------------------------------------
// | 官方网站: http://think.ctolog.com
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// +----------------------------------------------------------------------
// | github开源项目：https://github.com/zoujingli/Think.Admin
// +----------------------------------------------------------------------

define(['jquery', 'admin.plugs'], function () {

    /*! 定义当前body对象 */
    this.$body = $('.framework-body').add('.framework-topbar');

    /*! 注册 data-load 事件行为 */
    this.$body.on('click', '[data-load]', function () {
        var url = $(this).attr('data-load'), tips = $(this).attr('data-tips');
        if ($(this).attr('data-confirm')) {
            return $.msg.confirm($(this).attr('data-confirm'), _goLoad);
        }
        return _goLoad.call(this);
        function _goLoad() {
            $.form.load(url, {}, 'get', null, true, tips);
        }
    });

    /*! 注册 data-serach 表单搜索行为 */
    this.$body.on('submit', 'form.form-search', function () {
        var url = $(this).attr('action'), split = url.indexOf('?') === -1 ? '?' : '&';
        if ((this.method || 'get').toLowerCase() === 'get') {
            return window.location.href = '#' + $.menu.parseUri(url + split + $(this).serialize());
        }
        $.form.load(url, this, 'post');
    });

    /*! 注册 data-modal 事件行为 */
    this.$body.on('click', '[data-modal]', function () {
        return $.form.modal($(this).attr('data-modal'), 'open_type=modal', $(this).attr('data-title') || '编辑');
    });

    /*! 注册 data-open 事件行为 */
    this.$body.on('click', '[data-open]', function () {
        $.form.href($(this).attr('data-open'), this);
    });

    /*! 注册 data-reload 事件行为 */
    this.$body.on('click', '[data-reload]', function () {
        $.form.reload();
    });

    /*! 注册 data-check 事件行为 */
    this.$body.on('click', '[data-check-target]', function () {
        var checked = !!this.checked;
        $($(this).attr('data-check-target')).map(function () {
            this.checked = checked;
        });
    });

    /*! 注册 data-update 事件行为 */
    this.$body.on('click', '[data-update]', function () {
        var id = $(this).attr('data-update') || (function () {
            var data = [];
            return $($(this).attr('data-list-target') || 'input.list-check-box').map(function () {
                (this.checked) && data.push(this.value);
            }), data.join(',');
        }).call(this);
        if (id.length < 1) {
            return $.msg.tips('请选择需要操作的数据！');
        }
        var action = $(this).attr('data-action') || $(this).parents('[data-location]').attr('data-location');
        var value = $(this).attr('data-value') || 0, field = $(this).attr('data-field') || 'status';
        $.msg.confirm('确定要操作这些数据吗？', function () {
            $.form.load(action, {field: field, value: value, id: id}, 'POST');
        });
    });

    /*! 注册 data-href 事件行为 */
    this.$body.on('click', '[data-href]', function () {
        var href = $(this).attr('data-href');
        (href && href.indexOf('#') !== 0) && (window.location.href = href);
    });

    /*! 注册 data-page-href 事件行为 */
    this.$body.on('click', 'a[data-page-href]', function () {
        window.location.href = '#' + $.menu.parseUri(this.href, this);
    });

    /*! 注册 data-file 事件行为 */
    this.$body.on('click', '[data-file]', function () {
        var method = $(this).attr('data-file') === 'one' ? 'one' : 'mtl';
        var type = $(this).attr('data-type') || 'jpg,png', field = $(this).attr('data-field') || 'file';
        var title = $(this).attr('data-title') || '文件上传', uptype = $(this).attr('data-uptype') || '';
        var url = window.ROOT_URL + '/index.php/admin/plugs/upfile/mode/' + method + '.html?mode=' + method + '&uptype=' + uptype + '&type=' + type + '&field=' + field;
        $.form.iframe(url, title || '文件管理');
    });

    /*! 注册 data-iframe 事件行为 */
    this.$body.on('click', '[data-iframe]', function () {
        $.form.iframe($(this).attr('data-iframe'), $(this).attr('data-title') || '窗口');
    });

    /*! 注册 data-icon 事件行为 */
    this.$body.on('click', '[data-icon]', function () {
        var field = $(this).attr('data-icon') || $(this).attr('data-field') || 'icon';
        var url = window.ROOT_URL + '/index.php/admin/plugs/icon.html?field=' + field;
        $.form.iframe(url, '图标选择');
    });

    /*! 注册 data-tips-image 事件行为 */
    this.$body.on('click', '[data-tips-image]', function () {
        var img = new Image(), src = this.getAttribute('data-tips-image') || this.src;
        var imgWidth = this.getAttribute('data-width') || '480px';
        img.onload = function () {
            layer.open({type: 1, area: imgWidth, title: false, closeBtn: 1, skin: 'layui-layer-nobg', shadeClose: true, content: $(img).appendTo('body').css({background: '#fff', width: imgWidth, height: 'auto'}), end: function () {
                    $(img).remove();
                }
            });
        };
        img.src = src;
    });

    /*! 注册 data-tips-text 事件行为 */
    this.$body.on('mouseenter', '[data-tips-text]', function () {
        var text = $(this).attr('data-tips-text'), placement = $(this).attr('data-tips-placement') || 'auto';
        $(this).tooltip({title: text, placement: placement}).tooltip('show');
    });

    /*! 注册 data-phone-view 事件行为 */
    this.$body.on('click', '[data-phone-view]', function () {
        var $container = $('

公众号

').appendTo('body');
        $container.find('iframe').attr('src', this.getAttribute('data-phone-view') || this.href);
        layer.style(layer.open({type: 1, scrollbar: !1, area: ['335px', '600px'], title: !1, closeBtn: 1, skin: 'layui-layer-nobg', shadeClose: !1, content: $container, end: function () {
                $container.remove();
            }
        }), {boxShadow: 'none'});
    });

    /*! 后台菜单控制初始化 */
    $.menu.listen();
    /*! 表单监听初始化 */
    $.validate.listen(this);

});<commit_msg>[更新]修正body的on事件<commit_after>
// +----------------------------------------------------------------------
// | Think.Admin
// +----------------------------------------------------------------------
// | 版权所有 2014~2017 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// +----------------------------------------------------------------------
// | 官方网站: http://think.ctolog.com
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// +----------------------------------------------------------------------
// | github开源项目：https://github.com/zoujingli/Think.Admin
// +----------------------------------------------------------------------

define(['jquery', 'admin.plugs'], function () {

    /*! 定义当前body对象 */
    this.$body = $('.framework-body').add('.framework-topbar').add('body');

    /*! 注册 data-load 事件行为 */
    this.$body.on('click', '[data-load]', function () {
        var url = $(this).attr('data-load'), tips = $(this).attr('data-tips');
        if ($(this).attr('data-confirm')) {
            return $.msg.confirm($(this).attr('data-confirm'), _goLoad);
        }
        return _goLoad.call(this);
        function _goLoad() {
            $.form.load(url, {}, 'get', null, true, tips);
        }
    });

    /*! 注册 data-serach 表单搜索行为 */
    this.$body.on('submit', 'form.form-search', function () {
        var url = $(this).attr('action'), split = url.indexOf('?') === -1 ? '?' : '&';
        if ((this.method || 'get').toLowerCase() === 'get') {
            return window.location.href = '#' + $.menu.parseUri(url + split + $(this).serialize());
        }
        $.form.load(url, this, 'post');
    });

    /*! 注册 data-modal 事件行为 */
    this.$body.on('click', '[data-modal]', function () {
        return $.form.modal($(this).attr('data-modal'), 'open_type=modal', $(this).attr('data-title') || '编辑');
    });

    /*! 注册 data-open 事件行为 */
    this.$body.on('click', '[data-open]', function () {
        $.form.href($(this).attr('data-open'), this);
    });

    /*! 注册 data-reload 事件行为 */
    this.$body.on('click', '[data-reload]', function () {
        $.form.reload();
    });

    /*! 注册 data-check 事件行为 */
    this.$body.on('click', '[data-check-target]', function () {
        var checked = !!this.checked;
        $($(this).attr('data-check-target')).map(function () {
            this.checked = checked;
        });
    });

    /*! 注册 data-update 事件行为 */
    this.$body.on('click', '[data-update]', function () {
        var id = $(this).attr('data-update') || (function () {
            var data = [];
            return $($(this).attr('data-list-target') || 'input.list-check-box').map(function () {
                (this.checked) && data.push(this.value);
            }), data.join(',');
        }).call(this);
        if (id.length < 1) {
            return $.msg.tips('请选择需要操作的数据！');
        }
        var action = $(this).attr('data-action') || $(this).parents('[data-location]').attr('data-location');
        var value = $(this).attr('data-value') || 0, field = $(this).attr('data-field') || 'status';
        $.msg.confirm('确定要操作这些数据吗？', function () {
            $.form.load(action, {field: field, value: value, id: id}, 'POST');
        });
    });

    /*! 注册 data-href 事件行为 */
    this.$body.on('click', '[data-href]', function () {
        var href = $(this).attr('data-href');
        (href && href.indexOf('#') !== 0) && (window.location.href = href);
    });

    /*! 注册 data-page-href 事件行为 */
    this.$body.on('click', 'a[data-page-href]', function () {
        window.location.href = '#' + $.menu.parseUri(this.href, this);
    });

    /*! 注册 data-file 事件行为 */
    this.$body.on('click', '[data-file]', function () {
        var method = $(this).attr('data-file') === 'one' ? 'one' : 'mtl';
        var type = $(this).attr('data-type') || 'jpg,png', field = $(this).attr('data-field') || 'file';
        var title = $(this).attr('data-title') || '文件上传', uptype = $(this).attr('data-uptype') || '';
        var url = window.ROOT_URL + '/index.php/admin/plugs/upfile/mode/' + method + '.html?mode=' + method + '&uptype=' + uptype + '&type=' + type + '&field=' + field;
        $.form.iframe(url, title || '文件管理');
    });

    /*! 注册 data-iframe 事件行为 */
    this.$body.on('click', '[data-iframe]', function () {
        $.form.iframe($(this).attr('data-iframe'), $(this).attr('data-title') || '窗口');
    });

    /*! 注册 data-icon 事件行为 */
    this.$body.on('click', '[data-icon]', function () {
        var field = $(this).attr('data-icon') || $(this).attr('data-field') || 'icon';
        var url = window.ROOT_URL + '/index.php/admin/plugs/icon.html?field=' + field;
        $.form.iframe(url, '图标选择');
    });

    /*! 注册 data-tips-image 事件行为 */
    this.$body.on('click', '[data-tips-image]', function () {
        var img = new Image(), src = this.getAttribute('data-tips-image') || this.src;
        var imgWidth = this.getAttribute('data-width') || '480px';
        img.onload = function () {
            layer.open({type: 1, area: imgWidth, title: false, closeBtn: 1, skin: 'layui-layer-nobg', shadeClose: true, content: $(img).appendTo('body').css({background: '#fff', width: imgWidth, height: 'auto'}), end: function () {
                    $(img).remove();
                }
            });
        };
        img.src = src;
    });

    /*! 注册 data-tips-text 事件行为 */
    this.$body.on('mouseenter', '[data-tips-text]', function () {
        var text = $(this).attr('data-tips-text'), placement = $(this).attr('data-tips-placement') || 'auto';
        $(this).tooltip({title: text, placement: placement}).tooltip('show');
    });

    /*! 注册 data-phone-view 事件行为 */
    this.$body.on('click', '[data-phone-view]', function () {
        var $container = $('

公众号

').appendTo('body');
        $container.find('iframe').attr('src', this.getAttribute('data-phone-view') || this.href);
        layer.style(layer.open({type: 1, scrollbar: !1, area: ['335px', '600px'], title: !1, closeBtn: 1, skin: 'layui-layer-nobg', shadeClose: !1, content: $container, end: function () {
                $container.remove();
            }
        }), {boxShadow: 'none'});
    });

    /*! 后台菜单控制初始化 */
    $.menu.listen();
    /*! 表单监听初始化 */
    $.validate.listen(this);

});<|endoftext|>


<commit_before>var tick = function() {};

(function() {
	var editor = ace.edit("editor");
	editor.setTheme("ace/theme/monokai");
	editor.getSession().setMode("ace/mode/javascript");
	editor.setValue([
		'tick = function (submarine) {',
		'\t/*',
		'\tWrite your code in here',
		'\t',
		'\tFunctions available:',
		'\t\tsubmarine.moveForward()',
		'\t\tsubmarine.moveBackwards()',
		'\t\tsubmarine.rotate(degrees)',
		'\t\t\tMax rotation: +-5',
		'\t\tsubmarine.getInfo()',
		'\t\t\tReturns: { x, y, rotation, energy, minX, minY,',
		'\t\t\t\tmaxX, maxY, maxEnergy, collision }',
		'\t\tsubmarine.sonar()',
		'\t\t\tReturns: { distanceToEnemy }',
		'\t*/',
		'\t',
		'\t',
		'};'
	].join('\n'));
	editor.gotoLine(16);

	var windowHeight;

	var $statusBar;
	var socket;
	var isPlaying = false;
	var updater;

	var submarine, enemySubmarine;

	$(document).ready(function() {
		windowHeight = $(window).height();
		$statusBar = $('#statusText');
		resize();

		bindEvents();
		initSocketIO();
	});

	function resize() {
		$('#editor-container').height(windowHeight - 70);
		$('#canvas-container').height(windowHeight - 70);
	}

	function bindEvents() {
		$('#playBtn').click(play);
	}

	function setStatus(status) {
		$statusBar.text(status);
	}

	function initSocketIO() {
		socket = io('//localhost' + location.port ? ':' + location.port : '');
		socket.on('start', startGame);
		socket.on('update', updateEnemy);
		socket.on('disconnect', disconnected);
		socket.on('enemyleft', enemyLeft);
	}

	function enemyLeft() {
		isPlaying = false;
		setStatus('The enemy has left the game!');
	}

	function disconnected() {
		isPlaying = false;
		setStatus('You have been disconnected from the server!');
	}

	function updateEnemy(data) {
		enemySubmarine.update(data);
	}

	function startGame(data) {
		setStatus('Game started!');

		isPlaying = true;

		enemySubmarine = submarineFactory(data.player === 1 ? 2 : 1, true);
		submarine = submarineFactory(data.player, false, enemySubmarine);

		if(updater) clearInterval(updater);

		updater = setInterval(function() {
			if(!isPlaying) return;

			tick(submarine);
			update();
			paint();
		}, 50);
	}

	function play() {
		var code = editor.getValue();

		try {
			eval(code);
		}
		catch(e) {
			return setStatus('There is an error on your code. please fix');
		}

		if(!isPlaying) {
			socket.emit('queue');
			setStatus('In queue...');
		}
	}

	var canvas = document.getElementById("canvas");
	var ctx = canvas.getContext("2d");
	var subImgs = [
		document.getElementById("subImg"),
		document.getElementById("subImg2")
	];

	var lastStatus;

	function update() {
		var currentStatus = submarine.getInfo();

		if(!_.isEqual(currentStatus, lastStatus)) {
			socket.emit('update', {
				x: currentStatus.x,
				y: currentStatus.y,
				rotation: currentStatus.rotation,
				energy: currentStatus.energy
			});
		}

		lastStatus = currentStatus;
	}

	function paint() {
		ctx.beginPath();
		ctx.rect(0, 0, 600, 400);
		ctx.fillStyle = '#103669';
		ctx.fill();

		var info = submarine.getInfo();
		paintEnergyBar(info, 20, 20, 100, 20);

		ctx.beginPath();
		ctx.moveTo(info.x + 18, info.y - 14);
		ctx.lineTo(info.x + 32, info.y - 14);
		ctx.lineTo(info.x + 25, info.y - 3);
		ctx.closePath();
		ctx.fillStyle = "#500";
		ctx.fill();

		paintSubmarine(info);

		info = enemySubmarine.getInfo();
		paintSubmarine(info);
		paintEnergyBar(info, info.x + 5, info.y - 14, 40, 5);
	}

	function paintEnergyBar(info, x, y, w, h) {
		ctx.beginPath();
		ctx.rect(x, y, w, h);
		ctx.fillStyle = '#500';
		ctx.fill();
		ctx.beginPath();
		ctx.rect(x, y, info.energy * w / info.maxEnergy, h);
		ctx.fillStyle = '#050';
		ctx.fill();
	}

	function paintSubmarine(info) {
		ctx.translate(info.x + 25, info.y + 25);
		ctx.rotate(Math.PI / 180 * info.rotation);
		ctx.drawImage(subImgs[info.player === 1 ? 0 : 1] , -25, -25);
		ctx.setTransform(1, 0, 0, 1, 0, 0);
	}
})();
<commit_msg>updated instructions<commit_after>var tick = function() {};

(function() {
	var editor = ace.edit("editor");
	editor.setTheme("ace/theme/monokai");
	editor.getSession().setMode("ace/mode/javascript");
	editor.setValue([
		'tick = function (submarine) {',
		'\t/*',
		'\tWrite your code in here',
		'\t',
		'\tFunctions available:',
		'\t\tsubmarine.moveForward()',
		'\t\tsubmarine.moveBackwards()',
		'\t\tsubmarine.rotate(degrees)',
		'\t\t\tMax rotation: +-5',
		'\t\tsubmarine.getInfo()',
		'\t\t\tReturns: { x, y, rotation, energy, minX, minY,',
		'\t\t\t\tmaxX, maxY, maxEnergy, collision }',
		'\t\tsubmarine.sonar()',
		'\t\t\tReturns: { distanceToEnemy, incline }',
		'\t*/',
		'\t',
		'\t',
		'};'
	].join('\n'));
	editor.gotoLine(16);

	var windowHeight;

	var $statusBar;
	var socket;
	var isPlaying = false;
	var updater;

	var submarine, enemySubmarine;

	$(document).ready(function() {
		windowHeight = $(window).height();
		$statusBar = $('#statusText');
		resize();

		bindEvents();
		initSocketIO();
	});

	function resize() {
		$('#editor-container').height(windowHeight - 70);
		$('#canvas-container').height(windowHeight - 70);
	}

	function bindEvents() {
		$('#playBtn').click(play);
	}

	function setStatus(status) {
		$statusBar.text(status);
	}

	function initSocketIO() {
		socket = io('//localhost' + location.port ? ':' + location.port : '');
		socket.on('start', startGame);
		socket.on('update', updateEnemy);
		socket.on('disconnect', disconnected);
		socket.on('enemyleft', enemyLeft);
	}

	function enemyLeft() {
		isPlaying = false;
		setStatus('The enemy has left the game!');
	}

	function disconnected() {
		isPlaying = false;
		setStatus('You have been disconnected from the server!');
	}

	function updateEnemy(data) {
		enemySubmarine.update(data);
	}

	function startGame(data) {
		setStatus('Game started!');

		isPlaying = true;

		enemySubmarine = submarineFactory(data.player === 1 ? 2 : 1, true);
		submarine = submarineFactory(data.player, false, enemySubmarine);

		if(updater) clearInterval(updater);

		updater = setInterval(function() {
			if(!isPlaying) return;

			tick(submarine);
			update();
			paint();
		}, 50);
	}

	function play() {
		var code = editor.getValue();

		try {
			eval(code);
		}
		catch(e) {
			return setStatus('There is an error on your code. please fix');
		}

		if(!isPlaying) {
			socket.emit('queue');
			setStatus('In queue...');
		}
	}

	var canvas = document.getElementById("canvas");
	var ctx = canvas.getContext("2d");
	var subImgs = [
		document.getElementById("subImg"),
		document.getElementById("subImg2")
	];

	var lastStatus;

	function update() {
		var currentStatus = submarine.getInfo();

		if(!_.isEqual(currentStatus, lastStatus)) {
			socket.emit('update', {
				x: currentStatus.x,
				y: currentStatus.y,
				rotation: currentStatus.rotation,
				energy: currentStatus.energy
			});
		}

		lastStatus = currentStatus;
	}

	function paint() {
		ctx.beginPath();
		ctx.rect(0, 0, 600, 400);
		ctx.fillStyle = '#103669';
		ctx.fill();

		var info = submarine.getInfo();
		paintEnergyBar(info, 20, 20, 100, 20);

		ctx.beginPath();
		ctx.moveTo(info.x + 18, info.y - 14);
		ctx.lineTo(info.x + 32, info.y - 14);
		ctx.lineTo(info.x + 25, info.y - 3);
		ctx.closePath();
		ctx.fillStyle = "#500";
		ctx.fill();

		paintSubmarine(info);

		info = enemySubmarine.getInfo();
		paintSubmarine(info);
		paintEnergyBar(info, info.x + 5, info.y - 14, 40, 5);
	}

	function paintEnergyBar(info, x, y, w, h) {
		ctx.beginPath();
		ctx.rect(x, y, w, h);
		ctx.fillStyle = '#500';
		ctx.fill();
		ctx.beginPath();
		ctx.rect(x, y, info.energy * w / info.maxEnergy, h);
		ctx.fillStyle = '#050';
		ctx.fill();
	}

	function paintSubmarine(info) {
		ctx.translate(info.x + 25, info.y + 25);
		ctx.rotate(Math.PI / 180 * info.rotation);
		ctx.drawImage(subImgs[info.player === 1 ? 0 : 1] , -25, -25);
		ctx.setTransform(1, 0, 0, 1, 0, 0);
	}
})();
<|endoftext|>


<commit_before>var mosaic;

// Loads course data and user notes on page load
$(document).ready(
  function load() {
    loadUserNotes();
    loadCourses();

    $("#newNoteModal").on("show", function() {
      // get classes to put in the course select list
      $.post("/getCourses", null,
        loadCoursesIntoModal,
        "json"
      );
    });
    // onchange event for select list
    $("#newNoteCourse").change(function(e){
      // use the courses array and the new selected index to get the id
      var courseId = courses[this.selectedIndex].data.id;
      // get lectures for course to put in the lecture select list
      $.post("/getLecturesByCourseId", {
          courseId: courseId
        },
        loadLecturesIntoModal,
        "json"
      );
    });
    mosaic = $('.content').jstile();
  }
);

function loadLecturesIntoModal(data){
  $("#newNoteLecture").empty();
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      // Add the lecture to the modal
      $("#newNoteLecture").append($("<option></option>")
        .attr("value", i)
        .attr("id", data[i].id)
        .text(data[i].title));
    }
  }
}

function loadCoursesIntoModal(data){
  courses = loadCoursesCallback(data);
  $("#newNoteCourse").empty();
  for (i in courses) {
    if (courses.hasOwnProperty(i)) {
      $("#newNoteCourse").append($("<option></option>")
        .attr("value", i)
        .attr("id", courses[i].data.id)
        .text(courses[i].data.title));
      // load the first classes lectures into the lecture modal!
      if(i == 0){
        $.post("/getLecturesByCourseId", {
            courseId: courses[i].data.id
          },
          loadLecturesIntoModal,
          "json"
        );
      }
    }
  }
}

/**
 * Add event handler for when the new note button is clicked
 */
function createNoteClicked() {
    // Create Etherpad frame with name from newNoteName Input box
//    $('.content').html("");
    loadNoteIntoUserSpace($('#newNoteName').val());
    $('#newNoteModal').modal('hide');
    var id = 1;
    // Get lectureId by Lecture name

    // Pass lecture ID and Title to the the server to be placed in database
    $.post("/addNote",{lectureId: id, title:$("#newNoteName").val()}, createNoteClickedCallback());
}

function createNoteClickedCallback(data) {
    loadUserNotes();
    loadCourses();
    return;
}

/**
 * Function for placing /newPad/ frame
 */
function newNote() {
    $('.content').html("");
}

/**
 * The course returned by /getCourses.  If we can
 * find a way to get the courses from jstree, we won't need
 * this global variable.
 */
var courses;
var notes;

/**
 * Creates a jstree with id="#notes-tree".  Once loaded, lectures and notes are added to the tree.
 */
function loadCourses() {
  $("#notes-tree")
    .bind("loaded.jstree", function (event, data) {
      for (i in courses) {
        if (courses.hasOwnProperty(i)) {
          loadLecturesByCourseId(courses[i].data[0].id);
        }
      }

    })
    .jstree({
      plugins:[
        "themes", "json_data", "crrm", "dnd", "types", "ui", "sort"
      ],
      "json_data":{
        "ajax":{
          "type":"POST",
          "url":"/getCourses",
          "success":loadCoursesCallback
        }
      },
      "types": {
        // only course nodes as root nodes
        "valid_children" : ["course"],
          "types" : {
            "note" : {
              // This type should have no children
              "valid_children" : "none",
              "icon" : {
                "image" : "/static/img/fileicons.gif"
              },
              "select_node" : function(node){
                // put node in data so it works with the function
                data = {rslt: {obj: node}};
                onNewNoteButtonClick("select_node",data)
              }
            },
            "lecture" : {
              // can only have notes as children
              "valid_children" : ["note"],
              "move_node" : false,
              "start_drag" : false,
              "select_node" : toggleNode
            },
            "course" : {
              // can have lectures and notes as children
              "valid_children" : ["lecture", "note"],
              "move_node" : false,
              "start_drag" : false,
              "select_node" : toggleNode
            }
        }
      }
    });
}

// Toggles whether the node is open/closed
function toggleNode(node){
  $("#notes-tree").jstree("toggle_node",node);
}

/**
 * Modifies the data returned from the Facade so jstree can recognize each element as a node;
 * @param data
 */
function loadCoursesCallback(data) {
  courses = [];
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      courses[i] = {data:data[i], attr:{id:"course" + data[i].id, rel: "course"}};
      courses[i].data.title = courses[i].data.name;
    }
  }
  return courses;
}

/**
 * calls /getLecturesByCourseId
 * @param id
 */
function loadLecturesByCourseId(id) {
  $.post("/getLecturesByCourseId", {
      courseId:id
    },
    loadLecturesByCourseIdCallback,
    "json"
  );
}

function loadLecturesByCourseIdCallback(data) {
  var lecture;
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      lecture = {data:data[i], attr:{id:"lecture" + data[i].id, rel: "lecture"}};
      // Add the lecture to the ourse
      $("#notes-tree").jstree("create_node", $("#course" + data[i].courseId), "inside", lecture);
      $.post("/getNotesByLectureId", {
          lectureId:data[i].id
        },
        loadNotesByLectureIdCallback,
        "json"
      );
    }
  }
}

function loadNotesByLectureIdCallback(data) {
  var note;
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      note = {data:data[i], attr:{id:"note" + data[i].id, rel: "note"}};
      $("#notes-tree").jstree("create_node", $("#lecture" + data[i].lectureId), "inside", note);
    }
  }
}

/**
 * Triggers the callback to load the user note data.
 * Generates a tree at the user-notes div
 */
function loadUserNotes() {

  $("#user-notes")
    .jstree({
      "json_data":{
        "ajax":{
          "type":"POST",
          "url":"/getNotes",
          "success":loadUserNotesCallback
        }
      },
      "plugins":[
        "themes", "json_data", "crrm", "ui", "sort"
      ]
    })
    .bind("select_node.jstree", onNewNoteButtonClick)
    // 2) if not using the UI plugin - the Anchor tags work as expected
    //    so if the anchor has a HREF attirbute - the page will be changed
    //    you can actually prevent the default, etc (normal jquery usage)
    .delegate("a", "click", function (event, data) {
      event.preventDefault();
    })

}

/**
 * The callback triggered when we've loaded user note data from the server.
 */
function loadUserNotesCallback(data) {
  notes = [];
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      notes[i] = {data:data[i], attr:{id:"note" + data[i].id, title: data[i].title, href:data[i].location}};
    }
  }
  return notes;
}

/**
 * Executed when a user clicks on a note from the sidebar, triggers loading a note.
 */
function onNewNoteButtonClick(event, data) {
  var title = data.rslt.obj.attr("title");
  loadNoteIntoUserSpace(title);
}

/**
 * Loads the note based on the title onto the page
 * @param title
 */
function loadNoteIntoUserSpace(title){
  $('.content').find('.hero-unit').remove();
  // remove the "note" part of the li id.
  //id = id.replace("note", "");
  var newElement = $("

" + title + "

");
  mosaic.add(newElement);
  newElement.ready(function() {
    $('iframe').css('overflow-y', 'scroll')}
  );
}<commit_msg>Added some proper spacing & docs to home.js function<commit_after>var mosaic;

// Loads course data and user notes on page load
$(document).ready(
  function load() {
    loadUserNotes();
    loadCourses();

    $("#newNoteModal").on("show", function() {
      // get classes to put in the course select list
      $.post("/getCourses", null,
        loadCoursesIntoModal,
        "json"
      );
    });
    // onchange event for select list
    $("#newNoteCourse").change(function(e){
      // use the courses array and the new selected index to get the id
      var courseId = courses[this.selectedIndex].data.id;
      // get lectures for course to put in the lecture select list
      $.post("/getLecturesByCourseId", {
          courseId: courseId
        },
        loadLecturesIntoModal,
        "json"
      );
    });
    mosaic = $('.content').jstile();
  }
);

function loadLecturesIntoModal(data){
  $("#newNoteLecture").empty();
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      // Add the lecture to the modal
      $("#newNoteLecture").append($("<option></option>")
        .attr("value", i)
        .attr("id", data[i].id)
        .text(data[i].title));
    }
  }
}

function loadCoursesIntoModal(data){
  courses = loadCoursesCallback(data);
  $("#newNoteCourse").empty();
  for (i in courses) {
    if (courses.hasOwnProperty(i)) {
      $("#newNoteCourse").append($("<option></option>")
        .attr("value", i)
        .attr("id", courses[i].data.id)
        .text(courses[i].data.title));
      // load the first classes lectures into the lecture modal!
      if(i == 0){
        $.post("/getLecturesByCourseId", {
            courseId: courses[i].data.id
          },
          loadLecturesIntoModal,
          "json"
        );
      }
    }
  }
}

/**
 * Add event handler for when the new note button is clicked
 */
function createNoteClicked() {
    // Create Etherpad frame with name from newNoteName Input box
//    $('.content').html("");
    loadNoteIntoUserSpace($('#newNoteName').val());
    $('#newNoteModal').modal('hide');
    var id = 1;
    // Get lectureId by Lecture name

    // Pass lecture ID and Title to the the server to be placed in database
    $.post("/addNote",{lectureId: id, title:$("#newNoteName").val()}, createNoteClickedCallback());
}

function createNoteClickedCallback(data) {
    loadUserNotes();
    loadCourses();
    return;
}

/**
 * Function for placing /newPad/ frame
 */
function newNote() {
    $('.content').html("");
}

/**
 * The course returned by /getCourses.  If we can
 * find a way to get the courses from jstree, we won't need
 * this global variable.
 */
var courses;
var notes;

/**
 * Creates a jstree with id="#notes-tree".  Once loaded, lectures and notes are added to the tree.
 */
function loadCourses() {
  $("#notes-tree")
    .bind("loaded.jstree", function (event, data) {
      for (i in courses) {
        if (courses.hasOwnProperty(i)) {
          loadLecturesByCourseId(courses[i].data[0].id);
        }
      }

    })
    .jstree({
      plugins:[
        "themes", "json_data", "crrm", "dnd", "types", "ui", "sort"
      ],
      "json_data":{
        "ajax":{
          "type":"POST",
          "url":"/getCourses",
          "success":loadCoursesCallback
        }
      },
      "types": {
        // only course nodes as root nodes
        "valid_children" : ["course"],
          "types" : {
            "note" : {
              // This type should have no children
              "valid_children" : "none",
              "icon" : {
                "image" : "/static/img/fileicons.gif"
              },
              "select_node" : function(node){
                // put node in data so it works with the function
                data = {rslt: {obj: node}};
                onNewNoteButtonClick("select_node",data)
              }
            },
            "lecture" : {
              // can only have notes as children
              "valid_children" : ["note"],
              "move_node" : false,
              "start_drag" : false,
              "select_node" : toggleNode
            },
            "course" : {
              // can have lectures and notes as children
              "valid_children" : ["lecture", "note"],
              "move_node" : false,
              "start_drag" : false,
              "select_node" : toggleNode
            }
        }
      }
    });
}

// Toggles whether the node is open/closed
function toggleNode(node){
  $("#notes-tree").jstree("toggle_node",node);
}

/**
 * Modifies the data returned from the Facade so jstree can recognize each element as a node;
 * @param data
 */
function loadCoursesCallback(data) {
  courses = [];
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      courses[i] = {data:data[i], attr:{id:"course" + data[i].id, rel: "course"}};
      courses[i].data.title = courses[i].data.name;
    }
  }
  return courses;
}

/**
 * calls /getLecturesByCourseId
 * @param id
 */
function loadLecturesByCourseId(id) {
  $.post("/getLecturesByCourseId", {
      courseId:id
    },
    loadLecturesByCourseIdCallback,
    "json"
  );
}

function loadLecturesByCourseIdCallback(data) {
  var lecture;
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      lecture = {data:data[i], attr:{id:"lecture" + data[i].id, rel: "lecture"}};
      // Add the lecture to the ourse
      $("#notes-tree").jstree("create_node", $("#course" + data[i].courseId), "inside", lecture);
      $.post("/getNotesByLectureId", {
          lectureId:data[i].id
        },
        loadNotesByLectureIdCallback,
        "json"
      );
    }
  }
}

function loadNotesByLectureIdCallback(data) {
  var note;
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      note = {data:data[i], attr:{id:"note" + data[i].id, rel: "note"}};
      $("#notes-tree").jstree("create_node", $("#lecture" + data[i].lectureId), "inside", note);
    }
  }
}

/**
 * Triggers the callback to load the user note data.
 * Generates a tree at the user-notes div
 */
function loadUserNotes() {

  $("#user-notes")
    .jstree({
      "json_data":{
        "ajax":{
          "type":"POST",
          "url":"/getNotes",
          "success":loadUserNotesCallback
        }
      },
      "plugins":[
        "themes", "json_data", "crrm", "ui", "sort"
      ]
    })
    .bind("select_node.jstree", onNewNoteButtonClick)
    // 2) if not using the UI plugin - the Anchor tags work as expected
    //    so if the anchor has a HREF attirbute - the page will be changed
    //    you can actually prevent the default, etc (normal jquery usage)
    .delegate("a", "click", function (event, data) {
      event.preventDefault();
    })

}

/**
 * The callback triggered when we've loaded user note data from the server.
 */
function loadUserNotesCallback(data) {
  notes = [];
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      notes[i] = {data:data[i], attr:{id:"note" + data[i].id, title: data[i].title, href:data[i].location}};
    }
  }
  return notes;
}

/**
 * Executed when a user clicks on a note from the sidebar, triggers loading a note.
 */
function onNewNoteButtonClick(event, data) {
  var title = data.rslt.obj.attr("title");
  loadNoteIntoUserSpace(title);
}

/**
 * Loads the note based on the title onto the page
 * @param title The title of the new note
 */
function loadNoteIntoUserSpace(title){

  $('.content').find('.hero-unit').remove();

  var newElement = $("

" + title + "

");
  mosaic.add(newElement);

  newElement.ready(function() {
    $('iframe').css('overflow-y', 'scroll')}
  );
}<|endoftext|>


<commit_before>// Loads course data and user notes on page load
$(document).ready(
  function load() {
    loadUserNotes();
    loadCourses();

    $("#newNoteModal").on("show", function() {
      // get classes to put in the course select list
      $.post("/getCourses", null,
        loadCoursesIntoModal,
        "json"
      );
    });
    // onchange event for select list
    $("#newNoteCourse").change(function(e){
      // use the courses array and the new selected index to get the id
      var courseId = courses[this.selectedIndex].data.id;
      // get lectures for course to put in the lecture select list
      $.post("/getLecturesByCourseId", {
          courseId: courseId
        },
        loadLecturesIntoModal,
        "json"
      );
    });
  }
);

function loadLecturesIntoModal(data){
  $("#newNoteLecture").empty();
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      // Add the lecture to the modal
      $("#newNoteLecture").append($("<option></option>")
        .attr("value", i)
        .attr("id", data[i].id)
        .text(data[i].title));
    }
  }
}

function loadCoursesIntoModal(data){
  courses = loadCoursesCallback(data);
  $("#newNoteCourse").empty();
  for (i in courses) {
    if (courses.hasOwnProperty(i)) {
      $("#newNoteCourse").append($("<option></option>")
        .attr("value", i)
        .attr("id", courses[i].data.id)
        .text(courses[i].data.title));
      // load the first classes lectures into the lecture modal!
      if(i == 0){
        $.post("/getLecturesByCourseId", {
            courseId: courses[i].data.id
          },
          loadLecturesIntoModal,
          "json"
        );
      }
    }
  }
}

/**
 * Add event handler for when the new note button is clicked
 */
function createNoteClicked() {
    $('.content').html("");

}

/**
 * Function for placing /newPad/ frame
 */
function newNote() {
    $('.content').html("");
}

/**
 * The course returned by /getCourses.  If we can
 * find a way to get the courses from jstree, we won't need
 * this global variable.
 */
var courses;
var notes;

/**
 * Creates a jstree with id="#notes-tree".  Once loaded, lectures and notes are added to the tree.
 */
function loadCourses() {
  $("#notes-tree")
    .bind("loaded.jstree", function (event, data) {
      for (i in courses) {
        if (courses.hasOwnProperty(i)) {
          loadLecturesByCourseId(courses[i].data[0].id);
        }
      }

    })
    .jstree({
      plugins:[
        "themes", "json_data", "crrm", "dnd", "types", "ui", "sort"
      ],
      "json_data":{
        "ajax":{
          "type":"POST",
          "url":"/getCourses",
          "success":loadCoursesCallback
        }
      },
      "types": {
        // only course nodes as root nodes
        "valid_children" : ["course"],
          "types" : {
            "note" : {
              // This type should have no children
              "valid_children" : "none",
              "icon" : {
                "image" : "/static/img/fileicons.gif"
              },
              "select_node" : function(node){
                // put node in data so it works with the function
                data = {rslt: {obj: node}};
                loadNoteIntoUserSpace("select_node",data)
              }
            },
            "lecture" : {
              // can only have notes as children
              "valid_children" : ["note"],
              "move_node" : false,
              "start_drag" : false,
              "select_node" : toggleNode
            },
            "course" : {
              // can have lectures and notes as children
              "valid_children" : ["lecture", "note"],
              "move_node" : false,
              "start_drag" : false,
              "select_node" : toggleNode
            }
        }
      }
    });
}

// Toggles whether the node is open/closed
function toggleNode(node){
  $("#notes-tree").jstree("toggle_node",node);
}

/**
 * Modifies the data returned from the Facade so jstree can recognize each element as a node;
 * @param data
 */
function loadCoursesCallback(data) {
  courses = [];
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      courses[i] = {data:data[i], attr:{id:"course" + data[i].id, rel: "course"}};
      courses[i].data.title = courses[i].data.name;
    }
  }
  return courses;
}

/**
 * calls /getLecturesByCourseId
 * @param id
 */
function loadLecturesByCourseId(id) {
  $.post("/getLecturesByCourseId", {
      courseId:id
    },
    loadLecturesByCourseIdCallback,
    "json"
  );
}

function loadLecturesByCourseIdCallback(data) {
  var lecture;
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      lecture = {data:data[i], attr:{id:"lecture" + data[i].id, rel: "lecture"}};
      // Add the lecture to the ourse
      $("#notes-tree").jstree("create_node", $("#course" + data[i].courseId), "inside", lecture);
      $.post("/getNotesByLectureId", {
          lectureId:data[i].id
        },
        loadNotesByLectureIdCallback,
        "json"
      );
    }
  }
}

function loadNotesByLectureIdCallback(data) {
  var note;
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      note = {data:data[i], attr:{id:"note" + data[i].id, rel: "note"}};
      $("#notes-tree").jstree("create_node", $("#lecture" + data[i].lectureId), "inside", note);
    }
  }
}

/**
 * Triggers the callback to load the user note data.
 * Generates a tree at the user-notes div
 */
function loadUserNotes() {

  $("#user-notes")
    .jstree({
      "json_data":{
        "ajax":{
          "type":"POST",
          "url":"/getNotesByUserId",
          "success":loadUserNotesCallback
        }
      },
      "plugins":[
        "themes", "json_data", "crrm", "ui", "sort"
      ]
    })
    .bind("select_node.jstree", loadNoteIntoUserSpace)
    // 2) if not using the UI plugin - the Anchor tags work as expected
    //    so if the anchor has a HREF attirbute - the page will be changed
    //    you can actually prevent the default, etc (normal jquery usage)
    .delegate("a", "click", function (event, data) {
      event.preventDefault();
    })

}

/**
 * The callback triggered when we've loaded user note data from the server.
 */
function loadUserNotesCallback(data) {
  notes = [];
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      notes[i] = {data:data[i], attr:{id:"note" + data[i].id, href:data[i].location}};
    }
  }
  return notes;
}

/**
 * Executed when a user clicks on a note from the sidebar, triggers loading a note.
 */
function loadNoteIntoUserSpace(event, data) {
  var id = data.rslt.obj.attr("id");
  // remove the "note" part of the li id.
  id = id.replace("note", "");
//  $('.content').html("");
  $('.content').html("");
}<commit_msg>Dialog box closes after pressing create note and the pad fills the content<commit_after>// Loads course data and user notes on page load
$(document).ready(
  function load() {
    loadUserNotes();
    loadCourses();

    $("#newNoteModal").on("show", function() {
      // get classes to put in the course select list
      $.post("/getCourses", null,
        loadCoursesIntoModal,
        "json"
      );
    });
    // onchange event for select list
    $("#newNoteCourse").change(function(e){
      // use the courses array and the new selected index to get the id
      var courseId = courses[this.selectedIndex].data.id;
      // get lectures for course to put in the lecture select list
      $.post("/getLecturesByCourseId", {
          courseId: courseId
        },
        loadLecturesIntoModal,
        "json"
      );
    });
  }
);

function loadLecturesIntoModal(data){
  $("#newNoteLecture").empty();
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      // Add the lecture to the modal
      $("#newNoteLecture").append($("<option></option>")
        .attr("value", i)
        .attr("id", data[i].id)
        .text(data[i].title));
    }
  }
}

function loadCoursesIntoModal(data){
  courses = loadCoursesCallback(data);
  $("#newNoteCourse").empty();
  for (i in courses) {
    if (courses.hasOwnProperty(i)) {
      $("#newNoteCourse").append($("<option></option>")
        .attr("value", i)
        .attr("id", courses[i].data.id)
        .text(courses[i].data.title));
      // load the first classes lectures into the lecture modal!
      if(i == 0){
        $.post("/getLecturesByCourseId", {
            courseId: courses[i].data.id
          },
          loadLecturesIntoModal,
          "json"
        );
      }
    }
  }
}

/**
 * Add event handler for when the new note button is clicked
 */
function createNoteClicked() {
    $('.content').html("");
    $('#newNoteModal').modal('hide');
}

/**
 * Function for placing /newPad/ frame
 */
function newNote() {
    $('.content').html("");
}

/**
 * The course returned by /getCourses.  If we can
 * find a way to get the courses from jstree, we won't need
 * this global variable.
 */
var courses;
var notes;

/**
 * Creates a jstree with id="#notes-tree".  Once loaded, lectures and notes are added to the tree.
 */
function loadCourses() {
  $("#notes-tree")
    .bind("loaded.jstree", function (event, data) {
      for (i in courses) {
        if (courses.hasOwnProperty(i)) {
          loadLecturesByCourseId(courses[i].data[0].id);
        }
      }

    })
    .jstree({
      plugins:[
        "themes", "json_data", "crrm", "dnd", "types", "ui", "sort"
      ],
      "json_data":{
        "ajax":{
          "type":"POST",
          "url":"/getCourses",
          "success":loadCoursesCallback
        }
      },
      "types": {
        // only course nodes as root nodes
        "valid_children" : ["course"],
          "types" : {
            "note" : {
              // This type should have no children
              "valid_children" : "none",
              "icon" : {
                "image" : "/static/img/fileicons.gif"
              },
              "select_node" : function(node){
                // put node in data so it works with the function
                data = {rslt: {obj: node}};
                loadNoteIntoUserSpace("select_node",data)
              }
            },
            "lecture" : {
              // can only have notes as children
              "valid_children" : ["note"],
              "move_node" : false,
              "start_drag" : false,
              "select_node" : toggleNode
            },
            "course" : {
              // can have lectures and notes as children
              "valid_children" : ["lecture", "note"],
              "move_node" : false,
              "start_drag" : false,
              "select_node" : toggleNode
            }
        }
      }
    });
}

// Toggles whether the node is open/closed
function toggleNode(node){
  $("#notes-tree").jstree("toggle_node",node);
}

/**
 * Modifies the data returned from the Facade so jstree can recognize each element as a node;
 * @param data
 */
function loadCoursesCallback(data) {
  courses = [];
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      courses[i] = {data:data[i], attr:{id:"course" + data[i].id, rel: "course"}};
      courses[i].data.title = courses[i].data.name;
    }
  }
  return courses;
}

/**
 * calls /getLecturesByCourseId
 * @param id
 */
function loadLecturesByCourseId(id) {
  $.post("/getLecturesByCourseId", {
      courseId:id
    },
    loadLecturesByCourseIdCallback,
    "json"
  );
}

function loadLecturesByCourseIdCallback(data) {
  var lecture;
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      lecture = {data:data[i], attr:{id:"lecture" + data[i].id, rel: "lecture"}};
      // Add the lecture to the ourse
      $("#notes-tree").jstree("create_node", $("#course" + data[i].courseId), "inside", lecture);
      $.post("/getNotesByLectureId", {
          lectureId:data[i].id
        },
        loadNotesByLectureIdCallback,
        "json"
      );
    }
  }
}

function loadNotesByLectureIdCallback(data) {
  var note;
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      note = {data:data[i], attr:{id:"note" + data[i].id, rel: "note"}};
      $("#notes-tree").jstree("create_node", $("#lecture" + data[i].lectureId), "inside", note);
    }
  }
}

/**
 * Triggers the callback to load the user note data.
 * Generates a tree at the user-notes div
 */
function loadUserNotes() {

  $("#user-notes")
    .jstree({
      "json_data":{
        "ajax":{
          "type":"POST",
          "url":"/getNotesByUserId",
          "success":loadUserNotesCallback
        }
      },
      "plugins":[
        "themes", "json_data", "crrm", "ui", "sort"
      ]
    })
    .bind("select_node.jstree", loadNoteIntoUserSpace)
    // 2) if not using the UI plugin - the Anchor tags work as expected
    //    so if the anchor has a HREF attirbute - the page will be changed
    //    you can actually prevent the default, etc (normal jquery usage)
    .delegate("a", "click", function (event, data) {
      event.preventDefault();
    })

}

/**
 * The callback triggered when we've loaded user note data from the server.
 */
function loadUserNotesCallback(data) {
  notes = [];
  for (i in data) {
    if (data.hasOwnProperty(i)) {
      notes[i] = {data:data[i], attr:{id:"note" + data[i].id, href:data[i].location}};
    }
  }
  return notes;
}

/**
 * Executed when a user clicks on a note from the sidebar, triggers loading a note.
 */
function loadNoteIntoUserSpace(event, data) {
  var id = data.rslt.obj.attr("id");
  // remove the "note" part of the li id.
  id = id.replace("note", "");
//  $('.content').html("");
  $('.content').html("");
}<|endoftext|>


<commit_before>function generateRandomSnapshot(minX, maxX, minY, maxY, minZ, maxZ) {
    var activeSparsity = 0.1,
        predictiveSparsity = 0.3;

    var x = generateRandomInt(minX, maxX),
        y = generateRandomInt(minY, maxY),
        z = generateRandomInt(minZ, maxZ),
        total = x * y * z;

    var numActiveCells = generateRandomInt(1, total * activeSparsity),
        numPredictiveCells = generateRandomInt(1, total * predictiveSparsity),
        activeCells = [],
        predictiveCells = [];

    for (var i = 0; i < total; i++) {
        if (i < numActiveCells) activeCells.push(generateRandomInt(0, total));
        if (i < numPredictiveCells) predictiveCells.push(generateRandomInt(0, total));
    }

    return new TestNetworkSnapshot([x, y, z], activeCells, predictiveCells);
}

function generateRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}
<commit_msg>Refactored code<commit_after>function generateRandomSnapshot(minX, maxX, minY, maxY, minZ, maxZ) {
    var activeSparsity = 0.1,
        predictiveSparsity = 0.3;

    var x = generateRandomInt(minX, maxX),
        y = generateRandomInt(minY, maxY),
        z = generateRandomInt(minZ, maxZ),
        total = x * y * z;

    var activeCells = generateRandomArrayOfInts(1, total * activeSparsity, 0, total),
        predictiveCells = generateRandomArrayOfInts(1, total * predictiveSparsity, 0, total);

    return new TestNetworkSnapshot([x, y, z], activeCells, predictiveCells);
}

function generateRandomArrayOfInts(minN, maxN, minVal, maxVal) {
    var num = generateRandomInt(minN, maxN),
        ints = [];

    for (var i = 0; i < num; i++) {
        ints.push(generateRandomInt(minVal, maxVal));
    }

    return ints;
}

function generateRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Eyra Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

File author/s:
    Matthias Petursson <oldschool01123@gmail.com>
*/

(function () {
'use strict';

angular.module('daApp')
.controller('EvaluationController', EvaluationController);

EvaluationController.$inject = ['$document',
                                '$http',
                                '$q',
                                '$rootScope',
                                '$scope',
                                '$timeout',
                                'dataService',
                                'evaluationService',
                                'logger',
                                'utilityService'];

function EvaluationController($document, $http, $q, $rootScope, $scope, $timeout, dataService, evaluationService, logger, utilityService) {
  var evalCtrl = this;
  var evalService = evaluationService;
  var util = utilityService;

  evalCtrl.action = action;
  evalCtrl.skip = skip;
  evalCtrl.undo = undo;

  $scope.msg = ''; // single information msg
  $rootScope.isLoaded = false;

  evalCtrl.actionBtnDisabled = false;
  evalCtrl.skipBtnDisabled = false;
  evalCtrl.undoBtnDisabled = true;

  var actionType = 'play'; // current state

  var PLAYTEXT = 'Play'; // text under the buttons
  var PAUSETEXT = 'Pause';
  var PLAYGLYPH = 'glyphicon-play'; // bootstrap glyph class
  var PAUSEGLYPH = 'glyphicon-pause';
  evalCtrl.actionText = PLAYTEXT;
  evalCtrl.actionGlyph = PLAYGLYPH;

  var EVALUATIONCOMMENTSURL = 'json/evaluation-comments.json';
  evalCtrl.commentOpts = [];

  evalCtrl.currentUser = dataService.get('currentUser');
  evalCtrl.currentSet = dataService.get('currentSet');
  var isSetComplete = false;

  // save reference to the audio element on the page, for play/pause
  // thanks, Shushanth Pallegar, http://stackoverflow.com/a/30899643/5272567
  // TODO: would probably be nicer to make a directive
  var audioPlayback = $document.find("#audio-playback")[0];
  if (audioPlayback) {
    audioPlayback.addEventListener('ended', audioEnded);
  } else {
    $scope.msg = 'Something went wrong with the audio playback.';
  }

  activate();

  //////////

  function activate() {
    evalCtrl.displayToken = 'No prompt yet.';
    evalCtrl.uttsGraded = 0;
    evalCtrl.setCount = '?';
    evalCtrl.comments = '';
    evalCtrl.autoplay = false;

    evalCtrl.grade = undefined; // initially unchecked
    $scope.$watch(function(){ return evalCtrl.grade; }, watchGrade);

    var promise = initSet(evalCtrl.currentSet, evalCtrl.currentUser);
    if (promise) {
      promise.then(
        function success(data){
          evalCtrl.uttsGraded = evalService.getProgress();
          // grab initial prompt/utterance
          return next('initial').then(
            function success(response) {
              logger.log('Ready for evaluation.');
            },
            function error(error) {
              $scope.msg = 'Something went wrong.';
              logger.error(error);
            });
        },
        function error(response){
          $scope.msg = 'Something went wrong.';
          logger.error(response);
        }
      )
      // grab comments from server
      .then(
        function success(data){
          return $http.get(EVALUATIONCOMMENTSURL).then(
            function success(response) {
              evalCtrl.commentOpts = response.data;
            },
            function error(response) {
              $scope.msg = 'Something went wrong grabbing comments from server.';
              logger.error(response);
            }
          );
        },
        function error(response){
          $scope.msg = 'Something went wrong.';
          logger.error(response);
        }
      )
      // now page is loaded
      .then(
        function success(data){
          $rootScope.isLoaded = true;
        }, util.stdErrCallback
      );
    }
  }

  function action() {
    /*
    Signifies the combined rec/pause button
    */
    evalCtrl.actionBtnDisabled = true;
    if (actionType === 'play') {
      play();
    } else if (actionType === 'pause') {
      pause();
    }
    toggleActionBtn();
    evalCtrl.actionBtnDisabled = false;
  }

  function audioEnded() {
    /*
    Automatically hit pause on our custom controls on reaching audio playback end.
    */
    if (actionType === 'pause') {
      action();
      $scope.$apply();
    }
  }

  function disableAllControls() {
    /*
    Disable most user interaction on page.
    */
    evalCtrl.actionBtnDisabled = true;
    evalCtrl.skipBtnDisabled = true;
    evalCtrl.undoBtnDisabled = true;
  }

  function initSet(set, user) {
    /*
    Returns promise resolved when evalService has init'd set.
    */
    return evalService.initSet(set, user, setInfoReadyCallback, setCompleteCallback);
  }

  function next(grade, comments) {
    /*
    Sets next recording and prompt.

    Parameters:
      grade     the grade for the current prompt (1-4), if undefined,
                means the prompt was skipped

    Return: promise which is resolved when the audio playback is ready to go.
    */
    var recNPrompt = evalService.getNext(grade, comments);
    return handleRecNPrompt(recNPrompt);
  }

  function handleRecNPrompt(recNPrompt) {
    /*
    Update the current recording/prompt pair.

    Parameters:
      recNPrompt    format [recLink, prompt]

    Return: promise when playback is ready to go.
    */
    evalCtrl.grade = undefined; // reset grade
    evalCtrl.recording = recNPrompt[0];
    evalCtrl.displayToken = recNPrompt[1];

    var playbackReady = $q.defer();

    // thanks QuasarDonkey, http://stackoverflow.com/a/24600597/5272567 for the Mobi test quote
    if (/Mobi/i.test(navigator.userAgent)) {
      // workaround for mobile playback, where it didn't work on chrome/android.
      // fetch blob at url using xhr, and use url generated from that blob.
      // see issue: https://code.google.com/p/chromium/issues/detail?id=227476
      // thanks, gbrlg
      $http.get(evalCtrl.recording, {'responseType':'blob'}).then(
        function success(response) {
          var reBlob = response.data;
          if (reBlob) {
            evalCtrl.recording = (window.URL || window.webkitURL).createObjectURL(reBlob);
          }
          playbackReady.resolve(true);
        },
        function error(error) {
          logger.error(error);
          $scope.msg = 'Something went wrong.';
          playbackReady.reject(false);
        }
      );
    } else {
      return $q.resolve(true);
    }

    return playbackReady.promise;
  }

  function play() {
    /*
    Start playback on audio.
    */
    audioPlayback.play();
  }

  function skip() {
    evalCtrl.skipBtnDisabled = true;
    if (actionType === 'pause') {
      toggleActionBtn();
    }
    next(undefined).then(function(){
      evalCtrl.skipBtnDisabled = false;
    }, util.stdErrCallback);
  }

  function pause() {
    audioPlayback.pause();
  }

  function setCompleteCallback() {
    $scope.msg = 'Grading set complete. Thank you.';
    isSetComplete = true;
    disableAllControls();
  }

  function setInfoReadyCallback(info) {
    /*
    Function supplied to evaluation.service as a callback for when it has received
    info about the set (currently only count).
    */
    evalCtrl.setCount = info.data.count;
  }

  function toggleActionBtn() {
    if (actionType === 'play') {
      actionType = 'pause';
      evalCtrl.actionText = PAUSETEXT;
      evalCtrl.actionGlyph = PAUSEGLYPH;
    } else if (actionType === 'pause') {
      actionType = 'play';
      evalCtrl.actionText = PLAYTEXT;
      evalCtrl.actionGlyph = PLAYGLYPH;
    }
  }

  function undo() {
    /*
    Undo last grade and grade again. Can only be used to go back one grade.
    */
    evalCtrl.undoBtnDisabled = true;
    var recNPrompt = evalService.undo();
    evalCtrl.uttsGraded--;
    handleRecNPrompt(recNPrompt);
    if (actionType === 'pause') {
      toggleActionBtn();
    }
  }

  function watchGrade() {
    /*
    Passed as a function into $scope.$watch.

    Notifies us when a grade radio button has been ticked,
    and in doing so saves the choice and triggeres the next recording.
    */
    // if valid grade has been clicked
    // thanks, Gumbo, http://stackoverflow.com/a/4728164/5272567
    if (['1','2','3','4'].indexOf(evalCtrl.grade) > -1 && !isSetComplete) {
      logger.log('Grade: '+evalCtrl.grade+', and comment: '+evalCtrl.comments+
                 ', detected for prompt: '+evalCtrl.displayToken);

      $scope.msg = '';
      evalCtrl.skipBtnDisabled = true;

      // if grade is low (2 or lower) make comment mandatory
      if (evalCtrl.comments === '' && evalCtrl.grade <= 2) {
        $scope.msg = 'Please comment on your grade.';
        evalCtrl.grade = undefined;
        return;
      }
      // make evaluator at least have started listening
      if (audioPlayback.currentTime === 0) {
        $scope.msg = 'Please listen to the recording.';
        evalCtrl.grade = undefined;
        return;
      }

      evalCtrl.uttsGraded++;

      next(evalCtrl.grade, evalCtrl.comments).then(
        finishWatchGrade,
        util.stdErrCallback
      );
    }

    function finishWatchGrade() {
      if (actionType === 'pause') {
        toggleActionBtn();
      }
      evalCtrl.comments = '';
      evalCtrl.skipBtnDisabled = isSetComplete ? true : false;
      evalCtrl.undoBtnDisabled = isSetComplete ? true : false;

      if (evalCtrl.autoplay) {
        // just writing action() here didn't seem to work.
        // not sure about this. Perhaps the angular digest has
        // to finish before this can be called?
        // real messy. If the rest of the digest takes longer
        // than X ms, the autoplay might break.
        // TODO: fix this
        $timeout(function(){ action(); }, 100);
      }
    }
  }
}
}());
<commit_msg>Minor adjustment and allowing to grade after an undo without listening to the recording.<commit_after>/*
Copyright 2016 The Eyra Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

File author/s:
    Matthias Petursson <oldschool01123@gmail.com>
*/

(function () {
'use strict';

angular.module('daApp')
.controller('EvaluationController', EvaluationController);

EvaluationController.$inject = ['$document',
                                '$http',
                                '$q',
                                '$rootScope',
                                '$scope',
                                '$timeout',
                                'dataService',
                                'evaluationService',
                                'logger',
                                'utilityService'];

function EvaluationController($document, $http, $q, $rootScope, $scope, $timeout, dataService, evaluationService, logger, utilityService) {
  var evalCtrl = this;
  var evalService = evaluationService;
  var util = utilityService;

  evalCtrl.action = action;
  evalCtrl.skip = skip;
  evalCtrl.undo = undo;

  $scope.msg = ''; // single information msg
  $rootScope.isLoaded = false;

  evalCtrl.actionBtnDisabled = false;
  evalCtrl.skipBtnDisabled = false;
  evalCtrl.undoBtnDisabled = true;

  var actionType = 'play'; // current state

  var PLAYTEXT = 'Play'; // text under the buttons
  var PAUSETEXT = 'Pause';
  var PLAYGLYPH = 'glyphicon-play'; // bootstrap glyph class
  var PAUSEGLYPH = 'glyphicon-pause';
  evalCtrl.actionText = PLAYTEXT;
  evalCtrl.actionGlyph = PLAYGLYPH;

  var EVALUATIONCOMMENTSURL = 'json/evaluation-comments.json';
  evalCtrl.commentOpts = [];

  evalCtrl.currentUser = dataService.get('currentUser');
  evalCtrl.currentSet = dataService.get('currentSet');
  var isSetComplete = false;
  var undoFlag = false; // true if an undo utterance is the current one (used to circumvent needing to listen for grading)

  // save reference to the audio element on the page, for play/pause
  // thanks, Shushanth Pallegar, http://stackoverflow.com/a/30899643/5272567
  // TODO: would probably be nicer to make a directive
  var audioPlayback = $document.find("#audio-playback")[0];
  if (audioPlayback) {
    audioPlayback.addEventListener('ended', audioEnded);
  } else {
    $scope.msg = 'Something went wrong with the audio playback.';
  }

  activate();

  //////////

  function activate() {
    evalCtrl.displayToken = 'No prompt yet.';
    evalCtrl.uttsGraded = 0;
    evalCtrl.setCount = '?';
    evalCtrl.comments = '';
    evalCtrl.autoplay = false;

    evalCtrl.grade = undefined; // initially unchecked
    $scope.$watch(function(){ return evalCtrl.grade; }, watchGrade);

    var promise = initSet(evalCtrl.currentSet, evalCtrl.currentUser);
    if (promise) {
      promise.then(
        function success(data){
          evalCtrl.uttsGraded = evalService.getProgress();
          // grab initial prompt/utterance
          return next('initial').then(
            function success(response) {
              logger.log('Ready for evaluation.');
            },
            function error(error) {
              $scope.msg = 'Something went wrong.';
              logger.error(error);
            });
        },
        function error(response){
          $scope.msg = 'Something went wrong.';
          logger.error(response);
        }
      )
      // grab comments from server
      .then(
        function success(data){
          return $http.get(EVALUATIONCOMMENTSURL).then(
            function success(response) {
              evalCtrl.commentOpts = response.data;
            },
            function error(response) {
              $scope.msg = 'Something went wrong grabbing comments from server.';
              logger.error(response);
            }
          );
        },
        function error(response){
          $scope.msg = 'Something went wrong.';
          logger.error(response);
        }
      )
      // now page is loaded
      .then(
        function success(data){
          $rootScope.isLoaded = true;
        }, util.stdErrCallback
      );
    }
  }

  function action() {
    /*
    Signifies the combined rec/pause button
    */
    evalCtrl.actionBtnDisabled = true;
    if (actionType === 'play') {
      play();
    } else if (actionType === 'pause') {
      pause();
    }
    toggleActionBtn();
    evalCtrl.actionBtnDisabled = false;
  }

  function audioEnded() {
    /*
    Automatically hit pause on our custom controls on reaching audio playback end.
    */
    if (actionType === 'pause') {
      action();
      $scope.$apply();
    }
  }

  function disableAllControls() {
    /*
    Disable most user interaction on page.
    */
    evalCtrl.actionBtnDisabled = true;
    evalCtrl.skipBtnDisabled = true;
    evalCtrl.undoBtnDisabled = true;
  }

  function initSet(set, user) {
    /*
    Returns promise resolved when evalService has init'd set.
    */
    return evalService.initSet(set, user, setInfoReadyCallback, setCompleteCallback);
  }

  function next(grade, comments) {
    /*
    Sets next recording and prompt.

    Parameters:
      grade     the grade for the current prompt (1-4), if undefined,
                means the prompt was skipped

    Return: promise which is resolved when the audio playback is ready to go.
    */
    var recNPrompt = evalService.getNext(grade, comments);
    return handleRecNPrompt(recNPrompt);
  }

  function handleRecNPrompt(recNPrompt) {
    /*
    Update the current recording/prompt pair.

    Parameters:
      recNPrompt    format [recLink, prompt]

    Return: promise when playback is ready to go.
    */
    evalCtrl.grade = undefined; // reset grade
    evalCtrl.recording = recNPrompt[0];
    evalCtrl.displayToken = recNPrompt[1];

    var playbackReady = $q.defer();

    // thanks QuasarDonkey, http://stackoverflow.com/a/24600597/5272567 for the Mobi test quote
    if (/Mobi/i.test(navigator.userAgent)) {
      // workaround for mobile playback, where it didn't work on chrome/android.
      // fetch blob at url using xhr, and use url generated from that blob.
      // see issue: https://code.google.com/p/chromium/issues/detail?id=227476
      // thanks, gbrlg
      $http.get(evalCtrl.recording, {'responseType':'blob'}).then(
        function success(response) {
          var reBlob = response.data;
          if (reBlob) {
            evalCtrl.recording = (window.URL || window.webkitURL).createObjectURL(reBlob);
          }
          playbackReady.resolve(true);
        },
        function error(error) {
          logger.error(error);
          $scope.msg = 'Something went wrong.';
          playbackReady.reject(false);
        }
      );
    } else {
      return $q.resolve(true);
    }

    return playbackReady.promise;
  }

  function play() {
    /*
    Start playback on audio.
    */
    audioPlayback.play();
  }

  function skip() {
    evalCtrl.skipBtnDisabled = true;
    if (actionType === 'pause') {
      toggleActionBtn();
    }
    next(undefined).then(function(){
      evalCtrl.skipBtnDisabled = false;
    }, util.stdErrCallback);
  }

  function pause() {
    audioPlayback.pause();
  }

  function setCompleteCallback() {
    $scope.msg = 'Grading set complete. Thank you.';
    isSetComplete = true;
    disableAllControls();
  }

  function setInfoReadyCallback(info) {
    /*
    Function supplied to evaluation.service as a callback for when it has received
    info about the set (currently only count).
    */
    evalCtrl.setCount = info.data.count;
  }

  function toggleActionBtn() {
    if (actionType === 'play') {
      actionType = 'pause';
      evalCtrl.actionText = PAUSETEXT;
      evalCtrl.actionGlyph = PAUSEGLYPH;
    } else if (actionType === 'pause') {
      actionType = 'play';
      evalCtrl.actionText = PLAYTEXT;
      evalCtrl.actionGlyph = PLAYGLYPH;
    }
  }

  function undo() {
    /*
    Undo last grade and grade again. Can only be used to go back one grade.
    */
    evalCtrl.undoBtnDisabled = true;
    undoFlag = true;

    var recNPrompt = evalService.undo();
    evalCtrl.uttsGraded--;
    handleRecNPrompt(recNPrompt).then(function(){
      if (actionType === 'pause') {
        toggleActionBtn();
      }
    }, util.stdErrCallback);
  }

  function watchGrade() {
    /*
    Passed as a function into $scope.$watch.

    Notifies us when a grade radio button has been ticked,
    and in doing so saves the choice and triggeres the next recording.
    */
    // if valid grade has been clicked
    // thanks, Gumbo, http://stackoverflow.com/a/4728164/5272567
    if (['1','2','3','4'].indexOf(evalCtrl.grade) > -1 && !isSetComplete) {
      logger.log('Grade: '+evalCtrl.grade+', and comment: '+evalCtrl.comments+
                 ', detected for prompt: '+evalCtrl.displayToken);

      $scope.msg = '';
      evalCtrl.skipBtnDisabled = true;

      // if grade is low (2 or lower) make comment mandatory
      if (evalCtrl.comments === '' && evalCtrl.grade <= 2) {
        $scope.msg = 'Please comment on your grade.';
        evalCtrl.grade = undefined;
        return;
      }
      // make evaluator at least have started listening
      if (audioPlayback.currentTime === 0 && !undoFlag) {
        $scope.msg = 'Please listen to the recording.';
        evalCtrl.grade = undefined;
        return;
      }

      evalCtrl.uttsGraded++;

      next(evalCtrl.grade, evalCtrl.comments).then(
        finishWatchGrade,
        util.stdErrCallback
      );
    }

    function finishWatchGrade() {
      if (actionType === 'pause') {
        toggleActionBtn();
      }
      evalCtrl.comments = '';
      evalCtrl.skipBtnDisabled = isSetComplete ? true : false;
      evalCtrl.undoBtnDisabled = isSetComplete ? true : false;
      undoFlag = false;

      if (evalCtrl.autoplay) {
        // just writing action() here didn't seem to work.
        // not sure about this. Perhaps the angular digest has
        // to finish before this can be called?
        // real messy. If the rest of the digest takes longer
        // than X ms, the autoplay might break.
        // TODO: fix this
        $timeout(function(){ action(); }, 100);
      }
    }
  }
}
}());
<|endoftext|>


<commit_before><commit_msg>Update ogario.v4.master.js<commit_after><|endoftext|>


<commit_before><commit_msg>Delete ogarioworkingMGx.js<commit_after><|endoftext|>