<commit_before>var fs = require('fs'),
	sys = require('sys'),
	path = require('path'),
	util = require('./util'),
	bind = util.bind,
	map = util.map,
	repeat = util.repeat,
	boxComment = util.boxComment,
	q = util.q,
	tokenizer = require('./tokenizer'),
	parser = require('./parser'),
	compiler = exports,
	gModules = {}

exports.compile = util.intercept('CompileError', doCompile)

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

function doCompile(ast, rootContext) {
	rootContext = rootContext || { hookName: name('ROOT_HOOK'), referenceTable: {} }

	return code(
		'function initFunApp() {',
		'	var {{ rootHookName }} = fun.name("rootHook")',
		'	fun.setHook({{ rootHookName }}, document.body)',
			compile(rootContext, ast),
		'}',
		{
			rootHookName: rootContext.hookName
		})
		+ '\n\n' + map(gModules, function(module, name) {
			return '\n' + boxComment('Module: ' + name) + '\n' + module.jsCode
		}).join('\n')
		+ '\n\ninitFunApp() // let\'s kick it'
}

/*********************
 * Utility functions *
 *********************/
function name(readable) { return '_' + (readable || '') + '$' + (name._uniqueId++) }
name._uniqueId = 0

var emitReplaceRegex = /{{\s*(\w+)\s*}}/
function code(/* line1, line2, line3, ..., lineN, optionalValues */) {
	var argsLen = arguments.length,
		lastArg = arguments[argsLen - 1],
		injectObj = (typeof lastArg == 'string' ? null : lastArg),
		snippets = Array.prototype.slice.call(arguments, 0, injectObj ? argsLen - 1 : argsLen),
		code, match

	code = '\n' + snippets.join('\n')
	if (!injectObj) { return code }

	while (match = code.match(emitReplaceRegex)) {
		var wholeMatch = match[0],
			nameMatch = match[1],
			value = injectObj[nameMatch]
		code = code.replace(wholeMatch, typeof value == 'undefined' ? 'MISSING INJECT VALUE' : value)
	}
	return code
}

var assert = function(ok, ast, msg) { if (!ok) halt(ast, msg) }
var halt = function(ast, msg) {
	sys.puts(util.grabLine(ast.file, ast.line, ast.column, ast.span))
	throw new CompileError(ast.file, ast, msg)
}

/************************
 * Top level statements *
 ************************/
function compile(context, ast) {
	assert(context && context.hookName && context.referenceTable, ast, "compile called with invalid context")
	if (ast instanceof Array) {
		return map(ast, bind(this, compileStatement, context)).join('\n') + '\n'
	} else {
		return compileStatement(context, ast) + '\n'
	}
}

function compileStatement(context, ast) {
	switch (ast.type) {
		case 'STATIC_VALUE':
			return compileStaticValue(context, ast)
		case 'ALIAS':
			return compileAlias(context, ast)
		case 'XML':
			return compileXML(context, ast)
		case 'DECLARATION':
			return compileDeclaration(context, ast)
		case 'IF_STATEMENT':
			return compileIfStatement(context, ast)
		case 'FOR_LOOP':
			return compileForLoop(context, ast)
		case 'INVOCATION':
			return compileInvocation(context, ast)
		case 'IMPORT_MODULE':
			return compileModuleImport(context, ast)
		case 'IMPORT_FILE':
			return compileFileImport(cotext, ast)
		default:
			halt(ast, 'Unknown AST type ' + ast.type)
	}
}

/**********************
 * Aliases and Values *
 **********************/
function compileAlias(context, ast) {
	assert(ast.type == 'ALIAS', ast, 'Expected an ALIAS but found a ' + ast.type)
	var valueAST = _getAlias(context, ast)
	switch(valueAST.type) {
		case 'ALIAS':
			return compileAlias(context, valueAST)
		case 'STATIC_VALUE':
			return compileStaticValue(context, valueAST)
		case 'ITEM_PROPERTY':
			return _compileItemProperty(context, valueAST)
		default:
			halt(ast, 'Unknown value type ' + valueAST.type)
	}
}

function compileStaticValue(context, ast) {
	return code(
		'fun.hook({{ parentHook }}, fun.name("inlineString")).innerHTML = {{ value }}',
		{
			parentHook: context.hookName,
			value: JSON.stringify(ast.value)
		})
}

function _compileItemProperty(context, ast) {
	assert(ast.property.length > 0, ast, 'Missing property on item reference. "'+ast.namespace[0]+'" should probably be something like "'+ast.namespace[0]+'.foo"')
	assert(ast.property.length == 1, ast, 'TODO: Handle nested property references')
	var hookName = name('ITEM_PROPERTY_HOOK')
	return code(
		'fun.hook({{ parentHook }}, {{ hookName }})',
		'fun.observe({{ type }}, {{ id }}, {{ property }}, function(mutation, value) {',
		'	fun.getHook({{ hookName }}).innerHTML = value',
		'})',
		{
			parentHook: context.hookName,
			hookName: q(hookName),
			id: q(ast.item.id),
			property: q(ast.property[0]),
			type: q('BYTES')
		})
}

/*******
 * XML *
 *******/
function compileXML(context, ast) {
	var hookName = name('XML_HOOK'),
		newContext = util.shallowCopy(context, { hookName:hookName })

	// TODO add static attributes
	// TODO add dynamic attributes
	// TODO support style attribute
	// TODO support on* attributes (e.g. onclick)

	var attributes = _handleXMLAttributes(context, ast, hookName)
	return code(
		'var {{ hookName }} = fun.name()',
		'fun.hook({{ parentHook }}, {{ hookName }}, {{ tagName }}, {{ staticAttributes }})',
		'{{ dynamicAttributesCode }}',
		'{{ childCode }}',
		{
			parentHook: context.hookName,
			hookName: hookName,
			tagName: q(ast.tag),
			staticAttributes: q(attributes.staticAttributes),
			dynamicAttributesCode: attributes.dynamicCode,
			childCode: ast.block ? compile(newContext, ast.block) : ''
		})
}

function _handleXMLAttributes(context, ast, hookName) {
	var staticAttributes = {},
		dynamicCode = []
	for (var i=0, attribute; attribute = ast.attributes[i]; i++) {
		assert(attribute.namespace.length == 1, ast, 'TODO Handle dot notation XML attribute namespace (for e.g. style.width=100)')
		var name = attribute.namespace[0],
			value = _resolve(context, attribute.value)
		if (name == 'style') {
			assert(value.type == 'NESTED_ALIAS', ast, 'You can only assign the style attribute to a JSON object literal, e.g.
')
			_handleStyleAttribute(staticAttributes, dynamicCode, context, ast, hookName, value.content)
		} else if (value.type == 'STATIC_VALUE') {
			staticAttributes[name] = value.value
		} else {
			assert(value.type != 'NESTED_ALIAS', ast, 'Does not make sense to assign a JSON object literal to other attribtues than "style" (tried to assign to "'+name+'")')
			_handleDynamicAttribute(dynamicCode, context, ast, hookName, name, value)
		}
	}
	return { staticAttributes: staticAttributes, dynamicCode: dynamicCode.join('\n') }
}

// modifies staticAttrs and dynamicCode
function _handleStyleAttribute(staticAttrs, dynamicCode, context, ast, hookName, styles) {
	var styleAttribute = staticAttrs['style'] = {}
	for (var i=0, style; style = styles[i]; i++) {
		var value = _resolve(context, style.value)
		if (value.type == 'STATIC_VALUE') {
			styleAttribute[style.name] = value.value
		} else {
			_handleDynamicAttribute(dynamicCode, context, ast, hookName, 'style.' + style.name, value)
		}
	}
}

// modifies dynamicCode
function _handleDynamicAttribute(dynamicCode, context, ast, hookName, attrName, value) {
	assert(value.property.length == 1, ast, 'TODO: Handle nested item property references')
	dynamicCode.push(code(
		'fun.observe({{ type }}, {{ id }}, {{ property }}, function(mutation, value) {',
		'	fun.attr({{ hookName }}, {{ attr }}, value)',
		'})',
		{
			type: q('BYTES'),
			id: q(value.item.id),
			property: q(value.property[0]),
			attr: q(attrName),
			hookName: hookName
		}))
}

/**************************
 * Imports & Declarations *
 **************************/
function compileModuleImport(context, ast) {
	if (gModules[ast.name]) { return }
	var module = gModules[ast.name] = {
		name: ast.name,
		path: __dirname + '/modules/' + ast.name + '/'
	}
	assert(fs.statSync(module.path).isDirectory(), ast, '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
	if (fs.statSync(module.path + 'lib.fun').isFile()) {
		var tokens = tokenizer.tokenize(module.path + 'lib.fun')
		var newAST = parser.parse(tokens)
		var result = compile(context, newAST)
	}
	if (path.existsSync(module.path + 'lib.js')) {
		module.jsCode = fs.readFileSync(module.path + 'lib.js')
	} else {
		module.jsCode = '// No JS code for ' + module.name
	}
	return result
}

function compileFileImport(context, ast) {
	halt(ast, 'TODO compileFileImport')
	// read file
	// tokenize
	// parse
	// compile without output
	// merge contexts
}

function compileDeclaration(context, ast) {
	_storeAlias(context, ast)
	return ''
}

var _storeAlias = function(context, ast) {
	var baseValue = _getAlias(context, ast, true),
		namespace = ast.namespace,
		name = namespace[namespace.length - 1],
		value = ast.value

	assert(!baseValue['__alias__' + name], ast, 'Repeat declaration')
	if (value.type == 'NESTED_ALIAS') {
		baseValue['__alias__' + name] = { type: 'ALIAS' }
		for (var i=0, content; content = value.content[i]; i++) {
			var newNamespace = util.copyArray(ast.namespace).concat(content.name),
				newAST = util.shallowCopy(ast, { type: 'ALIAS', value: content.value, namespace: newNamespace })

			_storeAlias(context, newAST)
		}
	} else {
		baseValue['__alias__' + name] = ast.value
	}
}
var _getAlias = function(context, ast, skipLast) {
	var referenceTable = context.referenceTable,
		value = referenceTable,
		namespace = ast.namespace,
		len = namespace.length - (skipLast ? 1 : 0)

	for (var i=0; i < len; i++) {
		value = value['__alias__' + namespace[i]]
		if (!value) {
			halt(ast, 'Undeclared alias "'+namespace[i]+'"' + (i == 0 ? '' : ' on "'+namespace.slice(0, i).join('.')+'"'))
		}
		if (value.type == 'ITEM') {
			return util.shallowCopy(ast, { type: 'ITEM_PROPERTY', item:value, property:namespace.slice(i+1) })
		}
	}

	return value
}

var _resolve = function(context, aliasOrValue) {
	if (aliasOrValue.type != 'ALIAS') { return aliasOrValue }
	else { return _resolve(context, _getAlias(context, aliasOrValue)) }
}

/**********************
 * If/Else statements *
 **********************/
function compileIfStatement(context, ast) {
	halt(ast, 'TODO compileIfStatement not yet implemented')
}

/*************
 * For loops *
 *************/
function compileForLoop(context, ast) {
	halt(ast, 'TODO compileForLoop not yet implemented')
}

/****************************************
 * Invocations (handlers and templates) *
 ****************************************/
function compileInvocation(context, ast) {
	halt(ast, 'TODO compileInvocation not yet implemented')
}
<commit_msg>Remove old TODO items and add a TODO halt statement for on* handlers (e.g. onClick)<commit_after>var fs = require('fs'),
	sys = require('sys'),
	path = require('path'),
	util = require('./util'),
	bind = util.bind,
	map = util.map,
	repeat = util.repeat,
	boxComment = util.boxComment,
	q = util.q,
	tokenizer = require('./tokenizer'),
	parser = require('./parser'),
	compiler = exports,
	gModules = {}

exports.compile = util.intercept('CompileError', doCompile)

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

function doCompile(ast, rootContext) {
	rootContext = rootContext || { hookName: name('ROOT_HOOK'), referenceTable: {} }

	return code(
		'function initFunApp() {',
		'	var {{ rootHookName }} = fun.name("rootHook")',
		'	fun.setHook({{ rootHookName }}, document.body)',
			compile(rootContext, ast),
		'}',
		{
			rootHookName: rootContext.hookName
		})
		+ '\n\n' + map(gModules, function(module, name) {
			return '\n' + boxComment('Module: ' + name) + '\n' + module.jsCode
		}).join('\n')
		+ '\n\ninitFunApp() // let\'s kick it'
}

/*********************
 * Utility functions *
 *********************/
function name(readable) { return '_' + (readable || '') + '$' + (name._uniqueId++) }
name._uniqueId = 0

var emitReplaceRegex = /{{\s*(\w+)\s*}}/
function code(/* line1, line2, line3, ..., lineN, optionalValues */) {
	var argsLen = arguments.length,
		lastArg = arguments[argsLen - 1],
		injectObj = (typeof lastArg == 'string' ? null : lastArg),
		snippets = Array.prototype.slice.call(arguments, 0, injectObj ? argsLen - 1 : argsLen),
		code, match

	code = '\n' + snippets.join('\n')
	if (!injectObj) { return code }

	while (match = code.match(emitReplaceRegex)) {
		var wholeMatch = match[0],
			nameMatch = match[1],
			value = injectObj[nameMatch]
		code = code.replace(wholeMatch, typeof value == 'undefined' ? 'MISSING INJECT VALUE' : value)
	}
	return code
}

var assert = function(ok, ast, msg) { if (!ok) halt(ast, msg) }
var halt = function(ast, msg) {
	sys.puts(util.grabLine(ast.file, ast.line, ast.column, ast.span))
	throw new CompileError(ast.file, ast, msg)
}

/************************
 * Top level statements *
 ************************/
function compile(context, ast) {
	assert(context && context.hookName && context.referenceTable, ast, "compile called with invalid context")
	if (ast instanceof Array) {
		return map(ast, bind(this, compileStatement, context)).join('\n') + '\n'
	} else {
		return compileStatement(context, ast) + '\n'
	}
}

function compileStatement(context, ast) {
	switch (ast.type) {
		case 'STATIC_VALUE':
			return compileStaticValue(context, ast)
		case 'ALIAS':
			return compileAlias(context, ast)
		case 'XML':
			return compileXML(context, ast)
		case 'DECLARATION':
			return compileDeclaration(context, ast)
		case 'IF_STATEMENT':
			return compileIfStatement(context, ast)
		case 'FOR_LOOP':
			return compileForLoop(context, ast)
		case 'INVOCATION':
			return compileInvocation(context, ast)
		case 'IMPORT_MODULE':
			return compileModuleImport(context, ast)
		case 'IMPORT_FILE':
			return compileFileImport(cotext, ast)
		default:
			halt(ast, 'Unknown AST type ' + ast.type)
	}
}

/**********************
 * Aliases and Values *
 **********************/
function compileAlias(context, ast) {
	assert(ast.type == 'ALIAS', ast, 'Expected an ALIAS but found a ' + ast.type)
	var valueAST = _getAlias(context, ast)
	switch(valueAST.type) {
		case 'ALIAS':
			return compileAlias(context, valueAST)
		case 'STATIC_VALUE':
			return compileStaticValue(context, valueAST)
		case 'ITEM_PROPERTY':
			return _compileItemProperty(context, valueAST)
		default:
			halt(ast, 'Unknown value type ' + valueAST.type)
	}
}

function compileStaticValue(context, ast) {
	return code(
		'fun.hook({{ parentHook }}, fun.name("inlineString")).innerHTML = {{ value }}',
		{
			parentHook: context.hookName,
			value: JSON.stringify(ast.value)
		})
}

function _compileItemProperty(context, ast) {
	assert(ast.property.length > 0, ast, 'Missing property on item reference. "'+ast.namespace[0]+'" should probably be something like "'+ast.namespace[0]+'.foo"')
	assert(ast.property.length == 1, ast, 'TODO: Handle nested property references')
	var hookName = name('ITEM_PROPERTY_HOOK')
	return code(
		'fun.hook({{ parentHook }}, {{ hookName }})',
		'fun.observe({{ type }}, {{ id }}, {{ property }}, function(mutation, value) {',
		'	fun.getHook({{ hookName }}).innerHTML = value',
		'})',
		{
			parentHook: context.hookName,
			hookName: q(hookName),
			id: q(ast.item.id),
			property: q(ast.property[0]),
			type: q('BYTES')
		})
}

/*******
 * XML *
 *******/
function compileXML(context, ast) {
	var hookName = name('XML_HOOK'),
		newContext = util.shallowCopy(context, { hookName:hookName })

	var attributes = _handleXMLAttributes(context, ast, hookName)
	return code(
		'var {{ hookName }} = fun.name()',
		'fun.hook({{ parentHook }}, {{ hookName }}, {{ tagName }}, {{ staticAttributes }})',
		'{{ dynamicAttributesCode }}',
		'{{ childCode }}',
		{
			parentHook: context.hookName,
			hookName: hookName,
			tagName: q(ast.tag),
			staticAttributes: q(attributes.staticAttributes),
			dynamicAttributesCode: attributes.dynamicCode,
			childCode: ast.block ? compile(newContext, ast.block) : ''
		})
}

function _handleXMLAttributes(context, ast, hookName) {
	var staticAttributes = {},
		dynamicCode = []
	for (var i=0, attribute; attribute = ast.attributes[i]; i++) {
		assert(attribute.namespace.length == 1, ast, 'TODO Handle dot notation XML attribute namespace (for e.g. style.width=100)')
		var name = attribute.namespace[0],
			value = _resolve(context, attribute.value),
			match
		if (name == 'style') {
			assert(value.type == 'NESTED_ALIAS', ast, 'You can only assign the style attribute to a JSON object literal, e.g.
')
			_handleStyleAttribute(staticAttributes, dynamicCode, context, ast, hookName, value.content)
		} else if (match = name.match(/^on(\w+)$/)) {
			halt('TODO Handle on* xml attributes')
		} else if (value.type == 'STATIC_VALUE') {
			staticAttributes[name] = value.value
		} else {
			assert(value.type != 'NESTED_ALIAS', ast, 'Does not make sense to assign a JSON object literal to other attribtues than "style" (tried to assign to "'+name+'")')
			_handleDynamicAttribute(dynamicCode, context, ast, hookName, name, value)
		}
	}
	return { staticAttributes: staticAttributes, dynamicCode: dynamicCode.join('\n') }
}

// modifies staticAttrs and dynamicCode
function _handleStyleAttribute(staticAttrs, dynamicCode, context, ast, hookName, styles) {
	var styleAttribute = staticAttrs['style'] = {}
	for (var i=0, style; style = styles[i]; i++) {
		var value = _resolve(context, style.value)
		if (value.type == 'STATIC_VALUE') {
			styleAttribute[style.name] = value.value
		} else {
			_handleDynamicAttribute(dynamicCode, context, ast, hookName, 'style.' + style.name, value)
		}
	}
}

// modifies dynamicCode
function _handleDynamicAttribute(dynamicCode, context, ast, hookName, attrName, value) {
	assert(value.property.length == 1, ast, 'TODO: Handle nested item property references')
	dynamicCode.push(code(
		'fun.observe({{ type }}, {{ id }}, {{ property }}, function(mutation, value) {',
		'	fun.attr({{ hookName }}, {{ attr }}, value)',
		'})',
		{
			type: q('BYTES'),
			id: q(value.item.id),
			property: q(value.property[0]),
			attr: q(attrName),
			hookName: hookName
		}))
}

/**************************
 * Imports & Declarations *
 **************************/
function compileModuleImport(context, ast) {
	if (gModules[ast.name]) { return }
	var module = gModules[ast.name] = {
		name: ast.name,
		path: __dirname + '/modules/' + ast.name + '/'
	}
	assert(fs.statSync(module.path).isDirectory(), ast, '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
	if (fs.statSync(module.path + 'lib.fun').isFile()) {
		var tokens = tokenizer.tokenize(module.path + 'lib.fun')
		var newAST = parser.parse(tokens)
		var result = compile(context, newAST)
	}
	if (path.existsSync(module.path + 'lib.js')) {
		module.jsCode = fs.readFileSync(module.path + 'lib.js')
	} else {
		module.jsCode = '// No JS code for ' + module.name
	}
	return result
}

function compileFileImport(context, ast) {
	halt(ast, 'TODO compileFileImport')
	// read file
	// tokenize
	// parse
	// compile without output
	// merge contexts
}

function compileDeclaration(context, ast) {
	_storeAlias(context, ast)
	return ''
}

var _storeAlias = function(context, ast) {
	var baseValue = _getAlias(context, ast, true),
		namespace = ast.namespace,
		name = namespace[namespace.length - 1],
		value = ast.value

	assert(!baseValue['__alias__' + name], ast, 'Repeat declaration')
	if (value.type == 'NESTED_ALIAS') {
		baseValue['__alias__' + name] = { type: 'ALIAS' }
		for (var i=0, content; content = value.content[i]; i++) {
			var newNamespace = util.copyArray(ast.namespace).concat(content.name),
				newAST = util.shallowCopy(ast, { type: 'ALIAS', value: content.value, namespace: newNamespace })

			_storeAlias(context, newAST)
		}
	} else {
		baseValue['__alias__' + name] = ast.value
	}
}
var _getAlias = function(context, ast, skipLast) {
	var referenceTable = context.referenceTable,
		value = referenceTable,
		namespace = ast.namespace,
		len = namespace.length - (skipLast ? 1 : 0)

	for (var i=0; i < len; i++) {
		value = value['__alias__' + namespace[i]]
		if (!value) {
			halt(ast, 'Undeclared alias "'+namespace[i]+'"' + (i == 0 ? '' : ' on "'+namespace.slice(0, i).join('.')+'"'))
		}
		if (value.type == 'ITEM') {
			return util.shallowCopy(ast, { type: 'ITEM_PROPERTY', item:value, property:namespace.slice(i+1) })
		}
	}

	return value
}

var _resolve = function(context, aliasOrValue) {
	if (aliasOrValue.type != 'ALIAS') { return aliasOrValue }
	else { return _resolve(context, _getAlias(context, aliasOrValue)) }
}

/**********************
 * If/Else statements *
 **********************/
function compileIfStatement(context, ast) {
	halt(ast, 'TODO compileIfStatement not yet implemented')
}

/*************
 * For loops *
 *************/
function compileForLoop(context, ast) {
	halt(ast, 'TODO compileForLoop not yet implemented')
}

/****************************************
 * Invocations (handlers and templates) *
 ****************************************/
function compileInvocation(context, ast) {
	halt(ast, 'TODO compileInvocation not yet implemented')
}
<|endoftext|>


<commit_before>module.exports = {
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'es', 'fr', 'lt']
  },
  react: {
    useSuspense: false
  }
}
<commit_msg>Add nb_NO to the supported i18n locales<commit_after>module.exports = {
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'es', 'fr', 'lt', 'nb_NO']
  },
  react: {
    useSuspense: false
  }
}
<|endoftext|>


<commit_before>var mongoose = require('mongoose');

var messageModel = function () {

  var messageSchema = mongoose.Schema({
    name: { type: String, required: true, trim: true },
    email: { type: String, required: true, trim: true },
    message: { type: String, required: true, trim: true },
    created: { type: Date, default: Date.now },
    updated: { type: Date }
  });

  messageSchema.pre('update', function (next) {

    this.update = new Date();

    next();
  });

  return mongoose.model('Message', messageSchema);
};

module.exports = new messageModel();
<commit_msg>add app-key column for multi ContactWithMe service<commit_after>var mongoose = require('mongoose');

var messageModel = function () {

  var messageSchema = mongoose.Schema({
    app: { type: String, required: true, trim: true },
    name: { type: String, required: true, trim: true },
    email: { type: String, required: true, trim: true },
    message: { type: String, required: true, trim: true },
    created: { type: Date, default: Date.now },
    updated: { type: Date }
  });

  messageSchema.pre('update', function (next) {

    this.update = new Date();

    next();
  });

  return mongoose.model('Message', messageSchema);
};

module.exports = new messageModel();
<|endoftext|>


<commit_before>// Load modules

var Boom = require('boom');
var Catbox = require('catbox');
var Hoek = require('hoek');
var Schema = require('./schema');

// Declare internals

var internals = {};

exports = module.exports = internals.Methods = function (server) {

    this.server = server;
    this.methods = {};
    this._normalized = {};
};

internals.Methods.prototype.add = function (name, method, options, realm) {

    if (typeof name !== 'object') {
        return this._add(name, method, options, realm);
    }

    // {} or [{}, {}]

    var items = [].concat(name);
    for (var i = 0, il = items.length; i < il; ++i) {
        var item = items[i];
        this._add(item.name, item.method, item.options, realm);
    }
};

exports.methodNameRx = /^[_$a-zA-Z]\w*(?:\.[a-zA-Z]\w*)*$/;

internals.Methods.prototype._add = function (name, method, options, realm) {

    var self = this;

    Hoek.assert(typeof method === 'function', 'method must be a function');
    Hoek.assert(typeof name === 'string', 'name must be a string');
    Hoek.assert(name.match(exports.methodNameRx), 'Invalid name:', name);
    Hoek.assert(!Hoek.reach(this.methods, name, { functions: false }), 'Server method function name already exists');

    options = options || {};
    Schema.assert('method', options, name);

    var settings = Hoek.cloneWithShallow(options, ['bind']);
    settings.generateKey = settings.generateKey || internals.generateKey;
    var bind = settings.bind || realm.settings.bind || null;
    var bound = bind ? function () { return method.apply(bind, arguments); } : method;

    // Normalize methods

    var normalized = bound;
    if (settings.callback === false) {                                          // Defaults to true
        normalized = function (/* arg1, arg2, ..., argn, methodNext */) {

            var args = [];
            for (var i = 0, il = arguments.length; i < il - 1; ++i) {
                args.push(arguments[i]);
            }

            var methodNext = arguments[il - 1];

            var result = null;
            var error = null;

            try {
                result = method.apply(bind, args);
            }
            catch (err) {
                error = err;
            }

            if (result instanceof Error) {
                error = result;
                result = null;
            }

            if (error ||
                typeof result !== 'object' ||
                typeof result.then !== 'function') {

                return methodNext(error, result);
            }

            // Promise object

            var onFulfilled = function (result) {

                return methodNext(null, result);
            };

            var onRejected = function (err) {

                return methodNext(err);
            };

            result.then(onFulfilled, onRejected);
        };
    }

    // Not cached

    if (!settings.cache) {
        return this._assign(name, bound, normalized);
    }

    // Cached

    Hoek.assert(!settings.cache.generateFunc, 'Cannot set generateFunc with method caching');

    settings.cache.generateFunc = function (id, next) {

        id.args.push(next);                     // function (err, result, ttl)
        normalized.apply(bind, id.args);
    };

    var cache = this.server.cache(settings.cache, '#' + name);

    var func = function (/* arguments, methodNext */) {

        var args = [];
        for (var i = 0, il = arguments.length; i < il - 1; ++i) {
            args.push(arguments[i]);
        }

        var methodNext = arguments[il - 1];

        var key = settings.generateKey.apply(bind, args);
        if (key === null ||                                 // Value can be ''
            typeof key !== 'string') {                      // When using custom generateKey

            return Hoek.nextTick(methodNext)(Boom.badImplementation('Invalid method key when invoking: ' + name, { name: name, args: args }));
        }

        cache.get({ id: key, args: args }, methodNext);
    };

    func.cache = {
        drop: function (/* arguments, callback */) {

            var args = [];
            for (var i = 0, il = arguments.length; i < il - 1; ++i) {
                args.push(arguments[i]);
            }

            var methodNext = arguments[il - 1];

            var key = settings.generateKey.apply(null, args);
            if (key === null) {                             // Value can be ''
                return Hoek.nextTick(methodNext)(Boom.badImplementation('Invalid method key'));
            }

            return cache.drop(key, methodNext);
        }
    };

    this._assign(name, func, func);
};

internals.Methods.prototype._assign = function (name, method, normalized) {

    var path = name.split('.');
    var ref = this.methods;
    for (var i = 0, il = path.length; i < il; ++i) {
        if (!ref[path[i]]) {
            ref[path[i]] = (i + 1 === il ? method : {});
        }

        ref = ref[path[i]];
    }

    this._normalized[name] = normalized;
};

internals.generateKey = function () {

    var key = '';
    for (var i = 0, il = arguments.length; i < il; ++i) {
        var arg = arguments[i];
        if (typeof arg !== 'string' &&
            typeof arg !== 'number' &&
            typeof arg !== 'boolean') {

            return null;
        }

        key += (i ? ':' : '') + encodeURIComponent(arg.toString());
    }

    return key;
};
<commit_msg>changed regex for allowing methods to use _ and $<commit_after>// Load modules

var Boom = require('boom');
var Catbox = require('catbox');
var Hoek = require('hoek');
var Schema = require('./schema');

// Declare internals

var internals = {};

exports = module.exports = internals.Methods = function (server) {

    this.server = server;
    this.methods = {};
    this._normalized = {};
};

internals.Methods.prototype.add = function (name, method, options, realm) {

    if (typeof name !== 'object') {
        return this._add(name, method, options, realm);
    }

    // {} or [{}, {}]

    var items = [].concat(name);
    for (var i = 0, il = items.length; i < il; ++i) {
        var item = items[i];
        this._add(item.name, item.method, item.options, realm);
    }
};

exports.methodNameRx = /^[_$a-zA-Z]\w*(?:\.[_$a-zA-Z]\w*)*$/;

internals.Methods.prototype._add = function (name, method, options, realm) {

    var self = this;

    Hoek.assert(typeof method === 'function', 'method must be a function');
    Hoek.assert(typeof name === 'string', 'name must be a string');
    Hoek.assert(name.match(exports.methodNameRx), 'Invalid name:', name);
    Hoek.assert(!Hoek.reach(this.methods, name, { functions: false }), 'Server method function name already exists');

    options = options || {};
    Schema.assert('method', options, name);

    var settings = Hoek.cloneWithShallow(options, ['bind']);
    settings.generateKey = settings.generateKey || internals.generateKey;
    var bind = settings.bind || realm.settings.bind || null;
    var bound = bind ? function () { return method.apply(bind, arguments); } : method;

    // Normalize methods

    var normalized = bound;
    if (settings.callback === false) {                                          // Defaults to true
        normalized = function (/* arg1, arg2, ..., argn, methodNext */) {

            var args = [];
            for (var i = 0, il = arguments.length; i < il - 1; ++i) {
                args.push(arguments[i]);
            }

            var methodNext = arguments[il - 1];

            var result = null;
            var error = null;

            try {
                result = method.apply(bind, args);
            }
            catch (err) {
                error = err;
            }

            if (result instanceof Error) {
                error = result;
                result = null;
            }

            if (error ||
                typeof result !== 'object' ||
                typeof result.then !== 'function') {

                return methodNext(error, result);
            }

            // Promise object

            var onFulfilled = function (result) {

                return methodNext(null, result);
            };

            var onRejected = function (err) {

                return methodNext(err);
            };

            result.then(onFulfilled, onRejected);
        };
    }

    // Not cached

    if (!settings.cache) {
        return this._assign(name, bound, normalized);
    }

    // Cached

    Hoek.assert(!settings.cache.generateFunc, 'Cannot set generateFunc with method caching');

    settings.cache.generateFunc = function (id, next) {

        id.args.push(next);                     // function (err, result, ttl)
        normalized.apply(bind, id.args);
    };

    var cache = this.server.cache(settings.cache, '#' + name);

    var func = function (/* arguments, methodNext */) {

        var args = [];
        for (var i = 0, il = arguments.length; i < il - 1; ++i) {
            args.push(arguments[i]);
        }

        var methodNext = arguments[il - 1];

        var key = settings.generateKey.apply(bind, args);
        if (key === null ||                                 // Value can be ''
            typeof key !== 'string') {                      // When using custom generateKey

            return Hoek.nextTick(methodNext)(Boom.badImplementation('Invalid method key when invoking: ' + name, { name: name, args: args }));
        }

        cache.get({ id: key, args: args }, methodNext);
    };

    func.cache = {
        drop: function (/* arguments, callback */) {

            var args = [];
            for (var i = 0, il = arguments.length; i < il - 1; ++i) {
                args.push(arguments[i]);
            }

            var methodNext = arguments[il - 1];

            var key = settings.generateKey.apply(null, args);
            if (key === null) {                             // Value can be ''
                return Hoek.nextTick(methodNext)(Boom.badImplementation('Invalid method key'));
            }

            return cache.drop(key, methodNext);
        }
    };

    this._assign(name, func, func);
};

internals.Methods.prototype._assign = function (name, method, normalized) {

    var path = name.split('.');
    var ref = this.methods;
    for (var i = 0, il = path.length; i < il; ++i) {
        if (!ref[path[i]]) {
            ref[path[i]] = (i + 1 === il ? method : {});
        }

        ref = ref[path[i]];
    }

    this._normalized[name] = normalized;
};

internals.generateKey = function () {

    var key = '';
    for (var i = 0, il = arguments.length; i < il; ++i) {
        var arg = arguments[i];
        if (typeof arg !== 'string' &&
            typeof arg !== 'number' &&
            typeof arg !== 'boolean') {

            return null;
        }

        key += (i ? ':' : '') + encodeURIComponent(arg.toString());
    }

    return key;
};
<|endoftext|>


<commit_before>'use strict';
var slice = [].slice;
var extend = require('./extend');
var npath = require('path');

/**
 * Loads an environment configuration based on the value of an environment
 * variable.
 *
 * @param {String} envVariable Name of environment variable to use. If null
 *                             then the second argument is the config
 *                             name to use. Recommend 'NODE_ENV'.
 * @param {String} defaultEnv The  environment to use if environment
 *                            variable is not set or null.
 */
function MinConf(envVariable, defaultEnv) {
  if (!defaultEnv) {
    defaultEnv = 'development';
    if (!envVariable) {
      envVariable = 'NODE_ENV';
    }
  }
  this.currentEnv = process.env[envVariable] || defaultEnv;
  this.config = {};
}

/**
 * Sets an environment configuration IF the environment variable's
 * value matches name.
 *
 * @param {String} env The environment to set.
 * @param {Array|Object} objects An array of objects or objects as arguments.
 */
MinConf.prototype.set = function(env, objects) {
  if (!env) throw new Error('Missing required argument: env = environment name');
  if (!objects) throw new Error('Missing required argument: objects');

  if (!(objects instanceof Array)) {
    objects = slice.call(arguments, 1);
  }

  // only load the current environment
  if (this.currentEnv !== env) return this;

  if (!objects) throw new Error('One or more objects required');

  var i, object, length = objects.length;
  for (i = 0; i < length; i++) {
    object = objects[i];
    extend.deepExtend(this.config, object);
  }
  return this;
};

/**
 * Merge objects into the current environment.
 *
 * @param objects {Array[Object]|Object} Array of objects or objects as arguments.
 */
MinConf.prototype.merge = function(objects) {
  if (!(objects instanceof Array)) {
    objects = slice.call(arguments, 0);
  }
  return this.set(this.currentEnv, objects);
};

/**
 * Gets command line arguments using minimist.
 */
var _argv = null;
MinConf.argv = function argv(offset) {
  if (!offset) offset = 2;
  if (!_argv) _argv = require('minimist')(process.argv.slice(offset));
  return _argv;
};

/**
 * Gets environment variable with support for dots '.'.
 */
MinConf.env = function env(offset) {
  var environmentVars = {};

  var root, k, parts, val, i, obj;
  for (k in process.env) {
    if (process.env.hasOwnProperty(k)) {
      val = process.env[k];
      root = environmentVars;

       // create nested objects, eg obj.foo.bar
      if (k.indexOf('.') > 0) {
        parts = k.split('.');
        for (i = 0; i < parts.length - 1; i++) {
          root = root[parts[i]] = {};
        }
        // last part is the key to set
        k = parts[i];
      }

      root[k] = val;
    }
  }
  return environmentVars;
};

MinConf.config = function uber(config) {
  var envs = config._envs;

  var mc = new MinConf(envs._selector || 'NODE_ENV', envs._default || 'development');
  for (var name in envs) {
    if (name === '_selector' || name === '_default') continue;

    var spec = envs[name];
    var parts = spec.split(" ");
    parts.forEach(function(part) {
      var values;
      if (part === 'ARGV') {
        values  = MinConf.argv();
      } else if (part === 'ENV') {
        values = process.env;
      } else if (config[part]) {
        values = config[part];
      } else {
        throw new Error('config[' + name + '] not found');
      }
      mc.set(name, values);
    });
  }

  return mc.config;
};

/**
 * Loads configuration file `config` with an optional `options`. `options` may
 * also be embedded as `_options` in `config`.
 *
 * @param {Object} config The configuration object.
 * @param {Object} options Configuration options.
 */
MinConf.load = function load(config, options) {
  if (!options) options = config._options;
  var cwd = options._cwd || process.cwd();

  var mc = new MinConf(options._selector || 'NODE_ENV', options._default || 'development');
  for (var name in options) {
    // skip minconf settings
    if (name === '_selector' || name === '_default' || name === '_cwd') continue;

    var spec = options[name];
    var parts = spec.split(" ");
    parts.forEach(function(part) {
      var values;
      if (part === 'ARGV') {
        values  = MinConf.argv();
      } else if (part === 'ENV') {
        values = process.env;
      } else if (part.match(/\.(js|json)$/)) {
        // a file which overrides settings
        values = require(npath.resolve(cwd, part));
      } else if (config[part]) {
        values = config[part];
      } else {
        throw new Error('config['  + name + '%s] not found');
      }
      mc.set(name, values);
    });
  }

  return mc;
};

module.exports = MinConf;
<commit_msg>fix split with more than 1 space<commit_after>'use strict';
var slice = [].slice;
var extend = require('./extend');
var npath = require('path');

/**
 * Loads an environment configuration based on the value of an environment
 * variable.
 *
 * @param {String} envVariable Name of environment variable to use. If null
 *                             then the second argument is the config
 *                             name to use. Recommend 'NODE_ENV'.
 * @param {String} defaultEnv The  environment to use if environment
 *                            variable is not set or null.
 */
function MinConf(envVariable, defaultEnv) {
  if (!defaultEnv) {
    defaultEnv = 'development';
    if (!envVariable) {
      envVariable = 'NODE_ENV';
    }
  }
  this.currentEnv = process.env[envVariable] || defaultEnv;
  this.config = {};
}

/**
 * Sets an environment configuration IF the environment variable's
 * value matches name.
 *
 * @param {String} env The environment to set.
 * @param {Array|Object} objects An array of objects or objects as arguments.
 */
MinConf.prototype.set = function(env, objects) {
  if (!env) throw new Error('Missing required argument: env = environment name');
  if (!objects) throw new Error('Missing required argument: objects');

  if (!(objects instanceof Array)) {
    objects = slice.call(arguments, 1);
  }

  // only load the current environment
  if (this.currentEnv !== env) return this;

  if (!objects) throw new Error('One or more objects required');

  var i, object, length = objects.length;
  for (i = 0; i < length; i++) {
    object = objects[i];
    extend.deepExtend(this.config, object);
  }
  return this;
};

/**
 * Merge objects into the current environment.
 *
 * @param objects {Array[Object]|Object} Array of objects or objects as arguments.
 */
MinConf.prototype.merge = function(objects) {
  if (!(objects instanceof Array)) {
    objects = slice.call(arguments, 0);
  }
  return this.set(this.currentEnv, objects);
};

/**
 * Gets command line arguments using minimist.
 */
var _argv = null;
MinConf.argv = function argv(offset) {
  if (!offset) offset = 2;
  if (!_argv) _argv = require('minimist')(process.argv.slice(offset));
  return _argv;
};

/**
 * Gets environment variable with support for dots '.'.
 */
MinConf.env = function env(offset) {
  var environmentVars = {};

  var root, k, parts, val, i, obj;
  for (k in process.env) {
    if (process.env.hasOwnProperty(k)) {
      val = process.env[k];
      root = environmentVars;

       // create nested objects, eg obj.foo.bar
      if (k.indexOf('.') > 0) {
        parts = k.split('.');
        for (i = 0; i < parts.length - 1; i++) {
          root = root[parts[i]] = {};
        }
        // last part is the key to set
        k = parts[i];
      }

      root[k] = val;
    }
  }
  return environmentVars;
};

MinConf.config = function uber(config) {
  var envs = config._envs;

  var mc = new MinConf(envs._selector || 'NODE_ENV', envs._default || 'development');
  for (var name in envs) {
    if (name === '_selector' || name === '_default') continue;

    var spec = envs[name];
    var parts = spec.split(" ");
    parts.forEach(function(part) {
      var values;
      if (part === 'ARGV') {
        values  = MinConf.argv();
      } else if (part === 'ENV') {
        values = process.env;
      } else if (config[part]) {
        values = config[part];
      } else {
        throw new Error('config[' + name + '] not found');
      }
      mc.set(name, values);
    });
  }

  return mc.config;
};

/**
 * Loads configuration file `config` with an optional `options`. `options` may
 * also be embedded as `_options` in `config`.
 *
 * @param {Object} config The configuration object.
 * @param {Object} options Configuration options.
 */
MinConf.load = function load(config, options) {
  if (!options) options = config._options;
  var cwd = options._cwd || process.cwd();

  var mc = new MinConf(options._selector || 'NODE_ENV', options._default || 'development');
  for (var name in options) {
    // skip minconf settings
    if (name === '_selector' || name === '_default' || name === '_cwd') continue;

    var spec = options[name];
    var parts = spec.split(" ");
    parts.forEach(function(part) {

      var values;
      if (part === 'ARGV') {
        values  = MinConf.argv();
      } else if (part === 'ENV') {
        values = process.env;
      } else if (part.match(/\.(js|json)$/)) {
        // a file which overrides settings
        values = require(npath.resolve(cwd, part));
      } else if (config[part]) {
        values = config[part];
      } else if (!part) {
        return;
      } else {
        throw new Error('config['  + name + '] not found');
      }
      mc.set(name, values);
    });
  }

  return mc;
};

module.exports = MinConf;
<|endoftext|>


<commit_before>/*
 Multiple-select directive for AngularJS
 (c) 2013 Alec LaLonde (https://github.com/alalonde/angular-multi-select)
 License: MIT
 */
(function (angular) {
    'use strict';

    angular.module('multi-select', ["template/multiSelect.html"])
        .directive('multiSelect', ['$q', '$parse', function ($q, $parse) {

            function appendSelected(entities) {
                var newEntities = [];
                angular.forEach(entities, function (entity) {
                    var appended = entity;
                    appended.selected = false;
                    newEntities.push(appended);
                });
                return newEntities;
            }

            return {
                restrict: 'E',
                require: 'ngModel',
                scope: {
                    selectedLabel: "@",
                    availableLabel: "@",
                    selectedPlaceholder:"@",
                    availablePlaceholder:"@",
                    available: "=",
                    model: "=ngModel",
                    config: "="
                },
                templateUrl: "template/multiSelect.html",
                link: function (scope, elm, attrs, controllers) {
                    scope.selected = {
                        available: [],
                        current: []
                    };

                    /* Filters out items in original that are also in toFilter. Compares by reference. */
                    function filterOut(original, toFilter) {
                        var filtered = [];
                        angular.forEach(original, function (entity) {
                            var match = false;
                            for (var i = 0; i < toFilter.length; i++) {
                                if (scope.renderItem(toFilter[i]) == scope.renderItem(entity)) {
                                    match = true;
                                    break;
                                }
                            }
                            if (!match) {
                                filtered.push(entity);
                            }
                        });
                        return filtered;
                    }

                    function parseExpression(item, expr) {
                        var displayComponents = expr.match(/(.+)\s+as\s+(.+)/);
                        var ctx = {};
                        ctx[displayComponents[1]] = item;
                        return $parse(displayComponents[2])(ctx);
                    }

                    var requiredMin, inputModel;

                    function ensureMinSelected() {
                        if (requiredMin && scope.model) {
                            scope.numSelected = scope.model.length;
                            inputModel.$setValidity('min', scope.numSelected >= requiredMin);
                        }
                    }

                    scope.refreshAvailable = function () {
                        if (scope.model && scope.available) {
                            scope.available = filterOut(scope.available, scope.model);
                            scope.selected.available = appendSelected(scope.available);
                            scope.selected.current = appendSelected(scope.model);
                        }
                    };

                    scope.add = function () {
                        if (!scope.model.length)
                            scope.model = [];
                        scope.model = scope.model.concat(scope.selected(scope.selected.available));
                    };
                    scope.addAll = function () {
                        if (!scope.model.length) {
                            scope.model = [];
                        }
                        scope.model = scope.model.concat(scope.available);
                    };
                    scope.remove = function () {
                        var selected = scope.selected(scope.selected.current);
                        scope.available = scope.available.concat(selected);
                        scope.model = filterOut(scope.model, selected);
                    };
                    scope.removeAll = function () {
                        scope.available = scope.available.concat(scope.model);
                        scope.model = filterOut(scope.model, scope.model);
                    };
                    scope.selected = function (list) {
                        var found = [];
                        angular.forEach(list, function (item) {
                            if (item.selected === true) found.push(item);
                        });
                        return found;
                    };

                    //Watching the model, updating if the model is a resolved promise
                    scope.watchModel = function () {
                        if (scope.model && scope.model.hasOwnProperty('$promise') && !scope.model.$resolved) {
                            scope.model.then(function (results) {
                                scope.$watch('model', scope.watchModel);
                            });
                        }
                        else {
                            scope.refreshAvailable();
                            scope.$watch('model', scope.refreshAvailable);
                        }
                    };

                    //Watching the list of available items. Updating if it is a resolved promise, and refreshing the
                    //available list if the list has changed
                    var _oldAvailable = {};
                    scope.watchAvailable = function () {
                        if (scope.available && scope.available.hasOwnProperty('$promise') && !scope.available.$resolved) {
                            scope.available.$promise.then(function (results) {
                                scope.$watch('available', scope.watchAvailable);
                            });
                        }
                        else {
                            //We only want to refresh the list if the list of available items has changed
                            //and the variable is defined
                            if (scope.available && scope.available != _oldAvailable) {
                                scope.refreshAvailable();
                                _oldAvailable = scope.available;
                            }
                        }
                    };

                    scope.$watch("available", scope.watchAvailable);
                    scope.$watch("model", scope.watchModel);

                    scope.renderItem = function (item) {
                        return parseExpression(item, attrs.display);
                    };

                    scope.renderTitle = function (item) {
                        if (attrs.title) {
                            return parseExpression(item, attrs.title);
                        }
                        return "";
                    };

                    if (scope.config && angular.isDefined(scope.config.requiredMin)) {
                        var inputs = elm.find("input");
                        var validationInput = angular.element(inputs[inputs.length - 1]);
                        inputModel = validationInput.controller('ngModel');
                    }

                    scope.$watch('config.requiredMin', function (value) {
                        if (angular.isDefined(value)) {
                            requiredMin = parseInt(value, 10);
                            ensureMinSelected();
                        }
                    });

                    scope.$watch('model', function (selected) {
                        ensureMinSelected();
                    });
                }
            };
        }]);

    angular.module("template/multiSelect.html", []).run(["$templateCache", function ($templateCache) {
        $templateCache.put("template/multiSelect.html",
            '
' +
            '
' +
            '{{ availableLabel }} ' +
            '{{ availableLabel==""?"": "(" +available.length +")" }}' +
            '<input ng-model="searchAvailable" class="search" placeholder="{{availablePlaceholder}}">' +
                '
' +
                    '
' +
                        '' +
                            '<input type="checkbox" ng-model="selected.available[$index].selected"> ' +
                         '{{ renderItem(entity) }}' +
                        '' +
                    '
' +
                '
' +
            '
' +

            '
' +
                '<button class="btn mover left" ng-click="remove()" title="Remove selected" ' +
                    'ng-disabled="!selected(selected.current).length">' +
                    '' +
                '</button>' +
                '<button class="btn mover left-all" ng-click="removeAll()" title="Remove selected" ' +
                    'ng-disabled="!model.length">' +
                    '' +
                    '' +
                '</button>' +
                '<button class="btn mover right" ng-click="add()" title="Add selected" ' +
                    'ng-disabled="!selected(selected.available).length">' +
                    '' +
                '</button>' +
                '<button class="btn mover right-all" ng-click="addAll()" title="Add selected" ' +
                    'ng-disabled="!available.length">' +
                    '' +
                    '' +
                '</button>' +
                '
' +
            '
' +
                '{{ selectedLabel }} ' +
                    '{{selectedLabel==""?"":"("+model.length+")"}}' +
                    '<input ng-model="searchSelected" class="search" placeholder="{{selectedPlaceholder}}">' +
                    '
' +
                        '
' +
                           '' +
                               '<input type="checkbox" ng-model="selected.current[$index].selected"> ' +
                                '{{ renderItem(entity) }}' +
                          '' +
                        '
' +
                    '
' +
                    '
' +
            '
' +
            '<input type="number" name="numSelected" ng-model="numSelected" ' +
            'style="display: none">' +
            '
');
    }])
    ;
})(angular);<commit_msg>fix identify and use FA<commit_after>/*
 Multiple-select directive for AngularJS
 (c) 2013 Alec LaLonde (https://github.com/alalonde/angular-multi-select)
 License: MIT
 */
(function (angular) {
    'use strict';

    angular.module('multi-select', ["template/multiSelect.html"])
        .directive('multiSelect', ['$q', '$parse', function ($q, $parse) {

            function appendSelected(entities) {
                var newEntities = [];
                angular.forEach(entities, function (entity) {
                    var appended = entity;
                    appended.selected = false;
                    newEntities.push(appended);
                });
                return newEntities;
            }

            return {
                restrict: 'E',
                require: 'ngModel',
                scope: {
                    selectedLabel: "@",
                    availableLabel: "@",
                    selectedPlaceholder:"@",
                    availablePlaceholder:"@",
                    available: "=",
                    model: "=ngModel",
                    config: "="
                },
                templateUrl: "template/multiSelect.html",
                link: function (scope, elm, attrs, controllers) {
                    scope.selected = {
                        available: [],
                        current: []
                    };

                    /* Filters out items in original that are also in toFilter. Compares by reference. */
                    function filterOut(original, toFilter) {
                        var filtered = [];
                        angular.forEach(original, function (entity) {
                            var match = false;
                            for (var i = 0; i < toFilter.length; i++) {
                                if (scope.identifyItem(toFilter[i]) == scope.identifyItem(entity)) {
                                    match = true;
                                    break;
                                }
                            }
                            if (!match) {
                                filtered.push(entity);
                            }
                        });
                        return filtered;
                    }

                    function parseExpression(item, expr) {
                        var displayComponents = expr.match(/(.+)\s+as\s+(.+)/);
                        var ctx = {};
                        ctx[displayComponents[1]] = item;
                        return $parse(displayComponents[2])(ctx);
                    }

                    var requiredMin, inputModel;

                    function ensureMinSelected() {
                        if (requiredMin && scope.model) {
                            scope.numSelected = scope.model.length;
                            inputModel.$setValidity('min', scope.numSelected >= requiredMin);
                        }
                    }

                    scope.refreshAvailable = function () {
                        if (scope.model && scope.available) {
                            scope.available = filterOut(scope.available, scope.model);
                            scope.selected.available = appendSelected(scope.available);
                            scope.selected.current = appendSelected(scope.model);
                        }
                    };

                    scope.add = function () {
                        if (!scope.model.length && (scope.model.length !==0))
                            scope.model = [];
                        scope.model = scope.model.concat(scope.selected(scope.selected.available));
                    };
                    scope.addAll = function () {
                        if (!scope.model.length && (scope.model.length !==0)) {
                            scope.model = [];
                        }
                        scope.model = scope.model.concat(scope.available);
                    };
                    scope.remove = function () {
                        var selected = scope.selected(scope.selected.current);
                        scope.available = scope.available.concat(selected);
                        scope.model = filterOut(scope.model, selected);
                    };
                    scope.removeAll = function () {
                        scope.available = scope.available.concat(scope.model);
                        scope.model = filterOut(scope.model, scope.model);
                    };
                    scope.selected = function (list) {
                        var found = [];
                        angular.forEach(list, function (item) {
                            if (item.selected === true) found.push(item);
                        });
                        return found;
                    };

                    //Watching the model, updating if the model is a resolved promise
                    scope.watchModel = function () {
                        if (scope.model && scope.model.hasOwnProperty('$promise') && !scope.model.$resolved) {
                            scope.model.then(function (results) {
                                scope.$watch('model', scope.watchModel);
                            });
                        }
                        else {
                            scope.refreshAvailable();
                            scope.$watch('model', scope.refreshAvailable);
                        }
                    };

                    //Watching the list of available items. Updating if it is a resolved promise, and refreshing the
                    //available list if the list has changed
                    var _oldAvailable = {};
                    scope.watchAvailable = function () {
                        if (scope.available && scope.available.hasOwnProperty('$promise') && !scope.available.$resolved) {
                            scope.available.$promise.then(function (results) {
                                scope.$watch('available', scope.watchAvailable);
                            });
                        }
                        else {
                            //We only want to refresh the list if the list of available items has changed
                            //and the variable is defined
                            if (scope.available && scope.available != _oldAvailable) {
                                scope.refreshAvailable();
                                _oldAvailable = scope.available;
                            }
                        }
                    };

                    scope.$watch("available", scope.watchAvailable);
                    scope.$watch("model", scope.watchModel);

                    scope.renderItem = function (item) {
                        return parseExpression(item, attrs.display);
                    };

                    scope.identifyItem = function (item) {
                        return parseExpression(item, attrs.identify);
                    };

                    scope.renderTitle = function (item) {
                        if (attrs.title) {
                            return parseExpression(item, attrs.title);
                        }
                        return "";
                    };

                    if (scope.config && angular.isDefined(scope.config.requiredMin)) {
                        var inputs = elm.find("input");
                        var validationInput = angular.element(inputs[inputs.length - 1]);
                        inputModel = validationInput.controller('ngModel');
                    }

                    scope.$watch('config.requiredMin', function (value) {
                        if (angular.isDefined(value)) {
                            requiredMin = parseInt(value, 10);
                            ensureMinSelected();
                        }
                    });

                    scope.$watch('model', function (selected) {
                        ensureMinSelected();
                    });
                }
            };
        }]);

    angular.module("template/multiSelect.html", []).run(["$templateCache", function ($templateCache) {
        $templateCache.put("template/multiSelect.html",
            '
' +
            '
' +
            '{{ availableLabel }} ' +
            '{{ availableLabel==""?"": "(" +available.length +")" }}' +
            '<input ng-model="searchAvailable" class="search" placeholder="{{availablePlaceholder}}">' +
                '
' +
                    '
' +
                        '' +
                            '<input type="checkbox" ng-model="selected.available[$index].selected"> ' +
                         '{{ renderItem(entity) }}' +
                        '' +
                    '
' +
                '
' +
            '
' +

            '
' +
                '<button class="btn mover right" ng-click="add()" title="Add selected" ' +
                    'ng-disabled="!selected(selected.available).length">' +
                    '' +
                '</button>' +
                '<button class="btn mover right-all" ng-click="addAll()" title="Add selected" ' +
                    'ng-disabled="!available.length">' +
                    '' +
                '</button>' +
                '<button class="btn mover left" ng-click="remove()" title="Remove selected" ' +
                    'ng-disabled="!selected(selected.current).length">' +
                    '' +
                '</button>' +
                '<button class="btn mover left-all" ng-click="removeAll()" title="Remove selected" ' +
                    'ng-disabled="!model.length">' +
                    '' +
                '</button>' +

                '
' +
            '
' +
                '{{ selectedLabel }} ' +
                    '{{selectedLabel==""?"":"("+model.length+")"}}' +
                    '<input ng-model="searchSelected" class="search" placeholder="{{selectedPlaceholder}}">' +
                    '
' +
                        '
' +
                           '' +
                               '<input type="checkbox" ng-model="selected.current[$index].selected"> ' +
                                '{{ renderItem(entity) }}' +
                          '' +
                        '
' +
                    '
' +
                    '
' +
            '
' +
            '<input type="number" name="numSelected" ng-model="numSelected" ' +
            'style="display: none">' +
            '
');
    }])
    ;
})(angular);
<|endoftext|>


<commit_before>/*
 Multiple-select directive for AngularJS
 (c) 2013 Alec LaLonde (https://github.com/alalonde/angular-multi-select)
 License: MIT
*/
(function(angular) {
  'use strict';
  angular.module('multi-select', [])

  .directive('multiSelect', ['$q', '$parse', function($q, $parse) {

    function appendSelected(entities) {
      var newEntities = [];
      angular.forEach(entities, function(entity) {
        var appended = entity;
        appended.selected = false;
        newEntities.push(appended);
      });
      return newEntities;
    }

    return {
      restrict: 'E',
      require: 'ngModel',
      scope: {
        selectedLabel: "@",
        availableLabel: "@",
        available: "=",
        model: "=ngModel",
        config: "="
      },
      template: '
' +
                  '
' +
                    '{{ selectedLabel }} ' +
                        '({{ model.length }})' +
                    '
' +
                      '
' +
                        '' +
                          '<input type="checkbox" ng-model="selected.current[$index].selected"> ' +
                          '{{ renderItem(entity) }}' +
                        '' +
                      '
' +
                    '
' +
                  '
' +
                  '
' +
                    '<button class="btn mover left" ng-click="add()" title="Add selected" ' +
                        'ng-disabled="!selected(selected.available).length">' +
                      '' +
                    '</button>' +
                    '<button class="btn mover right" ng-click="remove()" title="Remove selected" ' +
                        'ng-disabled="!selected(selected.current).length">' +
                      '' +
                    '</button>' +
                  '
' +
                  '
' +
                    '{{ availableLabel }} ' +
                        '({{ available.length }})' +
                    '
' +
                      '
' +
                        '' +
                          '<input type="checkbox" ng-model="selected.available[$index].selected"> ' +
                          '{{ renderItem(entity) }}' +
                        '' +
                      '
' +
                    '
' +
                  '
' +
                  '<input type="number" name="numSelected" ng-model="numSelected" ' +
                      'style="display: none">' +
                '
',
      link: function(scope, elm, attrs, controllers) {
        scope.selected = {
          available: [],
          current: []
        };

        /* Handles cases where scope data hasn't been initialized yet */
        var dataLoading = function(scopeAttr) {
          var loading = $q.defer();
          if((scope[scopeAttr] && !scope[scopeAttr].hasOwnProperty('$promise')) ||
              (scope[scopeAttr] && scope[scopeAttr].length > 0)) {
            loading.resolve(scope[scopeAttr]);
          } else {
            scope.$watch(scopeAttr, function(newValue, oldValue) {
              if(newValue && newValue.length > 0)
                loading.resolve(newValue);
            });
          }
          return loading.promise;
        };

        /* Filters out items in original that are also in toFilter. Compares by reference. */
        function filterOut(original, toFilter) {
          var filtered = [];
          angular.forEach(original, function(entity) {
            var match = false;
            for(var i = 0; i < toFilter.length; i++) {
              if(scope.renderItem(toFilter[i]) == scope.renderItem(entity)) {
                match = true;
                break;
              }
            }
            if(!match) {
              filtered.push(entity);
            }
          });
          return filtered;
        }

        function parseExpression(item, expr) {
          var displayComponents = expr.match(/(.+)\s+as\s+(.+)/);
          var ctx = {};
          ctx[displayComponents[1]] = item;
          return $parse(displayComponents[2])(ctx);
        }

        var requiredMin, inputModel;
        function ensureMinSelected() {
          if(requiredMin && scope.model) {
            scope.numSelected = scope.model.length;
            inputModel.$setValidity('min', scope.numSelected >= requiredMin);
          }
        }

        scope.refreshAvailable = function() {
          scope.available = filterOut(scope.available, scope.model);
          scope.selected.available = appendSelected(scope.available);
          scope.selected.current = appendSelected(scope.model);
        };

        scope.add = function() {
          scope.model = scope.model.concat(scope.selected(scope.selected.available));
        };
        scope.remove = function() {
          var selected = scope.selected(scope.selected.current);
          scope.available = scope.available.concat(selected);
          scope.model = filterOut(scope.model, selected);
        };
        scope.selected = function(list) {
          var found = [];
          angular.forEach(list, function(item) { if(item.selected === true) found.push(item); });
          return found;
        };

        $q.all([dataLoading("model"), dataLoading("available")]).then(function(results) {
          scope.refreshAvailable();
          scope.$watch('model', scope.refreshAvailable);
        });

        scope.renderItem = function(item) {
          return parseExpression(item, attrs.display);
        };

        scope.renderTitle = function(item) {
          if(attrs.title) {
            return parseExpression(item, attrs.title);
          }
          return "";
        };

        if(scope.config && angular.isDefined(scope.config.requiredMin)) {
          var inputs = elm.find("input");
          var validationInput = angular.element(inputs[inputs.length - 1]);
          inputModel = validationInput.controller('ngModel');
        }

        scope.$watch('config.requiredMin', function(value) {
          if(angular.isDefined(value)) {
            requiredMin = parseInt(value, 10);
            ensureMinSelected();
          }
        });

        scope.$watch('model', function(selected) {
          ensureMinSelected();
        });
      }
    };
  }])
  ;
})(angular);<commit_msg>Extracted template into separate module<commit_after>/*
 Multiple-select directive for AngularJS
 (c) 2013 Alec LaLonde (https://github.com/alalonde/angular-multi-select)
 License: MIT
*/
(function(angular) {
  'use strict';

  angular.module('multi-select', ["template/multiSelect.html"])
  .directive('multiSelect', ['$q', '$parse', function($q, $parse) {

    function appendSelected(entities) {
      var newEntities = [];
      angular.forEach(entities, function(entity) {
        var appended = entity;
        appended.selected = false;
        newEntities.push(appended);
      });
      return newEntities;
    }

    return {
      restrict: 'E',
      require: 'ngModel',
      scope: {
        selectedLabel: "@",
        availableLabel: "@",
        available: "=",
        model: "=ngModel",
        config: "="
      },
      templateUrl: "template/multiSelect.html",
      link: function(scope, elm, attrs, controllers) {
        scope.selected = {
          available: [],
          current: []
        };

        /* Handles cases where scope data hasn't been initialized yet */
        var dataLoading = function(scopeAttr) {
          var loading = $q.defer();
          if((scope[scopeAttr] && !scope[scopeAttr].hasOwnProperty('$promise')) ||
              (scope[scopeAttr] && scope[scopeAttr].length > 0)) {
            loading.resolve(scope[scopeAttr]);
          } else {
            scope.$watch(scopeAttr, function(newValue, oldValue) {
              if(newValue && newValue.length > 0)
                loading.resolve(newValue);
            });
          }
          return loading.promise;
        };

        /* Filters out items in original that are also in toFilter. Compares by reference. */
        function filterOut(original, toFilter) {
          var filtered = [];
          angular.forEach(original, function(entity) {
            var match = false;
            for(var i = 0; i < toFilter.length; i++) {
              if(scope.renderItem(toFilter[i]) == scope.renderItem(entity)) {
                match = true;
                break;
              }
            }
            if(!match) {
              filtered.push(entity);
            }
          });
          return filtered;
        }

        function parseExpression(item, expr) {
          var displayComponents = expr.match(/(.+)\s+as\s+(.+)/);
          var ctx = {};
          ctx[displayComponents[1]] = item;
          return $parse(displayComponents[2])(ctx);
        }

        var requiredMin, inputModel;
        function ensureMinSelected() {
          if(requiredMin && scope.model) {
            scope.numSelected = scope.model.length;
            inputModel.$setValidity('min', scope.numSelected >= requiredMin);
          }
        }

        scope.refreshAvailable = function() {
          scope.available = filterOut(scope.available, scope.model);
          scope.selected.available = appendSelected(scope.available);
          scope.selected.current = appendSelected(scope.model);
        };

        scope.add = function() {
          scope.model = scope.model.concat(scope.selected(scope.selected.available));
        };
        scope.remove = function() {
          var selected = scope.selected(scope.selected.current);
          scope.available = scope.available.concat(selected);
          scope.model = filterOut(scope.model, selected);
        };
        scope.selected = function(list) {
          var found = [];
          angular.forEach(list, function(item) { if(item.selected === true) found.push(item); });
          return found;
        };

        $q.all([dataLoading("model"), dataLoading("available")]).then(function(results) {
          scope.refreshAvailable();
          scope.$watch('model', scope.refreshAvailable);
        });

        scope.renderItem = function(item) {
          return parseExpression(item, attrs.display);
        };

        scope.renderTitle = function(item) {
          if(attrs.title) {
            return parseExpression(item, attrs.title);
          }
          return "";
        };

        if(scope.config && angular.isDefined(scope.config.requiredMin)) {
          var inputs = elm.find("input");
          var validationInput = angular.element(inputs[inputs.length - 1]);
          inputModel = validationInput.controller('ngModel');
        }

        scope.$watch('config.requiredMin', function(value) {
          if(angular.isDefined(value)) {
            requiredMin = parseInt(value, 10);
            ensureMinSelected();
          }
        });

        scope.$watch('model', function(selected) {
          ensureMinSelected();
        });
      }
    };
  }]);

  angular.module("template/multiSelect.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("template/multiSelect.html",
      '
' +
        '
' +
          '{{ selectedLabel }} ' +
              '({{ model.length }})' +
          '
' +
            '
' +
              '' +
                '<input type="checkbox" ng-model="selected.current[$index].selected"> ' +
                '{{ renderItem(entity) }}' +
              '' +
            '
' +
          '
' +
        '
' +
        '
' +
          '<button class="btn mover left" ng-click="add()" title="Add selected" ' +
              'ng-disabled="!selected(selected.available).length">' +
            '' +
          '</button>' +
          '<button class="btn mover right" ng-click="remove()" title="Remove selected" ' +
              'ng-disabled="!selected(selected.current).length">' +
            '' +
          '</button>' +
        '
' +
        '
' +
          '{{ availableLabel }} ' +
              '({{ available.length }})' +
          '
' +
            '
' +
              '' +
                '<input type="checkbox" ng-model="selected.available[$index].selected"> ' +
                '{{ renderItem(entity) }}' +
              '' +
            '
' +
          '
' +
        '
' +
        '<input type="number" name="numSelected" ng-model="numSelected" ' +
            'style="display: none">' +
      '
');
  }])
  ;
})(angular);<|endoftext|>