<commit_before>import Ember from 'ember';
import { task, timeout, createObservable } from 'ember-concurrency';

// Wrap the $.getJSON API so that it's cancelable.
function cancelableGetJSON(url) {
  return createObservable(publish => {
    let xhr = Ember.$.getJSON(url);
    xhr.then(publish, publish.error);

    return () => {
      xhr.abort();
    };
  });
}

// BEGIN-SNIPPET debounced-search-with-cancelation
const DEBOUNCE_MS = 250;
export default Ember.Controller.extend({
  searchRepo: task(function * (term) {
    if (Ember.isBlank(term)) { return []; }

    // Pause here for DEBOUNCE_MS milliseconds. Because this
    // task is `.restartable()`, if the user starts typing again,
    // the current search will be canceled at this point and
    // start over from the beginning. This is the
    // ember-concurrency way of debouncing a task.
    yield timeout(DEBOUNCE_MS);

    let url = `https://api.github.com/search/repositories?q=${term}`;

    // We yield an AJAX request and wait for it to complete. If the task
    // is restarted before this request completes, the XHR request
    // is aborted (open the inspector and see for yourself :)
    let json = yield cancelableGetJSON(url);
    return json.items;
  }).restartable(),
});
// END-SNIPPET

<commit_msg>redo autocomplete example without observables<commit_after>import Ember from 'ember';
import { task, timeout } from 'ember-concurrency';

// BEGIN-SNIPPET debounced-search-with-cancelation
const DEBOUNCE_MS = 250;
export default Ember.Controller.extend({
  searchRepo: task(function * (term) {
    if (Ember.isBlank(term)) { return []; }

    // Pause here for DEBOUNCE_MS milliseconds. Because this
    // task is `.restartable()`, if the user starts typing again,
    // the current search will be canceled at this point and
    // start over from the beginning. This is the
    // ember-concurrency way of debouncing a task.
    yield timeout(DEBOUNCE_MS);

    let url = `https://api.github.com/search/repositories?q=${term}`;

    // We yield an AJAX request and wait for it to complete. If the task
    // is restarted before this request completes, the XHR request
    // is aborted (open the inspector and see for yourself :)
    let json = yield this.get('getJSON').perform(url);
    return json.items;
  }).restartable(),

  getJSON: task(function * (url) {
    let xhr;
    try {
      xhr = Ember.$.getJSON(url);
      yield xhr.promise();
    } finally {
      xhr.abort();
    }
  }),
});
// END-SNIPPET

<|endoftext|>


<commit_before>var should = require('should');
var setup = require('./../mocks/setup');
var assert = require('assert');

setup.makeSuite('search service', function() {

  var searchService = require('./../../src/services/search');
  var dataService = require('./../../src/services/data');
  var projectService = require('./../../src/services/project');
  var importService = require('./../../src/services/import');
  var elasticTools = require('./../../src/elastic/tools');
  var fs = require('fs-extra');

  before(function(done) {

    var data = [{
      name: 'Godfather'
    }, {
      name: 'Fight club'
    }, {
      name: 'test/test'
    }]

    projectService.ensureCollectionAsync({
      projectName: 'test',
      collectionName: 'movie'
    }).delay(30)
    .then(function(res) {
      dataService.addDocumentsAsync({
        refresh: true,
        collectionName: 'movie',
        body: data
      })
    }).delay(100)
    .then(function(res) {
      done();
    });
  });

  it('should search items', function(done) {
    searchService.searchAsync({
      collectionName: 'movie'
    }).then(function(res) {
      res.should.have.property('data')
      console.log(res);
      res.data.should.have.property('items').and.lengthOf(3);
      res.data.should.have.property('aggregations');
      res.data.aggregations.should.be.an.instanceOf(Object);
      res.data.aggregations.should.not.be.an.instanceOf(Array);
      res.data.aggregations.should.have.property('tags');
      res.should.have.property('pagination');
      res.pagination.should.have.property('page');
      res.pagination.should.have.property('total');
      res.pagination.should.have.property('per_page');
      res.should.have.property('meta');
      done();
    });
  });

  it('should search godfather', function(done) {
    searchService.searchAsync({
      collectionName: 'movie',
      query: 'godfather'
    }).then(function(res) {
      res.data.should.have.property('items').and.lengthOf(1);
      done();
    });
  });

  it('should search with not alphanumeric input', function(done) {
    searchService.searchAsync({
      collectionName: 'movie',
      query: 'test/test'
    }).then(function(res) {
      res.data.should.have.property('items').and.lengthOf(1);
      done();
    });
  });

  it('should search in conjunctive way (AND instead of OR between query words)', function(done) {
    searchService.searchAsync({
      collectionName: 'movie',
      query: 'godfather fight'
    }).then(function(res) {
      res.data.should.have.property('items').and.lengthOf(0);
      done();
    });
  });

  it('should search city and get aggregations as array', function(done) {
    searchService.searchAsync({
      collectionName: 'city'
    }).then(function(res) {
      res.should.have.property('data')
      res.data.should.have.property('items');
      res.data.should.have.property('aggregations');
      res.data.aggregations.should.be.an.instanceOf(Array);
      res.data.aggregations[0].should.have.property('name', 'country');
      done();
    });
  });

  describe('should search and sort items', function(done) {

    before(function(done) {
      fs.readFileAsync(__dirname + '/../fixtures/cities.json')
      .then(function(res) {
        return JSON.parse(res);
      })
      .then(function(res) {
        importService.import({
          projectName: 'test',
          collectionName: 'city',
          body: res
        }, function(err, res) {
          return elasticTools.refreshAsync({
          }).then(function(res) {
            done();
          });
        });
      })
    });

    it('should sort by city name', function(done) {
      searchService.searchAsync({
        collectionName: 'city',
        projectName: 'test',
        sort: 'city'
      }).then(function(res) {
        res.should.have.property('data')
        res.data.should.have.property('items').and.be.lengthOf(6);
        res.data.items[0].should.have.property('city', 'Berlin');
        done();
      });
    });

    it('should sort by geolocation (london)', function(done) {
      searchService.searchAsync({
        collectionName: 'city',
        projectName: 'test',
        sort: 'distance',
        aroundLatLng: '51.30, 0.08' // london
      }).then(function(res) {
        res.should.have.property('data')
        res.data.items[0].should.have.property('city', 'London');
        done();
      });
    });

    it('should sort by geolocation (moscow)', function(done) {
      searchService.searchAsync({
        collectionName: 'city',
        projectName: 'test',
        sort: 'distance',
        aroundLatLng: '55.45, 37.37' // moscow
      }).then(function(res) {
        res.should.have.property('data');
        res.data.items[0].should.have.property('city', 'Moscow');
        done();
      });
    });

    it('should sort by geolocation (empty location input)', function(done) {
      searchService.searchAsync({
        collectionName: 'city',
        projectName: 'test',
        sort: 'distance',
        aroundLatLng: undefined
      }).then(function(res) {
        res.should.have.property('data');
        res.data.should.have.property('items');
        done();
      });
    });
  });

  it('should get all facets for movie (object)', function(done) {
    searchService.getFacetsAsync({
      collectionName: 'movie'
    }).then(function(res) {
      res.should.be.an.instanceOf(Array);
      done();
    });
  });

  it('should get all facets for city (array)', function(done) {
    searchService.getFacetsAsync({
      collectionName: 'city'
    }).then(function(res) {
      res.should.be.an.instanceOf(Array);
      done();
    });
  });

  it('should get city country facet', function(done) {
    searchService.getFacetAsync({
      collectionName: 'city',
      facetName: 'country',
      size: 100
    }).then(function(res) {
      res.should.be.an.instanceOf(Object);
      res.should.have.property('name', 'country');
      res.should.have.property('type', 'terms');
      res.should.have.property('size', 100);
      done();
    });
  });

  it('should get movie actors_terms facet', function(done) {
    searchService.getFacetAsync({
      collectionName: 'movie',
      facetName: 'actors_terms',
      size: 100
    }).then(function(res) {
      res.should.be.an.instanceOf(Object);
      res.should.have.property('name', 'actors_terms');
      res.should.have.property('title', 'Actors');
      res.should.have.property('type', 'terms');
      res.should.have.property('size', 100);
      done();
    });
  });

  it('should not get movie notexistent_terms facet', function(done) {
    searchService.getFacetAsync({
      collectionName: 'movie',
      facetName: 'notexistent_terms'
    }).catch(function(res) {
      done();
    })
  });

  xit('should suggest items', function(done) {
    searchService.suggestAsync({
      collectionName: 'movie',
      query: 'bat'
    }).then(function(res) {
      done();
    });
  });
});
<commit_msg>removed console.log<commit_after>var should = require('should');
var setup = require('./../mocks/setup');
var assert = require('assert');

setup.makeSuite('search service', function() {

  var searchService = require('./../../src/services/search');
  var dataService = require('./../../src/services/data');
  var projectService = require('./../../src/services/project');
  var importService = require('./../../src/services/import');
  var elasticTools = require('./../../src/elastic/tools');
  var fs = require('fs-extra');

  before(function(done) {

    var data = [{
      name: 'Godfather'
    }, {
      name: 'Fight club'
    }, {
      name: 'test/test'
    }]

    projectService.ensureCollectionAsync({
      projectName: 'test',
      collectionName: 'movie'
    }).delay(30)
    .then(function(res) {
      dataService.addDocumentsAsync({
        refresh: true,
        collectionName: 'movie',
        body: data
      })
    }).delay(100)
    .then(function(res) {
      done();
    });
  });

  it('should search items', function(done) {
    searchService.searchAsync({
      collectionName: 'movie'
    }).then(function(res) {
      res.should.have.property('data')
      res.data.should.have.property('items').and.lengthOf(3);
      res.data.should.have.property('aggregations');
      res.data.aggregations.should.be.an.instanceOf(Object);
      res.data.aggregations.should.not.be.an.instanceOf(Array);
      res.data.aggregations.should.have.property('tags');
      res.should.have.property('pagination');
      res.pagination.should.have.property('page');
      res.pagination.should.have.property('total');
      res.pagination.should.have.property('per_page');
      res.should.have.property('meta');
      done();
    });
  });

  it('should search godfather', function(done) {
    searchService.searchAsync({
      collectionName: 'movie',
      query: 'godfather'
    }).then(function(res) {
      res.data.should.have.property('items').and.lengthOf(1);
      done();
    });
  });

  it('should search with not alphanumeric input', function(done) {
    searchService.searchAsync({
      collectionName: 'movie',
      query: 'test/test'
    }).then(function(res) {
      res.data.should.have.property('items').and.lengthOf(1);
      done();
    });
  });

  it('should search in conjunctive way (AND instead of OR between query words)', function(done) {
    searchService.searchAsync({
      collectionName: 'movie',
      query: 'godfather fight'
    }).then(function(res) {
      res.data.should.have.property('items').and.lengthOf(0);
      done();
    });
  });

  it('should search city and get aggregations as array', function(done) {
    searchService.searchAsync({
      collectionName: 'city'
    }).then(function(res) {
      res.should.have.property('data')
      res.data.should.have.property('items');
      res.data.should.have.property('aggregations');
      res.data.aggregations.should.be.an.instanceOf(Array);
      res.data.aggregations[0].should.have.property('name', 'country');
      done();
    });
  });

  describe('should search and sort items', function(done) {

    before(function(done) {
      fs.readFileAsync(__dirname + '/../fixtures/cities.json')
      .then(function(res) {
        return JSON.parse(res);
      })
      .then(function(res) {
        importService.import({
          projectName: 'test',
          collectionName: 'city',
          body: res
        }, function(err, res) {
          return elasticTools.refreshAsync({
          }).then(function(res) {
            done();
          });
        });
      })
    });

    it('should sort by city name', function(done) {
      searchService.searchAsync({
        collectionName: 'city',
        projectName: 'test',
        sort: 'city'
      }).then(function(res) {
        res.should.have.property('data')
        res.data.should.have.property('items').and.be.lengthOf(6);
        res.data.items[0].should.have.property('city', 'Berlin');
        done();
      });
    });

    it('should sort by geolocation (london)', function(done) {
      searchService.searchAsync({
        collectionName: 'city',
        projectName: 'test',
        sort: 'distance',
        aroundLatLng: '51.30, 0.08' // london
      }).then(function(res) {
        res.should.have.property('data')
        res.data.items[0].should.have.property('city', 'London');
        done();
      });
    });

    it('should sort by geolocation (moscow)', function(done) {
      searchService.searchAsync({
        collectionName: 'city',
        projectName: 'test',
        sort: 'distance',
        aroundLatLng: '55.45, 37.37' // moscow
      }).then(function(res) {
        res.should.have.property('data');
        res.data.items[0].should.have.property('city', 'Moscow');
        done();
      });
    });

    it('should sort by geolocation (empty location input)', function(done) {
      searchService.searchAsync({
        collectionName: 'city',
        projectName: 'test',
        sort: 'distance',
        aroundLatLng: undefined
      }).then(function(res) {
        res.should.have.property('data');
        res.data.should.have.property('items');
        done();
      });
    });
  });

  it('should get all facets for movie (object)', function(done) {
    searchService.getFacetsAsync({
      collectionName: 'movie'
    }).then(function(res) {
      res.should.be.an.instanceOf(Array);
      done();
    });
  });

  it('should get all facets for city (array)', function(done) {
    searchService.getFacetsAsync({
      collectionName: 'city'
    }).then(function(res) {
      res.should.be.an.instanceOf(Array);
      done();
    });
  });

  it('should get city country facet', function(done) {
    searchService.getFacetAsync({
      collectionName: 'city',
      facetName: 'country',
      size: 100
    }).then(function(res) {
      res.should.be.an.instanceOf(Object);
      res.should.have.property('name', 'country');
      res.should.have.property('type', 'terms');
      res.should.have.property('size', 100);
      done();
    });
  });

  it('should get movie actors_terms facet', function(done) {
    searchService.getFacetAsync({
      collectionName: 'movie',
      facetName: 'actors_terms',
      size: 100
    }).then(function(res) {
      res.should.be.an.instanceOf(Object);
      res.should.have.property('name', 'actors_terms');
      res.should.have.property('title', 'Actors');
      res.should.have.property('type', 'terms');
      res.should.have.property('size', 100);
      done();
    });
  });

  it('should not get movie notexistent_terms facet', function(done) {
    searchService.getFacetAsync({
      collectionName: 'movie',
      facetName: 'notexistent_terms'
    }).catch(function(res) {
      done();
    })
  });

  xit('should suggest items', function(done) {
    searchService.suggestAsync({
      collectionName: 'movie',
      query: 'bat'
    }).then(function(res) {
      done();
    });
  });
});
<|endoftext|>


<commit_before>var express = require('express');
var url = require('url');
var net = require('net');
var path = require('path');
var tls = require('tls');
var fs = require('fs');
var EventEmitter = require('events').EventEmitter;
var exnted = require('util')._extend;
var util = require('./util');
var rules = require('./rules');
var dispatch = require('./https');
var httpsUtil = require('./https/util');
var rulesUtil = require('./rules/util');
var initDataServer = require('./util/data-server');
var initLogServer = require('./util/log-server');
var config = require('../package.json');
var LOCALHOST = '127.0.0.1';

//see: https://github.com/joyent/node/issues/9272
if (typeof tls.checkServerIdentity == 'function') {
	var checkServerIdentity = tls.checkServerIdentity;
	tls.checkServerIdentity = function() {
		try {
			return checkServerIdentity.apply(this, arguments);
		} catch(err) {
			return err;
		}
	};
}

function tunnelProxy(server, proxy) {
	process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
	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, responsed;

		if (!options.hostname) {
			tunnelUrl = req.url = util.setProtocol(req.headers.host, true);
			options = parseUrl();
		}
		var hostname = options.hostname;
		abortIfUnavailable(reqSocket);
		var _rules = req.rules = rules.resolveRules(tunnelUrl);
		var filter = rules.resolveFilter(tunnelUrl);

		if (/local\.whistlejs\.com$/.test(hostname) || filter.https) {
			sendEstablished();
			dispatch(reqSocket, hostname, proxy);
			return;
		}

		var reqEmitter = new EventEmitter();
		var headers = req.headers;
		var reqData = {
				ip: util.getClientIp(req) || LOCALHOST,
				method: util.toUpperCase(req.method) || 'CONNECT',
				httpVersion: req.httpVersion || '1.1',
	            headers: headers
			};
		var resData = {headers: {}};
		var data = reqEmitter.data = {
				url: options.host,
				startTime: Date.now(),
				rules: _rules,
				req: reqData,
				res: resData,
				isHttps: true
		};

		var proxyUrl = util.rule.getProxy(_rules.rule);
		if (proxyUrl) {
			var isSocks = /^socks:\/\//.test(proxyUrl);
			data.realUrl = proxyUrl;
			var _url = 'https:' + util.removeProtocol(proxyUrl);
			data.proxy = true;
			resolveHost(_url, function(ip) {
				if (options.port == config.port && util.isLocalAddress(ip)) {
					options = parseUrl();
					return tunnel();
				}
				options = parseUrl(_url, isSocks ? 1080 : 8888);
				var opts = url.parse(tunnelUrl);
				headers.host = opts.hostname + ':' + (opts.port || 443);
				util.connect({
					isSocks: isSocks,
					host: ip,
					port: options.port,
					auth: options.auth,
					url: 'http://' + headers.host,
					headers: headers
				}, function(err, _proxySocket) {
					if (err) {
						return emitError(err);
					}
					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)) {
					options = parseUrl(_url);
					data.realUrl = _url;
				} else {
					var protocol = util.getProtocol(_url);
					protocol = protocol.substring(0, protocol.length - 1);
					var localProtocol = protocol + 'sslport';
					var port = config[localProtocol];
					if (port) {
						data.realUrl = _url;
						proxy.emit(localProtocol, {
							rule: _url,
							url: tunnelUrl,
							type: protocol,
							callback: function (err, data) {
								if (err) {
									return emitError(err);
								}
								if (data && data.port > 0) {
									port = data.port;
								}
								options = parseUrl(_url);
								options.protocol = 'https:';
								options.hostname = LOCALHOST;
								options.host = options.hostname + ':' + port;
								options.port = port;
								tunnel('https://' + hostname + ':' + port, options.hostname);
							}
						});
						return;
					} else {
						_url = null;//如果规则无法处理，则直接访问
					}
				}
			}

			tunnel(_url);
		}

		function tunnel(url, ip) {
			resolveHost(url || tunnelUrl, function(ip) {
				resSocket = net.connect(options.port, ip, function() {
		            resSocket.pipe(reqSocket).pipe(resSocket);
		            sendEstablished();
		        });
				abortIfUnavailable(resSocket);
			}, ip);
		}

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

		function resolveHost(url, callback, ip) {
			!filter.hide && proxy.emit('request', reqEmitter);
			rules.resolveHost(ip || url, function(err, ip) {
				data.requestTime = data.dnsTime = Date.now();
				resData.ip = ip || LOCALHOST;
				reqEmitter.emit('send', data);
				err ? emitError(err) : callback(ip);
			}, filter.host);
		}

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

		function sendEstablished() {
			reqSocket.write('HTTP/1.1 200 Connection Established\r\nProxy-Agent: ' + config.name + '\r\n\r\n');
			responsed = true;
			if (!reqEmitter) {
				return;
			}
			data.responseTime = data.endTime = Date.now();
            resData.statusCode = 200;
			reqEmitter.emit('response', data);
			reqEmitter.emit('end', data);
			return reqSocket;
		}

		function emitError(err) {
			if (responsed) {
				return;
			}
			responsed = true;
			resSocket && resSocket.destroy();
			proxySocket && proxySocket.destroy();
			reqSocket.destroy();
			data.responseTime = data.endTime = Date.now();

			if (!resData.ip) {
				resData.ip = LOCALHOST;
			}

			if (err) {
				err = new Error('aborted');
				data.reqError = true;
				resData.statusCode ='aborted';
				reqData.body = util.getErrorStack(err);
				reqEmitter.emit('abort', data);
			} else {
				data.resError = true;
				resData.statusCode = resData.statusCode || 502;
				resData.body = util.getErrorStack(err);
				util.emitError(reqEmitter, data);
			}
		}
    });

	return server;
}

function proxy(conf) {
	if (conf) {
		['port', 'sockets', '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 : util.createAgent(agentConfig);
	config.httpsAgent = config.debug ? false : util.createAgent(agentConfig, true);
	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);
	initDataServer(proxyEvents);
	initLogServer(proxyEvents);
	require('../biz/init')(proxyEvents);

	return proxyEvents;
}

function exportInterfaces(obj) {
	obj.rules = rules;
	obj.util = util;
	obj.rulesUtil = rulesUtil;
	obj.httpsUtil = httpsUtil;
	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 = util.getErrorStack(err);
	fs.writeFileSync(path.join(process.cwd(), config.name + '.log'), '\r\n' + stack + '\r\n', {flag: 'a'});
	console.error(stack);
	process.exit(1);
});

module.exports = exportInterfaces(proxy);<commit_msg>add hosts<commit_after>var express = require('express');
var url = require('url');
var net = require('net');
var path = require('path');
var tls = require('tls');
var fs = require('fs');
var EventEmitter = require('events').EventEmitter;
var exnted = require('util')._extend;
var util = require('./util');
var rules = require('./rules');
var dispatch = require('./https');
var httpsUtil = require('./https/util');
var rulesUtil = require('./rules/util');
var initDataServer = require('./util/data-server');
var initLogServer = require('./util/log-server');
var config = require('../package.json');
var LOCALHOST = '127.0.0.1';

//see: https://github.com/joyent/node/issues/9272
if (typeof tls.checkServerIdentity == 'function') {
	var checkServerIdentity = tls.checkServerIdentity;
	tls.checkServerIdentity = function() {
		try {
			return checkServerIdentity.apply(this, arguments);
		} catch(err) {
			return err;
		}
	};
}

function tunnelProxy(server, proxy) {
	process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
	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, responsed;

		if (!options.hostname) {
			tunnelUrl = req.url = util.setProtocol(req.headers.host, true);
			options = parseUrl();
		}
		var hostname = options.hostname;
		abortIfUnavailable(reqSocket);
		var _rules = req.rules = rules.resolveRules(tunnelUrl);
		var filter = rules.resolveFilter(tunnelUrl);

		if (/local\.whistlejs\.com$/.test(hostname) || filter.https) {
			sendEstablished();
			dispatch(reqSocket, hostname, proxy);
			return;
		}

		var reqEmitter = new EventEmitter();
		var headers = req.headers;
		var reqData = {
				ip: util.getClientIp(req) || LOCALHOST,
				method: util.toUpperCase(req.method) || 'CONNECT',
				httpVersion: req.httpVersion || '1.1',
	            headers: headers
			};
		var resData = {headers: {}};
		var data = reqEmitter.data = {
				url: options.host,
				startTime: Date.now(),
				rules: _rules,
				req: reqData,
				res: resData,
				isHttps: true
		};

		var proxyUrl = util.rule.getProxy(_rules.rule);
		if (proxyUrl) {
			var isSocks = /^socks:\/\//.test(proxyUrl);
			data.realUrl = proxyUrl;
			var _url = 'https:' + util.removeProtocol(proxyUrl);
			data.proxy = true;
			resolveHost(_url, function(ip) {
				if (options.port == config.port && util.isLocalAddress(ip)) {
					options = parseUrl();
					return tunnel();
				}
				options = parseUrl(_url, isSocks ? 1080 : 8888);
				var opts = url.parse(tunnelUrl);
				headers.host = opts.hostname + ':' + (opts.port || 443);
				util.connect({
					isSocks: isSocks,
					host: ip,
					port: options.port,
					auth: options.auth,
					url: 'http://' + headers.host,
					headers: headers
				}, function(err, _proxySocket) {
					if (err) {
						return emitError(err);
					}
					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)) {
					options = parseUrl(_url);
					data.realUrl = _url;
				} else {
					var protocol = util.getProtocol(_url);
					protocol = protocol.substring(0, protocol.length - 1);
					var localProtocol = protocol + 'sslport';
					var port = config[localProtocol];
					if (port) {
						data.realUrl = _url;
						proxy.emit(localProtocol, {
							rule: _url,
							url: tunnelUrl,
							type: protocol,
							callback: function (err, data) {
								if (err) {
									return emitError(err);
								}
								if (data && data.port > 0) {
									port = data.port;
								}
								options = parseUrl(_url);
								options.protocol = 'https:';
								options.hostname = LOCALHOST;
								options.host = options.hostname + ':' + port;
								options.port = port;
								tunnel('https://' + hostname + ':' + port, options.hostname);
							}
						});
						return;
					} else {
						_url = null;//如果规则无法处理，则直接访问
					}
				}
			}

			tunnel(_url);
		}

		function tunnel(url, ip) {
			resolveHost(url || tunnelUrl, function(ip) {
				resSocket = net.connect(options.port, ip, function() {
		            resSocket.pipe(reqSocket).pipe(resSocket);
		            sendEstablished();
		        });
				abortIfUnavailable(resSocket);
			}, ip);
		}

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

		function resolveHost(url, callback, ip) {
			!filter.hide && proxy.emit('request', reqEmitter);
			rules.resolveHost(ip || url, function(err, ip) {
				data.requestTime = data.dnsTime = Date.now();
				resData.ip = ip || LOCALHOST;
				reqEmitter.emit('send', data);
				err ? emitError(err) : callback(ip);
			}, filter.host);
		}

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

		function sendEstablished() {
			reqSocket.write('HTTP/1.1 200 Connection Established\r\nProxy-Agent: ' + config.name + '\r\n\r\n');
			responsed = true;
			if (!reqEmitter) {
				return;
			}
			data.responseTime = data.endTime = Date.now();
            resData.statusCode = 200;
			reqEmitter.emit('response', data);
			reqEmitter.emit('end', data);
			return reqSocket;
		}

		function emitError(err) {
			if (responsed) {
				return;
			}
			responsed = true;
			resSocket && resSocket.destroy();
			proxySocket && proxySocket.destroy();
			reqSocket.destroy();
			data.responseTime = data.endTime = Date.now();

			if (!resData.ip) {
				resData.ip = LOCALHOST;
			}

			if (err) {
				err = new Error('aborted');
				data.reqError = true;
				resData.statusCode ='aborted';
				reqData.body = util.getErrorStack(err);
				reqEmitter.emit('abort', data);
			} else {
				data.resError = true;
				resData.statusCode = resData.statusCode || 502;
				resData.body = util.getErrorStack(err);
				util.emitError(reqEmitter, data);
			}
		}
    });

	return server;
}

function proxy(conf) {
	if (conf) {
		['port', 'sockets', '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 : util.createAgent(agentConfig);
	config.httpsAgent = config.debug ? false : util.createAgent(agentConfig, true);
	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);
	initDataServer(proxyEvents);
	initLogServer(proxyEvents);
	require('../biz/init')(proxyEvents);

	return proxyEvents;
}

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

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

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

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


<commit_before>"use strict";

// jor1k compatibility

var VIRTIO_MAGIC_REG = 0x0;
var VIRTIO_VERSION_REG = 0x4;
var VIRTIO_DEVICE_REG = 0x8;
var VIRTIO_VENDOR_REG = 0xc;
var VIRTIO_HOSTFEATURES_REG = 0x10;
var VIRTIO_HOSTFEATURESSEL_REG = 0x14;
var VIRTIO_GUESTFEATURES_REG = 0x20;
var VIRTIO_GUESTFEATURESSEL_REG = 0x24;
var VIRTIO_GUEST_PAGE_SIZE_REG = 0x28;
var VIRTIO_QUEUESEL_REG = 0x30;
var VIRTIO_QUEUENUMMAX_REG = 0x34;
var VIRTIO_QUEUENUM_REG = 0x38;
var VIRTIO_QUEUEALIGN_REG = 0x3C;
var VIRTIO_QUEUEPFN_REG = 0x40;
var VIRTIO_QUEUENOTIFY_REG = 0x50;
var VIRTIO_INTERRUPTSTATUS_REG = 0x60;
var VIRTIO_INTERRUPTACK_REG = 0x64;
var VIRTIO_STATUS_REG = 0x70;

var VRING_DESC_F_NEXT =      1; /* This marks a buffer as continuing via the next field. */
var VRING_DESC_F_WRITE =     2; /* This marks a buffer as write-only (otherwise read-only). */
var VRING_DESC_F_INDIRECT =  4; /* This means the buffer contains a list of buffer descriptors. */

function Swap16(x)
{
    // OR1K is big endian, therefore no conversion needed for us
    return x;
}
function Swap32(x)
{
    return x;
}

function abort()
{
    if(DEBUG)
    {
        throw "abort";
    }
}
var hex8 = h;

var DebugMessage;

if(DEBUG)
{
    DebugMessage = console.log.bind(console);
}
else
{
    DebugMessage = function() {};
}

function LoadXMLResource(url, OnSuccess, OnError) {
    var req = new XMLHttpRequest();
    req.open('GET', url, true);
    //req.overrideMimeType('text/xml');
    req.onreadystatechange = function () {
        if (req.readyState != 4) {
            return;
        }
        if ((req.status != 200) && (req.status != 0)) {
            OnError("Error: Could not load XML file " + url);
            return;
        }
        OnSuccess(req.responseText);
    };
    req.send(null);
}

function LoadBinaryResource(url, OnSuccess, OnError) {
    var req = new XMLHttpRequest();
    req.open('GET', url, true);
    req.responseType = "arraybuffer";
    req.onreadystatechange = function () {
        if (req.readyState != 4) {
            return;
        }
        if ((req.status != 200) && (req.status != 0)) {
            OnError("Error: Could not load file " + url);
            return;
        }
        var arrayBuffer = req.response;
        if (arrayBuffer) {
            OnSuccess(arrayBuffer);
        } else {
            OnError("Error: No data received from: " + url);
        }
    };
    /*
        req.onload = function(e)
        {
                var arrayBuffer = req.response;
                if (arrayBuffer) {
                    OnLoadFunction(arrayBuffer);
                }
        };
    */
    req.send(null);
}

<commit_msg>improve for compilation<commit_after>"use strict";

// jor1k compatibility

var VIRTIO_MAGIC_REG = 0x0;
var VIRTIO_VERSION_REG = 0x4;
var VIRTIO_DEVICE_REG = 0x8;
var VIRTIO_VENDOR_REG = 0xc;
var VIRTIO_HOSTFEATURES_REG = 0x10;
var VIRTIO_HOSTFEATURESSEL_REG = 0x14;
var VIRTIO_GUESTFEATURES_REG = 0x20;
var VIRTIO_GUESTFEATURESSEL_REG = 0x24;
var VIRTIO_GUEST_PAGE_SIZE_REG = 0x28;
var VIRTIO_QUEUESEL_REG = 0x30;
var VIRTIO_QUEUENUMMAX_REG = 0x34;
var VIRTIO_QUEUENUM_REG = 0x38;
var VIRTIO_QUEUEALIGN_REG = 0x3C;
var VIRTIO_QUEUEPFN_REG = 0x40;
var VIRTIO_QUEUENOTIFY_REG = 0x50;
var VIRTIO_INTERRUPTSTATUS_REG = 0x60;
var VIRTIO_INTERRUPTACK_REG = 0x64;
var VIRTIO_STATUS_REG = 0x70;

/** @const */
var VRING_DESC_F_NEXT =      1; /* This marks a buffer as continuing via the next field. */
/** @const */
var VRING_DESC_F_WRITE =     2; /* This marks a buffer as write-only (otherwise read-only). */
/** @const */
var VRING_DESC_F_INDIRECT =  4; /* This means the buffer contains a list of buffer descriptors. */

function Swap16(x)
{
    // OR1K is big endian, therefore no conversion needed for us
    return x;
}
function Swap32(x)
{
    return x;
}

function abort()
{
    if(DEBUG)
    {
        throw "abort";
    }
}

function hex8(n)
{
    return h(n);
}

/** @param {...string} log */
function DebugMessage(log)
{
    if(DEBUG)
    {
        console.log.apply(console, arguments);
    }
}

function LoadXMLResource(url, OnSuccess, OnError) {
    var req = new XMLHttpRequest();
    req.open('GET', url, true);
    //req.overrideMimeType('text/xml');
    req.onreadystatechange = function () {
        if (req.readyState != 4) {
            return;
        }
        if ((req.status != 200) && (req.status != 0)) {
            OnError("Error: Could not load XML file " + url);
            return;
        }
        OnSuccess(req.responseText);
    };
    req.send(null);
}

function LoadBinaryResource(url, OnSuccess, OnError) {
    var req = new XMLHttpRequest();
    req.open('GET', url, true);
    req.responseType = "arraybuffer";
    req.onreadystatechange = function () {
        if (req.readyState != 4) {
            return;
        }
        if ((req.status != 200) && (req.status != 0)) {
            OnError("Error: Could not load file " + url);
            return;
        }
        var arrayBuffer = req.response;
        if (arrayBuffer) {
            OnSuccess(arrayBuffer);
        } else {
            OnError("Error: No data received from: " + url);
        }
    };
    /*
        req.onload = function(e)
        {
                var arrayBuffer = req.response;
                if (arrayBuffer) {
                    OnLoadFunction(arrayBuffer);
                }
        };
    */
    req.send(null);
}

<|endoftext|>


<commit_before>'use strict';

app.config([ '$locationProvider', function($locationProvider) {
	$locationProvider.html5Mode( {
		enabled: true,
		requireBase: false
	});
}]);

/*
app.provider('data', function() {
// Lookup Component. AutoGrouping in case more than once instances have the same attributes & Lookup Component. When adding row, get data from existing instance
//  Store returned REST/Json data to a Hash Array lookup/url+lu-data etc and check the Hash for existing data e.g. (app.config?) Data[crc32(url string) + "-" + crc32(lu-data string)] (unique?) = json data
//var DATA_STORAGE = {};
	this.pushData = function(srv) {
		srv.url, srv.dataset, srv.data
	};

	this.$get = function() {
		return function(srv) {

		};
	};
});
*/

app.component("lookup", {
	template: '<select ng-model="$ctrl.parentNgModel" ng-change="$ctrl.updateLookup()" name="{{$ctrl.luName}}"  id="{{$ctrl.luName}}" def="{{$ctrl.def}}" ng-options="option{{$ctrl.luValue}} as option{{$ctrl.luOption}} for option in $ctrl.data | orderBy:\'{{$ctrl.luOrder}}\'" class="slookup" required><option value=""></option></select>',
	bindings: {
		parentNgModel: '=',
		lookup: '@',
		luName: '@',
		name: '@',
		luValue : '@' ,
		luOption : '@' ,
		luOrder : '@' ,
		luData: '@',
		def : '@' ,
		autoselect: '@',
		hideonautoselect: '@',
		scp: '='
	},
	controller: ['$http', '$translate', '$location', function($http, $translate, $location) {
		var parent = this;
		this.$onInit = function() {
			if (!parent.luOption.toString().startsWith('[')) {
				parent.luOption = '.' + parent.luOption;
			}
			if (!parent.luValue.toString().startsWith('[')) {
				parent.luValue = '.' + parent.luValue;
			} else {
				parent.luValueStr = parent.luValue.replace(/(\[|\'|\])/g, "");
			}
			if (parent.luOrder) {
				parent.luOrder = parent.luOrder.toString().replace(/'/g, "\\\'");
			}
			$http.get(parent.lookup).then(function(response) {
				parent.data = response.data[parent.luData];
				if (parent.autoselect) {
					let qs = $location.search()[parent.autoselect];
					let fdata = parent.data.filter(function (item) {
						if (item[parent.luValueStr] === qs) return true;
					});
					if (fdata.length > 0) {
						parent.parentNgModel = qs;
						if (parent.hideonautoselect == 1 && $location.search()[parent.autoselect]){
							parent.scp['h_' + parent.name] = 1;
						}
					}
				}
			}, function error(response) {
				console.error("response", response);
			});
		};
	}]
});<commit_msg>Adding Data caching at EEA Lookup Component<commit_after>'use strict';

app.config([ '$locationProvider', function($locationProvider) {
	$locationProvider.html5Mode( {
		enabled: true,
		requireBase: false
	});
}]);

/*
app.provider('data', function() {
// Lookup Component. AutoGrouping in case more than once instances have the same attributes & Lookup Component. When adding row, get data from existing instance
//  Store returned REST/Json data to a Hash Array lookup/url+lu-data etc and check the Hash for existing data e.g. (CRC?) Data[crc32(url string) + "-" + crc32(lu-data string)] (unique?) = json data
//var DATA_STORAGE = {};
	this.pushData = function(srv) {
		srv.url, srv.dataset, srv.data
	};

	this.$get = function() {
		return function(srv) {

		};
	};
});
*/

app.component("lookup", {
	template: '<select ng-model="$ctrl.parentNgModel" ng-change="$ctrl.updateLookup()" name="{{$ctrl.luName}}"  id="{{$ctrl.luName}}" def="{{$ctrl.def}}" ng-options="option{{$ctrl.luValue}} as option{{$ctrl.luOption}} for option in $ctrl.data | orderBy:\'{{$ctrl.luOrder}}\'" class="slookup" required><option value=""></option></select>',
	bindings: {
		parentNgModel: '=',
		lookup: '@',
		luName: '@',
		name: '@',
		luValue : '@' ,
		luOption : '@' ,
		luOrder : '@' ,
		luData: '@',
		def : '@' ,
		autoselect: '@',
		hideonautoselect: '@',
		scp: '='
	},
	controller: ['$http', '$translate', '$location', function($http, $translate, $location) {
		var parent = this;
		this.$onInit = function() {
			if (!parent.luOption.toString().startsWith('[')) {
				parent.luOption = '.' + parent.luOption;
			}
			if (!parent.luValue.toString().startsWith('[')) {
				parent.luValue = '.' + parent.luValue;
			} else {
				parent.luValueStr = parent.luValue.replace(/(\[|\'|\])/g, "");
			}
			if (parent.luOrder) {
				parent.luOrder = parent.luOrder.toString().replace(/'/g, "\\\'");
			}
			$http.get(parent.lookup).then(function(response) {
				parent.data = response.data[parent.luData];
				if (parent.autoselect) {
					let qs = $location.search()[parent.autoselect];
					let fdata = parent.data.filter(function (item) {
						if (item[parent.luValueStr] === qs) return true;
					});
					if (fdata.length > 0) {
						parent.parentNgModel = qs;
						if (parent.hideonautoselect == 1 && $location.search()[parent.autoselect]){
							parent.scp['h_' + parent.name] = 1;
						}
					}
				}
			}, function error(response) {
				console.error("response", response);
			});
		};
	}]
});<|endoftext|>


<commit_before>/*
 * gulpfile.js
 *
 * Description: The Gophish gulpfile
 */

var gulp = require('gulp'),
    jshint = require('gulp-jshint'),
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    uglify = require('gulp-uglify'),
    wrap = require('gulp-wrap'),
    cleanCSS = require('gulp-clean-css'),

    js_directory = 'static/js/src/',
    css_directory = 'static/css/',
    vendor_directory = js_directory + 'vendor/',
    app_directory = js_directory + 'app/**/*.js',
    dest_js_directory = 'static/js/dist/',
    dest_css_directory = 'static/css/dist/';

gulp.task('default', ['watch']);

gulp.task('jshint', function() {
    return gulp.src(js_directory)
        .pipe(jshint())
        .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('build', function() {
    // Vendor minifying / concat
    gulp.src([
            vendor_directory + 'jquery.js',
            vendor_directory + 'bootstrap.min.js',
            vendor_directory + 'moment.min.js',
            vendor_directory + 'chartist.min.js',
            vendor_directory + 'papaparse.min.js',
            vendor_directory + 'd3.min.js',
            vendor_directory + 'topojson.min.js',
            vendor_directory + 'datamaps.min.js',
            vendor_directory + 'jquery.dataTables.min.js',
            vendor_directory + 'dataTables.bootstrap.js',
            vendor_directory + 'datetime-moment.js',
            vendor_directory + 'hogan.js',
            vendor_directory + 'jquery.ui.widget.js',
            vendor_directory + 'jquery.fileupload.js',
            vendor_directory + 'jquery.iframe-transport.js',
            vendor_directory + 'sweetalert2.min.js',
            vendor_directory + 'typeahead.min.js',
            vendor_directory + 'bootstrap-datetime.js'
        ])
        .pipe(concat('vendor.js'))
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(uglify())
        .pipe(gulp.dest(dest_js_directory));

    // Gophish app files
    gulp.src(app_directory)
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(uglify())
        .pipe(gulp.dest(dest_js_directory + 'app/'));

    return gulp.src([
            css_directory + 'bootstrap.min.css',
            css_directory + 'main.css',
            css_directory + 'dashboard.css',
            css_directory + 'flat-ui.css',
            css_directory + 'dataTables.bootstrap.css',
            css_directory + 'font-awesome.min.css',
            css_directory + 'chartist.min.css',
            css_directory + 'bootstrap-datetime.css',
            css_directory + 'checkbox.css',
            css_directory + 'sweetalert2.min.css'
        ])
        .pipe(cleanCSS({compatibilty: 'ie9'}))
        .pipe(concat('gophish.css'))
        .pipe(gulp.dest(dest_css_directory));
});

gulp.task('watch', function() {
    gulp.watch('static/src/**/*.js', ['jshint']);
});
<commit_msg>Fixed gulpfile "watch" path<commit_after>/*
 * gulpfile.js
 *
 * Description: The Gophish gulpfile
 */

var gulp = require('gulp'),
    jshint = require('gulp-jshint'),
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    uglify = require('gulp-uglify'),
    wrap = require('gulp-wrap'),
    cleanCSS = require('gulp-clean-css'),

    js_directory = 'static/js/src/',
    css_directory = 'static/css/',
    vendor_directory = js_directory + 'vendor/',
    app_directory = js_directory + 'app/**/*.js',
    dest_js_directory = 'static/js/dist/',
    dest_css_directory = 'static/css/dist/';

gulp.task('default', ['watch']);

gulp.task('jshint', function() {
    return gulp.src(js_directory)
        .pipe(jshint())
        .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('build', function() {
    // Vendor minifying / concat
    gulp.src([
            vendor_directory + 'jquery.js',
            vendor_directory + 'bootstrap.min.js',
            vendor_directory + 'moment.min.js',
            vendor_directory + 'chartist.min.js',
            vendor_directory + 'papaparse.min.js',
            vendor_directory + 'd3.min.js',
            vendor_directory + 'topojson.min.js',
            vendor_directory + 'datamaps.min.js',
            vendor_directory + 'jquery.dataTables.min.js',
            vendor_directory + 'dataTables.bootstrap.js',
            vendor_directory + 'datetime-moment.js',
            vendor_directory + 'hogan.js',
            vendor_directory + 'jquery.ui.widget.js',
            vendor_directory + 'jquery.fileupload.js',
            vendor_directory + 'jquery.iframe-transport.js',
            vendor_directory + 'sweetalert2.min.js',
            vendor_directory + 'typeahead.min.js',
            vendor_directory + 'bootstrap-datetime.js'
        ])
        .pipe(concat('vendor.js'))
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(uglify())
        .pipe(gulp.dest(dest_js_directory));

    // Gophish app files
    gulp.src(app_directory)
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(uglify())
        .pipe(gulp.dest(dest_js_directory + 'app/'));

    return gulp.src([
            css_directory + 'bootstrap.min.css',
            css_directory + 'main.css',
            css_directory + 'dashboard.css',
            css_directory + 'flat-ui.css',
            css_directory + 'dataTables.bootstrap.css',
            css_directory + 'font-awesome.min.css',
            css_directory + 'chartist.min.css',
            css_directory + 'bootstrap-datetime.css',
            css_directory + 'checkbox.css',
            css_directory + 'sweetalert2.min.css'
        ])
        .pipe(cleanCSS({compatibilty: 'ie9'}))
        .pipe(concat('gophish.css'))
        .pipe(gulp.dest(dest_css_directory));
});

gulp.task('watch', function() {
    gulp.watch('static/js/src/app/**/*.js', ['jshint']);
});
<|endoftext|>


<commit_before>define(["Filer"], function(Filer) {

  describe('fs.utimes', function() {
    beforeEach(function() {
      this.db_name = mk_db_name();
      this.fs = new Filer.FileSystem({
        name: this.db_name,
        flags: 'FORMAT'
      });
    });

    afterEach(function() {
      indexedDB.deleteDatabase(this.db_name);
      delete this.fs;
    });

    it('should be a function', function() {
      expect(typeof this.fs.utimes).toEqual('function');
    });

    it('should error when atime is negative', function () {
      var complete = false;
      var _error;
      var that = this;

      that.fs.writeFile('/testfile', '', function(error) {
        if (error) throw error;

        that.fs.utimes('/testfile', -1, Date.now(), function (error) {
          _error = error;
          complete = true;
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EInvalid');
      });
    });

    it('should error when mtime is negative', function () {
      var complete = false;
      var _error;
      var that = this;

      that.fs.writeFile('/testfile', '', function(error) {
        if (error) throw error;

        that.fs.utimes('/testfile', Date.now(), -1, function (error) {
          _error = error;
          complete = true;
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EInvalid');
      });
    });

    it('should error when atime is as invalid number', function () {
      var complete = false;
      var _error;
      var that = this;

      that.fs.writeFile('/testfile', '', function (error) {
        if (error) throw error;

        that.fs.utimes('/testfile', 'invalid datetime', Date.now(), function (error) {
          _error = error;
          complete = true;
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EInvalid');
      });
    });

    it ('should error when path does not exist', function () {
      var complete = false;
      var _error;
      var that = this;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.utimes('/pathdoesnotexist', atime, mtime, function (error) {
        _error = error;
        complete = true;
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('ENoEntry');
      });
    });

    it('should error when mtime is an invalid number', function () {
      var complete = false;
      var _error;
      var that = this;

      that.fs.writeFile('/testfile', '', function (error) {
        if (error) throw error;

        that.fs.utimes('/testfile', Date.now(), 'invalid datetime', function (error) {
          _error = error;
          complete = true;
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EInvalid');
      });
    });

    it ('should error when file descriptor is invalid', function () {
      var complete = false;
      var _error;
      var that = this;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.futimes(1, atime, mtime, function (error) {
        _error = error;
        complete = true;
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EBadFileDescriptor');
      });
    });

    it('should change atime and mtime of a file path', function () {
      var complete = false;
      var _error;
      var that = this;

      var _stat;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.writeFile('/testfile', '', function (error) {
        if (error) throw error;

        that.fs.utimes('/testfile', atime, mtime, function (error) {
          _error = error;

          that.fs.stat('/testfile', function (error, stat) {
            if (error) throw error;

            _stat = stat;
            complete = true;
          });
        });
      });

      waitsFor(function() {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function() {
        expect(_error).toEqual(null);
        expect(_stat.atime).toEqual(atime);
        expect(_stat.mtime).toEqual(mtime);
      });
    });

    it ('should change atime and mtime for a valid file descriptor', function (error) {
      var complete = false;
      var _error;
      var that = this;

      var ofd;
      var _stat;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.open('/testfile', 'w', function (error, result) {
        if (error) throw error;

        ofd = result;

        that.fs.futimes(ofd, atime, mtime, function (error) {
          _error = error;

          that.fs.fstat(ofd, function (error, stat) {
            if (error) throw error;

            _stat = stat;
            complete = true;
          });
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toEqual(null);
        expect(_stat.atime).toEqual(atime);
        expect(_stat.mtime).toEqual(mtime);
      });
    });

    it ('should update atime and mtime of directory path', function (error) {
      var complete = false;
      var _error;
      var that = this;

      var _stat;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.mkdir('/testdir', function (error) {
        if (error) throw error;

        that.fs.utimes('/testdir', atime, mtime, function (error) {
          _error = error;

          fs.stat('/testdir', function (error, stat) {
            if (error) throw error;

            _stat = stat;
            complete = true;
          });
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toEqual(null);
        expect(_stat.atime).toEqual(atime);
        expect(_stat.mtime).toEqual(mtime);
      });
    });

    it ('should update atime and mtime using current time if arguments are null', function () {
      var complete = false;
      var _error;
      var that = this;

      var atimeEst;
      var mtimeEst;
      var now;

      that.fs.writeFile('/myfile', '', function (error) {
        if (error) throw error;

        that.fs.utimes('/myfile', null, null, function (error) {
          _error = error;

          now = Date.now();

          that.fs.stat('/myfile', function (error, stat) {
            if (error) throw error;

            atimeEst = now - stat.atime;
            mtimeEst = now - stat.mtime;
            complete = true;
          });
        });
      });

      waitsFor(function (){
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toEqual(null);
        // Note: testing estimation as time may differ by a couple of milliseconds
        // This number should be increased if tests are on slow systems
        expect(atimeEst).toBeLessThan(10);
        expect(mtimeEst).toBeLessThan(10);
      });
    });
  });
});<commit_msg>test case fix<commit_after>define(["Filer"], function(Filer) {

  describe('fs.utimes', function() {
    beforeEach(function() {
      this.db_name = mk_db_name();
      this.fs = new Filer.FileSystem({
        name: this.db_name,
        flags: 'FORMAT'
      });
    });

    afterEach(function() {
      indexedDB.deleteDatabase(this.db_name);
      delete this.fs;
    });

    it('should be a function', function() {
      expect(typeof this.fs.utimes).toEqual('function');
    });

    it('should error when atime is negative', function () {
      var complete = false;
      var _error;
      var that = this;

      that.fs.writeFile('/testfile', '', function(error) {
        if (error) throw error;

        that.fs.utimes('/testfile', -1, Date.now(), function (error) {
          _error = error;
          complete = true;
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EInvalid');
      });
    });

    it('should error when mtime is negative', function () {
      var complete = false;
      var _error;
      var that = this;

      that.fs.writeFile('/testfile', '', function(error) {
        if (error) throw error;

        that.fs.utimes('/testfile', Date.now(), -1, function (error) {
          _error = error;
          complete = true;
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EInvalid');
      });
    });

    it('should error when atime is as invalid number', function () {
      var complete = false;
      var _error;
      var that = this;

      that.fs.writeFile('/testfile', '', function (error) {
        if (error) throw error;

        that.fs.utimes('/testfile', 'invalid datetime', Date.now(), function (error) {
          _error = error;
          complete = true;
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EInvalid');
      });
    });

    it ('should error when path does not exist', function () {
      var complete = false;
      var _error;
      var that = this;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.utimes('/pathdoesnotexist', atime, mtime, function (error) {
        _error = error;
        complete = true;
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('ENoEntry');
      });
    });

    it('should error when mtime is an invalid number', function () {
      var complete = false;
      var _error;
      var that = this;

      that.fs.writeFile('/testfile', '', function (error) {
        if (error) throw error;

        that.fs.utimes('/testfile', Date.now(), 'invalid datetime', function (error) {
          _error = error;
          complete = true;
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EInvalid');
      });
    });

    it ('should error when file descriptor is invalid', function () {
      var complete = false;
      var _error;
      var that = this;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.futimes(1, atime, mtime, function (error) {
        _error = error;
        complete = true;
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toBeDefined();
        expect(_error.name).toEqual('EBadFileDescriptor');
      });
    });

    it('should change atime and mtime of a file path', function () {
      var complete = false;
      var _error;
      var that = this;

      var _stat;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.writeFile('/testfile', '', function (error) {
        if (error) throw error;

        that.fs.utimes('/testfile', atime, mtime, function (error) {
          _error = error;

          that.fs.stat('/testfile', function (error, stat) {
            if (error) throw error;

            _stat = stat;
            complete = true;
          });
        });
      });

      waitsFor(function() {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function() {
        expect(_error).toEqual(null);
        expect(_stat.atime).toEqual(atime);
        expect(_stat.mtime).toEqual(mtime);
      });
    });

    it ('should change atime and mtime for a valid file descriptor', function (error) {
      var complete = false;
      var _error;
      var that = this;

      var ofd;
      var _stat;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.open('/testfile', 'w', function (error, result) {
        if (error) throw error;

        ofd = result;

        that.fs.futimes(ofd, atime, mtime, function (error) {
          _error = error;

          that.fs.fstat(ofd, function (error, stat) {
            if (error) throw error;

            _stat = stat;
            complete = true;
          });
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toEqual(null);
        expect(_stat.atime).toEqual(atime);
        expect(_stat.mtime).toEqual(mtime);
      });
    });

    it ('should update atime and mtime of directory path', function (error) {
      var complete = false;
      var _error;
      var that = this;

      var _stat;

      var atime = Date.parse('1 Oct 2000 15:33:22');
      var mtime = Date.parse('30 Sep 2000 06:43:54');

      that.fs.mkdir('/testdir', function (error) {
        if (error) throw error;

        that.fs.utimes('/testdir', atime, mtime, function (error) {
          _error = error;

          that.fs.stat('/testdir', function (error, stat) {
            if (error) throw error;

            _stat = stat;
            complete = true;
          });
        });
      });

      waitsFor(function () {
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toEqual(null);
        expect(_stat.atime).toEqual(atime);
        expect(_stat.mtime).toEqual(mtime);
      });
    });

    it ('should update atime and mtime using current time if arguments are null', function () {
      var complete = false;
      var _error;
      var that = this;

      var atimeEst;
      var mtimeEst;
      var now;

      that.fs.writeFile('/myfile', '', function (error) {
        if (error) throw error;

        that.fs.utimes('/myfile', null, null, function (error) {
          _error = error;

          now = Date.now();

          that.fs.stat('/myfile', function (error, stat) {
            if (error) throw error;

            atimeEst = now - stat.atime;
            mtimeEst = now - stat.mtime;
            complete = true;
          });
        });
      });

      waitsFor(function (){
        return complete;
      }, 'test to complete', DEFAULT_TIMEOUT);

      runs(function () {
        expect(_error).toEqual(null);
        // Note: testing estimation as time may differ by a couple of milliseconds
        // This number should be increased if tests are on slow systems
        expect(atimeEst).toBeLessThan(10);
        expect(mtimeEst).toBeLessThan(10);
      });
    });
  });
});<|endoftext|>


<commit_before>/**!
 * Originally written by:
 * https://github.com/sindresorhus/parse-json
 */
var fallback = require('../vendor/parse'),
    bomb = require('./bomb'),
    FALLBACK_MODE = 'json',
    ERROR_NAME = 'JSONError',
    FUNCTION = 'function',
    NUM_ZERO = 0,

    parse; // fn

/**
 * Accept a string as JSON and return an object.
 * @private
 *
 * @param {String} str The input stringified JSON object to be parsed.
 * @param {Function=} [reviver] A customizer function to be used within the fallback (BOM friendly) JSON parser.
 * @param {Boolean=} [strict] Set to true to treat the occurrence of BOM as a fatal error.
 *
 * @returns {Object} The parsed JSON object constructed from str
 * @throws {SyntaxError} If `str` is not a valid JSON
 * @throws {SyntaxError} In `strict` mode if `str` contains BOM
 */
parse = function (str, reviver, strict) {
    var bomMarkerIndex = bomb.indexOfBOM(str);

    if (bomMarkerIndex) {
        if (strict) {
            throw SyntaxError('Unexpected byte order mark found in first ' + bomMarkerIndex + ' character(s)');
        }
        // clean up BOM if not strict
        str = str.slice(bomMarkerIndex);
    }

    try { // first try and use normal JSON.parse as this is faster
        return JSON.parse(str, reviver);
    }
    catch (err) { // if JSON.parse fails, we try using a more verbose parser
        fallback.parse(str, {
            mode: FALLBACK_MODE,
            reviver: reviver
        });

        // if there was an error in this catch block, `fallback.parse` should raise same error. hence this `throw`
        // will never get executed. if it does not, we still throw original error.
        throw err;
    }
};

module.exports = {
    parse: function (str, reviver, relaxed) {
        if ((typeof reviver === 'boolean') && (relaxed === null)) {
            relaxed = reviver;
            reviver = null;
        }

        try {
            return parse(str, reviver, relaxed);
        }
        // we do this simply to set the error name and as such making it more identifiable
        catch (err) {
            err.name = ERROR_NAME;
            throw err;
        }
    },

    stringify: function () {
        try {
            // eslint-disable-next-line prefer-spread
            return JSON.stringify.apply(JSON, arguments);
        }
        // we do this simply to set the error name and as such making it more identifiable
        catch (err) {
            err.name = ERROR_NAME;
            throw err;
        }
    },

    async: {
        parse: function (text, options, callback) {
            // normalise polymorphic function to make `options` optional
            if (typeof options === FUNCTION && !callback) {
                callback = options;
                options = null;
            }

            // check whether callback is a function and otherwise unset to avoid erroneous calls
            (typeof callback !== FUNCTION) && (callback = null);

            // move up the event loop to make this asynchronous
            setTimeout(function () {
                var reviver,
                    relaxed,
                    json;

                // get options, if provided by user
                if (options) {
                    reviver = options.reviver;
                    relaxed = options.relaxed;
                }

                try {
                    json = parse(text, reviver, relaxed);
                }
                catch (err) {
                    err.name = ERROR_NAME;
                    callback && callback(err);
                }

                callback && callback(null, json);
            }, NUM_ZERO);
        },
        stringify: function (value, options, callback) {
            // normalise polymorphic function to make `options` optional
            if (typeof options === FUNCTION && !callback) {
                callback = options;
                options = null;
            }

            // check whether callback is a function and otherwise unset to avoid erroneous calls
            (typeof callback !== FUNCTION) && (callback = null);

            // move up the event loop to make this asynchronous
            setTimeout(function () {
                var replacer,
                    indent,
                    str;

                // get options, if provided by user
                if (options) {
                    replacer = options.replacer;
                    indent = options.indent;
                }

                try {
                    str = JSON.stringify(value, replacer, indent);
                }
                catch (err) {
                    err.name = ERROR_NAME;
                    callback && callback(err);
                }

                callback && callback(null, str);
            }, NUM_ZERO);
        }
    }
};
<commit_msg>Added documentation to functions<commit_after>/**!
 * Originally written by:
 * https://github.com/sindresorhus/parse-json
 */
var fallback = require('../vendor/parse'),
    bomb = require('./bomb'),
    FALLBACK_MODE = 'json',
    ERROR_NAME = 'JSONError',
    BOOLEAN = 'boolean',
    FUNCTION = 'function',
    NUM_ZERO = 0,

    parse; // fn

/**
 * Accept a string as JSON and return an object.
 * @private
 *
 * @param {String} text The input stringified JSON object to be parsed.
 * @param {Function=} [reviver] A customizer function to be used within the fallback (BOM friendly) JSON parser.
 * @param {Boolean=} [strict] Set to true to treat the occurrence of BOM as a fatal error.
 *
 * @returns {Object} The parsed JSON object constructed from text
 * @throws {SyntaxError} If `text` is not a valid JSON
 * @throws {SyntaxError} In `strict` mode if `text` contains BOM
 *
 * @returns {Object} - Returns JS Object derived after parsing `text` parameter
 */
parse = function (text, reviver, strict) {
    var bomMarkerIndex = bomb.indexOfBOM(text);

    if (bomMarkerIndex) {
        if (strict) {
            throw SyntaxError('Unexpected byte order mark found in first ' + bomMarkerIndex + ' character(s)');
        }
        // clean up BOM if not strict
        text = text.slice(bomMarkerIndex);
    }

    try { // first try and use normal JSON.parse as this is faster
        return JSON.parse(text, reviver);
    }
    catch (err) { // if JSON.parse fails, we try using a more verbose parser
        fallback.parse(text, {
            mode: FALLBACK_MODE,
            reviver: reviver
        });

        // if there was an error in this catch block, `fallback.parse` should raise same error. hence this `throw`
        // will never get executed. if it does not, we still throw original error.
        throw err;
    }
};

module.exports = {
    /**
     * Converts a JavaScript value to a JSON string, optionally replacing values if a replacer function is
     * specified, or optionally including only the specified properties if a replacer array is specified.
     *
     * @param {Object|Array} text - The value to convert to a JSON string
     *
     * @param {Function=} replacer - A function that alters the behavior of the stringification process,
     * or an array of String and Number objects that serve as a whitelist for selecting/filtering the properties of
     * the value object to be included in the JSON string. If this value is null or not provided, all properties of
     * the object are included in the resulting JSON string.
     *
     * @param  {Number|String=} indent A String or Number object that's used to insert white space into the output
     * JSON string for readability purposes. If this is a Number, it indicates the number of space characters to use
     * as white space; this number is capped at 10 if it's larger than that. Values less than 1 indicate that
     * no space should be used. If this is a String, the string (or the first 10 characters of the string, if it's
     * longer than that) is used as white space. If this parameter is not provided (or is null), no white space is
     * used.
     *
     * @returns {Object}
     *
     * @throws {JSONError} If the `text` parameter contains invalid JSON syntax
     */
    parse: function (text, reviver, relaxed) {
        if ((typeof reviver === BOOLEAN) && (relaxed === null)) {
            relaxed = reviver;
            reviver = null;
        }

        try {
            return parse(text, reviver, relaxed);
        }
        // we do this simply to set the error name and as such making it more identifiable
        catch (err) {
            err.name = ERROR_NAME;
            throw err;
        }
    },

    /**
     * Converts a JavaScript value to a JSON string, optionally replacing values if a replacer function is
     * specified, or optionally including only the specified properties if a replacer array is specified.
     *
     * @param {Object|Array} value - The value to convert to a JSON string
     *
     * @param {Function=} replacer - A function that alters the behavior of the stringification process,
     * or an array of String and Number objects that serve as a whitelist for selecting/filtering the properties of
     * the value object to be included in the JSON string. If this value is null or not provided, all properties of
     * the object are included in the resulting JSON string.
     *
     * @param  {Number|String=} indent A String or Number object that's used to insert white space into the output
     * JSON string for readability purposes. If this is a Number, it indicates the number of space characters to use
     * as white space; this number is capped at 10 if it's larger than that. Values less than 1 indicate that
     * no space should be used. If this is a String, the string (or the first 10 characters of the string, if it's
     * longer than that) is used as white space. If this parameter is not provided (or is null), no white space is
     * used.
     *
     * @returns {String}
     *
     * @throws {JSONError} If the value cannot be converted to JSON
     */
    stringify: function (value, replacer, indent) {
        try {
            // eslint-disable-next-line prefer-spread
            return JSON.stringify(value, replacer, indent);
        }
        // we do this simply to set the error name and as such making it more identifiable
        catch (err) {
            err.name = ERROR_NAME;
            throw err;
        }
    },

    async: {
        /**
         * Parses a JSON string, constructing the JavaScript value or object described by the string. An optional
         * reviver function can be provided to perform a transformation on the resulting object before it is returned.
         *
         * This is an asynchronous variant of JSON.parse() and accepts a callback where it returns the result as
         * well as the error (if any.) It does not throw anything that one would otherwise need to trap within a
         * try-catch block.
         *
         * @param {String} text - The string to parse as JSON
         *
         * @param {Object=} options
         *
         * @param {Function=} options.reviver - If a function, prescribes how the value originally produced by parsing
         * is transformed, before being returned.
         *
         * @param {Boolean=true} options.relaxed - When set to `false`, this causes the presence of `BOM` mark in `text`
         * parameter as a fatal error. Otherwise, the BOM is stripped out before parsing.
         *
         * @param {Function} callback - receives parameters `err:Error, json:Object`
         */
        parse: function (text, options, callback) {
            // normalise polymorphic function to make `options` optional
            if (typeof options === FUNCTION && !callback) {
                callback = options;
                options = null;
            }

            // check whether callback is a function and otherwise unset to avoid erroneous calls
            (typeof callback !== FUNCTION) && (callback = null);

            // move up the event loop to make this asynchronous
            setTimeout(function () {
                var reviver,
                    relaxed,
                    json;

                // get options, if provided by user
                if (options) {
                    reviver = options.reviver;
                    relaxed = options.relaxed;
                }

                try {
                    json = parse(text, reviver, relaxed);
                }
                catch (err) {
                    err.name = ERROR_NAME;
                    callback && callback(err);
                }

                callback && callback(null, json);
            }, NUM_ZERO);
        },

        /**
         * Converts a JavaScript value to a JSON string, optionally replacing values if a replacer function is
         * specified, or optionally including only the specified properties if a replacer array is specified.
         *
         * This is an asynchronous variant of JSON.stringify() and accepts a callback where it returns the result as
         * well as the error (if any.) It does not throw anything that one would otherwise need to trap within a
         * try-catch block.
         *
         * @param {Object|Array} value - The value to convert to a JSON string
         *
         * @param {Object=} options
         *
         * @param {Function=} options.replacer - A function that alters the behavior of the stringification process,
         * or an array of String and Number objects that serve as a whitelist for selecting/filtering the properties of
         * the value object to be included in the JSON string. If this value is null or not provided, all properties of
         * the object are included in the resulting JSON string.
         *
         * @param  {Number|String=} options.indent A String or Number object that's used to insert white space into the
         * output JSON string for readability purposes. If this is a Number, it indicates the number of space characters
         * to use as white space; this number is capped at 10 if it's larger than that. Values less than 1 indicate that
         * no space should be used. If this is a String, the string (or the first 10 characters of the string, if it's
         * longer than that) is used as white space. If this parameter is not provided (or is null), no white space is
         * used.
         *
         * @param {Function} callback - receives parameters `err:Error, str:String`
         */
        stringify: function (value, options, callback) {
            // normalise polymorphic function to make `options` optional
            if (typeof options === FUNCTION && !callback) {
                callback = options;
                options = null;
            }

            // check whether callback is a function and otherwise unset to avoid erroneous calls
            (typeof callback !== FUNCTION) && (callback = null);

            // move up the event loop to make this asynchronous
            setTimeout(function () {
                var replacer,
                    indent,
                    str;

                // get options, if provided by user
                if (options) {
                    replacer = options.replacer;
                    indent = options.indent;
                }

                try {
                    str = JSON.stringify(value, replacer, indent);
                }
                catch (err) {
                    err.name = ERROR_NAME;
                    callback && callback(err);
                }

                callback && callback(null, str);
            }, NUM_ZERO);
        }
    }
};
<|endoftext|>