commit
						9b1341be39
					
				@ -0,0 +1,9 @@
 | 
				
			||||
var/db/*
 | 
				
			||||
temp*
 | 
				
			||||
*.tmproj
 | 
				
			||||
.makefiles*
 | 
				
			||||
configs/*.js
 | 
				
			||||
build/*
 | 
				
			||||
*.node
 | 
				
			||||
.DS_Store
 | 
				
			||||
._*
 | 
				
			||||
@ -0,0 +1,65 @@
 | 
				
			||||
var sys	= require("sys"),
 | 
				
			||||
events	= require("events"),
 | 
				
			||||
fs		= require("fs");
 | 
				
			||||
 | 
				
			||||
var Configs	= this;
 | 
				
			||||
 | 
				
			||||
var load	= function (debug) {
 | 
				
			||||
	events.EventEmitter.call(this);
 | 
				
			||||
	
 | 
				
			||||
	var self	= this;
 | 
				
			||||
	Configs.debug	= debug || false;
 | 
				
			||||
	
 | 
				
			||||
	dir = __dirname + "/configs/";
 | 
				
			||||
	fs.readdir(dir, function (err, files) {
 | 
				
			||||
		if (err) {
 | 
				
			||||
			sys.puts(("[ warn ]  unable to load config directory: " + dir).magenta);
 | 
				
			||||
			return;
 | 
				
			||||
		}
 | 
				
			||||
		
 | 
				
			||||
		for (var k = 0, l = files.length; k < l; ++k) {
 | 
				
			||||
			if (!(/\.js$/.exec(files[k]))) {
 | 
				
			||||
				continue;
 | 
				
			||||
			}
 | 
				
			||||
			
 | 
				
			||||
			var filePath	= dir + files[k];
 | 
				
			||||
			var fullPath	= __dirname + filePath.substr(1);
 | 
				
			||||
			fileName		= filePath.replace(/(\.js)$/, "");
 | 
				
			||||
			fileKey			= fileName.replace(/^(.*)\//g, "");
 | 
				
			||||
			
 | 
				
			||||
			// Delete module cache
 | 
				
			||||
			if (typeof process.mainModule.moduleCache[fullPath] !== "undefined") {
 | 
				
			||||
				delete process.mainModule.moduleCache[fullPath];
 | 
				
			||||
			}
 | 
				
			||||
			
 | 
				
			||||
			var configFile	= require(fileName).Config;
 | 
				
			||||
			
 | 
				
			||||
			Object.keys(configFile).forEach(function (key) {
 | 
				
			||||
				Configs[key]	= configFile[key];
 | 
				
			||||
				
 | 
				
			||||
				if (Object.keys(configFile).length > 1) {
 | 
				
			||||
					if (key === "init" && typeof Configs.init === "function") {
 | 
				
			||||
						Configs.init.call(Configs, Configs);
 | 
				
			||||
						
 | 
				
			||||
						delete Configs.init;
 | 
				
			||||
					}
 | 
				
			||||
				} else {
 | 
				
			||||
					if (typeof Configs[key].init === "function") {
 | 
				
			||||
						Configs[key].init.call(Configs, Configs[key]);
 | 
				
			||||
						
 | 
				
			||||
						delete Configs[key].init;
 | 
				
			||||
					}
 | 
				
			||||
				}
 | 
				
			||||
			});
 | 
				
			||||
			
 | 
				
			||||
			sys.puts(("[ config ] ./" + fileKey).magenta);
 | 
				
			||||
		}
 | 
				
			||||
		
 | 
				
			||||
		Configs.loaded	= true;
 | 
				
			||||
		self.emit("config:loaded");
 | 
				
			||||
	});
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
sys.inherits(load, events.EventEmitter);
 | 
				
			||||
 | 
				
			||||
exports.load = load;
 | 
				
			||||
@ -0,0 +1,27 @@
 | 
				
			||||
var path	= require("path");
 | 
				
			||||
 | 
				
			||||
exports.Config = {
 | 
				
			||||
	version:		"0.0.1",
 | 
				
			||||
	
 | 
				
			||||
	responderDir:		"responders",
 | 
				
			||||
	
 | 
				
			||||
	color:			"green",
 | 
				
			||||
	
 | 
				
			||||
	colors:			{
 | 
				
			||||
		commands:	"red",
 | 
				
			||||
		modules:	"green",
 | 
				
			||||
		configs:	"magenta",
 | 
				
			||||
		success:	"green",
 | 
				
			||||
		failure:	"red"
 | 
				
			||||
	},
 | 
				
			||||
	
 | 
				
			||||
	init:	function(self)
 | 
				
			||||
	{
 | 
				
			||||
		if (this.debug === true) {
 | 
				
			||||
		}
 | 
				
			||||
		/*
 | 
				
			||||
		if (("fields" in self.ldap) === true) {
 | 
				
			||||
			self.ldap.users.replace(/(\?\*\?)/, self.ldap.fields.join(","));
 | 
				
			||||
		}*/
 | 
				
			||||
	}
 | 
				
			||||
};
 | 
				
			||||
@ -0,0 +1,198 @@
 | 
				
			||||
var debug				= true;
 | 
				
			||||
 | 
				
			||||
var	sys					= require("sys"),
 | 
				
			||||
	util				= require("util"),
 | 
				
			||||
	events				= require("events"),
 | 
				
			||||
	colors				= require("colors"),
 | 
				
			||||
	path				= require("path"),
 | 
				
			||||
	hash				= require("../deps/node-hash/lib/hash"),
 | 
				
			||||
	// We load this separately because we might need some things before the configs are loaded
 | 
				
			||||
	FreeNodeConfig		= require("../configs/freenode").Config,
 | 
				
			||||
	Configs				= require("../configs"),
 | 
				
			||||
	Responders			= require("../responders"),
 | 
				
			||||
	LDAPClient			= require("../deps/node-ldapsearch/build/default/ldap.node");
 | 
				
			||||
	
 | 
				
			||||
var FreeNode	= function() {
 | 
				
			||||
	var self		  		= this;
 | 
				
			||||
	this.uptime				= (new Date().getTime());
 | 
				
			||||
	this.loaders			= [];
 | 
				
			||||
	this.ConfigsLoader		= null;
 | 
				
			||||
	this.RespondersLoader	= null;
 | 
				
			||||
	
 | 
				
			||||
	process.title		= "FreeNode";
 | 
				
			||||
	
 | 
				
			||||
	events.EventEmitter.call(this);
 | 
				
			||||
	
 | 
				
			||||
	process.chdir(path.dirname(__dirname));
 | 
				
			||||
	
 | 
				
			||||
	var onInitedListerner	= function() {
 | 
				
			||||
		self.removeListener("loaded", onInitedListerner);
 | 
				
			||||
		self.onInited();
 | 
				
			||||
	};
 | 
				
			||||
	
 | 
				
			||||
	this.addListener("loaded", onInitedListerner);
 | 
				
			||||
	
 | 
				
			||||
	this.addDependencies();
 | 
				
			||||
	
 | 
				
			||||
	return this;
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
sys.inherits(FreeNode, events.EventEmitter);
 | 
				
			||||
exports.FreeNode	= FreeNode;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/**
 | 
				
			||||
* init: Processes the loader chain by starting each loader function 
 | 
				
			||||
* 	and waiting for the return object's loaded event before processing the next one.
 | 
				
			||||
*/
 | 
				
			||||
FreeNode.prototype.init	= function(reload) {
 | 
				
			||||
	var self	= this;
 | 
				
			||||
	
 | 
				
			||||
	this.reload	= false;
 | 
				
			||||
	if (typeof reload !== "undefined") {
 | 
				
			||||
		this.reload	= reload;
 | 
				
			||||
	}
 | 
				
			||||
	
 | 
				
			||||
	if (this.loaders.length > 0) {
 | 
				
			||||
		var setup	= function(params) {
 | 
				
			||||
			return function() {
 | 
				
			||||
				var success	= params.success;
 | 
				
			||||
				
 | 
				
			||||
				if (typeof success[2] === "undefined") {
 | 
				
			||||
					var obj			= success[0]();
 | 
				
			||||
				} else {
 | 
				
			||||
					var obj			= success[2];
 | 
				
			||||
				}
 | 
				
			||||
				
 | 
				
			||||
				if (typeof params.failure !== "undefined") {
 | 
				
			||||
					var failure		= params.failure;
 | 
				
			||||
					var cbFailure	= function(error) {
 | 
				
			||||
						obj.removeListener(failure[1], cbFailure);
 | 
				
			||||
						failure[0](error);
 | 
				
			||||
					};
 | 
				
			||||
				}
 | 
				
			||||
				
 | 
				
			||||
				var cbSuccess	= function() {
 | 
				
			||||
					obj.removeListener(success[1], cbSuccess);
 | 
				
			||||
					if (typeof failure !== "undefined") {
 | 
				
			||||
						obj.removeListener(failure[1], cbFailure);
 | 
				
			||||
					}
 | 
				
			||||
					
 | 
				
			||||
					self.loaders.shift();
 | 
				
			||||
					self.init(self.reload);
 | 
				
			||||
				};
 | 
				
			||||
				
 | 
				
			||||
				if (typeof params.failure !== "undefined") {
 | 
				
			||||
					obj.addListener(failure[1], cbFailure);
 | 
				
			||||
				}
 | 
				
			||||
				
 | 
				
			||||
				obj.addListener(success[1], cbSuccess);
 | 
				
			||||
				
 | 
				
			||||
				if (typeof success[2] !== "undefined") {
 | 
				
			||||
					success[0]();
 | 
				
			||||
				}
 | 
				
			||||
			};
 | 
				
			||||
		}(this.loaders[0]);
 | 
				
			||||
		
 | 
				
			||||
		setup();
 | 
				
			||||
	} else if (this.reload === false) {
 | 
				
			||||
		this.emit("loaded");
 | 
				
			||||
	} else if (this.reload === true) {
 | 
				
			||||
		this.emit("reloaded");
 | 
				
			||||
	}
 | 
				
			||||
	
 | 
				
			||||
	return this;
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
/**
 | 
				
			||||
* reinit: Prepares and initiates a processs resource reload
 | 
				
			||||
* 	(triggered by SIGHUP)
 | 
				
			||||
*/
 | 
				
			||||
FreeNode.prototype.reinit	= function(cbReturn) {
 | 
				
			||||
	var self	= this;
 | 
				
			||||
	
 | 
				
			||||
	this.addDependencies(true);
 | 
				
			||||
	
 | 
				
			||||
	var onReloaded	= function() {
 | 
				
			||||
		self.removeListener("reloaded", onReloaded);
 | 
				
			||||
		console.log(("\nFreeNode Reload Completed!\n\n")[Configs.color]);
 | 
				
			||||
	};
 | 
				
			||||
	
 | 
				
			||||
	this.addListener("reloaded", onReloaded);
 | 
				
			||||
	
 | 
				
			||||
	if (typeof cbReturn === "function") {
 | 
				
			||||
		var cbFunc	= function(cb) {
 | 
				
			||||
			return function() {
 | 
				
			||||
				self.removeListener("reloaded", cbFunc);
 | 
				
			||||
				cbReturn();
 | 
				
			||||
			};
 | 
				
			||||
		}(cbReturn);
 | 
				
			||||
		
 | 
				
			||||
		this.addListener("reloaded", cbFunc);
 | 
				
			||||
	}
 | 
				
			||||
	
 | 
				
			||||
	this.init(true);
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
FreeNode.prototype.onInited	= function() {
 | 
				
			||||
	
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
/**
 | 
				
			||||
* addDependencies: loads a list of the process' dependencies in the loading chain.
 | 
				
			||||
* Some dependencies are skipped if this is called in reload mode
 | 
				
			||||
*/
 | 
				
			||||
FreeNode.prototype.addDependencies	= function(reload) {
 | 
				
			||||
	var self	= this;
 | 
				
			||||
	
 | 
				
			||||
	if (typeof reload === "undefined") {
 | 
				
			||||
		var reload	= false;
 | 
				
			||||
	}
 | 
				
			||||
	
 | 
				
			||||
	this.addDependency(
 | 
				
			||||
		// Loader
 | 
				
			||||
		[function() {
 | 
				
			||||
			console.log(("Loading Configuration Files")[FreeNodeConfig.colors.configs]);
 | 
				
			||||
			self.ConfigsLoader	= new Configs.load(debug);
 | 
				
			||||
			return self.ConfigsLoader;
 | 
				
			||||
		}, "config:loaded"]
 | 
				
			||||
	);
 | 
				
			||||
	
 | 
				
			||||
	if (reload === true) {
 | 
				
			||||
		// We also want to wait for the MUC to bind
 | 
				
			||||
		this.addDependency(
 | 
				
			||||
			// Loader
 | 
				
			||||
			[function() {
 | 
				
			||||
				console.log(("Removing responders for reload")[Configs.colors.responders]);
 | 
				
			||||
				
 | 
				
			||||
				Responders.unload.apply(self.Responders, [self]);
 | 
				
			||||
			}, "responders:deloaded", this]
 | 
				
			||||
		);
 | 
				
			||||
	}
 | 
				
			||||
	
 | 
				
			||||
	this.addDependency(
 | 
				
			||||
		// Loader
 | 
				
			||||
		[function() {
 | 
				
			||||
			console.log(("Loading Responders")[Configs.colors.responders]);
 | 
				
			||||
			
 | 
				
			||||
			Responders.load.apply(self, [self]);
 | 
				
			||||
		}, "responders:loaded", this]
 | 
				
			||||
	);
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
/**
 | 
				
			||||
* addDependency: adds a dependency data to the loading chain
 | 
				
			||||
* 
 | 
				
			||||
* success[0]	= The object loader function, should return the object on which to bind the loaded event
 | 
				
			||||
* success[1]	= The "loaded" event which the object will trigger when it's done loading
 | 
				
			||||
* success[2]	= Optionally, you can specify an object on which to bind the event before calling the loader function
 | 
				
			||||
* 					This is useful in cases where the loading function triggers an the event on a different object that already exists
 | 
				
			||||
* Optional:
 | 
				
			||||
* failure[0]	= Callback function to bind to the object on failure
 | 
				
			||||
* failure[1]	= Error event name on which to bind the callback
 | 
				
			||||
*/
 | 
				
			||||
FreeNode.prototype.addDependency	= function(success, failure) {
 | 
				
			||||
	this.loaders[this.loaders.length]	= {success:success, failure:failure};
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
@ -0,0 +1,14 @@
 | 
				
			||||
var FreeNodeHandler	= require("./libs/freenode").FreeNode,
 | 
				
			||||
	Configs			= require("./configs");
 | 
				
			||||
 | 
				
			||||
// Instanciate our main object
 | 
				
			||||
var FreeNode	= new FreeNodeHandler();
 | 
				
			||||
 | 
				
			||||
FreeNode.init();
 | 
				
			||||
 | 
				
			||||
// Process SIGHUP by calling reinit
 | 
				
			||||
process.on("SIGHUP", function() {
 | 
				
			||||
	console.log(("\n\n**** Reloading FreeNode! ****\n")[Configs.color]);
 | 
				
			||||
	
 | 
				
			||||
	FreeNode.reinit();
 | 
				
			||||
});
 | 
				
			||||
@ -0,0 +1,133 @@
 | 
				
			||||
/*
 | 
				
			||||
 * Date Format 1.2.3
 | 
				
			||||
 * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
 | 
				
			||||
 * MIT license
 | 
				
			||||
 *
 | 
				
			||||
 * Includes enhancements by Scott Trenda <scott.trenda.net>
 | 
				
			||||
 * and Kris Kowal <cixar.com/~kris.kowal/>
 | 
				
			||||
 *
 | 
				
			||||
 * Accepts a date, a mask, or a date and a mask.
 | 
				
			||||
 * Returns a formatted version of the given date.
 | 
				
			||||
 * The date defaults to the current date/time.
 | 
				
			||||
 * The mask defaults to dateFormat.masks.default.
 | 
				
			||||
 * 
 | 
				
			||||
 * http://blog.stevenlevithan.com/archives/date-time-format
 | 
				
			||||
 *
 | 
				
			||||
 */
 | 
				
			||||
 | 
				
			||||
var dateFormat = function () {
 | 
				
			||||
	var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
 | 
				
			||||
		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
 | 
				
			||||
		timezoneClip = /[^-+\dA-Z]/g,
 | 
				
			||||
		pad = function (val, len) {
 | 
				
			||||
			val = String(val);
 | 
				
			||||
			len = len || 2;
 | 
				
			||||
			while (val.length < len) val = "0" + val;
 | 
				
			||||
			return val;
 | 
				
			||||
		};
 | 
				
			||||
 | 
				
			||||
	// Regexes and supporting functions are cached through closure
 | 
				
			||||
	return function (date, mask, utc) {
 | 
				
			||||
		var dF = dateFormat;
 | 
				
			||||
 | 
				
			||||
		// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
 | 
				
			||||
		if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
 | 
				
			||||
			mask = date;
 | 
				
			||||
			date = undefined;
 | 
				
			||||
		}
 | 
				
			||||
 | 
				
			||||
		// Passing date through Date applies Date.parse, if necessary
 | 
				
			||||
		date = date ? new Date(date) : new Date;
 | 
				
			||||
		if (isNaN(date)) throw SyntaxError("invalid date");
 | 
				
			||||
 | 
				
			||||
		mask = String(dF.masks[mask] || mask || dF.masks["default"]);
 | 
				
			||||
 | 
				
			||||
		// Allow setting the utc argument via the mask
 | 
				
			||||
		if (mask.slice(0, 4) == "UTC:") {
 | 
				
			||||
			mask = mask.slice(4);
 | 
				
			||||
			utc = true;
 | 
				
			||||
		}
 | 
				
			||||
		
 | 
				
			||||
		var	_ = utc ? "getUTC" : "get",
 | 
				
			||||
			d = date[_ + "Date"](),
 | 
				
			||||
			D = date[_ + "Day"](),
 | 
				
			||||
			m = date[_ + "Month"](),
 | 
				
			||||
			y = date[_ + "FullYear"](),
 | 
				
			||||
			H = date[_ + "Hours"](),
 | 
				
			||||
			M = date[_ + "Minutes"](),
 | 
				
			||||
			s = date[_ + "Seconds"](),
 | 
				
			||||
			L = date[_ + "Milliseconds"](),
 | 
				
			||||
			o = utc ? 0 : date.getTimezoneOffset(),
 | 
				
			||||
			flags = {
 | 
				
			||||
				d:    d,
 | 
				
			||||
				dd:   pad(d),
 | 
				
			||||
				ddd:  dF.i18n.dayNames[D],
 | 
				
			||||
				dddd: dF.i18n.dayNames[D + 7],
 | 
				
			||||
				m:    m + 1,
 | 
				
			||||
				mm:   pad(m + 1),
 | 
				
			||||
				mmm:  dF.i18n.monthNames[m],
 | 
				
			||||
				mmmm: dF.i18n.monthNames[m + 12],
 | 
				
			||||
				yy:   String(y).slice(2),
 | 
				
			||||
				yyyy: y,
 | 
				
			||||
				h:    H % 12 || 12,
 | 
				
			||||
				hh:   pad(H % 12 || 12),
 | 
				
			||||
				H:    H,
 | 
				
			||||
				HH:   pad(H),
 | 
				
			||||
				M:    M,
 | 
				
			||||
				MM:   pad(M),
 | 
				
			||||
				s:    s,
 | 
				
			||||
				ss:   pad(s),
 | 
				
			||||
				l:    pad(L, 3),
 | 
				
			||||
				L:    pad(L > 99 ? Math.round(L / 10) : L),
 | 
				
			||||
				t:    H < 12 ? "a"  : "p",
 | 
				
			||||
				tt:   H < 12 ? "am" : "pm",
 | 
				
			||||
				T:    H < 12 ? "A"  : "P",
 | 
				
			||||
				TT:   H < 12 ? "AM" : "PM",
 | 
				
			||||
				Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
 | 
				
			||||
				o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
 | 
				
			||||
				S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
 | 
				
			||||
			};
 | 
				
			||||
 | 
				
			||||
		return mask.replace(token, function ($0) {
 | 
				
			||||
			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
 | 
				
			||||
		});
 | 
				
			||||
	};
 | 
				
			||||
}();
 | 
				
			||||
 | 
				
			||||
// Some common format strings
 | 
				
			||||
dateFormat.masks = {
 | 
				
			||||
	"default":      "ddd mmm dd yyyy HH:MM:ss",
 | 
				
			||||
	httpHeaders:    "UTC:ddd, dd mmm yyyy HH:MM:ss \"GMT\"",
 | 
				
			||||
	mediumFull:     "ddd mmm dd HH:MM",
 | 
				
			||||
	mediumFile:     "ddd_mmm_dd-HH-MM",
 | 
				
			||||
	shortDate:      "m/d/yy",
 | 
				
			||||
	mediumDate:     "mmm d, yyyy",
 | 
				
			||||
	longDate:       "mmmm d, yyyy",
 | 
				
			||||
	fullDate:       "dddd, mmmm d, yyyy",
 | 
				
			||||
	shortTime:      "h:MM TT",
 | 
				
			||||
	mediumTime:     "h:MM:ss TT",
 | 
				
			||||
	longTime:       "h:MM:ss TT Z",
 | 
				
			||||
	isoDate:        "yyyy-mm-dd",
 | 
				
			||||
	isoTime:        "HH:MM:ss",
 | 
				
			||||
	isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
 | 
				
			||||
	isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
// Internationalization strings
 | 
				
			||||
dateFormat.i18n = {
 | 
				
			||||
	dayNames: [
 | 
				
			||||
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
 | 
				
			||||
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
 | 
				
			||||
	],
 | 
				
			||||
	monthNames: [
 | 
				
			||||
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
 | 
				
			||||
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
 | 
				
			||||
	]
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
exports.dateFormat	= dateFormat;
 | 
				
			||||
 | 
				
			||||
// For convenience...
 | 
				
			||||
Date.prototype.format = function (mask, utc) {
 | 
				
			||||
	return dateFormat(this, mask, utc);
 | 
				
			||||
};
 | 
				
			||||
@ -0,0 +1,115 @@
 | 
				
			||||
var sys		= require("sys"),
 | 
				
			||||
	util	= require("util"),
 | 
				
			||||
	events	= require("events"),
 | 
				
			||||
	colors	= require("colors"),
 | 
				
			||||
	fs		= require("fs");
 | 
				
			||||
 | 
				
			||||
/**
 | 
				
			||||
 * read a directory (recursively deep)
 | 
				
			||||
 * data[] = an object for each element in the directory
 | 
				
			||||
 *		.name = item's name (file or folder name)
 | 
				
			||||
 *		.stat = item's stat (.stat.isDirectory() == true IF a folder)
 | 
				
			||||
 *		.children = another data[] for the children
 | 
				
			||||
 * filter = an object with various filter settings:
 | 
				
			||||
 *		.depth		= max directory recursion depth to travel
 | 
				
			||||
 *						(0 or missing means: infinite)
 | 
				
			||||
 *						(1 means: only the folder passed in)
 | 
				
			||||
 *		.hidden		= true means: process hidden files and folders (defaults to false)
 | 
				
			||||
 *		.callback	= callback function: callback(name, path, filter) -- returns truthy to keep the file
 | 
				
			||||
 *
 | 
				
			||||
 *
 | 
				
			||||
 * @param path		= path to directory to read (".", ".\apps")
 | 
				
			||||
 * @param callback	= function to callback to: callback(err, data)
 | 
				
			||||
 * @param [filter]	= (optional) filter object
 | 
				
			||||
 * 
 | 
				
			||||
 * Shamelessly snagged from:
 | 
				
			||||
 * 	http://utahjs.com/2010/09/16/nodejs-events-and-recursion-readdir/
 | 
				
			||||
 */
 | 
				
			||||
exports.recurseDir = function(path, callback, filter) {
 | 
				
			||||
	if (filter) {
 | 
				
			||||
		// process filter. are we too deep yet?
 | 
				
			||||
		if (!filter.depthAt) filter.depthAt = 1;				// initialize what depth we are at
 | 
				
			||||
		if (filter.depth && filter.depth < filter.depthAt) {
 | 
				
			||||
			callback(undefined, []);							// we are too deep. return "nothing found"
 | 
				
			||||
			return;
 | 
				
			||||
		}
 | 
				
			||||
	}
 | 
				
			||||
 | 
				
			||||
	// queue up a "readdir" file system call (and return)
 | 
				
			||||
	fs.readdir(path, function(err, files) {
 | 
				
			||||
		if (err) {
 | 
				
			||||
			callback(err);
 | 
				
			||||
			return;
 | 
				
			||||
		}
 | 
				
			||||
		var doHidden = false;				// true means: process hidden files and folders
 | 
				
			||||
		if (filter && filter.hidden) {
 | 
				
			||||
			doHidden = true;				// filter requests to process hidden files and folders
 | 
				
			||||
		}
 | 
				
			||||
		var count = 0;						// count the number of "stat" calls queued up
 | 
				
			||||
		var countFolders = 0;				// count the number of "folders" calls queued up
 | 
				
			||||
		var data = [];						// the data to return
 | 
				
			||||
 | 
				
			||||
		// iterate over each file in the dir
 | 
				
			||||
		files.forEach(function (name) {
 | 
				
			||||
			// ignore files that start with a "." UNLESS requested to process hidden files and folders
 | 
				
			||||
			if (doHidden || name.indexOf(".") !== 0) {
 | 
				
			||||
				// queue up a "stat" file system call for every file (and return)
 | 
				
			||||
				count += 1;
 | 
				
			||||
				fs.stat(path + "/" + name, function(err, stat) {
 | 
				
			||||
					if (err) {
 | 
				
			||||
						callback(err);
 | 
				
			||||
						return;
 | 
				
			||||
					}
 | 
				
			||||
					var processFile = true;
 | 
				
			||||
					if (filter && filter.callback) {
 | 
				
			||||
						processFile = filter.callback(name, stat, filter);
 | 
				
			||||
					}
 | 
				
			||||
					if (processFile) {
 | 
				
			||||
						var obj = {};
 | 
				
			||||
						obj.name = name;
 | 
				
			||||
						obj.filepath = path + "/" + name;
 | 
				
			||||
						obj.stat = stat;
 | 
				
			||||
						data.push(obj);
 | 
				
			||||
						if (stat.isDirectory()) {
 | 
				
			||||
							countFolders += 1;
 | 
				
			||||
							// perform "recurseDir" on each child folder (which queues up a readdir and returns)
 | 
				
			||||
							(function(obj2) {
 | 
				
			||||
								// obj2 = the "obj" object
 | 
				
			||||
								exports.recurseDir(path + "/" + name, function(err, data2) {
 | 
				
			||||
									if (err) {
 | 
				
			||||
										callback(err);
 | 
				
			||||
										return;
 | 
				
			||||
									}
 | 
				
			||||
									// entire child folder info is in "data2" (1 fewer child folders to wait to be processed)
 | 
				
			||||
									countFolders -= 1;
 | 
				
			||||
									obj2.children = data2;
 | 
				
			||||
									if (countFolders <= 0) {
 | 
				
			||||
										// sub-folders found. This was the last sub-folder to processes.
 | 
				
			||||
										callback(undefined, data);		// callback w/ data
 | 
				
			||||
									} else {
 | 
				
			||||
										// more children folders to be processed. do nothing here.
 | 
				
			||||
									}
 | 
				
			||||
								});
 | 
				
			||||
							})(obj);
 | 
				
			||||
						}
 | 
				
			||||
					}
 | 
				
			||||
					// 1 more file has been processed (or skipped)
 | 
				
			||||
					count -= 1;
 | 
				
			||||
					if (count <= 0) {
 | 
				
			||||
						// all files have been processed.
 | 
				
			||||
						if (countFolders <= 0) {
 | 
				
			||||
							// no sub-folders were found. DONE. no sub-folders found
 | 
				
			||||
							callback(undefined, data);		// callback w/ data
 | 
				
			||||
						} else {
 | 
				
			||||
							// children folders were found. do nothing here (we are waiting for the children to callback)
 | 
				
			||||
						}
 | 
				
			||||
					}
 | 
				
			||||
				});
 | 
				
			||||
			}
 | 
				
			||||
		});
 | 
				
			||||
		if (count <= 0) {	// if no "stat" calls started, then this was an empty folder
 | 
				
			||||
			callback(undefined, []);		// callback w/ empty
 | 
				
			||||
		}
 | 
				
			||||
	});
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
@ -0,0 +1,141 @@
 | 
				
			||||
var sys			= require("sys"),
 | 
				
			||||
	util		= require("util"),
 | 
				
			||||
	events		= require("events"),
 | 
				
			||||
	colors		= require("colors"),
 | 
				
			||||
	recurseDir	= require("./deps/recurseDir").recurseDir,
 | 
				
			||||
	Configs		= require("./configs");
 | 
				
			||||
 | 
				
			||||
var load	= function (Foulinks) {
 | 
				
			||||
	var self		= this;
 | 
				
			||||
	
 | 
				
			||||
	if (typeof this.Responders	=== "undefined") {
 | 
				
			||||
		this.Responders	= {};
 | 
				
			||||
	}
 | 
				
			||||
	
 | 
				
			||||
	var loadResponder	= function(filePath, responderBase) {
 | 
				
			||||
		var fileKey			= filePath.replace(/^(.*)\/|\.js$/g, "");
 | 
				
			||||
		
 | 
				
			||||
		if (typeof responderBase[fileKey] === "undefined") {
 | 
				
			||||
			responderBase[fileKey]	= new Object();
 | 
				
			||||
		}
 | 
				
			||||
		
 | 
				
			||||
		try {
 | 
				
			||||
			var responderFile	= require(filePath).Responder;
 | 
				
			||||
			responderFile.forEach(function (func) {
 | 
				
			||||
				func.apply(responderBase[fileKey], [Foulinks]);
 | 
				
			||||
			
 | 
				
			||||
				responderBase[fileKey].responderFile	= filePath;
 | 
				
			||||
			
 | 
				
			||||
				if (typeof responderBase[fileKey].init === "function") {
 | 
				
			||||
					responderBase[fileKey].init();
 | 
				
			||||
				}
 | 
				
			||||
			});
 | 
				
			||||
		
 | 
				
			||||
			sys.puts(("[ responders ] ." + filePath.replace(__dirname+"/"+Configs.responderDir, "").replace(/(\.js)$/, ""))[Configs.colors.responders]);
 | 
				
			||||
		} catch (err) {
 | 
				
			||||
			// Don't keep a cache of failed includes!
 | 
				
			||||
			if (typeof process.mainModule.moduleCache[filePath] !== "undefined") {
 | 
				
			||||
				delete process.mainModule.moduleCache[filePath];
 | 
				
			||||
			}
 | 
				
			||||
			
 | 
				
			||||
			delete responderBase[fileKey];
 | 
				
			||||
			
 | 
				
			||||
			sys.puts(("[ responders ] ERROR Loading ." + 
 | 
				
			||||
						filePath.replace(__dirname+"/"+Configs.responderDir, "").replace(/(\.js)$/, "") + 
 | 
				
			||||
						": " + err.toString() + "\n" + err.stack.toString())[Configs.colors.failure]);
 | 
				
			||||
		}
 | 
				
			||||
	};
 | 
				
			||||
	
 | 
				
			||||
	var loadResponders	= function(data) {
 | 
				
			||||
		for (fileIndex in data) {
 | 
				
			||||
			if (data[fileIndex].stat.isDirectory()) {
 | 
				
			||||
				loadResponders(data[fileIndex].children);
 | 
				
			||||
			} else {
 | 
				
			||||
				var baseStart		= data[fileIndex].filepath.indexOf(Configs.responderDir) + Configs.responderDir.length + 1;
 | 
				
			||||
				var basePath		= data[fileIndex].filepath.substr(baseStart);
 | 
				
			||||
				var responderBase	= self.Responders;
 | 
				
			||||
				var baseSections	= basePath.split("/");
 | 
				
			||||
				
 | 
				
			||||
				for (index in baseSections) {
 | 
				
			||||
					if (index >= baseSections.length - 1) {
 | 
				
			||||
						continue;
 | 
				
			||||
					}
 | 
				
			||||
					
 | 
				
			||||
					if (typeof responderBase[baseSections[index]] === "undefined") {
 | 
				
			||||
						responderBase[baseSections[index]]	= {};
 | 
				
			||||
					}
 | 
				
			||||
					
 | 
				
			||||
					responderBase			= responderBase[baseSections[index]];
 | 
				
			||||
				}
 | 
				
			||||
				
 | 
				
			||||
				responderBase.isDir	= true;
 | 
				
			||||
				
 | 
				
			||||
				loadResponder(data[fileIndex].filepath, responderBase);
 | 
				
			||||
			}
 | 
				
			||||
		}
 | 
				
			||||
	};
 | 
				
			||||
	
 | 
				
			||||
	var validFile	= function(name, stat, filter) {
 | 
				
			||||
		if (stat.isDirectory() === true) {
 | 
				
			||||
			return true;
 | 
				
			||||
		} else {
 | 
				
			||||
			if (/\.js$/.test(name)) {
 | 
				
			||||
				return true;
 | 
				
			||||
			}
 | 
				
			||||
		}
 | 
				
			||||
		
 | 
				
			||||
		return false;
 | 
				
			||||
	};
 | 
				
			||||
	
 | 
				
			||||
	recurseDir(__dirname + "/" + Configs.responderDir, function(err, data) {
 | 
				
			||||
		if (typeof err === "undefined") {
 | 
				
			||||
			loadResponders(data);
 | 
				
			||||
			
 | 
				
			||||
			self.emit("responders:loaded");
 | 
				
			||||
		} else {
 | 
				
			||||
			self.emit("responders:error");
 | 
				
			||||
		}
 | 
				
			||||
	}, {callback: validFile});
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
exports.load = load;
 | 
				
			||||
 | 
				
			||||
var unload	= function(Foulinks, recurse) {
 | 
				
			||||
	// We went too far!
 | 
				
			||||
	if (typeof this.responderFile !== "undefined") {
 | 
				
			||||
		return;
 | 
				
			||||
	}
 | 
				
			||||
	
 | 
				
			||||
	for (fileKey in this) {
 | 
				
			||||
		if (typeof this[fileKey] === "object") {
 | 
				
			||||
			// This is a responder and we should remove it
 | 
				
			||||
			if (typeof this[fileKey].responderFile !== "undefined") {
 | 
				
			||||
				var filePath	= this[fileKey].responderFile;
 | 
				
			||||
				if (typeof this[fileKey].deinit === "function") {
 | 
				
			||||
					this[fileKey].deinit();
 | 
				
			||||
					
 | 
				
			||||
					delete this[fileKey];
 | 
				
			||||
				}
 | 
				
			||||
				
 | 
				
			||||
				// Delete responder cache
 | 
				
			||||
				if (typeof process.mainModule.moduleCache[filePath] !== "undefined") {
 | 
				
			||||
					delete process.mainModule.moduleCache[filePath];
 | 
				
			||||
				}
 | 
				
			||||
				
 | 
				
			||||
				delete this[fileKey];
 | 
				
			||||
				
 | 
				
			||||
				sys.puts(("[ responders ] removed: " + fileKey)[Configs.colors.responders]);
 | 
				
			||||
			// Sub responders follow... maybe
 | 
				
			||||
			} else {
 | 
				
			||||
				unload.apply(this[fileKey], [Foulinks, true]);
 | 
				
			||||
			}
 | 
				
			||||
		}
 | 
				
			||||
	}
 | 
				
			||||
	
 | 
				
			||||
	if (recurse !== true) {
 | 
				
			||||
		Foulinks.emit("responders:deloaded");
 | 
				
			||||
	}
 | 
				
			||||
};
 | 
				
			||||
 | 
				
			||||
exports.unload = unload;
 | 
				
			||||
 | 
				
			||||
					Loading…
					
					
				
		Reference in new issue