<commit_before>
(function() {
'use strict';

Elm.Debugger = null;
Elm.debuggerInit = function(module) {
  return {
    make: function(runtime) {
      Elm.Debugger = debuggerInit(module, runtime);
      return Elm.Debugger.moduleRetValue;
    }
  };
};

function debuggerInit(module, runtime) {

  var programPaused = false;
  var allNodes = null;

  var recordedEvents = [];
  var eventCounter = 0;
  var asyncTimers = [];
  var initialProgramState = {
    nodeValues: [],
    asyncTimers: []
  };

  function wrapNotify(id, v) {
    var timestep = Date.now();

    if (programPaused) {
      // ignore notify because we are stepping
    }
    else {
      runtime.notify(id, v, timestep);
      recordEvent(id, v, timestep);
    }
  };

  function wrapRunDelayed(func, delayMs) {
    return safeSetTimeout(func, delayMs);
  };

  function safeSetTimeout(func, delayMs) {
    var timerId = setTimeout(func, delayMs);
    asyncTimers.push({ func:func, delayMs:delayMs, timerId:timerId });
    return timerId;
  }

  function recordEvent(id, v, timestep) {
    recordedEvents.push({ id:id, value:v, timestep:timestep });
  }

  function resetProgram() {
    clearAsyncEvents();
    restoreNodeValues(allNodes, initialProgramState.nodeValues);
    redrawGraphics();
  }

  function restartProgram() {
    resetProgram();
    recordedEvents = [];
    initialProgramState.asyncTimers.forEach(function(timer) {
      var func = timer.func;
      func();
    });
  }

  function stepTo(index) {
    if (!programPaused) {
      programPaused = true;
      resetProgram();
    }

    if (index < 0 || index >= recordedEvents.length) {
      throw "Index out of bounds: " + index;
    }

    if (index < eventCounter) {
      resetProgram();
      eventCounter = 0;
    }

    assert(index >= eventCounter, "index must be bad");
    while (eventCounter < index) {
      var nextEvent = recordedEvents[eventCounter];
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);

      eventCounter += 1;
    }
    assert(eventCounter == index, "while loop didnt' work");
  }

  function getMaxSteps() {
    return recordedEvents.length - 1;
  }

  function clearAsyncEvents() {
    asyncTimers.forEach(function(timer) {
      clearTimeout(timer.timerId);
    });
  }

  function doPlayback(eventList) {
    var x = eventList.shift();
    var time = x[2];
    runtime.notify(x[0], x[1], x[2]);

    if (eventList.length > 0) {
      var delta = eventList[0][2] - time;
      setTimeout(function() { doPlayback(eventList); }, delta);
    }
  }

  function redrawGraphics() {
    var graphicsFoldp = moduleRetValue.main.kids[0];
    graphicsFoldp.recv(Date.now(), true, moduleRetValue.main.id);
  }

  var wrappedRuntime = {
    notify: wrapNotify,
    runDelayed: wrapRunDelayed,
    node: runtime.node,
    display: runtime.display,
    id: runtime.id,
    addListener: runtime.addListener,
    inputs: runtime.inputs
  };

  var moduleRetValue = module.make(wrappedRuntime);

  var elmDebugger = {
        moduleRetValue: moduleRetValue,

        // debugger functions
        reset: resetProgram,
        restart: restartProgram,
        getMaxSteps: getMaxSteps,
        stepTo: stepTo
  };

  allNodes = flattenNodes(wrappedRuntime.inputs);
  initialProgramState.nodeValues = saveNodeValues(allNodes);
  initialProgramState.asyncTimers = asyncTimers.slice();

  return elmDebugger;
};

function assert(bool, msg) {
  if (!bool) {
    throw "Assertion error: " + msg;
  }
}

function saveNodeValues(allNodes) {
  var nodeValues = [];

  allNodes.forEach(function(node) {
    nodeValues.push({ value: node.value, id: node.id });
  });

  return nodeValues;
};

function restoreNodeValues(allNodes, nodeStates) {
  assert(allNodes.length == nodeStates.length, "saved program state has wrong length");
  for (var i=0; i < allNodes.length; i++) {
    var node = allNodes[i];
    var state = nodeStates[i];
    assert(node.id == state.id, "the nodes moved position");

    node.value = state.value;
  }
}

function flattenNodes(nodes) {
  var treeReduce = function(prev, cur, i, arr) {
    var flatKids = cur.kids.reduce(treeReduce, []);
    flatKids.push(cur);
    return prev.concat(flatKids);
  }

  return nodes.reduce(treeReduce, []);
};

}());
<commit_msg>Debugger: rename<commit_after>
(function() {
'use strict';

Elm.Debugger = null;
Elm.debuggerInit = function(module) {
  return {
    make: function(runtime) {
      Elm.Debugger = debuggerInit(module, runtime);
      return Elm.Debugger.moduleInstance;
    }
  };
};

function debuggerInit(module, runtime) {

  var programPaused = false;
  var allNodes = null;

  var recordedEvents = [];
  var eventCounter = 0;
  var asyncTimers = [];
  var initialProgramState = {
    nodeValues: [],
    asyncTimers: []
  };

  function wrapNotify(id, v) {
    var timestep = Date.now();

    if (programPaused) {
      // ignore notify because we are stepping
    }
    else {
      runtime.notify(id, v, timestep);
      recordEvent(id, v, timestep);
    }
  };

  function wrapRunDelayed(func, delayMs) {
    return safeSetTimeout(func, delayMs);
  };

  function safeSetTimeout(func, delayMs) {
    var timerId = setTimeout(func, delayMs);
    asyncTimers.push({ func:func, delayMs:delayMs, timerId:timerId });
    return timerId;
  }

  function recordEvent(id, v, timestep) {
    recordedEvents.push({ id:id, value:v, timestep:timestep });
  }

  function resetProgram() {
    clearAsyncEvents();
    restoreNodeValues(allNodes, initialProgramState.nodeValues);
    redrawGraphics();
  }

  function restartProgram() {
    resetProgram();
    recordedEvents = [];
    initialProgramState.asyncTimers.forEach(function(timer) {
      var func = timer.func;
      func();
    });
  }

  function stepTo(index) {
    if (!programPaused) {
      programPaused = true;
      resetProgram();
    }

    if (index < 0 || index >= recordedEvents.length) {
      throw "Index out of bounds: " + index;
    }

    if (index < eventCounter) {
      resetProgram();
      eventCounter = 0;
    }

    assert(index >= eventCounter, "index must be bad");
    while (eventCounter < index) {
      var nextEvent = recordedEvents[eventCounter];
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);

      eventCounter += 1;
    }
    assert(eventCounter == index, "while loop didnt' work");
  }

  function getMaxSteps() {
    return recordedEvents.length - 1;
  }

  function clearAsyncEvents() {
    asyncTimers.forEach(function(timer) {
      clearTimeout(timer.timerId);
    });
  }

  function doPlayback(eventList) {
    var x = eventList.shift();
    var time = x[2];
    runtime.notify(x[0], x[1], x[2]);

    if (eventList.length > 0) {
      var delta = eventList[0][2] - time;
      setTimeout(function() { doPlayback(eventList); }, delta);
    }
  }

  function redrawGraphics() {
    var graphicsFoldp = moduleInstance.main.kids[0];
    graphicsFoldp.recv(Date.now(), true, moduleInstance.main.id);
  }

  var wrappedRuntime = {
    notify: wrapNotify,
    runDelayed: wrapRunDelayed,
    node: runtime.node,
    display: runtime.display,
    id: runtime.id,
    addListener: runtime.addListener,
    inputs: runtime.inputs
  };

  var moduleInstance = module.make(wrappedRuntime);

  var elmDebugger = {
        moduleInstance: moduleInstance,

        // debugger functions
        reset: resetProgram,
        restart: restartProgram,
        getMaxSteps: getMaxSteps,
        stepTo: stepTo
  };

  allNodes = flattenNodes(wrappedRuntime.inputs);
  initialProgramState.nodeValues = saveNodeValues(allNodes);
  initialProgramState.asyncTimers = asyncTimers.slice();

  return elmDebugger;
};

function assert(bool, msg) {
  if (!bool) {
    throw "Assertion error: " + msg;
  }
}

function saveNodeValues(allNodes) {
  var nodeValues = [];

  allNodes.forEach(function(node) {
    nodeValues.push({ value: node.value, id: node.id });
  });

  return nodeValues;
};

function restoreNodeValues(allNodes, nodeStates) {
  assert(allNodes.length == nodeStates.length, "saved program state has wrong length");
  for (var i=0; i < allNodes.length; i++) {
    var node = allNodes[i];
    var state = nodeStates[i];
    assert(node.id == state.id, "the nodes moved position");

    node.value = state.value;
  }
}

function flattenNodes(nodes) {
  var treeReduce = function(prev, cur, i, arr) {
    var flatKids = cur.kids.reduce(treeReduce, []);
    flatKids.push(cur);
    return prev.concat(flatKids);
  }

  return nodes.reduce(treeReduce, []);
};

}());
<|endoftext|>


<commit_before>var express = require('express');
var url = require('url');
var net = require('net');
var path = require('path');
var fs = require('fs');
var EventEmitter = require('events').EventEmitter;
var exnted = require('util')._extend;
var util = require('../util');
var rules = require('./rules');
var https = require('./https');
var rulesUtil = require('./rules/util');
var connect = require('./proxy/connect');
var config = require('../package.json');

function tunnelProxy(server, proxy) {

	server.on('connect', function(req, reqSocket, head) {//ws, wss, https proxy
		var tunnelUrl = req.url = util.setProtocol(req.url, true);
		var options = parseUrl();
		var resSocket, proxySocket;

		var port = options.port;
		if (rulesUtil.intercepts(options.hostname + (!port || port == 80 || port == 443 ? '' : ':' + port))) {
			sendEstablished();
			https.dispatch(reqSocket, proxy);
			return;
		}

		abortIfUnavailable(reqSocket);
		function emitError(err) {
			resSocket && resSocket.destroy();
			proxySocket && proxySocket.destroy();
			reqSocket.destroy();
		}

		function abortIfUnavailable(socket) {
			socket.on('error', emitError).on('close', emitError);
		}

		function parseUrl(_tunnelUrl, port) {
			_tunnelUrl = _tunnelUrl || tunnelUrl;
			options = req.options = url.parse(_tunnelUrl);
			options.port = options.port || port || 443;
			return options;
		}

		function sendEstablished() {
			reqSocket.write('HTTP/1.1 200 Connection Established\r\nProxy-Agent: ' + config.name + '\r\n\r\n');
			return reqSocket;
		}

		function tunnel(url) {
			var now = Date.now();
			rules.resolveHost(url || tunnelUrl, function(err, ip) {
				req.host = ip;
				req.clientHost  = reqSocket.remoteAddress;
				req.error = err;
				req.dnsTime = Date.now() - now;
				proxy.emit('tunnel', req);

				if (err) {
					emitError(err);
					return;
				}

				resSocket = net.connect(options.port, ip, function() {
					sendEstablished();
		            resSocket.pipe(reqSocket).pipe(resSocket);
		        });
				abortIfUnavailable(resSocket);
			});
		}

		var _rules = req.rules = rules.resolveRules(tunnelUrl);
		var proxyUrl = util.rule.getMatcher(_rules.proxy);

		if (proxyUrl) {
			var _tunnelUrl = 'https:' + util.removeProtocol(proxyUrl);
			var now = Date.now();
			rules.resolveHost(_tunnelUrl, function(err, ip) {
				req.host = ip;
				req.error = err;
				req.dnsTime = Date.now() - now;
				parseUrl(_tunnelUrl, config.port);
				if (!err && util.isLocalAddress(ip) && options.port == config.port) {
					parseUrl();
					return tunnel();
				}
				proxy.emit('tunnelProxy', req);
				if (err) {
					emitError(err);
					return;
				}
				options.host = ip;
				connect(req, function(err, _proxySocket) {
					if (err) {
						return emitError();
					}
					proxySocket = _proxySocket;
					abortIfUnavailable(_proxySocket);
					sendEstablished().pipe(_proxySocket).pipe(reqSocket);
				});
			});
		} else {
			if (_rules.rule) {
				var _url = util.setProtocol(util.rule.getMatcher(_rules.rule), true);
				if (/^https:/i.test(_url)) {
					parseUrl(_url);
				} else {
					port = util.getProtocol(_url);
					port = port.substring(0, port.length - 1);
					var localProtocol = port + 'sslport';
					if (port = config[localProtocol]) {
						proxy.emit(localProtocol, _url, tunnelUrl);
						parseUrl(_url);
						options.protocol = 'https:';
						options.hostname = '127.0.0.1';
						options.host = options.hostname + ':' + port;
						options.port = port;
						_url = 'ssl://' + options.host; //不解析_url的ip
					} else {
						_url = null;//如果规则无法处理，则直接访问
					}
				}
			}

			tunnel(_url);
		}
    });

	return server;
}

function preventThrowError(socket) {
	socket.removeListener('error', util.noop);
	socket.on('error', util.noop);
}

function proxy(conf) {
	if (conf) {
		['port', 'sockets', 'days', 'timeout',
		 'username', 'password', 'uipath', 'rules', 'debug']
			.forEach(function(name) {
				if (conf[name]) {
					config[name] = conf[name];
				}
			});

		if (Array.isArray(conf.ports)) {
			config.ports = config.ports.concat(conf.ports);
		}
	}

	if (!conf || typeof conf.middlewares != 'string') {
		if (!Array.isArray(conf.middlewares)) {
			config.middlewares = [];
		}
	} else {
		config.middlewares = conf.middlewares.trim().split(/\s*,\s*/g);
	}

	config.middlewares.map(util.resolvePath);

	var port = config.port;
	config.ports.forEach(function(name) {
		if (!/port$/.test(name) || name == 'port') {
			throw new Error('port name "' + name + '" must be end of "port", but not equals "port", like: ' + name + 'port');
		}
		config[name] = ++port;
	});

	var agentConfig = {
				maxSockets: config.sockets,
				keepAlive: config.keepAlive,
				keepAliveMsecs: config.keepAliveMsecs
			};
	config.httpAgent = config.debug ? false : new (require('http').Agent)(agentConfig).on('free', preventThrowError);
	config.httpsAgent = config.debug ? false : new (require('https').Agent)(agentConfig).on('free', preventThrowError);
	config.WEINRE_HOST = 'weinre.' + config.localUIHost;

	var app = express();
	var server = app.listen(config.port);
	var proxyEvents = new EventEmitter();
	var middlewares = ['./init',
	               '../biz']
				   .concat(require('./inspectors'))
				   .concat(config.middlewares)
	               .concat(require('./handlers'));

	proxyEvents.config = config;
	middlewares.forEach(function(mw) {
		mw && app.use((typeof mw == 'string' ? require(mw) : mw).bind(proxyEvents));
	});

	parseHosts(config);
	exportInterfaces(proxyEvents);
	tunnelProxy(server, proxyEvents);
	https.createServer(proxyEvents);
	require('./proxy')(proxyEvents);
	require('../biz/init')(proxyEvents);

	return proxyEvents;
}

function exportInterfaces(obj) {
	obj.rules = rules;
	obj.util = util;
	obj.rulesUtil = rulesUtil;
	return obj;
}

function parseHosts(config) {
	var rulesPath = config.rules;
	if (rulesPath) {
		try {
			rulesUtil.setPublicHosts(fs.readFileSync(path.resolve(rulesPath), {encoding: 'utf8'}));
			rulesUtil.enablePublicHosts();
		} catch(e) {}
	}

	rulesUtil.loadHosts(config);
}

process.on('uncaughtException', function(err){
	var stack = err.message;
	try {
		stack += '\r\n' + err.stack;
	} catch(e) {}
	fs.writeFileSync(path.join(process.cwd(), config.name + '.log'), '\r\n' + util.formatDate()  + '\r\n' +  stack + '\r\n', {flag: 'a'});
	console.error(stack);
	process.exit(1);
});

module.exports = exportInterfaces(proxy);<commit_msg>https settings<commit_after>var express = require('express');
var url = require('url');
var net = require('net');
var path = require('path');
var fs = require('fs');
var EventEmitter = require('events').EventEmitter;
var exnted = require('util')._extend;
var util = require('../util');
var rules = require('./rules');
var https = require('./https');
var cert = require('./https/cert');
var rulesUtil = require('./rules/util');
var connect = require('./proxy/connect');
var config = require('../package.json');

function tunnelProxy(server, proxy) {

	server.on('connect', function(req, reqSocket, head) {//ws, wss, https proxy
		var tunnelUrl = req.url = util.setProtocol(req.url, true);
		var options = parseUrl();
		var resSocket, proxySocket;

		var port = options.port;
		if (rulesUtil.intercepts(options.hostname + (!port || port == 80 || port == 443 ? '' : ':' + port))) {
			cert.createCert(options.hostname, function(err) {
				if (err) {
					//TODO 通知到抓包页面
				}
				sendEstablished();
				https.dispatch(reqSocket, proxy);
			});
			return;
		}

		abortIfUnavailable(reqSocket);
		function emitError(err) {
			resSocket && resSocket.destroy();
			proxySocket && proxySocket.destroy();
			reqSocket.destroy();
		}

		function abortIfUnavailable(socket) {
			socket.on('error', emitError).on('close', emitError);
		}

		function parseUrl(_tunnelUrl, port) {
			_tunnelUrl = _tunnelUrl || tunnelUrl;
			options = req.options = url.parse(_tunnelUrl);
			options.port = options.port || port || 443;
			return options;
		}

		function sendEstablished() {
			reqSocket.write('HTTP/1.1 200 Connection Established\r\nProxy-Agent: ' + config.name + '\r\n\r\n');
			return reqSocket;
		}

		function tunnel(url) {
			var now = Date.now();
			rules.resolveHost(url || tunnelUrl, function(err, ip) {
				req.host = ip;
				req.clientHost  = reqSocket.remoteAddress;
				req.error = err;
				req.dnsTime = Date.now() - now;
				proxy.emit('tunnel', req);

				if (err) {
					emitError(err);
					return;
				}

				resSocket = net.connect(options.port, ip, function() {
					sendEstablished();
		            resSocket.pipe(reqSocket).pipe(resSocket);
		        });
				abortIfUnavailable(resSocket);
			});
		}

		var _rules = req.rules = rules.resolveRules(tunnelUrl);
		var proxyUrl = util.rule.getMatcher(_rules.proxy);

		if (proxyUrl) {
			var _tunnelUrl = 'https:' + util.removeProtocol(proxyUrl);
			var now = Date.now();
			rules.resolveHost(_tunnelUrl, function(err, ip) {
				req.host = ip;
				req.error = err;
				req.dnsTime = Date.now() - now;
				parseUrl(_tunnelUrl, config.port);
				if (!err && util.isLocalAddress(ip) && options.port == config.port) {
					parseUrl();
					return tunnel();
				}
				proxy.emit('tunnelProxy', req);
				if (err) {
					emitError(err);
					return;
				}
				options.host = ip;
				connect(req, function(err, _proxySocket) {
					if (err) {
						return emitError();
					}
					proxySocket = _proxySocket;
					abortIfUnavailable(_proxySocket);
					sendEstablished().pipe(_proxySocket).pipe(reqSocket);
				});
			});
		} else {
			if (_rules.rule) {
				var _url = util.setProtocol(util.rule.getMatcher(_rules.rule), true);
				if (/^https:/i.test(_url)) {
					parseUrl(_url);
				} else {
					port = util.getProtocol(_url);
					port = port.substring(0, port.length - 1);
					var localProtocol = port + 'sslport';
					if (port = config[localProtocol]) {
						proxy.emit(localProtocol, _url, tunnelUrl);
						parseUrl(_url);
						options.protocol = 'https:';
						options.hostname = '127.0.0.1';
						options.host = options.hostname + ':' + port;
						options.port = port;
						_url = 'ssl://' + options.host; //不解析_url的ip
					} else {
						_url = null;//如果规则无法处理，则直接访问
					}
				}
			}

			tunnel(_url);
		}
    });

	return server;
}

function preventThrowError(socket) {
	socket.removeListener('error', util.noop);
	socket.on('error', util.noop);
}

function proxy(conf) {
	if (conf) {
		['port', 'sockets', 'days', 'timeout',
		 'username', 'password', 'uipath', 'rules', 'debug']
			.forEach(function(name) {
				if (conf[name]) {
					config[name] = conf[name];
				}
			});

		if (Array.isArray(conf.ports)) {
			config.ports = config.ports.concat(conf.ports);
		}
	}

	if (!conf || typeof conf.middlewares != 'string') {
		if (!Array.isArray(conf.middlewares)) {
			config.middlewares = [];
		}
	} else {
		config.middlewares = conf.middlewares.trim().split(/\s*,\s*/g);
	}

	config.middlewares.map(util.resolvePath);

	var port = config.port;
	config.ports.forEach(function(name) {
		if (!/port$/.test(name) || name == 'port') {
			throw new Error('port name "' + name + '" must be end of "port", but not equals "port", like: ' + name + 'port');
		}
		config[name] = ++port;
	});

	var agentConfig = {
				maxSockets: config.sockets,
				keepAlive: config.keepAlive,
				keepAliveMsecs: config.keepAliveMsecs
			};
	config.httpAgent = config.debug ? false : new (require('http').Agent)(agentConfig).on('free', preventThrowError);
	config.httpsAgent = config.debug ? false : new (require('https').Agent)(agentConfig).on('free', preventThrowError);
	config.WEINRE_HOST = 'weinre.' + config.localUIHost;

	var app = express();
	var server = app.listen(config.port);
	var proxyEvents = new EventEmitter();
	var middlewares = ['./init',
	               '../biz']
				   .concat(require('./inspectors'))
				   .concat(config.middlewares)
	               .concat(require('./handlers'));

	proxyEvents.config = config;
	middlewares.forEach(function(mw) {
		mw && app.use((typeof mw == 'string' ? require(mw) : mw).bind(proxyEvents));
	});

	parseHosts(config);
	exportInterfaces(proxyEvents);
	tunnelProxy(server, proxyEvents);
	https.createServer(proxyEvents);
	require('./proxy')(proxyEvents);
	require('../biz/init')(proxyEvents);

	return proxyEvents;
}

function exportInterfaces(obj) {
	obj.rules = rules;
	obj.util = util;
	obj.rulesUtil = rulesUtil;
	return obj;
}

function parseHosts(config) {
	var rulesPath = config.rules;
	if (rulesPath) {
		try {
			rulesUtil.setPublicHosts(fs.readFileSync(path.resolve(rulesPath), {encoding: 'utf8'}));
			rulesUtil.enablePublicHosts();
		} catch(e) {}
	}

	rulesUtil.loadHosts(config);
}

process.on('uncaughtException', function(err){
	var stack = err.message;
	try {
		stack += '\r\n' + err.stack;
	} catch(e) {}
	fs.writeFileSync(path.join(process.cwd(), config.name + '.log'), '\r\n' + util.formatDate()  + '\r\n' +  stack + '\r\n', {flag: 'a'});
	console.error(stack);
	process.exit(1);
});

module.exports = exportInterfaces(proxy);<|endoftext|>


<commit_before>const Color = require('color');
const fetchQueryColors = require('./bing').fetchQueryColors;
const createDefaultPalette = require('./palette').createDefaultPalette;
const matchTermToColor = require('./matcher').matchTermToColor;

const colorOf = (term, options) => {
  if (!term || (typeof term !== 'string') || term.length === 0) {
    throw new Error('Must provide a valid search term string');
  }
  if (!options) {
    throw new Error('Must provide an options object');
  }
  if (!options.bingApiKey) {
    throw new Error('Must provide a Bing search API string');
  }
  if (options.pallette) {
    if (Array.isArray(options.pallette)) {
      throw new Error('options.pallette must be an array of colors');
    } else if (options.pallette.length < 1) {
      throw new Error('options.pallette array cannot be empty');
    }
  }

  const numResults = options.numResults || 50;
  const palette = options.palette ? options.palette.map(c => Color(c))
      : createDefaultPalette();

  return fetchQueryColors(options.bingApiKey, term, numResults)
      .then(colors => matchTermToColor(colors, palette));
};

module.exports = colorOf;
<commit_msg>Fixed typo when checking for options.palette validity<commit_after>const Color = require('color');
const fetchQueryColors = require('./bing').fetchQueryColors;
const createDefaultPalette = require('./palette').createDefaultPalette;
const matchTermToColor = require('./matcher').matchTermToColor;

const colorOf = (term, options) => {
  if (!term || (typeof term !== 'string') || term.length === 0) {
    throw new Error('Must provide a valid search term string');
  }
  if (!options) {
    throw new Error('Must provide an options object');
  }
  if (!options.bingApiKey) {
    throw new Error('Must provide a Bing search API string');
  }
  if (options.palette) {
    if (!Array.isArray(options.palette)) {
      throw new Error('options.palette must be an array of colors');
    } else if (options.palette.length < 1) {
      throw new Error('options.palette array cannot be empty');
    }
  }

  const numResults = options.numResults || 50;
  const palette = options.palette ? options.palette.map(c => Color(c))
      : createDefaultPalette();

  return fetchQueryColors(options.bingApiKey, term, numResults)
      .then(colors => matchTermToColor(colors, palette));
};

module.exports = colorOf;
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    through = require('through'),
    ngHtml2Js = require('ng-html2js');

function isExtension (file, extension) {
  return new RegExp('\\.' + extension + '$').test(file);
}

ngHtml2jsify.configure = function (filename, opts) {
  if(typeof opts === 'undefined') { // gulp
    return ngHtml2jsify(filename);
  }else{ // CLI
    return ngHtml2jsify(opts)(filename);
  }

};

function ngHtml2jsify(opts) {
  opts = opts|| {};
  opts.module = opts.module || null;
  opts.extension = opts.extension || 'html';
  opts.baseDir = opts.baseDir || null;

  var fileMatching = new RegExp("^.*\\" + path.sep + "(.*)$");

  return function (file) {
    if (!isExtension(file, opts.extension)) return through();

    var data = '';
    var appDir = process.cwd();

    return through(write, end);

    function write (buf) { data += buf }
    function end () {
      var content, src, fileName;
      try {
        fileName = opts.baseDir ? file.replace(path.join(appDir, opts.baseDir), '') : file.match(fileMatching)[1];
        content = fs.readFileSync(file, 'utf-8');
        src = ngHtml2Js(fileName, content, opts.module, 'ngModule') + '\nmodule.exports = ngModule;';
      } catch (error) {
        this.emit('error', error);
      }
      this.queue(src);
      this.queue(null);
    }
  };
}

module.exports = ngHtml2jsify.configure;
<commit_msg>fixed node path output for windows into unix-styled<commit_after>var fs = require('fs'),
    path = require('path'),
    through = require('through'),
    ngHtml2Js = require('ng-html2js');

function isExtension (file, extension) {
  return new RegExp('\\.' + extension + '$').test(file);
}

ngHtml2jsify.configure = function (filename, opts) {
  if(typeof opts === 'undefined') { // gulp
    return ngHtml2jsify(filename);
  }else{ // CLI
    return ngHtml2jsify(opts)(filename);
  }

};

function ngHtml2jsify(opts) {
  opts = opts|| {};
  opts.module = opts.module || null;
  opts.extension = opts.extension || 'html';
  opts.baseDir = opts.baseDir || null;

  var fileMatching = new RegExp("^.*\\" + path.sep + "(.*)$");

  return function (file) {
    if (!isExtension(file, opts.extension)) return through();

    var data = '';
    var appDir = process.cwd();

    return through(write, end);

    function write (buf) { data += buf }
    function end () {
      var content, src, fileName;
      try {
        fileName = opts.baseDir ? file.replace(path.join(appDir, opts.baseDir), '').replace(/\\/g, '/') : file.match(fileMatching)[1];
        content = fs.readFileSync(file, 'utf-8');
        src = ngHtml2Js(fileName, content, opts.module, 'ngModule') + '\nmodule.exports = ngModule;';
      } catch (error) {
        this.emit('error', error);
      }
      this.queue(src);
      this.queue(null);
    }
  };
}

module.exports = ngHtml2jsify.configure;
<|endoftext|>


<commit_before>/*jslint browser: true, devel: true, node: true, ass: true, nomen: true, unparam: true, indent: 4 */

/**
 * @license
 * Copyright (c) 2015 The ExpandJS authors. All rights reserved.
 * This code may only be used under the BSD style license found at https://expandjs.github.io/LICENSE.txt
 * The complete set of authors may be found at https://expandjs.github.io/AUTHORS.txt
 * The complete set of contributors may be found at https://expandjs.github.io/CONTRIBUTORS.txt
 */
(function (global) {
    "use strict";

    // Vars
    var http      = require('http'),
        https     = require('https'),
        XP        = require('expandjs'),
        XPEmitter = require('xp-emitter');

    /*********************************************************************/

    /**
     * This class is used to perform XHR requests.
     *
     * @class XPRequest
     * @description This class is used to perform XHR requests.
     * @extends XPEmitter
     */
    module.exports = new XP.Class('XPRequest', {

        // EXTENDS
        extends: XPEmitter,

        // OPTIONS
        options: {
            dataType: 'text',
            headers: null,
            keepAlive: 0,
            method: 'GET',
            url: ''
        },

        /*********************************************************************/

        /**
         * Emitted when a chunk of data is received.
         *
         * @event chunk
         * @param {Buffer | string} chunk
         * @param {Object} emitter
         */

        /**
         * Emitted when the entire data is received.
         *
         * @event data
         * @param {Buffer | string} data
         * @param {Object} emitter
         */

        /**
         * Emitted when an error is received.
         *
         * @event error
         * @param {string} error
         * @param {Object} emitter
         */

        /**
         * Emitted when a response is received.
         *
         * @event response
         * @param {Object} response
         * @param {Object} emitter
         */

        /**
         * Emitted when the request state changes.
         *
         * @event state
         * @param {string} state
         * @param {Object} emitter
         */

        /*********************************************************************/

        /**
         * @constructs
         * @param {Object | string} opt The request url or options.
         *   @param {string} [opt.dataType = "text"] The type of data expected back from the server.
         *   @param {Object} [opt.headers] An object containing request headers.
         *   @param {number} [opt.keepAlive = 0] How often to send TCP KeepAlive packets over sockets being kept alive.
         *   @param {string} [opt.method = "GET"] A string specifying the HTTP request method.
         *   @param {string} opt.url The request url.
         */
        initialize: {
            promise: true,
            value: function (opt, resolver) {

                // Asserting
                XP.assertArgument(XP.isObject(opt) || XP.isString(opt, true), 1, 'Object or string');

                // Vars
                var self = this;

                // Super
                XPEmitter.call(self);

                // Setting
                self.chunks    = [];
                self.options   = XP.isString(opt) ? {} : opt;
                self.dataType  = self.options.dataType;
                self.headers   = self.options.headers || {};
                self.keepAlive = self.options.keepAlive;
                self.method    = self.options.method;
                self.url       = self.options.url;
                self.resolver  = resolver;
                self.state     = 'idle';

                // Adapting
                self.adapt();

                // Listening
                self.adaptee.on('error', self.handleError.bind(self));
                self.adaptee.on('response', self.handleResponse.bind(self));
            }
        },

        /*********************************************************************/

        /**
         * Aborts the request.
         *
         * @method abort
         * @returns {Object}
         */
        abort: function () {

            // Vars
            var self = this;

            // Checking
            if (self.timeAbort) { return self; }

            // Aborting
            self.adaptee.abort();

            // Setting
            self.state     = 'aborted';
            self.timeAbort = Date.now();

            return self;
        },

        /**
         * Sends the request, appending data to the request body.
         *
         * @method send
         * @param {Buffer | string} [data]
         * @returns {Object}
         */
        send: function (data) {

            // Asserting
            XP.assertArgument(XP.isVoid(data) || Buffer.isBuffer(data) || XP.isString(data, true), 1, 'Buffer or string');

            // Vars
            var self = this;

            // Checking
            if (self.timeSend) { return self; }

            // Ending
            self.adaptee.end(data);

            // Setting
            self.state    = 'pending';
            self.timeSend = Date.now();

            return self;
        },

        /*********************************************************************/

        /**
         * Creates the adpatee
         *
         * @method adapt
         * @returns {Object}
         * @private
         */
        adapt: {
            enumerable: false,
            value: function () {

                // Vars
                var parsed   = XP.parseURL(this.url),
                    self     = XP.assign(this, {secure: (parsed.protocol || global.location.protocol) === 'https:'}),
                    port     = self.secure ? 443 : 80,
                    protocol = self.secure ? https : http;

                // Adapting
                self.adaptee = protocol.request({
                    auth: parsed.auth,
                    headers: self.headers,
                    hostname: parsed.hostname || global.location.hostname,
                    keepAlive: self.keepAlive > 0,
                    keepAliveMsecs: Math.max(self.keepAlive, 0),
                    method: self.method,
                    path: parsed.path + (parsed.hash || ''),
                    port: parsed.port || (parsed.hostname && port) || global.location.port
                });

                return self;
            }
        },

        /**
         * Parses the data
         *
         * @method parse
         * @param {Buffer | string} data
         * @returns {*}
         * @private
         */
        parse: {
            enumerable: true,
            value: function (data) {
                var self = this;
                if (self.dataType === 'json') { return JSON.parse(data); }
                return data;
            }
        },

        /*********************************************************************/

        /**
         * TODO DOC
         *
         * @property adaptee
         * @type Object
         * @private
         */
        adaptee: {
            enumerable: false,
            set: function (val) { return this.request || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property chunks
         * @type Array
         * @readonly
         * @private
         */
        chunks: {
            set: function (val) { return this.chunks || val; },
            validate: function (val) { return XP.isArray(val); }
        },

        /**
         * TODO DOC
         *
         * @property data
         * @type Buffer | string
         * @readonly
         */
        data: {
            validate: function (val) { return XP.isString(val) || Buffer.isBuffer(val); }
        },

        /**
         * TODO DOC
         *
         * @property dataType
         * @type string
         * @default "text"
         */
        dataType: {
            set: function (val) { return this.dataType || val; },
            validate: function (val) { return XP.includes(this.dataTypes, val); }
        },

        /**
         * TODO DOC
         *
         * @property dataTypes
         * @type Array
         * @default ["json", "text"]
         * @readonly
         */
        dataTypes: {
            frozen: true,
            writable: false,
            value: ['json', 'text']
        },

        /**
         * TODO DOC
         *
         * @property header
         * @type Object
         */
        headers: {
            set: function (val) { return this.headers || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property keepAlive
         * @type number
         * @default 0
         */
        keepAlive: {
            set: function (val) { return this.keepAlive >= 0 ? this.keepAlive : val; },
            validate: function (val) { return XP.isInt(val, true); }
        },

        /**
         * TODO DOC
         * @property method
         * @type string
         * @default "GET"
         */
        method: {
            set: function (val) { return this.method || XP.upperCase(val); },
            validate: function (val) { return XP.isString(val, true); }
        },

        /**
         * TODO DOC
         * @property response
         * @type Object
         * @readonly
         */
        response: {
            set: function (val) { return this.response || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property secure
         * @type boolean
         * @readonly
         */
        secure: {
            set: function (val) { return XP.isBoolean(this.secure) ? this.secure : !!val; }
        },

        /**
         * TODO DOC
         *
         * @property state
         * @type string
         * @readonly
         */
        state: {
            then: function (post) { this.emit('state', post, this); },
            validate: function (val) { return XP.includes(this.states, val); }
        },

        /**
         * TODO DOC
         *
         * @property states
         * @type Array
         * @default ["aborted", "failed", "idle", "pending", "received", "receiving"]
         * @readonly
         */
        states: {
            frozen: true,
            writable: false,
            value: ['aborted', 'failed', 'idle', 'pending', 'received', 'receiving']
        },

        /**
         * TODO DOC
         *
         * @property timeAbort
         * @type number
         * @readonly
         */
        timeAbort: {
            set: function (val) { return this.timeAbort || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeData
         * @type number
         * @readonly
         */
        timeData: {
            set: function (val) { return this.timeData || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeResponse
         * @type number
         * @readonly
         */
        timeResponse: {
            set: function (val) { return this.timeResponse || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeSend
         * @type number
         * @readonly
         */
        timeSend: {
            set: function (val) { return this.timeSend || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property url
         * @type string
         */
        url: {
            set: function (val) { return this.url || val; },
            validate: function (val) { return XP.isString(val, true); }
        },

        /*********************************************************************/

        // HANDLER
        handleData: function (chunk) {

            // Vars
            var self = this;

            // Setting
            self.chunks.push(chunk);

            // Emitting
            self.emit('chunk', chunk, self);
        },

        // HANDLER
        handleEnd: function () {

            // Vars
            var self = this;

            // Trying
            try {

                // Setting
                self.data     = self.parse(Buffer.isBuffer(self.chunks[0]) ? Buffer.concat(self.chunks) : self.chunks.join(''));
                self.state    = 'received';
                self.timeData = Date.now();

            } catch (exc) {

                // Handling
                return self.handleError(exc.message);
            }

            // Resolving
            self.resolver(null, self.data);

            // Emitting
            self.emit('data', self.data, self);
        },

        // HANDLER
        handleError: function (error) {

            // Vars
            var self = this;

            // Setting
            self.state        = 'failed';
            self.timeResponse = Date.now();
            self.timeData     = self.timeResponse;

            // Rejecting
            self.resolver(error);

            // Emitting
            self.emit('response', null, self);
            self.emit('error', error, self);
        },

        // HANDLER
        handleResponse: function (response) {

            // Vars
            var self = this;

            // Setting
            self.response     = response;
            self.state        = 'receiving';
            self.timeResponse = Date.now();

            // Listening
            response.on('data', self.handleData.bind(self));
            response.on('end', self.handleEnd.bind(self));

            // Emitting
            self.emit('response', response, self);
        }
    });

}(typeof window !== "undefined" ? window : global));<commit_msg>Various fixes<commit_after>/*jslint browser: true, devel: true, node: true, ass: true, nomen: true, unparam: true, indent: 4 */

/**
 * @license
 * Copyright (c) 2015 The ExpandJS authors. All rights reserved.
 * This code may only be used under the BSD style license found at https://expandjs.github.io/LICENSE.txt
 * The complete set of authors may be found at https://expandjs.github.io/AUTHORS.txt
 * The complete set of contributors may be found at https://expandjs.github.io/CONTRIBUTORS.txt
 */
(function (global) {
    "use strict";

    // Vars
    var http      = require('http'),
        https     = require('https'),
        XP        = global.XP || require('expandjs'),
        XPEmitter = global.XPEmitter || require('xp-emitter');

    /*********************************************************************/

    /**
     * This class is used to perform XHR requests.
     *
     * @class XPRequest
     * @description This class is used to perform XHR requests.
     * @extends XPEmitter
     */
    module.exports = new XP.Class('XPRequest', {

        // EXTENDS
        extends: XPEmitter,

        // OPTIONS
        options: {
            dataType: 'text',
            headers: null,
            keepAlive: 0,
            method: 'GET',
            url: ''
        },

        /*********************************************************************/

        /**
         * Emitted when a chunk of data is received.
         *
         * @event chunk
         * @param {Buffer | string} chunk
         * @param {Object} emitter
         */

        /**
         * Emitted when the entire data is received.
         *
         * @event data
         * @param {Buffer | string} data
         * @param {Object} emitter
         */

        /**
         * Emitted when an error is received.
         *
         * @event error
         * @param {string} error
         * @param {Object} emitter
         */

        /**
         * Emitted when a response is received.
         *
         * @event response
         * @param {Object} response
         * @param {Object} emitter
         */

        /**
         * Emitted when the request state changes.
         *
         * @event state
         * @param {string} state
         * @param {Object} emitter
         */

        /*********************************************************************/

        /**
         * @constructs
         * @param {Object | string} opt The request url or options.
         *   @param {string} [opt.dataType = "text"] The type of data expected back from the server.
         *   @param {Object} [opt.headers] An object containing request headers.
         *   @param {number} [opt.keepAlive = 0] How often to send TCP KeepAlive packets over sockets being kept alive.
         *   @param {string} [opt.method = "GET"] A string specifying the HTTP request method.
         *   @param {string} opt.url The request url.
         */
        initialize: {
            promise: true,
            value: function (opt, resolver) {

                // Asserting
                XP.assertArgument(XP.isObject(opt) || XP.isString(opt, true), 1, 'Object or string');

                // Vars
                var self = this;

                // Super
                XPEmitter.call(self);

                // Setting
                self.chunks    = [];
                self.options   = XP.isString(opt) ? {} : opt;
                self.dataType  = self.options.dataType;
                self.headers   = self.options.headers || {};
                self.keepAlive = self.options.keepAlive;
                self.method    = self.options.method;
                self.url       = self.options.url;
                self.resolver  = resolver;
                self.state     = 'idle';

                // Adapting
                self.adapt();

                // Listening
                self.adaptee.on('error', self.handleError.bind(self));
                self.adaptee.on('response', self.handleResponse.bind(self));
            }
        },

        /*********************************************************************/

        /**
         * Aborts the request.
         *
         * @method abort
         * @returns {Object}
         */
        abort: function () {

            // Vars
            var self = this;

            // Checking
            if (self.timeAbort) { return self; }

            // Aborting
            self.adaptee.abort();

            // Setting
            self.state     = 'aborted';
            self.timeAbort = Date.now();

            return self;
        },

        /**
         * Sends the request, appending data to the request body.
         *
         * @method send
         * @param {Buffer | string} [data]
         * @returns {Object}
         */
        send: function (data) {

            // Asserting
            XP.assertArgument(XP.isVoid(data) || Buffer.isBuffer(data) || XP.isString(data, true), 1, 'Buffer or string');

            // Vars
            var self = this;

            // Checking
            if (self.timeSend) { return self; }

            // Ending
            self.adaptee.end(data);

            // Setting
            self.state    = 'pending';
            self.timeSend = Date.now();

            return self;
        },

        /*********************************************************************/

        /**
         * Creates the adpatee
         *
         * @method adapt
         * @returns {Object}
         * @private
         */
        adapt: {
            enumerable: false,
            value: function () {

                // Vars
                var parsed   = XP.parseURL(this.url),
                    self     = XP.assign(this, {secure: (parsed.protocol || global.location.protocol) === 'https:'}),
                    port     = self.secure ? 443 : 80,
                    protocol = self.secure ? https : http;

                // Adapting
                self.adaptee = protocol.request({
                    auth: parsed.auth,
                    headers: self.headers,
                    hostname: parsed.hostname || global.location.hostname,
                    keepAlive: self.keepAlive > 0,
                    keepAliveMsecs: Math.max(self.keepAlive, 0),
                    method: self.method,
                    path: parsed.path + (parsed.hash || ''),
                    port: parsed.port || (parsed.hostname && port) || global.location.port
                });

                return self;
            }
        },

        /**
         * Parses the data
         *
         * @method parse
         * @param {Buffer | string} data
         * @returns {*}
         * @private
         */
        parse: {
            enumerable: true,
            value: function (data) {
                var self = this;
                if (self.dataType === 'json') { return JSON.parse(data); }
                return data;
            }
        },

        /*********************************************************************/

        /**
         * TODO DOC
         *
         * @property adaptee
         * @type Object
         * @private
         */
        adaptee: {
            enumerable: false,
            set: function (val) { return this.request || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property chunks
         * @type Array
         * @readonly
         * @private
         */
        chunks: {
            set: function (val) { return this.chunks || val; },
            validate: function (val) { return XP.isArray(val); }
        },

        /**
         * TODO DOC
         *
         * @property data
         * @type Buffer | string
         * @readonly
         */
        data: {
            validate: function (val) { return XP.isString(val) || Buffer.isBuffer(val); }
        },

        /**
         * TODO DOC
         *
         * @property dataType
         * @type string
         * @default "text"
         */
        dataType: {
            set: function (val) { return this.dataType || val; },
            validate: function (val) { return XP.includes(this.dataTypes, val); }
        },

        /**
         * TODO DOC
         *
         * @property dataTypes
         * @type Array
         * @default ["json", "text"]
         * @readonly
         */
        dataTypes: {
            frozen: true,
            writable: false,
            value: ['json', 'text']
        },

        /**
         * TODO DOC
         *
         * @property header
         * @type Object
         */
        headers: {
            set: function (val) { return this.headers || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property keepAlive
         * @type number
         * @default 0
         */
        keepAlive: {
            set: function (val) { return this.keepAlive >= 0 ? this.keepAlive : val; },
            validate: function (val) { return XP.isInt(val, true); }
        },

        /**
         * TODO DOC
         * @property method
         * @type string
         * @default "GET"
         */
        method: {
            set: function (val) { return this.method || XP.upperCase(val); },
            validate: function (val) { return XP.isString(val, true); }
        },

        /**
         * TODO DOC
         * @property response
         * @type Object
         * @readonly
         */
        response: {
            set: function (val) { return this.response || val; },
            validate: function (val) { return XP.isObject(val); }
        },

        /**
         * TODO DOC
         *
         * @property secure
         * @type boolean
         * @readonly
         */
        secure: {
            set: function (val) { return XP.isBoolean(this.secure) ? this.secure : !!val; }
        },

        /**
         * TODO DOC
         *
         * @property state
         * @type string
         * @readonly
         */
        state: {
            then: function (post) { this.emit('state', post, this); },
            validate: function (val) { return XP.includes(this.states, val); }
        },

        /**
         * TODO DOC
         *
         * @property states
         * @type Array
         * @default ["aborted", "failed", "idle", "pending", "received", "receiving"]
         * @readonly
         */
        states: {
            frozen: true,
            writable: false,
            value: ['aborted', 'failed', 'idle', 'pending', 'received', 'receiving']
        },

        /**
         * TODO DOC
         *
         * @property timeAbort
         * @type number
         * @readonly
         */
        timeAbort: {
            set: function (val) { return this.timeAbort || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeData
         * @type number
         * @readonly
         */
        timeData: {
            set: function (val) { return this.timeData || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeResponse
         * @type number
         * @readonly
         */
        timeResponse: {
            set: function (val) { return this.timeResponse || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property timeSend
         * @type number
         * @readonly
         */
        timeSend: {
            set: function (val) { return this.timeSend || val; },
            validate: function (val) { return XP.isFinite(val, true); }
        },

        /**
         * TODO DOC
         *
         * @property url
         * @type string
         */
        url: {
            set: function (val) { return this.url || val; },
            validate: function (val) { return XP.isString(val, true); }
        },

        /*********************************************************************/

        // HANDLER
        handleData: function (chunk) {

            // Vars
            var self = this;

            // Setting
            self.chunks.push(chunk);

            // Emitting
            self.emit('chunk', chunk, self);
        },

        // HANDLER
        handleEnd: function () {

            // Vars
            var self = this;

            // Trying
            try {

                // Setting
                self.data     = self.parse(Buffer.isBuffer(self.chunks[0]) ? Buffer.concat(self.chunks) : self.chunks.join(''));
                self.state    = 'received';
                self.timeData = Date.now();

            } catch (exc) {

                // Handling
                return self.handleError(exc.message);
            }

            // Resolving
            self.resolver(null, self.data);

            // Emitting
            self.emit('data', self.data, self);
        },

        // HANDLER
        handleError: function (error) {

            // Vars
            var self = this;

            // Setting
            self.state        = 'failed';
            self.timeResponse = Date.now();
            self.timeData     = self.timeResponse;

            // Rejecting
            self.resolver(error);

            // Emitting
            self.emit('response', null, self);
            self.emit('error', error, self);
        },

        // HANDLER
        handleResponse: function (response) {

            // Vars
            var self = this;

            // Setting
            self.response     = response;
            self.state        = 'receiving';
            self.timeResponse = Date.now();

            // Listening
            response.on('data', self.handleData.bind(self));
            response.on('end', self.handleEnd.bind(self));

            // Emitting
            self.emit('response', response, self);
        }
    });

}(typeof window !== "undefined" ? window : global));<|endoftext|>


<commit_before>/**
 * Created by gillbeits on 01/04/15.
 */

var
  HOST        = 'http://fontello.com',

  fs          = require('fs'),
  crypto      = require('crypto'),
  needle      = require('needle'),
  through2    = require('through2'),
  AdmZip      = require('adm-zip'),
  path        = require('path'),
  Vinyl       = require('vinyl'),
  log         = require('fancy-log'),
  yargs       = require('yargs'),
  extend      = require('util')._extend,

  PluginError = require('gulp-error')
  ;

const PLUGIN_NAME = 'gulp-fontello';

function fontello (opts) {
  "use strict";

  opts = extend({
    assetsOnly: true,
    host: HOST
  }, extend(opts || {}, yargs.argv));

  return through2.obj(function (file, enc, callback) {
    var self = this;

    var processResponse = function (zipContents, callback) {
      var
        zip = new AdmZip(zipContents),
        zipEntries = zip.getEntries()
        ;

      zipEntries.forEach(function (zipEntry) {
        var dirName, fileName, pathName, _ref;

        if (zipEntry.isDirectory) return;

        pathName = zipEntry.entryName;
        dirName = (_ref = path.dirname(pathName).match(/\/([^\/]*)$/)) != null ? _ref[1] : void 0;
        fileName = path.basename(pathName);

        if (opts.assetsOnly && !dirName) return;

        var content = zipEntry.getData();
        if (opts['font'] && opts['font'] != 'font' && path.extname(fileName) == '.css') {
          content = new Buffer(String(content).replace(new RegExp('\.\.\/font\/', 'g'), '../' + opts['font'] + '/'));
        }

        var file = new Vinyl({
          cwd: "./",
          path: (dirName ? ((opts[dirName] ? opts[dirName] : dirName) + '/') : '') + fileName,
          contents: content
        });
        self.push(file);
      });

      callback();
    };

    var fetchFromHost = function (callback) {
      var stream = through2.obj(function (file) {
        if (!file.toString()) {
          throw new PluginError(PLUGIN_NAME, "No session at Fontello for zip archive");
        }

        var reqOpts = {};
        if (process.env.HTTP_PROXY) {
          reqOpts.proxy = process.env.HTTP_PROXY;
        }
        needle.get(opts.host + "/" + file.toString() + "/get", reqOpts, function (error, response) {
          if (error) {
            throw error;
          }

          // store in cache if configured
          if (opts.cache) {
            opts.cache.set(configHash, response.body);
          }

          processResponse(response.body, callback);
        });
      });

      var reqOpts = { multipart: true };
      if (process.env.HTTP_PROXY) {
        reqOpts.proxy = process.env.HTTP_PROXY;
      }
      needle.post(opts.host, {
        config: {
          buffer: file.contents,
          filename: 'fontello.json',
          content_type: 'application/json'
        }
      }, reqOpts).pipe(stream);
    };

    // create SHA256 of the contents of the config file
    var configHash = crypto.createHash('sha256').update(file.contents).digest('hex');

    // use cache if configured
    if (opts.cache) {
      // check cache first
      opts.cache.get(configHash, function (error, cachedResponseBody) {
        // on cache err or empty response use normal fetch
        if (error || !cachedResponseBody) {
          fetchFromHost(callback);
        } else {
          log('using cached fontello zip for config with sha1: ' + configHash);
          processResponse(cachedResponseBody, callback);
        }
      });
    } else {
      fetchFromHost(callback);
    }
  });
}

/**
 * simple file-system based cache
 *
 * @param cacheDir
 * @returns {{get: 'get', set: 'set'}}
 */
fontello.simpleFsCache = function(cacheDir) {
  if (!fs.lstatSync(cacheDir).isDirectory()) {
    fs.mkdirSync(cacheDir);
  }

  return {
    'get': function(file, cb) {
      fs.readFile(path.join(cacheDir, file + ".cached.zip"), function(err, result) {
        if (err || !result) {
          cb();
        } else {
          cb(null, result);
        }
      });
    },
    'set': function(file, response) {
      fs.writeFile(path.join(cacheDir, file + ".cached.zip"), response, function noop() {});
    }
  }
};

module.exports = fontello;
<commit_msg>Catch errors in initial request to fontello<commit_after>/**
 * Created by gillbeits on 01/04/15.
 */

var
  HOST        = 'http://fontello.com',

  fs          = require('fs'),
  crypto      = require('crypto'),
  needle      = require('needle'),
  through2    = require('through2'),
  AdmZip      = require('adm-zip'),
  path        = require('path'),
  Vinyl       = require('vinyl'),
  log         = require('fancy-log'),
  yargs       = require('yargs'),
  extend      = require('util')._extend,

  PluginError = require('gulp-error')
  ;

const PLUGIN_NAME = 'gulp-fontello';

function fontello (opts) {
  "use strict";

  opts = extend({
    assetsOnly: true,
    host: HOST
  }, extend(opts || {}, yargs.argv));

  return through2.obj(function (file, enc, callback) {
    var self = this;

    var processResponse = function (zipContents, callback) {
      var
        zip = new AdmZip(zipContents),
        zipEntries = zip.getEntries()
        ;

      zipEntries.forEach(function (zipEntry) {
        var dirName, fileName, pathName, _ref;

        if (zipEntry.isDirectory) return;

        pathName = zipEntry.entryName;
        dirName = (_ref = path.dirname(pathName).match(/\/([^\/]*)$/)) != null ? _ref[1] : void 0;
        fileName = path.basename(pathName);

        if (opts.assetsOnly && !dirName) return;

        var content = zipEntry.getData();
        if (opts['font'] && opts['font'] != 'font' && path.extname(fileName) == '.css') {
          content = new Buffer(String(content).replace(new RegExp('\.\.\/font\/', 'g'), '../' + opts['font'] + '/'));
        }

        var file = new Vinyl({
          cwd: "./",
          path: (dirName ? ((opts[dirName] ? opts[dirName] : dirName) + '/') : '') + fileName,
          contents: content
        });
        self.push(file);
      });

      callback();
    };

    var fetchFromHost = function (callback) {
      var stream = through2.obj(function (file) {
        if (!file.toString()) {
          throw new PluginError(PLUGIN_NAME, "No session at Fontello for zip archive");
        }

        var reqOpts = {};
        if (process.env.HTTP_PROXY) {
          reqOpts.proxy = process.env.HTTP_PROXY;
        }
        needle.get(opts.host + "/" + file.toString() + "/get", reqOpts, function (error, response) {
          if (error) {
            throw error;
          }

          // store in cache if configured
          if (opts.cache) {
            opts.cache.set(configHash, response.body);
          }

          processResponse(response.body, callback);
        });
      });

      var reqOpts = { multipart: true };
      if (process.env.HTTP_PROXY) {
        reqOpts.proxy = process.env.HTTP_PROXY;
      }
      needle.post(opts.host, {
        config: {
          buffer: file.contents,
          filename: 'fontello.json',
          content_type: 'application/json'
        }
      }, reqOpts).on('err', callback).pipe(stream);
    };

    // create SHA256 of the contents of the config file
    var configHash = crypto.createHash('sha256').update(file.contents).digest('hex');

    // use cache if configured
    if (opts.cache) {
      // check cache first
      opts.cache.get(configHash, function (error, cachedResponseBody) {
        // on cache err or empty response use normal fetch
        if (error || !cachedResponseBody) {
          fetchFromHost(callback);
        } else {
          log('using cached fontello zip for config with sha1: ' + configHash);
          processResponse(cachedResponseBody, callback);
        }
      });
    } else {
      fetchFromHost(callback);
    }
  });
}

/**
 * simple file-system based cache
 *
 * @param cacheDir
 * @returns {{get: 'get', set: 'set'}}
 */
fontello.simpleFsCache = function(cacheDir) {
  if (!fs.lstatSync(cacheDir).isDirectory()) {
    fs.mkdirSync(cacheDir);
  }

  return {
    'get': function(file, cb) {
      fs.readFile(path.join(cacheDir, file + ".cached.zip"), function(err, result) {
        if (err || !result) {
          cb();
        } else {
          cb(null, result);
        }
      });
    },
    'set': function(file, response) {
      fs.writeFile(path.join(cacheDir, file + ".cached.zip"), response, function noop() {});
    }
  }
};

module.exports = fontello;
<|endoftext|>


<commit_before>/*
 * (c) 2012 Johannes Wilm and Philip Schatz. Freely available under the AGPL. For further details see LICENSE.txt
 */
// Some timing stats: the time it takes to add each group (in seconds)
// bulk adding 50 pages at a time: 7+10+12+19+24+37+22+40+44+48
// bulk adding 100 pages at a time: 8+17+22+35+44
var bulkPagesToAdd = 100; // For larger books add many pages at a time so there is less time spent reflowing text

function pageCounter(show) {
    this.value = 1;
    this.show = function () {
        if (show === undefined) {
            return this.value;
        } else {
            return show(this.value);
        }
    }
}

var arabPageCounter = new pageCounter();
var romanPageCounter = new pageCounter(romanize);

function flowObject(name, pageCounter) {
    var fO = this;
    fO.name = name;
    fO.pageCounter = pageCounter;
    fO.rawdiv = '
';
    fO.rawselector = '#' + name + 'raw';
    fO.div = '
';
    fO.selector = '#' + name;

    fO.addPage = function () {
        $('#' + name).append('

' + pageCounter.show() + '

');
        pageCounter.value++;
    };

    fO.addContent = function (content) {
        $(this.rawselector).append(content);
    }

    // If text overflows from the region add more pages and then remove any empty ones
    fO.addPagesIfNeeded = function () {

        if (($(fO.rawselector)[0].innerText.length > 0) && (fO.pageCounter.value == 1)) {
            fO.addPage();
        }

        namedFlow = document.webkitGetFlowByName(fO.name);
        // TODO: We use firstEmptyRegionIndex as overset gives us incorrect values in current Chromium.
        if (namedFlow.firstEmptyRegionIndex == -1) {
            // Add several pages at a time
            // console.log(new Date());
            for (var i = 0; i < bulkPagesToAdd; i++) {
                fO.addPage();
            }
            // The browser becomes unresponsive if we loop
            // Instead, set a timeout
            setTimeout(fO.addPagesIfNeeded(), 1);
        } else {
            // Remove the empty pages (up to bulkPagesToAdd - 1)
            // (empty page needed to test if firstEmptyRegionIndex == -1 )
            while (namedFlow.firstEmptyRegionIndex != -1) {
                $('#' + fO.name + ' .page:last').detach();
                fO.pageCounter.value--;
            }
        }
    }

    fO.buildToc = function () {

        function findTocItems() {
            var namedFlow = document.webkitGetFlowByName(fO.name);
            var headlinePageList = [];

            $(fO.rawselector + ' h1').each(function () {

                var headlineContentDiv = namedFlow.getRegionsByContent(this)[0];
                var headlinePagenumber = $(headlineContentDiv).parent().find('.pagenumber').text();
                var headlineText = $(this).text();
                headlinePageList.push({
                    'text': headlineText,
                    'pagenumber': headlinePagenumber
                });
            });
            return headlinePageList;
        }

        var tocList = findTocItems();

        var tocContents = '
Table of Contents
';

        $.each(tocList, function (index, headline) {
            tocContents += '
' + headline.text + ' ' + headline.pagenumber + '
';
        });

        return tocContents;
    }

}

$(document).ready(function () {
    //Create and flow body
    bodyObject = new flowObject('body', arabPageCounter);
    $('body').wrapInner(bodyObject.rawdiv);
    $('body').append('
');
    $('#layout').append(bodyObject.div);
    bodyObject.addPagesIfNeeded();

    //Create and flow frontmatter
    fmObject = new flowObject('frontmatter', romanPageCounter);
    $('body').append(fmObject.rawdiv);
    fmObject.addContent('
Booktitle

by Author

' + bodyObject.buildToc());
    $('#layout').prepend(fmObject.div);
    fmObject.addPagesIfNeeded();
});<commit_msg>add description for automatic first page creation<commit_after>/*
 * (c) 2012 Johannes Wilm and Philip Schatz. Freely available under the AGPL. For further details see LICENSE.txt
 */
// Some timing stats: the time it takes to add each group (in seconds)
// bulk adding 50 pages at a time: 7+10+12+19+24+37+22+40+44+48
// bulk adding 100 pages at a time: 8+17+22+35+44
var bulkPagesToAdd = 100; // For larger books add many pages at a time so there is less time spent reflowing text

function pageCounter(show) {
    this.value = 1;
    this.show = function () {
        if (show === undefined) {
            return this.value;
        } else {
            return show(this.value);
        }
    }
}

var arabPageCounter = new pageCounter();
var romanPageCounter = new pageCounter(romanize);

function flowObject(name, pageCounter) {
    var fO = this;
    fO.name = name;
    fO.pageCounter = pageCounter;
    fO.rawdiv = '
';
    fO.rawselector = '#' + name + 'raw';
    fO.div = '
';
    fO.selector = '#' + name;

    fO.addPage = function () {
        $('#' + name).append('

' + pageCounter.show() + '

');
        pageCounter.value++;
    };

    fO.addContent = function (content) {
        $(this.rawselector).append(content);
    }

    // If text overflows from the region add more pages and then remove any empty ones
    fO.addPagesIfNeeded = function () {

        // If there is text to be displayed and we have not added a single page, then add the first page
        if (($(fO.rawselector)[0].innerText.length > 0) && (fO.pageCounter.value == 1)) {
            fO.addPage();
        }

        namedFlow = document.webkitGetFlowByName(fO.name);
        // TODO: We use firstEmptyRegionIndex as overset gives us incorrect values in current Chromium.
        if (namedFlow.firstEmptyRegionIndex == -1) {
            // Add several pages at a time
            // console.log(new Date());
            for (var i = 0; i < bulkPagesToAdd; i++) {
                fO.addPage();
            }
            // The browser becomes unresponsive if we loop
            // Instead, set a timeout
            setTimeout(fO.addPagesIfNeeded(), 1);
        } else {
            // Remove the empty pages (up to bulkPagesToAdd - 1)
            // (empty page needed to test if firstEmptyRegionIndex == -1 )
            while (namedFlow.firstEmptyRegionIndex != -1) {
                $('#' + fO.name + ' .page:last').detach();
                fO.pageCounter.value--;
            }
        }
    }

    fO.buildToc = function () {

        function findTocItems() {
            var namedFlow = document.webkitGetFlowByName(fO.name);
            var headlinePageList = [];

            $(fO.rawselector + ' h1').each(function () {

                var headlineContentDiv = namedFlow.getRegionsByContent(this)[0];
                var headlinePagenumber = $(headlineContentDiv).parent().find('.pagenumber').text();
                var headlineText = $(this).text();
                headlinePageList.push({
                    'text': headlineText,
                    'pagenumber': headlinePagenumber
                });
            });
            return headlinePageList;
        }

        var tocList = findTocItems();

        var tocContents = '
Table of Contents
';

        $.each(tocList, function (index, headline) {
            tocContents += '
' + headline.text + ' ' + headline.pagenumber + '
';
        });

        return tocContents;
    }

}

$(document).ready(function () {
    //Create and flow body
    bodyObject = new flowObject('body', arabPageCounter);
    $('body').wrapInner(bodyObject.rawdiv);
    $('body').append('
');
    $('#layout').append(bodyObject.div);
    bodyObject.addPagesIfNeeded();

    //Create and flow frontmatter
    fmObject = new flowObject('frontmatter', romanPageCounter);
    $('body').append(fmObject.rawdiv);
    fmObject.addContent('
Booktitle

by Author

' + bodyObject.buildToc());
    $('#layout').prepend(fmObject.div);
    fmObject.addPagesIfNeeded();
});<|endoftext|>


<commit_before>'use strict';
/**
 * @file Easy access to AnyFetch API.
 *
 * @see http://anyFetch.com
 */

var request = require('request');

// Url to retrieve an access_token
var ACCESSTOKEN_CREATION = '/oauth/token';
// Url to create a new document
var DOCUMENT_CREATION = '/documents';

/**
 * encode parentheses in url
 */
var encodeParentheses = function(string) {
  string = string.replace(/\(/g, "%28");
  string = string.replace(/\)/g, "%29");
  return string;
};

/**
 * AnyFetch Oauth libraries
 * Tools for easy communication with AnyFetch API.
 *
 * @param {string} your appId your application id, generated via AnyFetch
 * @param {string} appSecret your application secret, generated via AnyFetch
 * @param {string} (opt) replacement URL for anyFetchServer
 * @param {string} (opt) replacement URL for anyFetchFront
 */
module.exports = function(appId, appSecret, anyFetchServer, anyFetchFront) {
  // Root URL for the API. Can be updated for testing, Virtual Appliance or running in LAN.
  this.API_FRONT = anyFetchFront || process.env.ANYFETCH_SETTINGS_URL || 'http://settings.anyfetch.com';
  this.API_ROOT = anyFetchServer || process.env.ANYFETCH_API_URL || 'http://api.anyfetch.com';

  this.ACCESSTOKEN_CREATION = ACCESSTOKEN_CREATION;
  this.DOCUMENT_CREATION = DOCUMENT_CREATION;

  this.ERR_NO_ACCESS_TOKEN = new Error("This method requires you to define an accessToken.");

  /*************
  * CLASS ATTRIBUTES
  */

  // Closure for this item
  var self = this;
  // Store accessToken for this session

  var accessToken = null;
  /**
   * Retrieve an access token from AnyFetch
   *
   * @param {string} code value to be traded for tokens
   * @param {string} redirect_uri Your uri. totally useless, see https://github.com/applicake/doorkeeper/issues/280
   * @param {function} cb callback to be called once token are retrieved, will take as params the error and the new access_token. Additionnally, the access_token will be automatically set on this instance.
   */
  this.getAccessToken = function(code, redirect_uri, cb) {
    var params = {
      url: encodeParentheses(self.API_FRONT + self.ACCESSTOKEN_CREATION),
      form: {
        client_id: appId,
        client_secret: appSecret,
        redirect_uri: redirect_uri,
        code: code,
        grant_type: 'authorization_code',
      }
    };

    request.post(params, function(err, resp) {
      if(err) {
        return cb(err);
      }
      if(resp.statusCode !== 200) {
        return cb(new Error("AnyFetch returned non-200 code: " + resp.statusCode + '. ' + JSON.stringify(resp.body)));
      }

      cb(null, JSON.parse(resp.body).access_token);
    });
  };

  /*
   * Define the access token to use for all authenticated requests to AnyFetch API.
   *
   * @param {String} _accessToken Access token to use for subsequent requests.
   */
  this.setAccessToken = function(_accessToken) {
    self.accessToken = _accessToken;
  };

  /**
   * Send a document, and upload a file to it.
   * Simple wrapper around `sendDocument` and `sendFile`
   * See their respective doc.
   *
   * @param {Object} datas Datas parameter for `sendDocument`
   * @param {Object} fileConfig Config parameter for `sendFile`
   * @param {Function} cb Callback, error being first argument, then data about the new document.
   */
  this.sendDocumentAndFile = function(datas, fileConfig, cb) {
    self.sendDocument(datas, true, function(err, doc) {
      if(err) {
        return cb(err);
      }

      self.sendFile(datas.identifier, fileConfig, function(err) {
        cb(err, doc);
      });
    });
  };

  /**
   * Send a document to AnyFetch
   *
   * @param {Object} datas to be sent to AnyFetch, following the documentation for API_ROOT/providers/documents.
   * @param {bool} hasFile set to true if you intend to send a file with this document
   * @param {function} cb callback to be called once document has been created / updated. First parameter will be the error (if any), second will be the return from the API.
   *
   */
  this.sendDocument = function(datas, hasFile, cb) {
    if(!cb) {
      cb = hasFile;
      hasFile = false;
    }

    // We have a file pending, so let's skip hydration for now.
    if(hasFile) {
      datas.no_hydration = true;
    }

    if(!self.accessToken) {
      return cb(self.ERR_NO_ACCESS_TOKEN);
    }

    if(!datas.identifier) {
      return cb(new Error('Document must include an identifier.'));
    }

    var params = {
      url: encodeParentheses(self.API_ROOT + self.DOCUMENT_CREATION),
      json: datas,
      headers: {
        'Authorization': 'token ' + self.accessToken
      }
    };

    request.post(params, function(err, resp) {
      if(err) {
        return cb(err);
      }
      if(resp.statusCode !== 200) {
        return cb(new Error('AnyFetch returned non-200 code: ' + resp.statusCode + '. ' + JSON.stringify(resp.body)));
      }

      cb(null, resp.body);
    });
  };

  /**
   * Send a file for a document already uploaded
   *
   * @param {String} identifier Identifier for the document
   * @param {Object} config Configuration object to add a file to the document. Must at least contain a `key` param, which can either be a stream (e.g. fs.createReadStream) or a Buffer object. Warning: unfortunately, due to the variety of Stream, we can't type-check, so unexpected errors will occur if you specify weird file parameters. The object can also contains a `contentType` key (for MIME type), and a `filename`.
   * @param {Function} cb Callback with error if any.
   */
  this.sendFile = function(identifier, config, cb) {
    if(!self.accessToken) {
      return cb(self.ERR_NO_ACCESS_TOKEN);
    }

    if(typeof(config) === 'function') {
      config = config();
    }

    if(!config.file) {
      return cb(new Error("Config hash should include a file key"));
    }

    var params = {
      url: encodeParentheses(self.API_ROOT + self.DOCUMENT_CREATION + "/identifier/" + encodeURIComponent(identifier) + "/file"),
      headers: {
        'Authorization': 'token ' + self.accessToken
      }
    };

    var r = request.post(params, function(err, respFile) {
      if(err) {
        return cb(err);
      }

      if(respFile.statusCode !== 204) {
        return cb(new Error('AnyFetch returned non-204 code: ' + respFile.statusCode + '. ' + JSON.stringify(respFile.body)));
      }

      cb(null);
    });

    var form = r.form();

    var fileDatas = config.file;
    delete config.file;
    form.append('file', fileDatas, config);

    r.on('error', function(err) {
      console.log("Network error when sending " + identifier);
      console.log(err.stack);
    });
  };

  /**
   * Remove a document from anyFetch
   *
   * @param {string} identifier document identifier to be deleted
   * @param {Function} cb Callback post-delete, first argument is the error if any.
   */
  this.deleteDocument = function(identifier, cb) {
    if(!self.accessToken) {
      return cb(self.ERR_NO_ACCESS_TOKEN);
    }

    var params = {
      url: encodeParentheses(self.API_ROOT + self.DOCUMENT_CREATION),
      form: {
        'identifier': identifier
      },
      headers: {
        'Authorization': 'token ' + self.accessToken
      }
    };

    request.del(params, function(err, resp) {
      if(err) {
        return cb(err);
      }

      if(resp.statusCode !== 204) {
        return cb(new Error('AnyFetch returned non-204 code: ' + resp.statusCode + '. ' + JSON.stringify(resp.body)));
      }

      cb();
    });
  };
};

module.exports.debug = {
  createTestFrontServer: require('./test-servers/front.js'),
  createTestApiServer: require('./test-servers/api.js'),
  encodeParentheses: encodeParentheses,
};
<commit_msg>remove useless encoding and optimize code<commit_after>'use strict';
/**
 * @file Easy access to AnyFetch API.
 *
 * @see http://anyFetch.com
 */

var request = require('request');

// Url to retrieve an access_token
var ACCESSTOKEN_CREATION = '/oauth/token';
// Url to create a new document
var DOCUMENT_CREATION = '/documents';

/**
 * encode parentheses in url
 */
var encodeURIComponentAndParentheses = function(string) {
  string = encodeURIComponent(string);
  string = string.replace(/\(/g, "%28");
  string = string.replace(/\)/g, "%29");
  return string;
};

/**
 * AnyFetch Oauth libraries
 * Tools for easy communication with AnyFetch API.
 *
 * @param {string} your appId your application id, generated via AnyFetch
 * @param {string} appSecret your application secret, generated via AnyFetch
 * @param {string} (opt) replacement URL for anyFetchServer
 * @param {string} (opt) replacement URL for anyFetchFront
 */
module.exports = function(appId, appSecret, anyFetchServer, anyFetchFront) {
  // Root URL for the API. Can be updated for testing, Virtual Appliance or running in LAN.
  this.API_FRONT = anyFetchFront || process.env.ANYFETCH_SETTINGS_URL || 'http://settings.anyfetch.com';
  this.API_ROOT = anyFetchServer || process.env.ANYFETCH_API_URL || 'http://api.anyfetch.com';

  this.ACCESSTOKEN_CREATION = ACCESSTOKEN_CREATION;
  this.DOCUMENT_CREATION = DOCUMENT_CREATION;

  this.ERR_NO_ACCESS_TOKEN = new Error("This method requires you to define an accessToken.");

  /*************
  * CLASS ATTRIBUTES
  */

  // Closure for this item
  var self = this;
  // Store accessToken for this session

  var accessToken = null;
  /**
   * Retrieve an access token from AnyFetch
   *
   * @param {string} code value to be traded for tokens
   * @param {string} redirect_uri Your uri. totally useless, see https://github.com/applicake/doorkeeper/issues/280
   * @param {function} cb callback to be called once token are retrieved, will take as params the error and the new access_token. Additionnally, the access_token will be automatically set on this instance.
   */
  this.getAccessToken = function(code, redirect_uri, cb) {
    var params = {
      url: self.API_FRONT + self.ACCESSTOKEN_CREATION,
      form: {
        client_id: appId,
        client_secret: appSecret,
        redirect_uri: redirect_uri,
        code: code,
        grant_type: 'authorization_code',
      }
    };

    request.post(params, function(err, resp) {
      if(err) {
        return cb(err);
      }
      if(resp.statusCode !== 200) {
        return cb(new Error("AnyFetch returned non-200 code: " + resp.statusCode + '. ' + JSON.stringify(resp.body)));
      }

      cb(null, JSON.parse(resp.body).access_token);
    });
  };

  /*
   * Define the access token to use for all authenticated requests to AnyFetch API.
   *
   * @param {String} _accessToken Access token to use for subsequent requests.
   */
  this.setAccessToken = function(_accessToken) {
    self.accessToken = _accessToken;
  };

  /**
   * Send a document, and upload a file to it.
   * Simple wrapper around `sendDocument` and `sendFile`
   * See their respective doc.
   *
   * @param {Object} datas Datas parameter for `sendDocument`
   * @param {Object} fileConfig Config parameter for `sendFile`
   * @param {Function} cb Callback, error being first argument, then data about the new document.
   */
  this.sendDocumentAndFile = function(datas, fileConfig, cb) {
    self.sendDocument(datas, true, function(err, doc) {
      if(err) {
        return cb(err);
      }

      self.sendFile(datas.identifier, fileConfig, function(err) {
        cb(err, doc);
      });
    });
  };

  /**
   * Send a document to AnyFetch
   *
   * @param {Object} datas to be sent to AnyFetch, following the documentation for API_ROOT/providers/documents.
   * @param {bool} hasFile set to true if you intend to send a file with this document
   * @param {function} cb callback to be called once document has been created / updated. First parameter will be the error (if any), second will be the return from the API.
   *
   */
  this.sendDocument = function(datas, hasFile, cb) {
    if(!cb) {
      cb = hasFile;
      hasFile = false;
    }

    // We have a file pending, so let's skip hydration for now.
    if(hasFile) {
      datas.no_hydration = true;
    }

    if(!self.accessToken) {
      return cb(self.ERR_NO_ACCESS_TOKEN);
    }

    if(!datas.identifier) {
      return cb(new Error('Document must include an identifier.'));
    }

    var params = {
      url: self.API_ROOT + self.DOCUMENT_CREATION,
      json: datas,
      headers: {
        'Authorization': 'token ' + self.accessToken
      }
    };

    request.post(params, function(err, resp) {
      if(err) {
        return cb(err);
      }
      if(resp.statusCode !== 200) {
        return cb(new Error('AnyFetch returned non-200 code: ' + resp.statusCode + '. ' + JSON.stringify(resp.body)));
      }

      cb(null, resp.body);
    });
  };

  /**
   * Send a file for a document already uploaded
   *
   * @param {String} identifier Identifier for the document
   * @param {Object} config Configuration object to add a file to the document. Must at least contain a `key` param, which can either be a stream (e.g. fs.createReadStream) or a Buffer object. Warning: unfortunately, due to the variety of Stream, we can't type-check, so unexpected errors will occur if you specify weird file parameters. The object can also contains a `contentType` key (for MIME type), and a `filename`.
   * @param {Function} cb Callback with error if any.
   */
  this.sendFile = function(identifier, config, cb) {
    if(!self.accessToken) {
      return cb(self.ERR_NO_ACCESS_TOKEN);
    }

    if(typeof(config) === 'function') {
      config = config();
    }

    if(!config.file) {
      return cb(new Error("Config hash should include a file key"));
    }

    var params = {
      url: self.API_ROOT + self.DOCUMENT_CREATION + "/identifier/" + encodeURIComponentAndParentheses(identifier) + "/file",
      headers: {
        'Authorization': 'token ' + self.accessToken
      }
    };

    var r = request.post(params, function(err, respFile) {
      if(err) {
        return cb(err);
      }

      if(respFile.statusCode !== 204) {
        return cb(new Error('AnyFetch returned non-204 code: ' + respFile.statusCode + '. ' + JSON.stringify(respFile.body)));
      }

      cb(null);
    });

    var form = r.form();

    var fileDatas = config.file;
    delete config.file;
    form.append('file', fileDatas, config);

    r.on('error', function(err) {
      console.log("Network error when sending " + identifier);
      console.log(err.stack);
    });
  };

  /**
   * Remove a document from anyFetch
   *
   * @param {string} identifier document identifier to be deleted
   * @param {Function} cb Callback post-delete, first argument is the error if any.
   */
  this.deleteDocument = function(identifier, cb) {
    if(!self.accessToken) {
      return cb(self.ERR_NO_ACCESS_TOKEN);
    }

    var params = {
      url: self.API_ROOT + self.DOCUMENT_CREATION,
      form: {
        'identifier': identifier
      },
      headers: {
        'Authorization': 'token ' + self.accessToken
      }
    };

    request.del(params, function(err, resp) {
      if(err) {
        return cb(err);
      }

      if(resp.statusCode !== 204) {
        return cb(new Error('AnyFetch returned non-204 code: ' + resp.statusCode + '. ' + JSON.stringify(resp.body)));
      }

      cb();
    });
  };
};

module.exports.debug = {
  createTestFrontServer: require('./test-servers/front.js'),
  createTestApiServer: require('./test-servers/api.js'),
  encodeParentheses: encodeURIComponentAndParentheses,
};
<|endoftext|>


<commit_before>"object"!==typeof JSON&&(JSON={});
(function(){function m(a){return 10>a?"0"+a:a}function r(){return this.valueOf()}function t(a){u.lastIndex=0;return u.test(a)?'"'+a.replace(u,function(a){var c=w[a];return"string"===typeof c?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function p(a,l){var c,d,h,q,g=e,f,b=l[a];b&&"object"===typeof b&&"function"===typeof b.toJSON&&(b=b.toJSON(a));"function"===typeof k&&(b=k.call(l,a,b));switch(typeof b){case "string":return t(b);case "number":return isFinite(b)?String(b):
"null";case "boolean":case "null":return String(b);case "object":if(!b)return"null";e+=n;f=[];if("[object Array]"===Object.prototype.toString.apply(b)){q=b.length;for(c=0;c<q;c+=1)f[c]=p(c,b)||"null";h=0===f.length?"[]":e?"[\n"+e+f.join(",\n"+e)+"\n"+g+"]":"["+f.join(",")+"]";e=g;return h}if(k&&"object"===typeof k)for(q=k.length,c=0;c<q;c+=1)"string"===typeof k[c]&&(d=k[c],(h=p(d,b))&&f.push(t(d)+(e?": ":":")+h));else for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(h=p(d,b))&&f.push(t(d)+(e?
": ":":")+h);h=0===f.length?"{}":e?"{\n"+e+f.join(",\n"+e)+"\n"+g+"}":"{"+f.join(",")+"}";e=g;return h}}var x=/^[\],:{}\s]*$/,y=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,z=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,A=/(?:^|:|,)(?:\s*\[)+/g,u=/[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,v=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
"function"!==typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+m(this.getUTCMonth()+1)+"-"+m(this.getUTCDate())+"T"+m(this.getUTCHours())+":"+m(this.getUTCMinutes())+":"+m(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=r,Number.prototype.toJSON=r,String.prototype.toJSON=r);var e,n,w,k;"function"!==typeof JSON.stringify&&(w={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=
function(a,l,c){var d;n=e="";if("number"===typeof c)for(d=0;d<c;d+=1)n+=" ";else"string"===typeof c&&(n=c);if((k=l)&&"function"!==typeof l&&("object"!==typeof l||"number"!==typeof l.length))throw Error("JSON.stringify");return p("",{"":a})});"function"!==typeof JSON.parse&&(JSON.parse=function(a,e){function c(a,d){var g,f,b=a[d];if(b&&"object"===typeof b)for(g in b)Object.prototype.hasOwnProperty.call(b,g)&&(f=c(b,g),void 0!==f?b[g]=f:delete b[g]);return e.call(a,d,b)}var d;a=String(a);v.lastIndex=
0;v.test(a)&&(a=a.replace(v,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(x.test(a.replace(y,"@").replace(z,"]").replace(A,"")))return d=eval("("+a+")"),"function"===typeof e?c({"":d},""):d;throw new SyntaxError("JSON.parse");})})();<commit_msg>Added thank you to Douglas Crockford<commit_after>// Thanks, Douglas Crockford, for releasing this into the public domain!
// https://github.com/douglascrockford/JSON-js
"object"!==typeof JSON&&(JSON={});
(function(){function m(a){return 10>a?"0"+a:a}function r(){return this.valueOf()}function t(a){u.lastIndex=0;return u.test(a)?'"'+a.replace(u,function(a){var c=w[a];return"string"===typeof c?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function p(a,l){var c,d,h,q,g=e,f,b=l[a];b&&"object"===typeof b&&"function"===typeof b.toJSON&&(b=b.toJSON(a));"function"===typeof k&&(b=k.call(l,a,b));switch(typeof b){case "string":return t(b);case "number":return isFinite(b)?String(b):
"null";case "boolean":case "null":return String(b);case "object":if(!b)return"null";e+=n;f=[];if("[object Array]"===Object.prototype.toString.apply(b)){q=b.length;for(c=0;c<q;c+=1)f[c]=p(c,b)||"null";h=0===f.length?"[]":e?"[\n"+e+f.join(",\n"+e)+"\n"+g+"]":"["+f.join(",")+"]";e=g;return h}if(k&&"object"===typeof k)for(q=k.length,c=0;c<q;c+=1)"string"===typeof k[c]&&(d=k[c],(h=p(d,b))&&f.push(t(d)+(e?": ":":")+h));else for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(h=p(d,b))&&f.push(t(d)+(e?
": ":":")+h);h=0===f.length?"{}":e?"{\n"+e+f.join(",\n"+e)+"\n"+g+"}":"{"+f.join(",")+"}";e=g;return h}}var x=/^[\],:{}\s]*$/,y=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,z=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,A=/(?:^|:|,)(?:\s*\[)+/g,u=/[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,v=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
"function"!==typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+m(this.getUTCMonth()+1)+"-"+m(this.getUTCDate())+"T"+m(this.getUTCHours())+":"+m(this.getUTCMinutes())+":"+m(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=r,Number.prototype.toJSON=r,String.prototype.toJSON=r);var e,n,w,k;"function"!==typeof JSON.stringify&&(w={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=
function(a,l,c){var d;n=e="";if("number"===typeof c)for(d=0;d<c;d+=1)n+=" ";else"string"===typeof c&&(n=c);if((k=l)&&"function"!==typeof l&&("object"!==typeof l||"number"!==typeof l.length))throw Error("JSON.stringify");return p("",{"":a})});"function"!==typeof JSON.parse&&(JSON.parse=function(a,e){function c(a,d){var g,f,b=a[d];if(b&&"object"===typeof b)for(g in b)Object.prototype.hasOwnProperty.call(b,g)&&(f=c(b,g),void 0!==f?b[g]=f:delete b[g]);return e.call(a,d,b)}var d;a=String(a);v.lastIndex=
0;v.test(a)&&(a=a.replace(v,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(x.test(a.replace(y,"@").replace(z,"]").replace(A,"")))return d=eval("("+a+")"),"function"===typeof e?c({"":d},""):d;throw new SyntaxError("JSON.parse");})})();<|endoftext|>