<commit_before>"use strict";
var	ack = require('../../index.js'),
	multiparty = require('multiparty'),//used to parse inbound request form multi/part post variables
	cookies = require('cookies'),
	url = require('url')

//Request Return : Object to handle processing request (process client input, uploads, paths)
var reqrtn = function(req,res){
	if(req && req._headers){
		throw new Error('Received response object where request object was expected');
	}

	this.req = req
	this.res = res
	this.data = {
		//localHostIpArray:['localhost','127.0.0.1','::1']
	}
	this.isClientInputLoaded = false
	return this
}

reqrtn.prototype.getMethod = function(){
	return this.req.method
}

reqrtn.prototype.getUserAgent = function(){
	return this.input().headers().get('User-Agent')
}

reqrtn.prototype.referer = function(){
	return this.input().headers().get('Referer')
}

reqrtn.prototype.getPort = function(){
	var cKey = this.req.socket.server._connectionKey
	return cKey.split(':').pop()
}

reqrtn.prototype.ip = function(ip){
	if(ip){
		/* reset all known places the ip address might be stored */
			if(this.req.connection){
				if(this.req.connection.remoteAddress){
					this.req.connection.remoteAddress = ip
				}

				if(this.req.connection.socket && this.req.connection.socket.remoteAddress){
					this.req.connection.socket.remoteAddress = ip
				}
			}
			if(this.req.socket && this.req.socket.remoteAddress){
				this.req.socket.remoteAddress = ip
			}
		/* end */

		return ip
	}

	if(this.req.connection){
		if(this.req.connection.remoteAddress){
			return this.req.connection.remoteAddress
		}

		if(this.req.connection.socket && this.req.connection.socket.remoteAddress){
			return this.req.connection.socket.remoteAddress
		}
	}
	if(this.req.socket && this.req.socket.remoteAddress){
		return this.req.socket.remoteAddress
	}
}

reqrtn.prototype.isLocalNetwork = function(yesNo){
	if(yesNo!=null){
		this.data.localNetwork = yesNo;
		return this.data.localNetwork
	}

	if(this.data.localNetwork!=null){
		return this.data.localNetwork;
	}

	var ip = this.ip()
	this.data.localNetwork = this.isLocalHost() || (ip && ack.ip(ip).isPrivate());
	return this.data.localNetwork
}

/** is current request from the local-box */
reqrtn.prototype.isLocalHost = function(yesNo){
	if(yesNo!=null){
		this.data.isLocalHost = yesNo;
		return this.data.isLocalHost
	}
	if(this.data.isLocalHost!=null){
		return this.data.isLocalHost
	}

	var ip = this.ip();
	if(ip && ip.toLowerCase){
		ip = ip.toLowerCase()
		this.data.isLocalHost = ack.ip(ip).isPrivate()//this.data.localHostIpArray.indexOf(ip) >= 0
		return this.data.isLocalHost;
	}

	return false
}

reqrtn.prototype.acceptsHtml = function(){
	var accept = this.input().headers().get('Accept')
	if(accept && accept.search('html')>=0){
		return true
	}

	return false
}

reqrtn.prototype.isHttps = function(){
	return this.req.connection!=null && this.req.connection.encrypted!=null
}

//request folder path
reqrtn.prototype.Path = function(){
	var p = new reqrtn.path(this.req)// || this.req.url
	this.Path = function(){return p}//morph to deliver cache
	return p
}

reqrtn.prototype.path = function(){
	ack.deprecated('reqrtn.path() deprecated. Use reqrtn.Path()')
	return this.Path()
}

reqrtn.prototype.relativePath = function(){
	return this.Path().relative
}

/** options={isHttps:true|false} */
reqrtn.prototype.absoluteUrl = function(options){
	options = options || {}
	var protocol = 'http'
	var isHttps = options.isHttps || this.isHttps()
	if(isHttps){
		protocol += 's';
	}
	var relPath = this.req.originalUrl || this.req.url || ''

	var port = ''
	if(options.port){
		port = ':'+ options.port
	}else{
		var myPort = this.getPort()
		if(typeof(options.port)=='undefined' && myPort!=80 && myPort!=443){
			port = ':'+ myPort
		}
	}

	var abUrl = protocol + '://' + this.getHostName() + port + relPath
	return abUrl
}

//www.websitename.com
reqrtn.prototype.getHostName = function(){
	if(this.req.vhost && this.req.vhost.hostname){
		return this.req.vhost.hostname
	}

	var host = this.getHost()
	return host.split(':').shift()
}
reqrtn.prototype.hostName = reqrtn.prototype.getHostName//deprecated naming convention

//www.websitename.com:80
reqrtn.prototype.getHost = function(){
	if(this.req.vhost && this.req.vhost.host){
		return this.req.vhost.host
	}

	return this.input().headers().get('host')
}

//client input
reqrtn.prototype.input = function(){
	//if(!this.data.clientInput)throw 'Client input not yet loaded'
	if(!this.data.clientInput){
		this.data.clientInput = new reqrtn.clientInput(this.req, this.res)
	}
	return this.data.clientInput
}
//deprecated reference
reqrtn.prototype.ci = reqrtn.prototype.input

reqrtn.prototype.loadClientInput = function(){
	return ack.promise()
	.bind(this)
	.then(function(){
		this.data.clientInput = new reqrtn.clientInput(this.req, this.res)

		if(!this.req.body){
			return this.data.clientInput.parseFormVars().bind(this).set(this.data.clientInput)
		}

		return this.data.clientInput
	})
}
reqrtn.prototype.clientInput = reqrtn.prototype.loadClientInput

/**
	client file upload
	returns promise of upload result
*/
reqrtn.prototype.uploadByName = function(name, options){
	return ack.promise().bind(this).callback(function(next){
		ack.router().uploadByName(name,options)(this.req, this.res, next)
	})
	.then(function(){
		return this.req.files
	})
}

/** returns promise of upload result */
reqrtn.prototype.uploadOneByName = function(name, options){
	return ack.promise().bind(this).callback(function(next){
		ack.router().uploadOneByName(name,options)(this.req, this.res, next)
	})
	.then(function(){
		if(this.req[name])return this.req[name]
	})
}

reqrtn.prototype.isMultipart = function(){
	var ct = this.input().headers().get('content-type')
	return ct && (ct=='multipart/form-data' || ct.substring(0, 9)=='multipart') ? true : false
}

//!!!Non-prototypes below

//express request handler with array shifting
reqrtn.processArray = function(req, res, nextarray){
	if(!nextarray || !nextarray.length)return
	var proc = nextarray.shift()
	proc(req, res, function(){
		reqrtn.processArray(req,res,nextarray)
	})
}

//path component to aid in reading the request path
reqrtn.path = function(req){
	var oUrl = req.originalUrl || req.url
	this.string = oUrl.split('?')[0]
	this.relative = req.url.split('?')[0]
	return this
}

reqrtn.path.prototype.getString = function(){
	return this.string
}

reqrtn.path.prototype.array = function(){
	var s = this.string.trim().split('/')
	while(typeof(s[0]) != 'undefined' && s[0].length==0)s.shift()//remove opening empty-strings
	while(typeof(s[s.length-1]) != 'undefined' && s[s.length-1].length==0)s.pop()//remove ending empty-strings
	return s
}

//client input object
reqrtn.ci = function(a){
	this.data = a || {}
	return this
}

reqrtn.ci.prototype.dump = function(options){
	return nodedump.dump(this.data,options)
}

//full client input (includes extra form & url original ref)
reqrtn.clientInput = function(req, res){
	this.req = req;this.res = res
	this.data = {}
	return this
}

reqrtn.clientInput.prototype.combined = function(){
	if(this.Combined){
		return this.Combined
	}

	this.Combined = ack.accessors()
	this.Combined.set( this.url().data )

	if(this.req.body){
		this.Combined.set(this.req.body)//form post vars pasted ontop url vars
	}

	return this.Combined
}

reqrtn.clientInput.prototype.combine = function(varsOrScopeName1, varsOrScopeName2, varsOrScopeName3){
	var combined = ack.accessors()
		,args = Array.prototype.slice.call(arguments)

	for(var x=0; x < args.length; ++x){
		if(args[x]){
			if(args[x].constructor==String){
				switch(args[x]){
					case 'query':case 'queries':case 'url':case 'urls':
						combined.set(this.req.query)
					break;

					case 'form':case 'forms':case 'body':case 'bodies':
						if(this.req.body){
							combined.set(this.req.body)//form post vars pasted ontop url vars
						}
					break;

					case 'cookie':case 'cookies':
						combined.set( this.cookies().all() )//form post vars pasted ontop url vars
					break;

					case 'header':case 'headers':
						combined.set( this.req.headers )//form post vars pasted ontop url vars
					break;
				}
			}else{
				combined.set(args[x])
			}
		}
	}

	return combined
}

reqrtn.clientInput.prototype.getAuthBearer = function(){
	var authString = this.headers().get('Authorization')
	if(authString){
		return authString.replace(/^[^ \r\n\t]+[ \r\n\t]/g,'')//Authorization: Bearer TOKEN_STRING
	}
}

reqrtn.clientInput.prototype.cookies = function(){
	if(this.Cookies)return this.Cookies
	this.Cookies = new cookies(this.req, this.res)
	this.Cookies.all = function(){
		if(this.request.headers.cookie){
			var data={}, rows=this.request.headers.cookie.split(';')

			if(rows.constructor==Array){
				rows.forEach(function(v,i){
					rows[i] = v.split(';')[0]
				})
			}

			rows.forEach(function(row,i){
				var twoPiece = row.split('=')
				data[twoPiece[0].trim()] = twoPiece.length>1 ? twoPiece[1].trim() : null
			})
			return data
		}
	}
	return this.Cookies
}

reqrtn.clientInput.prototype.headers = function(){
	if(this.Headers)return this.Headers
	this.Headers = ack.accessors(this.req.headers)// get/set methods and convenient dump method
	return this.Headers
}

reqrtn.clientInput.prototype.url = function(){
	if(this.Url){
		return this.Url
	}

	if(!this.req.query){//url variables already parsed
		var urlString = this.req.url || this.req.originalUrl
		this.req.query = url.parse(urlString, true).query
		ack( this.req.query ).nullsToEmptyString()
	}

	this.Url = ack.accessors(this.req.query)// get/set methods and convenient dump method
	return this.Url
}

reqrtn.clientInput.prototype.multipart = function(){
	if(this.Multipart)return this.Multipart
	if(!this.req.body){
		throw new Error('multipart form variables have not yet been parsed! req.body is '+typeof(this.req.body));
	}
	this.Multipart = ack.accessors(this.req.body)// get/set methods and convenient dump method
	return this.Form
}

reqrtn.clientInput.prototype.form = function(){
	if(!this.req.body){
		var msg = 'form variables have not yet been parsed! req.body is '+typeof(this.req.body)+'. req is '+typeof(this.req)
		throw new Error(msg);
	}
	this.Form = ack.accessors(this.req.body)// get/set methods and convenient dump method
	this.form = function(){return this.Form}
	return this.Form
}

/** returns promise - {fields},{files}
 Warning, once engaged during a request, no other multi-part parser will be able to see the multi-part
*/
reqrtn.clientInput.prototype.parseMultiPart = function(){
	var Form = new multiparty.Form()
	return ack.promise().set(this.req).bind(Form).callback(Form.parse)
}

/** all multipart fields are added to request.body */
reqrtn.clientInput.prototype.mergeBodyAndMultipart = function(nullAsEmptyString){
	var promise = ack.promise().bind(this)
	var isMultipart = new reqrtn(this.req, this.res).isMultipart()
	if(isMultipart){
		promise = promise.then(this.parseMultiPart)
		.then(function(fields, files){
			this.data.multipart = {fields:fields, files:files}
			for(var x in fields){
				this.req.body[x] = fields[x][0]
			}
		})

		//null/undefined to empty-string
		if(nullAsEmptyString===null || nullAsEmptyString){
			promise = promise.then( this.nullsToEmptyString )
		}
	}

	return promise
	.then(function(){
		if(!this.req.body){
			this.req.body = {}
		}

		return [this.form(), this.req.body]
	})
	.spread()//cast array to positional-arguments
}

reqrtn.clientInput.prototype.nullsToEmptyString = function(){
	ack(this.req.body).nullsToEmptyString()
}

//returns promise
reqrtn.clientInput.prototype.parseFormVars = function(nullAsEmptyString){
	return ack.promise().bind(this).callback(function(callback){
		ack.router().parseBody({
			nullAsEmptyString:nullAsEmptyString==null?true:nullAsEmptyString
		})(this.req,this.res,callback)
	})
  .then(function(){
    if(!this.req.body){//maybe bad idea to default the body
      this.req.body = {}
    }

    return [this.form(), this.req.body]
  })
  .spread()//cast array to positional-arguments
}

module.exports = reqrtn<commit_msg>added getHostBase<commit_after>"use strict";
var	ack = require('../../index.js'),
	multiparty = require('multiparty'),//used to parse inbound request form multi/part post variables
	cookies = require('cookies'),
	url = require('url')

//Request Return : Object to handle processing request (process client input, uploads, paths)
var reqrtn = function(req,res){
	if(req && req._headers){
		throw new Error('Received response object where request object was expected');
	}

	this.req = req
	this.res = res
	this.data = {
		//localHostIpArray:['localhost','127.0.0.1','::1']
	}
	this.isClientInputLoaded = false
	return this
}

reqrtn.prototype.getMethod = function(){
	return this.req.method
}

reqrtn.prototype.getUserAgent = function(){
	return this.input().headers().get('User-Agent')
}

reqrtn.prototype.referer = function(){
	return this.input().headers().get('Referer')
}

reqrtn.prototype.getPort = function(){
	var cKey = this.req.socket.server._connectionKey
	return cKey.split(':').pop()
}

reqrtn.prototype.ip = function(ip){
	if(ip){
		/* reset all known places the ip address might be stored */
			if(this.req.connection){
				if(this.req.connection.remoteAddress){
					this.req.connection.remoteAddress = ip
				}

				if(this.req.connection.socket && this.req.connection.socket.remoteAddress){
					this.req.connection.socket.remoteAddress = ip
				}
			}
			if(this.req.socket && this.req.socket.remoteAddress){
				this.req.socket.remoteAddress = ip
			}
		/* end */

		return ip
	}

	if(this.req.connection){
		if(this.req.connection.remoteAddress){
			return this.req.connection.remoteAddress
		}

		if(this.req.connection.socket && this.req.connection.socket.remoteAddress){
			return this.req.connection.socket.remoteAddress
		}
	}
	if(this.req.socket && this.req.socket.remoteAddress){
		return this.req.socket.remoteAddress
	}
}

reqrtn.prototype.isLocalNetwork = function(yesNo){
	if(yesNo!=null){
		this.data.localNetwork = yesNo;
		return this.data.localNetwork
	}

	if(this.data.localNetwork!=null){
		return this.data.localNetwork;
	}

	var ip = this.ip()
	this.data.localNetwork = this.isLocalHost() || (ip && ack.ip(ip).isPrivate());
	return this.data.localNetwork
}

/** is current request from the local-box */
reqrtn.prototype.isLocalHost = function(yesNo){
	if(yesNo!=null){
		this.data.isLocalHost = yesNo;
		return this.data.isLocalHost
	}
	if(this.data.isLocalHost!=null){
		return this.data.isLocalHost
	}

	var ip = this.ip();
	if(ip && ip.toLowerCase){
		ip = ip.toLowerCase()
		this.data.isLocalHost = ack.ip(ip).isPrivate()//this.data.localHostIpArray.indexOf(ip) >= 0
		return this.data.isLocalHost;
	}

	return false
}

reqrtn.prototype.acceptsHtml = function(){
	var accept = this.input().headers().get('Accept')
	if(accept && accept.search('html')>=0){
		return true
	}

	return false
}

reqrtn.prototype.isHttps = function(){
	return this.req.connection!=null && this.req.connection.encrypted!=null
}

//request folder path
reqrtn.prototype.Path = function(){
	var p = new reqrtn.path(this.req)// || this.req.url
	this.Path = function(){return p}//morph to deliver cache
	return p
}

reqrtn.prototype.path = function(){
	ack.deprecated('reqrtn.path() deprecated. Use reqrtn.Path()')
	return this.Path()
}

reqrtn.prototype.relativePath = function(){
	return this.Path().relative
}

/** http://domain.com:80 */
reqrtn.prototype.getHostUrl = function(options){
	options = options || {}
	var protocol = 'http'
	var isHttps = options.isHttps || this.isHttps()
	if(isHttps){
		protocol += 's';
	}

	var port = ''
	if(options.port){
		port = ':'+ options.port
	}else{
		var myPort = this.getPort()
		if(typeof(options.port)=='undefined' && myPort!=80 && myPort!=443){
			port = ':'+ myPort
		}
	}

	return protocol + '://' + this.getHostName() + port
}

/** http://domain.com:80/path
	@options={isHttps:true|false}
*/
reqrtn.prototype.absoluteUrl = function(options){
	var relPath = this.req.originalUrl || this.req.url || ''

	var abUrl = this.getHostUrl(options) + relPath
	return abUrl
}

//www.websitename.com
reqrtn.prototype.getHostName = function(){
	if(this.req.vhost && this.req.vhost.hostname){
		return this.req.vhost.hostname
	}

	var host = this.getHost()
	return host.split(':').shift()
}
reqrtn.prototype.hostName = reqrtn.prototype.getHostName//deprecated naming convention

//www.websitename.com:80
reqrtn.prototype.getHost = function(){
	if(this.req.vhost && this.req.vhost.host){
		return this.req.vhost.host
	}

	return this.input().headers().get('host')
}

//client input
reqrtn.prototype.input = function(){
	//if(!this.data.clientInput)throw 'Client input not yet loaded'
	if(!this.data.clientInput){
		this.data.clientInput = new reqrtn.clientInput(this.req, this.res)
	}
	return this.data.clientInput
}
//deprecated reference
reqrtn.prototype.ci = reqrtn.prototype.input

reqrtn.prototype.loadClientInput = function(){
	return ack.promise()
	.bind(this)
	.then(function(){
		this.data.clientInput = new reqrtn.clientInput(this.req, this.res)

		if(!this.req.body){
			return this.data.clientInput.parseFormVars().bind(this).set(this.data.clientInput)
		}

		return this.data.clientInput
	})
}
reqrtn.prototype.clientInput = reqrtn.prototype.loadClientInput

/**
	client file upload
	returns promise of upload result
*/
reqrtn.prototype.uploadByName = function(name, options){
	return ack.promise().bind(this).callback(function(next){
		ack.router().uploadByName(name,options)(this.req, this.res, next)
	})
	.then(function(){
		return this.req.files
	})
}

/** returns promise of upload result */
reqrtn.prototype.uploadOneByName = function(name, options){
	return ack.promise().bind(this).callback(function(next){
		ack.router().uploadOneByName(name,options)(this.req, this.res, next)
	})
	.then(function(){
		if(this.req[name])return this.req[name]
	})
}

reqrtn.prototype.isMultipart = function(){
	var ct = this.input().headers().get('content-type')
	return ct && (ct=='multipart/form-data' || ct.substring(0, 9)=='multipart') ? true : false
}

//!!!Non-prototypes below

//express request handler with array shifting
reqrtn.processArray = function(req, res, nextarray){
	if(!nextarray || !nextarray.length)return
	var proc = nextarray.shift()
	proc(req, res, function(){
		reqrtn.processArray(req,res,nextarray)
	})
}

//path component to aid in reading the request path
reqrtn.path = function(req){
	var oUrl = req.originalUrl || req.url
	this.string = oUrl.split('?')[0]
	this.relative = req.url.split('?')[0]
	return this
}

reqrtn.path.prototype.getString = function(){
	return this.string
}

reqrtn.path.prototype.array = function(){
	var s = this.string.trim().split('/')
	while(typeof(s[0]) != 'undefined' && s[0].length==0)s.shift()//remove opening empty-strings
	while(typeof(s[s.length-1]) != 'undefined' && s[s.length-1].length==0)s.pop()//remove ending empty-strings
	return s
}

//client input object
reqrtn.ci = function(a){
	this.data = a || {}
	return this
}

reqrtn.ci.prototype.dump = function(options){
	return nodedump.dump(this.data,options)
}

//full client input (includes extra form & url original ref)
reqrtn.clientInput = function(req, res){
	this.req = req;this.res = res
	this.data = {}
	return this
}

reqrtn.clientInput.prototype.combined = function(){
	if(this.Combined){
		return this.Combined
	}

	this.Combined = ack.accessors()
	this.Combined.set( this.url().data )

	if(this.req.body){
		this.Combined.set(this.req.body)//form post vars pasted ontop url vars
	}

	return this.Combined
}

reqrtn.clientInput.prototype.combine = function(varsOrScopeName1, varsOrScopeName2, varsOrScopeName3){
	var combined = ack.accessors()
		,args = Array.prototype.slice.call(arguments)

	for(var x=0; x < args.length; ++x){
		if(args[x]){
			if(args[x].constructor==String){
				switch(args[x]){
					case 'query':case 'queries':case 'url':case 'urls':
						combined.set(this.req.query)
					break;

					case 'form':case 'forms':case 'body':case 'bodies':
						if(this.req.body){
							combined.set(this.req.body)//form post vars pasted ontop url vars
						}
					break;

					case 'cookie':case 'cookies':
						combined.set( this.cookies().all() )//form post vars pasted ontop url vars
					break;

					case 'header':case 'headers':
						combined.set( this.req.headers )//form post vars pasted ontop url vars
					break;
				}
			}else{
				combined.set(args[x])
			}
		}
	}

	return combined
}

reqrtn.clientInput.prototype.getAuthBearer = function(){
	var authString = this.headers().get('Authorization')
	if(authString){
		return authString.replace(/^[^ \r\n\t]+[ \r\n\t]/g,'')//Authorization: Bearer TOKEN_STRING
	}
}

reqrtn.clientInput.prototype.cookies = function(){
	if(this.Cookies)return this.Cookies
	this.Cookies = new cookies(this.req, this.res)
	this.Cookies.all = function(){
		if(this.request.headers.cookie){
			var data={}, rows=this.request.headers.cookie.split(';')

			if(rows.constructor==Array){
				rows.forEach(function(v,i){
					rows[i] = v.split(';')[0]
				})
			}

			rows.forEach(function(row,i){
				var twoPiece = row.split('=')
				data[twoPiece[0].trim()] = twoPiece.length>1 ? twoPiece[1].trim() : null
			})
			return data
		}
	}
	return this.Cookies
}

reqrtn.clientInput.prototype.headers = function(){
	if(this.Headers)return this.Headers
	this.Headers = ack.accessors(this.req.headers)// get/set methods and convenient dump method
	return this.Headers
}

reqrtn.clientInput.prototype.url = function(){
	if(this.Url){
		return this.Url
	}

	if(!this.req.query){//url variables already parsed
		var urlString = this.req.url || this.req.originalUrl
		this.req.query = url.parse(urlString, true).query
		ack( this.req.query ).nullsToEmptyString()
	}

	this.Url = ack.accessors(this.req.query)// get/set methods and convenient dump method
	return this.Url
}

reqrtn.clientInput.prototype.multipart = function(){
	if(this.Multipart)return this.Multipart
	if(!this.req.body){
		throw new Error('multipart form variables have not yet been parsed! req.body is '+typeof(this.req.body));
	}
	this.Multipart = ack.accessors(this.req.body)// get/set methods and convenient dump method
	return this.Form
}

reqrtn.clientInput.prototype.form = function(){
	if(!this.req.body){
		var msg = 'form variables have not yet been parsed! req.body is '+typeof(this.req.body)+'. req is '+typeof(this.req)
		throw new Error(msg);
	}
	this.Form = ack.accessors(this.req.body)// get/set methods and convenient dump method
	this.form = function(){return this.Form}
	return this.Form
}

/** returns promise - {fields},{files}
 Warning, once engaged during a request, no other multi-part parser will be able to see the multi-part
*/
reqrtn.clientInput.prototype.parseMultiPart = function(){
	var Form = new multiparty.Form()
	return ack.promise().set(this.req).bind(Form).callback(Form.parse)
}

/** all multipart fields are added to request.body */
reqrtn.clientInput.prototype.mergeBodyAndMultipart = function(nullAsEmptyString){
	var promise = ack.promise().bind(this)
	var isMultipart = new reqrtn(this.req, this.res).isMultipart()
	if(isMultipart){
		promise = promise.then(this.parseMultiPart)
		.then(function(fields, files){
			this.data.multipart = {fields:fields, files:files}
			for(var x in fields){
				this.req.body[x] = fields[x][0]
			}
		})

		//null/undefined to empty-string
		if(nullAsEmptyString===null || nullAsEmptyString){
			promise = promise.then( this.nullsToEmptyString )
		}
	}

	return promise
	.then(function(){
		if(!this.req.body){
			this.req.body = {}
		}

		return [this.form(), this.req.body]
	})
	.spread()//cast array to positional-arguments
}

reqrtn.clientInput.prototype.nullsToEmptyString = function(){
	ack(this.req.body).nullsToEmptyString()
}

//returns promise
reqrtn.clientInput.prototype.parseFormVars = function(nullAsEmptyString){
	return ack.promise().bind(this).callback(function(callback){
		ack.router().parseBody({
			nullAsEmptyString:nullAsEmptyString==null?true:nullAsEmptyString
		})(this.req,this.res,callback)
	})
  .then(function(){
    if(!this.req.body){//maybe bad idea to default the body
      this.req.body = {}
    }

    return [this.form(), this.req.body]
  })
  .spread()//cast array to positional-arguments
}

module.exports = reqrtn<|endoftext|>


<commit_before>var _ = require('lodash'),
  async = require('async'),
  child = require('child_process'),
  colors = require('colors'),
  commander = require('commander'),
  fs = require('fs'),
  iz = require('iz'),
  p = require('path'),
  util = require('util'),
  wrench = require('wrench');

/**
 * Parse command line arguments and execute actions based on the specified commands.
 * Uses commander.js to provide -V / --version to display version number,
 * and -h / --help to display help info.
 *
 * @param {String} base: base directory where the client module is located,
 *   used as a base directory to read command file and package.json file,
 *   ideally the value would be the client's __dirname
 * @param {Object} actions: action function for each command in format: { command: { action: function () {} }},
 *   the command name in actions object is then mapped to the command name specified in commandFile
 * @param {Object} opts: optional
 *   - commandFile: relative path to command file from base directory, defaults to 'conf/commands.json'
 */
function command(base, actions, opts) {

  actions = actions || {};
  opts = opts || {};

  var commands = JSON.parse(fs.readFileSync(p.join(base, opts.commandFile || '../conf/commands.json'))),
    pkg = JSON.parse(fs.readFileSync(p.join(base, '../package.json')));

  if (actions.commands && commands.commands) {
    _.each(actions.commands, function (command, name) {
      if (commands.commands[name]) {
        commands.commands[name].action = command.action;
      }
    });
  }

  commander.version(pkg.version);

  if (commands.options) {
    _.each(commands.options, function (option) {
      commander.option(option.arg, option.desc, option.action);
    });
  }

  _.each(commands.commands, function (command, name) {
    var program = commander
      .command(name)
      .description(command.desc);

    _.each(command.options, function (option) {
      program.option(option.arg, option.desc, option.action);
    });

    program.action(command.action);
  });

  _preCommand(commands.commands);

  commander.parse(process.argv);

  // NOTE: commander.args is populated by commander#parse,
  // hence _postCommand relies on commander#parse finishing without exiting or throwing error,
  // otherwise _postCommand won't be executed
  _postCommand(commander.args, commands.commands);
}

// Pre-command tasks:
// - if --help flag is specified, append examples after standard help output
function _preCommand(commands) {
  commander.on('--help', function () {

    var hasExample = _.pluck(_.values(commands), 'examples')
      .filter(function (elem) {
        return elem !== undefined;
      });
    if (hasExample.length > 0) {
      hasExample = hasExample.reduce(function (a, b) {
        return a.concat(b);
      });
    }
    hasExample = hasExample.length > 0;

    if (hasExample) {

      console.log('  Examples:\n');

      Object.keys(commands).forEach(function (command) {
        if (!_.isEmpty(commands[command].examples)) {
          console.log('    %s:', command);
          commands[command].examples.forEach(function (example) {
            console.log('      %s', example);
          });
        }
      });
    }
  });
}

// Post-command tasks:
// - if there's no command, display help then exit
// - if command is unknown, display error message then exit
// - if there's command, validate arguments then exit
function _postCommand(args, commands) {

  function _validate(arg, commandArg) {
    return function (rule) {
      if (iz.hasOwnProperty(rule)) {
        var isValid = iz[rule](arg);
        if (isValid === null || isValid === false) {
          exit(new Error(util.format('Invalid argument: <%s> must be %s', commandArg.name, rule)));
        }
      } else {
        exit(new Error(util.format('Invalid argument rule: %s', rule)));
      }
    };
  }

  if (!args) {
    return;

  } else if (args.length === 0) {
    // 2 -> 'node' and 'somecommand'
    // which means that the command is executed without args, hence display help menu
    // NOTE: this check is needed because for some reason commander.args
    // also returns empty array when one of the args is an opt (-- prefixed)
    if (process.argv.length === 2) {
      commander.help();
    }

  } else {
    var lastArg = args[args.length - 1],
      commandName = lastArg._name,
      commandArgs = (commands[commandName]) ? commands[commandName].args : undefined;

    // non-object last arg means command is not configured in setup
    if (typeof lastArg !== 'object') {
      exit(new Error(util.format('Unknown command: %s, use --help for more info', lastArg)));

    } else if (commandArgs && commandArgs.length > 0) {
      var programName = lastArg.parent._name;

      // display usage info when expected arguments don't exist
      if (args.length === 1) {
        exit(new Error(util.format('Usage: %s %s %s', programName, commandName, commandArgs.map(function (arg) {
          return util.format((arg.optional) ? '[%s]' : '<%s>', arg.name);
        }).join(' '))));
      } else {
        // validate arguments as configured in commands setup
        for (var i = 0, ln = commandArgs.length; i < ln; i += 1) {
          if (!commandArgs[i].optional) {
            commandArgs[i].rules.forEach(_validate(args[i], commandArgs[i]));
          }
        }
      }
    }
  }
}

/**
 * Execute a one-liner command.
 * Both stderr and stdout will be logged via console.error/log accordingly.
 * Fallthrough is handy in situation where there are multiple execs running in sequence/parallel,
 * and they all have to be executed regardless of success/error on either one of them.
 *
 * @param {String} command: command to execute
 * @param {Boolean} fallthrough: allow error to be camouflaged as a non-error
 * @param {Function} cb: standard cb(err, result) callback
 */
function exec(command, fallthrough, cb) {

  var _exec = child.exec(command, function (err) {
    var result;
    if (err && fallthrough) {
      // camouflage error to allow other execs to keep running
      result = err;
      err = null;
    }
    cb(err, result);
  });

  _exec.stdout.on('data', function (data) {
    process.stdout.write(data.green);
  });

  _exec.stderr.on('data', function (data) {
    process.stderr.write(data.red);
  });
}

/**
 * Handle process exit based on the existence of error.
 * This is handy for command-line tools to use as the final callback.
 * Exit status code 1 indicates an error, exit status code 0 indicates a success.
 * Error message will be logged to the console. Result object is only used for convenient debugging.
 *
 * @param {Error} err: error object existence indicates the occurence of an error
 * @param {Object} result: result object
 */
function exit(err, result) {
  if (err) {
    console.error(err.message.red);
    process.exit(1);
  } else {
    process.exit(0);
  }
}

/**
 * A higher order function that returns a process exit callback,
 * with error and success callbacks to handle error and result accordingly.
 * Exit status code 1 indicates an error, exit status code 0 indicates a success.
 *
 * @param {Function} errorCb: error callback accepts error argument, defaults to logging to console error
 * @param {Function} successCb: success callback accepts result argument, defaults to logging to console log
 */
function exitCb(errorCb, successCb) {

  if (!errorCb) {
    errorCb = function (err) {
      console.error(err.message.red);
    };
  }

  if (!successCb) {
    successCb = function (result) {
      console.log(result.green);
    };
  }

  return function (err, result) {
    if (err) {
      errorCb(err);
      process.exit(1);
    } else {
      successCb(result);
      process.exit(0);
    }
  };
}

/**
 * Get an array of files contained in specified items.
 * When a directory is specified, all files contained within that directory and its sub-directories will be included.
 *
 * @param {Array} items: an array of files and/or directories
 * @param {Object} opts: optional
 *   - match: regular expression to match against the file name
 * @return {Array} all files
 */
function files(items, opts) {
  opts = opts || {};
  var data = [];

  function addMatch(item) {
    if (opts.match === undefined || (opts.match && item.match(new RegExp(opts.match)))) {
      data.push(item);
    }
  }

  items.forEach(function (item) {
    var stat = fs.statSync(item);

    if (stat.isFile()) {
      addMatch(item);

    } else if (stat.isDirectory()) {
      var _items = wrench.readdirSyncRecursive(item);
      _items.forEach(function (_item) {
        _item = p.join(item, _item);
        if (fs.statSync(_item).isFile()) {
          addMatch(_item);
        }
      });
    }
  });
  return data;
}

/**
 * Synchronously read file based on these rules:
 * - if path is absolute, then check file at absolute path first
 * - if path is relative, then check file at current working directory
 * - if none of the above exists, check file at user home directory
 * - if none exists, throw an error
 * This allows simple file lookup which allows various locations.
 *
 * @param {String} file: the file name to read
 * @param {Object} opts: optional
 * - platform: needed for unit tests to override platform since node v0.11.x
 *             https://github.com/trevnorris/node/commit/c80f8fa8f108d8db598b260ddf26bafd2ec8a1f8
 * @return {String} content of the file
 */
function lookupFile(file, opts) {
  opts = opts || {};
  var data,
    platform = opts.platform || process.platform,
    baseDir = file.match(/^\//) ? p.dirname(file) : process.cwd(),
    homeDir = process.env[(platform === 'win32') ? 'USERPROFILE' : 'HOME'],
    files = _.map([ baseDir, homeDir ], function (dir) {
      return p.join(dir, file.match(/^\//) ? p.basename(file) : file);
    });

  for (var i = 0, ln = files.length; i < ln; i += 1) {
    try {
      data = fs.readFileSync(files[i]);
      break;
    } catch (err) {
    }
  }

  if (data) {
    return data;
  } else {
    throw new Error('Unable to lookup file in ' + files.join(', '));
  }
}

/**
 * Execute a command with an array of arguments.
 * E.g. command: make, arguments: -f somemakefile target1 target2 target3
 *      will be executed as: make -f somemakefile target1 target2 target3
 * NOTE: process.stdout.write and process.stderr.write are used because console.log adds a newline
 *
 * @param {String} command: command to execute
 * @param {Array} args: command arguments
 * @param {Function} cb: standard cb(err, result) callback
 */
function spawn(command, args, cb) {

  var _spawn = child.spawn(command, args);

  _spawn.stdout.on('data', function (data) {
    process.stdout.write(data.green);
  });

  _spawn.stderr.on('data', function (data) {
    process.stderr.write(data.red);
  });

  _spawn.on('exit', function (code) {
    cb((code !== 0) ? new Error(code) : undefined, code);
  });
}

exports.command = command;
exports._preCommand = _preCommand;
exports._postCommand = _postCommand;
exports.exec = exec;
exports.exit = exit;
exports.exitCb = exitCb;
exports.files = files;
exports.lookupFile = lookupFile;
exports.spawn = spawn;
<commit_msg>Modify post-command validation to support non-argument, rename some vars for clarity.<commit_after>var _ = require('lodash'),
  async = require('async'),
  child = require('child_process'),
  colors = require('colors'),
  commander = require('commander'),
  fs = require('fs'),
  iz = require('iz'),
  p = require('path'),
  util = require('util'),
  wrench = require('wrench');

/**
 * Parse command line arguments and execute actions based on the specified commands.
 * Uses commander.js to provide -V / --version to display version number,
 * and -h / --help to display help info.
 *
 * @param {String} base: base directory where the client module is located,
 *   used as a base directory to read command file and package.json file,
 *   ideally the value would be the client's __dirname
 * @param {Object} actions: action function for each command in format: { command: { action: function () {} }},
 *   the command name in actions object is then mapped to the command name specified in commandFile
 * @param {Object} opts: optional
 *   - commandFile: relative path to command file from base directory, defaults to 'conf/commands.json'
 */
function command(base, actions, opts) {

  actions = actions || {};
  opts = opts || {};

  var commands = JSON.parse(fs.readFileSync(p.join(base, opts.commandFile || '../conf/commands.json'))),
    pkg = JSON.parse(fs.readFileSync(p.join(base, '../package.json')));

  if (actions.commands && commands.commands) {
    _.each(actions.commands, function (command, name) {
      if (commands.commands[name]) {
        commands.commands[name].action = command.action;
      }
    });
  }

  commander.version(pkg.version);

  if (commands.options) {
    _.each(commands.options, function (option) {
      commander.option(option.arg, option.desc, option.action);
    });
  }

  _.each(commands.commands, function (command, name) {
    var program = commander
      .command(name)
      .description(command.desc);

    _.each(command.options, function (option) {
      program.option(option.arg, option.desc, option.action);
    });

    program.action(command.action);
  });

  _preCommand(commands.commands);

  commander.parse(process.argv);

  // NOTE: commander.args is populated by commander#parse,
  // hence _postCommand relies on commander#parse finishing without exiting or throwing error,
  // otherwise _postCommand won't be executed
  _postCommand(commander.args, commands.commands);
}

// Pre-command tasks:
// - if --help flag is specified, append examples after standard help output
function _preCommand(commands) {
  commander.on('--help', function () {

    var hasExample = _.pluck(_.values(commands), 'examples')
      .filter(function (elem) {
        return elem !== undefined;
      });
    if (hasExample.length > 0) {
      hasExample = hasExample.reduce(function (a, b) {
        return a.concat(b);
      });
    }
    hasExample = hasExample.length > 0;

    if (hasExample) {

      console.log('  Examples:\n');

      Object.keys(commands).forEach(function (command) {
        if (!_.isEmpty(commands[command].examples)) {
          console.log('    %s:', command);
          commands[command].examples.forEach(function (example) {
            console.log('      %s', example);
          });
        }
      });
    }
  });
}

// Post-command tasks:
// - if there's no command, display help then exit
// - if command is unknown, display error message then exit
// - if there's command, validate arguments then exit
function _postCommand(args, commandsConfig) {

  function _validate(value, name, desc) {
    return function (rule) {
      if (iz.hasOwnProperty(rule)) {
        var isValid = iz[rule](value);
        if (isValid === null || isValid === false) {
          exit(new Error(util.format('Invalid %s: <%s> must be %s', desc, name, rule)));
        }
      } else {
        exit(new Error(util.format('Invalid %s rule: %s', desc, rule)));
      }
    };
  }

  if (!args) {
    return;

  } else if (args.length === 0) {
    // 2 -> 'node' and 'somecommand'
    // which means that the command is executed without args, hence display help menu
    // NOTE: this check is needed because for some reason commander.args
    // also returns empty array when one of the args is an opt (-- prefixed)
    if (process.argv.length === 2) {
      commander.help();
    }

  } else {
    var lastArg = args[args.length - 1],
      commandName = lastArg._name,
      commandArgs = (commandsConfig[commandName]) ? commandsConfig[commandName].args : undefined;

    // non-object last arg means command is not configured in setup
    if (typeof lastArg !== 'object') {
      exit(new Error(util.format('Unknown command: %s, use --help for more info', lastArg)));

    } else if (commandArgs && commandArgs.length > 0) {
      var programName = lastArg.parent._name;

      // display usage info when expected arguments don't exist
      if (args.length === 1) {
        exit(new Error(util.format('Usage: %s %s %s', programName, commandName, commandArgs.map(function (arg) {
          return util.format((arg.optional) ? '[%s]' : '<%s>', arg.name);
        }).join(' '))));
      } else {
        // validate arguments as configured in commands setup
        for (var i = 0, ln = commandArgs.length; i < ln; i += 1) {
          if (!commandArgs[i].optional) {
            commandArgs[i].rules.forEach(_validate(args[i], commandArgs[i].name, 'argument'));
          }
        }
      }
    }
  }
}

/**
 * Execute a one-liner command.
 * Both stderr and stdout will be logged via console.error/log accordingly.
 * Fallthrough is handy in situation where there are multiple execs running in sequence/parallel,
 * and they all have to be executed regardless of success/error on either one of them.
 *
 * @param {String} command: command to execute
 * @param {Boolean} fallthrough: allow error to be camouflaged as a non-error
 * @param {Function} cb: standard cb(err, result) callback
 */
function exec(command, fallthrough, cb) {

  var _exec = child.exec(command, function (err) {
    var result;
    if (err && fallthrough) {
      // camouflage error to allow other execs to keep running
      result = err;
      err = null;
    }
    cb(err, result);
  });

  _exec.stdout.on('data', function (data) {
    process.stdout.write(data.green);
  });

  _exec.stderr.on('data', function (data) {
    process.stderr.write(data.red);
  });
}

/**
 * Handle process exit based on the existence of error.
 * This is handy for command-line tools to use as the final callback.
 * Exit status code 1 indicates an error, exit status code 0 indicates a success.
 * Error message will be logged to the console. Result object is only used for convenient debugging.
 *
 * @param {Error} err: error object existence indicates the occurence of an error
 * @param {Object} result: result object
 */
function exit(err, result) {
  if (err) {
    console.error(err.message.red);
    process.exit(1);
  } else {
    process.exit(0);
  }
}

/**
 * A higher order function that returns a process exit callback,
 * with error and success callbacks to handle error and result accordingly.
 * Exit status code 1 indicates an error, exit status code 0 indicates a success.
 *
 * @param {Function} errorCb: error callback accepts error argument, defaults to logging to console error
 * @param {Function} successCb: success callback accepts result argument, defaults to logging to console log
 */
function exitCb(errorCb, successCb) {

  if (!errorCb) {
    errorCb = function (err) {
      console.error(err.message.red);
    };
  }

  if (!successCb) {
    successCb = function (result) {
      console.log(result.green);
    };
  }

  return function (err, result) {
    if (err) {
      errorCb(err);
      process.exit(1);
    } else {
      successCb(result);
      process.exit(0);
    }
  };
}

/**
 * Get an array of files contained in specified items.
 * When a directory is specified, all files contained within that directory and its sub-directories will be included.
 *
 * @param {Array} items: an array of files and/or directories
 * @param {Object} opts: optional
 *   - match: regular expression to match against the file name
 * @return {Array} all files
 */
function files(items, opts) {
  opts = opts || {};
  var data = [];

  function addMatch(item) {
    if (opts.match === undefined || (opts.match && item.match(new RegExp(opts.match)))) {
      data.push(item);
    }
  }

  items.forEach(function (item) {
    var stat = fs.statSync(item);

    if (stat.isFile()) {
      addMatch(item);

    } else if (stat.isDirectory()) {
      var _items = wrench.readdirSyncRecursive(item);
      _items.forEach(function (_item) {
        _item = p.join(item, _item);
        if (fs.statSync(_item).isFile()) {
          addMatch(_item);
        }
      });
    }
  });
  return data;
}

/**
 * Synchronously read file based on these rules:
 * - if path is absolute, then check file at absolute path first
 * - if path is relative, then check file at current working directory
 * - if none of the above exists, check file at user home directory
 * - if none exists, throw an error
 * This allows simple file lookup which allows various locations.
 *
 * @param {String} file: the file name to read
 * @param {Object} opts: optional
 * - platform: needed for unit tests to override platform since node v0.11.x
 *             https://github.com/trevnorris/node/commit/c80f8fa8f108d8db598b260ddf26bafd2ec8a1f8
 * @return {String} content of the file
 */
function lookupFile(file, opts) {
  opts = opts || {};
  var data,
    platform = opts.platform || process.platform,
    baseDir = file.match(/^\//) ? p.dirname(file) : process.cwd(),
    homeDir = process.env[(platform === 'win32') ? 'USERPROFILE' : 'HOME'],
    files = _.map([ baseDir, homeDir ], function (dir) {
      return p.join(dir, file.match(/^\//) ? p.basename(file) : file);
    });

  for (var i = 0, ln = files.length; i < ln; i += 1) {
    try {
      data = fs.readFileSync(files[i]);
      break;
    } catch (err) {
    }
  }

  if (data) {
    return data;
  } else {
    throw new Error('Unable to lookup file in ' + files.join(', '));
  }
}

/**
 * Execute a command with an array of arguments.
 * E.g. command: make, arguments: -f somemakefile target1 target2 target3
 *      will be executed as: make -f somemakefile target1 target2 target3
 * NOTE: process.stdout.write and process.stderr.write are used because console.log adds a newline
 *
 * @param {String} command: command to execute
 * @param {Array} args: command arguments
 * @param {Function} cb: standard cb(err, result) callback
 */
function spawn(command, args, cb) {

  var _spawn = child.spawn(command, args);

  _spawn.stdout.on('data', function (data) {
    process.stdout.write(data.green);
  });

  _spawn.stderr.on('data', function (data) {
    process.stderr.write(data.red);
  });

  _spawn.on('exit', function (code) {
    cb((code !== 0) ? new Error(code) : undefined, code);
  });
}

exports.command = command;
exports._preCommand = _preCommand;
exports._postCommand = _postCommand;
exports.exec = exec;
exports.exit = exit;
exports.exitCb = exitCb;
exports.files = files;
exports.lookupFile = lookupFile;
exports.spawn = spawn;
<|endoftext|>


<commit_before>/**
 * @file Exports the Blizzard.js module.
 * @module lib/blizzard
 * @requires axios
 * @requires lib/endpoints
 * @requires lib/account
 * @requires lib/wow
 * @requires lib/sc2
 * @requires lib/d3
 */
'use strict';

const axios = require('axios');

const pkg = require('../package.json');
const endpoints = require('./endpoints');

const Account = require('./account');
const D3 = require('./d3');
const SC2 = require('./sc2');
const WoW = require('./wow');

/**
 * Blizzard.js class constructor.
 *
 * @constructor
 * @param  {Object} args            Blizzard.js configuration settings
 * @param  {String} args.apikey     Your private Blizzard API key
 * @param  {String} [args.origin]   The default API region
 * @param  {String} [args.locale]   The default API locale
 * @param  {Object} [instance]      An instance config object compatible with [axios]{@link https://github.com/mzabriskie/axios}
 * @return {Function}               Blizzard.js constructor function
 */
const Blizzard = function Blizzard (args, instance) {
  this.version = pkg.version;

  const defaultEndpoint = endpoints.getEndpoint(args.origin, args.locale);

  this.defaults = {
    apikey: args.apikey,
    origin: defaultEndpoint.origin,
    locale: defaultEndpoint.locale
  };

  this.instance = Object.assign(
    {},
    instance,
    {
      headers: {
        'User-Agent': 'Node.js/' + process.version + ' Blizzard.js/' + this.version
      }
    }
  );

  this.axios = axios.create(this.instance);

  /**
   * Account API methods
   *
   * @member {Object}
   * @see {@link lib/account}
   */
  this.account = new Account(this);

  /**
  * Diablo 3 API methods
  *
  * @member {Object}
  * @see {@link lib/d3}
  */
  this.d3 = new D3(this);

  /**
   * Starcraft 2 API methods
   *
   * @member {Object}
   * @see {@link lib/sc2}
   */
  this.sc2 = new SC2(this);

  /**
  * World of Warcraft API methods
  *
  * @member {Object}
  * @see {@link lib/wow}
  */
  this.wow = new WoW(this);

};

/**
 * Filter an objects keys from an array of keys.
 *
 * @param  {Array} keys A list of keys to be returned
 * @param  {Object} obj An object that may contain keys
 * @return {Object}     A new object containing keys
 */
Blizzard.prototype.params = function params (keys, obj) {
  return Object.keys(obj)
    .filter(key => keys.indexOf(key) !== -1)
    .reduce((value, key) => Object.assign(value, { [key]: obj[key] }), {});
};

/**
 * Perform a single request to the Blizzard API.
 *
 * @param  {String} path          The pathname of the API resource
 * @param  {Object} args          API request parameters
 * @param  {String} [args.origin] The region key
 * @param  {String} [args.locale] A locale code for this region
 * @param  {Object} [opts]        An instance config object compatible with [axios]{@link https://github.com/mzabriskie/axios}
 * @return {Promise}              A theneable Promises/A+ reference
 */
Blizzard.prototype.get = function get (path, args, opts) {
  const config = Object.assign({}, this.defaults, args);
  const instance = Object.assign({}, this.instance, opts);
  const endpoint = endpoints.getEndpoint(config.origin, config.locale);

  return this.axios.get(`${endpoint.hostname}${path}`, instance);
};

/**
 * Perform multiple requests to the Blizzard API.
 *
 * @param  {Array} requests A list of Blizzard get requests
 * @return {Promise}        A theneable Promises/A+ reference
 */
Blizzard.prototype.all = function all (requests) {
  return this.instance.all(requests);
};

module.exports = Blizzard;
<commit_msg>Add apikey to request params<commit_after>/**
 * @file Exports the Blizzard.js module.
 * @module lib/blizzard
 * @requires axios
 * @requires lib/endpoints
 * @requires lib/account
 * @requires lib/wow
 * @requires lib/sc2
 * @requires lib/d3
 */
'use strict';

const axios = require('axios');

const pkg = require('../package.json');
const endpoints = require('./endpoints');

const Account = require('./account');
const D3 = require('./d3');
const SC2 = require('./sc2');
const WoW = require('./wow');

/**
 * Blizzard.js class constructor.
 *
 * @constructor
 * @param  {Object} args            Blizzard.js configuration settings
 * @param  {String} args.apikey     Your private Blizzard API key
 * @param  {String} [args.origin]   The default API region
 * @param  {String} [args.locale]   The default API locale
 * @param  {Object} [instance]      An instance config object compatible with [axios]{@link https://github.com/mzabriskie/axios}
 * @return {Function}               Blizzard.js constructor function
 */
const Blizzard = function Blizzard (args, instance) {
  this.version = pkg.version;

  const defaultEndpoint = endpoints.getEndpoint(args.origin, args.locale);

  this.defaults = {
    origin: defaultEndpoint.origin,
    locale: defaultEndpoint.locale
  };

  this.instance = Object.assign(
    {},
    instance,
    {
      params: {
        apikey: args.apikey
      },
      headers: {
        'User-Agent': 'Node.js/' + process.version + ' Blizzard.js/' + this.version
      }
    }
  );

  this.axios = axios.create(this.instance);

  /**
   * Account API methods
   *
   * @member {Object}
   * @see {@link lib/account}
   */
  this.account = new Account(this);

  /**
  * Diablo 3 API methods
  *
  * @member {Object}
  * @see {@link lib/d3}
  */
  this.d3 = new D3(this);

  /**
   * Starcraft 2 API methods
   *
   * @member {Object}
   * @see {@link lib/sc2}
   */
  this.sc2 = new SC2(this);

  /**
  * World of Warcraft API methods
  *
  * @member {Object}
  * @see {@link lib/wow}
  */
  this.wow = new WoW(this);

};

/**
 * Filter an objects keys from an array of keys.
 *
 * @param  {Array} keys A list of keys to be returned
 * @param  {Object} obj An object that may contain keys
 * @return {Object}     A new object containing keys
 */
Blizzard.prototype.params = function params (keys, obj) {
  return Object.keys(obj)
    .filter(key => keys.indexOf(key) !== -1)
    .reduce((value, key) => Object.assign(value, { [key]: obj[key] }), {});
};

/**
 * Perform a single request to the Blizzard API.
 *
 * @param  {String} path          The pathname of the API resource
 * @param  {Object} args          API request parameters
 * @param  {String} [args.origin] The region key
 * @param  {String} [args.locale] A locale code for this region
 * @param  {Object} [opts]        An instance config object compatible with [axios]{@link https://github.com/mzabriskie/axios}
 * @return {Promise}              A theneable Promises/A+ reference
 */
Blizzard.prototype.get = function get (path, args, opts) {
  const config = Object.assign({}, this.defaults, args);
  const instance = Object.assign({}, this.instance, opts);
  const endpoint = endpoints.getEndpoint(config.origin, config.locale);

  return this.axios.get(`${endpoint.hostname}${path}`, instance);
};

/**
 * Perform multiple requests to the Blizzard API.
 *
 * @param  {Array} requests A list of Blizzard get requests
 * @return {Promise}        A theneable Promises/A+ reference
 */
Blizzard.prototype.all = function all (requests) {
  return this.instance.all(requests);
};

module.exports = Blizzard;
<|endoftext|>


<commit_before>// ==UserScript==
// @name         RohBot Imperial to Metric
// @version      1.0
// @description  Converts imperial to metric if it finds any
// @author       Spans
// @match        https://rohbot.net
// @grant        none
// ==/UserScript==

chatMgr.lineFilter.add(function (line, prepend, e) {
	e.filtered = false;
	line.Content = applyConversions(line.Content, feet, inches, yards, fahrenheit, pounds, ounces, gallons, mph);
});

String.prototype.splice = function(idx, rem, s) {
	return (this.slice(0, idx) + s + this.slice(idx + Math.abs(rem)));
};

function applyConversions(message) {
	var funcs = Array.slice(arguments, 1);
	var results = [];
	var lastIndex = 0;

	// aggregate the results
	funcs.forEach(function(func) {
		var result = func(message);

		if (result !== null) {
			results.splice(lastIndex++, 0, result);
		}
	});

	// sort them
	results.sort(function(a, b) {
		if (a.index > b.index) {
			return 1;
		}

		if (a.index < b.index) {
			return -1;
		}

		return 0;
	});

	var newMsg = message;
	var inserted = 0;
	// combine them all
	results.forEach(function(result) {
		var title = result.conversion.toLocaleString() + " " + result.unit
		var toInsert = "" + result.original + "";
		newMsg = newMsg.splice(result.index + inserted, result.original.length, toInsert);
		inserted += toInsert.length - result.original.length;
	});

	return newMsg;
}

function commonConversion(message, regex, divide, subtract, unit) {
	var m;
	while ((m = regex.exec(message)) !== null) {
		if (m.index === regex.lastIndex) {
			regex.lastIndex++;
		}

		var amount = Number(m[1]);
		var converted = Math.round(((amount - subtract) / divide) * 100) / 100;
		//console.log("Conversion: " + amount + " "  + m[2] + " to " + converted + " " + unit);
		return {original:m[0], index:m.index, conversion:converted, unit:unit};
	}

	return null;
}

function feet(message) {
	return commonConversion(message, /\b(\d+) ?(ft|feet)\b/ig, 3.2808, 0, "meters");
}

function inches(message) {
	return commonConversion(message, /\b(\d+) ?(in|inches|inch)\b/ig, 3.2808, 0, "centimeters");
}

function yards(message) {
	return commonConversion(message, /\b(\d+) ?(yd|yards|yard)\b/ig, 1.0936, 0, "meters");
}

function fahrenheit(message) {
	return commonConversion(message, /\b(\d+) ?(f|fahrenheit)\b/ig, 1.8, 32, "Celsius");
}

function pounds(message) {
	return commonConversion(message, /\b(\d+) ?(lb|lbs|pounds|pound)\b/ig, 2.2046, 0, "kilograms");
}

function ounces(message) {
	return commonConversion(message, /\b(\d+) ?(oz|ounces|ounce)\b/ig, 0.035274, 0, "grams");
}

function gallons(message) {
	return commonConversion(message, /\b(\d+) ?(gal|gallons|gallon)\b/ig, 0.26417, 0, "liters");
}

function mph(message) {
	return commonConversion(message, /\b(\d+) ?(mph|miles per hour)\b/ig, 1/1.6093, 0, "KPH");
}<commit_msg>Updated regexes to handle decimals<commit_after>// ==UserScript==
// @name         RohBot Imperial to Metric
// @version      1.0
// @description  Converts imperial to metric if it finds any
// @author       Spans
// @match        https://rohbot.net
// @grant        none
// ==/UserScript==

chatMgr.lineFilter.add(function (line, prepend, e) {
	e.filtered = false;
	line.Content = applyConversions(line.Content, feet, inches, yards, fahrenheit, pounds, ounces, gallons, mph);
});

String.prototype.splice = function(idx, rem, s) {
	return (this.slice(0, idx) + s + this.slice(idx + Math.abs(rem)));
};

function applyConversions(message) {
	var funcs = Array.slice(arguments, 1);
	var results = [];
	var lastIndex = 0;

	// aggregate the results
	funcs.forEach(function(func) {
		var result = func(message);

		if (result !== null) {
			results.splice(lastIndex++, 0, result);
		}
	});

	// sort them
	results.sort(function(a, b) {
		if (a.index > b.index) {
			return 1;
		}

		if (a.index < b.index) {
			return -1;
		}

		return 0;
	});

	var newMsg = message;
	var inserted = 0;
	// combine them all
	results.forEach(function(result) {
		var title = result.conversion.toLocaleString() + " " + result.unit
		var toInsert = "" + result.original + "";
		newMsg = newMsg.splice(result.index + inserted, result.original.length, toInsert);
		inserted += toInsert.length - result.original.length;
	});

	return newMsg;
}

function commonConversion(message, regex, divide, subtract, unit) {
	var m;
	while ((m = regex.exec(message)) !== null) {
		if (m.index === regex.lastIndex) {
			regex.lastIndex++;
		}

		var amount = Number(m[1]);
		var converted = Math.round(((amount - subtract) / divide) * 100) / 100;
		//console.log("Conversion: " + amount + " "  + m[2] + " to " + converted + " " + unit);
		return {original:m[0], index:m.index, conversion:converted, unit:unit};
	}

	return null;
}

function feet(message) {
	return commonConversion(message, /\b(\b\d+(?:(?:\.|,)\d+\b|\b)) ?(ft|feet)\b/ig, 3.2808, 0, "meters");
}

function inches(message) {
	return commonConversion(message, /\b(\b\d+(?:(?:\.|,)\d+\b|\b)) ?(in|inches|inch)\b/ig, 3.2808, 0, "centimeters");
}

function yards(message) {
	return commonConversion(message, /\b(\b\d+(?:(?:\.|,)\d+\b|\b)) ?(yd|yards|yard)\b/ig, 1.0936, 0, "meters");
}

function fahrenheit(message) {
	return commonConversion(message, /\b(\b\d+(?:(?:\.|,)\d+\b|\b)) ?(f|fahrenheit)\b/ig, 1.8, 32, "Celsius");
}

function pounds(message) {
	return commonConversion(message, /\b(\b\d+(?:(?:\.|,)\d+\b|\b)) ?(lb|lbs|pounds|pound)\b/ig, 2.2046, 0, "kilograms");
}

function ounces(message) {
	return commonConversion(message, /\b(\b\d+(?:(?:\.|,)\d+\b|\b)) ?(oz|ounces|ounce)\b/ig, 0.035274, 0, "grams");
}

function gallons(message) {
	return commonConversion(message, /\b(\b\d+(?:(?:\.|,)\d+\b|\b)) ?(gal|gallons|gallon)\b/ig, 0.26417, 0, "liters");
}

function mph(message) {
	return commonConversion(message, /\b(\b\d+(?:(?:\.|,)\d+\b|\b)) ?(mph|miles per hour)\b/ig, 1/1.6093, 0, "KPH");
}<|endoftext|>


<commit_before>// Copyright Adrian Ottiker (adrian@ottiker.com). All Rights Reserved.

// TODO send error to server
//window.onerror = function(error, url, line) {};

// clone object
Object.clone = function(object) {
    var Module = function() {};
    Module.prototype = object || {};
    return new Module();
};

var M = (function(miids) {

    var fn = 'function';

    // check and initialize websockets
    if (typeof WebSocket === 'undefined') {
        return alert('Update your f***ing browser!');
    }

    // open a websocket
    // TODO try to reconnect if a connection is closed (try to solve it with events instead of an interval)
    var webSocket = new WebSocket('ws://' + window.location.host + '/');

    // handle websocket messages events
    webSocket.onmessage = function (message) {

        // parse message ["miid:event:cbId","err","data"]
        try {
            message = JSON.parse(message.data);
        } catch (error) {
            return;
        }

        message[0] = message[0].split(':');

        // parse message
        var miid = message[0][0];
        var event = message[0][1];
        var cbId = message[0][2];
        var err = message[1];
        var data = message[2];

        if (miid && event && miids[miid]) {

            // call callback
            if (miids[miid].mono.ws[cbId]) {
                miids[miid].mono.ws[cbId].call(miids[miid] || Mono, err, data);
                delete miids[miid].mono.ws[cbId];
            }

            // emit event
            miids[miid].emit(event, err, data);
        }
    };

    // random string generator
    function uid (len) {
        for (var i = 0, l = len || 23, random = ''; i < l; ++i) {
            random += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[0 | Math.random() * 62];
        }
        return random;
    }

    // send event handler
    function send (event) {
        return function (err, data, callback) {

            // ["miid:event:cbId","err","data"]

            var message = [this.mono.miid + ':' + event, 0];

            if (err) {
                message[1] = err;
            }

            if (data) {
                message[2] = data;
            }

            if (callback) {
                var cbId = uid(5);
                this.mono.ws[cbId] = callback;
                message[0] += ':' + cbId;
            }

            try {
                message = JSON.stringify(message);
            } catch (err) {
                if (callback) {
                    callback(err);
                }
            }

            webSocket.send(message);
        };
    }

    function listen (event, handler, once) {
        var events = this.mono.events;

        if (typeof handler === fn) {
            // fire ready event immediately if miid is ready
            if (event === 'ready' && this.mono.ready) {
                return handler.call(this);
            }

            if (!events[event]) {
                events[event] = [];
            }

            handler.one = once;
            events[event].push(handler);
        }

        return this;
    }

    // module class
    var Mono = {

        // remove the module instance from dom and cache
        die: function () {
            var miid = miids[this.miid];

            if (miid) {

                // remove module from dom
                if (miid.dom) {
                    miid.dom.parentNode.removeChild(miid.dom);
                }

                // remove module
                miid = 0;
            }
        },
        // listen to events
        on: listen,
        one: function (event, handler) {
            listen.call(this, event, handler, true);
        },
        // remove listeners
        off: function (event, handler) {
            var events = this.mono.events;

            if (events && events[event]) {

                if (handler) {

                    for (var i = 0, l = events[event].length; i < l; ++i) {

                        if (events[event][i][0] === handler) {
                            events[event].splice(i, 1);
                        }
                    }

                } else {
                    delete events[event];
                }
            }

            return this;
        },
        // emit event
        emit: function(event) {
            var events = this.mono.events;

            // mark miid as ready
            if (event === 'ready') {
                this.mono.ready = true;
            }

            // slice first argument and apply the others to the callback function
            var args = Array.prototype.slice.call(arguments).slice(1);

            if (events && events[event]) {

                for (var i = 0, l = events[event].length; i < l; ++i) {

                    if (events[event][i]) {

                        // Fire registred Methods
                        events[event][i].apply(this, args);

                        // remove from event buffer, if once is true
                        if (events[event][i].one) {
                            events[event].splice(i, 1);
                        }
                    }
                }
            }

            return this;
        }

        // TODO emit to all miids
        //all: function (event) {},

        // TODO trigger and event on a miid
        //push: function (event, miid) {}
    };

    // module cache
    var modules = {};

    // get head reference
    var head = document.getElementsByTagName('head')[0];

    // css cache
    var css = {};

    // load scripts
    function initModule (miid, target) {

        miid = miids[miid].mono;

        // append module to the dom
        if (miid.dom && target) {
            target = typeof target === 'string' ? document.querySelector(target) : target;

            if (target) {
                target.appendChild(miid.dom);
            }
        }

        // call module init
        if (typeof miid.init === fn) {

            // handle waitFor
            if (miid.config.waitFor instanceof Array) {
                var count = miid.config.waitFor.length;
                var current = 0;
                var handler = function () {
                    if (current === count) {
                        miid.init.call(miids[miid.miid], miid.config.data);
                    }
                };

                for (;current < count; ++current) {
                    miids[miid.miid].one('ready', miid.config.waitFor[current], handler);
                }
            } else {
                miid.init.call(miids[miid.miid], miid.config.data);
            }
        }
    }

    // commonjs require
    function require (module) {
        return function (name) {

            if (name.indexOf('../') === 0) {

                var namePath = name.split('../');
                var stepBackLenght = namePath.length - 1;
                namePath = namePath.pop();

                name = module.base + (module.path.length === stepBackLenght ? namePath : module.path.slice(0, stepBackLenght).join('/') + '/' + namePath);

            } else if (name.indexOf('./') === 0) {
                var path = module.path.join('/');
                name = module.base + (path ? path + '/' : '') + name.substr(2);
            } else {

                if (name.split('/').length < 5 && name[name.length - 1] !== '/') {
                    name += '/';
                }

                for (var script in modules) {
                    if (script.indexOf(name) === 0) {
                        name = script;
                        break;
                    }
                }
            }

            name += name.slice(-3) !== '.js' ? '.js' : '';

            if (modules[name]) {
                return modules[name].exports;
            }
        };
    }

    // create CommonJS modules in order of the dependencies
    function createCommonJsModulesInOrder (miid, moduleSources, callback) {

        // init modules in order (desc)
        for (var i = (moduleSources.length - 1), l = 0; i >= l; --i) {

            // evaluate module script
            if (typeof modules[moduleSources[i]] === fn && !modules[moduleSources[i]]._eval) {

                    var module = {
                        id: moduleSources[i],
                        exports: {}
                    };

                    module.path = module.id.split('/');
                    module.file = module.path.pop();

                    if (module.id.indexOf('//') === 0) {

                        module.base = '//';
                        module.path = module.path.slice(2);

                    } else if (module.id[0] === '/') {

                        module.base = '/';
                        module.path = module.path.slice(1);

                    } else if (module.id.indexOf('://') > 0) {

                        module.base = module.path.slice(0,3).join('/') + '/';
                        module.path = module.path.slice(3);

                    } else {

                        module.base = module.path.slice(0,4).join('/') + '/';
                        module.path = module.path.slice(4);
                    }

                    // execute CommonJS module
                    modules[moduleSources[i]] = modules[moduleSources[i]].call(module.exports, require(module), module, module.exports);
                    modules[moduleSources[i]]._eval = true;
            }
        }

        // return first module of dependency list
        callback(miid, modules[moduleSources[0]] ? modules[moduleSources[0]].exports : null);
    }

    // load scripts (script tag)
    function loadJS (miid, moduleName, moduleSources, callback) {

        var length = moduleSources.length;

        var extDepLoaded = function (src) {
            return function () {
                modules[src] = 3;
                miids.M.emit(src);
            };
        };
        var modDepLoaded = function () {
            if (--length === 0) {
                createCommonJsModulesInOrder(miid, moduleSources, callback);
            }
        };

        for (var i = moduleSources.length - 1, source; i >= 0; --i) {

            source = moduleName + moduleSources[i];

            // ingore loading for unified code
            if (source[0] === '#') {
                // remove the control sign
                moduleSources[i] = source.substr(1);
                --length;
                continue;
            }

            // when script is loaded check if it's evaluated
            miids.M.one(source, modDepLoaded);

            // emit source event for already loaded scripts
            if (typeof modules[source] === fn || modules[source] === 3) {
                miids.M.emit(source);

            // load module scripts
            } else {
                var node = document.createElement('script');

                if (moduleSources[i][0] === '/' || moduleSources[i].indexOf('://') > 0) {
                    // set script status to: external script
                    modules[moduleSources[i]] = 1;
                    node.src = moduleSources[i];
                    node.onload = extDepLoaded(moduleSources[i]);
                } else {
                    // set script status to: module script not loaded
                    modules[source] = 2;
                    node.src = '/@/M/module/' + miid + '/' + moduleSources[i];
                    moduleSources[i] = source;
                }

                head.appendChild(node);
            }
        }
    }

    // build core module
    var core = Object.clone(Mono);
    core.mono = {
        events: {},
        miid: 'M',
        ws: {},
        ready: true
    };
    // load miid
    core.load = function (miid, target, parentMiid) {

        // don't load the same miid more than once
        if (!miid || miids[miid]) {
            return;
        }

        // wait for websocket
        if (webSocket.readyState !== webSocket.OPEN) {
            webSocket.onopen = function () {
                miids.M.load(miid, target, parentMiid);
            };
            return;
        }

        // get miid config
        miids.M.emit('load', null, miid, function (err, config) {

            if (err) {
                return;
            }

            // load dependend modules
            if (config.modules) {
                for (var selector in config.modules) {
                    M(config.modules[selector], selector, miid);
                }
            }

            // add parent miid to waitFor config
            if (parentMiid) {
                config.waitFor = config.waitFor || [];
                config.waitFor.push(parentMiid);
            }

            // create module instance
            var miidMono = {
                config: config,
                state: 0,
                events: {},
                miid: miid,
                name: config.name,
                ws: {}
            };
            miids[miid] = Object.clone(Mono);
            miids[miid].mono = miidMono;

            // load css
            if (config.css) {
                for (var i in config.css) {
                    if (!css[config.css[i]]) {

                        css[config.css[i]] = 1;

                        var link = document.createElement('link');
                        link.setAttribute('rel', 'stylesheet');
                        link.setAttribute('href', config.css[i]);

                        head.appendChild(link);
                    }
                }
            }

            // load html
            if (config.html) {
                this.emit('getHtml', null, config.html, function (err, html) {

                    // create module container
                    var container = document.createElement('div');
                    container.setAttribute('id', miid);
                    container.innerHTML = html || '';

                    // append the dom container to the module
                    miids[miid].mono.dom = container;

                    if (++miidMono.state === 2) {
                        initModule(miid, target);
                    }
                });

            } else {
                ++miidMono.state;
            }

            // load scripts and init module
            if (config.scripts && config.scripts.length > 0) {
                loadJS(miid, config.name, config.scripts, function (miid, moduleConstructor) {

                    // create module
                    if (miidMono.config.name) {
                        miidMono.init = miidMono.init || moduleConstructor;

                        // listen to send events
                        if (config.events) {
                            for (var i = 0, l = config.events.length; i < l; ++i) {
                                miids[miid].on(config.events[i], send(config.events[i]));
                            }
                        }

                        if (++miidMono.state === 2) {
                            initModule(miid, target);
                        }
                    }
                });

            } else if (++miidMono.state === 2) {
                initModule(miid, target);
            }
        });
    };
    // wrapping script
    core.load.wrap = function (name, module) {
        modules[name] = module;
        miids.M.emit(name);
    };

    // listen to core load event
    core.on('load', send('load'));
    core.on('getHtml', send('getHtml'));

    // miid cache
    miids.M = core;

    return core.load;
})({});
<commit_msg>implemented push and pushAll<commit_after>// Copyright Adrian Ottiker (adrian@ottiker.com). All Rights Reserved.

// TODO send error to server
//window.onerror = function(error, url, line) {};

// clone object
Object.clone = function(object) {
    var Module = function() {};
    Module.prototype = object || {};
    return new Module();
};

var M = (function(miids) {

    var fn = 'function';
    var convertToArray = Array.prototype.slice;

    // check and initialize websockets
    if (typeof WebSocket === 'undefined') {
        return alert('Update your f***ing browser!');
    }

    // open a websocket
    // TODO try to reconnect if a connection is closed (try to solve it with events instead of an interval)
    var webSocket = new WebSocket('ws://' + window.location.host + '/');

    // handle websocket messages events
    webSocket.onmessage = function (message) {

        // parse message ["miid:event:cbId","err","data"]
        try {
            message = JSON.parse(message.data);
        } catch (error) {
            return;
        }

        message[0] = message[0].split(':');

        // parse message
        var miid = message[0][0];
        var event = message[0][1];
        var cbId = message[0][2];
        var err = message[1];
        var data = message[2];

        if (miid && event && miids[miid]) {

            // call callback
            if (miids[miid].mono.ws[cbId]) {
                miids[miid].mono.ws[cbId].call(miids[miid] || Mono, err, data);
                delete miids[miid].mono.ws[cbId];
            }

            // emit event
            miids[miid].emit(event, err, data);
        }
    };

    // random string generator
    function uid (len) {
        for (var i = 0, l = len || 23, random = ''; i < l; ++i) {
            random += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[0 | Math.random() * 62];
        }
        return random;
    }

    // send event handler
    function send (event) {
        return function (err, data, callback) {

            // ["miid:event:cbId","err","data"]

            var message = [this.mono.miid + ':' + event, 0];

            if (err) {
                message[1] = err;
            }

            if (data) {
                message[2] = data;
            }

            if (callback) {
                var cbId = uid(5);
                this.mono.ws[cbId] = callback;
                message[0] += ':' + cbId;
            }

            try {
                message = JSON.stringify(message);
            } catch (err) {
                if (callback) {
                    callback(err);
                }
            }

            webSocket.send(message);
        };
    }

    function listen (event, handler, once) {
        var events = this.mono.events;

        if (typeof handler === fn) {
            // fire ready event immediately if miid is ready
            if (event === 'ready' && this.mono.ready) {
                return handler.call(this);
            }

            if (!events[event]) {
                events[event] = [];
            }

            handler.one = once;
            events[event].push(handler);
        }
    }

    // module class
    var Mono = {

        // remove the module instance from dom and cache
        die: function () {
            var miid = miids[this.miid];

            if (miid) {

                // remove module from dom
                if (miid.dom) {
                    miid.dom.parentNode.removeChild(miid.dom);
                }

                // remove module
                miid = 0;
            }
        },
        // listen to events
        on: listen,
        one: function (event, handler) {
            listen.call(this, event, handler, true);
        },
        // remove listeners
        off: function (event, handler) {
            var events = this.mono.events;

            if (events && events[event]) {

                if (handler) {

                    for (var i = 0, l = events[event].length; i < l; ++i) {

                        if (events[event][i][0] === handler) {
                            events[event].splice(i, 1);
                        }
                    }

                } else {
                    delete events[event];
                }
            }
        },
        // emit event
        emit: function(event) {
            var events = this.mono.events;

            // mark miid as ready
            if (event === 'ready') {
                this.mono.ready = true;
            }

            // slice first argument and apply the others to the callback function
            var args = convertToArray.call(arguments).slice(1);

            if (events && events[event]) {

                for (var i = 0, l = events[event].length; i < l; ++i) {

                    if (events[event][i]) {

                        // Fire registred Methods
                        events[event][i].apply(this, args);

                        // remove from event buffer, if once is true
                        if (events[event][i].one) {
                            events[event].splice(i, 1);
                        }
                    }
                }
            }
        },

        // trigger an event on a miid
        push: function (miid, event) {
            if (miids[miid]) {

                // slice first argument and apply the others to the callback function
                var args = convertToArray.call(arguments).slice(1);

                miids[miid].emit.apply(miids[miid], args);
            }
        },

        // emit on all miids
        pushAll: function (event) {

            // slice first argument and apply the others to the callback function
            var args = convertToArray.call(arguments);

            for (var miid in miids) {
                miids[miid].emit.apply(miids[miid], args);
            }
        }
    };

    // module cache
    var modules = {};

    // get head reference
    var head = document.getElementsByTagName('head')[0];

    // css cache
    var css = {};

    // load scripts
    function initModule (miid, target) {

        miid = miids[miid].mono;

        // append module to the dom
        if (miid.dom && target) {
            target = typeof target === 'string' ? document.querySelector(target) : target;

            if (target) {
                target.appendChild(miid.dom);
            }
        }

        // call module init
        if (typeof miid.init === fn) {

            // handle waitFor
            if (miid.config.waitFor instanceof Array) {
                var count = miid.config.waitFor.length;
                var current = 0;
                var handler = function () {
                    if (current === count) {
                        miid.init.call(miids[miid.miid], miid.config.data);
                    }
                };

                for (;current < count; ++current) {
                    miids[miid.miid].one('ready', miid.config.waitFor[current], handler);
                }
            } else {
                miid.init.call(miids[miid.miid], miid.config.data);
            }
        }
    }

    // commonjs require
    function require (module) {
        return function (name) {

            if (name.indexOf('../') === 0) {

                var namePath = name.split('../');
                var stepBackLenght = namePath.length - 1;
                namePath = namePath.pop();

                name = module.base + (module.path.length === stepBackLenght ? namePath : module.path.slice(0, stepBackLenght).join('/') + '/' + namePath);

            } else if (name.indexOf('./') === 0) {
                var path = module.path.join('/');
                name = module.base + (path ? path + '/' : '') + name.substr(2);
            } else {

                if (name.split('/').length < 5 && name[name.length - 1] !== '/') {
                    name += '/';
                }

                for (var script in modules) {
                    if (script.indexOf(name) === 0) {
                        name = script;
                        break;
                    }
                }
            }

            name += name.slice(-3) !== '.js' ? '.js' : '';

            if (modules[name]) {
                return modules[name].exports;
            }
        };
    }

    // create CommonJS modules in order of the dependencies
    function createCommonJsModulesInOrder (miid, moduleSources, callback) {

        // init modules in order (desc)
        for (var i = (moduleSources.length - 1), l = 0; i >= l; --i) {

            // evaluate module script
            if (typeof modules[moduleSources[i]] === fn && !modules[moduleSources[i]]._eval) {

                    var module = {
                        id: moduleSources[i],
                        exports: {}
                    };

                    module.path = module.id.split('/');
                    module.file = module.path.pop();

                    if (module.id.indexOf('//') === 0) {

                        module.base = '//';
                        module.path = module.path.slice(2);

                    } else if (module.id[0] === '/') {

                        module.base = '/';
                        module.path = module.path.slice(1);

                    } else if (module.id.indexOf('://') > 0) {

                        module.base = module.path.slice(0,3).join('/') + '/';
                        module.path = module.path.slice(3);

                    } else {

                        module.base = module.path.slice(0,4).join('/') + '/';
                        module.path = module.path.slice(4);
                    }

                    // execute CommonJS module
                    modules[moduleSources[i]] = modules[moduleSources[i]].call(module.exports, require(module), module, module.exports);
                    modules[moduleSources[i]]._eval = true;
            }
        }

        // return first module of dependency list
        callback(miid, modules[moduleSources[0]] ? modules[moduleSources[0]].exports : null);
    }

    // load scripts (script tag)
    function loadJS (miid, moduleName, moduleSources, callback) {

        var length = moduleSources.length;

        var extDepLoaded = function (src) {
            return function () {
                modules[src] = 3;
                miids.M.emit(src);
            };
        };
        var modDepLoaded = function () {
            if (--length === 0) {
                createCommonJsModulesInOrder(miid, moduleSources, callback);
            }
        };

        for (var i = moduleSources.length - 1, source; i >= 0; --i) {

            source = moduleName + moduleSources[i];

            // ingore loading for unified code
            if (source[0] === '#') {
                // remove the control sign
                moduleSources[i] = source.substr(1);
                --length;
                continue;
            }

            // when script is loaded check if it's evaluated
            miids.M.one(source, modDepLoaded);

            // emit source event for already loaded scripts
            if (typeof modules[source] === fn || modules[source] === 3) {
                miids.M.emit(source);

            // load module scripts
            } else {
                var node = document.createElement('script');

                if (moduleSources[i][0] === '/' || moduleSources[i].indexOf('://') > 0) {
                    // set script status to: external script
                    modules[moduleSources[i]] = 1;
                    node.src = moduleSources[i];
                    node.onload = extDepLoaded(moduleSources[i]);
                } else {
                    // set script status to: module script not loaded
                    modules[source] = 2;
                    node.src = '/@/M/module/' + miid + '/' + moduleSources[i];
                    moduleSources[i] = source;
                }

                head.appendChild(node);
            }
        }
    }

    // build core module
    var core = Object.clone(Mono);
    core.mono = {
        events: {},
        miid: 'M',
        ws: {},
        ready: true
    };
    // load miid
    core.load = function (miid, target, parentMiid) {

        // don't load the same miid more than once
        if (!miid || miids[miid]) {
            return;
        }

        // wait for websocket
        if (webSocket.readyState !== webSocket.OPEN) {
            webSocket.onopen = function () {
                miids.M.load(miid, target, parentMiid);
            };
            return;
        }

        // get miid config
        miids.M.emit('load', null, miid, function (err, config) {

            if (err) {
                return;
            }

            // load dependend modules
            if (config.modules) {
                for (var selector in config.modules) {
                    M(config.modules[selector], selector, miid);
                }
            }

            // add parent miid to waitFor config
            if (parentMiid) {
                config.waitFor = config.waitFor || [];
                config.waitFor.push(parentMiid);
            }

            // create module instance
            var miidMono = {
                config: config,
                state: 0,
                events: {},
                miid: miid,
                name: config.name,
                ws: {}
            };
            miids[miid] = Object.clone(Mono);
            miids[miid].mono = miidMono;

            // load css
            if (config.css) {
                for (var i in config.css) {
                    if (!css[config.css[i]]) {

                        css[config.css[i]] = 1;

                        var link = document.createElement('link');
                        link.setAttribute('rel', 'stylesheet');
                        link.setAttribute('href', config.css[i]);

                        head.appendChild(link);
                    }
                }
            }

            // load html
            if (config.html) {
                this.emit('getHtml', null, config.html, function (err, html) {

                    // create module container
                    var container = document.createElement('div');
                    container.setAttribute('id', miid);
                    container.innerHTML = html || '';

                    // append the dom container to the module
                    miids[miid].mono.dom = container;

                    if (++miidMono.state === 2) {
                        initModule(miid, target);
                    }
                });

            } else {
                ++miidMono.state;
            }

            // load scripts and init module
            if (config.scripts && config.scripts.length > 0) {
                loadJS(miid, config.name, config.scripts, function (miid, moduleConstructor) {

                    // create module
                    if (miidMono.config.name) {
                        miidMono.init = miidMono.init || moduleConstructor;

                        // listen to send events
                        if (config.events) {
                            for (var i = 0, l = config.events.length; i < l; ++i) {
                                miids[miid].on(config.events[i], send(config.events[i]));
                            }
                        }

                        if (++miidMono.state === 2) {
                            initModule(miid, target);
                        }
                    }
                });

            } else if (++miidMono.state === 2) {
                initModule(miid, target);
            }
        });
    };
    // wrapping script
    core.load.wrap = function (name, module) {
        modules[name] = module;
        miids.M.emit(name);
    };

    // listen to core load event
    core.on('load', send('load'));
    core.on('getHtml', send('getHtml'));

    // miid cache
    miids.M = core;

    return core.load;
})({});
<|endoftext|>