text: /**
   * timeRequire - measure the time to load all the subsequnt modules by hoocking require() calls
   *
   * @author Ciprian Popa (cyparu)
   * @since 0.0.1
   * @version 0.0.1
   */
  
  "use strict";
  
  var // setup vars
  		requireData = [],
  		write = process.stdout.write.bind(process.stdout),
  		relative = require("path").relative,
  		cwd = process.cwd(),
  		// require hooker should be first module loaded so all the other requires should count as well
  		/* jshint -W003 */
  		hook = require("./requireHook")(_hooker),
  		table = require("text-table"),
  		dateTime = require("date-time"),
  		prettyMs = require("pretty-ms"),
  		chalk = require("chalk"),
  		// extra locals
  		DEFAULT_COLUMNS = 80,
  		BAR_CHAR = process.platform === "win32" ? "■" : "▇",
  		sorted = hasArg("--sorted") || hasArg("--s"),
  		threshold = (hasArg("--verbose") || hasArg("--V")) ? 0.0: 0.01, // TODO - configure threshold using CLI ?
  		EXTRA_COLUMNS = sorted ? 24 : 20;
  
  /*
   Public API.
   */
  Object.defineProperty(exports, 'sorted', {
  	get: function () { return sorted; },
  	set: function (val) { sorted = val; }
  });
  Object.defineProperty(exports, 'threshold', {
  	get: function () { return threshold; },
  	set: function (val) { threshold = val; }
  });
  exports.logResults = logResults;
  
  /*
   Implementation.
   */
  
  // hook process exit to display the report at the end
  process.once("exit", logResults);
  
  
  function hasArg(arg) {
  	return process.argv.indexOf(arg) !== -1;
  }
  
  function pad(count, seq) {
  	return (count > 1) ? new Array(count).join(seq) : "";
  }
  
  function log(str) {
  	write(str + "\n", "utf8");
  }
  
  /**
   * Callback/listener used by requireHook hook to collect all the modules in their loading order
   */
  function _hooker(data) {
  	var filename = relative(cwd, data.filename);
  	// use the shortest name
  	if (filename.length > data.filename) {
  		filename = data.filename;
  	}
  	requireData.push({
  		order: requireData.length, // loading order
  		time: data.startedIn, // time
  		label: data.name + " (" + filename + ")"
  //		name: data.name,
  //		filename: filename
  	});
  }
  
  function formatTable(tableData, totalTime) {
  	var NAME_FILE_REX = /(.+)( \(.+\))/,
  			maxColumns = process.stdout.columns || DEFAULT_COLUMNS,
  			validCount = 0,
  			longestRequire = tableData.reduce(function(acc, data) {
  				var avg = data.time / totalTime;
  				if (avg < threshold) {
  					return acc;
  				}
  				validCount++;
  				return Math.max(acc, data.label.length);
  			}, 0),
  			maxBarWidth = (longestRequire > maxColumns / 2) ? ((maxColumns - EXTRA_COLUMNS) / 2) : (maxColumns - (longestRequire + EXTRA_COLUMNS)),
  			processedTableData = [],
  			counter, maxOrderChars;
  
  	function shorten(name) {
  		var nameLength = name.length,
  				partLength, start, end;
  		if (name.length < maxBarWidth) {
  			return name;
  		}
  		partLength = Math.floor((maxBarWidth - 3) / 2);
  		start = name.substr(0, partLength + 1);
  		end = name.substr(nameLength - partLength);
  		return start.trim() + "..." + end.trim();
  	}
  
  	function createBar(percentage) {
  		var rounded = Math.round(percentage * 100);
  		return ((rounded === 0) ? "0" : (pad(Math.ceil(maxBarWidth * percentage) + 1, BAR_CHAR) + " " + rounded)) + "%";
  	}
  
  	// sort the data if needed
  	if (sorted) {
  		tableData.sort(function(e1, e2) {
  			return e2.time - e1.time;
  		});
  	}
  	// initialize the counter
  	counter = 1;
  	// get num ber of chars for padding
  	maxOrderChars = tableData.length.toString().length;
  	// push the header
  	processedTableData.push(["#" + (sorted ? " [order]" : ""), "module", "time", "%"]);
  	tableData.forEach(function(data) {
  		var avg = data.time / totalTime,
  				counterLabel, label, match;
  		// slect just data over the threshold
  		if (avg >= threshold) {
  			counterLabel = counter++;
  			// for sorted collumns show the order loading with padding
  			if (sorted) {
  				counterLabel += pad(maxOrderChars - data.order.toString().length + 1, " ") + " [" + data.order + "]";
  			}
  			label = shorten(data.label);
  			match = label.match(NAME_FILE_REX);
  			if (match) {
  				label = chalk.green(match[1]) + match[2];
  			}
  			processedTableData.push([counterLabel, label, chalk.yellow(prettyMs(data.time)), chalk.blue(createBar(avg))]);
  		}
  	});
  
  	return table(processedTableData, {
  		align: ["r", "l", "r", "l"],
  		stringLength: function(str) {
  			return chalk.stripColor(str).length;
  		}
  	});
  }
  
  function logResults() {
  	var startTime = hook.hookedAt,
  			totalTime = Date.now() - startTime.getTime();
  	log("\n\n" + chalk.underline("Start time: " + chalk.yellow("(" + dateTime(startTime) + ")") + " [threshold=" + (threshold * 100) + "%" + (sorted ? ",sorted" : "") + "]"));
  	log(formatTable(requireData, totalTime));
  	log(chalk.bold.blue("Total require(): ") + chalk.yellow(requireData.length));
  	log(chalk.bold.blue("Total time: ") + chalk.yellow(prettyMs(totalTime)));
  }
  