<commit_before>"use strict";

const utils     = module.exports = {};
const async     = require("async");
const cd        = require("content-disposition");
const cpr       = require("cpr");
const crypto    = require("crypto");
const escRe     = require("escape-string-regexp");
const ext       = require("file-extension");
const fs        = require("graceful-fs");
const isBin     = require("isbinaryfile");
const mimeTypes = require("mime-types");
const mkdirp    = require("mkdirp");
const mv        = require("mv");
const path      = require("path");
const rimraf    = require("rimraf");
const url       = require("url");
const util      = require("util");
const validate  = require("valid-filename");

const paths  = require("./paths.js").get();
const log    = require("./log.js");

const forceBinaryTypes = [
  "pdf",
  "ps",
  "eps",
  "ai",
];

const overrideMimeTypes = {
  "video/x-matroska": "video/webm",
};

// mkdirp wrapper with array support
utils.mkdir = function(dir, cb) {
  if (Array.isArray(dir)) {
    async.each(dir, function(p, cb) {
      mkdirp(p, {fs: fs, mode: "755"}, cb);
    }, cb);
  } else if (typeof dir === "string") {
    mkdirp(dir, {fs: fs, mode: "755"}, cb);
  } else {
    cb(new Error("mkdir: Wrong dir type: " + typeof dir));
  }
};

// rimraf wrapper with 10 retries
utils.rm = function(p, cb) {
  rimraf(p, {maxBusyTries: 10, glob: {dot: true}}, cb);
};

// rimraf.sync wrapper with 10 retries
utils.rmSync = function(p) {
  let tries = 10;
  (function run() {
    try {
      rimraf.sync(p, {glob: {dot: true}});
    } catch (err) {
      if (tries-- > 0) run();
    }
  })();
};

utils.move = function(src, dst, cb) {
  mv(src, dst, function(err) {
    if (cb) cb(err);
  });
};

utils.copyFile = function(src, dst, cb) {
  let cbCalled = false;
  const read     = fs.createReadStream(src);
  const write    = fs.createWriteStream(dst);

  function done(err) {
    if (cbCalled) return;
    cbCalled = true;
    if (cb) cb(err);
  }

  read.on("error", done);
  write.on("error", done);
  write.on("close", done);
  read.pipe(write);
};

utils.copyDir = function(src, dst, cb) {
  cpr(src, dst, {overwrite: true}, function(errs) {
    if (errs) log.error(errs);
    if (cb) cb();
  });
};

// Valid link character, the characters "l", "1", "i", "o", "0" characters are skipped
// for easier communication of links.
utils.linkChars = "abcdefghjkmnpqrstuvwxyz23456789";

// Get a pseudo-random n-character lowercase string.
utils.getLink = function(links, length) {
  let link = "";
  do {
    while (link.length < length) {
      link += utils.linkChars.charAt(Math.floor(Math.random() * utils.linkChars.length));
    }
  } while (links[link]); // In case the RNG generates an existing link, go again

  return link;
};

utils.pretty = function(data) {
  return util.inspect(data, {colors: true})
    .replace(/^\s+/gm, " ").replace(/\s+$/gm, "")
    .replace(/[\r\n]+/gm, "");
};

utils.getNewPath = function(origPath, callback) {
  fs.stat(origPath, function(err, stats) {
    if (err) callback(origPath);
    else {
      let filename  = path.basename(origPath);
      const dirname   = path.dirname(origPath);
      let extension = "";

      if (filename.indexOf(".") !== -1 && stats.isFile()) {
        extension = filename.substring(filename.lastIndexOf("."));
        filename = filename.substring(0, filename.lastIndexOf("."));
      }

      if (!/-\d+$/.test(filename)) filename += "-1";

      let canCreate = false;
      async.until(
        function() {
          return canCreate;
        },
        function(cb) {
          const num = parseInt(filename.substring(filename.lastIndexOf("-") + 1));
          filename = filename.substring(0, filename.lastIndexOf("-") + 1) + (num + 1);
          fs.stat(path.join(dirname, filename + extension), function(err) {
            canCreate = err;
            cb();
          });
        },
        function() {
          callback(path.join(dirname, filename + extension));
        }
      );
    }
  });
};

utils.normalizePath = function(p) {
  return p.replace(/[\\|/]+/g, "/");
};

utils.addFilesPath = function(p) {
  return p === "/" ? paths.files : path.join(paths.files + "/" + p);
};

utils.removeFilesPath = function(p) {
  if (p.length > paths.files.length) {
    return utils.normalizePath(p.substring(paths.files.length));
  } else if (p === paths.files) {
    return "/";
  }
};

utils.isPathSane = function(p, isURL) {
  if (isURL) {
    // Navigating up/down the tree
    if (/(?:^|[\\/])\.\.(?:[\\/]|$)/.test(p)) {
      return false;
    }
    // Invalid URL path characters
    if (!/^[a-zA-Z0-9-._~:/?#[\]@!$&'()*+,;=%]+$/.test(p)) {
      return false;
    }
    return true;
  } else {
    return p.split(/[\\/]/gm).every(function(name) {
      if (name === "." || name === "..") return false;
      if (!name) return true;
      return validate(name); // will reject invalid filenames on Windows
    });
  }
};

utils.isBinary = function(p, callback) {
  if (forceBinaryTypes.indexOf(ext(p)) !== -1) {
    return callback(null, true);
  }

  isBin(p, function(err, result) {
    if (err) return callback(err);
    callback(null, result);
  });
};

// TODO async/await this in Node.js 8
utils.contentType = function(p) {
  const type = mimeTypes.lookup(p);
  if (overrideMimeTypes[type]) return overrideMimeTypes[type];

  if (type) {
    const charset = mimeTypes.charsets.lookup(type);
    return type + (charset ? "; charset=" + charset : "");
  } else {
    try {
      return isBin.sync(p) ? "application/octet-stream" : "text/plain";
    } catch (err) {
      return "application/octet-stream";
    }
  }
};

utils.getDispo = function(fileName, inline) {
  return cd(path.basename(fileName), {type: inline ? "inline" : "attachment"});
};

utils.createSid = function() {
  return crypto.randomBytes(64).toString("base64").substring(0, 48);
};

utils.readJsonBody = function(req) {
  return new Promise(function(resolve, reject) {
    let body = [];
    req.on("data", function(chunk) {
      body.push(chunk);
    }).on("end", function() {
      body = String(Buffer.concat(body));
      try {
        resolve(JSON.parse(body));
      } catch (err) {
        reject(err);
      }
    });
  });
};

utils.countOccurences = function(string, search) {
  let num = 0, pos = 0;
  while (true) {
    pos = string.indexOf(search, pos);
    if (pos >= 0) {
      num += 1;
      pos += search.length;
    } else break;
  }
  return num;
};

utils.formatBytes = function(num) {
  if (num < 1) return num + " B";
  const units = ["B", "kB", "MB", "GB", "TB", "PB"];
  const exp = Math.min(Math.floor(Math.log(num) / Math.log(1000)), units.length - 1);
  return (num / Math.pow(1000, exp)).toPrecision(3) + " " + units[exp];
};

// TODO: https://tools.ietf.org/html/rfc7239
utils.ip = function(req) {
  return req.headers && req.headers["x-forwarded-for"] &&
      req.headers["x-forwarded-for"].split(",")[0].trim() ||
    req.headers && req.headers["x-real-ip"] ||
    req.connection && req.connection.remoteAddress ||
    req.connection && req.connection.socket && req.connection.socket.remoteAddress ||
    req.addr || // custom cached property
    req.remoteAddress && req.remoteAddress;
};

utils.port = function(req) {
  return req.headers && req.headers["x-real-port"] ||
    req.connection && req.connection.remotePort ||
    req.connection && req.connection.socket && req.connection.socket.remotePort ||
    req.port || // custom cached property
    req.remotePort && req.remotePort;
};

utils.naturalSort = function(a, b) {
  const x = [], y = [];
  function strcmp(a, b) { return a > b ? 1 : a < b ? -1 : 0; }
  a.replace(/(\d+)|(\D+)/g, function(_, a, b) { x.push([a || 0, b]); });
  b.replace(/(\d+)|(\D+)/g, function(_, a, b) { y.push([a || 0, b]); });
  while (x.length && y.length) {
    const xx = x.shift();
    const yy = y.shift();
    const nn = (xx[0] - yy[0]) || strcmp(xx[1], yy[1]);
    if (nn) return nn;
  }
  if (x.length) return -1;
  if (y.length) return 1;
  return 0;
};

utils.extensionRe = function(arr) {
  arr = arr.map(function(ext) {
    return escRe(ext);
  });
  return RegExp("\\.(" + arr.join("|") + ")$", "i");
};

utils.readFile = function(p, cb) {
  if (typeof p !== "string") return cb(null);
  fs.stat(p, function(_, stats) {
    if (stats && stats.isFile()) {
      fs.readFile(p, function(err, data) {
        if (err) return cb(err);
        cb(null, String(data));
      });
    } else {
      cb(null);
    }
  });
};

utils.arrify = function(val) {
  return Array.isArray(val) ? val : [val];
};
<commit_msg>remove unused import<commit_after>"use strict";

const utils     = module.exports = {};
const async     = require("async");
const cd        = require("content-disposition");
const cpr       = require("cpr");
const crypto    = require("crypto");
const escRe     = require("escape-string-regexp");
const ext       = require("file-extension");
const fs        = require("graceful-fs");
const isBin     = require("isbinaryfile");
const mimeTypes = require("mime-types");
const mkdirp    = require("mkdirp");
const mv        = require("mv");
const path      = require("path");
const rimraf    = require("rimraf");
const util      = require("util");
const validate  = require("valid-filename");

const paths  = require("./paths.js").get();
const log    = require("./log.js");

const forceBinaryTypes = [
  "pdf",
  "ps",
  "eps",
  "ai",
];

const overrideMimeTypes = {
  "video/x-matroska": "video/webm",
};

// mkdirp wrapper with array support
utils.mkdir = function(dir, cb) {
  if (Array.isArray(dir)) {
    async.each(dir, function(p, cb) {
      mkdirp(p, {fs: fs, mode: "755"}, cb);
    }, cb);
  } else if (typeof dir === "string") {
    mkdirp(dir, {fs: fs, mode: "755"}, cb);
  } else {
    cb(new Error("mkdir: Wrong dir type: " + typeof dir));
  }
};

// rimraf wrapper with 10 retries
utils.rm = function(p, cb) {
  rimraf(p, {maxBusyTries: 10, glob: {dot: true}}, cb);
};

// rimraf.sync wrapper with 10 retries
utils.rmSync = function(p) {
  let tries = 10;
  (function run() {
    try {
      rimraf.sync(p, {glob: {dot: true}});
    } catch (err) {
      if (tries-- > 0) run();
    }
  })();
};

utils.move = function(src, dst, cb) {
  mv(src, dst, function(err) {
    if (cb) cb(err);
  });
};

utils.copyFile = function(src, dst, cb) {
  let cbCalled = false;
  const read     = fs.createReadStream(src);
  const write    = fs.createWriteStream(dst);

  function done(err) {
    if (cbCalled) return;
    cbCalled = true;
    if (cb) cb(err);
  }

  read.on("error", done);
  write.on("error", done);
  write.on("close", done);
  read.pipe(write);
};

utils.copyDir = function(src, dst, cb) {
  cpr(src, dst, {overwrite: true}, function(errs) {
    if (errs) log.error(errs);
    if (cb) cb();
  });
};

// Valid link character, the characters "l", "1", "i", "o", "0" characters are skipped
// for easier communication of links.
utils.linkChars = "abcdefghjkmnpqrstuvwxyz23456789";

// Get a pseudo-random n-character lowercase string.
utils.getLink = function(links, length) {
  let link = "";
  do {
    while (link.length < length) {
      link += utils.linkChars.charAt(Math.floor(Math.random() * utils.linkChars.length));
    }
  } while (links[link]); // In case the RNG generates an existing link, go again

  return link;
};

utils.pretty = function(data) {
  return util.inspect(data, {colors: true})
    .replace(/^\s+/gm, " ").replace(/\s+$/gm, "")
    .replace(/[\r\n]+/gm, "");
};

utils.getNewPath = function(origPath, callback) {
  fs.stat(origPath, function(err, stats) {
    if (err) callback(origPath);
    else {
      let filename  = path.basename(origPath);
      const dirname   = path.dirname(origPath);
      let extension = "";

      if (filename.indexOf(".") !== -1 && stats.isFile()) {
        extension = filename.substring(filename.lastIndexOf("."));
        filename = filename.substring(0, filename.lastIndexOf("."));
      }

      if (!/-\d+$/.test(filename)) filename += "-1";

      let canCreate = false;
      async.until(
        function() {
          return canCreate;
        },
        function(cb) {
          const num = parseInt(filename.substring(filename.lastIndexOf("-") + 1));
          filename = filename.substring(0, filename.lastIndexOf("-") + 1) + (num + 1);
          fs.stat(path.join(dirname, filename + extension), function(err) {
            canCreate = err;
            cb();
          });
        },
        function() {
          callback(path.join(dirname, filename + extension));
        }
      );
    }
  });
};

utils.normalizePath = function(p) {
  return p.replace(/[\\|/]+/g, "/");
};

utils.addFilesPath = function(p) {
  return p === "/" ? paths.files : path.join(paths.files + "/" + p);
};

utils.removeFilesPath = function(p) {
  if (p.length > paths.files.length) {
    return utils.normalizePath(p.substring(paths.files.length));
  } else if (p === paths.files) {
    return "/";
  }
};

utils.isPathSane = function(p, isURL) {
  if (isURL) {
    // Navigating up/down the tree
    if (/(?:^|[\\/])\.\.(?:[\\/]|$)/.test(p)) {
      return false;
    }
    // Invalid URL path characters
    if (!/^[a-zA-Z0-9-._~:/?#[\]@!$&'()*+,;=%]+$/.test(p)) {
      return false;
    }
    return true;
  } else {
    return p.split(/[\\/]/gm).every(function(name) {
      if (name === "." || name === "..") return false;
      if (!name) return true;
      return validate(name); // will reject invalid filenames on Windows
    });
  }
};

utils.isBinary = function(p, callback) {
  if (forceBinaryTypes.indexOf(ext(p)) !== -1) {
    return callback(null, true);
  }

  isBin(p, function(err, result) {
    if (err) return callback(err);
    callback(null, result);
  });
};

// TODO async/await this in Node.js 8
utils.contentType = function(p) {
  const type = mimeTypes.lookup(p);
  if (overrideMimeTypes[type]) return overrideMimeTypes[type];

  if (type) {
    const charset = mimeTypes.charsets.lookup(type);
    return type + (charset ? "; charset=" + charset : "");
  } else {
    try {
      return isBin.sync(p) ? "application/octet-stream" : "text/plain";
    } catch (err) {
      return "application/octet-stream";
    }
  }
};

utils.getDispo = function(fileName, inline) {
  return cd(path.basename(fileName), {type: inline ? "inline" : "attachment"});
};

utils.createSid = function() {
  return crypto.randomBytes(64).toString("base64").substring(0, 48);
};

utils.readJsonBody = function(req) {
  return new Promise(function(resolve, reject) {
    let body = [];
    req.on("data", function(chunk) {
      body.push(chunk);
    }).on("end", function() {
      body = String(Buffer.concat(body));
      try {
        resolve(JSON.parse(body));
      } catch (err) {
        reject(err);
      }
    });
  });
};

utils.countOccurences = function(string, search) {
  let num = 0, pos = 0;
  while (true) {
    pos = string.indexOf(search, pos);
    if (pos >= 0) {
      num += 1;
      pos += search.length;
    } else break;
  }
  return num;
};

utils.formatBytes = function(num) {
  if (num < 1) return num + " B";
  const units = ["B", "kB", "MB", "GB", "TB", "PB"];
  const exp = Math.min(Math.floor(Math.log(num) / Math.log(1000)), units.length - 1);
  return (num / Math.pow(1000, exp)).toPrecision(3) + " " + units[exp];
};

// TODO: https://tools.ietf.org/html/rfc7239
utils.ip = function(req) {
  return req.headers && req.headers["x-forwarded-for"] &&
      req.headers["x-forwarded-for"].split(",")[0].trim() ||
    req.headers && req.headers["x-real-ip"] ||
    req.connection && req.connection.remoteAddress ||
    req.connection && req.connection.socket && req.connection.socket.remoteAddress ||
    req.addr || // custom cached property
    req.remoteAddress && req.remoteAddress;
};

utils.port = function(req) {
  return req.headers && req.headers["x-real-port"] ||
    req.connection && req.connection.remotePort ||
    req.connection && req.connection.socket && req.connection.socket.remotePort ||
    req.port || // custom cached property
    req.remotePort && req.remotePort;
};

utils.naturalSort = function(a, b) {
  const x = [], y = [];
  function strcmp(a, b) { return a > b ? 1 : a < b ? -1 : 0; }
  a.replace(/(\d+)|(\D+)/g, function(_, a, b) { x.push([a || 0, b]); });
  b.replace(/(\d+)|(\D+)/g, function(_, a, b) { y.push([a || 0, b]); });
  while (x.length && y.length) {
    const xx = x.shift();
    const yy = y.shift();
    const nn = (xx[0] - yy[0]) || strcmp(xx[1], yy[1]);
    if (nn) return nn;
  }
  if (x.length) return -1;
  if (y.length) return 1;
  return 0;
};

utils.extensionRe = function(arr) {
  arr = arr.map(function(ext) {
    return escRe(ext);
  });
  return RegExp("\\.(" + arr.join("|") + ")$", "i");
};

utils.readFile = function(p, cb) {
  if (typeof p !== "string") return cb(null);
  fs.stat(p, function(_, stats) {
    if (stats && stats.isFile()) {
      fs.readFile(p, function(err, data) {
        if (err) return cb(err);
        cb(null, String(data));
      });
    } else {
      cb(null);
    }
  });
};

utils.arrify = function(val) {
  return Array.isArray(val) ? val : [val];
};
<|endoftext|>


<commit_before>
fc.sourceNormalizers = [];
fc.sourceFetchers = [];

var ajaxDefaults = {
	dataType: 'json',
	cache: false
};

var eventGUID = 1;

function EventManager(options, _sources) {
	var t = this;

	// exports
	t.isFetchNeeded = isFetchNeeded;
	t.fetchEvents = fetchEvents;
	t.addEventSource = addEventSource;
	t.removeEventSource = removeEventSource;
	t.updateEvent = updateEvent;
	t.renderEvent = renderEvent;
	t.removeEvents = removeEvents;
	t.clientEvents = clientEvents;
	t.normalizeEvent = normalizeEvent;

	// imports
	var trigger = t.trigger;
	var getView = t.getView;
	var reportEvents = t.reportEvents;

	// locals
	var stickySource = { events: [] };
	var sources = [ stickySource ];
	var rangeStart, rangeEnd;
	var currentFetchID = 0;
	var pendingSourceCnt = 0;
	var loadingLevel = 0;
	var cache = [];

	for (var i=0; i<_sources.length; i++) {
		_addEventSource(_sources[i]);
	}

	/* Fetching
	-----------------------------------------------------------------------------*/

	function isFetchNeeded(start, end) {
		return !rangeStart || start < rangeStart || end > rangeEnd;
	}

	function fetchEvents(start, end) {
		rangeStart = start;
		rangeEnd = end;
		cache = [];
		var fetchID = ++currentFetchID;
		var len = sources.length;
		pendingSourceCnt = len;
		for (var i=0; i<len; i++) {
			fetchEventSource(sources[i], fetchID);
		}
	}

	function fetchEventSource(source, fetchID) {
		_fetchEventSource(source, function(events) {
			if (fetchID == currentFetchID) {
				if (events) {

					if (options.eventDataTransform) {
						events = $.map(events, options.eventDataTransform);
					}
					if (source.eventDataTransform) {
						events = $.map(events, source.eventDataTransform);
					}
					// TODO: this technique is not ideal for static array event sources.
					//  For arrays, we'll want to process all events right in the beginning, then never again.

					for (var i=0; i<events.length; i++) {
						events[i].source = source;
						normalizeEvent(events[i]);
					}
					cache = cache.concat(events);
				}
				pendingSourceCnt--;
				if (!pendingSourceCnt) {
					reportEvents(cache);
				}
			}
		});
	}

	function _fetchEventSource(source, callback) {
		var i;
		var fetchers = fc.sourceFetchers;
		var res;
		for (i=0; i<fetchers.length; i++) {
			res = fetchers[i](source, rangeStart, rangeEnd, callback);
			if (res === true) {
				// the fetcher is in charge. made its own async request
				return;
			}
			else if (typeof res == 'object') {
				// the fetcher returned a new source. process it
				_fetchEventSource(res, callback);
				return;
			}
		}
		var events = source.events;
		if (events) {
			if ($.isFunction(events)) {
				pushLoading();
				events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
					callback(events);
					popLoading();
				});
			}
			else if ($.isArray(events)) {
				callback(events);
			}
			else {
				callback();
			}
		}else{
			var url = source.url;
			if (url) {
				var success = source.success;
				var error = source.error;
				var complete = source.complete;
				var data = $.extend({}, source.data || {});
				var startParam = firstDefined(source.startParam, options.startParam);
				var endParam = firstDefined(source.endParam, options.endParam);
				if (startParam) {
					data[startParam] = Math.round(+rangeStart / 1000);
				}
				if (endParam) {
					data[endParam] = Math.round(+rangeEnd / 1000);
				}
				pushLoading();
				$.ajax($.extend({}, ajaxDefaults, source, {
					data: data,
					success: function(events) {
						events = events || [];
						var res = applyAll(success, this, arguments);
						if ($.isArray(res)) {
							events = res;
						}
						callback(events);
					},
					error: function() {
						applyAll(error, this, arguments);
						callback();
					},
					complete: function() {
						applyAll(complete, this, arguments);
						popLoading();
					}
				}));
			}else{
				callback();
			}
		}
	}

	/* Sources
	-----------------------------------------------------------------------------*/

	function addEventSource(source) {
		source = _addEventSource(source);
		if (source) {
			pendingSourceCnt++;
			fetchEventSource(source, currentFetchID); // will eventually call reportEvents
		}
	}

	function _addEventSource(source) {
		if ($.isFunction(source) || $.isArray(source)) {
			source = { events: source };
		}
		else if (typeof source == 'string') {
			source = { url: source };
		}
		if (typeof source == 'object') {
			normalizeSource(source);
			sources.push(source);
			return source;
		}
	}

	function removeEventSource(source) {
		sources = $.grep(sources, function(src) {
			return !isSourcesEqual(src, source);
		});
		// remove all client events from that source
		cache = $.grep(cache, function(e) {
			return !isSourcesEqual(e.source, source);
		});
		reportEvents(cache);
	}

	/* Manipulation
	-----------------------------------------------------------------------------*/

	function updateEvent(event) { // update an existing event
		var i, len = cache.length, e,
			defaultEventEnd = getView().defaultEventEnd, // getView???
			startDelta = event.start - event._start,
			endDelta = event.end ?
				(event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
				: 0;                                                      // was null and event was just resized
		for (i=0; i<len; i++) {
			e = cache[i];
			if (e._id == event._id && e != event) {
				e.start = new Date(+e.start + startDelta);
				if (event.end) {
					if (e.end) {
						e.end = new Date(+e.end + endDelta);
					}else{
						e.end = new Date(+defaultEventEnd(e) + endDelta);
					}
				}else{
					e.end = null;
				}
				e.title = event.title;
				e.url = event.url;
				e.allDay = event.allDay;
				e.className = event.className;
				e.editable = event.editable;
				e.color = event.color;
				e.backgroudColor = event.backgroudColor;
				e.borderColor = event.borderColor;
				e.textColor = event.textColor;
				normalizeEvent(e);
			}
		}
		normalizeEvent(event);
		reportEvents(cache);
	}

	function renderEvent(event, stick) {
		normalizeEvent(event);
		if (!event.source) {
			if (stick) {
				stickySource.events.push(event);
				event.source = stickySource;
			}
			cache.push(event);
		}
		reportEvents(cache);
	}

	function removeEvents(filter) {
		if (!filter) { // remove all
			cache = [];
			// clear all array sources
			for (var i=0; i<sources.length; i++) {
				if ($.isArray(sources[i].events)) {
					sources[i].events = [];
				}
			}
		}else{
			if (!$.isFunction(filter)) { // an event ID
				var id = filter + '';
				filter = function(e) {
					return e._id == id;
				};
			}
			cache = $.grep(cache, filter, true);
			// remove events from array sources
			for (var i=0; i<sources.length; i++) {
				if ($.isArray(sources[i].events)) {
					sources[i].events = $.grep(sources[i].events, filter, true);
				}
			}
		}
		reportEvents(cache);
	}

	function clientEvents(filter) {
		if ($.isFunction(filter)) {
			return $.grep(cache, filter);
		}
		else if (filter) { // an event ID
			filter += '';
			return $.grep(cache, function(e) {
				return e._id == filter;
			});
		}
		return cache; // else, return all
	}

	/* Loading State
	-----------------------------------------------------------------------------*/

	function pushLoading() {
		if (!loadingLevel++) {
			trigger('loading', null, true);
		}
	}

	function popLoading() {
		if (!--loadingLevel) {
			trigger('loading', null, false);
		}
	}

	/* Event Normalization
	-----------------------------------------------------------------------------*/

	function normalizeEvent(event) {
		var source = event.source || {};
		var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
		event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
		if (event.date) {
			if (!event.start) {
				event.start = event.date;
			}
			delete event.date;
		}
		event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
		event.end = parseDate(event.end, ignoreTimezone);
		if (event.end && event.end <= event.start) {
			event.end = null;
		}
		event._end = event.end ? cloneDate(event.end) : null;
		if (event.allDay === undefined) {
			event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
		}
		if (event.className) {
			if (typeof event.className == 'string') {
				event.className = event.className.split(/\s+/);
			}
		}else{
			event.className = [];
		}
		// TODO: if there is no start date, return false to indicate an invalid event
	}

	/* Utils
	------------------------------------------------------------------------------*/

	function normalizeSource(source) {
		if (source.className) {
			// TODO: repeat code, same code for event classNames
			if (typeof source.className == 'string') {
				source.className = source.className.split(/\s+/);
			}
		}else{
			source.className = [];
		}
		var normalizers = fc.sourceNormalizers;
		for (var i=0; i<normalizers.length; i++) {
			normalizers[i](source);
		}
	}

	function isSourcesEqual(source1, source2) {
		return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
	}

	function getSourcePrimitive(source) {
		return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
	}

}
<commit_msg>Function for retrieving the currently added sources<commit_after>
fc.sourceNormalizers = [];
fc.sourceFetchers = [];

var ajaxDefaults = {
	dataType: 'json',
	cache: false
};

var eventGUID = 1;

function EventManager(options, _sources) {
	var t = this;

	// exports
	t.isFetchNeeded = isFetchNeeded;
	t.fetchEvents = fetchEvents;
	t.addEventSource = addEventSource;
	t.removeEventSource = removeEventSource;
	t.updateEvent = updateEvent;
	t.renderEvent = renderEvent;
	t.removeEvents = removeEvents;
	t.clientEvents = clientEvents;
	t.normalizeEvent = normalizeEvent;
    t.getEventSources = getEventSources;

	// imports
	var trigger = t.trigger;
	var getView = t.getView;
	var reportEvents = t.reportEvents;

	// locals
	var stickySource = { events: [] };
	var sources = [ stickySource ];
	var rangeStart, rangeEnd;
	var currentFetchID = 0;
	var pendingSourceCnt = 0;
	var loadingLevel = 0;
	var cache = [];

	for (var i=0; i<_sources.length; i++) {
		_addEventSource(_sources[i]);
	}

	/* Fetching
	-----------------------------------------------------------------------------*/

	function isFetchNeeded(start, end) {
		return !rangeStart || start < rangeStart || end > rangeEnd;
	}

	function fetchEvents(start, end) {
		rangeStart = start;
		rangeEnd = end;
		cache = [];
		var fetchID = ++currentFetchID;
		var len = sources.length;
		pendingSourceCnt = len;
		for (var i=0; i<len; i++) {
			fetchEventSource(sources[i], fetchID);
		}
	}

	function fetchEventSource(source, fetchID) {
		_fetchEventSource(source, function(events) {
			if (fetchID == currentFetchID) {
				if (events) {

					if (options.eventDataTransform) {
						events = $.map(events, options.eventDataTransform);
					}
					if (source.eventDataTransform) {
						events = $.map(events, source.eventDataTransform);
					}
					// TODO: this technique is not ideal for static array event sources.
					//  For arrays, we'll want to process all events right in the beginning, then never again.

					for (var i=0; i<events.length; i++) {
						events[i].source = source;
						normalizeEvent(events[i]);
					}
					cache = cache.concat(events);
				}
				pendingSourceCnt--;
				if (!pendingSourceCnt) {
					reportEvents(cache);
				}
			}
		});
	}

	function _fetchEventSource(source, callback) {
		var i;
		var fetchers = fc.sourceFetchers;
		var res;
		for (i=0; i<fetchers.length; i++) {
			res = fetchers[i](source, rangeStart, rangeEnd, callback);
			if (res === true) {
				// the fetcher is in charge. made its own async request
				return;
			}
			else if (typeof res == 'object') {
				// the fetcher returned a new source. process it
				_fetchEventSource(res, callback);
				return;
			}
		}
		var events = source.events;
		if (events) {
			if ($.isFunction(events)) {
				pushLoading();
				events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
					callback(events);
					popLoading();
				});
			}
			else if ($.isArray(events)) {
				callback(events);
			}
			else {
				callback();
			}
		}else{
			var url = source.url;
			if (url) {
				var success = source.success;
				var error = source.error;
				var complete = source.complete;
				var data = $.extend({}, source.data || {});
				var startParam = firstDefined(source.startParam, options.startParam);
				var endParam = firstDefined(source.endParam, options.endParam);
				if (startParam) {
					data[startParam] = Math.round(+rangeStart / 1000);
				}
				if (endParam) {
					data[endParam] = Math.round(+rangeEnd / 1000);
				}
				pushLoading();
				$.ajax($.extend({}, ajaxDefaults, source, {
					data: data,
					success: function(events) {
						events = events || [];
						var res = applyAll(success, this, arguments);
						if ($.isArray(res)) {
							events = res;
						}
						callback(events);
					},
					error: function() {
						applyAll(error, this, arguments);
						callback();
					},
					complete: function() {
						applyAll(complete, this, arguments);
						popLoading();
					}
				}));
			}else{
				callback();
			}
		}
	}

	/* Sources
	-----------------------------------------------------------------------------*/

    function getEventSources() {
        return sources;
    }

	function addEventSource(source) {
		source = _addEventSource(source);
		if (source) {
			pendingSourceCnt++;
			fetchEventSource(source, currentFetchID); // will eventually call reportEvents
		}
	}

	function _addEventSource(source) {
		if ($.isFunction(source) || $.isArray(source)) {
			source = { events: source };
		}
		else if (typeof source == 'string') {
			source = { url: source };
		}
		if (typeof source == 'object') {
			normalizeSource(source);
			sources.push(source);
			return source;
		}
	}

	function removeEventSource(source) {
		sources = $.grep(sources, function(src) {
			return !isSourcesEqual(src, source);
		});
		// remove all client events from that source
		cache = $.grep(cache, function(e) {
			return !isSourcesEqual(e.source, source);
		});
		reportEvents(cache);
	}

	/* Manipulation
	-----------------------------------------------------------------------------*/

	function updateEvent(event) { // update an existing event
		var i, len = cache.length, e,
			defaultEventEnd = getView().defaultEventEnd, // getView???
			startDelta = event.start - event._start,
			endDelta = event.end ?
				(event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
				: 0;                                                      // was null and event was just resized
		for (i=0; i<len; i++) {
			e = cache[i];
			if (e._id == event._id && e != event) {
				e.start = new Date(+e.start + startDelta);
				if (event.end) {
					if (e.end) {
						e.end = new Date(+e.end + endDelta);
					}else{
						e.end = new Date(+defaultEventEnd(e) + endDelta);
					}
				}else{
					e.end = null;
				}
				e.title = event.title;
				e.url = event.url;
				e.allDay = event.allDay;
				e.className = event.className;
				e.editable = event.editable;
				e.color = event.color;
				e.backgroudColor = event.backgroudColor;
				e.borderColor = event.borderColor;
				e.textColor = event.textColor;
				normalizeEvent(e);
			}
		}
		normalizeEvent(event);
		reportEvents(cache);
	}

	function renderEvent(event, stick) {
		normalizeEvent(event);
		if (!event.source) {
			if (stick) {
				stickySource.events.push(event);
				event.source = stickySource;
			}
			cache.push(event);
		}
		reportEvents(cache);
	}

	function removeEvents(filter) {
		if (!filter) { // remove all
			cache = [];
			// clear all array sources
			for (var i=0; i<sources.length; i++) {
				if ($.isArray(sources[i].events)) {
					sources[i].events = [];
				}
			}
		}else{
			if (!$.isFunction(filter)) { // an event ID
				var id = filter + '';
				filter = function(e) {
					return e._id == id;
				};
			}
			cache = $.grep(cache, filter, true);
			// remove events from array sources
			for (var i=0; i<sources.length; i++) {
				if ($.isArray(sources[i].events)) {
					sources[i].events = $.grep(sources[i].events, filter, true);
				}
			}
		}
		reportEvents(cache);
	}

	function clientEvents(filter) {
		if ($.isFunction(filter)) {
			return $.grep(cache, filter);
		}
		else if (filter) { // an event ID
			filter += '';
			return $.grep(cache, function(e) {
				return e._id == filter;
			});
		}
		return cache; // else, return all
	}

	/* Loading State
	-----------------------------------------------------------------------------*/

	function pushLoading() {
		if (!loadingLevel++) {
			trigger('loading', null, true);
		}
	}

	function popLoading() {
		if (!--loadingLevel) {
			trigger('loading', null, false);
		}
	}

	/* Event Normalization
	-----------------------------------------------------------------------------*/

	function normalizeEvent(event) {
		var source = event.source || {};
		var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
		event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
		if (event.date) {
			if (!event.start) {
				event.start = event.date;
			}
			delete event.date;
		}
		event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
		event.end = parseDate(event.end, ignoreTimezone);
		if (event.end && event.end <= event.start) {
			event.end = null;
		}
		event._end = event.end ? cloneDate(event.end) : null;
		if (event.allDay === undefined) {
			event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
		}
		if (event.className) {
			if (typeof event.className == 'string') {
				event.className = event.className.split(/\s+/);
			}
		}else{
			event.className = [];
		}
		// TODO: if there is no start date, return false to indicate an invalid event
	}

	/* Utils
	------------------------------------------------------------------------------*/

	function normalizeSource(source) {
		if (source.className) {
			// TODO: repeat code, same code for event classNames
			if (typeof source.className == 'string') {
				source.className = source.className.split(/\s+/);
			}
		}else{
			source.className = [];
		}
		var normalizers = fc.sourceNormalizers;
		for (var i=0; i<normalizers.length; i++) {
			normalizers[i](source);
		}
	}

	function isSourcesEqual(source1, source2) {
		return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
	}

	function getSourcePrimitive(source) {
		return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
	}

}
<|endoftext|>


<commit_before>import React from 'react';
import autoBind from 'react-autobind';
import {bem} from 'js/bem';
import {stores} from 'js/stores';
import DocumentTitle from 'react-document-title';
import ProjectExportsCreator from 'js/components/projectDownloads/projectExportsCreator';
import ProjectExportsList from 'js/components/projectDownloads/projectExportsList';
import LegacyExports from 'js/components/projectDownloads/legacyExports';
import ExportTypeSelector from 'js/components/projectDownloads/exportTypeSelector';
import exportsStore from 'js/components/projectDownloads/exportsStore';

/**
 * @prop {object} asset
 */
export default class ProjectDownloads extends React.Component {
  constructor(props){
    super(props);
    this.state = {selectedExportType: exportsStore.getExportType()};
    this.unlisteners = [];
    autoBind(this);
  }

  componentDidMount() {
    this.unlisteners.push(
      exportsStore.listen(this.onExportsStoreChange),
    );
  }

  componentWillUnmount() {
    this.unlisteners.forEach((clb) => {clb();});
  }

  onExportsStoreChange() {
    this.setState({selectedExportType: exportsStore.getExportType()});
  }

  renderLegacy() {
    return (
      <React.Fragment>
        <LegacyExports asset={this.props.asset} />
      </React.Fragment>
    );
  }

  renderNonLegacy() {
    if (stores.session.isLoggedIn) {
      return (
        <React.Fragment>
          <ProjectExportsCreator asset={this.props.asset} />
          <ProjectExportsList asset={this.props.asset} hideWhenEmpty/>
        </React.Fragment>
      );
    } else {
      return (
        <React.Fragment>
          <bem.ProjectDownloads__selectorRow>
            <ExportTypeSelector/>
          </bem.ProjectDownloads__selectorRow>
          <ProjectExportsList asset={this.props.asset}/>
        </React.Fragment>
      );
    }
  }

  render() {
    var docTitle = this.props.asset.name || t('Untitled');
    return (
      <DocumentTitle title={`${docTitle} | KoboToolbox`}>
        <bem.FormView className='project-downloads'>
          <bem.FormView__row>
            <bem.FormView__cell m={['page-title']}>
              {t('Downloads')}
            </bem.FormView__cell>

            {this.state.selectedExportType.isLegacy &&
              this.renderLegacy()
            }

            {!this.state.selectedExportType.isLegacy &&
              this.renderNonLegacy()
            }
          </bem.FormView__row>
        </bem.FormView>
      </DocumentTitle>
    );
  }
}
<commit_msg>add white space around selector for consistency<commit_after>import React from 'react';
import autoBind from 'react-autobind';
import {bem} from 'js/bem';
import {stores} from 'js/stores';
import DocumentTitle from 'react-document-title';
import ProjectExportsCreator from 'js/components/projectDownloads/projectExportsCreator';
import ProjectExportsList from 'js/components/projectDownloads/projectExportsList';
import LegacyExports from 'js/components/projectDownloads/legacyExports';
import ExportTypeSelector from 'js/components/projectDownloads/exportTypeSelector';
import exportsStore from 'js/components/projectDownloads/exportsStore';

/**
 * @prop {object} asset
 */
export default class ProjectDownloads extends React.Component {
  constructor(props){
    super(props);
    this.state = {selectedExportType: exportsStore.getExportType()};
    this.unlisteners = [];
    autoBind(this);
  }

  componentDidMount() {
    this.unlisteners.push(
      exportsStore.listen(this.onExportsStoreChange),
    );
  }

  componentWillUnmount() {
    this.unlisteners.forEach((clb) => {clb();});
  }

  onExportsStoreChange() {
    this.setState({selectedExportType: exportsStore.getExportType()});
  }

  renderLegacy() {
    return (
      <React.Fragment>
        <LegacyExports asset={this.props.asset} />
      </React.Fragment>
    );
  }

  renderNonLegacy() {
    if (stores.session.isLoggedIn) {
      return (
        <React.Fragment>
          <ProjectExportsCreator asset={this.props.asset} />
          <ProjectExportsList asset={this.props.asset} hideWhenEmpty/>
        </React.Fragment>
      );
    } else {
      return (
        <React.Fragment>
          <bem.FormView__cell m={['box', 'padding']}>
            <bem.ProjectDownloads__selectorRow>
              <ExportTypeSelector/>
            </bem.ProjectDownloads__selectorRow>
          </bem.FormView__cell>
          <ProjectExportsList asset={this.props.asset}/>
        </React.Fragment>
      );
    }
  }

  render() {
    var docTitle = this.props.asset.name || t('Untitled');
    return (
      <DocumentTitle title={`${docTitle} | KoboToolbox`}>
        <bem.FormView className='project-downloads'>
          <bem.FormView__row>
            <bem.FormView__cell m={['page-title']}>
              {t('Downloads')}
            </bem.FormView__cell>

            {this.state.selectedExportType.isLegacy &&
              this.renderLegacy()
            }

            {!this.state.selectedExportType.isLegacy &&
              this.renderNonLegacy()
            }
          </bem.FormView__row>
        </bem.FormView>
      </DocumentTitle>
    );
  }
}
<|endoftext|>


<commit_before>
Elm.Native.Basics = {};
Elm.Native.Basics.make = function(elm) {
  elm.Native = elm.Native || {};
  elm.Native.Basics = elm.Native.Basics || {};
  if (elm.Native.Basics.values) return elm.Native.Basics.values;

  var Utils = Elm.Native.Utils.make(elm);

  function div(a, b) {
      return (a/b)|0;
  }
  function rem(a, b) {
      return a % b;
  }
  function mod(a, b) {
        if (b === 0) {
            throw new Error("Cannot perform mod 0. Division by zero error.");
        }
        var r = a % b;
        var m = a === 0 ? 0 : (b > 0 ? (a >= 0 ? r : r+b) : -mod(-a,-b));

        return m === b ? 0 : m;
  }
  function logBase(base, n) {
      return Math.log(n) / Math.log(base);
  }
  function negate(n) {
      return -n;
  }
  function abs(n) {
      return n < 0 ? -n : n;
  }

  function min(a, b) {
      return Utils.cmp(a,b) < 0 ? a : b;
  }
  function max(a, b) {
      return Utils.cmp(a,b) > 0 ? a : b;
  }
  function clamp(lo, hi, n) {
      return Utils.cmp(n,lo) < 0 ? lo : Utils.cmp(n,hi) > 0 ? hi : n;
  }

  function xor(a, b) {
      return a !== b;
  }
  function not(b) {
      return !b;
  }
  function isInfinite(n) {
      return n === Infinity || n === -Infinity
  }

  function truncate(n) {
      return n|0;
  }

  function degrees(d) {
      return d * Math.PI / 180;
  }
  function turns(t) {
      return 2 * Math.PI * t;
  }
  function fromPolar(point) {
      var r = point._0;
      var t = point._1;
      return Utils.Tuple2(r * Math.cos(t), r * Math.sin(t));
  }
  function toPolar(point) {
      var x = point._0;
      var y = point._1;
      return Utils.Tuple2(Math.sqrt(x * x + y * y), Math.atan2(y,x));
  }

  function append(xs,ys) {
      // append Text
      if (xs.text || ys.text) {
          return Utils.txt(Utils.makeText(xs) + Utils.makeText(ys));
      }

      // append Strings
      if (typeof xs === "string") {
          return xs + ys;
      }

      // append Lists
      if (xs.ctor === '[]') { return ys; }
      var root = Cons(xs._0, Nil);
      var curr = root;
      xs = xs._1;
      while (xs.ctor !== '[]') {
          curr._1 = Cons(xs._0, Nil);
          xs = xs._1;
          curr = curr._1;
      }
      curr._1 = ys;
      return root;
  }

  var basics = {
      div: F2(div),
      rem: F2(rem),
      mod: F2(mod),

      pi: Math.PI,
      e: Math.E,
      cos: Math.cos,
      sin: Math.sin,
      tan: Math.tan,
      acos: Math.acos,
      asin: Math.asin,
      atan: Math.atan,
      atan2: F2(Math.atan2),

      degrees:  degrees,
      turns:  turns,
      fromPolar:  fromPolar,
      toPolar:  toPolar,

      sqrt: Math.sqrt,
      logBase: F2(logBase),
      negate: negate,
      abs: abs,
      min: F2(min),
      max: F2(max),
      clamp: F3(clamp),
      compare: Utils.compare,

      xor: F2(xor),
      not: not,

      truncate: truncate,
      ceiling: Math.ceil,
      floor: Math.floor,
      round: Math.round,
      toFloat: function(x) { return x; },
      isNaN: isNaN,
      isInfinite: isInfinite,

      append: F2(append)
  };

  return elm.Native.Basics.values = basics;
};
<commit_msg>Remove use of Cons and Nil<commit_after>
Elm.Native.Basics = {};
Elm.Native.Basics.make = function(elm) {
  elm.Native = elm.Native || {};
  elm.Native.Basics = elm.Native.Basics || {};
  if (elm.Native.Basics.values) return elm.Native.Basics.values;

  var Utils = Elm.Native.Utils.make(elm);

  function div(a, b) {
      return (a/b)|0;
  }
  function rem(a, b) {
      return a % b;
  }
  function mod(a, b) {
        if (b === 0) {
            throw new Error("Cannot perform mod 0. Division by zero error.");
        }
        var r = a % b;
        var m = a === 0 ? 0 : (b > 0 ? (a >= 0 ? r : r+b) : -mod(-a,-b));

        return m === b ? 0 : m;
  }
  function logBase(base, n) {
      return Math.log(n) / Math.log(base);
  }
  function negate(n) {
      return -n;
  }
  function abs(n) {
      return n < 0 ? -n : n;
  }

  function min(a, b) {
      return Utils.cmp(a,b) < 0 ? a : b;
  }
  function max(a, b) {
      return Utils.cmp(a,b) > 0 ? a : b;
  }
  function clamp(lo, hi, n) {
      return Utils.cmp(n,lo) < 0 ? lo : Utils.cmp(n,hi) > 0 ? hi : n;
  }

  function xor(a, b) {
      return a !== b;
  }
  function not(b) {
      return !b;
  }
  function isInfinite(n) {
      return n === Infinity || n === -Infinity
  }

  function truncate(n) {
      return n|0;
  }

  function degrees(d) {
      return d * Math.PI / 180;
  }
  function turns(t) {
      return 2 * Math.PI * t;
  }
  function fromPolar(point) {
      var r = point._0;
      var t = point._1;
      return Utils.Tuple2(r * Math.cos(t), r * Math.sin(t));
  }
  function toPolar(point) {
      var x = point._0;
      var y = point._1;
      return Utils.Tuple2(Math.sqrt(x * x + y * y), Math.atan2(y,x));
  }

  function append(xs,ys) {
      // append Text
      if (xs.text || ys.text) {
          return Utils.txt(Utils.makeText(xs) + Utils.makeText(ys));
      }

      // append Strings
      if (typeof xs === "string") {
          return xs + ys;
      }

      // append Lists
      if (xs.ctor === '[]') {
          return ys;
      }
      var root = Cons(xs._0, Nil);
      var curr = root;
      xs = xs._1;
      while (xs.ctor !== '[]') {
          curr._1 = {
              ctor: 'Cons',
              _0: xs._0,
              _1: { ctor: 'Nil' }
          };
          xs = xs._1;
          curr = curr._1;
      }
      curr._1 = ys;
      return root;
  }

  var basics = {
      div: F2(div),
      rem: F2(rem),
      mod: F2(mod),

      pi: Math.PI,
      e: Math.E,
      cos: Math.cos,
      sin: Math.sin,
      tan: Math.tan,
      acos: Math.acos,
      asin: Math.asin,
      atan: Math.atan,
      atan2: F2(Math.atan2),

      degrees:  degrees,
      turns:  turns,
      fromPolar:  fromPolar,
      toPolar:  toPolar,

      sqrt: Math.sqrt,
      logBase: F2(logBase),
      negate: negate,
      abs: abs,
      min: F2(min),
      max: F2(max),
      clamp: F3(clamp),
      compare: Utils.compare,

      xor: F2(xor),
      not: not,

      truncate: truncate,
      ceiling: Math.ceil,
      floor: Math.floor,
      round: Math.round,
      toFloat: function(x) { return x; },
      isNaN: isNaN,
      isInfinite: isInfinite,

      append: F2(append)
  };

  return elm.Native.Basics.values = basics;
};
<|endoftext|>


<commit_before>'use strict';

var downsize = require('downsize');
var React = require('react');
var config = require('../shared/config');
var HtmlComponent = React.createFactory(require('../content/themes/'+ config.theme+ '/Html'));
var marked = require('marked');
var Promise = require('es6-promise').Promise; // jshint ignore:line

module.exports = {
  detectEnvironment: function() {
    return process.env.NODE_ENV || 'development';
  },
  excerpt: function(md) {
    var res;
    res = md.replace(/<\/?[^>]+>/gi, '');
    res = res.replace(/(\r\n|\n|\r)+/gm, ' ');
    return downsize(res, {
      'words': 50
    });
  },
  render: function(res, markup, state) {
    state = state || res.locals.state;
    var html = React.renderToStaticMarkup(HtmlComponent({ //jshint ignore:line
      state: state,
      markup: markup,
      context: res.locals.context.getComponentContext()
    }));
    res.set({
      'content-type': 'text/html; charset=utf-8'
    });
    res.write('<!doctype>' + html);
    res.end();
  },
  md2html: function(md){
    return new Promise(function(resolve, reject) {
      marked(md, function (err, content) {
        if (err) {
          reject(err);
        }else{
          resolve(content);
        }
      });
    });
  }
};
<commit_msg>Fix DOCTYPE<commit_after>'use strict';

var downsize = require('downsize');
var React = require('react');
var config = require('../shared/config');
var HtmlComponent = React.createFactory(require('../content/themes/'+ config.theme+ '/Html'));
var marked = require('marked');
var Promise = require('es6-promise').Promise; // jshint ignore:line

module.exports = {
  detectEnvironment: function() {
    return process.env.NODE_ENV || 'development';
  },
  excerpt: function(md) {
    var res;
    res = md.replace(/<\/?[^>]+>/gi, '');
    res = res.replace(/(\r\n|\n|\r)+/gm, ' ');
    return downsize(res, {
      'words': 50
    });
  },
  render: function(res, markup, state) {
    state = state || res.locals.state;
    var html = React.renderToStaticMarkup(HtmlComponent({ //jshint ignore:line
      state: state,
      markup: markup,
      context: res.locals.context.getComponentContext()
    }));
    res.set({
      'content-type': 'text/html; charset=utf-8'
    });
    res.write('<!DOCTYPE html>' + html);
    res.end();
  },
  md2html: function(md){
    return new Promise(function(resolve, reject) {
      marked(md, function (err, content) {
        if (err) {
          reject(err);
        }else{
          resolve(content);
        }
      });
    });
  }
};
<|endoftext|>


<commit_before>/**

  Utils.js

  Various helper functions.

  • getRegExpMatches
  • isIndexFile
  • isProduction
  • makePathAbsolute
  • makeRoutes
  • mapFiles
  • readDirectory
  • readFileHead
  • sequence
  • uniqueExtensions
  • watch

**/

//
//  Dependencies
//

var fs       = require("fs"),
    url      = require("url"),
    path     = require("path"),
    chokidar = require("chokidar"),
    Promise  = require("bluebird"),
    reorg    = require("reorg"),
    zlib     = require("zlib"),
    Utils;

module.exports = Utils = {};

/**

  Makes a path absolute (if not already)

  @param {String} p Path
  @returns {String} Absolute path version of a string

**/

Utils.makePathAbsolute = function(p) {

  if (path.isAbsolute(p))
    return p;
  else
    return path.resolve(process.cwd(), p || "");

};

/**

  Tests to see if a file is of form "index*"

  @param {Stats} file File as returned by fs.lstat
  @returns {Boolean} true if a file is index-like

*/

Utils.isIndexFile = function(file) {
  return /^index[^\/]*$/i.test(file.name);
};

/**

  Retrieves all unique file extensions from a directory

  @param {String} p Directory path
  @returns {Array} Array of strings of extensions

**/

Utils.uniqueExtensions = function(p) {
  var exts = {};
  Utils.readDirectory(p).forEach(function(file) {
    if (file.ext && "" !== file.ext) exts[file.ext.toLowerCase()] = 1;
  });
  return Object.keys(exts);
};

/**

  Finds index files and marks as "bundles", then removes sibling and
  sub-diretories

  @param {String} p Absolute path
  @param {Array} files Array of file stats

**/

Utils.flagBundles = function(p, files, ext) {

  var indices = [],
      i, j, n;

  // Filter out index files (outside of master directory)
  i = files.length;
  while (i-- > 0)
    if ((p !== files[i].dir) && Utils.isIndexFile(files[i]))
      indices.push(files.splice(i, 1)[0]);

  // Remove indices that are subdirectories of each other
  i = indices.length;
  while (i-- > 0)
    for (j = 0, n = indices.length; j < n; j++)
      if (i !== j && 0 === indices[i].path.indexOf(indices[j].dir)) {
        indices.splice(i, 1);
        break;
      }

  // Remove files in subdirectories of indices
  i = files.length;
  while (i-- > 0)
    for (j = 0, n = indices.length; j < n; j++)
      if (0 === files[i].path.indexOf(indices[j].dir)) {
        files.splice(i, 1);
        break;
      }

  // Denote indices as bundles
  indices.forEach(function(file) {
    file.bundle = true;
    file.ignored = [
      path.join(file.dir, "*"),
      file.dir + "." + ext
    ];
  });

  // Rejoin indices back into files
  return files.concat(indices);

};

/**

  Sorts and preprocesses files in a directory. Adds absolute and relative paths.

  @param {String} p Path to map
  @param {Object} [options.recursive] If true, processes recursively
  @param {Boolean} [options.bundleFolders] If true, automatically bundles appropriate folders
  @param {String} [options.type] The file type of the bundles (e.g. js)

**/

Utils.mapFiles = function(p, options) {

  var files, i, indices, results = [];
  options = options || {};

  // Make path absolute before call (for comparison after)
  p = Utils.makePathAbsolute(p);
  files = Utils.readDirectory(p, options.recursive, p);

  // Sort in reverse order so that directory calls come last
  files.sort(function(a, b) { return (a.path < b.path) ? -1 : (a.path > b.path) ? 1 : 0; }).reverse();

  // Move "index" to end so that routes are from specific to unspecific
  i = files.length;
  while (i-- > 0)
    if (Utils.isIndexFile(files[i]))
      Array.prototype.push.apply(files, files.splice(i, 1));

  // Remove subdirectory files if index.* is encountered
  if (options.bundleFolders) files = Utils.flagBundles(p, files, options.type);

  return files;

};

/**

  Parses a file name and adds additional information

**/

Utils.parse = function(p, base) {

  var parsed;

  p = Utils.makePathAbsolute(p);
  parsed = path.parse(p);

  // Get rid of leading dot for an extension
  parsed.ext = parsed.ext.replace(/^\./, "");
  parsed.path = p;

  if (base) {
    base = Utils.makePathAbsolute(base);
    parsed.basePath = base;
    parsed.folder = path.relative(base, parsed.path.replace(new RegExp(parsed.base + "$"), ""));
  }

  return parsed;

};

/**

  Creates appropriate routes for a file: for example, /about/index.html needs
  /about/, /about/index, and /about/index.html.

  Files named "template" are special cases and serve as wildcard route handlers.

  @param {String} baseRoute Base route from which to build
  @param {Stat} file File object (as returned from fs.lStat)
  @param {Object} options Options
  @param {String} [options.type] Default file type (e.g. js for coffee)
  @returns {Array} Array of route strings

**/

Utils.makeRoutes = function(baseRoute, file, options) {

  var re = /\/\/+$/,
      route,
      routes,
      ext;

  options = options || {};

  // By defaulting to file type, we address templating and transpiling. We can
  // resolve coffee files to js, etc. We also remove leading dot.
  ext = options.type || file.ext || "";
  ext = ext.replace(/^\./, "");

  // Create the file's route by appending the folder to the base route. This
  // process is prone to double "//", which must be removed.
  route = (url.resolve((baseRoute + "/").replace(re, "/"), file.folder) + "/").replace(re, "/");

  // HTML-like files named "template" pull their route from the parent directory
  // and pass the subsequent URL on as a "query" parameter. Note that $ are not
  // allowed in params.
  if (Utils.isTemplate(file, ext)) {
    route = route.split("/").slice(0, -1).join("/");
    return [route + "/" + (options.params || ":slug").replace(/^\//, "")];
  }

  // Add file and default extension
  routes = [url.resolve(route, file.name + "." + ext)];

  // HTML files are special: not only can they be accessed sans extension, if
  // they are named "index", we allow access via a folder.
  if ("html" === ext) {
    routes.push(url.resolve(route, file.name));
    if (Utils.isIndexFile(file)) routes.push(route);
  }

  return routes;

};

/**

  Reads all files in a directory

  @param {String} p Path to read
  @param {Boolean} recursive Whether to read recursively (default: true)
  @param [String] base Optional base path from which to parse

**/

Utils.readDirectory = function(p, recursive, base) {

  var results = [];

  // Set defaults
  if ("undefined" == typeof recursive) recursive = true;
  p = Utils.makePathAbsolute(p);

  // If directory doesn't exist, return nothing
  try {
    fs.lstatSync(p);
  } catch (err) {
    return results;
  }

  // Read in files
  fs.readdirSync(p).forEach(function(file) {

    var stats, parsed;

    // Read file and stats
    file = path.join(p, file);
    stats = fs.lstatSync(file);

    // Recursively walk directory, or add file
    if (stats.isDirectory()) {
      if (recursive)
        results = results.concat(Utils.readDirectory(file, true, base));
    } else {
      results.push(Utils.parse(file, base || p));
    }

  });

  return results;

};

/**

 Reads a limited number of characters from a file.

 @param {String} path Path to file
 @param {Number} chars Number of chars (defaults to 500)
 @returns {String} First characters for a file

**/

Utils.readFileHead = function(path, chars) {

  var buffer = new Buffer(chars || 500),
      file   = fs.openSync(path, "r"),
      str,
      len;

  // Read in file
  len = fs.readSync(file, buffer, 0, chars, 0);

  // Convert to lines, and discard any fragments
  // (so that we can use template engines without through errors)
  str = buffer.slice(0, len).toString().split("\n");

  return str.slice(0, str.length - 1).join("\n");

};

/**

  Returns all regex matches for an expression

  @param {String} str String to search
  @param {RegExp} pattern Pattern to search with
  @param {Number} [idx] Search component to use
  @returns {Array} Array of strings with matches

**/

Utils.getRegExpMatches = function(str, pattern, idx) {

  var match,
      matches = [];

  // Clone the RE and add global flag (otherwise has endless loop)
  pattern = new RegExp(pattern);
  pattern.global = true;

  // Set default idx
  idx = idx || 0;

  while ((match = pattern.exec(str)))
    matches.push(match[idx]);

  return matches;

};

/**

  Watches a directory via chokidar

  @param {String} sourceDir The source directory (without wildcards)
  @param {String} sourceExt The source extension (defaults to "*")
  @param {Object} [options.chokidar] Options to pass to chokidar
  @param {Function} [options.*] Event handlers (file, compiled) where * is name of event

**/

Utils.watch = reorg(function(sourceDir, sourceExt, options) {

  var p;

  // Don't watch in production
  if (Utils.isProduction()) return;

  // For source extension, if not wildcard, convert to "*.ext"
  if ("*" !== sourceExt) sourceExt = "*." + sourceExt.replace(/^[\*\.]+/g, "");

  // Ensure we have chokidar options and that chokidar's cwd is root
  p = Utils.makePathAbsolute(path.join(sourceDir, "**", sourceExt));
  options.chokidar = options.chokidar || {};
  options.chokidar.cwd = path.parse(p).root;

  chokidar.watch(p, options.chokidar).on("all", function(event, file) {

    var parsed, compiled;

    if (options[event] || options.all) {

      // File is missing root directory
      file = path.join(options.chokidar.cwd, file);

      // Map file to compiled version
      if (options.type) {
        parsed = path.parse(file);
        compiled = path.join(parsed.root, parsed.dir, parsed.name + "." + options.type);
      }

      // Callback
      if (options[event]) options[event](file, compiled);
      if (options.all) options.all(file, compiled);

    }

  });

}, "string!", ["string", "*"], "object");

/**

  Determines whether environment is production

  @returns {Boolean} True if is a production environment

**/

Utils.isProduction = function() {
  return /^prod/i.test(process.env.NODE_ENV);
};

/**

  Runs a sequence of promises, similar to when.js

  @param {Array} tasks Array of "tasks" (functions that return promises)
  @param {*} initial Initial value to call task with

**/

Utils.sequence = function(tasks, initial) {
  return Promise.reduce(tasks || [], function(val, task) {
    return task(val);
  }, initial);
};

/**

  Tests whether a file is a template (HTML-like and named properly)

  @param {Stats} file File as returned by fs.lstat
  @param {String} type The type of file extension
  @returns {Boolean} Returns true is is a template

**/

Utils.isTemplate = function(file, type) {
  return ("template" === file.name) && Utils.isHTML(type);
};

/**

  Tests whether a type is HTML-like

  @param {String} type Type of file
  @returns {Boolean} Returns true if is HTML-like

**/

Utils.isHTML = function(type) {
  return /^\*?\.?html?$/i.test(type);
};

/**

  Tests that an input is a non-array object.

  @param {*} input The item to check
  @returns {Boolean} Returns true if is an object, but not an array

**/

Utils.isPlainObject = function(input) {
  return ("object" == typeof input) && !Array.isArray(input);
};

/**

  Tests for an array of type

  @param {*} input The item to check
  @param {String} type The requisite type
  @returns {Boolean} Returns true if is an array of strings

**/

Utils.isArrayOfType = function(input, type) {
  if (!Array.isArray(input)) return false;
  for (var i = 0, n = input.length; i < n; i++)
    if (type !== typeof input[i]) return false;
  return true;
};

/**

  Traverses a JSON-object and calls function on each end point.

**/

Utils.traverse = function(obj, fn) {

  var key, val;

  for (key in obj) {
    val = obj[key];
    if ("string" === typeof val)
      fn(obj, key, val);
    else if (Utils.isPlainObject(val))
      Utils.traverse(val, fn);
  }

};

/**

  Escapes regex. From lodash.com by John Dalton.

  @param {String} str The string to escape

**/

Utils.escapeRegex = function(str) {
  return str.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
};

/**

  Compresses a string.

  @param {String} method The compression method
  @param {String} str The string to compress
  @param {Boolean} maxCompress If true, maximum compression level
  @param {Function} next Callback of form (err, compressed)

**/

Utils.compress = function(method, str, level, next) {

  var zip = ("gzip" === method) ? zlib.gzip : zlib.deflate;

  level = level ? zlib.Z_BEST_COMPRESSION : zlib.Z_DEFAULT_COMPRESSION;
  zip(str, { level : level }, next);

};

/**

  Sends a compressed or uncompressed response.

  @param {Response} res The response
  @param {String} type The file type
  @param {Buffer} buf The buffer to send
  @param {String} enc The encoding to use
  @param {Number} [cache] Cache-control setting

**/

Utils.send = function(res, type, buf, enc, cache) {

  // File type
  res.type(type);

  // Cache control
  if (undefined !== cache)
    res.setHeader("Cache-Control", cache);

  // Raw versus compressed
  if ("identity" === enc || !enc) {
    res.send(buf);
  } else {
    res.setHeader("Content-Encoding", enc);
    res.removeHeader("Content-Length");
    res.send(buf);
  }

};
<commit_msg>Utils#useMiddleware: abstracts functionality out of server/index.js<commit_after>/**

  Utils.js

  Various helper functions.

  • getRegExpMatches
  • isIndexFile
  • isProduction
  • makePathAbsolute
  • makeRoutes
  • mapFiles
  • readDirectory
  • readFileHead
  • sequence
  • uniqueExtensions
  • watch

**/

//
//  Dependencies
//

var fs       = require("fs"),
    url      = require("url"),
    path     = require("path"),
    chokidar = require("chokidar"),
    Promise  = require("bluebird"),
    reorg    = require("reorg"),
    zlib     = require("zlib"),
    Utils;

module.exports = Utils = {};

/**

  Makes a path absolute (if not already)

  @param {String} p Path
  @returns {String} Absolute path version of a string

**/

Utils.makePathAbsolute = function(p) {

  if (path.isAbsolute(p))
    return p;
  else
    return path.resolve(process.cwd(), p || "");

};

/**

  Tests to see if a file is of form "index*"

  @param {Stats} file File as returned by fs.lstat
  @returns {Boolean} true if a file is index-like

*/

Utils.isIndexFile = function(file) {
  return /^index[^\/]*$/i.test(file.name);
};

/**

  Retrieves all unique file extensions from a directory

  @param {String} p Directory path
  @returns {Array} Array of strings of extensions

**/

Utils.uniqueExtensions = function(p) {
  var exts = {};
  Utils.readDirectory(p).forEach(function(file) {
    if (file.ext && "" !== file.ext) exts[file.ext.toLowerCase()] = 1;
  });
  return Object.keys(exts);
};

/**

  Finds index files and marks as "bundles", then removes sibling and
  sub-diretories

  @param {String} p Absolute path
  @param {Array} files Array of file stats

**/

Utils.flagBundles = function(p, files, ext) {

  var indices = [],
      i, j, n;

  // Filter out index files (outside of master directory)
  i = files.length;
  while (i-- > 0)
    if ((p !== files[i].dir) && Utils.isIndexFile(files[i]))
      indices.push(files.splice(i, 1)[0]);

  // Remove indices that are subdirectories of each other
  i = indices.length;
  while (i-- > 0)
    for (j = 0, n = indices.length; j < n; j++)
      if (i !== j && 0 === indices[i].path.indexOf(indices[j].dir)) {
        indices.splice(i, 1);
        break;
      }

  // Remove files in subdirectories of indices
  i = files.length;
  while (i-- > 0)
    for (j = 0, n = indices.length; j < n; j++)
      if (0 === files[i].path.indexOf(indices[j].dir)) {
        files.splice(i, 1);
        break;
      }

  // Denote indices as bundles
  indices.forEach(function(file) {
    file.bundle = true;
    file.ignored = [
      path.join(file.dir, "*"),
      file.dir + "." + ext
    ];
  });

  // Rejoin indices back into files
  return files.concat(indices);

};

/**

  Sorts and preprocesses files in a directory. Adds absolute and relative paths.

  @param {String} p Path to map
  @param {Object} [options.recursive] If true, processes recursively
  @param {Boolean} [options.bundleFolders] If true, automatically bundles appropriate folders
  @param {String} [options.type] The file type of the bundles (e.g. js)

**/

Utils.mapFiles = function(p, options) {

  var files, i, indices, results = [];
  options = options || {};

  // Make path absolute before call (for comparison after)
  p = Utils.makePathAbsolute(p);
  files = Utils.readDirectory(p, options.recursive, p);

  // Sort in reverse order so that directory calls come last
  files.sort(function(a, b) { return (a.path < b.path) ? -1 : (a.path > b.path) ? 1 : 0; }).reverse();

  // Move "index" to end so that routes are from specific to unspecific
  i = files.length;
  while (i-- > 0)
    if (Utils.isIndexFile(files[i]))
      Array.prototype.push.apply(files, files.splice(i, 1));

  // Remove subdirectory files if index.* is encountered
  if (options.bundleFolders) files = Utils.flagBundles(p, files, options.type);

  return files;

};

/**

  Parses a file name and adds additional information

**/

Utils.parse = function(p, base) {

  var parsed;

  p = Utils.makePathAbsolute(p);
  parsed = path.parse(p);

  // Get rid of leading dot for an extension
  parsed.ext = parsed.ext.replace(/^\./, "");
  parsed.path = p;

  if (base) {
    base = Utils.makePathAbsolute(base);
    parsed.basePath = base;
    parsed.folder = path.relative(base, parsed.path.replace(new RegExp(parsed.base + "$"), ""));
  }

  return parsed;

};

/**

  Creates appropriate routes for a file: for example, /about/index.html needs
  /about/, /about/index, and /about/index.html.

  Files named "template" are special cases and serve as wildcard route handlers.

  @param {String} baseRoute Base route from which to build
  @param {Stat} file File object (as returned from fs.lStat)
  @param {Object} options Options
  @param {String} [options.type] Default file type (e.g. js for coffee)
  @returns {Array} Array of route strings

**/

Utils.makeRoutes = function(baseRoute, file, options) {

  var re = /\/\/+$/,
      route,
      routes,
      ext;

  options = options || {};

  // By defaulting to file type, we address templating and transpiling. We can
  // resolve coffee files to js, etc. We also remove leading dot.
  ext = options.type || file.ext || "";
  ext = ext.replace(/^\./, "");

  // Create the file's route by appending the folder to the base route. This
  // process is prone to double "//", which must be removed.
  route = (url.resolve((baseRoute + "/").replace(re, "/"), file.folder) + "/").replace(re, "/");

  // HTML-like files named "template" pull their route from the parent directory
  // and pass the subsequent URL on as a "query" parameter. Note that $ are not
  // allowed in params.
  if (Utils.isTemplate(file, ext)) {
    route = route.split("/").slice(0, -1).join("/");
    return [route + "/" + (options.params || ":slug").replace(/^\//, "")];
  }

  // Add file and default extension
  routes = [url.resolve(route, file.name + "." + ext)];

  // HTML files are special: not only can they be accessed sans extension, if
  // they are named "index", we allow access via a folder.
  if ("html" === ext) {
    routes.push(url.resolve(route, file.name));
    if (Utils.isIndexFile(file)) routes.push(route);
  }

  return routes;

};

/**

  Reads all files in a directory

  @param {String} p Path to read
  @param {Boolean} recursive Whether to read recursively (default: true)
  @param [String] base Optional base path from which to parse

**/

Utils.readDirectory = function(p, recursive, base) {

  var results = [];

  // Set defaults
  if ("undefined" == typeof recursive) recursive = true;
  p = Utils.makePathAbsolute(p);

  // If directory doesn't exist, return nothing
  try {
    fs.lstatSync(p);
  } catch (err) {
    return results;
  }

  // Read in files
  fs.readdirSync(p).forEach(function(file) {

    var stats, parsed;

    // Read file and stats
    file = path.join(p, file);
    stats = fs.lstatSync(file);

    // Recursively walk directory, or add file
    if (stats.isDirectory()) {
      if (recursive)
        results = results.concat(Utils.readDirectory(file, true, base));
    } else {
      results.push(Utils.parse(file, base || p));
    }

  });

  return results;

};

/**

 Reads a limited number of characters from a file.

 @param {String} path Path to file
 @param {Number} chars Number of chars (defaults to 500)
 @returns {String} First characters for a file

**/

Utils.readFileHead = function(path, chars) {

  var buffer = new Buffer(chars || 500),
      file   = fs.openSync(path, "r"),
      str,
      len;

  // Read in file
  len = fs.readSync(file, buffer, 0, chars, 0);

  // Convert to lines, and discard any fragments
  // (so that we can use template engines without through errors)
  str = buffer.slice(0, len).toString().split("\n");

  return str.slice(0, str.length - 1).join("\n");

};

/**

  Returns all regex matches for an expression

  @param {String} str String to search
  @param {RegExp} pattern Pattern to search with
  @param {Number} [idx] Search component to use
  @returns {Array} Array of strings with matches

**/

Utils.getRegExpMatches = function(str, pattern, idx) {

  var match,
      matches = [];

  // Clone the RE and add global flag (otherwise has endless loop)
  pattern = new RegExp(pattern);
  pattern.global = true;

  // Set default idx
  idx = idx || 0;

  while ((match = pattern.exec(str)))
    matches.push(match[idx]);

  return matches;

};

/**

  Watches a directory via chokidar

  @param {String} sourceDir The source directory (without wildcards)
  @param {String} sourceExt The source extension (defaults to "*")
  @param {Object} [options.chokidar] Options to pass to chokidar
  @param {Function} [options.*] Event handlers (file, compiled) where * is name of event

**/

Utils.watch = reorg(function(sourceDir, sourceExt, options) {

  var p;

  // Don't watch in production
  if (Utils.isProduction()) return;

  // For source extension, if not wildcard, convert to "*.ext"
  if ("*" !== sourceExt) sourceExt = "*." + sourceExt.replace(/^[\*\.]+/g, "");

  // Ensure we have chokidar options and that chokidar's cwd is root
  p = Utils.makePathAbsolute(path.join(sourceDir, "**", sourceExt));
  options.chokidar = options.chokidar || {};
  options.chokidar.cwd = path.parse(p).root;

  chokidar.watch(p, options.chokidar).on("all", function(event, file) {

    var parsed, compiled;

    if (options[event] || options.all) {

      // File is missing root directory
      file = path.join(options.chokidar.cwd, file);

      // Map file to compiled version
      if (options.type) {
        parsed = path.parse(file);
        compiled = path.join(parsed.root, parsed.dir, parsed.name + "." + options.type);
      }

      // Callback
      if (options[event]) options[event](file, compiled);
      if (options.all) options.all(file, compiled);

    }

  });

}, "string!", ["string", "*"], "object");

/**

  Determines whether environment is production

  @returns {Boolean} True if is a production environment

**/

Utils.isProduction = function() {
  return /^prod/i.test(process.env.NODE_ENV);
};

/**

  Runs a sequence of promises, similar to when.js

  @param {Array} tasks Array of "tasks" (functions that return promises)
  @param {*} initial Initial value to call task with

**/

Utils.sequence = function(tasks, initial) {
  return Promise.reduce(tasks || [], function(val, task) {
    return task(val);
  }, initial);
};

/**

  Tests whether a file is a template (HTML-like and named properly)

  @param {Stats} file File as returned by fs.lstat
  @param {String} type The type of file extension
  @returns {Boolean} Returns true is is a template

**/

Utils.isTemplate = function(file, type) {
  return ("template" === file.name) && Utils.isHTML(type);
};

/**

  Tests whether a type is HTML-like

  @param {String} type Type of file
  @returns {Boolean} Returns true if is HTML-like

**/

Utils.isHTML = function(type) {
  return /^\*?\.?html?$/i.test(type);
};

/**

  Tests that an input is a non-array object.

  @param {*} input The item to check
  @returns {Boolean} Returns true if is an object, but not an array

**/

Utils.isPlainObject = function(input) {
  return ("object" == typeof input) && !Array.isArray(input);
};

/**

  Tests for an array of type

  @param {*} input The item to check
  @param {String} type The requisite type
  @returns {Boolean} Returns true if is an array of strings

**/

Utils.isArrayOfType = function(input, type) {
  if (!Array.isArray(input)) return false;
  for (var i = 0, n = input.length; i < n; i++)
    if (type !== typeof input[i]) return false;
  return true;
};

/**

  Traverses a JSON-object and calls function on each end point.

**/

Utils.traverse = function(obj, fn) {

  var key, val;

  for (key in obj) {
    val = obj[key];
    if ("string" === typeof val)
      fn(obj, key, val);
    else if (Utils.isPlainObject(val))
      Utils.traverse(val, fn);
  }

};

/**

  Escapes regex. From lodash.com by John Dalton.

  @param {String} str The string to escape

**/

Utils.escapeRegex = function(str) {
  return str.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
};

/**

  Compresses a string.

  @param {String} method The compression method
  @param {String} str The string to compress
  @param {Boolean} maxCompress If true, maximum compression level
  @param {Function} next Callback of form (err, compressed)

**/

Utils.compress = function(method, str, level, next) {

  var zip = ("gzip" === method) ? zlib.gzip : zlib.deflate;

  level = level ? zlib.Z_BEST_COMPRESSION : zlib.Z_DEFAULT_COMPRESSION;
  zip(str, { level : level }, next);

};

/**

  Sends a compressed or uncompressed response.

  @param {Response} res The response
  @param {String} type The file type
  @param {Buffer} buf The buffer to send
  @param {String} enc The encoding to use
  @param {Number} [cache] Cache-control setting

**/

Utils.send = function(res, type, buf, enc, cache) {

  // File type
  res.type(type);

  // Cache control
  if (undefined !== cache)
    res.setHeader("Cache-Control", cache);

  // Raw versus compressed
  if ("identity" === enc || !enc) {
    res.send(buf);
  } else {
    res.setHeader("Content-Encoding", enc);
    res.removeHeader("Content-Length");
    res.send(buf);
  }

};

/**

  Attaches middleware to server, or ignores if blank.

  @param {Express} server Server instance
  @param {Middleware} library (req, res)-style middleware
  @param {Boolean} [skipCheck] If true, ignores errors (defaults to false)

**/

Utils.useMiddleware = function(server, library, skipCheck) {
  if (!library || Array.isArray(library) && !library.length) {
    if (skipCheck) return;
    return new Error("No middleware added");
  }
  server.use(library);
};
<|endoftext|>


<commit_before>describe('Visualization', function () {

  var
    H = humblevis;

  function MockChild () {
    this.render = function (element) {
      this.container = element;
    }
  }

  function cleanup (div) {
    return function () {
      document.body.removeChild(div)
    };
  }

  it('defines visualization', function () {
    expect(H.Visualization).toBeDefined();
  });

  it('creates a visualization', function () {
    var
      vis = new H.Visualization();
    expect(vis).toBeDefined();
  });

  it('renders', function () {

    var
      vis = new H.Visualization(),
      div = document.createElement('div');

    document.body.appendChild(div);
    vis.render(div);
    expect($(div)).toContain('.humble-vis-visualization');

    this.after(cleanup(div));
  });

  it('renders inside a configured element', function () {

    var
      div = document.createElement('div'),
      vis = new H.Visualization({element : div});

    vis.render();
    expect($(div)).toContain('.humble-vis-visualization');
  });

  it('has children member', function () {
    var
      vis = new H.Visualization();
    expect(vis.children).toBeDefined();
    expect(vis.children.length).toEqual(0);
  });

  it('adds a child', function () {
    var
      vis = new H.Visualization(),
      child = new MockChild();
    vis.add(child);
    expect(vis.children).toContain(child);
  });

  it('adds children', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild();

    vis.add(a);
    vis.add(b);

    expect(vis.children).toContain(a);
    expect(vis.children).toContain(b);
  });

  it('gets a childs index', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild();

    vis.add(a);
    vis.add(b);

    expect(vis.indexOf(a)).toBe(0);
    expect(vis.indexOf(b)).toBe(1);
  });

  it('reorders children', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      c = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.add(c);

    expect(vis.setPosition(c, 0)).toBe(true);
    expect(vis.indexOf(c)).toBe(0);
    expect(vis.indexOf(a)).toBe(1);
    expect(vis.indexOf(b)).toBe(2);
  });

  it('fails to reorder when index out of bounds', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      c = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.add(c);

    expect(vis.setPosition(c, 3)).toBeFalsy();
    expect(vis.indexOf(a)).toBe(0);
    expect(vis.indexOf(b)).toBe(1);
    expect(vis.indexOf(c)).toBe(2);
  });

  it('removes a child', function () {
    var
      vis = new H.Visualization(),
      child = new MockChild();
    vis.remove(child);
    expect(vis.children).not.toContain(child);
  });

  it('renders a child', function () {
    var
      vis = new H.Visualization(),
      child = new MockChild(),
      div = document.createElement('div');

    document.body.appendChild(div);
    vis.add(child);
    vis.render(div);

    expect($(div)).toContain('.humble-vis-child-container.humble-vis-first.humble-vis-last');

    this.after(cleanup(div));
  });

  it('renders children', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      c = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.add(c);
    vis.render(div);

    expect($div.find('.humble-vis-child-container').size()).toBe(3);
    expect($div).toContain('.humble-vis-child-container.humble-vis-first');
    expect($(a.container)).toBe('.humble-vis-child-container.humble-vis-first');
    expect($div).toContain('.humble-vis-child-container.humble-vis-last');
    expect($(b.container)).not.toHaveClass('humble-vis-first');
    expect($(b.container)).not.toHaveClass('humble-vis-last');
    expect($(c.container)).toBe('.humble-vis-child-container.humble-vis-last');
    expect($div.find('.humble-vis-first')).not.toBe($div.find('.humble-vis-last'));

    this.after(cleanup(div));
  });

  it('adds new children rendering dynamically', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.render(div);
    vis.add(a);
    expect($div).toContain('.humble-vis-child-container.humble-vis-first.humble-vis-last');

    vis.add(b);
    expect($div).toContain('.humble-vis-child-container.humble-vis-first');
    expect($div).toContain('.humble-vis-child-container.humble-vis-last');
    expect($div.find('.humble-vis-first')).not.toBe($div.find('.humble-vis-last'));

    this.after(cleanup(div));
  });

  it('removes a rendered child', function () {
    var
      vis = new H.Visualization(),
      child = new MockChild(),
      div = document.createElement('div');

    document.body.appendChild(div);
    vis.add(child);
    vis.render(div);
    vis.remove(child);

    expect($(div)).not.toContain('.humble-vis-child-container');

    this.after(cleanup(div));
  });

  it('removes the first child', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.render(div);
    vis.remove(a);

    expect($div).toContain('.humble-vis-child-container.humble-vis-first.humble-vis-last');
    expect($(b.container)).toBe('.humble-vis-child-container.humble-vis-first.humble-vis-last');
    expect($(a.container)).not.toBe($(b.container));

    this.after(cleanup(div));
  });

  it('removes the last child', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.render(div);
    vis.remove(b);

    expect($div).toContain('.humble-vis-child-container.humble-vis-first.humble-vis-last');
    expect($(a.container)).toBe('.humble-vis-child-container.humble-vis-first.humble-vis-last');
    expect($(a.container)).not.toBe($(b.container));

    this.after(cleanup(div));
  });

  it('reorders rendered children', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      c = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.add(c);
    vis.render(div);

    vis.setPosition(c, 0);

    expect($(c.container)).toHaveClass('humble-vis-first');
    expect($(a.container)).not.toHaveClass('humble-vis-first');
    expect($(a.container)).not.toHaveClass('humble-vis-last');
    expect($(b.container)).toHaveClass('humble-vis-last');

    vis.setPosition(c, 2);
    expect($(c.container)).toHaveClass('humble-vis-last');
    expect($(c.container)).not.toHaveClass('humble-vis-first');

    this.after(cleanup(div));
  });
});

<commit_msg>Move class name / selector constants to top.<commit_after>describe('Visualization', function () {

  var
    CN_FIRST          = 'humble-vis-first',
    CN_LAST           = 'humble-vis-last',
    CN_VISUALIZATION  = 'humble-vis-visualization',
    CN_CONTAINER      = 'humble-vis-child-container',

    S_FIRST           = '.' + CN_FIRST,
    S_LAST            = '.' + CN_LAST,
    S_VISUALIZATION   = '.' + CN_VISUALIZATION,
    S_CONTAINER       = '.' + CN_CONTAINER,

    H = humblevis;

  function MockChild () {
    this.render = function (element) {
      this.container = element;
    }
  }

  function cleanup (div) {
    return function () {
      document.body.removeChild(div)
    };
  }

  it('defines visualization', function () {
    expect(H.Visualization).toBeDefined();
  });

  it('creates a visualization', function () {
    var
      vis = new H.Visualization();
    expect(vis).toBeDefined();
  });

  it('renders', function () {

    var
      vis = new H.Visualization(),
      div = document.createElement('div');

    document.body.appendChild(div);
    vis.render(div);
    expect($(div)).toContain(S_VISUALIZATION);

    this.after(cleanup(div));
  });

  it('renders inside a configured element', function () {

    var
      div = document.createElement('div'),
      vis = new H.Visualization({element : div});

    vis.render();
    expect($(div)).toContain(S_VISUALIZATION);
  });

  it('has children member', function () {
    var
      vis = new H.Visualization();
    expect(vis.children).toBeDefined();
    expect(vis.children.length).toEqual(0);
  });

  it('adds a child', function () {
    var
      vis = new H.Visualization(),
      child = new MockChild();
    vis.add(child);
    expect(vis.children).toContain(child);
  });

  it('adds children', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild();

    vis.add(a);
    vis.add(b);

    expect(vis.children).toContain(a);
    expect(vis.children).toContain(b);
  });

  it('gets a childs index', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild();

    vis.add(a);
    vis.add(b);

    expect(vis.indexOf(a)).toBe(0);
    expect(vis.indexOf(b)).toBe(1);
  });

  it('reorders children', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      c = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.add(c);

    expect(vis.setPosition(c, 0)).toBe(true);
    expect(vis.indexOf(c)).toBe(0);
    expect(vis.indexOf(a)).toBe(1);
    expect(vis.indexOf(b)).toBe(2);
  });

  it('fails to reorder when index out of bounds', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      c = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.add(c);

    expect(vis.setPosition(c, 3)).toBeFalsy();
    expect(vis.indexOf(a)).toBe(0);
    expect(vis.indexOf(b)).toBe(1);
    expect(vis.indexOf(c)).toBe(2);
  });

  it('removes a child', function () {
    var
      vis = new H.Visualization(),
      child = new MockChild();
    vis.remove(child);
    expect(vis.children).not.toContain(child);
  });

  it('renders a child', function () {
    var
      vis = new H.Visualization(),
      child = new MockChild(),
      div = document.createElement('div');

    document.body.appendChild(div);
    vis.add(child);
    vis.render(div);

    expect($(div)).toContain(S_CONTAINER + S_FIRST + S_LAST);

    this.after(cleanup(div));
  });

  it('renders children', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      c = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.add(c);
    vis.render(div);

    expect($div.find(S_CONTAINER).size()).toBe(3);
    expect($div).toContain(S_CONTAINER + S_FIRST);
    expect($(a.container)).toBe(S_CONTAINER + S_FIRST);
    expect($(b.container)).not.toHaveClass(CN_FIRST);
    expect($(b.container)).not.toHaveClass(CN_LAST);
    expect($div).toContain(S_CONTAINER + S_LAST);
    expect($(c.container)).toBe(S_CONTAINER + S_LAST);
    expect($div.find(S_FIRST)).not.toBe($div.find(S_LAST));

    this.after(cleanup(div));
  });

  it('adds new children rendering dynamically', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.render(div);
    vis.add(a);
    expect($div).toContain(S_CONTAINER + S_FIRST + S_LAST);

    vis.add(b);
    expect($div).toContain(S_CONTAINER + S_FIRST);
    expect($div).toContain(S_CONTAINER + S_LAST);
    expect($div.find(S_FIRST)).not.toBe($div.find(S_LAST));

    this.after(cleanup(div));
  });

  it('removes a rendered child', function () {
    var
      vis = new H.Visualization(),
      child = new MockChild(),
      div = document.createElement('div');

    document.body.appendChild(div);
    vis.add(child);
    vis.render(div);
    vis.remove(child);

    expect($(div)).not.toContain(S_CONTAINER);

    this.after(cleanup(div));
  });

  it('removes the first child', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.render(div);
    vis.remove(a);

    expect($div).toContain(S_CONTAINER + S_FIRST + S_LAST);
    expect($(b.container)).toBe(S_CONTAINER + S_FIRST + S_LAST);
    expect($(a.container)).not.toBe($(b.container));

    this.after(cleanup(div));
  });

  it('removes the last child', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.render(div);
    vis.remove(b);

    expect($div).toContain(S_CONTAINER + S_FIRST + S_LAST);
    expect($(a.container)).toBe(S_CONTAINER + S_FIRST + S_LAST);
    expect($(a.container)).not.toBe($(b.container));

    this.after(cleanup(div));
  });

  it('reorders rendered children', function () {
    var
      vis = new H.Visualization(),
      a = new MockChild(),
      b = new MockChild(),
      c = new MockChild(),
      div = document.createElement('div'),
      $div = $(div);

    document.body.appendChild(div);
    vis.add(a);
    vis.add(b);
    vis.add(c);
    vis.render(div);

    vis.setPosition(c, 0);

    expect($(c.container)).toHaveClass(CN_FIRST);
    expect($(a.container)).not.toHaveClass(CN_FIRST);
    expect($(a.container)).not.toHaveClass(CN_LAST);
    expect($(b.container)).toHaveClass(CN_LAST);

    vis.setPosition(c, 2);
    expect($(c.container)).toHaveClass(CN_LAST);
    expect($(c.container)).not.toHaveClass(CN_FIRST);

    this.after(cleanup(div));
  });
});

<|endoftext|>


<commit_before>#ifndef __browser_Browser__
#define __browser_Browser__

////////////////////////////////////////////////////////////////////////////////
// Browser

Browser = new function() {
	var name = window.orientation != undefined ? 'ipod'
			: (navigator.platform.match(/mac|win|linux|nix/i) || ['other'])[0].toLowerCase();
	var ret = {
		PLATFORM: name,
		XPATH: !!document.evaluate,
		QUERY: !!document.querySelector
	};
	var engines = {
		presto: function() {
			return !window.opera ? false : arguments.callee.caller ? 960 : document.getElementsByClassName ? 950 : 925;
		},

		trident: function() {
			var ver/*@cc_on=@_jscript_version@*/;
			return !ver ? false : ver >= 5 && ver < 5.5 ? 5 : ver == 5.5 ? 5.5 : ver * 10 - 50;
		},

		webkit: function() {
			return navigator.taintEnabled ? false : ret.XPATH ? ret.QUERY ? 525 : 420 : 419;
		},

		gecko: function() {
			return !document.getBoxObjectFor ? false : document.getElementsByClassName ? 19 : 18;
		}
	};
	for (var engine in engines) {
		var version = engines[engine]();
		if (version) {
			ret.ENGINE = engine;
			ret.VERSION = version;
			engine = engine.toUpperCase();
			ret[engine] = true;
			ret[(engine + version).replace(/\./g, '')] = true;
			break;
		}
	}
	// Add platform name directly in uppercase too
	ret[name.toUpperCase()] = true;
	return ret;
};

#ifdef BROWSER_LEGACY
if (!this.encodeURIComponent) {
	encodeURIComponent = escape;
	decodeURIComponent = unescape;
}
#endif // BROWSER_LEGACY

#endif // __browser_Browser__<commit_msg>Added Browser.log as a short-cut to window.console.log and alternatives.<commit_after>#ifndef __browser_Browser__
#define __browser_Browser__

////////////////////////////////////////////////////////////////////////////////
// Browser

Browser = new function() {
	var name = window.orientation != undefined ? 'ipod'
			: (navigator.platform.match(/mac|win|linux|nix/i) || ['other'])[0].toLowerCase();
	var fields = {
		PLATFORM: name,
		XPATH: !!document.evaluate,
		QUERY: !!document.querySelector
	};
	var engines = {
		presto: function() {
			return !window.opera ? false : arguments.callee.caller ? 960 : document.getElementsByClassName ? 950 : 925;
		},

		trident: function() {
			var ver/*@cc_on=@_jscript_version@*/;
			return !ver ? false : ver >= 5 && ver < 5.5 ? 5 : ver == 5.5 ? 5.5 : ver * 10 - 50;
		},

		webkit: function() {
			return navigator.taintEnabled ? false : fields.XPATH ? fields.QUERY ? 525 : 420 : 419;
		},

		gecko: function() {
			return !document.getBoxObjectFor ? false : document.getElementsByClassName ? 19 : 18;
		}
	};
	for (var engine in engines) {
		var version = engines[engine]();
		if (version) {
			fields.ENGINE = engine;
			fields.VERSION = version;
			engine = engine.toUpperCase();
			fields[engine] = true;
			fields[(engine + version).replace(/\./g, '')] = true;
			break;
		}
	}
	// Add platform name directly in uppercase too
	fields[name.toUpperCase()] = true;

	// Add console loggin on most browsers as good as we can.
	fields.log = function() {
		// IE does not seem to join with ' ' and has problems with apply
		if (!Browser.TRIDENT && window.console && console.log)
			console.log.apply(console, arguments);
		else
			(window.console && console.log
				|| window.opera && opera.postError
				|| alert)(Array.join(arguments, ' '));
	}

	return fields;
};

#ifdef BROWSER_LEGACY
if (!this.encodeURIComponent) {
	encodeURIComponent = escape;
	decodeURIComponent = unescape;
}
#endif // BROWSER_LEGACY

#endif // __browser_Browser__<|endoftext|>