<commit_before>'use strict';

// Dependencies

var browser = require('bowser').browser,
	debug = require('debug')('rtcninja:Adapter'),
	debugerror = require('debug')('rtcninja:ERROR:Adapter'),

	// Internal vars
	getUserMedia = null,
	RTCPeerConnection = null,
	RTCSessionDescription = null,
	RTCIceCandidate = null,
	MediaStreamTrack = null,
	getMediaDevices = null,
	attachMediaStream = null,
	canRenegotiate = false,
	oldSpecRTCOfferOptions = false,
	browserVersion = Number(browser.version) || 0,
	isDesktop = !!(!browser.mobile || !browser.tablet),
	hasWebRTC = false,
	virtGlobal, virtNavigator;

debugerror.log = console.warn.bind(console);

// Dirty trick to get this library working in a Node-webkit & Browserify envs
virtGlobal = global.window || global;
// Don't fail in Node
virtNavigator = virtGlobal.navigator || {};

// Constructor.

function Adapter(options) {
	// Chrome desktop, Chrome Android, Opera desktop, Opera Android, Android native browser
	// or generic Webkit browser.
	if (
		(isDesktop && browser.chrome && browserVersion >= 32) ||
		(browser.android && browser.chrome && browserVersion >= 39) ||
		(isDesktop && browser.opera && browserVersion >= 27) ||
		(browser.android && browser.opera && browserVersion >= 24) ||
		(browser.android && browser.webkit && !browser.chrome && browserVersion >= 37) ||
		(virtNavigator.webkitGetUserMedia && virtGlobal.webkitRTCPeerConnection)
	) {
		hasWebRTC = true;
		getUserMedia = virtNavigator.webkitGetUserMedia.bind(virtNavigator);
		RTCPeerConnection = virtGlobal.webkitRTCPeerConnection;
		RTCSessionDescription = virtGlobal.RTCSessionDescription;
		RTCIceCandidate = virtGlobal.RTCIceCandidate;
		MediaStreamTrack = virtGlobal.MediaStreamTrack;
		if (MediaStreamTrack && MediaStreamTrack.getSources) {
			getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack);
		} else if (virtNavigator.getMediaDevices) {
			getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator);
		}
		attachMediaStream = function (element, stream) {
			element.src = URL.createObjectURL(stream);
			return element;
		};
		canRenegotiate = true;
		oldSpecRTCOfferOptions = false;
	// Firefox desktop, Firefox Android.
	} else if (
		(isDesktop && browser.firefox && browserVersion >= 22) ||
		(browser.android && browser.firefox && browserVersion >= 33) ||
		(virtNavigator.mozGetUserMedia && virtGlobal.mozRTCPeerConnection)
	) {
		hasWebRTC = true;
		getUserMedia = virtNavigator.mozGetUserMedia.bind(virtNavigator);
		RTCPeerConnection = virtGlobal.mozRTCPeerConnection;
		RTCSessionDescription = virtGlobal.mozRTCSessionDescription;
		RTCIceCandidate = virtGlobal.mozRTCIceCandidate;
		MediaStreamTrack = virtGlobal.MediaStreamTrack;
		attachMediaStream = function (element, stream) {
			element.src = URL.createObjectURL(stream);
			return element;
		};
		canRenegotiate = false;
		oldSpecRTCOfferOptions = false;
		// WebRTC plugin required. For example IE or Safari with the Temasys plugin.
	} else if (
		options.plugin &&
		typeof options.plugin.isRequired === 'function' &&
		options.plugin.isRequired() &&
		typeof options.plugin.isInstalled === 'function' &&
		options.plugin.isInstalled()
	) {
		var pluginiface = options.plugin.iface;

		hasWebRTC = true;
		getUserMedia = pluginiface.getUserMedia;
		RTCPeerConnection = pluginiface.RTCPeerConnection;
		RTCSessionDescription = pluginiface.RTCSessionDescription;
		RTCIceCandidate = pluginiface.RTCIceCandidate;
		MediaStreamTrack = pluginiface.MediaStreamTrack;
		// TODO: getSources() freezes IE so disable it.
		if (browser.safari) {
			if (MediaStreamTrack && MediaStreamTrack.getSources) {
				getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack);
			} else if (virtNavigator.getMediaDevices) {
				getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator);
			}
		}
		attachMediaStream = pluginiface.attachMediaStream;
		canRenegotiate = pluginiface.canRenegotiate;
		oldSpecRTCOfferOptions = true;  // TODO: Update when fixed in the plugin.
	// Best effort (may be adater.js is loaded).
	} else if (virtNavigator.getUserMedia && virtGlobal.RTCPeerConnection) {
		hasWebRTC = true;
		getUserMedia = virtNavigator.getUserMedia.bind(virtNavigator);
		RTCPeerConnection = virtGlobal.RTCPeerConnection;
		RTCSessionDescription = virtGlobal.RTCSessionDescription;
		RTCIceCandidate = virtGlobal.RTCIceCandidate;
		MediaStreamTrack = virtGlobal.MediaStreamTrack;
		if (MediaStreamTrack && MediaStreamTrack.getSources) {
			getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack);
		} else if (virtNavigator.getMediaDevices) {
			getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator);
		}
		attachMediaStream = virtGlobal.attachMediaStream || function (element, stream) {
			element.src = URL.createObjectURL(stream);
			return element;
		};
		canRenegotiate = false;
		oldSpecRTCOfferOptions = false;
	}

	function throwNonSupported(item) {
		return function () {
			throw new Error('rtcninja: WebRTC not supported, missing ' + item +
			' [browser: ' + browser.name + ' ' + browser.version + ']');
		};
	}

	// Public API.

	// Expose a WebRTC checker.
	Adapter.hasWebRTC = function () {
		return hasWebRTC;
	};

	// Expose getUserMedia.
	if (getUserMedia) {
		Adapter.getUserMedia = function (constraints, successCallback, errorCallback) {
			debug('getUserMedia() | constraints:', constraints);

			try {
				getUserMedia(constraints,
					function (stream) {
						debug('getUserMedia() | success');
						if (successCallback) {
							successCallback(stream);
						}
					},
					function (error) {
						debug('getUserMedia() | error:', error);
						if (errorCallback) {
							errorCallback(error);
						}
					}
				);
			}
			catch (error) {
				debugerror('getUserMedia() | error:', error);
				if (errorCallback) {
					errorCallback(error);
				}
			}
		};
	} else {
		Adapter.getUserMedia = function (constraints, successCallback, errorCallback) {
			debugerror('getUserMedia() | WebRTC not supported');
			if (errorCallback) {
				errorCallback(new Error('rtcninja: WebRTC not supported, missing ' +
				'getUserMedia [browser: ' + browser.name + ' ' + browser.version + ']'));
			} else {
				throwNonSupported('getUserMedia');
			}
		};
	}

	// Expose RTCPeerConnection.
	Adapter.RTCPeerConnection = RTCPeerConnection || throwNonSupported('RTCPeerConnection');

	// Expose RTCSessionDescription.
	Adapter.RTCSessionDescription = RTCSessionDescription || throwNonSupported('RTCSessionDescription');

	// Expose RTCIceCandidate.
	Adapter.RTCIceCandidate = RTCIceCandidate || throwNonSupported('RTCIceCandidate');

	// Expose MediaStreamTrack.
	Adapter.MediaStreamTrack = MediaStreamTrack || throwNonSupported('MediaStreamTrack');

	// Expose getMediaDevices.
	Adapter.getMediaDevices = getMediaDevices;

	// Expose MediaStreamTrack.
	Adapter.attachMediaStream = attachMediaStream || throwNonSupported('attachMediaStream');

	// Expose canRenegotiate attribute.
	Adapter.canRenegotiate = canRenegotiate;

	// Expose closeMediaStream.
	Adapter.closeMediaStream = function (stream) {
		if (!stream) {
			return;
		}

		// Latest spec states that MediaStream has no stop() method and instead must
		// call stop() on every MediaStreamTrack.
		if (MediaStreamTrack && MediaStreamTrack.prototype && MediaStreamTrack.prototype.stop) {
			debug('closeMediaStream() | calling stop() on all the MediaStreamTrack');

			var tracks, i, len;

			if (stream.getTracks) {
				tracks = stream.getTracks();
				for (i = 0, len = tracks.length; i < len; i += 1) {
					tracks[i].stop();
				}
			} else {
				tracks = stream.getAudioTracks();
				for (i = 0, len = tracks.length; i < len; i += 1) {
					tracks[i].stop();
				}

				tracks = stream.getVideoTracks();
				for (i = 0, len = tracks.length; i < len; i += 1) {
					tracks[i].stop();
				}
			}
		// Deprecated by the spec, but still in use.
		} else if (typeof stream.stop === 'function') {
			debug('closeMediaStream() | calling stop() on the MediaStream');

			stream.stop();
		}
	};

	// Expose fixPeerConnectionConfig.
	Adapter.fixPeerConnectionConfig = function (pcConfig) {
		var i, len, iceServer, hasUrls, hasUrl;

		if (!Array.isArray(pcConfig.iceServers)) {
			pcConfig.iceServers = [];
		}

		for (i = 0, len = pcConfig.iceServers.length; i < len; i += 1) {
			iceServer = pcConfig.iceServers[i];
			hasUrls = iceServer.hasOwnProperty('urls');
			hasUrl = iceServer.hasOwnProperty('url');

			if (typeof iceServer === 'object') {
				// Has .urls but not .url, so add .url with a single string value.
				if (hasUrls && !hasUrl) {
					iceServer.url = (Array.isArray(iceServer.urls) ? iceServer.urls[0] : iceServer.urls);
				// Has .url but not .urls, so add .urls with same value.
				} else if (!hasUrls && hasUrl) {
					iceServer.urls = (Array.isArray(iceServer.url) ? iceServer.url.slice() : iceServer.url);
				}

				// Ensure .url is a single string.
				if (hasUrl && Array.isArray(iceServer.url)) {
					iceServer.url = iceServer.url[0];
				}
			}
		}
	};

	// Expose fixRTCOfferOptions.
	Adapter.fixRTCOfferOptions = function (options) {
		options = options || {};

		// New spec.
		if (!oldSpecRTCOfferOptions) {
			if (options.mandatory && options.mandatory.OfferToReceiveAudio) {
				options.offerToReceiveAudio = 1;
			}
			if (options.mandatory && options.mandatory.OfferToReceiveVideo) {
				options.offerToReceiveVideo = 1;
			}
			delete options.mandatory;
		// Old spec.
		} else {
			if (options.offerToReceiveAudio) {
				options.mandatory = options.mandatory || {};
				options.mandatory.OfferToReceiveAudio = true;
			}
			if (options.offerToReceiveVideo) {
				options.mandatory = options.mandatory || {};
				options.mandatory.OfferToReceiveVideo = true;
			}
		}
	};

	return Adapter;
}

// Expose the Adapter function/object.
module.exports = Adapter;
<commit_msg>Comment cleared<commit_after>'use strict';

// Dependencies

var browser = require('bowser').browser,
	debug = require('debug')('rtcninja:Adapter'),
	debugerror = require('debug')('rtcninja:ERROR:Adapter'),

	// Internal vars
	getUserMedia = null,
	RTCPeerConnection = null,
	RTCSessionDescription = null,
	RTCIceCandidate = null,
	MediaStreamTrack = null,
	getMediaDevices = null,
	attachMediaStream = null,
	canRenegotiate = false,
	oldSpecRTCOfferOptions = false,
	browserVersion = Number(browser.version) || 0,
	isDesktop = !!(!browser.mobile || !browser.tablet),
	hasWebRTC = false,
	virtGlobal, virtNavigator;

debugerror.log = console.warn.bind(console);

// Dirty trick to get this library working in a Node-webkit env with browserified libs
virtGlobal = global.window || global;
// Don't fail in Node
virtNavigator = virtGlobal.navigator || {};

// Constructor.

function Adapter(options) {
	// Chrome desktop, Chrome Android, Opera desktop, Opera Android, Android native browser
	// or generic Webkit browser.
	if (
		(isDesktop && browser.chrome && browserVersion >= 32) ||
		(browser.android && browser.chrome && browserVersion >= 39) ||
		(isDesktop && browser.opera && browserVersion >= 27) ||
		(browser.android && browser.opera && browserVersion >= 24) ||
		(browser.android && browser.webkit && !browser.chrome && browserVersion >= 37) ||
		(virtNavigator.webkitGetUserMedia && virtGlobal.webkitRTCPeerConnection)
	) {
		hasWebRTC = true;
		getUserMedia = virtNavigator.webkitGetUserMedia.bind(virtNavigator);
		RTCPeerConnection = virtGlobal.webkitRTCPeerConnection;
		RTCSessionDescription = virtGlobal.RTCSessionDescription;
		RTCIceCandidate = virtGlobal.RTCIceCandidate;
		MediaStreamTrack = virtGlobal.MediaStreamTrack;
		if (MediaStreamTrack && MediaStreamTrack.getSources) {
			getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack);
		} else if (virtNavigator.getMediaDevices) {
			getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator);
		}
		attachMediaStream = function (element, stream) {
			element.src = URL.createObjectURL(stream);
			return element;
		};
		canRenegotiate = true;
		oldSpecRTCOfferOptions = false;
	// Firefox desktop, Firefox Android.
	} else if (
		(isDesktop && browser.firefox && browserVersion >= 22) ||
		(browser.android && browser.firefox && browserVersion >= 33) ||
		(virtNavigator.mozGetUserMedia && virtGlobal.mozRTCPeerConnection)
	) {
		hasWebRTC = true;
		getUserMedia = virtNavigator.mozGetUserMedia.bind(virtNavigator);
		RTCPeerConnection = virtGlobal.mozRTCPeerConnection;
		RTCSessionDescription = virtGlobal.mozRTCSessionDescription;
		RTCIceCandidate = virtGlobal.mozRTCIceCandidate;
		MediaStreamTrack = virtGlobal.MediaStreamTrack;
		attachMediaStream = function (element, stream) {
			element.src = URL.createObjectURL(stream);
			return element;
		};
		canRenegotiate = false;
		oldSpecRTCOfferOptions = false;
		// WebRTC plugin required. For example IE or Safari with the Temasys plugin.
	} else if (
		options.plugin &&
		typeof options.plugin.isRequired === 'function' &&
		options.plugin.isRequired() &&
		typeof options.plugin.isInstalled === 'function' &&
		options.plugin.isInstalled()
	) {
		var pluginiface = options.plugin.iface;

		hasWebRTC = true;
		getUserMedia = pluginiface.getUserMedia;
		RTCPeerConnection = pluginiface.RTCPeerConnection;
		RTCSessionDescription = pluginiface.RTCSessionDescription;
		RTCIceCandidate = pluginiface.RTCIceCandidate;
		MediaStreamTrack = pluginiface.MediaStreamTrack;
		// TODO: getSources() freezes IE so disable it.
		if (browser.safari) {
			if (MediaStreamTrack && MediaStreamTrack.getSources) {
				getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack);
			} else if (virtNavigator.getMediaDevices) {
				getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator);
			}
		}
		attachMediaStream = pluginiface.attachMediaStream;
		canRenegotiate = pluginiface.canRenegotiate;
		oldSpecRTCOfferOptions = true;  // TODO: Update when fixed in the plugin.
	// Best effort (may be adater.js is loaded).
	} else if (virtNavigator.getUserMedia && virtGlobal.RTCPeerConnection) {
		hasWebRTC = true;
		getUserMedia = virtNavigator.getUserMedia.bind(virtNavigator);
		RTCPeerConnection = virtGlobal.RTCPeerConnection;
		RTCSessionDescription = virtGlobal.RTCSessionDescription;
		RTCIceCandidate = virtGlobal.RTCIceCandidate;
		MediaStreamTrack = virtGlobal.MediaStreamTrack;
		if (MediaStreamTrack && MediaStreamTrack.getSources) {
			getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack);
		} else if (virtNavigator.getMediaDevices) {
			getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator);
		}
		attachMediaStream = virtGlobal.attachMediaStream || function (element, stream) {
			element.src = URL.createObjectURL(stream);
			return element;
		};
		canRenegotiate = false;
		oldSpecRTCOfferOptions = false;
	}

	function throwNonSupported(item) {
		return function () {
			throw new Error('rtcninja: WebRTC not supported, missing ' + item +
			' [browser: ' + browser.name + ' ' + browser.version + ']');
		};
	}

	// Public API.

	// Expose a WebRTC checker.
	Adapter.hasWebRTC = function () {
		return hasWebRTC;
	};

	// Expose getUserMedia.
	if (getUserMedia) {
		Adapter.getUserMedia = function (constraints, successCallback, errorCallback) {
			debug('getUserMedia() | constraints:', constraints);

			try {
				getUserMedia(constraints,
					function (stream) {
						debug('getUserMedia() | success');
						if (successCallback) {
							successCallback(stream);
						}
					},
					function (error) {
						debug('getUserMedia() | error:', error);
						if (errorCallback) {
							errorCallback(error);
						}
					}
				);
			}
			catch (error) {
				debugerror('getUserMedia() | error:', error);
				if (errorCallback) {
					errorCallback(error);
				}
			}
		};
	} else {
		Adapter.getUserMedia = function (constraints, successCallback, errorCallback) {
			debugerror('getUserMedia() | WebRTC not supported');
			if (errorCallback) {
				errorCallback(new Error('rtcninja: WebRTC not supported, missing ' +
				'getUserMedia [browser: ' + browser.name + ' ' + browser.version + ']'));
			} else {
				throwNonSupported('getUserMedia');
			}
		};
	}

	// Expose RTCPeerConnection.
	Adapter.RTCPeerConnection = RTCPeerConnection || throwNonSupported('RTCPeerConnection');

	// Expose RTCSessionDescription.
	Adapter.RTCSessionDescription = RTCSessionDescription || throwNonSupported('RTCSessionDescription');

	// Expose RTCIceCandidate.
	Adapter.RTCIceCandidate = RTCIceCandidate || throwNonSupported('RTCIceCandidate');

	// Expose MediaStreamTrack.
	Adapter.MediaStreamTrack = MediaStreamTrack || throwNonSupported('MediaStreamTrack');

	// Expose getMediaDevices.
	Adapter.getMediaDevices = getMediaDevices;

	// Expose MediaStreamTrack.
	Adapter.attachMediaStream = attachMediaStream || throwNonSupported('attachMediaStream');

	// Expose canRenegotiate attribute.
	Adapter.canRenegotiate = canRenegotiate;

	// Expose closeMediaStream.
	Adapter.closeMediaStream = function (stream) {
		if (!stream) {
			return;
		}

		// Latest spec states that MediaStream has no stop() method and instead must
		// call stop() on every MediaStreamTrack.
		if (MediaStreamTrack && MediaStreamTrack.prototype && MediaStreamTrack.prototype.stop) {
			debug('closeMediaStream() | calling stop() on all the MediaStreamTrack');

			var tracks, i, len;

			if (stream.getTracks) {
				tracks = stream.getTracks();
				for (i = 0, len = tracks.length; i < len; i += 1) {
					tracks[i].stop();
				}
			} else {
				tracks = stream.getAudioTracks();
				for (i = 0, len = tracks.length; i < len; i += 1) {
					tracks[i].stop();
				}

				tracks = stream.getVideoTracks();
				for (i = 0, len = tracks.length; i < len; i += 1) {
					tracks[i].stop();
				}
			}
		// Deprecated by the spec, but still in use.
		} else if (typeof stream.stop === 'function') {
			debug('closeMediaStream() | calling stop() on the MediaStream');

			stream.stop();
		}
	};

	// Expose fixPeerConnectionConfig.
	Adapter.fixPeerConnectionConfig = function (pcConfig) {
		var i, len, iceServer, hasUrls, hasUrl;

		if (!Array.isArray(pcConfig.iceServers)) {
			pcConfig.iceServers = [];
		}

		for (i = 0, len = pcConfig.iceServers.length; i < len; i += 1) {
			iceServer = pcConfig.iceServers[i];
			hasUrls = iceServer.hasOwnProperty('urls');
			hasUrl = iceServer.hasOwnProperty('url');

			if (typeof iceServer === 'object') {
				// Has .urls but not .url, so add .url with a single string value.
				if (hasUrls && !hasUrl) {
					iceServer.url = (Array.isArray(iceServer.urls) ? iceServer.urls[0] : iceServer.urls);
				// Has .url but not .urls, so add .urls with same value.
				} else if (!hasUrls && hasUrl) {
					iceServer.urls = (Array.isArray(iceServer.url) ? iceServer.url.slice() : iceServer.url);
				}

				// Ensure .url is a single string.
				if (hasUrl && Array.isArray(iceServer.url)) {
					iceServer.url = iceServer.url[0];
				}
			}
		}
	};

	// Expose fixRTCOfferOptions.
	Adapter.fixRTCOfferOptions = function (options) {
		options = options || {};

		// New spec.
		if (!oldSpecRTCOfferOptions) {
			if (options.mandatory && options.mandatory.OfferToReceiveAudio) {
				options.offerToReceiveAudio = 1;
			}
			if (options.mandatory && options.mandatory.OfferToReceiveVideo) {
				options.offerToReceiveVideo = 1;
			}
			delete options.mandatory;
		// Old spec.
		} else {
			if (options.offerToReceiveAudio) {
				options.mandatory = options.mandatory || {};
				options.mandatory.OfferToReceiveAudio = true;
			}
			if (options.offerToReceiveVideo) {
				options.mandatory = options.mandatory || {};
				options.mandatory.OfferToReceiveVideo = true;
			}
		}
	};

	return Adapter;
}

// Expose the Adapter function/object.
module.exports = Adapter;
<|endoftext|>


<commit_before>var async = require('async');
var Botkit = require('botkit');
var BotmockWorker = require('./BotmockWorker');

function BotMock(configuration){
    // Create a core botkit bot
    var botmock_botkit = Botkit.core(configuration);

    //override default botkit startTicking
    botmock_botkit.startTicking = function() {
        if (!botmock_botkit.tickInterval) {
            // set up a once a second tick to process messages
            botmock_botkit.tickInterval = setInterval(function() {
                botmock_botkit.tick();
            }, 10);
        }
    };

    botmock_botkit.on('message_received', function(bot, message){
        return botmock_botkit.trigger(message.type, [bot, message]);
    });

    botmock_botkit.defineBot(BotmockWorker);

    return botmock_botkit;
}

module.exports = BotMock;<commit_msg>update syntax<commit_after>var async = require('async');
var Botkit = require('botkit');
var BotmockWorker = require('./BotmockWorker');

function Botmock(configuration){
    // Create a core botkit bot
    var botmock = Botkit.core(configuration);

    //override default botkit startTicking
    botmock.startTicking = function() {
        if (!botmock.tickInterval) {
            // set up a once a second tick to process messages
            botmock.tickInterval = setInterval(function() {
                botmock.tick();
            }, 10);
        }
    };

    botmock.on('message_received', function(bot, message){
        return botmock.trigger(message.type, [bot, message]);
    });

    botmock.defineBot(BotmockWorker);

    return botmock;
}

module.exports = Botmock;<|endoftext|>


<commit_before>//-------------------------------------------------------------------------------
// Declare Class
//-------------------------------------------------------------------------------

var BugPack = {};

//-------------------------------------------------------------------------------
// Static Variables
//-------------------------------------------------------------------------------

BugPack.loadedPacks = {};

BugPack.registry = {};

//-------------------------------------------------------------------------------
// Static Methods
//-------------------------------------------------------------------------------

BugPack.declare = function(packName) {

};

BugPack.export = function(packName, pack) {
    BugPack.packs[packName] = pack;
};

BugPack.hasPack = function(packName) {
    return Object.prototype.hasOwnProperty.call(BugPack.packs, packName);
};

BugPack.lookupPack = function(packName) {

    throw new Error("Unknown bug pack '" + packName + "'");
};

BugPack.require = function(packName) {
    if (BugPack.hasPack(packName)) {
        return BugPack.packs[packName];
    } else {
        BugPack.lookupPack(packName);
    }
};
<commit_msg>[New] adding start of registry building<commit_after>//-------------------------------------------------------------------------------
// Requires
//-------------------------------------------------------------------------------

var child_process = require('child_process');
var fs = require('fs');

//TEST
console.log(module.filename);

//-------------------------------------------------------------------------------
// Declare Class
//-------------------------------------------------------------------------------

var BugPack = {};

//-------------------------------------------------------------------------------
// Static Variables
//-------------------------------------------------------------------------------

BugPack.loadedPacks = {};

BugPack.registry = null;

BugPack.registryBuilding = false;

//-------------------------------------------------------------------------------
// Static Methods
//-------------------------------------------------------------------------------

BugPack.buildRegistry = function(sourceRoots) {
    var newRegistry = {};
    var sourceFileArray = scanDirectoryForSourceFiles(sourceRoots);
    sourceFileArray.forEach(function(sourceFile) {
        var childProcess = child_process.fork(sourceFile);
        childProcess.on('message', function(message) {
            if (message.packName) {
                var packName = message.packName;
                newRegistry[packName] = sourceFile;
            } else {
                throw new Error("JS file '" + sourceFile + "' did not declare a pack name");
            }
        });
    });
    return newRegistry;
};

BugPack.declare = function(packName) {
    if (BugPack.registryBuilding) {
        process.send(JSON.stringify({
            packName: packName
        }));
        process.exit();
    }
};

BugPack.export = function(packName, pack) {
    BugPack.loadedPacks[packName] = pack;
};

BugPack.hasPackLoaded = function(packName) {
    return Object.prototype.hasOwnProperty.call(BugPack.loadedPacks, packName);
};

BugPack.loadPack = function(packName) {
    if (BugPack.registry === null) {
        BugPack.loadRegistry();
    }
    throw new Error("Unknown bug pack '" + packName + "'");
};

BugPack.loadRegistry = function() {
    //TODO BRN: Try using "require.main.filename" to find the root of the module. Also try "module.filename"
    // If neither of these work, use a similar search pattern as the node js module system. search backwards until the
    // first "node_modules" dir is found.

    //TODO BRN: Load the bugpack-registry.json file which should be located at the root of the module dir /bugpack-registry.json
};

BugPack.require = function(packName) {
    if (BugPack.hasPackLoaded(packName)) {
        return BugPack.loadedPacks[packName];
    } else {
        BugPack.loadPack(packName);
    }
};

/**
 * @param {string} directoryPathString
 * @param {boolean} scanRecursively (defaults to true)
 * @return {Array<string>}
 */
function scanDirectoryForSourceFiles(directoryPathString, scanRecursively) {
    console.log("scanning directory for js files - " + directoryPathString);
    if (scanRecursively === undefined) {
        scanRecursively = true;
    }
    var sourcePathArray = [];
    var fileStringArray = fs.readdirSync(directoryPathString);
    for (var i = 0, size = fileStringArray.length; i < size; i++) {
        var pathString = directoryPathString + "/" + fileStringArray[i];
        var stat = fs.statSync(pathString);
        if (stat.isDirectory()) {
            if (scanRecursively) {
                var childModulePathArray = scanDirectoryForSourceFiles(pathString);
                sourcePathArray.splice(0, 0, childModulePathArray);
            }
        } else if (stat.isFile()) {
            if (pathString.lastIndexOf('.js') === pathString.length - 3) {
                sourcePathArray.push(pathString);
            }
        }
    }
    return sourcePathArray;
}

//-------------------------------------------------------------------------------
// Exports
//-------------------------------------------------------------------------------

module.exports = BugPack;
<|endoftext|>


<commit_before>const window = require('xwindow')
const DomNode = require('./DomNode')

const { document } = window

/**
 * @see https://dom.spec.whatwg.org/#interface-element
 */
class DomElem extends DomNode
{
  className = undefined

  static prefix = ''

  static namespace = ''

  /**
   * @return {*|Element}
   */
  createNode() {
    let name, constructor = this.constructor
    const { prefix, namespace } = constructor
    do if(constructor.name.startsWith(prefix)) {
      name = constructor.name.slice(prefix.length)
      return document.createElementNS(namespace, name.toLowerCase())
    }
    while(constructor = Object.getPrototypeOf(constructor))
    return super.createNode()
  }

  /**
   * @param {boolean} deep
   */
  init(deep = false) {
    super.init(deep)
    this.setClassName()
  }

  setClassName() {
    if(this.className !== undefined) {
      if(this.className !== null) {
        this.node.className = this.className
      }
      return
    }
    let constructor = this.constructor
    const prefix = constructor.prefix
    const classList = this.node.classList
    do {
      if(constructor.name.startsWith(prefix)) {
        break
      }
      classList.add(constructor.name)
    }
    while(constructor = Object.getPrototypeOf(constructor))
  }

  /**
   * @param {constructor|string} attr
   * @param {function} [attr.get]
   * @param {function} [attr.has]
   * @param {*|null} [attr.defaultValue]
   * @returns {string|*|null}
   */
  getAttr(attr) {
    if(typeof attr === 'function') {
      return attr.has(this)? attr.get(this) : attr.defaultValue
    }
    return this.node.getAttribute(attr)
  }

  /**
   * @param {constructor|string} attr
   * @param {function} [attr.removeOnValue]
   * @param {function} [attr.set]
   * @param {string|null|*} value
   */
  setAttr(attr, value) {
    if(typeof attr === 'function') {
      if(!attr.removeOnValue(this, value)) {
        attr.set(this, value)
      }
    }
    else if(value === null) {
      this.node.removeAttribute(attr)
    }
    else this.node.setAttribute(attr, value)
  }

  /**
   * @param {constructor|string} attr
   * @param {function} [attr.has]
   * @returns {boolean}
   */
  hasAttr(attr) {
    return typeof attr === 'function'?
      attr.has(this) :
      this.node.hasAttribute(attr)
  }

  /**
   * @param {constructor|string} attr
   * @param {function} [attr.remove]
   */
  removeAttr(attr) {
    if(typeof attr === 'function') {
      attr.remove(this)
    }
    else this.node.removeAttribute(attr)
  }

  /**
   * @return {string}
   */
  toString() {
    return this.node.outerHTML
  }

  /**
   * Get all attributes of the element as an array
   * @returns {{}}
   */
  get attrs() {
    const attrs = {}
    for(const { name, value } of this.node.attributes) {
      attrs[name] = value
    }
    return attrs
  }

  /**
   * Set content attributes on the element
   * @param {{}} attrs
   */
  set attrs(attrs) {
    for(const [name, value] of Object.entries(attrs)) {
      value === null?
        this.removeAttr(name) :
        this.setAttr(name, value)
    }
  }

  /**
   * @return {DOMTokenList}
   */
  get class() {
    return this.node.classList
  }

  /**
   * @param {*} value {string|string[]|{}}
   */
  set class(value) {
    if(Array.isArray(value)) {
      this.node.classList.add(...value.filter(Boolean))
    }
    else if(value.constructor === Object) {
      for(const token of Object.keys(value)) {
        this.node.classList.toggle(token, value[token])
      }
    }
    else this.node.classList = value
  }

  /**
   * @returns {string}
   */
  get html() {
    return this.node.innerHTML
  }

  /**
   * @param {string} html
   */
  set html(html) {
    this.node.innerHTML = html
  }

  /**
   * @returns {DOMRect}
   */
  get rect() {
    return this.node.getBoundingClientRect()
  }

  /**
   * Get a text content of the node
   * @returns {string}
   */
  get text() {
    return this.node.textContent
  }

  /**
   * Set a text content of the node
   * @param {string} text
   */
  set text(text) {
    this.node.textContent = text
  }

  /**
   * @param {string[]|constructor[]} attrs
   */
  static defineAttrs(attrs) {
    let attr, name
    for(attr of attrs) {
      name = typeof attr === 'string'? attr : attr.attrName
      Object.defineProperty(this.prototype, name, {
        configurable : true,
        get() {
          return this.getAttr(attr)
        },
        set(value) {
          this.setAttr(attr, value)
        },
      })
    }
  }
}

DomElem.defineMethods([
  'scrollTo',
  'scrollBy',
])

DomElem.defineProps([
  'clientLeft',
  'clientTop',
  'clientWidth',
  'clientHeight',
  'scrollLeft',
  'scrollTop',
  'scrollWidth',
  'scrollHeight',
])

DomElem.defineProps([
  'id',
  'innerHTML',
  'textContent',
], true)

module.exports = DomElem
<commit_msg>DomElem: refactoring, "classList" getter added<commit_after>const window = require('xwindow')
const DomNode = require('./DomNode')

const { document } = window

/**
 * @see https://dom.spec.whatwg.org/#interface-element
 */
class DomElem extends DomNode
{
  className = undefined

  static prefix = ''

  static namespace = ''

  /**
   * @return {*|Element}
   */
  createNode() {
    let name, constructor = this.constructor
    const { prefix, namespace } = constructor
    do if(constructor.name.startsWith(prefix)) {
      name = constructor.name.slice(prefix.length)
      return document.createElementNS(namespace, name.toLowerCase())
    }
    while(constructor = Object.getPrototypeOf(constructor))
    return super.createNode()
  }

  /**
   * @param {boolean} deep
   */
  init(deep = false) {
    super.init(deep)
    this.setClassName()
  }

  setClassName() {
    if(this.className !== undefined) {
      if(this.className !== null) {
        this.node.className = this.className
      }
      return
    }
    let constructor = this.constructor
    const prefix = constructor.prefix
    const classList = this.node.classList
    do {
      if(constructor.name.startsWith(prefix)) {
        break
      }
      classList.add(constructor.name)
    }
    while(constructor = Object.getPrototypeOf(constructor))
  }

  /**
   * @param {constructor|string} attr
   * @param {function} [attr.get]
   * @param {function} [attr.has]
   * @param {*|null} [attr.defaultValue]
   * @returns {string|*|null}
   */
  getAttr(attr) {
    if(typeof attr === 'function') {
      return attr.has(this)? attr.get(this) : attr.defaultValue
    }
    return this.node.getAttribute(attr)
  }

  /**
   * @param {constructor|string} attr
   * @param {function} [attr.removeOnValue]
   * @param {function} [attr.set]
   * @param {string|null|*} value
   */
  setAttr(attr, value) {
    if(typeof attr === 'function') {
      if(!attr.removeOnValue(this, value)) {
        attr.set(this, value)
      }
    }
    else if(value === null) {
      this.node.removeAttribute(attr)
    }
    else this.node.setAttribute(attr, value)
  }

  /**
   * @param {constructor|string} attr
   * @param {function} [attr.has]
   * @returns {boolean}
   */
  hasAttr(attr) {
    return typeof attr === 'function'?
      attr.has(this) :
      this.node.hasAttribute(attr)
  }

  /**
   * @param {constructor|string} attr
   * @param {function} [attr.remove]
   */
  removeAttr(attr) {
    if(typeof attr === 'function') {
      attr.remove(this)
    }
    else this.node.removeAttribute(attr)
  }

  /**
   * @return {string}
   */
  toString() {
    return this.node.outerHTML
  }

  /**
   * Get all attributes of the element as an array
   * @returns {{}}
   */
  get attrs() {
    const attrs = {}
    for(const { name, value } of this.node.attributes) {
      attrs[name] = value
    }
    return attrs
  }

  /**
   * Set content attributes on the element
   * @param {{}} attrs
   */
  set attrs(attrs) {
    for(const [name, value] of Object.entries(attrs)) {
      value === null?
        this.removeAttr(name) :
        this.setAttr(name, value)
    }
  }

  /**
   * @return {string[]}
   */
  get class() {
    return this.node.className.split(' ')
  }

  /**
   * @param {*} value {string|string[]|{}}
   */
  set class(value) {
    if(Array.isArray(value)) {
      this.node.classList.add(...value.filter(Boolean))
    }
    else if(value.constructor === Object) {
      for(const token of Object.keys(value)) {
        this.node.classList.toggle(token, value[token])
      }
    }
    else this.node.classList = value
  }

  /**
   * @returns {DOMRect}
   */
  get rect() {
    return this.node.getBoundingClientRect()
  }

  /**
   * @param {string[]|constructor[]} attrs
   */
  static defineAttrs(attrs) {
    let attr, name
    for(attr of attrs) {
      name = typeof attr === 'string'? attr : attr.attrName
      Object.defineProperty(this.prototype, name, {
        configurable : true,
        get() {
          return this.getAttr(attr)
        },
        set(value) {
          this.setAttr(attr, value)
        },
      })
    }
  }
}

DomElem.defineMethods([
  'scrollTo',
  'scrollBy',
])

DomElem.defineProps([
  'classList',
  'clientLeft',
  'clientTop',
  'clientWidth',
  'clientHeight',
  'scrollLeft',
  'scrollTop',
  'scrollWidth',
  'scrollHeight',
])

DomElem.defineProps([
  'id',
  'innerHTML',
  'textContent',
], true)

Object.defineProperties(DomElem.prototype, {
  html : Object.getOwnPropertyDescriptor(DomElem.prototype, 'innerHTML'),
  text : Object.getOwnPropertyDescriptor(DomElem.prototype, 'textContent'),
})

module.exports = DomElem
<|endoftext|>


<commit_before>var foldHeaderLine = require('./foldHeaderLine'),
    formatHeaderName = require('./formatHeaderName'),
    isRegExp = require('./isRegExp');

function Headers(obj, doNotStringify) {
    this.valuesByName = {};
    this.populate(obj, doNotStringify);
}

Headers.prototype.populate = function (obj, doNotStringify) {
    if (typeof obj === 'string') {
        this.populateFromString(obj);
    } else if (obj && typeof obj === 'object') {
        this.populateFromObject(obj, doNotStringify);
   }
};

Headers.prototype.populateFromString = function (str) {
    var that = this,
        state = 'startLine',
        currentHeaderName = '',
        currentValue = '';

    function flush() {
        if (currentHeaderName.length > 0) {
            that.set(currentHeaderName, currentValue);
        }
        currentHeaderName = '';
        currentValue = '';
        state = 'startLine';
    }
    for (var i = 0 ; i < str.length ; i += 1) {
        var ch = str[i];
        if (state === 'startLine') {
            if (ch === ':') {
                state = 'startHeaderValue';
            } else if (ch === '\r' || ch === '\n') {
                // Parse error or terminating CRLFCRLF

                if (ch === '\r' && str[i + 1] === '\n' || (ch === '\n' && str[i + 1] === '\r')) {
                    i += 2;
                } else {
                    i += 1;
                }
                flush();
                return i;
            } else {
                currentHeaderName += ch;
            }
        } else if (state === 'startHeaderValue' || state === 'headerValue') {
            if (state === 'startHeaderValue') {
                if (ch === ' ') {
                    // Ignore space after :
                    continue;
                } else {
                    state = 'headerValue';
                }
            }
            if (ch === '\r') {
                if (str[i + 1] === '\n') {
                    if (/[ \t]/.test(str[i + 2])) {
                        // Skip past CRLF\s fold
                        i += 2;
                    } else {
                        i += 1;
                        flush();
                    }
                } else if (/[ \t]/.test(str[i + 1])) {
                    // Skip past CR\s fold
                    i += 1;
                } else {
                    flush();
                }
            } else if (ch === '\n') {
                if (str[i + 1] === '\r') {
                    if (/[ \t]/.test(str[i + 2])) {
                        // Skip past LFCR\s fold
                        i += 2;
                    } else {
                        i += 1;
                        flush();
                    }
                } else if (/[ \t]/.test(str[i + 1])) {
                    // Skip past LF\s fold
                    i += 1;
                } else {
                    flush();
                }
            } else {
                currentValue += ch;
            }
        }
    }
    flush();
    return i;
};

Headers.prototype.populateFromObject = function (valuesByName, doNotStringify) {
    Object.keys(valuesByName).forEach(function (headerName) {
        var value = valuesByName[headerName],
            headerNameLowerCase = headerName.toLowerCase();
        if (Array.isArray(value)) {
            this.valuesByName[headerNameLowerCase] = doNotStringify ? [].concat(value) : value.map(String);
        } else if (typeof value !== 'undefined') {
            this.valuesByName[headerNameLowerCase] = doNotStringify ? [value] : [String(value)];
        }
    }, this);
};

Headers.prototype.get = function (headerName, valueNumber) {
    valueNumber = valueNumber || 0;
    var values = this.valuesByName[headerName.toLowerCase()];
    if (values) {
        return values[valueNumber];
    }
};

Headers.prototype.getAll = function (headerName) {
    var values = this.valuesByName[headerName.toLowerCase()];
    if (values) {
        return [].concat(values);
    }
};

Headers.prototype.getNames = function (headerName) {
    return Object.keys(this.valuesByName);
};

Headers.prototype.count = function (headerName) {
    var values = this.valuesByName[headerName.toLowerCase()];
    if (values) {
        return values.length;
    } else {
        return 0;
    }
};

Headers.prototype.set = function (headerName, valueOrValues) {
    var headerNameLowerCase = headerName.toLowerCase();
    if (Array.isArray(valueOrValues)) {
        this.valuesByName[headerNameLowerCase] = valueOrValues.map(String);
    } else {
        (this.valuesByName[headerNameLowerCase] = this.valuesByName[headerNameLowerCase] || []).push(String(valueOrValues));
    }
};

Headers.prototype.remove = function (headerName, valueNumber) {
    var headerNameLowerCase = headerName.toLowerCase(),
        values = this.valuesByName[headerNameLowerCase];
    if (typeof valueNumber === 'undefined' || (values.length === 1 && valueNumber === 0)) {
        delete this.valuesByName[headerNameLowerCase];
    } else if (values) {
        values.splice(valueNumber, 1);
    }
};

// has('Content-Length')
// has('Content-Type', 'text/html');
// has('Cookie', ['foo=bar', 'baz=quux']);
Headers.prototype.has = function (headerName, stringOrArrayOrRegExp) {
    var values = this.valuesByName[headerName.toLowerCase()];
    if (typeof stringOrArrayOrRegExp === 'undefined') {
        return !!values;
    } else if (typeof values === 'undefined') {
        return false;
    } else {
        if (Array.isArray(stringOrArrayOrRegExp)) {
            return stringOrArrayOrRegExp.every(function (expectedValue) {
                if (isRegExp(expectedValue)) {
                    return values.some(function (value) {
                        return expectedValue.test(value);
                    });
                } else {
                    return values.indexOf(String(expectedValue)) !== -1;
                }
            });
        } else {
            return values.length === 1 && values[0] === String(stringOrArrayOrRegExp);
        }
    }
};

Headers.prototype.equals = function (other) {
    var headerNames = this.getNames(),
        otherHeaderNames = other.getNames();
    if (headerNames.length !== otherHeaderNames.length) {
        return false;
    }
    headerNames.sort();
    otherHeaderNames.sort();
    for (var i = 0 ; i < headerNames.length ; i += 1) {
        var headerName = headerNames[i];
        if (headerName !== otherHeaderNames[i]) {
            return false;
        }
        var headerValues = this.getAll(headerName),
            otherHeaderValues = other.getAll(headerName);
        if (headerValues.length !== otherHeaderValues.length) {
            return false;
        }
        if (headerValues.length === 1 && otherHeaderValues.length === 1) {
            if (headerValues[0] !== otherHeaderValues[0]) {
                return false;
            }
        } else {
            headerValues.sort();
            otherHeaderValues.sort();
            for (var j = 0 ; j < headerValues.length ; j += 1) {
                if (headerValues[i] !== otherHeaderValues[i]) {
                    return false;
                }
            }
        }
    }
    return true;
};

// Exploratory work wrt. https://github.com/sunesimonsen/unexpected/issues/40
Headers.prototype.satisfy = function (spec, mustBeExhaustive) {
    if (isRegExp(spec)) {
        return spec.test(this.toString());
    }
    if (spec && typeof spec === 'object' && !(spec instanceof Headers)) {
        var specPropertyNames = Object.keys(spec);
        // A value of 'undefined' means that the header must be absent:
        if (specPropertyNames.some(function (headerName) {
            return typeof spec[headerName] === 'undefined' && this.has(headerName);
        }, this)) {
            return false;
        }
    }

    var expectedHeaders = spec instanceof Headers ? spec : new Headers(spec, true),
        expectedHeaderNames = expectedHeaders.getNames();

    if (mustBeExhaustive && expectedHeaderNames.length < this.getNames().length) {
        return false;
    }
    return expectedHeaderNames.every(function (expectedHeaderName) {
        var expectedValues = expectedHeaders.getAll(expectedHeaderName);
        if (mustBeExhaustive) {
            var values = this.getAll(expectedHeaderName);

            if (expectedValues.length !== values.length) {
                return false;
            }
            if (values.length > 1 || expectedValues.length > 1) {
                values.sort();
                expectedValues.sort();
                if (!values.every(function (value, i) {
                    return isRegExp(expectedValues[i]) ? expectedValues[i].test(value) : value === expectedValues[i];
                })) {
                    return false;
                }
            }
        }
        return this.has(expectedHeaderName, expectedValues);
    }, this);
};

Headers.prototype.toString = function (maxLineLength) {
    var result = '',
        lowerCaseHeaderNames = this.getNames();
    lowerCaseHeaderNames.forEach(function (lowerCaseHeaderName) {
        this.valuesByName[lowerCaseHeaderName].forEach(function (value) {
            result += foldHeaderLine(formatHeaderName(lowerCaseHeaderName) + ': ' + value + "\r\n", maxLineLength);
        });
    }, this);
    return result;
};

module.exports = Headers;
<commit_msg>Implemented Headers.prototype.clone.<commit_after>var foldHeaderLine = require('./foldHeaderLine'),
    formatHeaderName = require('./formatHeaderName'),
    isRegExp = require('./isRegExp');

function Headers(obj, doNotStringify) {
    this.valuesByName = {};
    this.populate(obj, doNotStringify);
}

Headers.prototype.populate = function (obj, doNotStringify) {
    if (typeof obj === 'string') {
        this.populateFromString(obj);
    } else if (obj && typeof obj === 'object') {
        this.populateFromObject(obj, doNotStringify);
   }
};

Headers.prototype.populateFromString = function (str) {
    var that = this,
        state = 'startLine',
        currentHeaderName = '',
        currentValue = '';

    function flush() {
        if (currentHeaderName.length > 0) {
            that.set(currentHeaderName, currentValue);
        }
        currentHeaderName = '';
        currentValue = '';
        state = 'startLine';
    }
    for (var i = 0 ; i < str.length ; i += 1) {
        var ch = str[i];
        if (state === 'startLine') {
            if (ch === ':') {
                state = 'startHeaderValue';
            } else if (ch === '\r' || ch === '\n') {
                // Parse error or terminating CRLFCRLF

                if (ch === '\r' && str[i + 1] === '\n' || (ch === '\n' && str[i + 1] === '\r')) {
                    i += 2;
                } else {
                    i += 1;
                }
                flush();
                return i;
            } else {
                currentHeaderName += ch;
            }
        } else if (state === 'startHeaderValue' || state === 'headerValue') {
            if (state === 'startHeaderValue') {
                if (ch === ' ') {
                    // Ignore space after :
                    continue;
                } else {
                    state = 'headerValue';
                }
            }
            if (ch === '\r') {
                if (str[i + 1] === '\n') {
                    if (/[ \t]/.test(str[i + 2])) {
                        // Skip past CRLF\s fold
                        i += 2;
                    } else {
                        i += 1;
                        flush();
                    }
                } else if (/[ \t]/.test(str[i + 1])) {
                    // Skip past CR\s fold
                    i += 1;
                } else {
                    flush();
                }
            } else if (ch === '\n') {
                if (str[i + 1] === '\r') {
                    if (/[ \t]/.test(str[i + 2])) {
                        // Skip past LFCR\s fold
                        i += 2;
                    } else {
                        i += 1;
                        flush();
                    }
                } else if (/[ \t]/.test(str[i + 1])) {
                    // Skip past LF\s fold
                    i += 1;
                } else {
                    flush();
                }
            } else {
                currentValue += ch;
            }
        }
    }
    flush();
    return i;
};

Headers.prototype.populateFromObject = function (valuesByName, doNotStringify) {
    Object.keys(valuesByName).forEach(function (headerName) {
        var value = valuesByName[headerName],
            headerNameLowerCase = headerName.toLowerCase();
        if (Array.isArray(value)) {
            this.valuesByName[headerNameLowerCase] = doNotStringify ? [].concat(value) : value.map(String);
        } else if (typeof value !== 'undefined') {
            this.valuesByName[headerNameLowerCase] = doNotStringify ? [value] : [String(value)];
        }
    }, this);
};

Headers.prototype.clone = function () {
    var clone = new Headers();
    this.getNames().forEach(function (headerName) {
        clone.set(headerName, this.getAll(headerName));
    }, this);
    return clone;
};

Headers.prototype.get = function (headerName, valueNumber) {
    valueNumber = valueNumber || 0;
    var values = this.valuesByName[headerName.toLowerCase()];
    if (values) {
        return values[valueNumber];
    }
};

Headers.prototype.getAll = function (headerName) {
    var values = this.valuesByName[headerName.toLowerCase()];
    if (values) {
        return [].concat(values);
    }
};

Headers.prototype.getNames = function (headerName) {
    return Object.keys(this.valuesByName);
};

Headers.prototype.count = function (headerName) {
    var values = this.valuesByName[headerName.toLowerCase()];
    if (values) {
        return values.length;
    } else {
        return 0;
    }
};

Headers.prototype.set = function (headerName, valueOrValues) {
    var headerNameLowerCase = headerName.toLowerCase();
    if (Array.isArray(valueOrValues)) {
        this.valuesByName[headerNameLowerCase] = valueOrValues.map(String);
    } else {
        (this.valuesByName[headerNameLowerCase] = this.valuesByName[headerNameLowerCase] || []).push(String(valueOrValues));
    }
};

Headers.prototype.remove = function (headerName, valueNumber) {
    var headerNameLowerCase = headerName.toLowerCase(),
        values = this.valuesByName[headerNameLowerCase];
    if (typeof valueNumber === 'undefined' || (values.length === 1 && valueNumber === 0)) {
        delete this.valuesByName[headerNameLowerCase];
    } else if (values) {
        values.splice(valueNumber, 1);
    }
};

// has('Content-Length')
// has('Content-Type', 'text/html');
// has('Cookie', ['foo=bar', 'baz=quux']);
Headers.prototype.has = function (headerName, stringOrArrayOrRegExp) {
    var values = this.valuesByName[headerName.toLowerCase()];
    if (typeof stringOrArrayOrRegExp === 'undefined') {
        return !!values;
    } else if (typeof values === 'undefined') {
        return false;
    } else {
        if (Array.isArray(stringOrArrayOrRegExp)) {
            return stringOrArrayOrRegExp.every(function (expectedValue) {
                if (isRegExp(expectedValue)) {
                    return values.some(function (value) {
                        return expectedValue.test(value);
                    });
                } else {
                    return values.indexOf(String(expectedValue)) !== -1;
                }
            });
        } else {
            return values.length === 1 && values[0] === String(stringOrArrayOrRegExp);
        }
    }
};

Headers.prototype.equals = function (other) {
    var headerNames = this.getNames(),
        otherHeaderNames = other.getNames();
    if (headerNames.length !== otherHeaderNames.length) {
        return false;
    }
    headerNames.sort();
    otherHeaderNames.sort();
    for (var i = 0 ; i < headerNames.length ; i += 1) {
        var headerName = headerNames[i];
        if (headerName !== otherHeaderNames[i]) {
            return false;
        }
        var headerValues = this.getAll(headerName),
            otherHeaderValues = other.getAll(headerName);
        if (headerValues.length !== otherHeaderValues.length) {
            return false;
        }
        if (headerValues.length === 1 && otherHeaderValues.length === 1) {
            if (headerValues[0] !== otherHeaderValues[0]) {
                return false;
            }
        } else {
            headerValues.sort();
            otherHeaderValues.sort();
            for (var j = 0 ; j < headerValues.length ; j += 1) {
                if (headerValues[i] !== otherHeaderValues[i]) {
                    return false;
                }
            }
        }
    }
    return true;
};

// Exploratory work wrt. https://github.com/sunesimonsen/unexpected/issues/40
Headers.prototype.satisfy = function (spec, mustBeExhaustive) {
    if (isRegExp(spec)) {
        return spec.test(this.toString());
    }
    if (spec && typeof spec === 'object' && !(spec instanceof Headers)) {
        var specPropertyNames = Object.keys(spec);
        // A value of 'undefined' means that the header must be absent:
        if (specPropertyNames.some(function (headerName) {
            return typeof spec[headerName] === 'undefined' && this.has(headerName);
        }, this)) {
            return false;
        }
    }

    var expectedHeaders = spec instanceof Headers ? spec : new Headers(spec, true),
        expectedHeaderNames = expectedHeaders.getNames();

    if (mustBeExhaustive && expectedHeaderNames.length < this.getNames().length) {
        return false;
    }
    return expectedHeaderNames.every(function (expectedHeaderName) {
        var expectedValues = expectedHeaders.getAll(expectedHeaderName);
        if (mustBeExhaustive) {
            var values = this.getAll(expectedHeaderName);

            if (expectedValues.length !== values.length) {
                return false;
            }
            if (values.length > 1 || expectedValues.length > 1) {
                values.sort();
                expectedValues.sort();
                if (!values.every(function (value, i) {
                    return isRegExp(expectedValues[i]) ? expectedValues[i].test(value) : value === expectedValues[i];
                })) {
                    return false;
                }
            }
        }
        return this.has(expectedHeaderName, expectedValues);
    }, this);
};

Headers.prototype.toString = function (maxLineLength) {
    var result = '',
        lowerCaseHeaderNames = this.getNames();
    lowerCaseHeaderNames.forEach(function (lowerCaseHeaderName) {
        this.valuesByName[lowerCaseHeaderName].forEach(function (value) {
            result += foldHeaderLine(formatHeaderName(lowerCaseHeaderName) + ': ' + value + "\r\n", maxLineLength);
        });
    }, this);
    return result;
};

module.exports = Headers;
<|endoftext|>


<commit_before>/**
 * log.js
 * Andrea Tino - 2015
 */

/**
 * Responsible for handling logging.
 */
module.exports = function() {
  var fs = require('fs');
  var path = require('path');

  return {
    /**
     * Initializes the module.
     * filepath: Path to destination file.
     */
    initialize: function(filepath) {
      if (!filepath) {
        throw 'Error: filepath cannot be null or undefined!';
      }

      // TODO
    },

    /**
     * Logs an information.
     */
    info: function(text) {
      if (!text) {
        return;
      }

      // TODO
    },

    /**
     * Logs a warning.
     */
    warn: function(text) {
      if (!text) {
        return;
      }

      // TODO
    },

    /**
     * Logs an error.
     */
    err: function(text) {
      if (!text) {
        return;
      }

      // TODO
    }
  };
};

<commit_msg>Introducing logger<commit_after>/**
 * log.js
 * Andrea Tino - 2015
 */

/**
 * Responsible for handling logging.
 */
module.exports = function(_filepath) {
  var fs = require('fs');
  var path = require('path');

  var filepath = _filepath;

  var logMsg = function(header, message) {

  };

  return {
    /**
     * Logs an information.
     */
    info: function(text) {
      if (!text) {
        return;
      }

      console.log('info: ' + text);
    },

    /**
     * Logs a warning.
     */
    warn: function(text) {
      if (!text) {
        return;
      }

      console.log('warning: ' + text);
    },

    /**
     * Logs an error.
     */
    err: function(text) {
      if (!text) {
        return;
      }

      console.log('error: ' + text);
    }
  };
};

<|endoftext|>


<commit_before>/*global unescape*/
var Headers = require('./Headers'),
    isRegExp = require('./isRegExp');

function Message(obj) {
    this.headers = new Headers();
    this.populate(obj);
}

Message.prototype.isMessyMessage = true;

Message.prototype.populate = function (obj) {
    if (typeof Buffer === 'function' && Buffer.isBuffer(obj)) {
        this.populateFromBuffer(obj);
    } else if (typeof obj === 'string') {
        this.populateFromString(obj);
    } else if (obj && typeof obj === 'object') {
        this.populateFromObject(obj);
    }
    return this;
};

Message.prototype.populateFromObject = function (obj) {
    if (typeof obj.headers !== 'undefined') {
        this.headers.populate(obj.headers);
    }
    if (typeof obj.body !== 'undefined') {
        this.body = obj.body;
    }
    return this;
};

Message.prototype.populateFromBuffer = function (buffer) {
    // Hack: Interpret non-ASCII in headers as iso-8859-1:
    var str = '';
    for (var i = 0 ; i < buffer.length ; i += 1) {
        var octet = buffer[i];
        if (octet > 127) {
            str += unescape('%' + octet.toString(16));
        } else {
            str += String.fromCharCode(octet);
        }
        if (/\r\r$|\n\n$|\r\n\r\n$|\n\r\n\r$/.test(str)) {
            i += 1;
            if (i < buffer.length) {
                this.body = buffer.slice(i);
            }
            break;
        }
    }
    this.headers.populateFromString(str, true);
    return this;
};

Message.prototype.populateFromString = function (str) {
    var bodyStartIndex = this.headers.populateFromStringAndReturnBodyStartIndex(str);
    if (bodyStartIndex < str.length) {
        this.body = str.substr(bodyStartIndex);
    }
    return this;
};

function buffersEqual(a, b) {
    if (a === b) {
        return true;
    }

    if (a.length !== b.length) return false;

    for (var i = 0; i < a.length; i += 1) {
        if (a[i] !== b[i]) return false;
    }

    return true;
}

function isNonBufferNonRegExpObject(obj) {
    return obj && typeof obj === 'object' && (typeof Buffer === 'undefined' || !Buffer.isBuffer(obj)) && !isRegExp(obj);
}

function canonicalizeObject(obj, stack) {
    stack = stack || [];

    if (stack.indexOf(obj) !== -1) return '[Circular]';

    var canonicalizedObject;

    if ({}.toString.call(obj) === '[object Array]') {
        stack.push(obj);
        canonicalizedObject = obj.map(function (item) {
            return canonicalizeObject(item, stack);
        });
        stack.pop();
    } else if (typeof obj === 'object' && obj !== null) {
        stack.push(obj);
        canonicalizedObject = {};
        Object.keys(obj).sort().forEach(function (key) {
            canonicalizedObject[key] = canonicalizeObject(obj[key], stack);
        });
        stack.pop();
    } else {
        canonicalizedObject = obj;
    }

    return canonicalizedObject;
}

Message.prototype.clone = function () {
    return new Message({
        headers: this.headers.clone(),
        body: this.body // Not sure
    });
};

Message.prototype.toString = function (maxLineLength) {
    if (typeof maxLineLength === 'undefined') {
        maxLineLength = 72;
    }
    var result = this.headers.toString(maxLineLength);
    if (typeof this.body !== 'undefined') {
        result += '\r\n';
        if (this.body && typeof this.body === 'object' && isNonBufferNonRegExpObject(this.body) && /^application\/json\b|\+json/i.test(this.headers.get('Content-Type'))) {
            result += JSON.stringify(this.body);
        } else {
            result += this.body;
        }
    }
    return result;
};

Message.prototype.equals = function (other) {
    return this === other || (
        this.headers.equals(other.headers) &&
        (this.body === other.body ||
        (typeof Buffer === 'function' && Buffer.isBuffer(this.body) && Buffer.isBuffer(other.body) && buffersEqual(this.body, other.body)))
    );
};

Message.prototype.hasEmptyBody = function () {
    if (typeof this.body === 'string') {
        return this.body.length === 0;
    } else if (typeof Buffer === 'function' && Buffer.isBuffer(this.body)) {
        return this.body.length === 0;
    } else if (this.body && typeof this.body === 'object') {
        return false;
    } else {
        return true;
    }
};

function isTextualContentType(contentType) {
    if (typeof contentType === 'string') {
        contentType = contentType.toLowerCase().trim().replace(/\s*;.*$/, '');
        return (
            /^text\//.test(contentType) ||
            /^application\/(json|javascript)$/.test(contentType) ||
            /^application\/xml/.test(contentType) ||
            /^application\/x-www-form-urlencoded\b/.test(contentType) ||
            /\+xml$/.test(contentType)
        );
    }
    return false;
}

function bufferCanBeInterpretedAsUtf8(buffer) {
    // Hack: Since Buffer.prototype.toString('utf-8') is very forgiving, convert the buffer to a string
    // with percent-encoded octets, then see if decodeURIComponent accepts it.
    try {
        decodeURIComponent(Array.prototype.map.call(buffer, function (octet) {
            return '%' + (octet < 16 ? '0' : '') + octet.toString(16);
        }).join(''));
    } catch (e) {
        return false;
    }
    return true;
}

Message.prototype.upgradeBody = function () {
    if (typeof this.body !== 'undefined') {
        var isJson = /^application\/json\b/i.test(this.headers.get('Content-Type'));
        if ((typeof Buffer === 'function' && Buffer.isBuffer(this.body)) && isTextualContentType(this.headers.get('Content-Type')) && bufferCanBeInterpretedAsUtf8(this.body)) {
            this.body = this.body.toString('utf-8');
        }
        if (isJson && typeof this.body === 'string') {
            try {
                this.body = JSON.parse(this.body);
            } catch (e) {
                // The body cannot be parsed as JSON, keep as a string instance
            }
        }
    }
};

Message.prototype.upgradeOrDowngradeBodyToMatchSpec = function (spec) {
    if (typeof this.body !== 'undefined') {
        var isJson = /^application\/json\b/i.test(this.headers.get('Content-Type'));
        if (isNonBufferNonRegExpObject(this.body) && isJson) {
            if (typeof spec.body === 'string' || (typeof Buffer === 'function' && Buffer.isBuffer(spec.body))) {
                var parsedSpecBody;
                try {
                    parsedSpecBody = JSON.parse(spec.body);
                } catch (e) {}
                if (typeof parsedSpecBody !== 'undefined') {
                    spec.body = JSON.stringify(canonicalizeObject(parsedSpecBody), undefined, '  ');
                    this.body = JSON.stringify(this.body, undefined, '  ');
                }
            } else if (isRegExp(spec.body)) {
                this.body = JSON.stringify(this.body, undefined, '  ');
            }
        }
        if (typeof Buffer === 'function' && Buffer.isBuffer(this.body) && ((typeof spec.body === 'string' || isRegExp(spec.body) || isNonBufferNonRegExpObject(spec.body)) || (bufferCanBeInterpretedAsUtf8(this.body) && isTextualContentType(this.headers.get('Content-Type'))))) {
            try {
                this.body = this.body.toString('utf-8');
            } catch (e) {
                // The body cannot be intepreted as utf-8, keep it as a Buffer instance
            }
        }
        if (isJson && typeof this.body === 'string' && (typeof spec.body === 'undefined' || typeof spec.body === 'function' || isNonBufferNonRegExpObject(spec.body))) {
            try {
                this.body = JSON.parse(this.body);
            } catch (e) {
                // The body cannot be parsed as JSON, keep as a string instance
            }
        } else if (typeof Buffer === 'function' && Buffer.isBuffer(spec.body) && (!this.body || typeof this.body === 'string')) {
            this.body = new Buffer(this.body, 'utf-8');
        }
    } else if (typeof Buffer === 'function' && Buffer.isBuffer(spec.body) && spec.body.length === 0) {
        this.body = new Buffer([]);
    } else if (spec.body === '') {
        this.body = '';
    }
};

module.exports = Message;
<commit_msg>Message: Remove code related to upgrading and downgrading the body to match a spec.<commit_after>/*global unescape*/
var Headers = require('./Headers'),
    isRegExp = require('./isRegExp');

function Message(obj) {
    this.headers = new Headers();
    this.populate(obj);
}

Message.prototype.isMessyMessage = true;

Message.prototype.populate = function (obj) {
    if (typeof Buffer === 'function' && Buffer.isBuffer(obj)) {
        this.populateFromBuffer(obj);
    } else if (typeof obj === 'string') {
        this.populateFromString(obj);
    } else if (obj && typeof obj === 'object') {
        this.populateFromObject(obj);
    }
    return this;
};

Message.prototype.populateFromObject = function (obj) {
    if (typeof obj.headers !== 'undefined') {
        this.headers.populate(obj.headers);
    }
    if (typeof obj.body !== 'undefined') {
        this.body = obj.body;
    }
    return this;
};

Message.prototype.populateFromBuffer = function (buffer) {
    // Hack: Interpret non-ASCII in headers as iso-8859-1:
    var str = '';
    for (var i = 0 ; i < buffer.length ; i += 1) {
        var octet = buffer[i];
        if (octet > 127) {
            str += unescape('%' + octet.toString(16));
        } else {
            str += String.fromCharCode(octet);
        }
        if (/\r\r$|\n\n$|\r\n\r\n$|\n\r\n\r$/.test(str)) {
            i += 1;
            if (i < buffer.length) {
                this.body = buffer.slice(i);
            }
            break;
        }
    }
    this.headers.populateFromString(str, true);
    return this;
};

Message.prototype.populateFromString = function (str) {
    var bodyStartIndex = this.headers.populateFromStringAndReturnBodyStartIndex(str);
    if (bodyStartIndex < str.length) {
        this.body = str.substr(bodyStartIndex);
    }
    return this;
};

function buffersEqual(a, b) {
    if (a === b) {
        return true;
    }

    if (a.length !== b.length) return false;

    for (var i = 0; i < a.length; i += 1) {
        if (a[i] !== b[i]) return false;
    }

    return true;
}

function isNonBufferNonRegExpObject(obj) {
    return obj && typeof obj === 'object' && (typeof Buffer === 'undefined' || !Buffer.isBuffer(obj)) && !isRegExp(obj);
}

Message.prototype.clone = function () {
    return new Message({
        headers: this.headers.clone(),
        body: this.body // Not sure
    });
};

Message.prototype.toString = function (maxLineLength) {
    if (typeof maxLineLength === 'undefined') {
        maxLineLength = 72;
    }
    var result = this.headers.toString(maxLineLength);
    if (typeof this.body !== 'undefined') {
        result += '\r\n';
        if (this.body && typeof this.body === 'object' && isNonBufferNonRegExpObject(this.body) && /^application\/json\b|\+json/i.test(this.headers.get('Content-Type'))) {
            result += JSON.stringify(this.body);
        } else {
            result += this.body;
        }
    }
    return result;
};

Message.prototype.equals = function (other) {
    return this === other || (
        this.headers.equals(other.headers) &&
        (this.body === other.body ||
        (typeof Buffer === 'function' && Buffer.isBuffer(this.body) && Buffer.isBuffer(other.body) && buffersEqual(this.body, other.body)))
    );
};

Message.prototype.hasEmptyBody = function () {
    if (typeof this.body === 'string') {
        return this.body.length === 0;
    } else if (typeof Buffer === 'function' && Buffer.isBuffer(this.body)) {
        return this.body.length === 0;
    } else if (this.body && typeof this.body === 'object') {
        return false;
    } else {
        return true;
    }
};

module.exports = Message;
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var Emitter = require('events').EventEmitter
  , path = require('path')
  , dirname = path.dirname
  , basename = path.basename
  , extname = path.extname
  , join = path.join
  , mkdir = require('mkdirp').mkdirp
  , request = require('superagent')
  , debug = require('debug')('component:installer')
  , Batch = require('batch')
  , http = require('http')
  , url = require('url')
  , fs = require('fs');

/**
 * In-flight requests.
 */

var inFlight = {};

/**
 * Expose installer.
 */

module.exports = Package;

/**
 * Initialize a new `Package` with
 * the given `pkg` name and `version`.
 *
 * Options:
 *
 *  - `dest` destination directory
 *  - `force` installation when previously installed
 *  - `remote` remote url defaulting to "https://raw.github.com"
 *
 * @param {String} pkg
 * @param {String} version
 * @param {Object} options
 * @api private
 */

function Package(pkg, version, options) {
  options = options || {};
  if ('*' == version) version = 'master';
  debug('installing %s@%s %j', pkg, version, options);
  if (!pkg) throw new Error('pkg required');
  if (!version) throw new Error('version required');
  this.name = pkg;
  this.slug = pkg + '@' + version;
  this.dest = options.dest || 'components';
  this.remotes = options.remotes || ['https://raw.github.com'];
  this.auth = options.auth;
  this.force = !! options.force;
  this.version = version;
  if (inFlight[this.slug]) this.install = function(){};
  inFlight[this.slug] = true;
}

/**
 * Inherit from `Emitter.prototype`.
 */

Package.prototype.__proto__ = Emitter.prototype;

/**
 * Return dirname for this package.
 * For example "component/dialog"
 * becomes "component-dialog".
 *
 * @return {String}
 * @api private
 */

Package.prototype.dirname = function(){
  return join(this.dest, this.name.split('/').join('-'));
};

/**
 * Join `path` to this package's dirname.
 *
 * @param {String} path
 * @return {String}
 * @api private
 */

Package.prototype.join = function(path){
  return join(this.dirname(), path);
};

/**
 * Return URL to `file`.
 *
 * @param {String} file
 * @return {String}
 * @api private
 */

Package.prototype.url = function(file){
  var remote = this.remote
    ? this.remote.href
    : this.remotes[0];

  return remote + '/' + this.name + '/' + this.version + '/' + file;
};

/**
 * Conditionaly mkdir `dir` unless we've
 * already done so previously.
 *
 * @param {String} dir
 * @param {Function} fn
 * @api private
 */

Package.prototype.mkdir = function(dir, fn){
  this.dirs = this.dirs || {};
  if (this.dirs[dir]) return fn();
  mkdir(dir, fn);
};

/**
 * Get local json if the component is installed
 * and callback `fn(err, obj)`.
 *
 * @param {Function} fn
 * @api private
 */

Package.prototype.getLocalJSON = function(fn){
  var path = this.join('component.json');
  fs.readFile(path, 'utf8', function(err, json){
    if (err) return fn(err);
    try {
      json = JSON.parse(json);
    } catch (err) {
      err.message += ' in ' + path;
      return fn(err);
    }
    fn(null, json);
  });
};

/**
 * Get component.json and callback `fn(err, obj)`.
 *
 * @param {Function} fn
 * @api private
 */

Package.prototype.getJSON = function(fn){
  var self = this;
  var url = this.url('component.json');

  debug('fetching %s', url);
  var req = request.get(url);
  req.set('Accept-Encoding', 'gzip');

  req.end(function(res){
    if (res.error) return fn(error(res, url));
    try {
      debug('got %s', url);
      var json = JSON.parse(res.text);
    } catch (err) {
      err.message += ' in ' + url;
      return fn(err);
    }
    fn(null, json);
  });

  req.on('error', function(err){
    if ('getaddrinfo' == err.syscall) err.message = 'dns lookup failed';
    fn(err);
  });
};

/**
 * Fetch `files` and write them to disk and callback `fn(err)`.
 *
 * @param {Array} files
 * @param {Function} fn
 * @api private
 */

Package.prototype.getFiles = function(files, fn){
  var self = this;
  var batch = new Batch;

  files.forEach(function(file){
    batch.push(function(done){
      var url = self.url(file);
      debug('fetching %s', url);
      self.emit('file', file, url);
      var dst = self.join(file);

      // mkdir
      self.mkdir(dirname(dst), function(err){
        if (err) return fn(err);

        // pipe file
        var req = request.get(url);
        req.set('Accept-Encoding', 'gzip');
        req.buffer(false);

        if (self.auth) req.auth(self.auth.user, self.auth.pass);

        req.end(function(res){
          if (res.error) return done(error(res, url));
          res.pipe(fs.createWriteStream(dst));
          res.on('error', done);
          res.on('end', done);
        });
      });
    });
  });

  batch.end(fn);
};

/**
 * Write `file` with `str` contents to disk and callback `fn(err)`.
 *
 * @param {String} file
 * @param {String} str
 * @param {Function} fn
 * @api private
 */

Package.prototype.writeFile = function(file, str, fn){
  file = this.join(file);
  debug('write %s', file);
  fs.writeFile(file, str, fn);
};

/**
 * Install `deps` and callback `fn()`.
 *
 * @param {Array} deps
 * @param {Function} fn
 * @api private
 */

Package.prototype.getDependencies = function(deps, fn){
  var self = this;
  var batch = new Batch;

  Object.keys(deps).forEach(function(name){
    var version = deps[name];
    debug('dep %s@%s', name, version);
    batch.push(function(done){
      var pkg = new Package(name, version, {
        dest: self.dest,
        force: self.force,
        remotes: self.remotes
      });
      self.emit('dep', pkg);
      pkg.on('end', done);
      pkg.on('exists', function() { done(); });
      pkg.install();
    });
  });

  batch.end(fn);
};

/**
 * Check if the component exists already,
 * otherwise install it for realllll.
 *
 * @api public
 */

Package.prototype.install = function(){
  var self = this;
  var name = this.name;

  if (!~name.indexOf('/')) {
    return this.emit('error', new Error('invalid component name "' + name + '"'));
  }

  this.getLocalJSON(function(err, json){
    if (err && err.code == 'ENOENT') {
      self.reallyInstall();
    } else if (err) {
      self.emit('error', err);
    } else if (!self.force) {
      self.emit('exists', self);
    } else {
      self.reallyInstall();
    }
  });
};

/**
 * Really install the component.
 *
 * @api public
 */

Package.prototype.reallyInstall = function(){
  var self = this;
  var i = 0;
  var batch;
  var last;

  next();

  function next() {
    self.remote = self.remotes[i++];
    if (!self.remote) return self.emit('error', new Error('can\'t find remote for "' + self.name + '"'));

    // parse remote
    last = i == self.remotes.length;
    self.remote = url.parse(self.remote);

    // strip trailing /
    self.remote.href = self.remote.href.slice(0, -1);

    // only error on the last remote otherwise
    // we assume it may be fetchable
    self.once('error', next);

    // kick off installation
    batch = new Batch;
    self.getJSON(function(err, json){
      if (err) {
        err.fatal = 404 != err.status || last;
        return self.emit('error', err);
      }

      var files = [];
      if (json.scripts) files = files.concat(json.scripts);
      if (json.styles) files = files.concat(json.styles);
      if (json.templates) files = files.concat(json.templates);
      if (json.files) files = files.concat(json.files);
      if (json.images) files = files.concat(json.images);
      if (json.fonts) files = files.concat(json.fonts);
      json.repo = json.repo || self.remote.href + '/' + self.name;

      if (json.dependencies) {
        batch.push(function(done){
          self.getDependencies(json.dependencies, done);
        });
      }

      batch.push(function(done){
        self.mkdir(self.dirname(), function(err){
          json = JSON.stringify(json, null, 2);
          self.writeFile('component.json', json, done);
        });
      });

      batch.push(function(done){
        self.mkdir(self.dirname(), function(err){
          self.getFiles(files, done);
        });
      });

      batch.end(function(err){
        if (err) {
          err.fatal = last;
          return self.emit('error', err);
        }
        self.emit('end');
      });
    });
  }
};

/**
 * Return an error for `res` / `url`.
 *
 * @param {Response} res
 * @param {String} url
 * @return {Error}
 * @api private
 */

function error(res, url) {
  var name = http.STATUS_CODES[res.status];
  var err = new Error('failed to fetch ' + url + ', got ' + res.status + ' "' + name + '"');
  err.status = res.status;
  return err;
}
<commit_msg>fix a call to fn() that should be done()<commit_after>
/**
 * Module dependencies.
 */

var Emitter = require('events').EventEmitter
  , path = require('path')
  , dirname = path.dirname
  , basename = path.basename
  , extname = path.extname
  , join = path.join
  , mkdir = require('mkdirp').mkdirp
  , request = require('superagent')
  , debug = require('debug')('component:installer')
  , Batch = require('batch')
  , http = require('http')
  , url = require('url')
  , fs = require('fs');

/**
 * In-flight requests.
 */

var inFlight = {};

/**
 * Expose installer.
 */

module.exports = Package;

/**
 * Initialize a new `Package` with
 * the given `pkg` name and `version`.
 *
 * Options:
 *
 *  - `dest` destination directory
 *  - `force` installation when previously installed
 *  - `remote` remote url defaulting to "https://raw.github.com"
 *
 * @param {String} pkg
 * @param {String} version
 * @param {Object} options
 * @api private
 */

function Package(pkg, version, options) {
  options = options || {};
  if ('*' == version) version = 'master';
  debug('installing %s@%s %j', pkg, version, options);
  if (!pkg) throw new Error('pkg required');
  if (!version) throw new Error('version required');
  this.name = pkg;
  this.slug = pkg + '@' + version;
  this.dest = options.dest || 'components';
  this.remotes = options.remotes || ['https://raw.github.com'];
  this.auth = options.auth;
  this.force = !! options.force;
  this.version = version;
  if (inFlight[this.slug]) this.install = function(){};
  inFlight[this.slug] = true;
}

/**
 * Inherit from `Emitter.prototype`.
 */

Package.prototype.__proto__ = Emitter.prototype;

/**
 * Return dirname for this package.
 * For example "component/dialog"
 * becomes "component-dialog".
 *
 * @return {String}
 * @api private
 */

Package.prototype.dirname = function(){
  return join(this.dest, this.name.split('/').join('-'));
};

/**
 * Join `path` to this package's dirname.
 *
 * @param {String} path
 * @return {String}
 * @api private
 */

Package.prototype.join = function(path){
  return join(this.dirname(), path);
};

/**
 * Return URL to `file`.
 *
 * @param {String} file
 * @return {String}
 * @api private
 */

Package.prototype.url = function(file){
  var remote = this.remote
    ? this.remote.href
    : this.remotes[0];

  return remote + '/' + this.name + '/' + this.version + '/' + file;
};

/**
 * Conditionaly mkdir `dir` unless we've
 * already done so previously.
 *
 * @param {String} dir
 * @param {Function} fn
 * @api private
 */

Package.prototype.mkdir = function(dir, fn){
  this.dirs = this.dirs || {};
  if (this.dirs[dir]) return fn();
  mkdir(dir, fn);
};

/**
 * Get local json if the component is installed
 * and callback `fn(err, obj)`.
 *
 * @param {Function} fn
 * @api private
 */

Package.prototype.getLocalJSON = function(fn){
  var path = this.join('component.json');
  fs.readFile(path, 'utf8', function(err, json){
    if (err) return fn(err);
    try {
      json = JSON.parse(json);
    } catch (err) {
      err.message += ' in ' + path;
      return fn(err);
    }
    fn(null, json);
  });
};

/**
 * Get component.json and callback `fn(err, obj)`.
 *
 * @param {Function} fn
 * @api private
 */

Package.prototype.getJSON = function(fn){
  var self = this;
  var url = this.url('component.json');

  debug('fetching %s', url);
  var req = request.get(url);
  req.set('Accept-Encoding', 'gzip');

  req.end(function(res){
    if (res.error) return fn(error(res, url));
    try {
      debug('got %s', url);
      var json = JSON.parse(res.text);
    } catch (err) {
      err.message += ' in ' + url;
      return fn(err);
    }
    fn(null, json);
  });

  req.on('error', function(err){
    if ('getaddrinfo' == err.syscall) err.message = 'dns lookup failed';
    fn(err);
  });
};

/**
 * Fetch `files` and write them to disk and callback `fn(err)`.
 *
 * @param {Array} files
 * @param {Function} fn
 * @api private
 */

Package.prototype.getFiles = function(files, fn){
  var self = this;
  var batch = new Batch;

  files.forEach(function(file){
    batch.push(function(done){
      var url = self.url(file);
      debug('fetching %s', url);
      self.emit('file', file, url);
      var dst = self.join(file);

      // mkdir
      self.mkdir(dirname(dst), function(err){
        if (err) return done(err);

        // pipe file
        var req = request.get(url);
        req.set('Accept-Encoding', 'gzip');
        req.buffer(false);

        if (self.auth) req.auth(self.auth.user, self.auth.pass);

        req.end(function(res){
          if (res.error) return done(error(res, url));
          res.pipe(fs.createWriteStream(dst));
          res.on('error', done);
          res.on('end', done);
        });
      });
    });
  });

  batch.end(fn);
};

/**
 * Write `file` with `str` contents to disk and callback `fn(err)`.
 *
 * @param {String} file
 * @param {String} str
 * @param {Function} fn
 * @api private
 */

Package.prototype.writeFile = function(file, str, fn){
  file = this.join(file);
  debug('write %s', file);
  fs.writeFile(file, str, fn);
};

/**
 * Install `deps` and callback `fn()`.
 *
 * @param {Array} deps
 * @param {Function} fn
 * @api private
 */

Package.prototype.getDependencies = function(deps, fn){
  var self = this;
  var batch = new Batch;

  Object.keys(deps).forEach(function(name){
    var version = deps[name];
    debug('dep %s@%s', name, version);
    batch.push(function(done){
      var pkg = new Package(name, version, {
        dest: self.dest,
        force: self.force,
        remotes: self.remotes
      });
      self.emit('dep', pkg);
      pkg.on('end', done);
      pkg.on('exists', function() { done(); });
      pkg.install();
    });
  });

  batch.end(fn);
};

/**
 * Check if the component exists already,
 * otherwise install it for realllll.
 *
 * @api public
 */

Package.prototype.install = function(){
  var self = this;
  var name = this.name;

  if (!~name.indexOf('/')) {
    return this.emit('error', new Error('invalid component name "' + name + '"'));
  }

  this.getLocalJSON(function(err, json){
    if (err && err.code == 'ENOENT') {
      self.reallyInstall();
    } else if (err) {
      self.emit('error', err);
    } else if (!self.force) {
      self.emit('exists', self);
    } else {
      self.reallyInstall();
    }
  });
};

/**
 * Really install the component.
 *
 * @api public
 */

Package.prototype.reallyInstall = function(){
  var self = this;
  var i = 0;
  var batch;
  var last;

  next();

  function next() {
    self.remote = self.remotes[i++];
    if (!self.remote) return self.emit('error', new Error('can\'t find remote for "' + self.name + '"'));

    // parse remote
    last = i == self.remotes.length;
    self.remote = url.parse(self.remote);

    // strip trailing /
    self.remote.href = self.remote.href.slice(0, -1);

    // only error on the last remote otherwise
    // we assume it may be fetchable
    self.once('error', next);

    // kick off installation
    batch = new Batch;
    self.getJSON(function(err, json){
      if (err) {
        err.fatal = 404 != err.status || last;
        return self.emit('error', err);
      }

      var files = [];
      if (json.scripts) files = files.concat(json.scripts);
      if (json.styles) files = files.concat(json.styles);
      if (json.templates) files = files.concat(json.templates);
      if (json.files) files = files.concat(json.files);
      if (json.images) files = files.concat(json.images);
      if (json.fonts) files = files.concat(json.fonts);
      json.repo = json.repo || self.remote.href + '/' + self.name;

      if (json.dependencies) {
        batch.push(function(done){
          self.getDependencies(json.dependencies, done);
        });
      }

      batch.push(function(done){
        self.mkdir(self.dirname(), function(err){
          json = JSON.stringify(json, null, 2);
          self.writeFile('component.json', json, done);
        });
      });

      batch.push(function(done){
        self.mkdir(self.dirname(), function(err){
          self.getFiles(files, done);
        });
      });

      batch.end(function(err){
        if (err) {
          err.fatal = last;
          return self.emit('error', err);
        }
        self.emit('end');
      });
    });
  }
};

/**
 * Return an error for `res` / `url`.
 *
 * @param {Response} res
 * @param {String} url
 * @return {Error}
 * @api private
 */

function error(res, url) {
  var name = http.STATUS_CODES[res.status];
  var err = new Error('failed to fetch ' + url + ', got ' + res.status + ' "' + name + '"');
  err.status = res.status;
  return err;
}
<|endoftext|>


<commit_before>'use strict';

var util = require('./util');
var Stack = require('./Stack');

exports = module.exports = Runtime;

function Runtime(mixin){
  if(!(this instanceof Runtime)){
    return new Runtime(mixin);
  }

  util.merge(this, util.type(mixin).plainObject);
};

//
//
//
Runtime.prototype.Stack = Stack;

//
//
//
Runtime.prototype.onHandle = function(){ };
Runtime.prototype.onHandleError = function(err){ throw err; };

//
//
//
Runtime.prototype.compose = function(_handle, _stack){
  var fns, self = this;
  if(_handle === tick){ tick(_stack.pop(), _stack); } else {
    fns = arguments;
    tick.stack = new self.Stack();
    return tick;
  }

  function tick(handle, stack){
    if(fns){
      stack = new self.Stack(fns, true); stack.host = 0;
      if(handle === tick){ stack.host = stack; stack.callback = next; }
      stack.args = stack.host ? stack.host.args.concat() : util.slice(arguments);
      stack.callback = typeof stack.args[stack.args.length-1] === 'function' && stack.args.pop() || next;
      return self.compose(tick, stack);
    }

    var result;
    next.wait = stack.wait;
    function next(err){
      if(err){ self.onHandleError(err, handle, stack, next); }
      if(next.wait && arguments.length > 1){
        stack.args.push.apply(stack.args, util.slice(arguments, 1));
      }

      self.onHandle(handle, stack, next);

      if(stack.length){
        tick(stack.pop(), stack);
      } else if(stack.host.length){
        tick(stack.host.pop(), stack.host);
      } else if(stack.callback){
        stack.callback.apply(self, stack.args);
      }

      return result;
    }

    var args = handle.stack instanceof Stack
      ? [handle, stack, next]
      : stack.args.concat(next);

    util.asyncDone(function(){
      self.onHandle(handle, stack, next);
      result = handle.apply(stack, args);
      if(next.wait) { return result; }
      if(stack.length){ tick(stack.pop(), stack); }
      if(stack.host.length){ tick(stack.host.pop(), stack.host); }
      return result;
    }, next);
  }
};
<commit_msg>dev: introduce a composer function to simplify the first run<commit_after>'use strict';

var util = require('./util');
var Stack = require('./Stack');

exports = module.exports = Runtime;

function Runtime(mixin){
  if(!(this instanceof Runtime)){
    return new Runtime(mixin);
  }

  util.merge(this, util.type(mixin).plainObject);
};

//
//
//
Runtime.prototype.Stack = Stack;

//
//
//
Runtime.prototype.onHandle = function(){ };
Runtime.prototype.onHandleError = function(err){ throw err; };

//
//
//
Runtime.prototype.compose = function(_handle, _stack){
  var fns = arguments;
  var self = this;

  // setup the stack
  composer.stack = new self.Stack();
  function composer(handle, stack, next){
    var stack = new self.Stack(fns); stack.host = 0;
    if(handle === composer){ stack.host = stack; }
    stack.args = stack.host ? stack.host.args.concat() : util.slice(arguments);
    stack.callback = typeof stack.args[stack.args.length-1] === 'function' && stack.args.pop();
    return tick(stack.pop(), stack);
  }

  // run the each callsite
  function tick(handle, stack){
    var result;

    next.wait = stack.wait;
    function next(err){
      if(err){ self.onHandleError(err, handle, next); }
      if(next.wait && arguments.length > 1){
        stack.args.push.apply(stack.args, util.slice(arguments, 1));
      }

      self.onHandle(handle, next);
      if(stack.length){ tick(stack.pop(), stack); }
      else if(stack.host.length){ tick(stack.host.pop(), stack.host); }
      else if(stack.callback){ stack.callback.apply(self, stack.args); }

      return result;
    }

    var args = handle.stack instanceof Stack
      ? [handle, stack, next]
      : stack.args.concat(next);

    util.asyncDone(function(){
      self.onHandle(handle, stack, next);
      result = handle.apply(self, args);
      if(next.wait){ return result; }
      if(stack.length){ tick(stack.pop(), stack); }
      if(stack.host.length){ tick(stack.host.pop(), stack.host); }
      return result;
    }, next);
  }

  return composer;
};
<|endoftext|>