<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 socks = require('socksv5');
var EventEmitter = require('events').EventEmitter;
var extend = require('util')._extend;
var util = require('./util');
var logger = require('./util/logger');
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 pluginMgr = require('./plugins');
var config = require('./config');
var LOCALHOST = '127.0.0.1';
var WHISTLE_POLICY_HEADER = 'x-whistle-policy';
var LOCAL_URL_RE = new RegExp(config.localUIHost.replace(/\./g, '\\.') + '$', 'i');
var index = 0;

function tunnelProxy(server, proxy) {
	//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) {
				logger.error(err);
				return err;
			}
		};
	}

	process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
	server.on('connect', function(req, reqSocket, head) {//ws, wss, https proxy
		var tunnelUrl = req.fullUrl = util.setProtocol(/^[^:\/]+:\d+$/.test(req.url) ? req.url : req.headers.host, true);
		var options = parseUrl();
		var tunnelUrl = req.fullUrl = 'tunnel://' + options.host;
		var resSocket, proxySocket, responsed;
		req.isTunnel = true;
		var hostname = options.hostname;
		var _rules = req.rules = rules.resolveRules(tunnelUrl);
		var filter = rules.resolveFilter(tunnelUrl);
		var disable = rules.resolveDisable(tunnelUrl);
		var plugin = pluginMgr.getPluginByRuleUrl(util.rule.getUrl(_rules.rule));
		var rulesMgr = plugin && plugin.rulesMgr;
		util.handlePluginRules(req, rulesMgr);
		if (rulesMgr && !filter.rule) {
			extend(_rules, rulesMgr.resolveRules(req.fullUrl));
		}

		var useTunnelPolicy = req.headers[WHISTLE_POLICY_HEADER] == 'tunnel';
		if (!useTunnelPolicy && (LOCAL_URL_RE.test(hostname) || ((filter.https || filter.tunnel
				|| rulesUtil.properties.get('interceptHttpsConnects')) && !filter.intercept && !disable.intercept))) {
			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
		};
		req.clientIp = reqData.ip;
		abortIfUnavailable(reqSocket);
		if (!rulesUtil.properties.get('hideHttpsConnects') && !filter.hide) {
			proxy.emit('request', reqEmitter);
		}

		if (disable.tunnel) {
			return reqSocket.destroy();
		}

		var _plugin = plugin;
		if (!plugin && _rules.plugin) {
			var pluginRule = util.resolvePluginRule(_rules.plugin);
			if (pluginRule) {
				plugin = pluginMgr.getPlugin(pluginRule.name + ':');
				req.ruleValue = pluginRule.value;
			}
		}
		req.pluginMgr = plugin;
		req.reqId = ++index;
		pluginMgr.getTunnelRules(req, function(_rulesMgr) {
			if (_rulesMgr) {
				rulesMgr = _rulesMgr;
				var pluginRules = rulesMgr.resolveRules(tunnelUrl);
				var _disable = rulesMgr.resolveDisable(tunnelUrl);
				if (pluginRules.rule) {
					_rules.rule = pluginRules.rule;
				}
				if (!disable.tunnel && _disable.tunnel) {
					_rules.disable = pluginRules.disable;
					return reqSocket.destroy();
				}
				var _filter = rulesMgr.resolveFilter(tunnelUrl);
				if (!filter.rule && _filter.rule) {
				  _rules.filter = pluginRules.filter;
				  filter.rule = true;
				}
			}

			var tunnelPort;
			if (filter.rule) {
			  delete _rules.rule;
			} else {
			  tunnelPort = _plugin && _plugin.ports && _plugin.ports.tunnelPort;
			}
			var proxyUrl = tunnelPort ? 'proxy://127.0.0.1:' + tunnelPort : util.rule.getProxy(_rules.rule);
			if (!tunnelPort) {
			  delete headers[pluginMgr.CLIENT_IP_HEADER];
			  delete headers[pluginMgr.RULE_VALUE_HEADER];
			  delete headers[pluginMgr.REQ_ID_HEADER];
			}
			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);
          if (isSocks) {
            var dstOptions = url.parse(tunnelUrl);
            dstOptions.proxyHost = ip;
		        dstOptions.proxyPort = parseInt(options.port, 10) || 1080;
		        dstOptions.host = dstOptions.hostname;
		        dstOptions.localDNS = false;
		        dstOptions.auths = config.getAuths(options);
		        dstOptions.port = dstOptions.port || 443;
            socks.connect(dstOptions, function(_proxySocket) {
              proxySocket = _proxySocket;
  			      abortIfUnavailable(proxySocket);
  			      sendEstablished().pipe(proxySocket).pipe(reqSocket);
			      }).on('error', emitError);
          } else {
            util.connect({
              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 {
				var _url = _rules.rule && util.setProtocol(util.rule.getMatcher(_rules.rule), true);
				if (/^https:/i.test(_url)) {
					options = parseUrl(_url);
					data.realUrl = _url;
					tunnel(_url);
				} else {
					tunnel();
				}
			}
		});

		function tunnel(url, ip) {
			resolveHost(url || tunnelUrl, function(ip, port) {
				resData.ip = port ? ip + ':' + port : ip;
				resSocket = net.connect(port || 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) {
			rules.resolveHost(ip || url, function(err, ip, port, host) {
				if (host) {
					_rules.host = host;
				}
				data.requestTime = data.dnsTime = Date.now();
				resData.ip = ip || LOCALHOST;
				reqEmitter.emit('send', data);
				err ? emitError(err) : callback(ip, port);
			}, rulesMgr);
		}

		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(callback) {
	var app = express();
	var server = app.listen(config.port, callback);
	var proxyEvents = new EventEmitter();
	var middlewares = ['./init',
	               '../biz']
				   .concat(require('./inspectors'))
				   .concat(config.middlewares)
	               .concat(require('./handlers'));

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

	exportInterfaces(proxyEvents);
	tunnelProxy(server, proxyEvents);
	initDataServer(proxyEvents);
	initLogServer(proxyEvents);
	require('../biz/init')(proxyEvents);
	config.debug && rules.disableDnsCache();
	return proxyEvents;
}

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

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>refactor: Refine socks protocol v5<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 socks = require('socksv5');
var EventEmitter = require('events').EventEmitter;
var extend = require('util')._extend;
var util = require('./util');
var logger = require('./util/logger');
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 pluginMgr = require('./plugins');
var config = require('./config');
var LOCALHOST = '127.0.0.1';
var WHISTLE_POLICY_HEADER = 'x-whistle-policy';
var LOCAL_URL_RE = new RegExp(config.localUIHost.replace(/\./g, '\\.') + '$', 'i');
var index = 0;

function tunnelProxy(server, proxy) {
	//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) {
				logger.error(err);
				return err;
			}
		};
	}

	process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
	server.on('connect', function(req, reqSocket, head) {//ws, wss, https proxy
		var tunnelUrl = req.fullUrl = util.setProtocol(/^[^:\/]+:\d+$/.test(req.url) ? req.url : req.headers.host, true);
		var options = parseUrl();
		var tunnelUrl = req.fullUrl = 'tunnel://' + options.host;
		var resSocket, proxySocket, responsed;
		req.isTunnel = true;
		var hostname = options.hostname;
		var _rules = req.rules = rules.resolveRules(tunnelUrl);
		var filter = rules.resolveFilter(tunnelUrl);
		var disable = rules.resolveDisable(tunnelUrl);
		var plugin = pluginMgr.getPluginByRuleUrl(util.rule.getUrl(_rules.rule));
		var rulesMgr = plugin && plugin.rulesMgr;
		util.handlePluginRules(req, rulesMgr);
		if (rulesMgr && !filter.rule) {
			extend(_rules, rulesMgr.resolveRules(req.fullUrl));
		}

		var useTunnelPolicy = req.headers[WHISTLE_POLICY_HEADER] == 'tunnel';
		if (!useTunnelPolicy && (LOCAL_URL_RE.test(hostname) || ((filter.https || filter.tunnel
				|| rulesUtil.properties.get('interceptHttpsConnects')) && !filter.intercept && !disable.intercept))) {
			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
		};
		req.clientIp = reqData.ip;
		abortIfUnavailable(reqSocket);
		if (!rulesUtil.properties.get('hideHttpsConnects') && !filter.hide) {
			proxy.emit('request', reqEmitter);
		}

		if (disable.tunnel) {
			return reqSocket.destroy();
		}

		var _plugin = plugin;
		if (!plugin && _rules.plugin) {
			var pluginRule = util.resolvePluginRule(_rules.plugin);
			if (pluginRule) {
				plugin = pluginMgr.getPlugin(pluginRule.name + ':');
				req.ruleValue = pluginRule.value;
			}
		}
		req.pluginMgr = plugin;
		req.reqId = ++index;
		pluginMgr.getTunnelRules(req, function(_rulesMgr) {
			if (_rulesMgr) {
				rulesMgr = _rulesMgr;
				var pluginRules = rulesMgr.resolveRules(tunnelUrl);
				var _disable = rulesMgr.resolveDisable(tunnelUrl);
				if (pluginRules.rule) {
					_rules.rule = pluginRules.rule;
				}
				if (!disable.tunnel && _disable.tunnel) {
					_rules.disable = pluginRules.disable;
					return reqSocket.destroy();
				}
				var _filter = rulesMgr.resolveFilter(tunnelUrl);
				if (!filter.rule && _filter.rule) {
				  _rules.filter = pluginRules.filter;
				  filter.rule = true;
				}
			}

			var tunnelPort;
			if (filter.rule) {
			  delete _rules.rule;
			} else {
			  tunnelPort = _plugin && _plugin.ports && _plugin.ports.tunnelPort;
			}
			var proxyUrl = tunnelPort ? 'proxy://127.0.0.1:' + tunnelPort : util.rule.getProxy(_rules.rule);
			if (!tunnelPort) {
			  delete headers[pluginMgr.CLIENT_IP_HEADER];
			  delete headers[pluginMgr.RULE_VALUE_HEADER];
			  delete headers[pluginMgr.REQ_ID_HEADER];
			}
			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);
					var onConnect = function(_proxySocket) {
            proxySocket = _proxySocket;
            abortIfUnavailable(proxySocket);
            sendEstablished().pipe(proxySocket).pipe(reqSocket);
					};
          if (isSocks) {
            var dstOptions = url.parse(tunnelUrl);
            dstOptions.proxyHost = ip;
		        dstOptions.proxyPort = parseInt(options.port, 10) || 1080;
		        dstOptions.host = dstOptions.hostname;
		        dstOptions.localDNS = false;
		        dstOptions.auths = config.getAuths(options);
		        dstOptions.port = dstOptions.port || 443;
            socks.connect(dstOptions, onConnect).on('error', emitError);
          } else {
            util.connect({
              host: ip,
              port: options.port,
              auth: options.auth,
              url: 'http://' + headers.host,
              headers: headers
            }, function(err, _proxySocket) {
			        if (err) {
			          return emitError(err);
			        }
			        onConnect(_proxySocket);
			      });
		     }
				});
			} else {
				var _url = _rules.rule && util.setProtocol(util.rule.getMatcher(_rules.rule), true);
				if (/^https:/i.test(_url)) {
					options = parseUrl(_url);
					data.realUrl = _url;
					tunnel(_url);
				} else {
					tunnel();
				}
			}
		});

		function tunnel(url, ip) {
			resolveHost(url || tunnelUrl, function(ip, port) {
				resData.ip = port ? ip + ':' + port : ip;
				resSocket = net.connect(port || 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) {
			rules.resolveHost(ip || url, function(err, ip, port, host) {
				if (host) {
					_rules.host = host;
				}
				data.requestTime = data.dnsTime = Date.now();
				resData.ip = ip || LOCALHOST;
				reqEmitter.emit('send', data);
				err ? emitError(err) : callback(ip, port);
			}, rulesMgr);
		}

		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(callback) {
	var app = express();
	var server = app.listen(config.port, callback);
	var proxyEvents = new EventEmitter();
	var middlewares = ['./init',
	               '../biz']
				   .concat(require('./inspectors'))
				   .concat(config.middlewares)
	               .concat(require('./handlers'));

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

	exportInterfaces(proxyEvents);
	tunnelProxy(server, proxyEvents);
	initDataServer(proxyEvents);
	initLogServer(proxyEvents);
	require('../biz/init')(proxyEvents);
	config.debug && rules.disableDnsCache();
	return proxyEvents;
}

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

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";
// smtp network server

var net         = require('./tls_socket');
var logger      = require('./logger');
var config      = require('./config');
var conn        = require('./connection');
var out         = require('./outbound');
var plugins     = require('./plugins');
var constants   = require('./constants');
var os          = require('os');
var cluster     = require('cluster');
var async       = require('async');

var daemon;
try { daemon = require('daemon'); } // npm install daemon
catch (err) {}

// Need these here so we can run hooks
for (var key in logger) {
    if (key.match(/^log\w/)) {
        exports[key] = (function (key) {
            return function () {
                var args = ["[server] "];
                for (var i=0, l=arguments.length; i<l; i++) {
                    args.push(arguments[i]);
                }
                logger[key].apply(logger, args);
            }
        })(key);
    }
}

var Server = exports;

var defaults = {
    inactivity_timeout: 600,
    daemonize: false,
    daemon_log_file: '/var/log/haraka.log',
    daemon_pid_file: '/var/run/haraka.pid'
};

function apply_defaults(obj) {
    var key;
    for (key in defaults) {
        obj[key] = obj[key] || defaults[key];
    }
}

Server.daemonize = function (config_data) {
    if (/^(?:1|true|yes|enabled|on)$/i.test(config_data.main.daemonize)) {
        if (!daemon) {
            logger.logdebug('unable to daemonize; daemon module not installed');
        }
        else {
            daemon.daemonize(config_data.main.daemon_log_file,
                config_data.main.daemon_pid_file,
                function (err, pid) {
                    if (err) {
                        throw err;
                    }
                    logger.lognotice('daemon started with pid: ' + pid);
                }
            );
        }
    }
}

Server.createServer = function (params) {
    var config_data = config.get('smtp.ini');
    var param_key;
    for (param_key in params) {
        if (typeof params[param_key] !== 'function') {
            config_data.main[param_key] = params[param_key];
        }
    }

    // config_data defaults
    apply_defaults(config_data.main);

    var listeners = (config_data.main.listen || '').split(/\s*,\s*/);
    if (listeners[0] === '') listeners = [];
    if (config_data.main.port) {
        Server.default_host = true;
        var host = config_data.main.listen_host || '[::0]';
        listeners.unshift(host + ':' + config_data.main.port);
    }
    if (!listeners.length) {
        Server.default_host = true;
        listeners.push('[::0]:25');
    }

    Server.notes = {};
    plugins.server = Server;
    plugins.load_plugins();

    var inactivity_timeout = (config_data.main.inactivity_timeout || 300) * 1000;

    // Cluster
    if (cluster && config_data.main.nodes) {
        Server.cluster = cluster;
        if (cluster.isMaster) {
            this.daemonize(config_data);
            // Fork workers
            var workers = (config_data.main.nodes === 'cpus') ?
                os.cpus().length : config_data.main.nodes;
            for (var i=0; i<workers; i++) {
                cluster.fork({ CLUSTER_MASTER_PID: process.pid });
            }
            cluster.on('online', function (worker) {
                logger.lognotice('worker ' + worker.id + ' started pid=' + worker.process.pid);
            });
            cluster.on('listening', function (worker, address) {
                logger.lognotice('worker ' + worker.id + ' listening on ' + address.address + ':' + address.port);
            });
            cluster.on('exit', function (worker, code, signal) {
                if (signal) {
                    logger.lognotice('worker ' + worker.id + ' killed by signal ' + signal);
                }
                else if (code !== 0) {
                    logger.lognotice('worker ' + worker.id + ' exited with error code: ' + code);
                }
                if (signal || code !== 0) {
                    // Restart worker
                    cluster.fork({ CLUSTER_MASTER_PID: process.pid });
                }
            });
            plugins.run_hooks('init_master', Server);
        }
        else {
            // Workers
            setup_listeners(listeners, plugins, "child", inactivity_timeout);
        }
    }
    else {
        this.daemonize(config_data);
        setup_listeners(listeners, plugins, "master", inactivity_timeout);
    }
};

function setup_listeners (listeners, plugins, type, inactivity_timeout) {
    async.each(listeners, function (host_port, cb) {
        var hp = /^\[?([^\]]+)\]?:(\d+)$/.exec(host_port);
        if (!hp) {
            return cb(new Error("Invalid format for listen parameter in smtp.ini"));
        }

        var server = net.createServer(function (client) {
            client.setTimeout(inactivity_timeout);
            conn.createConnection(client, server);
        });

        server.notes = Server.notes;
        if (Server.cluster) server.cluster = Server.cluster;

        server.on('listening', function () {
            var addr = this.address();
            logger.lognotice("Listening on " + addr.address + ':' + addr.port);
            cb();
        });

        // Fallback from IPv6 to IPv4 if not supported
        // But only if we supplied the default of [::0]:25
        server.on('error', function (e) {
            if (e.code === 'EAFNOSUPPORT' && /^::0?/.test(hp[1]) && Server.default_host) {
                server.listen(hp[2], '0.0.0.0');
            }
            else {
                // Pass error to callback
                cb(e);
            }
        });

        server.listen(hp[2], hp[1]);
    }, function (err) {
        if (err) {
            logger.logerror("Failed to setup listeners: " + err.message);
            return process.exit(-1);
        }
        listening();
        plugins.run_hooks('init_' + type, Server);
    })
}

Server.init_master_respond = function (retval, msg) {
    Server.ready = 1;
    switch(retval) {
        case constants.ok:
        case constants.cont:
                out.load_queue();
                break;
        default:
                Server.logerror("init_master returned error" + ((msg) ? ': ' + msg : ''));
                process.exit(1);
    }
}

Server.init_child_respond = function (retval, msg) {
    switch(retval) {
        case constants.ok:
        case constants.cont:
                break;
        default:
                var pid = process.env.CLUSTER_MASTER_PID;
                Server.logerror("init_child returned error" + ((msg) ? ': ' + msg : ''));
                try {
                    if (pid) {
                        process.kill(pid);
                        Server.logerror('Killing master (pid=' + pid + ')');
                    }
                }
                catch (err) {
                    Server.logerror('Terminating child');
                }
                process.exit(1);
    }
}

function listening () {
    var config_data = config.get('smtp.ini');

    // Drop privileges
    if (config_data.main.group) {
        Server.lognotice('Switching from current gid: ' + process.getgid());
        process.setgid(config_data.main.group);
        Server.lognotice('New gid: ' + process.getgid());
    }
    if (config_data.main.user) {
        Server.lognotice('Switching from current uid: ' + process.getuid());
        process.setuid(config_data.main.user);
        Server.lognotice('New uid: ' + process.getuid());
    }

    Server.ready = 1;
}
<commit_msg>Throw error if IPv6 is implicitly specified and OS does not support it<commit_after>"use strict";
// smtp network server

var net         = require('./tls_socket');
var logger      = require('./logger');
var config      = require('./config');
var conn        = require('./connection');
var out         = require('./outbound');
var plugins     = require('./plugins');
var constants   = require('./constants');
var os          = require('os');
var cluster     = require('cluster');
var async       = require('async');

var daemon;
try { daemon = require('daemon'); } // npm install daemon
catch (err) {}

// Need these here so we can run hooks
for (var key in logger) {
    if (key.match(/^log\w/)) {
        exports[key] = (function (key) {
            return function () {
                var args = ["[server] "];
                for (var i=0, l=arguments.length; i<l; i++) {
                    args.push(arguments[i]);
                }
                logger[key].apply(logger, args);
            }
        })(key);
    }
}

var Server = exports;

var defaults = {
    inactivity_timeout: 600,
    daemonize: false,
    daemon_log_file: '/var/log/haraka.log',
    daemon_pid_file: '/var/run/haraka.pid'
};

function apply_defaults(obj) {
    var key;
    for (key in defaults) {
        obj[key] = obj[key] || defaults[key];
    }
}

Server.daemonize = function (config_data) {
    if (/^(?:1|true|yes|enabled|on)$/i.test(config_data.main.daemonize)) {
        if (!daemon) {
            logger.logdebug('unable to daemonize; daemon module not installed');
        }
        else {
            daemon.daemonize(config_data.main.daemon_log_file,
                config_data.main.daemon_pid_file,
                function (err, pid) {
                    if (err) {
                        throw err;
                    }
                    logger.lognotice('daemon started with pid: ' + pid);
                }
            );
        }
    }
}

Server.createServer = function (params) {
    var config_data = config.get('smtp.ini');
    var param_key;
    for (param_key in params) {
        if (typeof params[param_key] !== 'function') {
            config_data.main[param_key] = params[param_key];
        }
    }

    // config_data defaults
    apply_defaults(config_data.main);

    var listeners = (config_data.main.listen || '').split(/\s*,\s*/);
    if (listeners[0] === '') listeners = [];
    if (config_data.main.port) {
        var host = config_data.main.listen_host;
        if (!host) {
            host = '[::0]';
            Server.default_host = true;
        }
        listeners.unshift(host + ':' + config_data.main.port);
    }
    if (!listeners.length) {
        Server.default_host = true;
        listeners.push('[::0]:25');
    }

    Server.notes = {};
    plugins.server = Server;
    plugins.load_plugins();

    var inactivity_timeout = (config_data.main.inactivity_timeout || 300) * 1000;

    // Cluster
    if (cluster && config_data.main.nodes) {
        Server.cluster = cluster;
        if (cluster.isMaster) {
            this.daemonize(config_data);
            // Fork workers
            var workers = (config_data.main.nodes === 'cpus') ?
                os.cpus().length : config_data.main.nodes;
            for (var i=0; i<workers; i++) {
                cluster.fork({ CLUSTER_MASTER_PID: process.pid });
            }
            cluster.on('online', function (worker) {
                logger.lognotice('worker ' + worker.id + ' started pid=' + worker.process.pid);
            });
            cluster.on('listening', function (worker, address) {
                logger.lognotice('worker ' + worker.id + ' listening on ' + address.address + ':' + address.port);
            });
            cluster.on('exit', function (worker, code, signal) {
                if (signal) {
                    logger.lognotice('worker ' + worker.id + ' killed by signal ' + signal);
                }
                else if (code !== 0) {
                    logger.lognotice('worker ' + worker.id + ' exited with error code: ' + code);
                }
                if (signal || code !== 0) {
                    // Restart worker
                    cluster.fork({ CLUSTER_MASTER_PID: process.pid });
                }
            });
            plugins.run_hooks('init_master', Server);
        }
        else {
            // Workers
            setup_listeners(listeners, plugins, "child", inactivity_timeout);
        }
    }
    else {
        this.daemonize(config_data);
        setup_listeners(listeners, plugins, "master", inactivity_timeout);
    }
};

function setup_listeners (listeners, plugins, type, inactivity_timeout) {
    async.each(listeners, function (host_port, cb) {
        var hp = /^\[?([^\]]+)\]?:(\d+)$/.exec(host_port);
        if (!hp) {
            return cb(new Error("Invalid format for listen parameter in smtp.ini"));
        }

        var server = net.createServer(function (client) {
            client.setTimeout(inactivity_timeout);
            conn.createConnection(client, server);
        });

        server.notes = Server.notes;
        if (Server.cluster) server.cluster = Server.cluster;

        server.on('listening', function () {
            var addr = this.address();
            logger.lognotice("Listening on " + addr.address + ':' + addr.port);
            cb();
        });

        // Fallback from IPv6 to IPv4 if not supported
        // But only if we supplied the default of [::0]:25
        server.on('error', function (e) {
            if (e.code === 'EAFNOSUPPORT' && /^::0?/.test(hp[1]) && Server.default_host) {
                server.listen(hp[2], '0.0.0.0');
            }
            else {
                // Pass error to callback
                cb(e);
            }
        });

        server.listen(hp[2], hp[1]);
    }, function (err) {
        if (err) {
            logger.logerror("Failed to setup listeners: " + err.message);
            return process.exit(-1);
        }
        listening();
        plugins.run_hooks('init_' + type, Server);
    })
}

Server.init_master_respond = function (retval, msg) {
    Server.ready = 1;
    switch(retval) {
        case constants.ok:
        case constants.cont:
                out.load_queue();
                break;
        default:
                Server.logerror("init_master returned error" + ((msg) ? ': ' + msg : ''));
                process.exit(1);
    }
}

Server.init_child_respond = function (retval, msg) {
    switch(retval) {
        case constants.ok:
        case constants.cont:
                break;
        default:
                var pid = process.env.CLUSTER_MASTER_PID;
                Server.logerror("init_child returned error" + ((msg) ? ': ' + msg : ''));
                try {
                    if (pid) {
                        process.kill(pid);
                        Server.logerror('Killing master (pid=' + pid + ')');
                    }
                }
                catch (err) {
                    Server.logerror('Terminating child');
                }
                process.exit(1);
    }
}

function listening () {
    var config_data = config.get('smtp.ini');

    // Drop privileges
    if (config_data.main.group) {
        Server.lognotice('Switching from current gid: ' + process.getgid());
        process.setgid(config_data.main.group);
        Server.lognotice('New gid: ' + process.getgid());
    }
    if (config_data.main.user) {
        Server.lognotice('Switching from current uid: ' + process.getuid());
        process.setuid(config_data.main.user);
        Server.lognotice('New uid: ' + process.getuid());
    }

    Server.ready = 1;
}
<|endoftext|>


<commit_before>/* ----------------------- LOGGING SHORTCUTS -------------------------- */
function log(msg) {
  if (console && console.log) {
    console.log(msg);
  }
}
function error(msg) {
  if (console && console.error) {
    console.error(msg);
  }
}
function warn(msg) {
  if (console && console.warn) {
    console.warn(msg);
  }
}

/* ----------------------- Testing undefined and null value ---------------------- */

function isUndefined(v) {
  return typeof v === "undefined";
}

function isUnset(v) {
  return (typeof v === "undefined") || (v === null);
}

/* ----------------------- Testing safari browser ---------------------- */

function isSafari() {
  return /^((?!chrome|android|ubuntu).)*safari/i.test(navigator.userAgent);
}

/* ----------------------- LOGGING SHORTCUTS -------------------------- */
/* Extract URL parameters from current location */
function getParameterByName(name, defaultValue) {
  name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
  var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
      results = regex.exec(location.search);
  return results === null ? (isUnset(defaultValue) ? defaultValue : "") : decodeURIComponent(results[1].replace(/\+/g, " "));
}

/* ----------------------- Local storage -------------------------- */
<!-- Begin Cookie Consent plugin by Silktide - http://silktide.com/cookieconsent -->
window.cookieconsent_options = {
  "message":"This website uses cookies to ensure you get the best experience on our website",
  "dismiss":"Got it!",
  "learnMore":"More",
  "link":"doc/#privacy",
  "target":"_blank",
  //"container":"#map",
  "theme":"dark-bottom"
};

function canValBeSaved() {
  return window.hasCookieConsent || (window.location.toString().indexOf("file:") == 0);
}

function storeVal(name, val) {
  //log("store " + name + "=" + val);
  if (canValBeSaved()) {
    var store = window.localStorage;
    if (store) {
      if (val === "") {
        store.removeItem(name);
      } else {
        store.setItem(name, val);
      }
    }
  }
}
function storeJsonVal(name, val) {
  if (JSON && JSON.stringify) {
    v = JSON.stringify(val);
    storeVal(name, v);
  }
}

function getVal(name, defval) {
  var v = window.localStorage ? window.localStorage.getItem(name) : undefined;
  return isUnset(v) ? defval : v;
}
function getJsonVal(name, defval) {
  var v = getVal(name);
  var val = v && JSON && JSON.parse ? JSON.parse(v) : undefined;
  return isUnset(v) ? defval : val;
}

/* ---------------------- GOOGLE ANALYTICS ------------------------- */

function initGoogleAnalytics(trackingid) {
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
  ga('create', trackingid, 'auto');
  ga('send', 'pageview');
}

/* ---------------------- EMAIL ------------------------- */
/*
 * Open a new mail in default mail client with recipent and subject.
 * It protects the email address from spam robots by splitting the address in several parts,
 * and only assemblying them when needed for the duration of a click.
 *
 * parameters:
 * - selector: the css selector of the link or button which should open the email
 * - name: the name part of the recepient email address
 * - domain: the domain part of the recepient email address
 * - subject: the subject string of the newly created email
 *
 * Attachs a click handler to the html element designated by <selector>
 * that opens the following URL:
 * "mailto:<name>@<domain>?subject=<subject>
 */
function setEmailListener(selector, name, domain, subject) {
  $(selector).click(function(){
    function doEmail(d, i, tail) {
      location.href = "mailto:" + i + "@" + d + tail;
    }
    doEmail(domain, name, "?subject="+subject);
    return false;
  })
}

/* ------------------------------ Encoding ---------------------------------*/
function n10dLocation() {
  var res = window.location.toString();
  res = res.replace(/\?.*$/,"").replace(/\#.*$/,"");
  res = res.replace(/^.*:\/\//, "//");
  res = res.replace(/index.html$/, "");
  res = res.replace(/\/*$/, "/");
  return res;
}
function n10dUA() {
  var res = getVal("wtracks.code");
  return res;
}

function strxor(s, k) {
  var enc = "";
  var str = "";
  // make sure that input is string
  str = s.toString();
  for (var i = 0; i < s.length; i++) {
    // create block
    var a = s.charCodeAt(i);
    // bitwise XOR
    var b = a ^ k.charCodeAt(i % k.length);
    enc = enc + String.fromCharCode(b);
  }
  return enc;
}
function strencode(s, k) {
  return encodeURIComponent(strxor(s, k))
}
function strdecode(s1, s2) {
  var f = (window.location.toString().indexOf("file:") == 0);
  var s = f ? s2 : s1;
  var k = f ? n10dUA() : n10dLocation();
  return s ? strxor(decodeURIComponent(s), k) : s;
}

// Base 64 encoding / decoding

function supportsBase64() {
  return btoa && atob ? true : false;
}
function b64EncodeUnicode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
        return String.fromCharCode('0x' + p1);
    }));
}
function b64DecodeUnicode(str) {
    return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

function copyToClipboard(msg, text) {
  window.prompt(msg + "\nCopy to clipboard: Ctrl+C, Enter", text);
}

/* ---------------------- Start service worker ------------------------*/

if ('serviceWorker' in navigator) {
  navigator.serviceWorker
           .register('./service-worker.js')
           .then(function() { console.log('Service Worker Registered'); });
}
<commit_msg>fix dev error on new devices<commit_after>/* ----------------------- LOGGING SHORTCUTS -------------------------- */
function log(msg) {
  if (console && console.log) {
    console.log(msg);
  }
}
function error(msg) {
  if (console && console.error) {
    console.error(msg);
  }
}
function warn(msg) {
  if (console && console.warn) {
    console.warn(msg);
  }
}

/* ----------------------- Testing undefined and null value ---------------------- */

function isUndefined(v) {
  return typeof v === "undefined";
}

function isUnset(v) {
  return (typeof v === "undefined") || (v === null);
}

/* ----------------------- Testing safari browser ---------------------- */

function isSafari() {
  return /^((?!chrome|android|ubuntu).)*safari/i.test(navigator.userAgent);
}

/* ----------------------- LOGGING SHORTCUTS -------------------------- */
/* Extract URL parameters from current location */
function getParameterByName(name, defaultValue) {
  name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
  var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
      results = regex.exec(location.search);
  return results === null ? (isUnset(defaultValue) ? defaultValue : "") : decodeURIComponent(results[1].replace(/\+/g, " "));
}

/* ----------------------- Local storage -------------------------- */
<!-- Begin Cookie Consent plugin by Silktide - http://silktide.com/cookieconsent -->
window.cookieconsent_options = {
  "message":"This website uses cookies to ensure you get the best experience on our website",
  "dismiss":"Got it!",
  "learnMore":"More",
  "link":"doc/#privacy",
  "target":"_blank",
  //"container":"#map",
  "theme":"dark-bottom"
};

function canValBeSaved() {
  return window.hasCookieConsent || (window.location.toString().indexOf("file:") == 0);
}

function storeVal(name, val) {
  //log("store " + name + "=" + val);
  if (canValBeSaved()) {
    var store = window.localStorage;
    if (store) {
      if (val === "") {
        store.removeItem(name);
      } else {
        store.setItem(name, val);
      }
    }
  }
}
function storeJsonVal(name, val) {
  if (JSON && JSON.stringify) {
    v = JSON.stringify(val);
    storeVal(name, v);
  }
}

function getVal(name, defval) {
  var v = window.localStorage ? window.localStorage.getItem(name) : undefined;
  return isUnset(v) ? defval : v;
}
function getJsonVal(name, defval) {
  var v = getVal(name);
  var val = v && JSON && JSON.parse ? JSON.parse(v) : undefined;
  return isUnset(v) ? defval : val;
}

/* ---------------------- GOOGLE ANALYTICS ------------------------- */

function initGoogleAnalytics(trackingid) {
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
  ga('create', trackingid, 'auto');
  ga('send', 'pageview');
}

/* ---------------------- EMAIL ------------------------- */
/*
 * Open a new mail in default mail client with recipent and subject.
 * It protects the email address from spam robots by splitting the address in several parts,
 * and only assemblying them when needed for the duration of a click.
 *
 * parameters:
 * - selector: the css selector of the link or button which should open the email
 * - name: the name part of the recepient email address
 * - domain: the domain part of the recepient email address
 * - subject: the subject string of the newly created email
 *
 * Attachs a click handler to the html element designated by <selector>
 * that opens the following URL:
 * "mailto:<name>@<domain>?subject=<subject>
 */
function setEmailListener(selector, name, domain, subject) {
  $(selector).click(function(){
    function doEmail(d, i, tail) {
      location.href = "mailto:" + i + "@" + d + tail;
    }
    doEmail(domain, name, "?subject="+subject);
    return false;
  })
}

/* ------------------------------ Encoding ---------------------------------*/
function n10dLocation() {
  var res = window.location.toString();
  res = res.replace(/\?.*$/,"").replace(/\#.*$/,"");
  res = res.replace(/^.*:\/\//, "//");
  res = res.replace(/index.html$/, "");
  res = res.replace(/\/*$/, "/");
  return res;
}
function n10dUA() {
  var res = getVal("wtracks.code");
  return res;
}

function strxor(s, k) {
  var enc = "";
  var str = "";
  // make sure that input is string
  str = s.toString();
  for (var i = 0; i < s.length; i++) {
    // create block
    var a = s.charCodeAt(i);
    // bitwise XOR
    var b = a ^ k.charCodeAt(i % k.length);
    enc = enc + String.fromCharCode(b);
  }
  return enc;
}
function strencode(s, k) {
  return encodeURIComponent(strxor(s, k))
}
function strdecode(s1, s2) {
  var f = (window.location.toString().indexOf("file:") == 0);
  var s = f ? s2 : s1;
  var k = f ? n10dUA() : n10dLocation();
  return s ? strxor(decodeURIComponent(s), k ? k : "") : s;
}

// Base 64 encoding / decoding

function supportsBase64() {
  return btoa && atob ? true : false;
}
function b64EncodeUnicode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
        return String.fromCharCode('0x' + p1);
    }));
}
function b64DecodeUnicode(str) {
    return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

function copyToClipboard(msg, text) {
  window.prompt(msg + "\nCopy to clipboard: Ctrl+C, Enter", text);
}

/* ---------------------- Start service worker ------------------------*/

if ('serviceWorker' in navigator) {
  navigator.serviceWorker
           .register('./service-worker.js')
           .then(function() { console.log('Service Worker Registered'); });
}
<|endoftext|>


<commit_before>import { APP_NAME, $document, $pjaxWrapper } from './utils/environment';

import globals from './globals';

import { arrayContains, removeFromArray } from './utils/array';
import { getNodeData } from './utils/html';
import { isFunction } from './utils/is';

// Basic modules
import * as modules from './modules';

const MODULE_NAME = 'App';
const EVENT_NAMESPACE = `${APP_NAME}.${MODULE_NAME}`;

export const EVENT = {
    INIT_MODULES: `initModules.${EVENT_NAMESPACE}`,
    INIT_SCOPED_MODULES: `initScopedModules.${EVENT_NAMESPACE}`,
    DELETE_SCOPED_MODULES: `deleteScopedModules.${EVENT_NAMESPACE}`
};

class App {
    constructor() {
        this.modules = modules;
        this.currentModules = [];

        $document.on(EVENT.INIT_MODULES, (event) => {
            this.initGlobals(event.firstBlood)
                .deleteModules(event)
                .initModules(event);
        });

        $document.on(EVENT.INIT_SCOPED_MODULES, (event) => {
            this.initModules(event);
        });

        $document.on(EVENT.DELETE_SCOPED_MODULES, (event) => {
            this.deleteModules(event);
        });
    }

    /**
     * Destroy all existing modules or a specific scope of modules
     * @param  {Object} event The event being triggered.
     * @return {Object}       Self (allows chaining)
     */
    deleteModules(event) {
        let destroyAll = true;
        let moduleIds = [];

        // Check for scope first
        if (event.$scope instanceof jQuery && event.$scope.length > 0) {
            // Modules within scope
            const $modules = event.$scope.find('[data-module]');

            // Determine their uids
            moduleIds = $.makeArray($modules.map(function(index) {
                return $modules.eq(index).data('uid');
            }));

            if (moduleIds.length > 0) {
                destroyAll = false;
            } else {
                return this;
            }
        }

        // Loop modules and destroying all of them, or specific ones
        let i = this.currentModules.length;

        while (i--) {
            if (destroyAll || arrayContains(moduleIds, this.currentModules[i].uid)) {
                removeFromArray(moduleIds, this.currentModules[i].uid);
                this.currentModules[i].destroy();
                this.currentModules.splice(i);
            }
        }

        return this;
    }

    /**
     * Execute global functions and settings
     * Allows you to initialize global modules only once if you need
     * (ex.: when using Barba.js or SmoothState.js)
     * @return {Object} Self (allows chaining)
     */
    initGlobals(firstBlood) {
        globals(firstBlood);
        return this;
    }

    /**
     * Find modules and initialize them
     * @param  {Object} event The event being triggered.
     * @return {Object}       Self (allows chaining)
     */
    initModules(event) {
        // Elements with module
        let $moduleEls = [];

        // If first blood, load all modules in the DOM
        // If scoped, render elements with modules
        // If Barba, load modules contained in Barba container
        if (event.firstBlood) {
            $moduleEls = $document.find('[data-module]');
        } else if (event.$scope instanceof jQuery && event.$scope.length > 0) {
            $moduleEls = event.$scope.find('[data-module]');
        } else if (event.isPjax) {
            $moduleEls = $pjaxWrapper.find('[data-module]');
        }

        // Loop through elements
        let i = 0;
        const elsLen = $moduleEls.length;

        for (; i < elsLen; i++) {

            // Current element
            let el = $moduleEls[i];

            // All data- attributes considered as options
            let options = getNodeData(el);

            // Add current DOM element and jQuery element
            options.el = el;
            options.$el = $moduleEls.eq(i);

            // Module does exist at this point
            let attr = options.module;

            // Splitting modules found in the data-attribute
            let moduleIdents = attr.split(/[,\s]+/g);

            // Loop modules
            let j = 0;
            let modulesLen = moduleIdents.length;

            for (; j < modulesLen; j++) {
                let moduleAttr = moduleIdents[j];

                if (typeof this.modules[moduleAttr] === 'function') {
                    let module = new this.modules[moduleAttr](options);
                    this.currentModules.push(module);
                    module.init();
                }
            }
        }

        return this;
    }
}

// IIFE for loading the application
// ==========================================================================
(function() {
    new App();
    $document.triggerHandler({
        type: EVENT.INIT_MODULES,
        firstBlood: true
    });
})();
<commit_msg>@joel Fixed deleteModules splice : deleteCount param wasn't present, causing all modules with index > i to be removed from this.currentModules. Fortunately, this was only happening if a scope was given to deleteModules.<commit_after>import { APP_NAME, $document, $pjaxWrapper } from './utils/environment';

import globals from './globals';

import { arrayContains, removeFromArray } from './utils/array';
import { getNodeData } from './utils/html';
import { isFunction } from './utils/is';

// Basic modules
import * as modules from './modules';

const MODULE_NAME = 'App';
const EVENT_NAMESPACE = `${APP_NAME}.${MODULE_NAME}`;

export const EVENT = {
    INIT_MODULES: `initModules.${EVENT_NAMESPACE}`,
    INIT_SCOPED_MODULES: `initScopedModules.${EVENT_NAMESPACE}`,
    DELETE_SCOPED_MODULES: `deleteScopedModules.${EVENT_NAMESPACE}`
};

class App {
    constructor() {
        this.modules = modules;
        this.currentModules = [];

        $document.on(EVENT.INIT_MODULES, (event) => {
            this.initGlobals(event.firstBlood)
                .deleteModules(event)
                .initModules(event);
        });

        $document.on(EVENT.INIT_SCOPED_MODULES, (event) => {
            this.initModules(event);
        });

        $document.on(EVENT.DELETE_SCOPED_MODULES, (event) => {
            this.deleteModules(event);
        });
    }

    /**
     * Destroy all existing modules or a specific scope of modules
     * @param  {Object} event The event being triggered.
     * @return {Object}       Self (allows chaining)
     */
    deleteModules(event) {
        let destroyAll = true;
        let moduleIds = [];

        // Check for scope first
        if (event.$scope instanceof jQuery && event.$scope.length > 0) {
            // Modules within scope
            const $modules = event.$scope.find('[data-module]');

            // Determine their uids
            moduleIds = $.makeArray($modules.map(function(index) {
                return $modules.eq(index).data('uid');
            }));

            if (moduleIds.length > 0) {
                destroyAll = false;
            } else {
                return this;
            }
        }

        // Loop modules and destroying all of them, or specific ones
        let i = this.currentModules.length;

        while (i--) {
            if (destroyAll || arrayContains(moduleIds, this.currentModules[i].uid)) {
                removeFromArray(moduleIds, this.currentModules[i].uid);
                this.currentModules[i].destroy();
                this.currentModules.splice(i, 1);
            }
        }

        return this;
    }

    /**
     * Execute global functions and settings
     * Allows you to initialize global modules only once if you need
     * (ex.: when using Barba.js or SmoothState.js)
     * @return {Object} Self (allows chaining)
     */
    initGlobals(firstBlood) {
        globals(firstBlood);
        return this;
    }

    /**
     * Find modules and initialize them
     * @param  {Object} event The event being triggered.
     * @return {Object}       Self (allows chaining)
     */
    initModules(event) {
        // Elements with module
        let $moduleEls = [];

        // If first blood, load all modules in the DOM
        // If scoped, render elements with modules
        // If Barba, load modules contained in Barba container
        if (event.firstBlood) {
            $moduleEls = $document.find('[data-module]');
        } else if (event.$scope instanceof jQuery && event.$scope.length > 0) {
            $moduleEls = event.$scope.find('[data-module]');
        } else if (event.isPjax) {
            $moduleEls = $pjaxWrapper.find('[data-module]');
        }

        // Loop through elements
        let i = 0;
        const elsLen = $moduleEls.length;

        for (; i < elsLen; i++) {

            // Current element
            let el = $moduleEls[i];

            // All data- attributes considered as options
            let options = getNodeData(el);

            // Add current DOM element and jQuery element
            options.el = el;
            options.$el = $moduleEls.eq(i);

            // Module does exist at this point
            let attr = options.module;

            // Splitting modules found in the data-attribute
            let moduleIdents = attr.split(/[,\s]+/g);

            // Loop modules
            let j = 0;
            let modulesLen = moduleIdents.length;

            for (; j < modulesLen; j++) {
                let moduleAttr = moduleIdents[j];

                if (typeof this.modules[moduleAttr] === 'function') {
                    let module = new this.modules[moduleAttr](options);
                    this.currentModules.push(module);
                    module.init();
                }
            }
        }

        return this;
    }
}

// IIFE for loading the application
// ==========================================================================
(function() {
    new App();
    $document.triggerHandler({
        type: EVENT.INIT_MODULES,
        firstBlood: true
    });
})();
<|endoftext|>


<commit_before>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
   <commit_msg>Reverting changes<commit_after>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
    <|endoftext|>


<commit_before>'use strict'

var gulp           = require('gulp'),
    rimraf         = require('rimraf'),
    runSequence    = require('run-sequence'),
    markdown       = require('gulp-markdown'),
    highlight      = require('gulp-highlight'),
    autoprefixer   = require('gulp-autoprefixer'),
    sass           = require('gulp-ruby-sass'),
    nodeSass       = require('gulp-sass'),
    uglify         = require('gulp-uglify'),
    concat         = require('gulp-concat'),
    connect        = require('gulp-connect'),
    modRewrite     = require('connect-modrewrite'),
    dynamicRouting = require('./bin/gulp-dynamic-routing'),
    karma          = require('gulp-karma'),
    rsync          = require('gulp-rsync');

// Deploy
gulp.task('deploy', ['build'], function() {
  return gulp.src('build/**')
    .pipe(rsync({
      root: 'build',
      hostname: 'deployer@72.32.134.77',
      destination: '/home/deployer/sites/foundation-apps/current'
    }));
});

// Clean build directory
gulp.task('clean', function(cb) {
  rimraf('./build', cb);
});

// Copy static files (but not the Angular templates, Sass, or JS)
gulp.task('copy', function() {
  var dirs = [
    './docs/**/*.*',
    '!./docs/templates/**/*.*',
    '!./docs/assets/{scss,js}/**/*.*'
  ];
  gulp.src(dirs, {
    base: './docs/'
  })
    .pipe(gulp.dest('build'));

  return gulp.src('./iconic/**/*')
    .pipe(gulp.dest('build/assets/img/iconic/'));
});

gulp.task('clean-partials', function(cb) {
  rimraf('./build/partials', cb);
});

gulp.task('copy-partials', ['clean-partials'], function() {
  return gulp.src(['js/angular/partials/**.*'])
    .pipe(gulp.dest('./build/partials/'));
});

gulp.task('css', ['sass'], function() {
  //copy css
  var dirs = [
    'bower_components/allmighty-autocomplete/style/autocomplete.css',
    'build/assets/css/app.css'
  ];
  return gulp.src(dirs)
    .pipe(concat('app.css'))
    .pipe(gulp.dest('build/assets/css'))
  ;

});

gulp.task('sass', function() {
  return gulp.src('docs/assets/scss/app.scss')
    .pipe(sass({
      loadPath: ['scss'],
      style: 'nested',
      bundleExec: true
    }))
    .on('error', function(e) {
      console.log(e);
    })
    .pipe(autoprefixer({
      browsers: ['last 2 versions', 'ie 10']
    }))
    .pipe(gulp.dest('./build/assets/css/'));
});

gulp.task('node-sass', function() {
  return gulp.src('docs/assets/scss/app.scss')
    .pipe(nodeSass({
      includePaths: ['scss'],
      outputStyle: 'nested',
      errLogToConsole: true
    }))
    .pipe(autoprefixer({
      browsers: ['last 2 versions', 'ie 10']
    }))
    .pipe(concat('app_node.css'))
    .pipe(gulp.dest('./build/assets/css/'));
});

// Process Foundation JS
gulp.task('uglify', ['uglify-app'], function() {
  var libs = [
    'bower_components/fastclick/lib/fastclick.js',
    'bower_components/viewport-units-buggyfill/viewport-units-buggyfill.js',
    'bower_components/notify.js/notify.js',
    'bower_components/tether/tether.js',
    'bower_components/marked/lib/marked.js',
    'bower_components/angular/angular.js',
    'bower_components/allmighty-autocomplete/script/autocomplete.js',
    'bower_components/angular-animate/angular-animate.js',
    'bower_components/ui-router/release/angular-ui-router.js',
    'bower_components/angular-highlightjs/angular-highlightjs.js',
    'bower_components/highlightjs/highlight.pack.js',
    'js/vendor/**/*.js',
  ];

  return gulp.src(libs)
    .pipe(uglify({
      beautify: true,
      mangle: false
    }).on('error', function(e) {
      console.log(e);
    }))
    .pipe(concat('app.js'))
    .pipe(gulp.dest('./build/assets/js/'))
  ;
});

// Process Angular JS
gulp.task('uglify-app', function() {
  var libs = [
    'docs/assets/js/angular.js',
    'js/angular/**/*.js',
    '!js/angular/app.js'
  ];

  return gulp.src(libs)
    .pipe(uglify({
      beautify: true,
      mangle: false
    }))
    .pipe(concat('angular-app.js'))
    .pipe(gulp.dest('./build/assets/js/'))
  ;

});

gulp.task('copy-templates', ['copy'], function() {
  var config = [];

  return gulp.src('./docs/templates/**/*.html')
    .pipe(dynamicRouting({
      path: 'build/assets/js/routes.js',
      root: 'docs'
    }))
    .pipe(markdown())
    .pipe(highlight())
    .pipe(gulp.dest('./build/templates'))
  ;
});

gulp.task('server:start', function() {
  connect.server({
    root: './build',
    middleware: function() {
      return [
        modRewrite(['^[^\\.]*$ /index.html [L]'])
      ];
    },
  });
});

gulp.task('karma-test', ['build', 'node-sass'], function() {
  var testFiles = [
    'build/assets/js/app.js',
    'build/assets/js/angular-app.js',
    'bower_components/angular-mocks/angular-mocks.js',
    'bower_components/jsdiff/diff.js',
    'build/assets/css/app.css',
    'build/assets/css/app_node.css',
    'tests/unit/common/*Spec.js'
  ];

  return gulp.src(testFiles)
    .pipe(karma({
      configFile: 'karma.conf.js',
      action: 'run'
    }))
    .on('error', function(err) {
      throw err;
    })
  ;

});

gulp.task('test', ['karma-test'], function() {
  console.log('Tests finished.');
});

gulp.task('build', function(cb) {
  runSequence('clean', ['copy', 'copy-partials', 'css', 'uglify'], 'copy-templates', function() {
    console.log('Successfully built.');
    cb();
  });
});

gulp.task('default', ['build', 'server:start'], function() {
  // Watch Sass
  gulp.watch(['./docs/assets/scss/**/*', './scss/**/*'], ['css']);

  // Watch JavaScript
  gulp.watch(['./docs/assets/js/**/*', './js/**/*'], ['uglify']);

  // Watch static files
  gulp.watch(['./docs/**/*.*', '!./docs/templates/**/*.*', '!./docs/assets/{scss,js}/**/*.*'], ['copy']);

  // Watch Angular partials
  gulp.watch(['js/angular/partials/**.*'], ['copy-partials']);

  // Watch Angular templates
  gulp.watch(['docs/templates/**/*.html'], ['copy-templates']);
});
<commit_msg>Karma test fix<commit_after>'use strict'

var gulp           = require('gulp'),
    rimraf         = require('rimraf'),
    runSequence    = require('run-sequence'),
    markdown       = require('gulp-markdown'),
    highlight      = require('gulp-highlight'),
    autoprefixer   = require('gulp-autoprefixer'),
    sass           = require('gulp-ruby-sass'),
    nodeSass       = require('gulp-sass'),
    uglify         = require('gulp-uglify'),
    concat         = require('gulp-concat'),
    connect        = require('gulp-connect'),
    modRewrite     = require('connect-modrewrite'),
    dynamicRouting = require('./bin/gulp-dynamic-routing'),
    karma          = require('gulp-karma'),
    rsync          = require('gulp-rsync');

// Deploy
gulp.task('deploy', ['build'], function() {
  return gulp.src('build/**')
    .pipe(rsync({
      root: 'build',
      hostname: 'deployer@72.32.134.77',
      destination: '/home/deployer/sites/foundation-apps/current'
    }));
});

// Clean build directory
gulp.task('clean', function(cb) {
  rimraf('./build', cb);
});

// Copy static files (but not the Angular templates, Sass, or JS)
gulp.task('copy', function() {
  var dirs = [
    './docs/**/*.*',
    '!./docs/templates/**/*.*',
    '!./docs/assets/{scss,js}/**/*.*'
  ];
  gulp.src(dirs, {
    base: './docs/'
  })
    .pipe(gulp.dest('build'));

  return gulp.src('./iconic/**/*')
    .pipe(gulp.dest('build/assets/img/iconic/'));
});

gulp.task('clean-partials', function(cb) {
  rimraf('./build/partials', cb);
});

gulp.task('copy-partials', ['clean-partials'], function() {
  return gulp.src(['js/angular/partials/**.*'])
    .pipe(gulp.dest('./build/partials/'));
});

gulp.task('css', ['sass'], function() {
  //copy css
  var dirs = [
    'bower_components/allmighty-autocomplete/style/autocomplete.css',
    'build/assets/css/app.css'
  ];
  return gulp.src(dirs)
    .pipe(concat('app.css'))
    .pipe(gulp.dest('build/assets/css'))
  ;

});

gulp.task('sass', function() {
  return gulp.src('docs/assets/scss/app.scss')
    .pipe(sass({
      loadPath: ['scss'],
      style: 'nested',
      bundleExec: true
    }))
    .on('error', function(e) {
      console.log(e);
    })
    .pipe(autoprefixer({
      browsers: ['last 2 versions', 'ie 10']
    }))
    .pipe(gulp.dest('./build/assets/css/'));
});

gulp.task('node-sass', function() {
  return gulp.src('docs/assets/scss/app.scss')
    .pipe(nodeSass({
      includePaths: ['scss'],
      outputStyle: 'nested',
      errLogToConsole: true
    }))
    .pipe(autoprefixer({
      browsers: ['last 2 versions', 'ie 10']
    }))
    .pipe(concat('app_node.css'))
    .pipe(gulp.dest('./build/assets/css/'));
});

// Process Foundation JS
gulp.task('uglify', ['uglify-app'], function() {
  var libs = [
    'bower_components/fastclick/lib/fastclick.js',
    'bower_components/viewport-units-buggyfill/viewport-units-buggyfill.js',
    'bower_components/notify.js/notify.js',
    'bower_components/tether/tether.js',
    'bower_components/marked/lib/marked.js',
    'bower_components/angular/angular.js',
    'bower_components/allmighty-autocomplete/script/autocomplete.js',
    'bower_components/angular-animate/angular-animate.js',
    'bower_components/ui-router/release/angular-ui-router.js',
    'bower_components/angular-highlightjs/angular-highlightjs.js',
    'bower_components/highlightjs/highlight.pack.js',
    'js/vendor/**/*.js',
  ];

  return gulp.src(libs)
    .pipe(uglify({
      beautify: true,
      mangle: false
    }).on('error', function(e) {
      console.log(e);
    }))
    .pipe(concat('app.js'))
    .pipe(gulp.dest('./build/assets/js/'))
  ;
});

// Process Angular JS
gulp.task('uglify-app', function() {
  var libs = [
    'docs/assets/js/angular.js',
    'js/angular/**/*.js',
    '!js/angular/app.js'
  ];

  return gulp.src(libs)
    .pipe(uglify({
      beautify: true,
      mangle: false
    }))
    .pipe(concat('angular-app.js'))
    .pipe(gulp.dest('./build/assets/js/'))
  ;

});

gulp.task('copy-templates', ['copy'], function() {
  var config = [];

  return gulp.src('./docs/templates/**/*.html')
    .pipe(dynamicRouting({
      path: 'build/assets/js/routes.js',
      root: 'docs'
    }))
    .pipe(markdown())
    .pipe(highlight())
    .pipe(gulp.dest('./build/templates'))
  ;
});

gulp.task('server:start', function() {
  connect.server({
    root: './build',
    middleware: function() {
      return [
        modRewrite(['^[^\\.]*$ /index.html [L]'])
      ];
    },
  });
});

gulp.task('karma-test', ['build', 'node-sass'], function() {
  var testFiles = [
    'build/assets/js/foundation.js',
    'build/assets/js/app.js',
    'bower_components/angular-mocks/angular-mocks.js',
    'bower_components/jsdiff/diff.js',
    'build/assets/css/app.css',
    'build/assets/css/app_node.css',
    'tests/unit/common/*Spec.js'
  ];

  return gulp.src(testFiles)
    .pipe(karma({
      configFile: 'karma.conf.js',
      action: 'run'
    }))
    .on('error', function(err) {
      throw err;
    })
  ;

});

gulp.task('test', ['karma-test'], function() {
  console.log('Tests finished.');
});

gulp.task('build', function(cb) {
  runSequence('clean', ['copy', 'copy-partials', 'css', 'uglify'], 'copy-templates', function() {
    console.log('Successfully built.');
    cb();
  });
});

gulp.task('default', ['build', 'server:start'], function() {
  // Watch Sass
  gulp.watch(['./docs/assets/scss/**/*', './scss/**/*'], ['css']);

  // Watch JavaScript
  gulp.watch(['./docs/assets/js/**/*', './js/**/*'], ['uglify']);

  // Watch static files
  gulp.watch(['./docs/**/*.*', '!./docs/templates/**/*.*', '!./docs/assets/{scss,js}/**/*.*'], ['copy']);

  // Watch Angular partials
  gulp.watch(['js/angular/partials/**.*'], ['copy-partials']);

  // Watch Angular templates
  gulp.watch(['docs/templates/**/*.html'], ['copy-templates']);
});
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React from 'react'
import _omit from 'lodash/omit'

import componentTypes from '../actionComponentTypes'
import {modeFitsScopes} from '../actions'
import ActionGroup from './ActionGroup'
import SingleAction from './SingleAction'

const ActionVisual = ({definition, onClick, selection, parent, mode, callback, disabled, customActions}) => {
  if (customActions && customActions[definition.id]) {
    return customActions[definition.id]()
  }

  if (!modeFitsScopes(mode, definition.scopes)) return null

  const ActionType = definition.componentType === componentTypes.ACTION_GROUP ? ActionGroup : SingleAction

  return (
    <ActionType
      definition={definition}
      onClick={definition => {
        onClick(_omit(definition, ['label']), selection, parent, callback)
      }}
      selectedCount={selection.count}
      disabled={disabled}
    />
  )
}

ActionVisual.propTypes = {
  definition: PropTypes.shape({
    id: PropTypes.string,
    type: PropTypes.string,
    useLabel: PropTypes.string,
    icon: PropTypes.string,
    label: PropTypes.string,
    config: PropTypes.object,
    scopes: PropTypes.arrayOf(PropTypes.string),
    componentType: PropTypes.string,
    children: PropTypes.Array
  }).isRequired,
  selection: PropTypes.shape({
    entityName: PropTypes.string.isRequired,
    if: (props, propName, componentName) => {
      if (props.selection && props.selection.type === 'SELECTION' && !props.selection[propName]) {
        return new Error(`Selection.ids prop not defined. Component: ${componentName}'.`)
      }
    },
    query: (props, propName, componentName) => {
      if (props.selection && props.selection.type === 'QUERY' && !props.selection[propName]) {
        return new Error(`Selection.query prop not defined. Component: ${componentName}'.`)
      }
    },
    type: PropTypes.oneOf(['ID', 'QUERY']).isRequired,
    count: PropTypes.number
  }).isRequired,
  onClick: PropTypes.func.isRequired,
  callback: PropTypes.func,
  mode: PropTypes.string,
  parent: PropTypes.shape({
    key: PropTypes.string.isRequired,
    model: PropTypes.string.isRequired,
    reverseRelationName: PropTypes.string
  }),
  disabled: PropTypes.bool,
  customActions: PropTypes.objectOf(PropTypes.func)
}

const Action = props => {
  if (props.definition.componentType === componentTypes.ACTION_BAR) {
    return props.definition.children.map(child => (
      <ActionVisual key={`action-${props.definition.id}`} {...props} definition={child}/>
    )
    )
  }

  return <ActionVisual {...props}/>
}

Action.propTypes = {
  definition: PropTypes.shape({
    id: PropTypes.string,
    componentType: PropTypes.string,
    children: PropTypes.Array
  }).isRequired
}

export default Action
<commit_msg>refactor(app-extensions): improve key on action component<commit_after>import PropTypes from 'prop-types'
import React from 'react'
import _omit from 'lodash/omit'

import componentTypes from '../actionComponentTypes'
import {modeFitsScopes} from '../actions'
import ActionGroup from './ActionGroup'
import SingleAction from './SingleAction'

const ActionVisual = ({definition, onClick, selection, parent, mode, callback, disabled, customActions}) => {
  if (customActions && customActions[definition.id]) {
    return customActions[definition.id]()
  }

  if (!modeFitsScopes(mode, definition.scopes)) return null

  const ActionType = definition.componentType === componentTypes.ACTION_GROUP ? ActionGroup : SingleAction

  return (
    <ActionType
      definition={definition}
      onClick={definition => {
        onClick(_omit(definition, ['label']), selection, parent, callback)
      }}
      selectedCount={selection.count}
      disabled={disabled}
    />
  )
}

ActionVisual.propTypes = {
  definition: PropTypes.shape({
    id: PropTypes.string,
    type: PropTypes.string,
    useLabel: PropTypes.string,
    icon: PropTypes.string,
    label: PropTypes.string,
    config: PropTypes.object,
    scopes: PropTypes.arrayOf(PropTypes.string),
    componentType: PropTypes.string,
    children: PropTypes.Array
  }).isRequired,
  selection: PropTypes.shape({
    entityName: PropTypes.string.isRequired,
    if: (props, propName, componentName) => {
      if (props.selection && props.selection.type === 'SELECTION' && !props.selection[propName]) {
        return new Error(`Selection.ids prop not defined. Component: ${componentName}'.`)
      }
    },
    query: (props, propName, componentName) => {
      if (props.selection && props.selection.type === 'QUERY' && !props.selection[propName]) {
        return new Error(`Selection.query prop not defined. Component: ${componentName}'.`)
      }
    },
    type: PropTypes.oneOf(['ID', 'QUERY']).isRequired,
    count: PropTypes.number
  }).isRequired,
  onClick: PropTypes.func.isRequired,
  callback: PropTypes.func,
  mode: PropTypes.string,
  parent: PropTypes.shape({
    key: PropTypes.string.isRequired,
    model: PropTypes.string.isRequired,
    reverseRelationName: PropTypes.string
  }),
  disabled: PropTypes.bool,
  customActions: PropTypes.objectOf(PropTypes.func)
}

const Action = props => {
  if (props.definition.componentType === componentTypes.ACTION_BAR) {
    return props.definition.children.map(child => (
      <ActionVisual key={`action-${props.definition.id}-${child.id}`} {...props} definition={child}/>
    )
    )
  }

  return <ActionVisual {...props}/>
}

Action.propTypes = {
  definition: PropTypes.shape({
    id: PropTypes.string,
    componentType: PropTypes.string,
    children: PropTypes.Array
  }).isRequired
}

export default Action
<|endoftext|>