<commit_before>var uglify = require('uglify-js'), sqwish = require('sqwish'), underscore = require('underscore');
var crypto = require('crypto'), fs = require('fs'), util = require('util');

var globalSettings = {
	active : true,
	sqwishStrict : true,
	inmemory : false,
	path : {
		css : './public/css',
		js : './public/js'
	},
	location : {
		css : '/css',
		js : '/js'
	},
	contentType : {
		css : 'text/css; charset=utf-8',
		js : 'text/javascript; charset=utf-8'
	},
	maxAge : {
		css : 3600,
		js : 3600
	}
};

var cssManager, jsManager, StaticHandler = {};

/* global configs */
StaticHandler.globalSettings = function(configs) {
	globalSettings = underscore.extend(globalSettings, configs);
	return this;
};
/**
 * if not calling with block this one will be use
 *
 */
StaticHandler.GLOBAL = '*';

StaticHandler.addTemplateHelpers = function(app) {
	var jsManager = this.getJsManager();
	var cssManager = this.getCssManager();

	/* template functions */
	app.dynamicHelpers({
		renderScriptTags : function(req, res) {
			return function(block) {
				var blocks, html = '';
				if(block) {
					return jsHandler.renderTags(block);
				}
				blocks = jsManager.getLocationMap(req.route.path);
				blocks.forEach(function(block) {
					html += jsManager.renderTags(block);
				});
				return html;
			}
		},
		renderStyleTags : function(req, res) {
			return function(block) {
				var blocks, html = '';
				if(block) {
					return cssManager.renderTags(block);
				}
				blocks = cssManager.getLocationMap(req.route.path);
				blocks.forEach(function(block) {
					html += cssManager.renderTags(block);
				});
				return html;
			}
		},
		addBottomScript : function(req, res) {
			/* possibly call once when the request is made but for sure */
			if(!res.local("bottomScripts")) {
				res.local("bottomScripts", []);
			}
			return function(script) {
				res.local("bottomScripts").push(script);
			};
		},
		/* bottomScripts is accessible in template; provide this for reset value since it is reasonable */
		renderBottomScripts : function(req, res) {
			return function() {
				var scripts = res.local("bottomScripts") || [];
				res.local("bottomScripts", []);
				return scripts.join("");
			};
		}
	});
};
/**
 *
 *
 * @param app
 * 	Server
 * @param config
 * Object
 *
 */
StaticHandler.boostrap = function(app, config) {
	var jsManager, cssManager;
	config = underscore.extend(globalSettings, config);

	jsManager = this.getJsManager(config);
	cssManager = this.getCssManager(config);
	this.addTemplateHelpers(app);

	app.get(new RegExp(cssManager.location + '.+.css'), handleCssRequest);
	app.get(new RegExp(jsManager.location + '.+.js'), handleJsRequest);
};

/**
 * Alternative usage
 *
 * need to export template functions separately
 *
 */
StaticHandler.jcash = function(globalConfigs) {
	var jsManager, cssManager;
	this.globalSettings(globalConfigs);

	return function(req, res, next) {
		/* can use mime for lookup */
		handleCssRequest(req, res, function() {
			handleJsRequest(req, res, next);
		})
	};
};

StaticHandler.getCssManager = function(config) {
	if(!cssManager) {
		config = underscore.extend(globalSettings, config);
		cssManager = new Manager(config.path.css, config.location.css, 'css', config.active, config.inmemory, config.sqwishStrict);
	}
	return cssManager;
};

StaticHandler.getJsManager = function(config) {
	if(!jsManager) {
		config = underscore.extend(globalSettings, config);
		jsManager = new Manager(config.path.js, config.location.js, 'js', config.active, config.inmemory, config.sqwishStrict);
	}
	return jsManager;
};

module.exports = StaticHandler;

var handleCssRequest = function(req, res, next) {
	var cssManager = StaticHandler.getCssManager();
	try {
		if(cssManager.hasContent(req.url)) {
			res.setHeader('Content-Type', globalSettings.contentType.css);
			res.setHeader('Cache-Control', 'public, max-age=' + globalSettings.maxAge.css);
			res.setHeader('Date', new Date());
			res.setHeader('Expires', new Date(Date.now() + globalSettings.maxAge.css));
			res.send(cssManager.getContent(req.url));
			return;
		}
	} catch(e) {

	}
	next();
};

var handleJsRequest = function(req, res, next) {
	var jsManager = StaticHandler.getJsManager();
	try {
		if(jsManager.hasContent(req.url)) {
			res.setHeader('Content-Type', globalSettings.contentType.js);
			res.setHeader('Cache-Control', 'public, max-age=' + globalSettings.maxAge.js);
			res.setHeader('Date', new Date());
			res.setHeader('Expires', new Date(Date.now() + globalSettings.maxAge.js));
			res.send(jsManager.getContent(req.url));
			return;
		}
	} catch(e) {
	}
	next();
};

var md5 = function(str) {
	return crypto.createHash('md5').update(str).digest('hex');
};

/**
 *
 * @param path
 * 	the path to static files, ie /home/..../css
 * @param location
 * 	the path where static files serving, ie /css
 * @param type
 * 	type of static files, ie css or js
 * @param active
 *
 * @inmemory
 *
 */
var Manager = function(path, location, type, active, inmemory, sqwishStrict) {
	this.path = path;
	this.location = location;
	this.type = type;
	this.files = {};
	this.urls = {};
	this.cache = {};
	this.cacheMap = {};
	this.active = active;
	this.sqwishStrict = sqwishStrict;
	this.inmemory = inmemory;
	this.locationMap = {};
};

Manager.prototype = {
	parseConfig : function(config) {
		var block, location;

		if(!config)
			return;

		if(config.hasOwnProperty('files')) {
			for(block in config.files) {
				this.addFiles(block, config.files[block]);
			}
		}

		if(config.hasOwnProperty('urls')) {
			for(block in config.urls) {
				this.addUrls(block, config.urls[block]);
			}
		}

		if(config.hasOwnProperty('locationMap')) {
			for(location in config.locationMap) {
				this.addLocationMap(location, config.locationMap[location]);
			}
		}
	},
	addLocationMap : function(location, blocks) {
		if(!util.isArray(blocks)) {
			blocks = [blocks];
		}
		this.locationMap[location] = blocks;
	},
	getLocationMap : function(location) {
		var blocks = [];

		if(this.locationMap.hasOwnProperty(StaticHandler.GLOBAL)) {
			blocks = this.locationMap[StaticHandler.GLOBAL];
		}

		if(this.locationMap.hasOwnProperty(location)) {
			blocks = blocks.concat(this.locationMap[location]);
		}

		return blocks;
	},
	addFiles : function(block, filenames) {
		var self = this;

		if(!this.files.hasOwnProperty(block)) {
			this.files[block] = [];
		}
		len = this.files[block].length;

		if(!util.isArray(filenames)) {
			filenames = [filenames];
		}

		filenames.forEach(function(filename) {
			var i, len = self.files[block].length;
			for( i = 0; i < len; i++) {
				if(self.files[block][i] == filename) {
					/* entry exists */
					return;
				}
			};
			self.files[block].push(filename);
		});
		return this;
	},
	addUrls : function(block, urls) {
		var self = this;

		if(!this.urls.hasOwnProperty(block)) {
			this.urls[block] = [];
		}

		if(!util.isArray(urls)) {
			urls = [urls];
		}

		urls.forEach(function(url) {
			var i, len = self.urls[block].length;
			for( i = 0; i < len; i++) {
				if(self.urls[block][i] == url) {
					return;
				}
			};
			self.urls[block].push(url);
		});
		return this;
	},
	getUrls : function(block) {
		var files = [], urls = [], returnFiles = [], location = this.location;

		if(this.files.hasOwnProperty(block)) {
			files = this.files[block];
		}

		if(this.urls.hasOwnProperty(block)) {
			urls = this.urls[block];
		}

		if(this.active) {
			return urls.concat(this._run(files));
		} else {
			/* serve with location for instance /css */
			files.forEach(function(file) {
				returnFiles.push(location + '/' + file);
			});
			return urls.concat(returnFiles);
		}
	},
	hasContent : function(url) {
		console.log('sdfsdfdsfds');
		console.log(this.cacheMap.hasOwnProperty(url));
		return this.inmemory && this.cacheMap.hasOwnProperty(url);
	},
	getContent : function(url) {
		try {
			console.log('getContet');
			console.log(this.cache);
			return this.cache[this.cacheMap[url]][1] || "";
		} catch(e) {
			console.log(e);
			return "";
		}
	},
	_getCacheKey : function(files) {
		return files.join(';');
	},
	_getCache : function(files) {
		return this.cache[this._getCacheKey(files)];
	},
	_setAndCache : function(files) {
		var funk = require('funk')('parallel'), path = this.path + '/', type = this.type, manager = this, location = this.location;

		files.forEach(function(el) {
			fs.readFile(path + el, 'utf8', funk.result(el));
		});

		funk.run(function() {
			var self = this, minified, bundled = '', md5dist = '', distPath = 'dist', cacheKey;

			files.forEach(function(el) {
				if(type == 'css') {
					bundled += self[el];
				} else {
					bundled += self[el] + ';';
				}
			});
			if(type === 'css') {
				minified = sqwish.minify(bundled, self.sqwishStrict);
			} else {( function() {
					var jsp = uglify.parser, pro = uglify.uglify, ast = jsp.parse(bundled);
					ast = pro.ast_mangle(ast);
					ast = pro.ast_squeeze(ast);
					minified = pro.gen_code(ast);
				}());
			}
			md5dist = distPath + '/' + md5(minified) + '.' + type;
			md5Location = manager.location + '/' + md5dist;
			cacheKey = manager._getCacheKey(files);
			manager.cacheMap[md5Location] = cacheKey;
			// write to static file
			if(manager.inmemory) {
				manager.cache[cacheKey] = [manager.location + '/' + md5dist, minified];
			} else {
				manager.cache[cacheKey] = [manager.location + '/' + md5dist, null];
				fs.writeFile(path + md5dist, minified);
			}
		});
	},
	_run : function(files) {
		var cached = this._getCache(files), returnFiles, location = this.location;

		if(cached) {
			return [cached[0]];
		} else {
			if(files.length) {
				this._setAndCache(files);
			}
			returnFiles = [];
			files.forEach(function(file) {
				returnFiles.push(location + '/' + file);
			});
			return returnFiles;
		}
	},
	renderJsTag : function(url) {
		return '<script src="' + url + '" type="text/javascript"></script>';
	},
	renderCssTag : function(url) {
		return '<link href="' + url + '" rel="stylesheet" />';
	},
	renderTags : function(block) {
		var html = '', urls = this.getUrls(block), renderfunc, location = this.location;

		if(this.type == 'css') {
			renderfunc = this.renderCssTag;
		} else {
			renderfunc = this.renderJsTag;
		}

		urls.forEach(function(url) {
			html += renderfunc(url);
		});
		return html;
	},
	preRenderAll : function() {
		var block;
		for(block in this.files) {
			this.renderTags(block);
		}
	}
};
<commit_msg>Right, console.log!<commit_after>var uglify = require('uglify-js'), sqwish = require('sqwish'), underscore = require('underscore');
var crypto = require('crypto'), fs = require('fs'), util = require('util');

var globalSettings = {
	active : true,
	sqwishStrict : true,
	inmemory : false,
	path : {
		css : './public/css',
		js : './public/js'
	},
	location : {
		css : '/css',
		js : '/js'
	},
	contentType : {
		css : 'text/css; charset=utf-8',
		js : 'text/javascript; charset=utf-8'
	},
	maxAge : {
		css : 3600,
		js : 3600
	}
};

var cssManager, jsManager, StaticHandler = {};

/* global configs */
StaticHandler.globalSettings = function(configs) {
	globalSettings = underscore.extend(globalSettings, configs);
	return this;
};
/**
 * if not calling with block this one will be use
 *
 */
StaticHandler.GLOBAL = '*';

StaticHandler.addTemplateHelpers = function(app) {
	var jsManager = this.getJsManager();
	var cssManager = this.getCssManager();

	/* template functions */
	app.dynamicHelpers({
		renderScriptTags : function(req, res) {
			return function(block) {
				var blocks, html = '';
				if(block) {
					return jsHandler.renderTags(block);
				}
				blocks = jsManager.getLocationMap(req.route.path);
				blocks.forEach(function(block) {
					html += jsManager.renderTags(block);
				});
				return html;
			}
		},
		renderStyleTags : function(req, res) {
			return function(block) {
				var blocks, html = '';
				if(block) {
					return cssManager.renderTags(block);
				}
				blocks = cssManager.getLocationMap(req.route.path);
				blocks.forEach(function(block) {
					html += cssManager.renderTags(block);
				});
				return html;
			}
		},
		addBottomScript : function(req, res) {
			/* possibly call once when the request is made but for sure */
			if(!res.local("bottomScripts")) {
				res.local("bottomScripts", []);
			}
			return function(script) {
				res.local("bottomScripts").push(script);
			};
		},
		/* bottomScripts is accessible in template; provide this for reset value since it is reasonable */
		renderBottomScripts : function(req, res) {
			return function() {
				var scripts = res.local("bottomScripts") || [];
				res.local("bottomScripts", []);
				return scripts.join("");
			};
		}
	});
};
/**
 *
 *
 * @param app
 * 	Server
 * @param config
 * Object
 *
 */
StaticHandler.boostrap = function(app, config) {
	var jsManager, cssManager;
	config = underscore.extend(globalSettings, config);

	jsManager = this.getJsManager(config);
	cssManager = this.getCssManager(config);
	this.addTemplateHelpers(app);

	app.get(new RegExp(cssManager.location + '.+.css'), handleCssRequest);
	app.get(new RegExp(jsManager.location + '.+.js'), handleJsRequest);
};

/**
 * Alternative usage
 *
 * need to export template functions separately
 *
 */
StaticHandler.jcash = function(globalConfigs) {
	var jsManager, cssManager;
	this.globalSettings(globalConfigs);

	return function(req, res, next) {
		/* can use mime for lookup */
		handleCssRequest(req, res, function() {
			handleJsRequest(req, res, next);
		})
	};
};

StaticHandler.getCssManager = function(config) {
	if(!cssManager) {
		config = underscore.extend(globalSettings, config);
		cssManager = new Manager(config.path.css, config.location.css, 'css', config.active, config.inmemory, config.sqwishStrict);
	}
	return cssManager;
};

StaticHandler.getJsManager = function(config) {
	if(!jsManager) {
		config = underscore.extend(globalSettings, config);
		jsManager = new Manager(config.path.js, config.location.js, 'js', config.active, config.inmemory, config.sqwishStrict);
	}
	return jsManager;
};

module.exports = StaticHandler;

var handleCssRequest = function(req, res, next) {
	var cssManager = StaticHandler.getCssManager();
	try {
		if(cssManager.hasContent(req.url)) {
			res.setHeader('Content-Type', globalSettings.contentType.css);
			res.setHeader('Cache-Control', 'public, max-age=' + globalSettings.maxAge.css);
			res.setHeader('Date', new Date());
			res.setHeader('Expires', new Date(Date.now() + globalSettings.maxAge.css));
			res.send(cssManager.getContent(req.url));
			return;
		}
	} catch(e) {

	}
	next();
};

var handleJsRequest = function(req, res, next) {
	var jsManager = StaticHandler.getJsManager();
	try {
		if(jsManager.hasContent(req.url)) {
			res.setHeader('Content-Type', globalSettings.contentType.js);
			res.setHeader('Cache-Control', 'public, max-age=' + globalSettings.maxAge.js);
			res.setHeader('Date', new Date());
			res.setHeader('Expires', new Date(Date.now() + globalSettings.maxAge.js));
			res.send(jsManager.getContent(req.url));
			return;
		}
	} catch(e) {
	}
	next();
};

var md5 = function(str) {
	return crypto.createHash('md5').update(str).digest('hex');
};

/**
 *
 * @param path
 * 	the path to static files, ie /home/..../css
 * @param location
 * 	the path where static files serving, ie /css
 * @param type
 * 	type of static files, ie css or js
 * @param active
 *
 * @inmemory
 *
 */
var Manager = function(path, location, type, active, inmemory, sqwishStrict) {
	this.path = path;
	this.location = location;
	this.type = type;
	this.files = {};
	this.urls = {};
	this.cache = {};
	this.cacheMap = {};
	this.active = active;
	this.sqwishStrict = sqwishStrict;
	this.inmemory = inmemory;
	this.locationMap = {};
};

Manager.prototype = {
	parseConfig : function(config) {
		var block, location;

		if(!config)
			return;

		if(config.hasOwnProperty('files')) {
			for(block in config.files) {
				this.addFiles(block, config.files[block]);
			}
		}

		if(config.hasOwnProperty('urls')) {
			for(block in config.urls) {
				this.addUrls(block, config.urls[block]);
			}
		}

		if(config.hasOwnProperty('locationMap')) {
			for(location in config.locationMap) {
				this.addLocationMap(location, config.locationMap[location]);
			}
		}
	},
	addLocationMap : function(location, blocks) {
		if(!util.isArray(blocks)) {
			blocks = [blocks];
		}
		this.locationMap[location] = blocks;
	},
	getLocationMap : function(location) {
		var blocks = [];

		if(this.locationMap.hasOwnProperty(StaticHandler.GLOBAL)) {
			blocks = this.locationMap[StaticHandler.GLOBAL];
		}

		if(this.locationMap.hasOwnProperty(location)) {
			blocks = blocks.concat(this.locationMap[location]);
		}

		return blocks;
	},
	addFiles : function(block, filenames) {
		var self = this;

		if(!this.files.hasOwnProperty(block)) {
			this.files[block] = [];
		}
		len = this.files[block].length;

		if(!util.isArray(filenames)) {
			filenames = [filenames];
		}

		filenames.forEach(function(filename) {
			var i, len = self.files[block].length;
			for( i = 0; i < len; i++) {
				if(self.files[block][i] == filename) {
					/* entry exists */
					return;
				}
			};
			self.files[block].push(filename);
		});
		return this;
	},
	addUrls : function(block, urls) {
		var self = this;

		if(!this.urls.hasOwnProperty(block)) {
			this.urls[block] = [];
		}

		if(!util.isArray(urls)) {
			urls = [urls];
		}

		urls.forEach(function(url) {
			var i, len = self.urls[block].length;
			for( i = 0; i < len; i++) {
				if(self.urls[block][i] == url) {
					return;
				}
			};
			self.urls[block].push(url);
		});
		return this;
	},
	getUrls : function(block) {
		var files = [], urls = [], returnFiles = [], location = this.location;

		if(this.files.hasOwnProperty(block)) {
			files = this.files[block];
		}

		if(this.urls.hasOwnProperty(block)) {
			urls = this.urls[block];
		}

		if(this.active) {
			return urls.concat(this._run(files));
		} else {
			/* serve with location for instance /css */
			files.forEach(function(file) {
				returnFiles.push(location + '/' + file);
			});
			return urls.concat(returnFiles);
		}
	},
	hasContent : function(url) {
		return this.inmemory && this.cacheMap.hasOwnProperty(url);
	},
	getContent : function(url) {
		try {
			return this.cache[this.cacheMap[url]][1] || "";
		} catch(e) {
			return "";
		}
	},
	_getCacheKey : function(files) {
		return files.join(';');
	},
	_getCache : function(files) {
		return this.cache[this._getCacheKey(files)];
	},
	_setAndCache : function(files) {
		var funk = require('funk')('parallel'), path = this.path + '/', type = this.type, manager = this, location = this.location;

		files.forEach(function(el) {
			fs.readFile(path + el, 'utf8', funk.result(el));
		});

		funk.run(function() {
			var self = this, minified, bundled = '', md5dist = '', distPath = 'dist', cacheKey;

			files.forEach(function(el) {
				if(type == 'css') {
					bundled += self[el];
				} else {
					bundled += self[el] + ';';
				}
			});
			if(type === 'css') {
				minified = sqwish.minify(bundled, self.sqwishStrict);
			} else {( function() {
					var jsp = uglify.parser, pro = uglify.uglify, ast = jsp.parse(bundled);
					ast = pro.ast_mangle(ast);
					ast = pro.ast_squeeze(ast);
					minified = pro.gen_code(ast);
				}());
			}
			md5dist = distPath + '/' + md5(minified) + '.' + type;
			md5Location = manager.location + '/' + md5dist;
			cacheKey = manager._getCacheKey(files);
			manager.cacheMap[md5Location] = cacheKey;
			// write to static file
			if(manager.inmemory) {
				manager.cache[cacheKey] = [manager.location + '/' + md5dist, minified];
			} else {
				manager.cache[cacheKey] = [manager.location + '/' + md5dist, null];
				fs.writeFile(path + md5dist, minified);
			}
		});
	},
	_run : function(files) {
		var cached = this._getCache(files), returnFiles, location = this.location;

		if(cached) {
			return [cached[0]];
		} else {
			if(files.length) {
				this._setAndCache(files);
			}
			returnFiles = [];
			files.forEach(function(file) {
				returnFiles.push(location + '/' + file);
			});
			return returnFiles;
		}
	},
	renderJsTag : function(url) {
		return '<script src="' + url + '" type="text/javascript"></script>';
	},
	renderCssTag : function(url) {
		return '<link href="' + url + '" rel="stylesheet" />';
	},
	renderTags : function(block) {
		var html = '', urls = this.getUrls(block), renderfunc, location = this.location;

		if(this.type == 'css') {
			renderfunc = this.renderCssTag;
		} else {
			renderfunc = this.renderJsTag;
		}

		urls.forEach(function(url) {
			html += renderfunc(url);
		});
		return html;
	},
	preRenderAll : function() {
		var block;
		for(block in this.files) {
			this.renderTags(block);
		}
	}
};
<|endoftext|>


<commit_before>/*
 * status.js: Collects status information about CouchDB replication jobs.
 *
 * (C) 2013, Nodejitsu Inc.
 *
 */

var url = require('url'),
    async = require('async'),
    request = require('request');

//
// ### function status (options, callback)
// #### @options.uri || url {String}  CouchDB URL to get status from
// #### @options.filter     {Array}   Array of `_replicator` doc IDs to filter against
// #### @options.sanitize   {Boolean} Indiciates we should strip sensitive data.
//
// Returns all of the replication status about `options.uri || options.url`
// filtering to anything matching `options.filter`. If `options.sanitize`
// is set then no sensitive data (i.e. `.url`) is included in the status response.
//
module.exports = function (options, callback) {
  var uri = (options.url || options.uri).replace(/\/$/, '');
  if (!uri) {
    return callback(new Error('options.uri or options.url is required'));
  }

  //
  // Helper function for making the request with
  // the default parameters
  //
  function askCouch(to, qs, next) {
    if (!next && typeof qs === 'function') {
      next = qs;
      qs = null;
    }

    return request({
      uri:       to,
      qs:        qs,
      strictSSL: false,
      json:      true
    }, function (err, res, body) {
      return !err
        ? next(null, body)
        : next(err);
    });
  }

  //
  // Helper function which either queries the
  // remote database or attempts to make the check
  // "locally" against `options.url`.
  //
  function getDbInfo(name, next) {
    var parsed = url.parse(name),
        dbUri  = !parsed.protocol
          ? uri + '/' + name
          : name;

    askCouch(dbUri, function (err, db) {
      if (err) { return next(err) }

      db.name = parsed.path;
      if (parsed.protocol) {
        if (!options.sanitize) {
          db.uri = name;
        }

        db.name = db.name.slice(1);
      }

      next(null, db);
    });
  }

  //
  // Helper function to build the replicator
  // status entry for the given task and replicator
  // doc.
  //
  function buildStatus(task, doc, next) {
    async.parallel({
      source: async.apply(getDbInfo, doc.source),
      target: async.apply(getDbInfo, doc.target)
    }, function (err, dbs) {
      if (err) { return next(err) }

      //
      // Normalize time(s) on the source and target db information
      //
      ['source', 'target'].forEach(function (key) {
        if (dbs[key] && dbs[key].instance_start_time) {
          dbs[key].instance_start_time = '' + (dbs[key].instance_start_time / 1000).toFixed(0);
        }
      });

      next(null, {
        id: task.doc_id,
        continuous: task.continuous,
        started_on: '' + (task.started_on * 1000),
        updated_on: '' + (task.updated_on * 1000),

        state:      doc._replication_state,
        state_time: '' + (+new Date(doc._replication_state_time)),

        checkpointed_source_seq: task.checkpointed_source_seq,

        source: dbs.source,
        target: dbs.target,

        remaining: dbs.source.disk_size - dbs.target.disk_size,
        progress: {
          docs:       ((dbs.target.doc_count         / dbs.source.doc_count)  * 100).toFixed(2),
          disk_size:  ((dbs.target.disk_size         / dbs.source.disk_size)  * 100).toFixed(2),
          data_size:  ((dbs.target.data_size         / dbs.source.data_size)  * 100).toFixed(2),
          update_seq: ((task.checkpointed_source_seq / dbs.source.update_seq) * 100).toFixed(2)
        }
      });
    });
  }

  async.parallel({
    //
    // * Get everything at `/_replicator/_all_docs?include_docs=true`
    //
    docs: async.apply(
      askCouch,
      uri + '/_replicator/_all_docs',
      { include_docs: true }
    ),
    //
    // * Get everything at `/_active_tasks`
    //
    tasks: async.apply(
      askCouch,
      uri + '/_active_tasks'
    )
  }, function (err, results) {
    if (err) {
      return callback(err);
    }

    //
    // If there are resulting replicator documents
    // then filter and reduce them into an Object.
    //
    if (results.docs && Array.isArray(results.docs.rows)) {
      results.docs = results.docs.rows
        .map(function (row) {
          return row.doc;
        })
        .filter(function (doc) {
          return !/^_design/.test(doc._id);
        })
        .reduce(function (all, doc) {
          all[doc._id] = doc;
          return all;
        }, {});
    }

    //
    // Filter tasks to only replication tasks
    // and reduce them into an Object.
    //
    results.tasks = results.tasks
      .filter(function (task) {
        return task.type === 'replication';
      })
      .reduce(function (all, task) {
        all[task.doc_id] = task;
        return all;
      }, {});

    var keys = Object.keys(results.tasks),
        all  = {};

    //
    // Filter any known keys.
    //
    if (options.filter) {
      keys = keys.filter(function (key) {
        return ~options.filter.indexOf(key);
      });
    }

    async.forEachLimit(keys, 5, function (key, next) {
      buildStatus(results.tasks[key], results.docs[key], function (err, stat) {
        if (err) { return next(err) }
        all[key] = stat;
        next();
      });
    }, function (err) {
      return err ? callback(err) : callback(null, all);
    });
  });
};<commit_msg>[fix api] Added basic retry logic defaulting to 3 retries.<commit_after>/*
 * status.js: Collects status information about CouchDB replication jobs.
 *
 * (C) 2013, Nodejitsu Inc.
 *
 */

var url = require('url'),
    async = require('async'),
    request = require('request');

//
// ### function status (options, callback)
// #### @options.uri || url {String}  CouchDB URL to get status from.
// #### @options.filter     {Array}   Array of `_replicator` doc IDs to filter against.
// #### @options.sanitize   {Boolean} Indiciates we should strip sensitive data.
// #### @options.maxTries   {Number}  Maximum number of retries.
//
// Returns all of the replication status about `options.uri || options.url`
// filtering to anything matching `options.filter`. If `options.sanitize`
// is set then no sensitive data (i.e. `.url`) is included in the status response.
//
module.exports = function (options, callback) {
  var uri      = (options.url || options.uri).replace(/\/$/, ''),
      maxTries = options.maxTries || 3;

  if (!uri) {
    return callback(new Error('options.uri or options.url is required'));
  }

  //
  // Helper function for making the request with
  // the default parameters
  //
  function askCouch(to, qs, next) {
    if (!next && typeof qs === 'function') {
      next = qs;
      qs = null;
    }

    return request({
      uri:       to,
      qs:        qs,
      strictSSL: false,
      json:      true
    }, function (err, res, body) {
      return !err
        ? next(null, body)
        : next(err);
    });
  }

  //
  // Helper function which either queries the
  // remote database or attempts to make the check
  // "locally" against `options.url`.
  //
  function getDbInfo(name, next) {
    var parsed = url.parse(name),
        dbUri  = !parsed.protocol
          ? uri + '/' + name
          : name;

    askCouch(dbUri, function (err, db) {
      if (err) { return next(err) }

      db.name = parsed.path;
      if (parsed.protocol) {
        if (!options.sanitize) {
          db.uri = name;
        }

        db.name = db.name.slice(1);
      }

      next(null, db);
    });
  }

  //
  // Helper function to build the replicator
  // status entry for the given task and replicator
  // doc.
  //
  function buildStatus(task, doc, next) {
    async.parallel({
      source: async.apply(getDbInfo, doc.source),
      target: async.apply(getDbInfo, doc.target)
    }, function (err, dbs) {
      if (err) { return next(err) }

      //
      // Normalize time(s) on the source and target db information
      //
      ['source', 'target'].forEach(function (key) {
        if (dbs[key] && dbs[key].instance_start_time) {
          dbs[key].instance_start_time = '' + (dbs[key].instance_start_time / 1000).toFixed(0);
        }
      });

      next(null, {
        id: task.doc_id,
        continuous: task.continuous,
        started_on: '' + (task.started_on * 1000),
        updated_on: '' + (task.updated_on * 1000),

        state:      doc._replication_state,
        state_time: '' + (+new Date(doc._replication_state_time)),

        checkpointed_source_seq: task.checkpointed_source_seq,

        source: dbs.source,
        target: dbs.target,

        remaining: dbs.source.disk_size - dbs.target.disk_size,
        progress: {
          docs:       ((dbs.target.doc_count         / dbs.source.doc_count)  * 100).toFixed(2),
          disk_size:  ((dbs.target.disk_size         / dbs.source.disk_size)  * 100).toFixed(2),
          data_size:  ((dbs.target.data_size         / dbs.source.data_size)  * 100).toFixed(2),
          update_seq: ((task.checkpointed_source_seq / dbs.source.update_seq) * 100).toFixed(2)
        }
      });
    });
  }

  //
  // Setup `options.attempt` if it does not exist.
  //
  options.attempt = options.attempt || 0;
  async.parallel({
    //
    // * Get everything at `/_replicator/_all_docs?include_docs=true`
    //
    docs: async.apply(
      askCouch,
      uri + '/_replicator/_all_docs',
      { include_docs: true }
    ),
    //
    // * Get everything at `/_active_tasks`
    //
    tasks: async.apply(
      askCouch,
      uri + '/_active_tasks'
    )
  }, function (err, results) {
    if (err) {
      return callback(err);
    }
    else if (!Array.isArray(results.tasks)) {
      //
      // _active_tasks will ALWAYS be an Array, even an empty one
      // when nothing is running.
      //
      return ++(options.attempt) < maxTries
        ? setTimeout(module.exports.bind(null, options, callback), 10 * 1000)
        : callback(new Error('No active tasks returned from CouchDB'));
    }

    //
    // If there are resulting replicator documents
    // then filter and reduce them into an Object.
    //
    if (results.docs && Array.isArray(results.docs.rows)) {
      results.docs = results.docs.rows
        .map(function (row) {
          return row.doc;
        })
        .filter(function (doc) {
          return !/^_design/.test(doc._id);
        })
        .reduce(function (all, doc) {
          all[doc._id] = doc;
          return all;
        }, {});
    }

    //
    // Filter tasks to only replication tasks
    // and reduce them into an Object.
    //
    results.tasks = results.tasks
      .filter(function (task) {
        return task.type === 'replication';
      })
      .reduce(function (all, task) {
        all[task.doc_id] = task;
        return all;
      }, {});

    var keys = Object.keys(results.tasks),
        all  = {};

    //
    // Filter any known keys.
    //
    if (options.filter) {
      keys = keys.filter(function (key) {
        return ~options.filter.indexOf(key);
      });
    }

    async.forEachLimit(keys, 5, function (key, next) {
      buildStatus(results.tasks[key], results.docs[key], function (err, stat) {
        if (err) { return next(err) }
        all[key] = stat;
        next();
      });
    }, function (err) {
      return err ? callback(err) : callback(null, all);
    });
  });
};<|endoftext|>


<commit_before>const fs = require('fs')
const pathUtils = require('path')
const keywordsRepo = require('./keywords')
const libManager = require('./library-manager')
const common = require('./common')
const robotParser = require('./parse-robot')

function getResourceInfo(resourcePath){
  const info = {status: 'n/a', resourceName: 'n/a', resourceFileName: 'n/a', resourcePath: 'n/a', libraries: [], resources: []}
  try{
    if(!resourcePath){
      info.status = `Error occurred while gathering status: current editor path could not be determined`
    } else{
      const resourceKey = common.getResourceKey(resourcePath)
      const currentResource = keywordsRepo.resourcesMap[resourceKey]
      info.resourcePath = resourcePath
      info.resourceFileName = pathUtils.basename(resourcePath)
      if(!currentResource){
        info.resourcePath = resourcePath
        info.resourceName = 'n/a'
        const fileContent = fs.readFileSync(resourcePath).toString()
        const isRobotFile = robotParser.isRobot(fileContent)
        if(isRobotFile){
          info.status = `Resource is not indexed. Make sure it is part of current Atom projects.`
        } else{
          info.status = `Resource is not a valid robot file.`
        }
      } else{
        info.status = 'Parsed/Ok'
        info.resourcePath = resourcePath
        info.resourceName = currentResource.name

        // Imported libraries
        let libraryNames = currentResource.imports.libraries.map(lib => lib.name)
        libraryNames = new Set(['BuiltIn', ...libraryNames])
        for(const libraryName of libraryNames){
          const libInfo = {name: libraryName, status: 'n/a', sourcePath: 'n/a', libdocPath: 'n/a'}
          const managedLibrary = libManager.getLibrariesByName().get(libraryName)
          if(managedLibrary){
            if(managedLibrary.fallback){
              libInfo.status = 'Loaded'
              libInfo.error = `Falled back on internal library since python import failed: ${managedLibrary.message}`
            } else{
              libInfo.status = managedLibrary.status==='success'?'Loaded':'Errors occurred during loading'
              libInfo.error = managedLibrary.message
            }
            libInfo.sourcePath = managedLibrary.sourcePath || libInfo.sourcePath
            libInfo.libdocPath = managedLibrary.xmlLibdocPath || libInfo.libdocPath
          } else{
            libInfo.status =  'Library is in process of being imported'
          }
          info.libraries.push(libInfo)
        }

        // Imported resources
        for(const importedResource of currentResource.imports.resources){
          const resourceName = importedResource.name
          const resourceExtension = importedResource.extension
          const resInfo = {name: `${resourceName}${resourceExtension}`, status: 'n/a', multiple: false, sourcePath: 'n/a', sourcePaths: []}
          let resourcesByName = keywordsRepo.getResourcesByName(true).get(resourceName.toLowerCase()) || []
          resourcesByName = resourcesByName.filter(res => res.extension.toLowerCase() === resourceExtension.toLowerCase())
          if(!resourcesByName){
            resInfo.status =  'Resource could not be found'
          } else if(resourcesByName.length === 1){
            const resource = resourcesByName[0]
            resInfo.status = 'Loaded'
            resInfo.sourcePath = resource.path
          } else{
            resInfo.status = 'Multiple resources detected'
            resInfo.multiple = true
            resInfo.sourcePaths = resourcesByName.map(res => res.path)
          }
          info.resources.push(resInfo)
        }
      }
    }
  }catch(error){
    info.status = 'Error occurred while gathering status'
    info.error =  error.stack ? error.stack : error
  }

  return info
}

function tab(text, tabsNo){
  const tabs = Array(tabsNo).fill('    ').join('')
  const output = text.split('\n').map(line => `${tabs}${line}`)
  return output.join('\n')
}
function trim(text){
  const output = text.split('\n').map(line => `${line.trim()}`).filter(line => line.length>0)
  return output.join('\n')
}

function oneLine(text){
  const output = text.split('\n').map(line => `${line.trim()}`).filter(line => line.length>0)
  return output.join(' ')
}

function buildImportedLibrariesOutput(libraries, tabs){
  const output = []
  output.push(tab('Libraries', tabs))
  if(libraries.length==0){
    output.push(tab('No libraries imported', tabs+1))
  }
  for(const library of libraries){
    output.push(tab(library.name, tabs+1))
    output.push(tab(`Status: ${library.status}`, tabs+2))
    if(library.error){
      output.push(tab('Error:', tabs+2))
      output.push(tab(trim(library.error), tabs+3))
    }
    output.push(tab(`Source: ${library.sourcePath}`, tabs+2))
    output.push(tab(`Libdoc: ${library.libdocPath}`, tabs+2))
  }
  return output
}

function buildImportedResourcesOutput(resources, tabs){
  const output = []
  output.push(tab('Resources', tabs))
  if(resources.length==0){
    output.push(tab('No resources imported', tabs+1))
  }
  for(const resource of resources){
    output.push(tab(resource.name, tabs+1))
    output.push(tab(`Status: ${resource.status}`, tabs+2))
    if(resource.error){
      output.push(tab('Error:', tabs+2))
      output.push(tab(trim(resource.error), tabs+3))
    }
    if(resource.multiple){
      output.push(tab(`Paths:`, tabs+2))
      for(const path of resource.sourcePaths){
        output.push(tab(path, tabs+3))
      }
    } else{
      output.push(tab(`Path: ${resource.sourcePath}`, tabs+2))
    }
  }
  return output
}

function buildOutput(resourceInfo, environmentInfo){
  const output = []
  output.push(`File: ${resourceInfo.resourceFileName}`)
  output.push(tab(`Status: ${resourceInfo.status}`, 1))

  // Current file
  if(resourceInfo.error){
      output.push(tab('Error:', 1))
      output.push(tab(trim(resourceInfo.error), 2))
  }
  output.push(tab(`Path: ${resourceInfo.resourcePath}`, 1))
  output.push(...buildImportedLibrariesOutput(resourceInfo.libraries, 1))
  output.push(...buildImportedResourcesOutput(resourceInfo.resources, 1))

  // Environment
  output.push(`Environment:`)
  output.push(tab(`Python executable: ${environmentInfo.pythonExecutable}`, 1))
  if(environmentInfo.message){
      output.push(tab('Error:', 1))
      output.push(tab(trim(environmentInfo.message), 2))
  }
  output.push(tab(`Python version: ${oneLine(environmentInfo.pythonVersion)}`, 1))
  output.push(tab(`Platform: ${oneLine(environmentInfo.platform)}`, 1))
  output.push(tab(`PYTHONPATH: ${environmentInfo.pythonPath}`, 1))
  output.push(tab(`JYTHONPATH: ${environmentInfo.jythonPath}`, 1))
  output.push(tab(`CLASSPATH: ${environmentInfo.classPath}`, 1))
  output.push(tab(`IRONPYTHONPATH: ${environmentInfo.ironpythonPath}`, 1))
  if(environmentInfo.moduleSearchPath.length==0){
    output.push(tab(`Module search path: n/a`, 1))
  } else{
    output.push(tab(`Module search path:`, 1))
    environmentInfo.moduleSearchPath.filter(p => p.trim()!=='').forEach(p => output.push(tab(p, 2)))
  }

  return output.join('\n')
}

module.exports = {
  // Opens a new Atom panel and prints status information.
  renderStatus(resourcePath, settings){
    const resourceInfo = getResourceInfo(resourcePath)
    const markdownUri = 'arfm://arfm'
    const view = document.createElement("pre")
    view.getTitle = () => 'Robot'
    view.getURI= () => markdownUri
    view.getIconName= () => 'info'
    view.classList.add('native-key-bindings')
    view.setAttribute('tabindex', -1)
    view.innerHTML = buildOutput(resourceInfo, libManager.getPythonEnvironmentStatus())

    const disposable = atom.workspace.addOpener((uri) => uri === markdownUri ? view : undefined)
    atom.workspace.open(markdownUri)
    disposable.dispose()
  }
}
<commit_msg>Fix status exception when empty library is found - 'Library  '<commit_after>const fs = require('fs')
const pathUtils = require('path')
const keywordsRepo = require('./keywords')
const libManager = require('./library-manager')
const common = require('./common')
const robotParser = require('./parse-robot')

function getResourceInfo(resourcePath){
  const info = {status: 'n/a', resourceName: 'n/a', resourceFileName: 'n/a', resourcePath: 'n/a', libraries: [], resources: []}
  try{
    if(!resourcePath){
      info.status = `Error occurred while gathering status: current editor path could not be determined`
    } else{
      const resourceKey = common.getResourceKey(resourcePath)
      const currentResource = keywordsRepo.resourcesMap[resourceKey]
      info.resourcePath = resourcePath
      info.resourceFileName = pathUtils.basename(resourcePath)
      if(!currentResource){
        info.resourcePath = resourcePath
        info.resourceName = 'n/a'
        const fileContent = fs.readFileSync(resourcePath).toString()
        const isRobotFile = robotParser.isRobot(fileContent)
        if(isRobotFile){
          info.status = `Resource is not indexed. Make sure it is part of current Atom projects.`
        } else{
          info.status = `Resource is not a valid robot file.`
        }
      } else{
        info.status = 'Parsed/Ok'
        info.resourcePath = resourcePath
        info.resourceName = currentResource.name

        // Imported libraries
        let libraryNames = currentResource.imports.libraries.map(lib => lib.name)
        libraryNames = new Set(['BuiltIn', ...libraryNames])
        for(const libraryName of libraryNames){
          const libInfo = {name: libraryName, status: 'n/a', sourcePath: 'n/a', libdocPath: 'n/a'}
          const managedLibrary = libManager.getLibrariesByName().get(libraryName)
          if(managedLibrary){
            if(managedLibrary.fallback){
              libInfo.status = 'Loaded'
              libInfo.error = `Falled back on internal library since python import failed: ${managedLibrary.message}`
            } else{
              libInfo.status = managedLibrary.status==='success'?'Loaded':'Errors occurred during loading'
              libInfo.error = managedLibrary.message
            }
            libInfo.sourcePath = managedLibrary.sourcePath || libInfo.sourcePath
            libInfo.libdocPath = managedLibrary.xmlLibdocPath || libInfo.libdocPath
          } else{
            libInfo.status =  'Library is in process of being imported'
          }
          info.libraries.push(libInfo)
        }

        // Imported resources
        for(const importedResource of currentResource.imports.resources){
          const resourceName = importedResource.name
          const resourceExtension = importedResource.extension
          const resInfo = {name: `${resourceName}${resourceExtension}`, status: 'n/a', multiple: false, sourcePath: 'n/a', sourcePaths: []}
          let resourcesByName = keywordsRepo.getResourcesByName(true).get(resourceName.toLowerCase()) || []
          resourcesByName = resourcesByName.filter(res => res.extension.toLowerCase() === resourceExtension.toLowerCase())
          if(!resourcesByName){
            resInfo.status =  'Resource could not be found'
          } else if(resourcesByName.length === 1){
            const resource = resourcesByName[0]
            resInfo.status = 'Loaded'
            resInfo.sourcePath = resource.path
          } else{
            resInfo.status = 'Multiple resources detected'
            resInfo.multiple = true
            resInfo.sourcePaths = resourcesByName.map(res => res.path)
          }
          info.resources.push(resInfo)
        }
      }
    }
  }catch(error){
    info.status = 'Error occurred while gathering status'
    info.error =  error.stack ? error.stack : error
  }

  return info
}

function tab(text, tabsNo){
  const tabs = Array(tabsNo).fill('    ').join('')
  const output = text.split('\n').map(line => `${tabs}${line}`)
  return output.join('\n')
}
function trim(text){
  const output = text.split('\n').map(line => `${line.trim()}`).filter(line => line.length>0)
  return output.join('\n')
}

function oneLine(text){
  const output = text.split('\n').map(line => `${line.trim()}`).filter(line => line.length>0)
  return output.join(' ')
}

function buildImportedLibrariesOutput(libraries, tabs){
  const output = []
  output.push(tab('Libraries', tabs))
  if(libraries.length==0){
    output.push(tab('No libraries imported', tabs+1))
  }
  for(const library of libraries){
    if(!library.name) continue
    output.push(tab(library.name, tabs+1))
    output.push(tab(`Status: ${library.status}`, tabs+2))
    if(library.error){
      output.push(tab('Error:', tabs+2))
      output.push(tab(trim(library.error), tabs+3))
    }
    output.push(tab(`Source: ${library.sourcePath}`, tabs+2))
    output.push(tab(`Libdoc: ${library.libdocPath}`, tabs+2))
  }
  return output
}

function buildImportedResourcesOutput(resources, tabs){
  const output = []
  output.push(tab('Resources', tabs))
  if(resources.length==0){
    output.push(tab('No resources imported', tabs+1))
  }
  for(const resource of resources){
    output.push(tab(resource.name, tabs+1))
    output.push(tab(`Status: ${resource.status}`, tabs+2))
    if(resource.error){
      output.push(tab('Error:', tabs+2))
      output.push(tab(trim(resource.error), tabs+3))
    }
    if(resource.multiple){
      output.push(tab(`Paths:`, tabs+2))
      for(const path of resource.sourcePaths){
        output.push(tab(path, tabs+3))
      }
    } else{
      output.push(tab(`Path: ${resource.sourcePath}`, tabs+2))
    }
  }
  return output
}

function buildOutput(resourceInfo, environmentInfo){
  const output = []
  output.push(`File: ${resourceInfo.resourceFileName}`)
  output.push(tab(`Status: ${resourceInfo.status}`, 1))

  // Current file
  if(resourceInfo.error){
      output.push(tab('Error:', 1))
      output.push(tab(trim(resourceInfo.error), 2))
  }
  output.push(tab(`Path: ${resourceInfo.resourcePath}`, 1))
  output.push(...buildImportedLibrariesOutput(resourceInfo.libraries, 1))
  output.push(...buildImportedResourcesOutput(resourceInfo.resources, 1))

  // Environment
  output.push(`Environment:`)
  output.push(tab(`Python executable: ${environmentInfo.pythonExecutable}`, 1))
  if(environmentInfo.message){
      output.push(tab('Error:', 1))
      output.push(tab(trim(environmentInfo.message), 2))
  }
  output.push(tab(`Python version: ${oneLine(environmentInfo.pythonVersion)}`, 1))
  output.push(tab(`Platform: ${oneLine(environmentInfo.platform)}`, 1))
  output.push(tab(`PYTHONPATH: ${environmentInfo.pythonPath}`, 1))
  output.push(tab(`JYTHONPATH: ${environmentInfo.jythonPath}`, 1))
  output.push(tab(`CLASSPATH: ${environmentInfo.classPath}`, 1))
  output.push(tab(`IRONPYTHONPATH: ${environmentInfo.ironpythonPath}`, 1))
  if(environmentInfo.moduleSearchPath.length==0){
    output.push(tab(`Module search path: n/a`, 1))
  } else{
    output.push(tab(`Module search path:`, 1))
    environmentInfo.moduleSearchPath.filter(p => p.trim()!=='').forEach(p => output.push(tab(p, 2)))
  }

  return output.join('\n')
}

module.exports = {
  // Opens a new Atom panel and prints status information.
  renderStatus(resourcePath, settings){
    const resourceInfo = getResourceInfo(resourcePath)
    const markdownUri = 'arfm://arfm'
    const view = document.createElement("pre")
    view.getTitle = () => 'Robot'
    view.getURI= () => markdownUri
    view.getIconName= () => 'info'
    view.classList.add('native-key-bindings')
    view.setAttribute('tabindex', -1)
    view.innerHTML = buildOutput(resourceInfo, libManager.getPythonEnvironmentStatus())

    const disposable = atom.workspace.addOpener((uri) => uri === markdownUri ? view : undefined)
    atom.workspace.open(markdownUri)
    disposable.dispose()
  }
}
<|endoftext|>


<commit_before>var _ = require('lodash'),
    Promise = require('bluebird'),
    Readable = require('stream').Readable,
    util = require('util');

function MarketoStream(marketoResultPromise, options) {
  options = _.defaults(options || {}, {objectMode: true});
  Readable.call(this, options);
  this._setData(marketoResultPromise || Promise.resolve({}));
}
util.inherits(MarketoStream, Readable);

MarketoStream.prototype._setData = function(dataPromise) {
  this._ready = false;

  var self = this;
  this._dataPromise = dataPromise;
  return dataPromise.then(
    function(data) {
      if (data.result) {
        self._data = data;
        self._resultIndex = 0;
        self._ready = true;
        self._pushNext();
      } else {
        self.push(null);
      }
    },
    function(err) {
      self.emit('error', err);
      // end the stream
      self.push(null);
    });
};

MarketoStream.prototype._read = function() {

  if (!this._ready) {
    return;
  }

  if (this._data.result) {
    if (this._pushNext()) {
      return;
    } else if (this._data.moreResult) {
      this._setData(this._data.nextPage())
    } else {
      // No data left and no more data from marketo
      this.push(null);
    }
  } else {
    this.push(null);
  }
};

MarketoStream.prototype._pushNext = function() {
  var result;
  if (this._data.result) {
    result = this._data.result;
    if (this._resultIndex < result.length) {
      this.push(result[this._resultIndex++]);
      return true;
    }
  }

  return false;
};

MarketoStream.prototype.endMarketoStream = function() {
  this.push(null);
}

module.exports = MarketoStream;
<commit_msg>fixing the issue when the query returns no results, the stream was not starting/ending<commit_after>var _ = require('lodash'),
    Promise = require('bluebird'),
    Readable = require('stream').Readable,
    util = require('util');

function MarketoStream(marketoResultPromise, options) {
  options = _.defaults(options || {}, {objectMode: true});
  Readable.call(this, options);
  this._setData(marketoResultPromise || Promise.resolve({}));
}
util.inherits(MarketoStream, Readable);

MarketoStream.prototype._setData = function(dataPromise) {
  this._ready = false;

  var self = this;
  this._dataPromise = dataPromise;
  return dataPromise.then(
    function(data) {
      if (data.result.length > 0) {
        self._data = data;
        self._resultIndex = 0;
        self._ready = true;
        self._pushNext();
      } else {
        self.emit('error', 'Leads list is empty');
        // end the stream
        self.push(null);
      }
    },
    function(err) {
      self.emit('error', err);
      // end the stream
      self.push(null);
    });
};

MarketoStream.prototype._read = function() {

  if (!this._ready) {
    return;
  }

  if (this._data.result) {
    if (this._pushNext()) {
      return;
    } else if (this._data.moreResult) {
      this._setData(this._data.nextPage())
    } else {
      // No data left and no more data from marketo
      this.push(null);
    }
  } else {
    this.push(null);
  }
};

MarketoStream.prototype._pushNext = function() {
  var result;
  if (this._data.result) {
    result = this._data.result;
    if (this._resultIndex < result.length) {
      this.push(result[this._resultIndex++]);
      return true;
    }
  }

  return false;
};

MarketoStream.prototype.endMarketoStream = function() {
  this.push(null);
}

module.exports = MarketoStream;
<|endoftext|>


<commit_before>import React from "react";
import { Provider } from "react-redux";

import Player from "../src/screens/Player/Player";
import store from "../src/store/store";

import "../src/index.css";

export default function Application({ Component, pageProps }) {
  return (
    <Provider store={store}>
      <Component {...pageProps} />
      <Player />
    </Provider>
  );
}
<commit_msg>Add title to head<commit_after>import React from "react";
import { Provider } from "react-redux";
import Head from "next/head";

import Player from "../src/screens/Player/Player";
import store from "../src/store/store";

import "../src/index.css";

export default function Application({ Component, pageProps }) {
  return (
    <Provider store={store}>
      <Head>
        <title>Reddio</title>
      </Head>
      <Component {...pageProps} />
      <Player />
    </Provider>
  );
}
<|endoftext|>


<commit_before>
/*!
 * string
 * JavaScript string utilities library
 * Copyright (c) 2011 Enrico Marino <enrico.marino@email.com>
 * MIT License
 */

 !(function (exports) {

  var undefined
    , toString = {}.toString
    ;

  exports.string = {};

  /**
   * Library version.
   */
  string.version = '0.0.0';

  /**
   * Test if 'self' is a string.
   *
   * @param {String} self string
   * @return {Boolean} true if 'self' is a string, false otherwise
   * @api public
   */

  string.is = function (self) {
    return '[object String]' === toString.call(self);
  };

  /**
   * Test if 'self' is blank.
   *
   * @param {String} self string
   * @return {Boolean} true if 'self' is blank, false otherwise
   * @api public
   */

  string.is.blank = function (self) {
    return !!self.match(/^\s*$/);
  };

  /**
   * Capitalize 'self'.
   *
   * @param {String} self string
   * @return {String} 'self' capitalized
   * @api public
   */

  string.capitalize = function (self) {
    return self.charAt(0).toUpperCase()
      + self.substring(1).toLowerCase();
  };

  /**
   * Chop 'self' by 'step'.
   *
   * @param {String} self string
   * @param {Number} step step
   * @return {Array} parts of 'self' chopped
   */

  string.chop = function (self, step) {
    var result = []
      , len = self.length
      , i;

    step || (step = len);

    for (i = 0; i < len; i += step) {
      result.push(self.slice(i, i + step));
    }

    return result;
  };

  /**
   * Count 'substr' occurrencies in 'self'.
   *
   * @param {String} self string
   * @param {String} substr substring
   * @return {Number} number of occurrencies of 'substr' in 'self'
   * @api public
   */

  string.count = function (self, substr) {
    var result = 0
      , len = self.length
      , step = substr.length
      , index = 0
      , i
      ;

    for (i = 0; i < len; i += index + step) {
      index = self.indexOf(substr, i);
      if (index < 0) {
        return result;
      }
      result += 1;
    }

    return result;
  };

  /**
   * Get chars of 'self'.
   *
   * @param {String} self string
   * @return {Array} chars of 'self'
   * @api public
   */

  string.chars = function (self) {
    return self.split('');
  };

  /**
   * Escape html.
   *
   * @param {String} self string
   * @return {String} string escaped
   * @api public
   */

  string.escapeHTML = function (self) {
    return self
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"')
      .replace(/'/g, "'");
  };

  /**
   * Unescape html.
   *
   * @param {String} self string
   * @return {String} string unescaped
   * @api public
   */

  string.unescapeHTML = function (self) {

    return self
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"')
      .replace(/'/g, "'");
  };

  /**
   * Escape regular expression.
   *
   * @param {String} self string
   * @return {String} string unescaped
   * @api public
   */

  string.escapeRegExp = function (self) {
    return self.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
  };

}(this));<commit_msg>added insert<commit_after>
/*!
 * string
 * JavaScript string utilities library
 * Copyright (c) 2011 Enrico Marino <enrico.marino@email.com>
 * MIT License
 */

 !(function (exports) {

  var undefined
    , toString = {}.toString
    ;

  exports.string = {};

  /**
   * Library version.
   */
  string.version = '0.0.0';

  /**
   * Test if 'self' is a string.
   *
   * @param {String} self string
   * @return {Boolean} true if 'self' is a string, false otherwise
   * @api public
   */

  string.is = function (self) {
    return '[object String]' === toString.call(self);
  };

  /**
   * Test if 'self' is blank.
   *
   * @param {String} self string
   * @return {Boolean} true if 'self' is blank, false otherwise
   * @api public
   */

  string.is.blank = function (self) {
    return !!self.match(/^\s*$/);
  };

  /**
   * Capitalize 'self'.
   *
   * @param {String} self string
   * @return {String} 'self' capitalized
   * @api public
   */

  string.capitalize = function (self) {
    return self.charAt(0).toUpperCase()
      + self.substring(1).toLowerCase();
  };

  /**
   * Chop 'self' by 'step'.
   *
   * @param {String} self string
   * @param {Number} step step
   * @return {Array} parts of 'self' chopped
   */

  string.chop = function (self, step) {
    var result = []
      , len = self.length
      , i;

    step || (step = len);

    for (i = 0; i < len; i += step) {
      result.push(self.slice(i, i + step));
    }

    return result;
  };

  /**
   * Count 'substr' occurrencies in 'self'.
   *
   * @param {String} self string
   * @param {String} substr substring
   * @return {Number} number of occurrencies of 'substr' in 'self'
   * @api public
   */

  string.count = function (self, substr) {
    var result = 0
      , len = self.length
      , step = substr.length
      , index = 0
      , i
      ;

    for (i = 0; i < len; i += index + step) {
      index = self.indexOf(substr, i);
      if (index < 0) {
        return result;
      }
      result += 1;
    }

    return result;
  };

  /**
   * Get chars of 'self'.
   *
   * @param {String} self string
   * @return {Array} chars of 'self'
   * @api public
   */

  string.chars = function (self) {
    return self.split('');
  };

  /**
   * Escape html.
   *
   * @param {String} self string
   * @return {String} string escaped
   * @api public
   */

  string.escapeHTML = function (self) {
    return self
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"')
      .replace(/'/g, "'");
  };

  /**
   * Unescape html.
   *
   * @param {String} self string
   * @return {String} string unescaped
   * @api public
   */

  string.unescapeHTML = function (self) {

    return self
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"')
      .replace(/'/g, "'");
  };

  /**
   * Escape regular expression.
   *
   * @param {String} self string
   * @return {String} string unescaped
   * @api public
   */

  string.escapeRegExp = function (self) {
    return self.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
  };

  /**
   * Insert 'substr' in 'self' from the 'i'-th char.
   *
   * @param {String} self string
   * @param {String} substr substring
   * @param {Number} [i = self.length]
   * @return {String} 'self' with 'substr' inserted from the 'i'-th char
   */

  string.insert = function (self, substr, i) {
    i || (i = self.length);

    return self
      .split('')
      .splice(i, 0, substr)
      .join('');
  };

}(this));<|endoftext|>


<commit_before>'use strict';

var utils = require('./utils');

/**
 * camelCase the characters in `string`.
 *
 * ```js
 * <%= camelcase("foo bar baz") %>
 * //=> 'fooBarBaz'
 * ```
 *
 * @param  {String} `string` The string to camelcase.
 * @return {String}
 * @api public
 */

exports.camelcase = function camelcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) {
    return str.toLowerCase();
  }

  var re = /[-_.\W\s]+(\w|$)/g;
  str = exports.chop(str).toLowerCase();
  return str.replace(re, function(_, ch) {
    return ch.toUpperCase();
  });
};

/**
 * Center align the characters in a string using
 * non-breaking spaces.
 *
 * ```js
 * <%= centerAlign("abc") %>
 * ```
 *
 * @param  {String} `str` The string to reverse.
 * @return {String} Centered string.
 * @related rightAlign
 * @api public
 */

exports.centerAlign = function centerAlign(str) {
  return exports.isString(str) ? utils.alignCenter(str, ' ') : '';
};

/**
 * Like trim, but removes both extraneous whitespace and
 * non-word characters from the beginning and end of a string.
 *
 * ```js
 * <%= chop("_ABC_") %>
 * //=> 'ABC'
 *
 * <%= chop("-ABC-") %>
 * //=> 'ABC'
 *
 * <%= chop(" ABC ") %>
 * //=> 'ABC'
 * ```
 *
 * @param  {String} `string` The string to chop.
 * @return {String}
 * @api public
 */

exports.chop = function chop(str) {
  if (!exports.isString(str)) return '';
  var re = /^[-_.\W\s]+|[-_.\W\s]+$/g;
  return str.trim().replace(re, '');
};

/**
 * Count the number of occurrances of a substring
 * within a string.
 *
 * ```js
 * <%= count("abcabcabc", "a") %>
 * //=> '3'
 * ```
 *
 * @param  {String} `string`
 * @param  {String} `substring`
 * @return {Number} The occurances of `substring` in `string`
 * @api public
 */

exports.count = function count(str, sub) {
  if (exports.isString(str)) {
    return (sub ? (str.split(sub).length - 1) : '0');
  }
  return '';
};

/**
 * dot.case the characters in `string`.
 *
 * ```js
 * <%= dotcase("a-b-c d_e") %>
 * //=> 'a.b.c.d.e'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.dotcase = function dotcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toLowerCase(); }
  var re = /[-_.\W\s]+(\w|$)/g;
  return exports.chop(str).replace(re, function(_, ch) {
    return '.' + ch;
  });
};

/**
 * Truncate a string to the specified `length`, and append
 * it with an elipsis, `…`.
 *
 * ```js
 * <%= ellipsis("<span>foo bar baz</span>", 7) %>
 * //=> 'foo bar…'
 * ```
 *
 * @param  {String} `str`
 * @param  {Number} `length` The desired length of the returned string.
 * @param  {String} `ch` Optionally pass custom characters to append. Default is `…`.
 * @return {String} The truncated string.
 * @api public
 */

exports.ellipsis = function ellipsis(str, limit, ch) {
  return exports.isString(str)
    ? (exports.truncate(str, limit) + (ch || '…'))
    : '';
};

/**
 * Returns true if the value is a string.
 *
 * ```js
 * <%= isString('abc') %>
 * //=> 'true'
 *
 * <%= isString(null) %>
 * //=> 'false'
 * ```
 *
 * @param  {String} `val`
 * @return {Boolean} True if the value is a string.
 * @api public
 */

exports.isString = function isString(str) {
  return str && typeof str === 'string';
};

/**
 * Lowercase the characters in the given `string`.
 *
 * ```js
 * <%= lowercase("ABC") %>
 * //=> 'abc'
 * ```
 *
 * @param  {String} `string` The string to lowercase.
 * @return {String}
 * @api public
 */

exports.lowercase = exports.lower = function lowercase(str) {
  return exports.isString(str) ? str.toLowerCase() : '';
};

/**
 * PascalCase the characters in `string`.
 *
 * ```js
 * <%= pascalcase("foo bar baz") %>
 * //=> 'FooBarBaz'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.pascalcase = function pascalcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toUpperCase(); }
  str = exports.camelcase(str);
  return str[0].toUpperCase() + str.slice(1);
};

/**
 * snake_case the characters in `string`.
 *
 * ```js
 * <%= snakecase("a-b-c d_e") %>
 * //=> 'a_b_c_d_e'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.snakecase = function snakecase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toLowerCase(); }
  var re = /[-_.\W\s]+(\w|$)/g;
  return exports.chop(str).replace(re, function(_, ch) {
    return '_' + ch;
  });
};

/**
 * Split `string` by the given `character`.
 *
 * ```js
 * <%= split("a,b,c", ",") %>
 * //=> ['a', 'b', 'c']
 * ```
 *
 * @param  {String} `string` The string to split.
 * @return {String} `character` Default is `,`
 * @api public
 */

exports.split = function split(str, ch) {
  return exports.isString(str) ? str.split(ch || ',') : '';
};

/**
 * Strip `substring` from the given `string`.
 *
 * ```js
 * <%= strip("foo-bar", "foo-") %>
 * //=> 'bar'
 * ```
 *
 * @param  {String|RegExp} `substring` The string or regex pattern of the substring to remove.
 * @param  {String} `string` The target string.
 * @api public
 */

exports.strip = function strip(val, str) {
  if (!exports.isString(val) && !(val instanceof RegExp)) return '';
  if (!exports.isString(str)) return '';
  return str.split(val).join('');
};

/**
 * Strip the indentation from a `string`.
 *
 * ```js
 * <%= stripIndent("  _ABC_") %>
 * //=> 'ABC'
 * ```
 *
 * @param  {String} `string` The string to strip indentation from.
 * @return {String}
 * @api public
 */

exports.stripIndent = function stripIndent(str) {
  if (!exports.isString(str)) return '';

  var matches = str.match(/^[ \t]+(?!\s)/gm);
  if (!matches) return str;

  var ws = matches.reverse().pop();
  var len = ws.length;
  if (len) {
    var re = new RegExp('^[ \\t]{' + len + '}', 'gm');
    return str.replace(re, '');
  }
  return str;
};

/**
 * Trim extraneous whitespace from the beginning and end
 * of a string.
 *
 * ```js
 * <%= trim("  ABC   ") %>
 * //=> 'ABC'
 * ```
 *
 * @param  {String} `string` The string to trim.
 * @return {String}
 * @api public
 */

exports.trim = function trim(str) {
  return exports.isString(str) ? str.trim() : '';
};

/**
 * dash-case the characters in `string`. This is similar to [slugify],
 * but [slugify] makes the string compatible to be used as a URL slug.
 *
 * ```js
 * <%= dashcase("a b.c d_e") %>
 * //=> 'a-b-c-d-e'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @tags dasherize, dashify, hyphenate, case
 * @api public
 */

exports.dashcase = function dashcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toLowerCase(); }
  var re = /[-_.\W\s]+(\w|$)/g;
  return exports.chop(str).replace(re, function(_, ch) {
    return '-' + ch;
  });
};

/**
 * path/case the characters in `string`.
 *
 * ```js
 * <%= pathcase("a-b-c d_e") %>
 * //=> 'a/b/c/d/e'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.pathcase = function pathcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toLowerCase(); }
  var re = /[_.-\W\s]+(\w|$)/g;
  return exports.chop(str).replace(re, function(_, ch) {
    return '/' + ch;
  });
};

/**
 * Sentence-case the characters in `string`.
 *
 * ```js
 * <%= sentencecase("foo bar baz.") %>
 * //=> 'Foo bar baz.'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.sentencecase = function sentencecase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toUpperCase(); }
  return str.charAt(0).toUpperCase() + str.slice(1);
};

/**
 * Replace spaces in a string with hyphens. This
 *
 * ```js
 * <%= hyphenate("a b c") %>
 * //=> 'a-b-c'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.hyphenate = function hyphenate(str) {
  return exports.isString(str) ? exports.chop(str).split(' ').join('-') : '';
};

/**
 * TODO: Need to use a proper slugifier for this.
 * I don't want to use `node-slug`, it's way too
 * huge for this.
 *
 * ```js
 * <%= slugify('This is a post title.') %>
 * //=> 'this-is-a-post-title'
 * ```
 *
 * @param  {String} `string` The string to slugify.
 * @return {String} Slug.
 * @api draft
 */

exports.slugify = function slugify(str) {
  return exports.isString(str) ? exports.dashcase(str) : '';
};

/**
 * Reverse the characters in a string.
 *
 * ```js
 * <%= reverse("abc") %>
 * //=> 'cba'
 * ```
 *
 * @param  {String} `str` The string to reverse.
 * @return {String}
 * @api public
 */

exports.reverse = function reverse(str) {
  return exports.isString(str) ? str.split('').reverse().join('') : '';
};

/**
 * Right align the characters in a string using
 * non-breaking spaces.
 *
 * ```js
 * <%= rightAlign(str) %>
 * ```
 *
 * @param  {String} `str` The string to reverse.
 * @return {String} Right-aligned string.
 * @related centerAlign
 * @api public
 */

exports.rightAlign = function rightAlign(str) {
  return exports.isString(str) ? utils.alignRight(str) : '';
};

/**
 * Replace occurrences of `a` with `b`.
 *
 * ```js
 * <%= replace("abcabc", /a/, "z") %>
 * //=> 'zbczbc'
 * ```
 *
 * @param  {String} `str`
 * @param  {String|RegExp} `a` Can be a string or regexp.
 * @param  {String} `b`
 * @return {String}
 * @api public
 */

exports.replace = function replace(str, a, b) {
  if (!exports.isString(str)) return '';
  if (!a) { return str; }
  if (!b) { b = ''; }
  return str.split(a).join(b);
};

/**
 * Truncate a string by removing all HTML tags and limiting the result
 * to the specified `length`.
 *
 * ```js
 * <%= titlecase("big deal") %>
 * //=> 'foo bar'
 * ```
 *
 * @param  {String} `str`
 * @param  {Number} `length` The desired length of the returned string.
 * @return {String} The truncated string.
 * @api public
 */

exports.titlecase = function titlecase(str, length) {
  return exports.isString(str) ? utils.titlecase(str) : '';
};

/**
 * Truncate a string by removing all HTML tags and limiting the result
 * to the specified `length`.
 *
 * ```js
 * <%= truncate("<span>foo bar baz</span>", 7) %>
 * //=> 'foo bar'
 * ```
 *
 * @param  {String} `str`
 * @param  {Number} `length` The desired length of the returned string.
 * @return {String} The truncated string.
 * @api public
 */

exports.truncate = function truncate(str, length) {
  return exports.isString(str) ? str.substr(0, length) : '';
};

/**
 * Uppercase the characters in a string.
 *
 * ```js
 * <%= uppercase("abc") %>
 * //=> 'ABC'
 * ```
 *
 * @param  {String} `string` The string to uppercase.
 * @return {String}
 * @api public
 */

exports.uppercase = exports.upper = function uppercase(str) {
  return exports.isString(str) ? str.toUpperCase() : '';
};
// ensure code conext picks up alias as a method
exports.upper = function upper(str) {
  return exports.uppercase.apply(null, arguments);
};

/**
 * Wrap words to a specified width using [word-wrap].
 *
 * ```js
 * <%= wordwrap("a b c d e f", {width: 5, newline: '<br>  '}) %>
 * //=> '  a b c <br>  d e f'
 * ```
 *
 * @param  {String} `string` The string with words to wrap.
 * @param  {Options} `object` Options to pass to [word-wrap]
 * @return {String} Formatted string.
 * @api public
 */

exports.wordwrap = function wordwrap(str) {
  return exports.isString(str) ? utils.wrap.apply(utils.wrap, arguments) : '';
};
<commit_msg>alias `titlecase` as `titleize`<commit_after>'use strict';

var utils = require('./utils');

/**
 * camelCase the characters in `string`.
 *
 * ```js
 * <%= camelcase("foo bar baz") %>
 * //=> 'fooBarBaz'
 * ```
 *
 * @param  {String} `string` The string to camelcase.
 * @return {String}
 * @api public
 */

exports.camelcase = function camelcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) {
    return str.toLowerCase();
  }

  var re = /[-_.\W\s]+(\w|$)/g;
  str = exports.chop(str).toLowerCase();
  return str.replace(re, function(_, ch) {
    return ch.toUpperCase();
  });
};

/**
 * Center align the characters in a string using
 * non-breaking spaces.
 *
 * ```js
 * <%= centerAlign("abc") %>
 * ```
 *
 * @param  {String} `str` The string to reverse.
 * @return {String} Centered string.
 * @related rightAlign
 * @api public
 */

exports.centerAlign = function centerAlign(str) {
  return exports.isString(str) ? utils.alignCenter(str, ' ') : '';
};

/**
 * Like trim, but removes both extraneous whitespace and
 * non-word characters from the beginning and end of a string.
 *
 * ```js
 * <%= chop("_ABC_") %>
 * //=> 'ABC'
 *
 * <%= chop("-ABC-") %>
 * //=> 'ABC'
 *
 * <%= chop(" ABC ") %>
 * //=> 'ABC'
 * ```
 *
 * @param  {String} `string` The string to chop.
 * @return {String}
 * @api public
 */

exports.chop = function chop(str) {
  if (!exports.isString(str)) return '';
  var re = /^[-_.\W\s]+|[-_.\W\s]+$/g;
  return str.trim().replace(re, '');
};

/**
 * Count the number of occurrances of a substring
 * within a string.
 *
 * ```js
 * <%= count("abcabcabc", "a") %>
 * //=> '3'
 * ```
 *
 * @param  {String} `string`
 * @param  {String} `substring`
 * @return {Number} The occurances of `substring` in `string`
 * @api public
 */

exports.count = function count(str, sub) {
  if (exports.isString(str)) {
    return (sub ? (str.split(sub).length - 1) : '0');
  }
  return '';
};

/**
 * dot.case the characters in `string`.
 *
 * ```js
 * <%= dotcase("a-b-c d_e") %>
 * //=> 'a.b.c.d.e'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.dotcase = function dotcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toLowerCase(); }
  var re = /[-_.\W\s]+(\w|$)/g;
  return exports.chop(str).replace(re, function(_, ch) {
    return '.' + ch;
  });
};

/**
 * Truncate a string to the specified `length`, and append
 * it with an elipsis, `…`.
 *
 * ```js
 * <%= ellipsis("<span>foo bar baz</span>", 7) %>
 * //=> 'foo bar…'
 * ```
 *
 * @param  {String} `str`
 * @param  {Number} `length` The desired length of the returned string.
 * @param  {String} `ch` Optionally pass custom characters to append. Default is `…`.
 * @return {String} The truncated string.
 * @api public
 */

exports.ellipsis = function ellipsis(str, limit, ch) {
  return exports.isString(str)
    ? (exports.truncate(str, limit) + (ch || '…'))
    : '';
};

/**
 * Returns true if the value is a string.
 *
 * ```js
 * <%= isString('abc') %>
 * //=> 'true'
 *
 * <%= isString(null) %>
 * //=> 'false'
 * ```
 *
 * @param  {String} `val`
 * @return {Boolean} True if the value is a string.
 * @api public
 */

exports.isString = function isString(str) {
  return str && typeof str === 'string';
};

/**
 * Lowercase the characters in the given `string`.
 *
 * ```js
 * <%= lowercase("ABC") %>
 * //=> 'abc'
 * ```
 *
 * @param  {String} `string` The string to lowercase.
 * @return {String}
 * @api public
 */

exports.lowercase = exports.lower = function lowercase(str) {
  return exports.isString(str) ? str.toLowerCase() : '';
};

/**
 * PascalCase the characters in `string`.
 *
 * ```js
 * <%= pascalcase("foo bar baz") %>
 * //=> 'FooBarBaz'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.pascalcase = function pascalcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toUpperCase(); }
  str = exports.camelcase(str);
  return str[0].toUpperCase() + str.slice(1);
};

/**
 * snake_case the characters in `string`.
 *
 * ```js
 * <%= snakecase("a-b-c d_e") %>
 * //=> 'a_b_c_d_e'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.snakecase = function snakecase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toLowerCase(); }
  var re = /[-_.\W\s]+(\w|$)/g;
  return exports.chop(str).replace(re, function(_, ch) {
    return '_' + ch;
  });
};

/**
 * Split `string` by the given `character`.
 *
 * ```js
 * <%= split("a,b,c", ",") %>
 * //=> ['a', 'b', 'c']
 * ```
 *
 * @param  {String} `string` The string to split.
 * @return {String} `character` Default is `,`
 * @api public
 */

exports.split = function split(str, ch) {
  return exports.isString(str) ? str.split(ch || ',') : '';
};

/**
 * Strip `substring` from the given `string`.
 *
 * ```js
 * <%= strip("foo-bar", "foo-") %>
 * //=> 'bar'
 * ```
 *
 * @param  {String|RegExp} `substring` The string or regex pattern of the substring to remove.
 * @param  {String} `string` The target string.
 * @api public
 */

exports.strip = function strip(val, str) {
  if (!exports.isString(val) && !(val instanceof RegExp)) return '';
  if (!exports.isString(str)) return '';
  return str.split(val).join('');
};

/**
 * Strip the indentation from a `string`.
 *
 * ```js
 * <%= stripIndent("  _ABC_") %>
 * //=> 'ABC'
 * ```
 *
 * @param  {String} `string` The string to strip indentation from.
 * @return {String}
 * @api public
 */

exports.stripIndent = function stripIndent(str) {
  if (!exports.isString(str)) return '';

  var matches = str.match(/^[ \t]+(?!\s)/gm);
  if (!matches) return str;

  var ws = matches.reverse().pop();
  var len = ws.length;
  if (len) {
    var re = new RegExp('^[ \\t]{' + len + '}', 'gm');
    return str.replace(re, '');
  }
  return str;
};

/**
 * Trim extraneous whitespace from the beginning and end
 * of a string.
 *
 * ```js
 * <%= trim("  ABC   ") %>
 * //=> 'ABC'
 * ```
 *
 * @param  {String} `string` The string to trim.
 * @return {String}
 * @api public
 */

exports.trim = function trim(str) {
  return exports.isString(str) ? str.trim() : '';
};

/**
 * dash-case the characters in `string`. This is similar to [slugify],
 * but [slugify] makes the string compatible to be used as a URL slug.
 *
 * ```js
 * <%= dashcase("a b.c d_e") %>
 * //=> 'a-b-c-d-e'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @tags dasherize, dashify, hyphenate, case
 * @api public
 */

exports.dashcase = function dashcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toLowerCase(); }
  var re = /[-_.\W\s]+(\w|$)/g;
  return exports.chop(str).replace(re, function(_, ch) {
    return '-' + ch;
  });
};

/**
 * path/case the characters in `string`.
 *
 * ```js
 * <%= pathcase("a-b-c d_e") %>
 * //=> 'a/b/c/d/e'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.pathcase = function pathcase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toLowerCase(); }
  var re = /[_.-\W\s]+(\w|$)/g;
  return exports.chop(str).replace(re, function(_, ch) {
    return '/' + ch;
  });
};

/**
 * Sentence-case the characters in `string`.
 *
 * ```js
 * <%= sentencecase("foo bar baz.") %>
 * //=> 'Foo bar baz.'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.sentencecase = function sentencecase(str) {
  if (!exports.isString(str)) return '';
  if (str.length === 1) { return str.toUpperCase(); }
  return str.charAt(0).toUpperCase() + str.slice(1);
};

/**
 * Replace spaces in a string with hyphens. This
 *
 * ```js
 * <%= hyphenate("a b c") %>
 * //=> 'a-b-c'
 * ```
 *
 * @param  {String} `string`
 * @return {String}
 * @api public
 */

exports.hyphenate = function hyphenate(str) {
  return exports.isString(str) ? exports.chop(str).split(' ').join('-') : '';
};

/**
 * TODO: Need to use a proper slugifier for this.
 * I don't want to use `node-slug`, it's way too
 * huge for this.
 *
 * ```js
 * <%= slugify('This is a post title.') %>
 * //=> 'this-is-a-post-title'
 * ```
 *
 * @param  {String} `string` The string to slugify.
 * @return {String} Slug.
 * @api draft
 */

exports.slugify = function slugify(str) {
  return exports.isString(str) ? exports.dashcase(str) : '';
};

/**
 * Reverse the characters in a string.
 *
 * ```js
 * <%= reverse("abc") %>
 * //=> 'cba'
 * ```
 *
 * @param  {String} `str` The string to reverse.
 * @return {String}
 * @api public
 */

exports.reverse = function reverse(str) {
  return exports.isString(str) ? str.split('').reverse().join('') : '';
};

/**
 * Right align the characters in a string using
 * non-breaking spaces.
 *
 * ```js
 * <%= rightAlign(str) %>
 * ```
 *
 * @param  {String} `str` The string to reverse.
 * @return {String} Right-aligned string.
 * @related centerAlign
 * @api public
 */

exports.rightAlign = function rightAlign(str) {
  return exports.isString(str) ? utils.alignRight(str) : '';
};

/**
 * Replace occurrences of `a` with `b`.
 *
 * ```js
 * <%= replace("abcabc", /a/, "z") %>
 * //=> 'zbczbc'
 * ```
 *
 * @param  {String} `str`
 * @param  {String|RegExp} `a` Can be a string or regexp.
 * @param  {String} `b`
 * @return {String}
 * @api public
 */

exports.replace = function replace(str, a, b) {
  if (!exports.isString(str)) return '';
  if (!a) { return str; }
  if (!b) { b = ''; }
  return str.split(a).join(b);
};

/**
 * Truncate a string by removing all HTML tags and limiting the result
 * to the specified `length`.
 *
 * ```js
 * <%= titlecase("big deal") %>
 * //=> 'foo bar'
 * ```
 *
 * @param  {String} `str`
 * @param  {Number} `length` The desired length of the returned string.
 * @return {String} The truncated string.
 * @api public
 */

exports.titlecase = exports.titleize = function titlecase(str, length) {
  return exports.isString(str) ? utils.titlecase(str) : '';
};

/**
 * Truncate a string by removing all HTML tags and limiting the result
 * to the specified `length`.
 *
 * ```js
 * <%= truncate("<span>foo bar baz</span>", 7) %>
 * //=> 'foo bar'
 * ```
 *
 * @param  {String} `str`
 * @param  {Number} `length` The desired length of the returned string.
 * @return {String} The truncated string.
 * @api public
 */

exports.truncate = function truncate(str, length) {
  return exports.isString(str) ? str.substr(0, length) : '';
};

/**
 * Uppercase the characters in a string.
 *
 * ```js
 * <%= uppercase("abc") %>
 * //=> 'ABC'
 * ```
 *
 * @param  {String} `string` The string to uppercase.
 * @return {String}
 * @api public
 */

exports.uppercase = exports.upper = function uppercase(str) {
  return exports.isString(str) ? str.toUpperCase() : '';
};
// ensure code conext picks up alias as a method
exports.upper = function upper(str) {
  return exports.uppercase.apply(null, arguments);
};

/**
 * Wrap words to a specified width using [word-wrap].
 *
 * ```js
 * <%= wordwrap("a b c d e f", {width: 5, newline: '<br>  '}) %>
 * //=> '  a b c <br>  d e f'
 * ```
 *
 * @param  {String} `string` The string with words to wrap.
 * @param  {Options} `object` Options to pass to [word-wrap]
 * @return {String} Formatted string.
 * @api public
 */

exports.wordwrap = function wordwrap(str) {
  return exports.isString(str) ? utils.wrap.apply(utils.wrap, arguments) : '';
};
<|endoftext|>