text: /*
   *  Phusion Passenger - https://www.phusionpassenger.com/
   *  Copyright (c) 2012-2013 Phusion
   *
   *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the "Software"), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  
  /**
   * Class for reading a stream line-by-line.
   * Usage:
   *
   * reader = new LineReader(stream);
   * reader.readLine(function(line) {
   *     ...
   *     // When done:
   *     reader.close();
   * });
   */
  function LineReader(stream) {
  	var self = this;
  	this.stream = stream;
  
  	this.buffer = '';
  	this.lines = [];
  	this.callbacks = [];
  	this.paused = false;
  
  	function handleLineBuffer() {
  		if (self.lineBufferIsFull()) {
  			stream.pause();
  			self.paused = true;
  		}
  
  		while (self.buffer != undefined && self.lines.length > 0 && self.callbacks.length > 0) {
  			line = self.lines.shift();
  			callback = self.callbacks.shift();
  			callback(line);
  		}
  
  		self._autoPauseOrResume();
  	}
  
  	function onData(data) {
  		var index, line, callback;
  
  		if (self.buffer == undefined) {
  			// Already closed.
  			return;
  		}
  
  		self.buffer += data;
  		while ((index = self.buffer.indexOf("\n")) != -1) {
  			line = self.buffer.substr(0, index + 1);
  			self.buffer = self.buffer.substr(index + 1);
  			self.lines.push(line);
  		}
  		handleLineBuffer();
  	}
  
  	function onEnd() {
  		if (self.buffer != undefined) {
  			self.lines.push(self.buffer);
  			self.buffer = '';
  			self.eof = true;
  			handleLineBuffer();
  		}
  	}
  
  	this.onData = onData;
  	this.onEnd  = onEnd;
  	stream.on('data', onData);
  	stream.on('end', onEnd);
  	stream.resume();
  }
  
  LineReader.prototype.close = function() {
  	this.stream.removeListener('data', this.onData);
  	this.stream.removeListener('end', this.onEnd);
  	this._pause();
  	this.buffer = undefined;
  	this.lines = undefined;
  }
  
  LineReader.prototype.isClosed = function() {
  	return this.buffer === undefined;
  }
  
  LineReader.prototype.endReached = function() {
  	return this.eof;
  }
  
  LineReader.prototype.lineBufferIsFull = function() {
  	return this.lines.length > 0;
  }
  
  LineReader.prototype.readLine = function(callback) {
  	if (this.lines.length > 0) {
  		var line = this.lines.shift();
  		if (!this.lineBufferIsFull() && this.paused) {
  			this.paused = false;
  			this.stream.resume();
  		}
  		callback(line);
  		this._autoPauseOrResume();
  	} else if (this.eof) {
  		callback(undefined);
  	} else {
  		this.callbacks.push(callback);
  	}
  }
  
  LineReader.prototype._autoPauseOrResume = function() {
  	if (this.buffer != undefined) {
  		if (this.lineBufferIsFull() || this.eof) {
  			this._pause();
  		} else {
  			this._resume();
  		}
  	}
  }
  
  LineReader.prototype._pause = function() {
  	if (!this.paused) {
  		this.paused = true;
  		this.stream.pause();
  	}
  }
  
  LineReader.prototype._resume = function() {
  	if (this.paused) {
  		this.paused = false;
  		this.stream.resume();
  	}
  }
  
  exports.LineReader = LineReader;
  