<commit_before>/**
 * Connection
 */

var crypto = require('crypto');
var inherits = require('util').inherits;
var events = require('events');
var tls = require('tls');
var net = require('net');

/**
 * Connection constructor
 *
 * @param  string host
 * @param  int port
 * @param  object options
 * @param  function callback
 */
var Connection = function(host, port, options, callback) {

    events.EventEmitter.call(this);

    this.stream = null;
    this.connected = false;
    this.buffer = [];
    this.requestBuffer = [];

    this.host = host;
    this.port = port;

    options = options || {};
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }
    this.options = options;

    if (callback) {
        this.connect(callback);
    }
};

inherits(Connection, events.EventEmitter);

/**
 * Initiate stream connection
 *
 * @param  function callback
 */
Connection.prototype.connect = function(callback) {

    var self = this;
    var proto = this.options.clear ? net : tls;

    this.stream = proto.connect({
        host: this.host,
        port: this.port,
        rejectUnauthorized: this.options.verifyCert === false ? false : true
    }, function(stream) {
        self.connected = true;
        self.flushRequestBuffer();
        callback && callback(self);
        self.emit('connect');
    });
    this.stream.on('error', this.close.bind(this));
    this.stream.on('data', this.receive.bind(this));
    this.stream.on('close', this.close.bind(this));
    this.stream.on('timeout', this.close.bind(this));
    this.stream.setEncoding('utf8');
    this.stream.setTimeout(10000);
};

/**
 * Call remote server method
 *
 * @param  string method
 * ...
 * @param  function response (last argument)
 */
Connection.prototype.request = function(method) {

    // Copy args to avoid leaking
    var args = new Array(arguments.length);
    for (var i = 0; i < arguments.length; i++) {
        args[i] = arguments[i];
    }

    this.requestBuffer.push(args);

    if (!this.connected) {
        if (!this.stream) {
            this.connect();
        }
        return;
    }

    var request = JSON.stringify(args.slice(0, -1));
    this.stream.write(request + "\n");
};

/**
 * Receive data from the server
 *
 * @param  object buffer
 */
Connection.prototype.receive = function(buffer) {

    // Split buffer data on newline char
    for (var i = 0, j = 0; i < buffer.length; i++) {
        if (buffer[i] === '\n') {
            this.buffer.push(buffer.slice(j, i));

            var data = this.buffer.join('');

            this.buffer = [];
            this.receiveResponse(data);

            j = i + 1;
        }
    }
    if (j < buffer.length) {
        this.buffer.push(buffer.slice(j, buffer.length));
    }
};

/**
 * Handle server response data (JSON string)
 *
 * @param  string data
 */
Connection.prototype.receiveResponse = function(data) {

    var response;
    var responder = this.requestBuffer.shift().pop();

    try {
        response = JSON.parse(data);
    } catch (err) {
        this.emit('error.protocol', 'Unable to parse response from server ('+data+')');
        // TODO: respond with something to the responder
        return;
    }

    if (!response || typeof response !== 'object') {
        this.emit('error.protocol', 'Invalid response from server ('+data+')');
        // TODO: respond with something to the responder
        return;
    }

    if (response.$error) {
        this.emit('error.server', response.$error);
    }
    if (response.$end) {
        this.close();
    }

    // Note: response always returns in the same order as request
    if (typeof responder === 'function') {
        responder(response);
    }
};

/**
 * Close this connection
 */
Connection.prototype.close = function(error) {

    if (!this.connected) {
        return;
    }

    if (this.stream && this.stream.writable) {
        this.stream.end();
    }

    this.connected = false;
    this.stream = null;
    this.emit('close');

    var hasRequests = this.requestBuffer.length;
    if (hasRequests || error) {
        this.emit('error.server', error || 'Server unexpectedly closed network connection.');
    }

    var responder;
    while (--hasRequests >= 0) {
        responder = this.requestBuffer.shift().pop();
        responder({
          $status: 500,
          $data: null
        });
    }
};

/**
 * Flush local request buffer when connected
 *
 * @return void
 */
Connection.prototype.flushRequestBuffer = function() {

    if (!this.connected) {
        return;
    }

    var hasRequests = this.requestBuffer.length;
    while (--hasRequests >= 0) {
        this.request.apply(this, this.requestBuffer.shift());
    }
};

// Exports
exports.Connection = Connection;
<commit_msg>Add responders to all errors, include proper content in the response object<commit_after>/**
 * Connection
 */

var crypto = require('crypto');
var inherits = require('util').inherits;
var events = require('events');
var tls = require('tls');
var net = require('net');

var DEFAULT_NETWORK_ERROR = 'Server unexpectedly closed network connection.';

/**
 * Connection constructor
 *
 * @param  string host
 * @param  int port
 * @param  object options
 * @param  function callback
 */
var Connection = function(host, port, options, callback) {

    events.EventEmitter.call(this);

    this.stream = null;
    this.connected = false;
    this.buffer = [];
    this.requestBuffer = [];

    this.host = host;
    this.port = port;

    options = options || {};
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }
    this.options = options;

    if (callback) {
        this.connect(callback);
    }
};

inherits(Connection, events.EventEmitter);

/**
 * Initiate stream connection
 *
 * @param  function callback
 */
Connection.prototype.connect = function(callback) {

    var self = this;
    var proto = this.options.clear ? net : tls;

    this.stream = proto.connect({
        host: this.host,
        port: this.port,
        rejectUnauthorized: this.options.verifyCert === false ? false : true
    }, function(stream) {
        self.connected = true;
        self.flushRequestBuffer();
        callback && callback(self);
        self.emit('connect');
    });
    this.stream.on('error', this.close.bind(this));
    this.stream.on('data', this.receive.bind(this));
    this.stream.on('close', this.close.bind(this));
    this.stream.on('timeout', this.close.bind(this));
    this.stream.setEncoding('utf8');
    this.stream.setTimeout(10000);
};

/**
 * Call remote server method
 *
 * @param  string method
 * ...
 * @param  function response (last argument)
 */
Connection.prototype.request = function(method) {

    // Copy args to avoid leaking
    var args = new Array(arguments.length);
    for (var i = 0; i < arguments.length; i++) {
        args[i] = arguments[i];
    }

    this.requestBuffer.push(args);

    if (!this.connected) {
        if (!this.stream) {
            this.connect();
        }
        return;
    }

    var request = JSON.stringify(args.slice(0, -1));
    this.stream.write(request + "\n");
};

/**
 * Receive data from the server
 *
 * @param  object buffer
 */
Connection.prototype.receive = function(buffer) {

    // Split buffer data on newline char
    for (var i = 0, j = 0; i < buffer.length; i++) {
        if (buffer[i] === '\n') {
            this.buffer.push(buffer.slice(j, i));

            var data = this.buffer.join('');

            this.buffer = [];
            this.receiveResponse(data);

            j = i + 1;
        }
    }
    if (j < buffer.length) {
        this.buffer.push(buffer.slice(j, buffer.length));
    }
};

/**
 * Handle server response data (JSON string)
 *
 * @param  string data
 */
Connection.prototype.receiveResponse = function(data) {

    var response;
    var responder = this.requestBuffer.shift().pop();

    try {
        response = JSON.parse(data);
    } catch (err) {
        response = 'Unable to parse response from server ('+data+')';
        this.emit('error.protocol', response);
        return responder({
            $status: 500,
            $error: response
        });
    }

    if (!response || typeof response !== 'object') {
        response = 'Invalid response from server ('+data+')';
        this.emit('error.protocol', response);
        return responder({
            $status: 500,
            $error: response
        });
    }

    if (response.$error) {
        this.emit('error.server', response.$error);
    }
    if (response.$end) {
        this.close();
    }

    // Note: response always returns in the same order as request
    if (typeof responder === 'function') {
        responder(response);
    }
};

/**
 * Close this connection
 */
Connection.prototype.close = function(error) {

    if (!this.connected) {
        return;
    }

    if (this.stream && this.stream.writable) {
        this.stream.end();
    }

    this.connected = false;
    this.stream = null;
    this.emit('close');

    var hasRequests = this.requestBuffer.length;
    if (hasRequests || error) {
        this.emit('error.network', error || DEFAULT_NETWORK_ERROR);
    }

    var responder;
    while (--hasRequests >= 0) {
        responder = this.requestBuffer.shift().pop();
        responder({
            $status: 500,
            $error: error || DEFAULT_NETWORK_ERROR
        });
    }
};

/**
 * Flush local request buffer when connected
 *
 * @return void
 */
Connection.prototype.flushRequestBuffer = function() {

    if (!this.connected) {
        return;
    }

    var hasRequests = this.requestBuffer.length;
    while (--hasRequests >= 0) {
        this.request.apply(this, this.requestBuffer.shift());
    }
};

// Exports
exports.Connection = Connection;
<|endoftext|>


<commit_before>(function(module) {
	"use strict";
	var util;
	/* Check if clientside or serverside */
	if( typeof module === 'undefined') {
		window.pagination = {};
		module = {
			exports : window.pagination
		};
		util = {
			inherits : function(subc, superc) {
				if(!superc || !subc) {
					throw new Error("extend failed, please check that all dependencies are included.");
				}
				var F = function() {};
				F.prototype = superc.prototype;
				subc.prototype = new F();
				subc.prototype.constructor = subc;
				subc.superclass = superc.prototype;
				if(superc.prototype.constructor === Object.prototype.constructor) {
					superc.prototype.constructor = superc;
				}
			}
		};

		if(!Object.keys) {
			Object.keys = function(o) {
				var rt = [], p, hasOwnProperty = Object.prototype.hasOwnProperty;
				if(o !== Object(o)) {
					throw new TypeError('Object.keys called on a non-object');
				}
				for(p in o) {
					if(hasOwnProperty.call(o, p)) {
						rt.push(p);
					}
				}
				return rt;
			};
		}
	} else {
		util = require('util');
	}

	var TemplateEngine = module.exports.TemplateEngine = (function() {
		var cache = {};
		var parse = function(str, options) {
			var options = options || {}
			,open = options.open || '<%'
			,close = options.close || '%>';
			var prefix, postfix, i, end, len, js, start, n;
			var buf = ["var buf = [];",
			           "\nwith (paginationData) {",
			           "\n  buf.push('"];
			for (i = 0, len = str.length; i < len; ++i) {
				if (str.slice(i, open.length + i) === open) {
					i += open.length;
					switch (str.substr(i, 1)) {
						case '=':
							prefix = "', escape(";
							postfix = "), '";
							++i;
							break;
						case '-':
							prefix = "', (" ;
							postfix = "), '";
							++i;
							break;
						default:
							prefix = "');";
							postfix = "; buf.push('";
					}
					end = str.indexOf(close, i);
					js = str.substring(i, end);
					start = i; n = 0;
					while (~(n = js.indexOf("\n", n))) n++;
					buf.push(prefix, js, postfix);
					i += end - start + close.length - 1;
				} else if (str.substr(i, 1) === "\\") {
					buf.push("\\\\");
				} else if (str.substr(i, 1) === "'") {
					buf.push("\\'");
				} else if (str.substr(i, 1) === "\r") {
					buf.push(" ");
				} else if (str.substr(i, 1) === "\n") {
					buf.push("\\n");
				} else {
					buf.push(str.substr(i, 1));
				}
			}
			buf.push("');\n}\nreturn buf.join('');");
			return buf.join('');
		};
		var _escape = function(text) {
			return String(text).replace(/&(?!\w+;)/g, '&').replace(/</g, '<')
					.replace(/>/g, '>').replace(/"/g, '"');
		};
		var _compile = function(str, options) {
			var fn = new Function('paginationData', parse(str, options));
			return function(paginationData){
				return fn.call(this, paginationData, _escape);
			};
		};

		var compile = function(str, options){
			var fn, options = options || {};
			if (options.cache) {
				if (options.id) {
					fn = cache[options.id] || (cache[options.id] = _compile(str, options));
				} else {
					throw new Error('"cache" option requires "id"');
				}
			} else {
				fn = _compile(str, options);
			}
			return fn;
		};

		return {
			parse : parse,
			compile : compile
		};
	})();

	var translations = {
		'NEXT' : 'Next',
		'PREVIOUS' : 'Previous',
		'FIRST' : 'First',
		'LAST' : 'Last',
		'CURRENT_PAGE_REPORT' : 'Results {FromResult} - {ToResult} of {TotalResult}'
	};

	var translationKeys = Object.keys(translations);

	var translationCache = {
		CURRENT_PAGE_REPORT : {}
	};

	var translator = function(str) {
		return translations[str];
	};
	var Paginator = function(options) {
		var keys, i, len;
		/* validate in this.set if needed */
		this.options = {
			totalResult : 0,
			prelink : '',
			rowsPerPage : 10,
			pageLinks : 5,
			current : 1,
			translator : translator,
			translationCache : false,
			translationCacheKey : 'en',
			pageParamName : 'page',
			slashSeparator : false
		};
		for( keys = Object.keys(options), i = 0, len = keys.length; i < len; i++) {
			this.set(keys[i], options[keys[i]]);
		}
		this._result = null;
	};

	module.exports.Paginator = Paginator;

	Paginator.prototype = {
		getPaginationData : function() {
			if(!this._result) {
				this._result = this.calc();
			}
			return this._result;
		},
		calc : function() {
			var totalResult = this.options.totalResult;
			var pageLinks = this.options.pageLinks;
			var rowsPerPage = this.options.rowsPerPage;
			var current = this.options.current;
			var startPage, endPage, pageCount;
			var oldPageLinks = (pageLinks % 2 === 0) ? 1 : 0, i, half;
			var result = {
				prelink : this.options.prelink,
				current : current,
				previous : null,
				next : null,
				first : null,
				last : null,
				range : [],
				fromResult : null,
				toResult : null,
				totalResult : totalResult,
				pageCount : null
			};
			/* zero division; negative */
			if(rowsPerPage <= 0) {
				return result;
			}
			pageCount = Math.ceil(totalResult / rowsPerPage);
			result.pageCount = pageCount;
			if(pageCount < 2) {
				result.fromResult = 1;
				result.toResult = totalResult;
				return result;
			}

			if(current > pageCount) {
				current = pageCount;
				result.current = current;
			}
			half = Math.floor(pageLinks / 2);
			startPage = current - half;
			endPage = current + half - oldPageLinks;

			if(startPage < 1) {
				startPage = 1;
				endPage = startPage + pageLinks -1;
				if(endPage > pageCount) {
					endPage = pageCount;
				}
			}

			if(endPage > pageCount) {
				endPage = pageCount;
				startPage = endPage - pageLinks + 1;
				if(startPage < 1) {
					startPage = 1;
				}
			}

			for( i = startPage; i <= endPage; i++) {
				result.range.push(i);
			}

			if(current > 1) {
				result.first = 1;
				result.previous = current - 1;
			}

			if(current < pageCount) {
				result.last = pageCount;
				result.next = current + 1;
			}

			result.fromResult = (current - 1) * rowsPerPage + 1;
			if(current === pageCount) {
				result.toResult = totalResult;
			} else {
				result.toResult = result.fromResult + rowsPerPage - 1;
			}

			return result;
		},
		set : function(option, value) {
			if(this.options.hasOwnProperty(option)) {
				switch(option) {
					case 'current':
					case 'totalResult':
					case 'pageLinks':
					case 'rowsPerPage':
						value = parseInt(value, 10);
						if (isNaN(value)) {
							throw new Error('Invalid value for "' + option + '", expected an integer');
						}
						break;
					case 'translator':
						if (!(value && value.constructor && value.call && value.apply)) {
							throw new Error('Translator must be a function');
						}
						break;
					case 'translationCacheKey':
					case 'pageParamName':
					case 'prelink':
						value = String(value);
						break;
				}
				this.options[option] = value;
				if (this._result) {
					this._result = null;
				}
			}
		},
		preparePreLink : function(prelink) {
			if (this.options.slashSeparator) {
				if (prelink[prelink.length - 1] !== '/') {
					prelink += '/';
				}
				return prelink + this.options.pageParamName + '/';
			}
			if(prelink.indexOf('?') !== -1) {
				if(prelink[prelink.length - 1] !== '?' && prelink[prelink.length - 1] !== '&') {
					prelink += '&';
				}
			} else {
				prelink += '?';
			}

			return prelink + this.options.pageParamName + '=';
		},
		render : function() {
			throw new Error('Implement');
		}
	};

	var SearchPaginator = function(options) {
		Paginator.call(this, options);

	};

	module.exports.SearchPaginator = SearchPaginator;

	util.inherits(SearchPaginator, Paginator);

	SearchPaginator.prototype.render = function() {
		var i, len, className, prelink;
		var result = this.getPaginationData();
		var html = '
';

		if(result.pageCount < 2) {
			html += '
';
			return html;
		}

		prelink = this.preparePreLink(result.prelink);

		if(result.previous) {
			html += '' + this.options.translator('PREVIOUS') + '';
		}

		if(result.range.length) {
			for( i = 0, len = result.range.length; i < len; i++) {
				className = 'paginator-page';

				if(result.range[i] === result.current) {
					className = 'paginator-current';
				}
				if(i === 0) {
					className += ' paginator-page-first';
				} else if(i === len - 1) {
					className += ' paginator-page-last';
				}
				html += '' + result.range[i] + '';
			}
		}
		if(result.next) {
			html += '' + this.options.translator('NEXT') + '';
		}
		html += '
';
		return html;
	};
	var ItemPaginator = function(options) {
		Paginator.call(this, options);
		this.set('pageLinks', 1);

	};
	module.exports.ItemPaginator = ItemPaginator;

	util.inherits(ItemPaginator, Paginator);

	ItemPaginator.prototype.renderCurrentPageReport = function(fromResult, toResult, totalResult) {
		var template;
		if(!this.options.translationCache) {
			return this.options.translator('CURRENT_PAGE_REPORT').replace('{FromResult}', fromResult).replace('{ToResult}', toResult).replace('{TotalResult}', totalResult);
		}
		if(!translationCache.CURRENT_PAGE_REPORT.hasOwnProperty(this.options.translationCacheKey)) {
			template = "return '" + (this.options.translator('CURRENT_PAGE_REPORT').replace("'", "\'").replace('{FromResult}', "' + fromResult + '").replace('{ToResult}', "' + toResult + '").replace('{TotalResult}', "' + totalResult + '")) + "';";
			translationCache.CURRENT_PAGE_REPORT[this.options.translationCacheKey] = new Function('fromResult, toResult, totalResult', template);
		}
		return translationCache.CURRENT_PAGE_REPORT[this.options.translationCacheKey](fromResult, toResult, totalResult);
	};

	ItemPaginator.prototype.render = function() {
		var result = this.getPaginationData();
		var prelink = this.preparePreLink(result.prelink);
		var html = '
';
		html += '';
		html += this.renderCurrentPageReport(result.fromResult, result.toResult, result.totalResult);
		html += '';

		if(result.first) {
			html += '' + this.options.translator('FIRST') + '';
		} else {
			html += '' + this.options.translator('FIRST') + '';
		}

		if(result.previous) {
			html += '' + this.options.translator('PREVIOUS') + '';
		} else {
			html += '' + this.options.translator('PREVIOUS') + '';
		}

		if(result.next) {
			html += '' + this.options.translator('NEXT') + '';
		} else {
			html += '' + this.options.translator('NEXT') + '';
		}

		if(result.last) {
			html += '' + this.options.translator('LAST') + '';
		} else {
			html += '' + this.options.translator('LAST') + '';
		}
		html += '
';
		return html;
	};

	var TemplatePaginator = function(options) {
		var template = options.template;
		if (!template) {
			throw new Error('Template compile to function needed');
		}

		if (!(template.constructor && template.call && template.apply)) {
			template = TemplateEngine.compile(String(template), options);
		}
		Paginator.call(this, options);
		this.renderer = template;
	};
	module.exports.TemplatePaginator = TemplatePaginator;
	util.inherits(TemplatePaginator, Paginator);

	TemplatePaginator.prototype.render = function() {
		var i, len, data = this.getPaginationData();
		data.preparedPreLink = this.preparePreLink(data.prelink);
		data.translations = {};
		for (i = 0, len = translationKeys.length; i < len; i++) {
			data.translations[translationKeys[i]] = this.options.translator(translationKeys[i]);
		}
		return this.renderer(data);
	};

	module.exports.create = function(type, options) {
		switch(type) {
			case 'template':
				return new TemplatePaginator(options);
			case 'item':
				return new ItemPaginator(options);
			case 'search':
			default:
				return new SearchPaginator(options);
		}
	};
})(( typeof module !== 'undefined' && module.exports) ? module : undefined);
<commit_msg>Make util public for browser case so any one want to extend and override render to suite the need<commit_after>(function(module) {
	"use strict";
	var util;
	/* Check if clientside or serverside */
	if( typeof module === 'undefined') {
		window.pagination = {};
		module = {
			exports : window.pagination
		};
		util = {
			inherits : function(subc, superc) {
				if(!superc || !subc) {
					throw new Error("extend failed, please check that all dependencies are included.");
				}
				var F = function() {};
				F.prototype = superc.prototype;
				subc.prototype = new F();
				subc.prototype.constructor = subc;
				subc.superclass = superc.prototype;
				if(superc.prototype.constructor === Object.prototype.constructor) {
					superc.prototype.constructor = superc;
				}
			}
		};
		module.exports.util = util;
		if(!Object.keys) {
			Object.keys = function(o) {
				var rt = [], p, hasOwnProperty = Object.prototype.hasOwnProperty;
				if(o !== Object(o)) {
					throw new TypeError('Object.keys called on a non-object');
				}
				for(p in o) {
					if(hasOwnProperty.call(o, p)) {
						rt.push(p);
					}
				}
				return rt;
			};
		}
	} else {
		util = require('util');
	}

	var TemplateEngine = module.exports.TemplateEngine = (function() {
		var cache = {};
		var parse = function(str, options) {
			var options = options || {}
			,open = options.open || '<%'
			,close = options.close || '%>';
			var prefix, postfix, i, end, len, js, start, n;
			var buf = ["var buf = [];",
			           "\nwith (paginationData) {",
			           "\n  buf.push('"];
			for (i = 0, len = str.length; i < len; ++i) {
				if (str.slice(i, open.length + i) === open) {
					i += open.length;
					switch (str.substr(i, 1)) {
						case '=':
							prefix = "', escape(";
							postfix = "), '";
							++i;
							break;
						case '-':
							prefix = "', (" ;
							postfix = "), '";
							++i;
							break;
						default:
							prefix = "');";
							postfix = "; buf.push('";
					}
					end = str.indexOf(close, i);
					js = str.substring(i, end);
					start = i; n = 0;
					while (~(n = js.indexOf("\n", n))) n++;
					buf.push(prefix, js, postfix);
					i += end - start + close.length - 1;
				} else if (str.substr(i, 1) === "\\") {
					buf.push("\\\\");
				} else if (str.substr(i, 1) === "'") {
					buf.push("\\'");
				} else if (str.substr(i, 1) === "\r") {
					buf.push(" ");
				} else if (str.substr(i, 1) === "\n") {
					buf.push("\\n");
				} else {
					buf.push(str.substr(i, 1));
				}
			}
			buf.push("');\n}\nreturn buf.join('');");
			return buf.join('');
		};
		var _escape = function(text) {
			return String(text).replace(/&(?!\w+;)/g, '&').replace(/</g, '<')
					.replace(/>/g, '>').replace(/"/g, '"');
		};
		var _compile = function(str, options) {
			var fn = new Function('paginationData', parse(str, options));
			return function(paginationData){
				return fn.call(this, paginationData, _escape);
			};
		};

		var compile = function(str, options){
			var fn, options = options || {};
			if (options.cache) {
				if (options.id) {
					fn = cache[options.id] || (cache[options.id] = _compile(str, options));
				} else {
					throw new Error('"cache" option requires "id"');
				}
			} else {
				fn = _compile(str, options);
			}
			return fn;
		};

		return {
			parse : parse,
			compile : compile
		};
	})();

	var translations = {
		'NEXT' : 'Next',
		'PREVIOUS' : 'Previous',
		'FIRST' : 'First',
		'LAST' : 'Last',
		'CURRENT_PAGE_REPORT' : 'Results {FromResult} - {ToResult} of {TotalResult}'
	};

	var translationKeys = Object.keys(translations);

	var translationCache = {
		CURRENT_PAGE_REPORT : {}
	};

	var translator = function(str) {
		return translations[str];
	};
	var Paginator = function(options) {
		var keys, i, len;
		/* validate in this.set if needed */
		this.options = {
			totalResult : 0,
			prelink : '',
			rowsPerPage : 10,
			pageLinks : 5,
			current : 1,
			translator : translator,
			translationCache : false,
			translationCacheKey : 'en',
			pageParamName : 'page',
			slashSeparator : false
		};
		for( keys = Object.keys(options), i = 0, len = keys.length; i < len; i++) {
			this.set(keys[i], options[keys[i]]);
		}
		this._result = null;
	};

	module.exports.Paginator = Paginator;

	Paginator.prototype = {
		getPaginationData : function() {
			if(!this._result) {
				this._result = this.calc();
			}
			return this._result;
		},
		calc : function() {
			var totalResult = this.options.totalResult;
			var pageLinks = this.options.pageLinks;
			var rowsPerPage = this.options.rowsPerPage;
			var current = this.options.current;
			var startPage, endPage, pageCount;
			var oldPageLinks = (pageLinks % 2 === 0) ? 1 : 0, i, half;
			var result = {
				prelink : this.options.prelink,
				current : current,
				previous : null,
				next : null,
				first : null,
				last : null,
				range : [],
				fromResult : null,
				toResult : null,
				totalResult : totalResult,
				pageCount : null
			};
			/* zero division; negative */
			if(rowsPerPage <= 0) {
				return result;
			}
			pageCount = Math.ceil(totalResult / rowsPerPage);
			result.pageCount = pageCount;
			if(pageCount < 2) {
				result.fromResult = 1;
				result.toResult = totalResult;
				return result;
			}

			if(current > pageCount) {
				current = pageCount;
				result.current = current;
			}
			half = Math.floor(pageLinks / 2);
			startPage = current - half;
			endPage = current + half - oldPageLinks;

			if(startPage < 1) {
				startPage = 1;
				endPage = startPage + pageLinks -1;
				if(endPage > pageCount) {
					endPage = pageCount;
				}
			}

			if(endPage > pageCount) {
				endPage = pageCount;
				startPage = endPage - pageLinks + 1;
				if(startPage < 1) {
					startPage = 1;
				}
			}

			for( i = startPage; i <= endPage; i++) {
				result.range.push(i);
			}

			if(current > 1) {
				result.first = 1;
				result.previous = current - 1;
			}

			if(current < pageCount) {
				result.last = pageCount;
				result.next = current + 1;
			}

			result.fromResult = (current - 1) * rowsPerPage + 1;
			if(current === pageCount) {
				result.toResult = totalResult;
			} else {
				result.toResult = result.fromResult + rowsPerPage - 1;
			}

			return result;
		},
		set : function(option, value) {
			if(this.options.hasOwnProperty(option)) {
				switch(option) {
					case 'current':
					case 'totalResult':
					case 'pageLinks':
					case 'rowsPerPage':
						value = parseInt(value, 10);
						if (isNaN(value)) {
							throw new Error('Invalid value for "' + option + '", expected an integer');
						}
						break;
					case 'translator':
						if (!(value && value.constructor && value.call && value.apply)) {
							throw new Error('Translator must be a function');
						}
						break;
					case 'translationCacheKey':
					case 'pageParamName':
					case 'prelink':
						value = String(value);
						break;
				}
				this.options[option] = value;
				if (this._result) {
					this._result = null;
				}
			}
		},
		preparePreLink : function(prelink) {
			if (this.options.slashSeparator) {
				if (prelink[prelink.length - 1] !== '/') {
					prelink += '/';
				}
				return prelink + this.options.pageParamName + '/';
			}
			if(prelink.indexOf('?') !== -1) {
				if(prelink[prelink.length - 1] !== '?' && prelink[prelink.length - 1] !== '&') {
					prelink += '&';
				}
			} else {
				prelink += '?';
			}

			return prelink + this.options.pageParamName + '=';
		},
		render : function() {
			throw new Error('Implement');
		}
	};

	var SearchPaginator = function(options) {
		Paginator.call(this, options);

	};

	module.exports.SearchPaginator = SearchPaginator;

	util.inherits(SearchPaginator, Paginator);

	SearchPaginator.prototype.render = function() {
		var i, len, className, prelink;
		var result = this.getPaginationData();
		var html = '
';

		if(result.pageCount < 2) {
			html += '
';
			return html;
		}

		prelink = this.preparePreLink(result.prelink);

		if(result.previous) {
			html += '' + this.options.translator('PREVIOUS') + '';
		}

		if(result.range.length) {
			for( i = 0, len = result.range.length; i < len; i++) {
				className = 'paginator-page';

				if(result.range[i] === result.current) {
					className = 'paginator-current';
				}
				if(i === 0) {
					className += ' paginator-page-first';
				} else if(i === len - 1) {
					className += ' paginator-page-last';
				}
				html += '' + result.range[i] + '';
			}
		}
		if(result.next) {
			html += '' + this.options.translator('NEXT') + '';
		}
		html += '
';
		return html;
	};
	var ItemPaginator = function(options) {
		Paginator.call(this, options);
		this.set('pageLinks', 1);

	};
	module.exports.ItemPaginator = ItemPaginator;

	util.inherits(ItemPaginator, Paginator);

	ItemPaginator.prototype.renderCurrentPageReport = function(fromResult, toResult, totalResult) {
		var template;
		if(!this.options.translationCache) {
			return this.options.translator('CURRENT_PAGE_REPORT').replace('{FromResult}', fromResult).replace('{ToResult}', toResult).replace('{TotalResult}', totalResult);
		}
		if(!translationCache.CURRENT_PAGE_REPORT.hasOwnProperty(this.options.translationCacheKey)) {
			template = "return '" + (this.options.translator('CURRENT_PAGE_REPORT').replace("'", "\'").replace('{FromResult}', "' + fromResult + '").replace('{ToResult}', "' + toResult + '").replace('{TotalResult}', "' + totalResult + '")) + "';";
			translationCache.CURRENT_PAGE_REPORT[this.options.translationCacheKey] = new Function('fromResult, toResult, totalResult', template);
		}
		return translationCache.CURRENT_PAGE_REPORT[this.options.translationCacheKey](fromResult, toResult, totalResult);
	};

	ItemPaginator.prototype.render = function() {
		var result = this.getPaginationData();
		var prelink = this.preparePreLink(result.prelink);
		var html = '
';
		html += '';
		html += this.renderCurrentPageReport(result.fromResult, result.toResult, result.totalResult);
		html += '';

		if(result.first) {
			html += '' + this.options.translator('FIRST') + '';
		} else {
			html += '' + this.options.translator('FIRST') + '';
		}

		if(result.previous) {
			html += '' + this.options.translator('PREVIOUS') + '';
		} else {
			html += '' + this.options.translator('PREVIOUS') + '';
		}

		if(result.next) {
			html += '' + this.options.translator('NEXT') + '';
		} else {
			html += '' + this.options.translator('NEXT') + '';
		}

		if(result.last) {
			html += '' + this.options.translator('LAST') + '';
		} else {
			html += '' + this.options.translator('LAST') + '';
		}
		html += '
';
		return html;
	};

	var TemplatePaginator = function(options) {
		var template = options.template;
		if (!template) {
			throw new Error('Template compile to function needed');
		}

		if (!(template.constructor && template.call && template.apply)) {
			template = TemplateEngine.compile(String(template), options);
		}
		Paginator.call(this, options);
		this.renderer = template;
	};
	module.exports.TemplatePaginator = TemplatePaginator;
	util.inherits(TemplatePaginator, Paginator);

	TemplatePaginator.prototype.render = function() {
		var i, len, data = this.getPaginationData();
		data.preparedPreLink = this.preparePreLink(data.prelink);
		data.translations = {};
		for (i = 0, len = translationKeys.length; i < len; i++) {
			data.translations[translationKeys[i]] = this.options.translator(translationKeys[i]);
		}
		return this.renderer(data);
	};

	module.exports.create = function(type, options) {
		switch(type) {
			case 'template':
				return new TemplatePaginator(options);
			case 'item':
				return new ItemPaginator(options);
			case 'search':
			default:
				return new SearchPaginator(options);
		}
	};
})(( typeof module !== 'undefined' && module.exports) ? module : undefined);
<|endoftext|>


<commit_before>var net = require("net");
var xml = require("./xml2json");

module.exports = function (ccg) {
	ccg.prototype.connected = false;
	ccg.prototype.options = {
		reconnect: true,
		address: "localhost",
		port: 5250
	};

	var client;
	var disconnecting = false;
	var response = "";
	var readyForNextCommand = true;
	var waitingForDoubleTerminator = false;

	// Queue of commands and callbacks that haven't completed yet
	var commandQueue = [];

	// ccg.connect
	// -----------
	// Connect to Caspar CG
	//
	// `ccg.connect(host, port[, cb]);`
	//
	// or use `ccg.connect();` after setting `ccg.options`
	ccg.prototype.connect = function (host, port, cb) {
		var self = this;

		if (host && port) {
			ccg.options.host = host;
			ccg.option.port = port;
		} else if (!port && !cb) {
			cb = host;
		} else {
			return false;
		}

		disconnecting = false;

		client = net.connect({port: self.options.port, host: self.options.host}, function () {
			console.log("CCG: Connected to", self.options.address, self.options.port);
			self.connected = true;

			if (cb) {
				cb();
			}
		});

		client.on("data", onData);

		client.on("end", function () {
			console.log("CCG: Discconnected");
			self.connected = false;
			client = false;

			if (!disconnecting && self.options.reconnect) {
				self.connect();
			}
		});
	};

	// ccg.disconnect
	// --------------
	// Empties command queue and closes connection to Caspar CG
	ccg.prototype.disconnect = function () {
		disconnecting = true;
		commandQueue = [];

		if (client && this.connected) {
			client.end();
		}
	};

	// ccg.sendCommand
	// ---------------
	// Sends raw command to CasparCG
	ccg.prototype.sendCommand = function (command, doubleTerminator, cb) {
		if (!cb && typeof(doubleTerminator) == "function") {
			cb = doubleTerminator;
			doubleTerminator = false;
		}

		if (typeof(cb) != "function") cb = false;

		// check for connection first
		if (!this.connected) {
			if (cb) {
				cb(new Error("Not connected"));
			}

			return false;
		}

		if (command.substr(-2) != "\r\n") command += "\r\n";

		commandQueue.push([command, cb || false, doubleTerminator]);

		if (readyForNextCommand) {
			readyForNextCommand = false;
			client.write(commandQueue[0][0]);
		}
	};

	var onData = function (chunk) {
		response += chunk.toString();

		// check for complete response before doing anything
		if (
				(commandQueue[0][2] && response.substr(-4) == "\r\n\r\n") ||
				(!commandQueue[0][2] && response.substr(-2) == "\r\n")
			) {
			// response is complete remove command from queue
			var item = commandQueue.shift();
			var cb = item[1];

			// remove the terminator
			while (response.substr(-2) == "\r\n") {
				response = response.substr(0, response.length - 2);
			}

			var responseMessage = response.substr(0, response.indexOf("\r\n"));

			if (responseMessage == "") responseMessage = response;

			console.log("CCG:", responseMessage);

			// check for error code
			if (responseMessage.substr(0, 1) != "2") {
				// callback with error
				if (cb) {
					cb(new Error(response));
				}
			} else {
				if (cb) {
					// remove response code and message from response data
					response = response.substr(responseMessage.length + 2);

					// check for XML response
					if (response.substr(0, 1) == "<") {
						response = response.substr(response.indexOf("\n") + 1);
						xml(response, cb);
					} else {
						if (response == "") {
							cb();
						} else {
							response = response.split("\r\n");
							cb(null, response);
						}
					}
				}
			}

			response = "";
			if (commandQueue.length > 0) {
				// if there are queue commands send the next one
				client.write(commandQueue[0][0]);
			} else {
				// queue is empty we are ready for another command
				readyForNextCommand = true;
			}
		}
	};
};
<commit_msg>fix missing arguments in connection<commit_after>var net = require("net");
var xml = require("./xml2json");

module.exports = function (ccg) {
	ccg.prototype.connected = false;
	ccg.prototype.options = {
		reconnect: true,
		address: "localhost",
		port: 5250
	};

	var client;
	var disconnecting = false;
	var response = "";
	var readyForNextCommand = true;
	var waitingForDoubleTerminator = false;

	// Queue of commands and callbacks that haven't completed yet
	var commandQueue = [];

	// ccg.connect
	// -----------
	// Connect to Caspar CG
	//
	// `ccg.connect(host, port[, cb]);`
	//
	// or use `ccg.connect();` after setting `ccg.options`
	ccg.prototype.connect = function (host, port, cb) {
		var self = this;

		if (typeof(host) != "string") {
			cb = port;
			port = host;
			host = false;
		}

		if (typeof(port) != "number") {
			cb = port;
			port = false;
		}

		if (typeof(cb) != "function") cb = false;

		if (host) ccg.options.host = host;
		if (port) ccg.option.port = port;

		disconnecting = false;

		client = net.connect({port: self.options.port, host: self.options.host}, function () {
			console.log("CCG: Connected to", self.options.address, self.options.port);
			self.connected = true;

			if (cb) {
				cb();
			}
		});

		client.on("data", onData);

		client.on("end", function () {
			console.log("CCG: Discconnected");
			self.connected = false;
			client = false;

			if (!disconnecting && self.options.reconnect) {
				self.connect();
			}
		});
	};

	// ccg.disconnect
	// --------------
	// Empties command queue and closes connection to Caspar CG
	ccg.prototype.disconnect = function () {
		disconnecting = true;
		commandQueue = [];

		if (client && this.connected) {
			client.end();
		}
	};

	// ccg.sendCommand
	// ---------------
	// Sends raw command to CasparCG
	ccg.prototype.sendCommand = function (command, doubleTerminator, cb) {
		if (!cb && typeof(doubleTerminator) == "function") {
			cb = doubleTerminator;
			doubleTerminator = false;
		}

		if (typeof(cb) != "function") cb = false;

		// check for connection first
		if (!this.connected) {
			if (cb) {
				cb(new Error("Not connected"));
			}

			return false;
		}

		if (command.substr(-2) != "\r\n") command += "\r\n";

		commandQueue.push([command, cb || false, doubleTerminator]);

		if (readyForNextCommand) {
			readyForNextCommand = false;
			client.write(commandQueue[0][0]);
		}
	};

	var onData = function (chunk) {
		response += chunk.toString();

		// check for complete response before doing anything
		if (
				(commandQueue[0][2] && response.substr(-4) == "\r\n\r\n") ||
				(!commandQueue[0][2] && response.substr(-2) == "\r\n")
			) {
			// response is complete remove command from queue
			var item = commandQueue.shift();
			var cb = item[1];

			// remove the terminator
			while (response.substr(-2) == "\r\n") {
				response = response.substr(0, response.length - 2);
			}

			var responseMessage = response.substr(0, response.indexOf("\r\n"));

			if (responseMessage == "") responseMessage = response;

			console.log("CCG:", responseMessage);

			// check for error code
			if (responseMessage.substr(0, 1) != "2") {
				// callback with error
				if (cb) {
					cb(new Error(response));
				}
			} else {
				if (cb) {
					// remove response code and message from response data
					response = response.substr(responseMessage.length + 2);

					// check for XML response
					if (response.substr(0, 1) == "<") {
						response = response.substr(response.indexOf("\n") + 1);
						xml(response, cb);
					} else {
						if (response == "") {
							cb();
						} else {
							response = response.split("\r\n");
							cb(null, response);
						}
					}
				}
			}

			response = "";
			if (commandQueue.length > 0) {
				// if there are queue commands send the next one
				client.write(commandQueue[0][0]);
			} else {
				// queue is empty we are ready for another command
				readyForNextCommand = true;
			}
		}
	};
};
<|endoftext|>


<commit_before>var Cassandra = require('cassandra-driver') ;

var db = null;
var CassandraConnection = {
  Client: function(config, options) {
    var overriddenOptions = config;
    // The old driver used to specify hosts as the field, this is changed to contactPoints. Hosts is used as config param for backward compatibility
    overriddenOptions.contactPoints = config.hosts;
    if (config.keyspace) {
      overriddenOptions.keyspace = config.keyspace.toLowerCase();
    }
    db = new Cassandra.Client(overriddenOptions);
    return this;
  },
  execute: function(query, params, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }
    // This function also runs a prepared statement because that is required in the new versions of cassandra driver
    // As it gives an error whenever int is used to conform to the Number type. Cassandra assumed an int to be double
    // Recommended way of fixing this is preparing queries
    var overriddenOptions = Object.assign({}, options, {prepare: true});
    db.execute(query, params, overriddenOptions, callback);
  },
  stream: function(query, params, options, callback) {
    return db.stream(query, params, options, callback);
  },
  executePrepared: function(query, params, options, callback) {
    var overriddenOptions = Object.assign({}, options, {prepare: true});
    db.execute(query, params, overriddenOptions, callback);
  },
  executeBatch: function(queryArray, connection, options, callback) {
    if (options) {
      options.prepare = true;
    }
    else {
      options = {
        prepare: true
      };
    }
    if (!queryArray || queryArray.length == 0) {
      return callback();
    }
    db.batch(queryArray, options, callback);
  },
  shutdown: function(callback) {
    // The cassandra-driver does not have a close method for the db
    callback();
  }
};

module.exports = CassandraConnection;
<commit_msg>Prepared Stream Queries as well<commit_after>var Cassandra = require('cassandra-driver') ;

var db = null;
var CassandraConnection = {
  Client: function(config, options) {
    var overriddenOptions = config;
    // The old driver used to specify hosts as the field, this is changed to contactPoints. Hosts is used as config param for backward compatibility
    overriddenOptions.contactPoints = config.hosts;
    if (config.keyspace) {
      overriddenOptions.keyspace = config.keyspace.toLowerCase();
    }
    db = new Cassandra.Client(overriddenOptions);
    return this;
  },
  execute: function(query, params, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }
    // This function also runs a prepared statement because that is required in the new versions of cassandra driver
    // As it gives an error whenever int is used to conform to the Number type. Cassandra assumed an int to be double
    // Recommended way of fixing this is preparing queries
    var overriddenOptions = Object.assign({}, options, {prepare: true});
    db.execute(query, params, overriddenOptions, callback);
  },
  stream: function(query, params, options, callback) {
    var overriddenOptions = Object.assign({}, options, {prepare: true});
    return db.stream(query, params, overriddenOptions, callback);
  },
  executePrepared: function(query, params, options, callback) {
    var overriddenOptions = Object.assign({}, options, {prepare: true});
    db.execute(query, params, overriddenOptions, callback);
  },
  executeBatch: function(queryArray, connection, options, callback) {
    var overriddenOptions = Object.assign({}, options, {prepare: true});
    if (!queryArray || queryArray.length == 0) {
      return callback();
    }
    db.batch(queryArray, overriddenOptions, callback);
  },
  shutdown: function(callback) {
    // The cassandra-driver does not have a close method for the db
    callback();
  }
};

module.exports = CassandraConnection;
<|endoftext|>


<commit_before>var gracenode = require('../../gracenode');
var log = gracenode.log.create('server-controller');
var Request = require('./request');
var response = require('./response');
var serverError = require('./error');
var hook = require('../hooks/request');

var config = null;

module.exports.readConfig = function (configIn) {
	if (!configIn || !configIn.controllerPath) {
		throw new Error('invalid configurations:\n', JSON.stringify(configIn, null, 4));
	}
	config = configIn;
};

module.exports.exec = function (resource) {

	// check for not found error
	if (resource.parsedUrl.notFound) {
		return errorHandler(resource, new Request(resource.rawRequest, resource.rawResponse, resource.parsedUrl));
	}

	var request = new Request(resource.rawRequest, resource.rawResponse, resource.parsedUrl);
	request.setup(function (error) {
		if (error) {
			return errorHandler(resource);
		}
		module.exports.handle(resource, request);
	});
};

module.exports.handle = function (resource, requestObj) {
	// path: controllerDirectory/methodFile
	var path = gracenode.getRootPath() + config.controllerPath + resource.parsedUrl.controller + '/' + resource.parsedUrl.method + resource.parsedUrl.subdir;

	var method = require(path);

	// controller method
	var methodExec = method[requestObj.getMethod()] || null;

	// if the controller expects a GET and the request is HEAD, we let the HEAD request through
	if (!methodExec && requestObj.getMethod() === 'HEAD' && method.GET) {
		log.info(requestObj.url + ' expects GET, but HEAD is also allowed');
		methodExec = method.GET;
	}

	// validate request method
	if (!methodExec) {
		var msg = requestObj.url + ' does not accept "' + requestObj.getMethod() + '"';
		return errorHandler(resource, requestObj, msg, 400);
	}

	// check for mapped parameters (optional)
	var paramList = method.params || [];
	requestObj.setParamMap(paramList);

	// check for expected (optional)
	var expected = method.expected || null;
	if (expected) {
		var error = handleExpected(requestObj, expected);
		if (error) {
			return errorHandler(resource, requestObj, error.message, 400);
		}
	}

	// create response object
	var responseObj = response.create(resource, requestObj);

	// if this is pre-defined error controller, use the assigned status
	if (resource.error) {
		responseObj._setDefaultStatus(resource.error.status);
	}

	// override _errorHandler for responseObj.error()
	responseObj._errorHandler = function (error, status) {
		errorHandler(resource, requestObj, error, status);
	};

	// check for request hook
	var requestHookExecuted = hook.exec(resource, requestObj, responseObj, methodExec);
	if (requestHookExecuted) {
		return;
	}

	log.verbose(resource.parsedUrl.controller + '/' + resource.parsedUrl.method + ' [' + requestObj.getMethod() + '] executed');

	// invoke the controller method
	methodExec(requestObj, responseObj);
};

function handleExpected(req, expected) {
	// execute given validation function on each expected data
	for (var name in expected) {
		var data = req.data(name);
		var error = expected[name](data);
		log.verbose('expected request data validation [' + name + ']');
		if (error instanceof Error) {
			log.error('expected request data [' + name + '] validation failed (url:' + req.url + ')');
			return error;
		}
		log.verbose('expected request data validated [' + name + ']:', data);
	}
	return null;
}

function errorHandler(resource, requestObj, msg, status) {
	var sError = serverError.create(resource);
	if (requestObj) {
		sError.setRequest(requestObj);
	}
	sError.handle(msg, status);
}
<commit_msg>expected data validation logging improved 2<commit_after>var gracenode = require('../../gracenode');
var log = gracenode.log.create('server-controller');
var Request = require('./request');
var response = require('./response');
var serverError = require('./error');
var hook = require('../hooks/request');

var config = null;

module.exports.readConfig = function (configIn) {
	if (!configIn || !configIn.controllerPath) {
		throw new Error('invalid configurations:\n', JSON.stringify(configIn, null, 4));
	}
	config = configIn;
};

module.exports.exec = function (resource) {

	// check for not found error
	if (resource.parsedUrl.notFound) {
		return errorHandler(resource, new Request(resource.rawRequest, resource.rawResponse, resource.parsedUrl));
	}

	var request = new Request(resource.rawRequest, resource.rawResponse, resource.parsedUrl);
	request.setup(function (error) {
		if (error) {
			return errorHandler(resource);
		}
		module.exports.handle(resource, request);
	});
};

module.exports.handle = function (resource, requestObj) {
	// path: controllerDirectory/methodFile
	var path = gracenode.getRootPath() + config.controllerPath + resource.parsedUrl.controller + '/' + resource.parsedUrl.method + resource.parsedUrl.subdir;

	var method = require(path);

	// controller method
	var methodExec = method[requestObj.getMethod()] || null;

	// if the controller expects a GET and the request is HEAD, we let the HEAD request through
	if (!methodExec && requestObj.getMethod() === 'HEAD' && method.GET) {
		log.info(requestObj.url + ' expects GET, but HEAD is also allowed');
		methodExec = method.GET;
	}

	// validate request method
	if (!methodExec) {
		var msg = requestObj.url + ' does not accept "' + requestObj.getMethod() + '"';
		return errorHandler(resource, requestObj, msg, 400);
	}

	// check for mapped parameters (optional)
	var paramList = method.params || [];
	requestObj.setParamMap(paramList);

	// check for expected (optional)
	var expected = method.expected || null;
	if (expected) {
		var error = handleExpected(requestObj, expected);
		if (error) {
			return errorHandler(resource, requestObj, error.message, 400);
		}
	}

	// create response object
	var responseObj = response.create(resource, requestObj);

	// if this is pre-defined error controller, use the assigned status
	if (resource.error) {
		responseObj._setDefaultStatus(resource.error.status);
	}

	// override _errorHandler for responseObj.error()
	responseObj._errorHandler = function (error, status) {
		errorHandler(resource, requestObj, error, status);
	};

	// check for request hook
	var requestHookExecuted = hook.exec(resource, requestObj, responseObj, methodExec);
	if (requestHookExecuted) {
		return;
	}

	log.verbose(resource.parsedUrl.controller + '/' + resource.parsedUrl.method + ' [' + requestObj.getMethod() + '] executed');

	// invoke the controller method
	methodExec(requestObj, responseObj);
};

function handleExpected(req, expected) {
	// execute given validation function on each expected data
	for (var name in expected) {
		var data = req.data(name);
		var error = expected[name](data);
		if (error instanceof Error) {
			log.error('expected request data [' + name + '] validation failed (url:' + req.url + ')');
			return error;
		}
		log.verbose('expected request data [' + name + '] validated:', data, '(url:' + req.url + ')');
	}
	return null;
}

function errorHandler(resource, requestObj, msg, status) {
	var sError = serverError.create(resource);
	if (requestObj) {
		sError.setRequest(requestObj);
	}
	sError.handle(msg, status);
}
<|endoftext|>


<commit_before>'use strict';

const PinaLexer = ((() => {
    let _streamText;
    let _streamLen;
    let _cursorPos;
    let _line;

    let _operators = {
        '+':  'PLUS',
        '-':  'MINUS',
        '*':  'MULTIPLY',
        '.':  'PERIOD',
        '\\': 'BACKSLASH',
        ':':  'COLON',
        '%':  'PERCENT',
        '|':  'PIPE',
        '!':  'EXCLAMATION',
        '?':  'QUESTION',
        '#':  'POUND',
        '&':  'AMPERSAND',
        ';':  'SEMI',
        ',':  'COMMA',
        '(':  'L_PAREN',
        ')':  'R_PAREN',
        '<':  'L_ANG',
        '>':  'R_ANG',
        '{':  'L_BRACE',
        '}':  'R_BRACE',
        '[':  'L_BRACKET',
        ']':  'R_BRACKET',
        '=':  'EQUALS'
    };

    class Lexer {
        constructor(streamText) {
            _streamText = streamText;
            _streamLen = streamText.length;
            _cursorPos = 0;
            _line = 1;
        }

        static getCurrentChar() {
            return _streamText.charAt(_cursorPos);
        };

        static skipWhiteSpace() {
            while (_cursorPos < _streamLen) {
                let currentChar = Lexer.getCurrentChar();

                if (currentChar == ' ' || currentChar == '\t' ||
                    currentChar == '\r' || currentChar == '\n') {

                    if (currentChar == '\n') {
                        _line++;
                    }
                    _cursorPos++;
                }
                else {
                    break;
                }
            }
        }

        static isAlphaNumeric (char) {
            return (char >= 'a' && char <= 'z') ||
                (char >= 'A' && char <= 'Z') ||
                (char >= '0' && char <= '9') ||
                char == '_' || char == '-' || char == '$';
        }

        static isNumber (char) {
            return ((char >0 && char <=9) || char == ".");
        }

        static isOperator(char) {
            return _operators[char] !== undefined ? _operators[char] : false;
        };

        static isQuote(char) {
            return char == '"' || char == "'" || char == "`";
        };

        static isNewLine(char) {
            return char == '\n' || char == "\r";
        };

        static lexIdentifier() {
            let _endPos = _cursorPos + 1;

            while (_endPos < _streamLen &&
            Lexer.isAlphaNumeric(_streamText.charAt(_endPos))) {
                _endPos++;
            }

            let token = {
                tokenType: 'IDENTIFIER',
                value: _streamText.substring(_cursorPos, _endPos),
                position: _cursorPos,
                line: _line
            };

            _cursorPos = _endPos;

            return token;
        }

        static lexNumber() {
            let _endPos = _cursorPos + 1;

            while (_endPos < _streamLen &&
            Lexer.isNumber(_streamText.charAt(_endPos))) {
                _endPos++;
            }

            let token = {
                tokenType: 'NUMBER',
                value: _streamText.substring(_cursorPos, _endPos),
                position: _cursorPos,
                line: _line
            };

            _cursorPos = _endPos;

            return token;
        }

        static lexString() {
            let _endQuote;

            if (Lexer.getCurrentChar() == '"' ||
                Lexer.getCurrentChar() == "'" ||
                Lexer.getCurrentChar() == "`") {

                _endQuote = _streamText.indexOf(Lexer.getCurrentChar(), _cursorPos + 1);

                if (_endQuote !== -1) {
                    _cursorPos++;
                }
                else {
                    return false;
                }
            }

            let token = {
                tokenType: 'STRING',
                value: _streamText.substring(_cursorPos, _endQuote),
                position: _cursorPos,
                line: _line
            };

            _cursorPos = _endQuote + 1;

            return token;
        }

        static lexComment() {

            let _endPos = _cursorPos + 2;

            while (_endPos < _streamLen &&
            !Lexer.isNewLine(_streamText.charAt(_endPos))) {
                _endPos++;
            }

            let token = {
                tokenType: 'COMMENT',
                value: _streamText.substring(_cursorPos, _endPos),
                position: _cursorPos,
                line: _line
            };

            _cursorPos = _endPos;

            return token;
        }
    }

    Lexer.prototype.nextToken = () => {
        Lexer.skipWhiteSpace();

        if (_cursorPos >= _streamLen) {
            return false;
        }

        let currentChar = Lexer.getCurrentChar();

        if (currentChar == "/") {
            return Lexer.lexComment();
        }
        else if (Lexer.isNumber(currentChar)) {
            return Lexer.lexNumber();
        }
        else if (Lexer.isAlphaNumeric(currentChar)) {
            return Lexer.lexIdentifier();
        }
        else if (Lexer.isQuote(currentChar)) {
            return Lexer.lexString();
        }
        else if (Lexer.isOperator(currentChar)) {

            let token = {
                tokenType: _operators[currentChar],
                value: currentChar,
                position: _cursorPos,
                line: _line
            };

            _cursorPos++;

            return token;
        }
        else {
            _cursorPos++;
        }

        return false;
    };

    return Lexer;
})());

if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && typeof module.exports) {
        exports = module.exports = PinaLexer;
    }
}<commit_msg>fix: devide operator and number lexer zero problem.<commit_after>'use strict';

const PinaLexer = ((() => {
    let _streamText;
    let _streamLen;
    let _cursorPos;
    let _line;

    let _operators = {
        '+':  'PLUS',
        '-':  'MINUS',
        '*':  'MULTIPLY',
        '.':  'PERIOD',
        '\\': 'BACKSLASH',
        ':':  'COLON',
        '%':  'PERCENT',
        '|':  'PIPE',
        '!':  'EXCLAMATION',
        '?':  'QUESTION',
        '#':  'POUND',
        '&':  'AMPERSAND',
        ';':  'SEMI',
        ',':  'COMMA',
        '(':  'L_PAREN',
        ')':  'R_PAREN',
        '<':  'L_ANG',
        '>':  'R_ANG',
        '{':  'L_BRACE',
        '}':  'R_BRACE',
        '[':  'L_BRACKET',
        ']':  'R_BRACKET',
        '=':  'EQUALS'
    };

    class Lexer {
        constructor(streamText) {
            _streamText = streamText;
            _streamLen = streamText.length;
            _cursorPos = 0;
            _line = 1;
        }

        static getCurrentChar() {
            return _streamText.charAt(_cursorPos);
        };

        static skipWhiteSpace() {
            while (_cursorPos < _streamLen) {
                let currentChar = Lexer.getCurrentChar();

                if (currentChar == ' ' || currentChar == '\t' ||
                    currentChar == '\r' || currentChar == '\n') {

                    if (currentChar == '\n') {
                        _line++;
                    }
                    _cursorPos++;
                }
                else {
                    break;
                }
            }
        }

        static isAlphaNumeric (char) {
            return (char >= 'a' && char <= 'z') ||
                (char >= 'A' && char <= 'Z') ||
                (char >= '0' && char <= '9') ||
                char == '_' || char == '-' || char == '$';
        }

        static isNumber (char) {
            return ((char >= 0 && char <= 9) || char == ".");
        }

        static isOperator(char) {
            return _operators[char] !== undefined ? _operators[char] : false;
        };

        static isQuote(char) {
            return char == '"' || char == "'" || char == "`";
        };

        static isNewLine(char) {
            return char == '\n' || char == "\r";
        };

        static lexIdentifier() {
            let _endPos = _cursorPos + 1;

            while (_endPos < _streamLen &&
            Lexer.isAlphaNumeric(_streamText.charAt(_endPos))) {
                _endPos++;
            }

            let token = {
                tokenType: 'IDENTIFIER',
                value: _streamText.substring(_cursorPos, _endPos),
                position: _cursorPos,
                line: _line
            };

            _cursorPos = _endPos;

            return token;
        }

        static lexNumber() {
            let _endPos = _cursorPos + 1;

            while (_endPos < _streamLen &&
            Lexer.isNumber(_streamText.charAt(_endPos))) {
                _endPos++;
            }

            let token = {
                tokenType: 'NUMBER',
                value: _streamText.substring(_cursorPos, _endPos),
                position: _cursorPos,
                line: _line
            };

            _cursorPos = _endPos;

            return token;
        }

        static lexString() {
            let _endQuote;

            if (Lexer.getCurrentChar() == '"' ||
                Lexer.getCurrentChar() == "'" ||
                Lexer.getCurrentChar() == "`") {

                _endQuote = _streamText.indexOf(Lexer.getCurrentChar(), _cursorPos + 1);

                if (_endQuote !== -1) {
                    _cursorPos++;
                }
                else {
                    return false;
                }
            }

            let token = {
                tokenType: 'STRING',
                value: _streamText.substring(_cursorPos, _endQuote),
                position: _cursorPos,
                line: _line
            };

            _cursorPos = _endQuote + 1;

            return token;
        }

        static lexComment() {

            if (_streamText.charAt(_cursorPos + 1) != "/") {
                _cursorPos++;

                return {
                    tokenType: 'DEVIDE',
                    value: _streamText.substring(_cursorPos-1, _cursorPos),
                    position: _cursorPos,
                    line: _line
                };

            }

            let _endPos = _cursorPos + 2;

            while (_endPos < _streamLen &&
            !Lexer.isNewLine(_streamText.charAt(_endPos))) {
                _endPos++;
            }

            let token = {
                tokenType: 'COMMENT',
                value: _streamText.substring(_cursorPos, _endPos),
                position: _cursorPos,
                line: _line
            };

            _cursorPos = _endPos;

            return token;
        }
    }

    Lexer.prototype.nextToken = () => {
        Lexer.skipWhiteSpace();

        if (_cursorPos >= _streamLen) {
            return false;
        }

        let currentChar = Lexer.getCurrentChar();

        if (currentChar == "/") {
            return Lexer.lexComment();
        }
        else if (Lexer.isNumber(currentChar)) {
            return Lexer.lexNumber();
        }
        else if (Lexer.isAlphaNumeric(currentChar)) {
            return Lexer.lexIdentifier();
        }
        else if (Lexer.isQuote(currentChar)) {
            return Lexer.lexString();
        }
        else if (Lexer.isOperator(currentChar)) {

            let token = {
                tokenType: _operators[currentChar],
                value: currentChar,
                position: _cursorPos,
                line: _line
            };

            _cursorPos++;

            return token;
        }
        else {
            _cursorPos++;
        }

        return false;
    };

    return Lexer;
})());

if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && typeof module.exports) {
        exports = module.exports = PinaLexer;
    }
}<|endoftext|>


<commit_before>module.exports = {
    delegate: (args, req, res) => {
        var module = {
            raw: () => {
                return {
                    req: req,
                    res: res
                }
            },
            params: (key) => {
                return req.body[key];
            },
            redirect: (url, statusCode) => {
                if (statusCode) {
                    res.redirect(url, statusCode)
                }
                else {
                    res.redirect(url)
                }
            },
            render: (view, params) => {
                var renderString = await(args.view.render(req, res, view, params))
                module.finish(renderString)
            },
            finish: (body) => {
                module.finishWithCode(200, body)
            },
            finishWithCode: (code, body) => {
                res.status(code).send(body)
            }
        }

        return module
    }
}
<commit_msg>add request header getter.<commit_after>module.exports = {
    delegate: (args, req, res) => {
        var module = {
            raw: () => {
                return {
                    req: req,
                    res: res
                }
            },
            params: (key) => {
                return req.body[key];
            },
            headers: (key) => {
                return req.get(key);
            },
            redirect: (url, statusCode) => {
                if (statusCode) {
                    res.redirect(url, statusCode)
                }
                else {
                    res.redirect(url)
                }
            },
            render: (view, params) => {
                var renderString = await(args.view.render(req, res, view, params))
                module.finish(renderString)
            },
            finish: (body) => {
                module.finishWithCode(200, body)
            },
            finishWithCode: (code, body) => {
                res.status(code).send(body)
            }
        }

        return module
    }
}
<|endoftext|>


<commit_before>'use strict';

const { dirname, extname, resolve } = require('path');
const { readFile, stat, writeFile } = require('fs');
const co = require('co');
const mkdirp = require('mkdirp');
const listFilepaths = require('list-filepaths');
const prepareProblem = require('./prepare-problem');
const prepareSolution = require('./prepare-solution');

const mkdirpAsync = function (dirpath) {
  return new Promise((res, rej) => {
    mkdirp(dirpath, err => {
      if (err) {
        rej(err);
      } else {
        // Resolve with the original directory path instead of the default value, `made`
        res(dirpath);
      }
    });
  });
};

const readFileAsync = function (filepath, encoding) {
  return new Promise((res, rej) => {
    readFile(filepath, encoding, (err, data) => {
      if (err) {
        rej(err);
      } else {
        res(data);
      }
    });
  });
};

const statAsync = function (targetPath) {
  return new Promise((res, rej) => {
    stat(targetPath, (err, stats) => {
      if (err) {
        rej(err);
      } else {
        res(stats);
      }
    });
  });
};

const writeFileAsync = function (filepath, data) {
  return new Promise((res, rej) => {
    writeFile(filepath, data, err => {
      if (err) {
        rej(err);
      } else {
        // Resolve with the original file path instead of the default value, `undefined`
        res(filepath);
      }
    });
  });
};

module.exports = co.wrap(function* (directory) {
  const stats = yield statAsync(directory);
  if (!stats.isDirectory()) {
    // TODO: Consider throwing a custom Error
    throw new Error('Please enter a valid directory.');
  }

  const basepath = resolve(directory);
  const problemBasepath = `${basepath}-problem`;
  const solutionBasepath = `${basepath}-solution`;
  // Reject: .git, node_modules, dist, bower_components, tem, temp, jspm_packages, .DS_Store
  const gitIgnoreRegex = /\.git(\/|$)|node_modules(\/|$)|dist(\/|$)|bower_components(\/|$)|temp*(\/|$)|jspm_packages(\/|$)|\.DS_Store/g;
  const filepaths = yield listFilepaths(directory, { reject: gitIgnoreRegex });
  const problemPromiseMap = filepaths.map(filepath => {
    const problemDirpath = dirname(filepath).replace(basepath, problemBasepath);
    return mkdirpAsync(problemDirpath);
  });

  const solutionPromiseMap = filepaths.map(filepath => {
    const solutionDirpath = dirname(filepath).replace(basepath, solutionBasepath);
    return mkdirpAsync(solutionDirpath);
  });

  yield Promise.all([...problemPromiseMap, ...solutionPromiseMap]);
  const writePromiseMap = [];
  for (const filepath of filepaths) {
    const problemFilepath = filepath.replace(basepath, problemBasepath);
    const solutionFilepath = filepath.replace(basepath, solutionBasepath);
    const fileData = yield readFileAsync(filepath, 'utf8');
    if (extname(filepath) === '.js') {
      writePromiseMap.push(writeFileAsync(problemFilepath, prepareProblem(fileData)));
      writePromiseMap.push(writeFileAsync(solutionFilepath, prepareSolution(fileData)));
    } else {
      writePromiseMap.push(writeFileAsync(problemFilepath, fileData));
      writePromiseMap.push(writeFileAsync(solutionFilepath, fileData));
    }
  }

  return yield Promise.all(writePromiseMap);
});
<commit_msg>Copy files in parallel<commit_after>'use strict';

const { dirname, extname, resolve } = require('path');
const { readFile, stat, writeFile } = require('fs');
const co = require('co');
const mkdirp = require('mkdirp');
const listFilepaths = require('list-filepaths');
const prepareProblem = require('./prepare-problem');
const prepareSolution = require('./prepare-solution');

const mkdirpAsync = function (dirpath) {
  return new Promise((res, rej) => {
    mkdirp(dirpath, (err, made) => {
      if (err) {
        rej(err);
      } else {
        res(made);
      }
    });
  });
};

const readFileAsync = function (filepath, encoding) {
  return new Promise((res, rej) => {
    readFile(filepath, encoding, (err, data) => {
      if (err) {
        rej(err);
      } else {
        res(data);
      }
    });
  });
};

const statAsync = function (targetPath) {
  return new Promise((res, rej) => {
    stat(targetPath, (err, stats) => {
      if (err) {
        rej(err);
      } else {
        res(stats);
      }
    });
  });
};

const writeFileAsync = function (filepath, data) {
  return new Promise((res, rej) => {
    writeFile(filepath, data, err => {
      if (err) {
        rej(err);
      } else {
        // Resolve with the original file path instead of the default value, `undefined`
        res(filepath);
      }
    });
  });
};

module.exports = co.wrap(function* (directory) {
  const stats = yield statAsync(directory);
  if (!stats.isDirectory()) {
    throw new TypeError(`invalid directory, ${directory}`);
  }

  const basepath = resolve(directory);
  const problemBasepath = `${basepath}-problem`;
  const solutionBasepath = `${basepath}-solution`;
  // Reject: .git, node_modules, dist, bower_components, tem, temp, jspm_packages, .DS_Store
  const gitIgnoreRegex = /\.git(\/|$)|node_modules(\/|$)|dist(\/|$)|bower_components(\/|$)|temp*(\/|$)|jspm_packages(\/|$)|\.DS_Store/g;
  const filepaths = yield listFilepaths(directory, { reject: gitIgnoreRegex });
  const problemPromiseMap = filepaths.map(filepath => {
    const problemDirpath = dirname(filepath).replace(basepath, problemBasepath);
    const problemFilepath = filepath.replace(basepath, problemBasepath);
    return mkdirpAsync(problemDirpath)
      .then(() => readFileAsync(filepath, 'utf8'))
      .then(fileData => {
        if (extname(filepath) === '.js') {
          return writeFileAsync(problemFilepath, prepareProblem(fileData));
        }

        return writeFileAsync(problemFilepath, fileData);
      });
  });

  const solutionPromiseMap = filepaths.map(filepath => {
    const solutionDirpath = dirname(filepath).replace(basepath, solutionBasepath);
    const solutionFilepath = filepath.replace(basepath, solutionBasepath);
    return mkdirpAsync(solutionDirpath)
      .then(() => readFileAsync(filepath, 'utf8'))
      .then(fileData => {
        if (extname(filepath) === '.js') {
          return writeFileAsync(solutionFilepath, prepareSolution(fileData));
        }

        return writeFileAsync(solutionFilepath, fileData);
      });
  });

  return yield Promise.all([...problemPromiseMap, ...solutionPromiseMap]);
});
<|endoftext|>


<commit_before>/**
* @class Roguelike.Utils
* @classdesc In this class are the functions stored that are being
* used in other functions
*/
Roguelike.Utils = {

	/*
	* Function to generate a random number between two values
	* @param {int} from - The minimum number
	* @param {int} to - The maximum number
	*/
	randomNumber: function(from,to){

		return Math.floor(Math.random()*(to-from+1)+from);

	},

	/*
	* Function to extend the default options with the users options
	* @param {object} a - The original object to extend
	* @param {object} b - The new settings that override the original object
	*/
	extend: function(a, b){
		for(var key in b)
			if(b.hasOwnProperty(key))
				a[key] = b[key];
		return a;
	}

};
<commit_msg>Added a contains function that checks an object against a list.<commit_after>/**
* @class Roguelike.Utils
* @classdesc In this class are the functions stored that are being
* used in other functions
*/
Roguelike.Utils = {

	/*
	* Function to generate a random number between two values
	* @param {int} from - The minimum number
	* @param {int} to - The maximum number
	*/
	randomNumber: function(from,to){

		return Math.floor(Math.random()*(to-from+1)+from);

	},

	/*
	* Function to extend the default options with the users options
	* @param {object} a - The original object to extend
	* @param {object} b - The new settings that override the original object
	*/
	extend: function(a, b){

		//Check each property
		for(var key in b){
			if(b.hasOwnProperty(key)){
				a[key] = b[key];
			}
		}

		//Return the extended object
		return a;

	},

	contains: function(object, list){

		//Loop through the list
		for (var i = 0; i < list.length; i++) {

			//If the current list item equals the object, return true!
			if (list[i] === obj) {
				return true;
			}
		}

		//No results found, the list doesn't contain the object
		return false;

	}

};
<|endoftext|>


<commit_before>module.exports = createApp

const express = require('express')
const session = require('express-session')
const handlebars = require('express-handlebars')
const uuid = require('uuid')
const cors = require('cors')
const LDP = require('./ldp')
const LdpMiddleware = require('./ldp-middleware')
const corsProxy = require('./handlers/cors-proxy')
const authProxy = require('./handlers/auth-proxy')
const SolidHost = require('./models/solid-host')
const AccountManager = require('./models/account-manager')
const vhost = require('vhost')
const EmailService = require('./services/email-service')
const TokenService = require('./services/token-service')
const capabilityDiscovery = require('./capability-discovery')
const API = require('./api')
const errorPages = require('./handlers/error-pages')
const config = require('./server-config')
const defaults = require('../config/defaults')
const options = require('./handlers/options')
const debug = require('./debug')
const path = require('path')
const { routeResolvedFile } = require('./utils')
const ResourceMapper = require('./resource-mapper')
const aclCheck = require('@solid/acl-check')
const version = require('../package.json').version

const corsSettings = cors({
  methods: [
    'OPTIONS', 'HEAD', 'GET', 'PATCH', 'POST', 'PUT', 'DELETE'
  ],
  exposedHeaders: 'Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate',
  credentials: true,
  maxAge: 1728000,
  origin: true,
  preflightContinue: true
})

function createApp (argv = {}) {
  // Override default configs (defaults) with passed-in params (argv)
  argv = Object.assign({}, defaults, argv)

  argv.host = SolidHost.from({ port: argv.port, serverUri: argv.serverUri })

  argv.resourceMapper = new ResourceMapper({
    rootUrl: argv.serverUri,
    rootPath: path.resolve(argv.root || process.cwd()),
    includeHost: argv.multiuser,
    defaultContentType: argv.defaultContentType
  })

  const configPath = config.initConfigDir(argv)
  argv.templates = config.initTemplateDirs(configPath)

  const ldp = new LDP(argv)

  const app = express()

  initAppLocals(app, argv, ldp)
  initHeaders(app)
  initViews(app, configPath)
  initLoggers()

  // Serve the public 'common' directory (for shared CSS files, etc)
  app.use('/common', express.static(path.join(__dirname, '../common')))
  routeResolvedFile(app, '/common/js/', 'mashlib/dist/mashlib.js')
  routeResolvedFile(app, '/common/js/', 'mashlib/dist/mashlib.min.js')
  routeResolvedFile(app, '/common/js/', 'mashlib/dist/mashlib.min.js.map')
  routeResolvedFile(app, '/common/js/', 'solid-auth-client/dist-lib/solid-auth-client.bundle.js')
  routeResolvedFile(app, '/common/js/', 'solid-auth-client/dist-lib/solid-auth-client.bundle.js.map')
  app.use('/.well-known', express.static(path.join(__dirname, '../common/well-known')))

  // Serve bootstrap from it's node_module directory
  routeResolvedFile(app, '/common/css/', 'bootstrap/dist/css/bootstrap.min.css')
  routeResolvedFile(app, '/common/css/', 'bootstrap/dist/css/bootstrap.min.css.map')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.eot')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.svg')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.ttf')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.woff')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.woff2')

  // Serve OWASP password checker from it's node_module directory
  routeResolvedFile(app, '/common/js/', 'owasp-password-strength-test/owasp-password-strength-test.js')
  // Serve the TextEncoder polyfill
  routeResolvedFile(app, '/common/js/', 'text-encoder-lite/text-encoder-lite.min.js')

  // Add CORS proxy
  if (argv.proxy) {
    console.warn('The proxy configuration option has been renamed to corsProxy.')
    argv.corsProxy = argv.corsProxy || argv.proxy
    delete argv.proxy
  }
  if (argv.corsProxy) {
    corsProxy(app, argv.corsProxy)
  }

  // Options handler
  app.options('/*', options)

  // Set up API
  if (argv.apiApps) {
    app.use('/api/apps', express.static(argv.apiApps))
  }

  // Authenticate the user
  if (argv.webid) {
    initWebId(argv, app, ldp)
  }
  // Add Auth proxy (requires authentication)
  if (argv.authProxy) {
    authProxy(app, argv.authProxy)
  }

  // Attach the LDP middleware
  app.use('/', LdpMiddleware(corsSettings))

  // Errors
  app.use(errorPages.handler)

  return app
}

/**
 * Initializes `app.locals` parameters for downstream use (typically by route
 * handlers).
 *
 * @param app {Function} Express.js app instance
 * @param argv {Object} Config options hashmap
 * @param ldp {LDP}
 */
function initAppLocals (app, argv, ldp) {
  app.locals.ldp = ldp
  app.locals.appUrls = argv.apps  // used for service capability discovery
  app.locals.host = argv.host
  app.locals.authMethod = argv.auth
  app.locals.localAuth = argv.localAuth
  app.locals.tokenService = new TokenService()
  app.locals.enforceToc = argv.enforceToc
  app.locals.tocUri = argv.tocUri
  app.locals.disablePasswordChecks = argv.disablePasswordChecks

  if (argv.email && argv.email.host) {
    app.locals.emailService = new EmailService(argv.templates.email, argv.email)
  }
}

/**
 * Sets up headers common to all Solid requests (CORS-related, Allow, etc).
 *
 * @param app {Function} Express.js app instance
 */
function initHeaders (app) {
  app.use(corsSettings)

  app.use((req, res, next) => {
    res.set('X-Powered-By', 'solid-server/' + version)

    // Cors lib adds Vary: Origin automatically, but inreliably
    res.set('Vary', 'Accept, Authorization, Origin')

    // Set default Allow methods
    res.set('Allow', 'OPTIONS, HEAD, GET, PATCH, POST, PUT, DELETE')
    next()
  })

  app.use('/', capabilityDiscovery())
}

/**
 * Sets up the express rendering engine and views directory.
 *
 * @param app {Function} Express.js app
 * @param configPath {string}
 */
function initViews (app, configPath) {
  const viewsPath = config.initDefaultViews(configPath)

  app.set('views', viewsPath)
  app.engine('.hbs', handlebars({
    extname: '.hbs',
    partialsDir: viewsPath
  }))
  app.set('view engine', '.hbs')
}

/**
 * Sets up WebID-related functionality (account creation and authentication)
 *
 * @param argv {Object}
 * @param app {Function}
 * @param ldp {LDP}
 */
function initWebId (argv, app, ldp) {
  config.ensureWelcomePage(argv)

  // Store the user's session key in a cookie
  // (for same-domain browsing by people only)
  const useSecureCookies = !!argv.sslKey // use secure cookies when over HTTPS
  const sessionHandler = session(sessionSettings(useSecureCookies, argv.host))
  app.use(sessionHandler)
  // Reject cookies from third-party applications.
  // Otherwise, when a user is logged in to their Solid server,
  // any third-party application could perform authenticated requests
  // without permission by including the credentials set by the Solid server.
  app.use((req, res, next) => {
    const origin = req.get('origin')
    const trustedOrigins = argv.trustedOrigins
    const userId = req.session.userId
    // Exception: allow logout requests from all third-party apps
    // such that OIDC client can log out via cookie auth
    // TODO: remove this exception when OIDC clients
    // use Bearer token to authenticate instead of cookie
    // (https://github.com/solid/node-solid-server/pull/835#issuecomment-426429003)
    if (!argv.host.allowsSessionFor(userId, origin, trustedOrigins) && !isLogoutRequest(req)) {
      debug.authentication(`Rejecting session for ${userId} from ${origin}`)
      // Destroy session data
      delete req.session.userId
      // Ensure this modified session is not saved
      req.session.save = (done) => done()
    }
    next()
  })

  let accountManager = AccountManager.from({
    authMethod: argv.auth,
    emailService: app.locals.emailService,
    tokenService: app.locals.tokenService,
    host: argv.host,
    accountTemplatePath: argv.templates.account,
    store: ldp,
    multiuser: argv.multiuser
  })
  app.locals.accountManager = accountManager

  // Account Management API (create account, new cert)
  app.use('/', API.accounts.middleware(accountManager))

  // Set up authentication-related API endpoints and app.locals
  initAuthentication(app, argv)

  if (argv.multiuser) {
    app.use(vhost('*', LdpMiddleware(corsSettings)))
  }
}

function initLoggers () {
  aclCheck.configureLogger(debug.ACL)
}

/**
 * Determines whether the given request is a logout request
 */
function isLogoutRequest (req) {
  // TODO: this is a hack that hard-codes OIDC paths,
  // this code should live in the OIDC module
  return req.path === '/logout' || req.path === '/goodbye'
}

/**
 * Sets up authentication-related routes and handlers for the app.
 *
 * @param app {Object} Express.js app instance
 * @param argv {Object} Config options hashmap
 */
function initAuthentication (app, argv) {
  const auth = argv.forceUser ? 'forceUser' : argv.auth
  if (!(auth in API.authn)) {
    throw new Error(`Unsupported authentication scheme: ${auth}`)
  }
  API.authn[auth].initialize(app, argv)
}

/**
 * Returns a settings object for Express.js sessions.
 *
 * @param secureCookies {boolean}
 * @param host {SolidHost}
 *
 * @return {Object} `express-session` settings object
 */
function sessionSettings (secureCookies, host) {
  let sessionSettings = {
    secret: uuid.v1(),
    saveUninitialized: false,
    resave: false,
    rolling: true,
    cookie: {
      maxAge: 24 * 60 * 60 * 1000
    }
  }
  // Cookies should set to be secure if https is on
  if (secureCookies) {
    sessionSettings.cookie.secure = true
  }

  // Determine the cookie domain
  sessionSettings.cookie.domain = host.cookieDomain

  return sessionSettings
}
<commit_msg>Improve syntax<commit_after>module.exports = createApp

const express = require('express')
const session = require('express-session')
const handlebars = require('express-handlebars')
const uuid = require('uuid')
const cors = require('cors')
const LDP = require('./ldp')
const LdpMiddleware = require('./ldp-middleware')
const corsProxy = require('./handlers/cors-proxy')
const authProxy = require('./handlers/auth-proxy')
const SolidHost = require('./models/solid-host')
const AccountManager = require('./models/account-manager')
const vhost = require('vhost')
const EmailService = require('./services/email-service')
const TokenService = require('./services/token-service')
const capabilityDiscovery = require('./capability-discovery')
const API = require('./api')
const errorPages = require('./handlers/error-pages')
const config = require('./server-config')
const defaults = require('../config/defaults')
const options = require('./handlers/options')
const debug = require('./debug')
const path = require('path')
const { routeResolvedFile } = require('./utils')
const ResourceMapper = require('./resource-mapper')
const aclCheck = require('@solid/acl-check')
const { version } = require('../package.json')

const corsSettings = cors({
  methods: [
    'OPTIONS', 'HEAD', 'GET', 'PATCH', 'POST', 'PUT', 'DELETE'
  ],
  exposedHeaders: 'Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate',
  credentials: true,
  maxAge: 1728000,
  origin: true,
  preflightContinue: true
})

function createApp (argv = {}) {
  // Override default configs (defaults) with passed-in params (argv)
  argv = Object.assign({}, defaults, argv)

  argv.host = SolidHost.from({ port: argv.port, serverUri: argv.serverUri })

  argv.resourceMapper = new ResourceMapper({
    rootUrl: argv.serverUri,
    rootPath: path.resolve(argv.root || process.cwd()),
    includeHost: argv.multiuser,
    defaultContentType: argv.defaultContentType
  })

  const configPath = config.initConfigDir(argv)
  argv.templates = config.initTemplateDirs(configPath)

  const ldp = new LDP(argv)

  const app = express()

  initAppLocals(app, argv, ldp)
  initHeaders(app)
  initViews(app, configPath)
  initLoggers()

  // Serve the public 'common' directory (for shared CSS files, etc)
  app.use('/common', express.static(path.join(__dirname, '../common')))
  routeResolvedFile(app, '/common/js/', 'mashlib/dist/mashlib.js')
  routeResolvedFile(app, '/common/js/', 'mashlib/dist/mashlib.min.js')
  routeResolvedFile(app, '/common/js/', 'mashlib/dist/mashlib.min.js.map')
  routeResolvedFile(app, '/common/js/', 'solid-auth-client/dist-lib/solid-auth-client.bundle.js')
  routeResolvedFile(app, '/common/js/', 'solid-auth-client/dist-lib/solid-auth-client.bundle.js.map')
  app.use('/.well-known', express.static(path.join(__dirname, '../common/well-known')))

  // Serve bootstrap from it's node_module directory
  routeResolvedFile(app, '/common/css/', 'bootstrap/dist/css/bootstrap.min.css')
  routeResolvedFile(app, '/common/css/', 'bootstrap/dist/css/bootstrap.min.css.map')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.eot')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.svg')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.ttf')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.woff')
  routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.woff2')

  // Serve OWASP password checker from it's node_module directory
  routeResolvedFile(app, '/common/js/', 'owasp-password-strength-test/owasp-password-strength-test.js')
  // Serve the TextEncoder polyfill
  routeResolvedFile(app, '/common/js/', 'text-encoder-lite/text-encoder-lite.min.js')

  // Add CORS proxy
  if (argv.proxy) {
    console.warn('The proxy configuration option has been renamed to corsProxy.')
    argv.corsProxy = argv.corsProxy || argv.proxy
    delete argv.proxy
  }
  if (argv.corsProxy) {
    corsProxy(app, argv.corsProxy)
  }

  // Options handler
  app.options('/*', options)

  // Set up API
  if (argv.apiApps) {
    app.use('/api/apps', express.static(argv.apiApps))
  }

  // Authenticate the user
  if (argv.webid) {
    initWebId(argv, app, ldp)
  }
  // Add Auth proxy (requires authentication)
  if (argv.authProxy) {
    authProxy(app, argv.authProxy)
  }

  // Attach the LDP middleware
  app.use('/', LdpMiddleware(corsSettings))

  // Errors
  app.use(errorPages.handler)

  return app
}

/**
 * Initializes `app.locals` parameters for downstream use (typically by route
 * handlers).
 *
 * @param app {Function} Express.js app instance
 * @param argv {Object} Config options hashmap
 * @param ldp {LDP}
 */
function initAppLocals (app, argv, ldp) {
  app.locals.ldp = ldp
  app.locals.appUrls = argv.apps  // used for service capability discovery
  app.locals.host = argv.host
  app.locals.authMethod = argv.auth
  app.locals.localAuth = argv.localAuth
  app.locals.tokenService = new TokenService()
  app.locals.enforceToc = argv.enforceToc
  app.locals.tocUri = argv.tocUri
  app.locals.disablePasswordChecks = argv.disablePasswordChecks

  if (argv.email && argv.email.host) {
    app.locals.emailService = new EmailService(argv.templates.email, argv.email)
  }
}

/**
 * Sets up headers common to all Solid requests (CORS-related, Allow, etc).
 *
 * @param app {Function} Express.js app instance
 */
function initHeaders (app) {
  app.use(corsSettings)

  app.use((req, res, next) => {
    res.set('X-Powered-By', 'solid-server/' + version)

    // Cors lib adds Vary: Origin automatically, but inreliably
    res.set('Vary', 'Accept, Authorization, Origin')

    // Set default Allow methods
    res.set('Allow', 'OPTIONS, HEAD, GET, PATCH, POST, PUT, DELETE')
    next()
  })

  app.use('/', capabilityDiscovery())
}

/**
 * Sets up the express rendering engine and views directory.
 *
 * @param app {Function} Express.js app
 * @param configPath {string}
 */
function initViews (app, configPath) {
  const viewsPath = config.initDefaultViews(configPath)

  app.set('views', viewsPath)
  app.engine('.hbs', handlebars({
    extname: '.hbs',
    partialsDir: viewsPath
  }))
  app.set('view engine', '.hbs')
}

/**
 * Sets up WebID-related functionality (account creation and authentication)
 *
 * @param argv {Object}
 * @param app {Function}
 * @param ldp {LDP}
 */
function initWebId (argv, app, ldp) {
  config.ensureWelcomePage(argv)

  // Store the user's session key in a cookie
  // (for same-domain browsing by people only)
  const useSecureCookies = !!argv.sslKey // use secure cookies when over HTTPS
  const sessionHandler = session(sessionSettings(useSecureCookies, argv.host))
  app.use(sessionHandler)
  // Reject cookies from third-party applications.
  // Otherwise, when a user is logged in to their Solid server,
  // any third-party application could perform authenticated requests
  // without permission by including the credentials set by the Solid server.
  app.use((req, res, next) => {
    const origin = req.get('origin')
    const trustedOrigins = argv.trustedOrigins
    const userId = req.session.userId
    // Exception: allow logout requests from all third-party apps
    // such that OIDC client can log out via cookie auth
    // TODO: remove this exception when OIDC clients
    // use Bearer token to authenticate instead of cookie
    // (https://github.com/solid/node-solid-server/pull/835#issuecomment-426429003)
    if (!argv.host.allowsSessionFor(userId, origin, trustedOrigins) && !isLogoutRequest(req)) {
      debug.authentication(`Rejecting session for ${userId} from ${origin}`)
      // Destroy session data
      delete req.session.userId
      // Ensure this modified session is not saved
      req.session.save = (done) => done()
    }
    next()
  })

  let accountManager = AccountManager.from({
    authMethod: argv.auth,
    emailService: app.locals.emailService,
    tokenService: app.locals.tokenService,
    host: argv.host,
    accountTemplatePath: argv.templates.account,
    store: ldp,
    multiuser: argv.multiuser
  })
  app.locals.accountManager = accountManager

  // Account Management API (create account, new cert)
  app.use('/', API.accounts.middleware(accountManager))

  // Set up authentication-related API endpoints and app.locals
  initAuthentication(app, argv)

  if (argv.multiuser) {
    app.use(vhost('*', LdpMiddleware(corsSettings)))
  }
}

function initLoggers () {
  aclCheck.configureLogger(debug.ACL)
}

/**
 * Determines whether the given request is a logout request
 */
function isLogoutRequest (req) {
  // TODO: this is a hack that hard-codes OIDC paths,
  // this code should live in the OIDC module
  return req.path === '/logout' || req.path === '/goodbye'
}

/**
 * Sets up authentication-related routes and handlers for the app.
 *
 * @param app {Object} Express.js app instance
 * @param argv {Object} Config options hashmap
 */
function initAuthentication (app, argv) {
  const auth = argv.forceUser ? 'forceUser' : argv.auth
  if (!(auth in API.authn)) {
    throw new Error(`Unsupported authentication scheme: ${auth}`)
  }
  API.authn[auth].initialize(app, argv)
}

/**
 * Returns a settings object for Express.js sessions.
 *
 * @param secureCookies {boolean}
 * @param host {SolidHost}
 *
 * @return {Object} `express-session` settings object
 */
function sessionSettings (secureCookies, host) {
  let sessionSettings = {
    secret: uuid.v1(),
    saveUninitialized: false,
    resave: false,
    rolling: true,
    cookie: {
      maxAge: 24 * 60 * 60 * 1000
    }
  }
  // Cookies should set to be secure if https is on
  if (secureCookies) {
    sessionSettings.cookie.secure = true
  }

  // Determine the cookie domain
  sessionSettings.cookie.domain = host.cookieDomain

  return sessionSettings
}
<|endoftext|>