<commit_before>/*
	cloudman::
		description: here we should have a provider-less implementation for the API.

	cloudman::create
		description: create given instances.

		input: 	array of matching instances (see src/models/newInstance) to create.

		output: array of create request (see src/models/actionRequest) with request resolution.

	Assumptions:
		- Everything will return promises. When pass = resolve output params. When fail = reject err.

*/
var 	_ = require('lodash'),
 		Promise = require('bluebird'),
	 	credentials = require('./cred'),
	 	aws = require('./providers/aws/aws'),
        _do = require('./providers/do/do'); //do is reserved in javascript, so _do it is

/*
* cloudman::status
*
* description: 		get status vm's (machines, instances) in provided keyNames (in the account
* 					related to the keyName, see cred.js).
*
* input: 	Array of unique keyName  ['keyname1', 'keyname2'...] (see keyName in cred.js).
*
* output: 	Array of instances. [{}, {}...] (See src/models/instances).
*
* */
exports.status = function(keyNames){
	return new Promise(function(resolve, reject){
		if(!keyNames) return reject('keyNames parameter is required');

        var cred = splitProvidersFromCredentials(keyNames, credentials);

		var promise_aws = _.map(cred.aws, function(cr){
            return aws.status(cr);
        });

        var promise_do = _.map(cred.do, function(cr){
            return _do.status(cr);
        });

        return flattenize([promise_aws, promise_do])//we have to do flattenize first because Array.map return array
            .then(function(data){
                return Promise.all(data);
            })
            .then(flattenize)
            .then(resolve);

	});
};

/*
* cloudman::start
*
* description: 		start vm's (machines, instances) in provided matchingInstances (in the account
* 					related to the keyName, see cred.js).
*
* input: 	Array of matching instances (see src/models/matchingInstance) to start.
*
* output: 	Array of instances. [{}, {}...] (See src/models/actionRequest).
*
* */
exports.start = function(matchingInstances){
	return new Promise(function(resolve, reject){
        if(!_.get(matchingInstances, '[0].keyName')) return reject('matchingInstances parameter is invalid');

		var matchingInstancesKeyNames = matchingInstances.map(function(mi){
			return mi.keyName;
		});

		var cred = setCredentials(matchingInstancesKeyNames, credentials);
		var awsCred = filterProvider(cred, 'aws');
		var awsMatchingInstances = filterMatchingInstances(awsCred, matchingInstances);

		var promise_aws = _.map(awsCred, function(cr){
			var currentAwsInstances = awsMatchingInstances.filter(function(instance){
				return instance.keyName === cr.keyName;
			});
			var currentAwsInstancesIds = currentAwsInstances.map(function(instance){
				return instance.instanceId;
			});

			return aws.start(cr, currentAwsInstancesIds);
		});

		return Promise.all(promise_aws)
			.then(flattenize)
			.then(resolve);
	});
};

/*
* cloudman::stop
*
* description: 		stop vm's (machines, instances) in provided matchingInstances (in the account
* 					related to the keyName, see cred.js).
*
* input: 	Array of matching instances (see src/models/matchingInstance) to stop.
*
* output: 	Array of instances. [{}, {}...] (See src/models/actionRequest).
*
* */
exports.stop = function(matchingInstances){
	return new Promise(function(resolve, reject){
        if(!_.get(matchingInstances, '[0].keyName')) return reject('matchingInstances parameter is invalid');

		var matchingInstancesKeyNames = matchingInstances.map(function(mi){
			return mi.keyName;
		});
		var cred = setCredentials(matchingInstancesKeyNames, credentials);
		var awsCred = filterProvider(cred, 'aws');
		var awsMatchingInstances = filterMatchingInstances(awsCred, matchingInstances);

		var promise_aws = _.map(awsCred, function(cr){
			var currentAwsInstances = awsMatchingInstances.filter(function(instance){
				return instance.keyName === cr.keyName;
			});
			var currentAwsInstancesIds = currentAwsInstances.map(function(instance){
				return instance.instanceId;
			});

			return aws.stop(cr, currentAwsInstancesIds);
		});

		return Promise.all(promise_aws)
			.then(flattenize)
			.then(resolve);
	});
};

/*
* cloudman::terminate
*
* description: 		terminate vm's (machines, instances) in provided matchingInstances (in the account
* 					related to the keyName, see cred.js).
*
* input: 	Array of matching instances (see src/models/matchingInstance) to terminate.
*
* output: 	Array of instances. [{}, {}...] (See src/models/actionRequest).
*
* */
exports.terminate = function(matchingInstances){
	return new Promise(function(resolve, reject){
        if(!_.get(matchingInstances, '[0].keyName')) return reject('matchingInstances parameter is invalid');

		var matchingInstancesKeyNames = matchingInstances.map(function(mi){
			return mi.keyName;
		});
		var cred = setCredentials(matchingInstancesKeyNames, credentials);
		var awsCred = filterProvider(cred, 'aws');
		var awsMatchingInstances = filterMatchingInstances(awsCred, matchingInstances);

		var promise_aws = _.map(awsCred, function(cr){
			var currentAwsInstances = awsMatchingInstances.filter(function(instance){
				return instance.keyName === cr.keyName;
			});
			var currentAwsInstancesIds = currentAwsInstances.map(function(instance){
				return instance.instanceId;
			});

			return aws.terminate(cr, currentAwsInstancesIds);
		});

		return Promise.all(promise_aws)
			.then(flattenize)
			.then(resolve);
	});
};

/*
 * cloudman::flattenize
 *
 * description: 	Helper to flatten arrays.
 *
 * input: 	data to flattenize.
 *
 * output: 	flattenized array.
 *
 * */
var flattenize = function(data){
	return new Promise(function(resolve, reject){
		resolve(_.flatten(data));
	});
};

/*
 * cloudman::setCredentials
 *
 * description: 	Receives one array of keyNames and one array of credentials
  * 				and returns all the credentials with the given keyNames.
 *
 * input: 	keyNames array
 * 			credentials array.
 *
 * output: 	filtered array.
 *
 * */
var setCredentials = function(_keyNames, _credentials){
	return _.filter(_credentials, function(cred){
		return _keyNames.indexOf(cred.keyName) >= 0;
	});
};

/*
 * cloudman::filterProvider
 *
 * description: 	Receives one array of credentials and one provider and
 * 					returns all the credentials for the given provider.
 *
 * input: 	keyNames array
 * 			credentials array.
 *
 * output: 	filtered array.
 *
 * */
var filterProvider = function(_cred, _provider){
	return _.filter(_cred, function(cr){
		return cr.provider === _provider;
	});
};

/*
 * cloudman::filterMatchingInstances
 *
 * description: 	Receives one array of credentials and one array of MatchingInstances and
 * 					returns all the MatchingInstances for these credentials.
 *
 * input: 	credentials array.
 * 			atchingInstances array.
 *
 * output: 	filtered array.
 *
 * */
var filterMatchingInstances = function(_cred, _matchingInstances){
	var _credKeyNames = _cred.map(function(cr){
		return cr.keyName;
	});

	return _.filter(_matchingInstances, function(mi){
		return _credKeyNames.indexOf(mi.keyName) >= 0;
	});
};

/*
 * cloudman::splitProvidersFromCredentials
 *
 * description: 	Receives one array of keyNames and one array of credentials and
 * 					returns an object with providers as properties and each provider
 * 				    with an array of credentials
 *
 * input: 	keyName array.
 * 			credentials array.
 *
 * output: 	filtered array.
 *
 * */
var splitProvidersFromCredentials = function(_keyNames, _credentials){
    var validCred = _.filter(_credentials, function(cred){
        return _keyNames.indexOf(cred.keyName) >= 0;
    });

    var providers = validCred.reduce(function(res, cred){
        res[cred.provider] = res[cred.provider]  || [];
        res[cred.provider].push(cred);
        return res;
    }, {});

    return providers;
};<commit_msg>refactoring cloudman<commit_after>/*
	cloudman::
		description: here we should have a provider-less implementation for the API.

	cloudman::create
		description: create given instances.

		input: 	array of matching instances (see src/models/newInstance) to create.

		output: array of create request (see src/models/actionRequest) with request resolution.

	Assumptions:
		- Everything will return promises. When pass = resolve output params. When fail = reject err.

*/
var 	_ = require('lodash'),
 		Promise = require('bluebird'),
	 	credentials = require('./cred'),
	 	aws = require('./providers/aws/aws'),
        _do = require('./providers/do/do'); //do is reserved in javascript, so _do it is

/*
* cloudman::status
*
* description: 		get status vm's (machines, instances) in provided keyNames (in the account
* 					related to the keyName, see cred.js).
*
* input: 	Array of unique keyName  ['keyname1', 'keyname2'...] (see keyName in cred.js).
*
* output: 	Array of instances. [{}, {}...] (See src/models/instances).
*
* */
exports.status = function(keyNames){
	return new Promise(function(resolve, reject){
		if(!keyNames) return reject('keyNames parameter is required');

        var cred = splitProvidersFromCredentials(keyNames, credentials);

		var promise_aws = _.map(cred.aws, function(cr){
            return aws.status(cr);
        });

        var promise_do = _.map(cred.do, function(cr){
            return _do.status(cr);
        });

        return flattenize([promise_aws, promise_do])//we have to do flattenize first because Array.map return array
            .then(function(data){
                return Promise.all(data);
            })
            .then(flattenize)
            .then(resolve);

	});
};

/*
* cloudman::start
*
* description: 		start vm's (machines, instances) in provided matchingInstances (in the account
* 					related to the keyName, see cred.js).
*
* input: 	Array of matching instances (see src/models/matchingInstance) to start.
*
* output: 	Array of instances. [{}, {}...] (See src/models/actionRequest).
*
* */
exports.start = function(matchingInstances){
	return new Promise(function(resolve, reject){
        if(!_.get(matchingInstances, '[0].keyName')) return reject('matchingInstances parameter is invalid');

        var insWithCred = splitInstancesWithCredentials(matchingInstances, credentials);

		var promise_aws = _.map(insWithCred.aws, function(i){
			return aws.start(i.cred, [i.instanceId]);
		});

		return Promise.all(promise_aws)
			.then(flattenize)
			.then(resolve);
	});
};

/*
* cloudman::stop
*
* description: 		stop vm's (machines, instances) in provided matchingInstances (in the account
* 					related to the keyName, see cred.js).
*
* input: 	Array of matching instances (see src/models/matchingInstance) to stop.
*
* output: 	Array of instances. [{}, {}...] (See src/models/actionRequest).
*
* */
exports.stop = function(matchingInstances){
	return new Promise(function(resolve, reject){
        if(!_.get(matchingInstances, '[0].keyName')) return reject('matchingInstances parameter is invalid');

        var insWithCred = splitInstancesWithCredentials(matchingInstances, credentials);

        var promise_aws = _.map(insWithCred.aws, function(i){
            return aws.stop(i.cred, [i.instanceId]);
        });

		return Promise.all(promise_aws)
			.then(flattenize)
			.then(resolve);
	});
};

/*
* cloudman::terminate
*
* description: 		terminate vm's (machines, instances) in provided matchingInstances (in the account
* 					related to the keyName, see cred.js).
*
* input: 	Array of matching instances (see src/models/matchingInstance) to terminate.
*
* output: 	Array of instances. [{}, {}...] (See src/models/actionRequest).
*
* */
exports.terminate = function(matchingInstances){
	return new Promise(function(resolve, reject){
        if(!_.get(matchingInstances, '[0].keyName')) return reject('matchingInstances parameter is invalid');

        var insWithCred = splitInstancesWithCredentials(matchingInstances, credentials);

        var promise_aws = _.map(insWithCred.aws, function(i){
            return aws.terminate(i.cred, [i.instanceId]);
        });

		return Promise.all(promise_aws)
			.then(flattenize)
			.then(resolve);
	});
};

/*
 * cloudman::flattenize
 *
 * description: 	Helper to flatten arrays.
 *
 * input: 	data to flattenize.
 *
 * output: 	flattenized array.
 *
 * */
var flattenize = function(data){
	return new Promise(function(resolve, reject){
		resolve(_.flatten(data));
	});
};
/*
 * cloudman::splitProvidersFromCredentials
 *
 * description: 	Receives one array of keyNames and one array of credentials and
 * 					returns an array ofcredentials grouped by provider.
 *
 * input: 	keyName array.
 * 			credentials array.
 *
 * output: 	filtered array.
 *
 * */
var splitProvidersFromCredentials = function(_keyNames, _credentials){
    return _.filter(_credentials, function(cred){
        return _keyNames.indexOf(cred.keyName) >= 0;
    }).reduce(function(res, cred){
        res[cred.provider] = res[cred.provider]  || [];
        res[cred.provider].push(cred);
        return res;
    }, {});
};

/*
 * cloudman::splitInstancesWithCredentials
 *
 * description: 	Receives one array of instances {instanceID, keyName} and one array of
 * 					credentials and returns an each instance with their corresponding cre-
 * 				    dential grouped by provider.
 *
 * input: 	instance array.
 * 			credentials array.
 *
 * output: 	filtered array.
 *
 * */
var splitInstancesWithCredentials = function(_instances, _credentials){
    return _instances.map(function(instance){ //add their credential to each instance
        instance.credential = _.find(credentials,{keyName: instance.keyName});
        return instance;
    }).reduce(function(res, instance){ //return an object grouped by [provider][keyName]
        res[instance.credential.provider] = res[instance.credential.provider] || [];
        res[instance.credential.provider].push({instanceId: instance.instanceId, cred: instance.credential});
        return res;
    },{});
};

/*DEPRECATED METHODS*/
/*
 * cloudman::setCredentials
 *
 * description: 	Receives one array of keyNames and one array of credentials
 * 				and returns all the credentials with the given keyNames.
 *
 * input: 	keyNames array
 * 			credentials array.
 *
 * output: 	filtered array.
 *
 * */
var setCredentials = function(_keyNames, _credentials){
    return _.filter(_credentials, function(cred){
        return _keyNames.indexOf(cred.keyName) >= 0;
    });
};

/*
 * cloudman::filterProvider
 *
 * description: 	Receives one array of credentials and one provider and
 * 					returns all the credentials for the given provider.
 *
 * input: 	keyNames array
 * 			credentials array.
 *
 * output: 	filtered array.
 *
 * */
var filterProvider = function(_cred, _provider){
    return _.filter(_cred, function(cr){
        return cr.provider === _provider;
    });
};

/*
 * cloudman::filterMatchingInstances
 *
 * description: 	Receives one array of credentials and one array of MatchingInstances and
 * 					returns all the MatchingInstances for these credentials.
 *
 * input: 	credentials array.
 * 			atchingInstances array.
 *
 * output: 	filtered array.
 *
 * */
var filterMatchingInstances = function(_cred, _matchingInstances){
    var _credKeyNames = _cred.map(function(cr){
        return cr.keyName;
    });

    return _.filter(_matchingInstances, function(mi){
        return _credKeyNames.indexOf(mi.keyName) >= 0;
    });
};
<|endoftext|>


<commit_before>(function($) {
    var plugin = $(this);

	$.fn.validate = function(options) {

		// Default config
		var defaults = {
			auto: false,
			error: '#EB4847',
			success: '#63C76A'
		};

		// Update all configs
		var settings = $.extend(defaults, options);

		var has_errors = false;

		// Check all fields
		$(this).each(function() {

			if(settings.auto === true){
				var error = plugin.check($(this), settings);
				if(error === true){
					has_errors = true;
				}
			}

			$(this).keyup(function(e) {
				e.preventDefault();
				// Ignore enter
				if (e.which === 13) {
					return;
				}
				var error = plugin.check($(this), settings);
				if(error === true){
					has_errors = true;
				}
			});
		});

		// Invert result !error = validation successful
		return !has_errors;
	};

	/**
	 * Run the rules in the field.
	 *
 	 * @param object value
 	 * @param object value
	 */
	plugin.check = function(element, settings) {
		// Explode rules
		var rules = $(element).data('rules').split('|');

		var error = false;
		for (var i = 0; i < rules.length; i++) {
			var rule = rules[i];
			var regex = /(.*?)\[(.*?)\]/;

			var func = rule;
			var options = '';

			// Check if arguments are enclosed in brackets
			var groups = regex.exec(rule);
			if(groups !== null){
				func = groups[1];
				options = groups[2];
			}

			// If the function exists call it.
			if (typeof(plugin[func]) !== "undefined") {
				error = plugin[func]($(element).val(), options);
			}
		}

		// Apply the correct css to the field.
		if(error){
			$(element).css('border-color', settings.error);
		}
		else{
			$(element).css('border-color', settings.success);
		}
		return error;
	};

	/**
	 * Checks if the field is filled.
	 *
 	 * @param string value
	 */
	plugin.required = function(value) {
		if(value.length === 0){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is x characters long.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	plugin.exactlength = function(value, options) {
		if(value.length === options){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is at least x characters long.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	plugin.minlength = function(value, options) {
		if(value.length < options){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is less than x characters long.
 	 * @param string value
 	 * @param integer value
	 */
	plugin.maxlength = function(value, options) {
		if(value.length > options){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field only contains letters.
	 *
 	 * @param string value
	 */
	plugin.alpha = function(value) {
		var regex = /^([a-z])+$/i;
		return !regex.test(value);
	};

	/**
	 * Checks if the field only contains alpha-numeric characters.
	 *
 	 * @param string value
	 */
	plugin.alphanumeric = function(value) {
		var regex = /^([a-z0-9])+$/i;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is numeric.
	 *
 	 * @param string value
	 */
	plugin.numeric = function(value) {
		var regex = /^[\-+]?[0-9]*\.?[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is integer.
	 *
 	 * @param string value
	 */
	plugin.integer = function(value) {
		var regex = /^[\-+]?[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is natrual.
	 *
 	 * @param string value
	 */
	plugin.natrual = function(value) {
		var regex = /^[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is decimal.
	 *
 	 * @param string value
	 */
	plugin.decimal = function(value) {
		var regex = /^[\-+]?[0-9]+\.[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the numeric value is between two values.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	plugin.between = function(value, options) {
		var minmax = options.split('-');
		if(value < minmax[0]){
			return true;
		}
		if(value > minmax[1]){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the value matches with the field.
	 *
 	 * @param string value
 	 * @param string options
	 */
	plugin.matches = function(value, options) {
		var other = $(options).val();
		if(value !== other){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the value is a valid email.
	 * http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
	 *
 	 * @param string value
	 */
	plugin.email = function(value) {
		var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid url.
	 * http://stackoverflow.com/questions/1303872/trying-to-validate-url-using-javascript
	 *
 	 * @param string value
	 */
	plugin.url = function(value) {
		var regex = /^(http|https):\/\/(([a-zA-Z0-9$\-_.+!*'(),;:&=]|%[0-9a-fA-F]{2})+@)?(((25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])){3})|localhost|([a-zA-Z0-9\-\u00C0-\u017F]+\.)+([a-zA-Z]{2,}))(:[0-9]+)?(\/(([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*(\/([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*)*)?(\?([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?(\#([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?)?$/;;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid ip.
	 * http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
	 *
 	 * @param string value
	 */
	plugin.ip = function(value) {
		var regex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid time.
	 *
 	 * @param string value
	 */
	plugin.time = function(value) {
		var regex = /^([0]\d|[1][0-2]):([0-5]\d)\s?(?:AM|PM)$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid date.
	 *
 	 * @param string value
	 */
	plugin.date = function(value) {
		var regex = /^\d{2}[./-]\d{2}[./-]\d{4}$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid credit card number.
	 *
 	 * @param string value
 	 * @param string options
	 */
	plugin.cc = function(value, options) {
		var cc = [
			/^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/, // Visa
			/^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/, // Mastercard
			/^6011-?\d{4}-?\d{4}-?\d{4}$/, // Discover
			/^3[4,7]\d{13}$/, // American Express
			/^3[0,6,8]\d{12}$/ // Diners
		];

		// If options is not set check with all regex
		var multi = true;
		if(options.length > 0){
			multi = false;
		}

		if (options === "visa"){
			var regex = cc[0];
	   	}
	   	else if (options === "mastercard"){
			var regex = cc[1];
	   	}
	   	else if (options === "discover"){
			var regex = cc[2];
	   	}
	   	else if (options === "amex"){
			var regex = cc[3];
	   	}
	   	else if (options === "diners"){
			var regex = cc[4];
		}

		if(multi === true){
			var valid = false;
			for(var i = 0; i < cc.length; i++){
				if(cc[i].test(value)){
					valid = true;
				}
			}
			if(valid === false){
				return true;
			}
		}
		else{
			if(regex.test(value) === false){
				return true;
			}
		}

		// Remove all dashes for the checksum
		value = value.split("-").join("");

		// Add even digits in even length strings or odd digits in odd length strings.
		var checksum = 0;
		for (var i=(2-(value.length % 2)); i<=value.length; i+=2) {
			checksum += parseInt(value.charAt(i-1));
		}
		// Analyze odd digits in even length strings or even digits in odd length strings.
		for (var i=(value.length % 2) + 1; i<value.length; i+=2) {
			var digit = parseInt(value.charAt(i-1)) * 2;
			if (digit < 10) {
				checksum += digit;
			}
			else {
				checksum += (digit-9);
			}
		}
		return !((checksum % 10) === 0);
	};

}(jQuery));<commit_msg>Cleaned some lint errors<commit_after>(function($) {
    var plugin = $(this);

	$.fn.validate = function(options) {

		// Default config
		var defaults = {
			auto: false,
			error: '#EB4847',
			success: '#63C76A'
		};

		// Update all configs
		var settings = $.extend(defaults, options);

		var has_errors = false;

		// Check all fields
		$(this).each(function() {

			if(settings.auto === true){
				var error = plugin.check($(this), settings);
				if(error === true){
					has_errors = true;
				}
			}

			$(this).keyup(function(e) {
				e.preventDefault();
				// Ignore enter
				if (e.which === 13) {
					return;
				}
				var error = plugin.check($(this), settings);
				if(error === true){
					has_errors = true;
				}
			});
		});

		// Invert result !error = validation successful
		return !has_errors;
	};

	/**
	 * Run the rules in the field.
	 *
 	 * @param object value
 	 * @param object value
	 */
	plugin.check = function(element, settings) {
		// Explode rules
		var rules = $(element).data('rules').split('|');

		var error = false;
		for (var i = 0; i < rules.length; i++) {
			var rule = rules[i];
			var regex = /(.*?)\[(.*?)\]/;

			var func = rule;
			var options = '';

			// Check if arguments are enclosed in brackets
			var groups = regex.exec(rule);
			if(groups !== null){
				func = groups[1];
				options = groups[2];
			}

			// If the function exists call it.
			if (typeof(plugin[func]) !== "undefined") {
				error = plugin[func]($(element).val(), options);
			}
		}

		// Apply the correct css to the field.
		if(error){
			$(element).css('border-color', settings.error);
		}
		else{
			$(element).css('border-color', settings.success);
		}
		return error;
	};

	/**
	 * Checks if the field is filled.
	 *
 	 * @param string value
	 */
	plugin.required = function(value) {
		if(value.length === 0){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is x characters long.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	plugin.exactlength = function(value, options) {
		if(value.length === options){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is at least x characters long.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	plugin.minlength = function(value, options) {
		if(value.length < options){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is less than x characters long.
 	 * @param string value
 	 * @param integer value
	 */
	plugin.maxlength = function(value, options) {
		if(value.length > options){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field only contains letters.
	 *
 	 * @param string value
	 */
	plugin.alpha = function(value) {
		var regex = /^([a-z])+$/i;
		return !regex.test(value);
	};

	/**
	 * Checks if the field only contains alpha-numeric characters.
	 *
 	 * @param string value
	 */
	plugin.alphanumeric = function(value) {
		var regex = /^([a-z0-9])+$/i;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is numeric.
	 *
 	 * @param string value
	 */
	plugin.numeric = function(value) {
		var regex = /^[\-+]?[0-9]*\.?[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is integer.
	 *
 	 * @param string value
	 */
	plugin.integer = function(value) {
		var regex = /^[\-+]?[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is natrual.
	 *
 	 * @param string value
	 */
	plugin.natrual = function(value) {
		var regex = /^[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is decimal.
	 *
 	 * @param string value
	 */
	plugin.decimal = function(value) {
		var regex = /^[\-+]?[0-9]+\.[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the numeric value is between two values.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	plugin.between = function(value, options) {
		var minmax = options.split('-');
		if(value < minmax[0]){
			return true;
		}
		if(value > minmax[1]){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the value matches with the field.
	 *
 	 * @param string value
 	 * @param string options
	 */
	plugin.matches = function(value, options) {
		var other = $(options).val();
		if(value !== other){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the value is a valid email.
	 * http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
	 *
 	 * @param string value
	 */
	plugin.email = function(value) {
		var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid url.
	 * http://stackoverflow.com/questions/1303872/trying-to-validate-url-using-javascript
	 *
 	 * @param string value
	 */
	plugin.url = function(value) {
		var regex = /^(http|https):\/\/(([a-zA-Z0-9$\-_.+!*'(),;:&=]|%[0-9a-fA-F]{2})+@)?(((25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])){3})|localhost|([a-zA-Z0-9\-\u00C0-\u017F]+\.)+([a-zA-Z]{2,}))(:[0-9]+)?(\/(([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*(\/([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*)*)?(\?([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?(\#([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?)?$/;;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid ip.
	 * http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
	 *
 	 * @param string value
	 */
	plugin.ip = function(value) {
		var regex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid time.
	 *
 	 * @param string value
	 */
	plugin.time = function(value) {
		var regex = /^([0]\d|[1][0-2]):([0-5]\d)\s?(?:AM|PM)$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid date.
	 *
 	 * @param string value
	 */
	plugin.date = function(value) {
		var regex = /^\d{2}[./-]\d{2}[./-]\d{4}$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid credit card number.
	 *
 	 * @param string value
 	 * @param string options
	 */
	plugin.cc = function(value, options) {
		var cc = [
			/^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/, // Visa
			/^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/, // Mastercard
			/^6011-?\d{4}-?\d{4}-?\d{4}$/, // Discover
			/^3[4,7]\d{13}$/, // American Express
			/^3[0,6,8]\d{12}$/ // Diners
		];

		// If options is not set check with all regex
		var multi = true;
		if(options.length > 0){
			multi = false;
		}

		var regex;
		if(options === "visa"){
			regex = cc[0];
	   	}
	   	else if(options === "mastercard"){
			regex = cc[1];
		}
	   	else if(options === "discover"){
			regex = cc[2];
	   	}
	   	else if(options === "amex"){
			regex = cc[3];
	   	}
	   	else if(options === "diners"){
			regex = cc[4];
		}

		if(multi === true){
			var valid = false;
			for(var i = 0; i < cc.length; i++){
				if(cc[i].test(value)){
					valid = true;
				}
			}
			if(valid === false){
				return true;
			}
		}
		else{
			if(regex.test(value) === false){
				return true;
			}
		}

		// Remove all dashes for the checksum
		value = value.split("-").join("");

		// Add even digits in even length strings or odd digits in odd length strings.
		var checksum = 0;
		for (var i=(2-(value.length % 2)); i<=value.length; i+=2) {
			checksum += parseInt(value.charAt(i-1));
		}
		// Analyze odd digits in even length strings or even digits in odd length strings.
		for (var i=(value.length % 2) + 1; i<value.length; i+=2) {
			var digit = parseInt(value.charAt(i-1)) * 2;
			if (digit < 10) {
				checksum += digit;
			}
			else {
				checksum += (digit-9);
			}
		}
		return ((checksum % 10) !== 0);
	};

}(jQuery));<|endoftext|>


<commit_before>/*
* Form Validation 1.1, jQuery plugin
* Repository: http://github.com/seinoxygen/jquery-validation
*
* Copyright(c) 2014, Pablo Cornehl
* http://www.seinoxygen.com
*
* Licensed under the MIT License
*/
(function($) {
    var plugin = $(this);

	$.fn.validate = function(options) {

		// Default config
		var defaults = {
			force: false,
			wapper: 'jqv-',
			submit: '',
			valid: function(element){},
			invalid: function(element, error){},
			success: function(){},
			error: function(){}
		};

		// Update all configs
		options = $.extend(true, {}, defaults, options);

		var is_valid = true;

		// Check all fields
		$(this).each(function() {
			if(options.force === true){
				var error = $.fn.validate.check($(this), options);
				if(error === true){
					is_valid = false;
				}
			}

			// For textboxes
			$(this).keyup(function(e) {
				e.preventDefault();
				// Ignore enter
				if (e.which === 13) {
					return;
				}
				var error = $.fn.validate.check($(this), options);
				if(error === true){
					is_valid = false;
				}
			});

			// For radio, checkbox and selects
			var type = $(this).attr("type");
			if (type == "radio" || type == "checkbox"){
				$(this).change(function() {
					var error = $.fn.validate.check($(this), options);
					if(error === true){
						is_valid = false;
					}
				});
			}
		});

		if(is_valid === true){
			options.success.call(this);
		}
		else{
			options.error.call(this);
		}

		if(is_valid === true && options.submit.length > 0){
			$(options.submit).submit();
		}

		return this;
	};

	/**
	 * Run the rules in the field.
	 *
 	 * @param object value
 	 * @param object value
	 */
	$.fn.validate.check = function(element, options) {

		// If attribute is not defined return false because there is no rules to apply
		if (typeof(element.data('rules')) === "undefined") {
			return false;
		}

		// Explode rules
		var rules = element.data('rules').split('|');

		var error = false;
		for (var i = 0; i < rules.length; i++) {
			var rule = rules[i];
			var regex = /(.*?)\[(.*?)\]/;

			var func = rule;
			var filter = '';

			// Check if arguments are enclosed in brackets
			var groups = regex.exec(rule);
			if(groups !== null){
				func = groups[1];
				filter = groups[2];
			}

			// If the function exists call it.
			if (typeof($.fn.validate[func]) !== "undefined") {

				var value = element.val();

				// Verify the tipe of element. If they're radios or checkboxes look for checkeds
				var type = element.attr("type");
				if (type == "radio" || type == "checkbox"){
					// lenght is obtained ahead in the rules
					value = $('input[name=' + element.attr("name") + ']:checked');
				}

				error = $.fn.validate[func](value, filter);
				if(error === true){
					break;
				}
			}
		}

		// Apply the correct css to the field.
		if(error === true){

			var field_label = $.fn.validate.fieldlabel(element);

			// Call the invalid function
			var parts = filter.split('-');

			if(func == "matches"){
				parts[0] = $.fn.validate.fieldlabel($(parts[0]));
			}

			var msg = validation_lang[func].replace("%s", field_label).replace("%k", parts[0]).replace("%n", parts[1]);

			options.invalid.call(this, element, msg);
			$(element).removeClass(options.wapper + 'valid').addClass(options.wapper + 'invalid');
		}
		else{
			options.valid.call(this, element);
			$(element).removeClass(options.wapper + 'invalid').addClass(options.wapper + 'valid');
		}
		return error;
	};

	$.fn.validate.fieldlabel = function(element){
		var field_label = '';
		// If is set the field title use it
		if (typeof(element.attr('title')) !== "undefined") {
			field_label = element.attr('title');
		}
		// If is set the data attribute name use it
		if (typeof(element.data('name')) !== "undefined") {
			field_label = element.data('name');
		}

		return field_label;
	};

	/**
	 * Checks if the field is filled.
	 *
 	 * @param string value
	 */
	$.fn.validate.required = function(value) {
		if(value.length === 0){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is x characters long.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	$.fn.validate.exactlength = function(value, options) {
		if(value.length !== Number(options)){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is at least x characters long.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	$.fn.validate.minlength = function(value, options) {
		if(value.length < Number(options)){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is less than x characters long.
 	 * @param string value
 	 * @param integer value
	 */
	$.fn.validate.maxlength = function(value, options) {
		if(value.length > Number(options)){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field only contains letters.
	 *
 	 * @param string value
	 */
	$.fn.validate.alpha = function(value) {
		var regex = /^([a-z])+$/i;
		return !regex.test(value);
	};

	/**
	 * Checks if the field only contains alpha-numeric characters.
	 *
 	 * @param string value
	 */
	$.fn.validate.alphanumeric = function(value) {
		var regex = /^([a-z0-9])+$/i;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is numeric.
	 *
 	 * @param string value
	 */
	$.fn.validate.numeric = function(value) {
		var regex = /^[\-+]?[0-9]*\.?[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is integer.
	 *
 	 * @param string value
	 */
	$.fn.validate.integer = function(value) {
		var regex = /^[\-+]?[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is natrual.
	 *
 	 * @param string value
	 */
	$.fn.validate.natrual = function(value) {
		var regex = /^[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is decimal.
	 *
 	 * @param string value
	 */
	$.fn.validate.decimal = function(value) {
		var regex = /^[\-+]?[0-9]+\.[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the numeric value is between two values.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	$.fn.validate.between = function(value, options) {
		var minmax = options.split('-');
		if(value < Number(minmax[0])){
			return true;
		}
		if(value > Number(minmax[1])){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the value matches with the field.
	 *
 	 * @param string value
 	 * @param string options
	 */
	$.fn.validate.matches = function(value, options) {
		var other = $(options).val();
		if(value !== other){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the value is a valid email.
	 * http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
	 *
 	 * @param string value
	 */
	$.fn.validate.email = function(value) {
		var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid url.
	 * http://stackoverflow.com/questions/1303872/trying-to-validate-url-using-javascript
	 *
 	 * @param string value
	 */
	$.fn.validate.url = function(value) {
		var regex = /^(http|https):\/\/(([a-zA-Z0-9$\-_.+!*'(),;:&=]|%[0-9a-fA-F]{2})+@)?(((25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])){3})|localhost|([a-zA-Z0-9\-\u00C0-\u017F]+\.)+([a-zA-Z]{2,}))(:[0-9]+)?(\/(([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*(\/([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*)*)?(\?([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?(\#([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?)?$/;;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid ip.
	 * http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
	 *
 	 * @param string value
	 */
	$.fn.validate.ip = function(value) {
		var regex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid time.
	 *
 	 * @param string value
	 */
	$.fn.validate.time = function(value) {
		var regex = /^([0]\d|[1][0-2]):([0-5]\d)\s?(?:AM|PM)$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid date.
	 *
 	 * @param string value
	 */
	$.fn.validate.date = function(value) {
		var regex = /^\d{2}[./-]\d{2}[./-]\d{4}$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid credit card number.
	 *
 	 * @param string value
 	 * @param string options
	 */
	$.fn.validate.cc = function(value, options) {
		var cc = [
			/^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/, // Visa
			/^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/, // Mastercard
			/^6011-?\d{4}-?\d{4}-?\d{4}$/, // Discover
			/^3[4,7]\d{13}$/, // American Express
			/^3[0,6,8]\d{12}$/ // Diners
		];

		// If options is not set check with all regex
		if(options.length === 0){
			var valid = false;
			for(var i = 0; i < cc.length; i++){
				if(cc[i].test(value)){
					valid = true;
				}
			}
			if(valid === false){
				return true;
			}
		}
		else{
			var regex;
			if(options === "visa"){
				regex = cc[0];
			}
			else if(options === "mastercard"){
				regex = cc[1];
			}
			else if(options === "discover"){
				regex = cc[2];
			}
			else if(options === "amex"){
				regex = cc[3];
			}
			else if(options === "diners"){
				regex = cc[4];
			}

			return !regex.test(value);
		}

		// Remove all dashes for the checksum
		value = value.split("-").join("");

		// Add even digits in even length strings or odd digits in odd length strings.
		var checksum = 0;
		for (var i=(2-(value.length % 2)); i<=value.length; i+=2) {
			checksum += parseInt(value.charAt(i-1),10);
		}
		// Analyze odd digits in even length strings or even digits in odd length strings.
		for (var i=(value.length % 2) + 1; i<value.length; i+=2) {
			var digit = parseInt(value.charAt(i-1),10) * 2;
			if (digit < 10) {
				checksum += digit;
			}
			else {
				checksum += (digit-9);
			}
		}
		return ((checksum % 10) !== 0);
	};

}(jQuery));<commit_msg>Renamed option<commit_after>/*
* Form Validation 1.1, jQuery plugin
* Repository: http://github.com/seinoxygen/jquery-validation
*
* Copyright(c) 2014, Pablo Cornehl
* http://www.seinoxygen.com
*
* Licensed under the MIT License
*/
(function($) {
    var plugin = $(this);

	$.fn.validate = function(options) {

		// Default config
		var defaults = {
			force: false,
			wrapper: 'jqv-',
			submit: '',
			valid: function(element){},
			invalid: function(element, error){},
			success: function(){},
			error: function(){}
		};

		// Update all configs
		options = $.extend(true, {}, defaults, options);

		var is_valid = true;

		// Check all fields
		$(this).each(function() {
			if(options.force === true){
				var error = $.fn.validate.check($(this), options);
				if(error === true){
					is_valid = false;
				}
			}

			// For textboxes
			$(this).keyup(function(e) {
				e.preventDefault();
				// Ignore enter
				if (e.which === 13) {
					return;
				}
				var error = $.fn.validate.check($(this), options);
				if(error === true){
					is_valid = false;
				}
			});

			// For radio, checkbox and selects
			var type = $(this).attr("type");
			if (type == "radio" || type == "checkbox"){
				$(this).change(function() {
					var error = $.fn.validate.check($(this), options);
					if(error === true){
						is_valid = false;
					}
				});
			}
		});

		if(is_valid === true){
			options.success.call(this);
		}
		else{
			options.error.call(this);
		}

		if(is_valid === true && options.submit.length > 0){
			$(options.submit).submit();
		}

		return this;
	};

	/**
	 * Run the rules in the field.
	 *
 	 * @param object value
 	 * @param object value
	 */
	$.fn.validate.check = function(element, options) {

		// If attribute is not defined return false because there is no rules to apply
		if (typeof(element.data('rules')) === "undefined") {
			return false;
		}

		// Explode rules
		var rules = element.data('rules').split('|');

		var error = false;
		for (var i = 0; i < rules.length; i++) {
			var rule = rules[i];
			var regex = /(.*?)\[(.*?)\]/;

			var func = rule;
			var filter = '';

			// Check if arguments are enclosed in brackets
			var groups = regex.exec(rule);
			if(groups !== null){
				func = groups[1];
				filter = groups[2];
			}

			// If the function exists call it.
			if (typeof($.fn.validate[func]) !== "undefined") {

				var value = element.val();

				// Verify the tipe of element. If they're radios or checkboxes look for checkeds
				var type = element.attr("type");
				if (type == "radio" || type == "checkbox"){
					// lenght is obtained ahead in the rules
					value = $('input[name=' + element.attr("name") + ']:checked');
				}

				error = $.fn.validate[func](value, filter);
				if(error === true){
					break;
				}
			}
		}

		// Apply the correct css to the field.
		if(error === true){

			var field_label = $.fn.validate.fieldlabel(element);

			// Call the invalid function
			var parts = filter.split('-');

			if(func == "matches"){
				parts[0] = $.fn.validate.fieldlabel($(parts[0]));
			}

			var msg = validation_lang[func].replace("%s", field_label).replace("%k", parts[0]).replace("%n", parts[1]);

			options.invalid.call(this, element, msg);
			$(element).removeClass(options.wrapper + 'valid').addClass(options.wrapper + 'invalid');
		}
		else{
			options.valid.call(this, element);
			$(element).removeClass(options.wrapper + 'invalid').addClass(options.wrapper + 'valid');
		}
		return error;
	};

	$.fn.validate.fieldlabel = function(element){
		var field_label = '';
		// If is set the field title use it
		if (typeof(element.attr('title')) !== "undefined") {
			field_label = element.attr('title');
		}
		// If is set the data attribute name use it
		if (typeof(element.data('name')) !== "undefined") {
			field_label = element.data('name');
		}

		return field_label;
	};

	/**
	 * Checks if the field is filled.
	 *
 	 * @param string value
	 */
	$.fn.validate.required = function(value) {
		if(value.length === 0){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is x characters long.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	$.fn.validate.exactlength = function(value, options) {
		if(value.length !== Number(options)){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is at least x characters long.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	$.fn.validate.minlength = function(value, options) {
		if(value.length < Number(options)){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field is less than x characters long.
 	 * @param string value
 	 * @param integer value
	 */
	$.fn.validate.maxlength = function(value, options) {
		if(value.length > Number(options)){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the field only contains letters.
	 *
 	 * @param string value
	 */
	$.fn.validate.alpha = function(value) {
		var regex = /^([a-z])+$/i;
		return !regex.test(value);
	};

	/**
	 * Checks if the field only contains alpha-numeric characters.
	 *
 	 * @param string value
	 */
	$.fn.validate.alphanumeric = function(value) {
		var regex = /^([a-z0-9])+$/i;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is numeric.
	 *
 	 * @param string value
	 */
	$.fn.validate.numeric = function(value) {
		var regex = /^[\-+]?[0-9]*\.?[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is integer.
	 *
 	 * @param string value
	 */
	$.fn.validate.integer = function(value) {
		var regex = /^[\-+]?[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is natrual.
	 *
 	 * @param string value
	 */
	$.fn.validate.natrual = function(value) {
		var regex = /^[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the field is decimal.
	 *
 	 * @param string value
	 */
	$.fn.validate.decimal = function(value) {
		var regex = /^[\-+]?[0-9]+\.[0-9]+$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the numeric value is between two values.
	 *
 	 * @param string value
 	 * @param integer value
	 */
	$.fn.validate.between = function(value, options) {
		var minmax = options.split('-');
		if(value < Number(minmax[0])){
			return true;
		}
		if(value > Number(minmax[1])){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the value matches with the field.
	 *
 	 * @param string value
 	 * @param string options
	 */
	$.fn.validate.matches = function(value, options) {
		var other = $(options).val();
		if(value !== other){
			return true;
		}
		return false;
	};

	/**
	 * Checks if the value is a valid email.
	 * http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
	 *
 	 * @param string value
	 */
	$.fn.validate.email = function(value) {
		var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid url.
	 * http://stackoverflow.com/questions/1303872/trying-to-validate-url-using-javascript
	 *
 	 * @param string value
	 */
	$.fn.validate.url = function(value) {
		var regex = /^(http|https):\/\/(([a-zA-Z0-9$\-_.+!*'(),;:&=]|%[0-9a-fA-F]{2})+@)?(((25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9]|[1-9][0-9]|[0-9])){3})|localhost|([a-zA-Z0-9\-\u00C0-\u017F]+\.)+([a-zA-Z]{2,}))(:[0-9]+)?(\/(([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*(\/([a-zA-Z0-9$\-_.+!*'(),;:@&=]|%[0-9a-fA-F]{2})*)*)?(\?([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?(\#([a-zA-Z0-9$\-_.+!*'(),;:@&=\/?]|%[0-9a-fA-F]{2})*)?)?$/;;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid ip.
	 * http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
	 *
 	 * @param string value
	 */
	$.fn.validate.ip = function(value) {
		var regex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid time.
	 *
 	 * @param string value
	 */
	$.fn.validate.time = function(value) {
		var regex = /^([0]\d|[1][0-2]):([0-5]\d)\s?(?:AM|PM)$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid date.
	 *
 	 * @param string value
	 */
	$.fn.validate.date = function(value) {
		var regex = /^\d{2}[./-]\d{2}[./-]\d{4}$/;
		return !regex.test(value);
	};

	/**
	 * Checks if the value is a valid credit card number.
	 *
 	 * @param string value
 	 * @param string options
	 */
	$.fn.validate.cc = function(value, options) {
		var cc = [
			/^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/, // Visa
			/^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/, // Mastercard
			/^6011-?\d{4}-?\d{4}-?\d{4}$/, // Discover
			/^3[4,7]\d{13}$/, // American Express
			/^3[0,6,8]\d{12}$/ // Diners
		];

		// If options is not set check with all regex
		if(options.length === 0){
			var valid = false;
			for(var i = 0; i < cc.length; i++){
				if(cc[i].test(value)){
					valid = true;
				}
			}
			if(valid === false){
				return true;
			}
		}
		else{
			var regex;
			if(options === "visa"){
				regex = cc[0];
			}
			else if(options === "mastercard"){
				regex = cc[1];
			}
			else if(options === "discover"){
				regex = cc[2];
			}
			else if(options === "amex"){
				regex = cc[3];
			}
			else if(options === "diners"){
				regex = cc[4];
			}

			return !regex.test(value);
		}

		// Remove all dashes for the checksum
		value = value.split("-").join("");

		// Add even digits in even length strings or odd digits in odd length strings.
		var checksum = 0;
		for (var i=(2-(value.length % 2)); i<=value.length; i+=2) {
			checksum += parseInt(value.charAt(i-1),10);
		}
		// Analyze odd digits in even length strings or even digits in odd length strings.
		for (var i=(value.length % 2) + 1; i<value.length; i+=2) {
			var digit = parseInt(value.charAt(i-1),10) * 2;
			if (digit < 10) {
				checksum += digit;
			}
			else {
				checksum += (digit-9);
			}
		}
		return ((checksum % 10) !== 0);
	};

}(jQuery));<|endoftext|>


<commit_before>var Poll = {
	reset: function (roomId) {
		Poll[roomId] = {
			question: undefined,
			optionList: [],
			options: {},
			display: '',
			topOption: ''
		};
	},

	splint: function (target) {
		var parts = target.split(',');
		var len = parts.length;
		while (len--) {
			parts[len] = parts[len].trim();
		}
		return parts;
	}
};

for (var id in Rooms.rooms) {
	if (Rooms.rooms[id].type === 'chat' && !Poll[id]) {
		Poll[id] = {};
		Poll.reset(id);
	}
}

exports.commands = {
	poll: function (target, room, user) {
		if (!this.can('broadcast', null, room)) return false;
		if (!Poll[room.id]) Poll.reset(room.id);
		if (Poll[room.id].question) return this.sendReply("There is currently a poll going on already.");
		if (!this.canTalk()) return;

		var options = Poll.splint(target);
		if (options.length < 3) return this.parse('/help poll');

		var question = options.shift();

		options = options.join(',').toLowerCase().split(',');

		Poll[room.id].question = question;
		Poll[room.id].optionList = options;

		var pollOptions = '';
		var start = 0;
		while (start < Poll[room.id].optionList.length) {
			pollOptions += '<button name="send" value="/vote ' + Tools.escapeHTML(Poll[room.id].optionList[start]) + '">' + Tools.escapeHTML(Poll[room.id].optionList[start]) + '</button> ';
			start++;
		}
		Poll[room.id].display = '
' + Tools.escapeHTML(Poll[room.id].question) + '  /vote OPTION
Poll started by ' + user.name + '

    ' + pollOptions;
		room.add('|raw|
' + Poll[room.id].display + '
');
	},

	pollhelp: ["/poll [question], [option 1], [option 2]... - Create a poll where users can vote on an option."],
	endpoll: function (target, room, user) {
		if (!this.can('broadcast', null, room)) return false;
		if (!Poll[room.id]) Poll.reset(room.id);
		if (!Poll[room.id].question) return this.sendReply("There is no poll to end in this room.");

		var votes = Object.keys(Poll[room.id].options).length;

		if (votes === 0) {
			Poll.reset(room.id);
			return room.add('|raw|
The poll was canceled because of lack of voters.
');
		}

		var options = {};

		for (var l in Poll[room.id].optionList) {
			options[Poll[room.id].optionList[l]] = 0;
		}

		for (var o in Poll[room.id].options) {
			options[Poll[room.id].options[o]]++;
		}

		var data = [];
		for (var i in options) {
			data.push([i, options[i]]);
		}
		data.sort(function (a, b) {
			return a[1] - b[1];
		});

		var results = '';
		var len = data.length;
		var topOption = data[len - 1][0];
		while (len--) {
			if (data[len][1] > 0) {
				results += '• ' + data[len][0] + ' - ' + Math.floor(data[len][1] / votes * 100) + '% (' + data[len][1] + ')
';
			}
		}
		room.add('|raw|

Results to "' + Poll[room.id].question + '"
Poll ended by ' + user.name + '

' + results + '
');
		Poll.reset(room.id);
		Poll[room.id].topOption = topOption;
	},

	easytour: 'etour',
	elimtour: 'etour',
	etour: function (target, room, user) {
		if (!this.can('broadcast', null, room)) return;
		this.parse('/tour new ' + target + ', elimination');
	},

	roundrobintour: 'rtour',
	cancertour: 'rtour',
	rtour: function (target, room, user) {
		if (!this.can('broadcast', null, room)) return;
		this.parse('/tour new ' + target + ', roundrobin');
	},

	randomtour: 'randtour',
	randtour: function (target, room, user) {
		var rand = ['ou', 'pu', 'randombattle', 'ubers', 'uu', 'ru', 'pu', '1v1', 'hackmonscup', 'monotype', 'challengecup1v1', 'ubers', 'lc'][Math.floor(Math.random() * 13)];
		if (!this.can('broadcast', null, room)) return;
		this.parse('/tour new ' + rand + ', elimination');
	},

	pr: 'pollremind',
	pollremind: function (target, room, user) {
		if (!Poll[room.id]) Poll.reset(room.id);
		if (!Poll[room.id].question) return this.sendReply("There is no poll currently going on in this room.");
		if (!this.canBroadcast()) return;
		this.sendReplyBox(Poll[room.id].display);
	},

	formatpoll: 'tierpoll',
	tpoll: 'tierpoll',
	tierspoll: 'tierpoll',
	tierpoll: function (target, room, user) {
		if (!this.can('broadcast', null, room)) return false;
		this.parse("/poll Tournament tier?," + "Random Battle, OU, Ubers, UU, RU, NU, LC, Anything Goes, Battle Spot Singles, Custom Game, Random Doubles Battle, Doubles OU, Battle Spot Doubles (VGC 2015), Doubles Custom Game, Random Triples Battle, Smogon Triples, Triples Custom Game, CAP, Battle Factory, Challenge Cup 1v1, Balanced Hackmons, 1v1, Monotype, Tier Shift, PU, Inverse Battle, Monotype Random Battle");
	},

	vote: function (target, room, user) {
		if (!Poll[room.id]) Poll.reset(room.id);
		if (!Poll[room.id].question) return this.sendReply("There is no poll currently going on in this room.");
		if (!target) return this.parse('/help vote');
		if (Poll[room.id].optionList.indexOf(target.toLowerCase()) === -1) return this.sendReply("'" + target + "' is not an option for the current poll.");

		var ips = JSON.stringify(user.ips);
		Poll[room.id].options[ips] = target.toLowerCase();

		return this.sendReply("You are now voting for " + target + ".");
	},
	votehelp: ["/vote [option] - Vote for an option in the poll."],

	votes: function (target, room, user) {
		if (!this.canBroadcast()) return;
		if (!Poll[room.id]) Poll.reset(room.id);
		if (!Poll[room.id].question) return this.sendReply("There is no poll currently going on in this room.");
		this.sendReply("NUMBER OF VOTES: " + Object.keys(Poll[room.id].options).length);
	}
};
<commit_msg>Update poll.js<commit_after>/*
* Poll chat plugin
* By bumbadadabum and Zarel.
*/

var permission = 'broadcast';
var bluebutton = 'padding:2px; background-color:#000; border:1px solid #666; color:white';

var Poll = (function () {
	function Poll(room, question, options) {
		if (room.pollNumber) {
			room.pollNumber++;
		} else {
			room.pollNumber = 1;
		}
		this.room = room;
		this.question = question;
		this.voters = new Set();
		this.totalVotes = 0;
		this.timeout = null;
		this.timeoutMins = 0;

		this.options = new Map();
		for (var i = 0; i < options.length; i++) {
			this.options.set(i + 1, {name: options[i], votes: 0});
		}
	}

	Poll.prototype.vote = function (user, option) {
		if (this.voters.has(user.latestIp)) {
			return user.sendTo(this.room, "You have already voted for this poll.");
		} else {
			this.voters.add(user.latestIp);
			if (this.room.id === 'lobby' && user.named) {
				economy.writeMoney('money', user.userid, +1);
				user.sendTo(this.room, "Thank you for voting for this Lobby poll.  You have received 1 buck as a result!");
			}
		}

		this.options.get(option).votes++;
		this.totalVotes++;

		this.update();
	};

	Poll.prototype.blankvote = function (user, option) {
		if (this.voters.has(user.latestIp)) {
			return user.sendTo(this.room, "You're already looking at the results.");
		} else {
			this.voters.add(user.latestIp);
		}

		this.update();
	};

	Poll.prototype.generateVotes = function () {
		var output = '

 Poll ' + Tools.escapeHTML(this.question) + '
';
		this.options.forEach(function (option, number) {
			output += '
<button style="' + blackbutton + '" value="/poll vote ' + number + '" name="send" title="Vote for ' + number + '. ' + Tools.escapeHTML(option.name) + '">' + number + '. ' + Tools.escapeHTML(option.name) + '</button>
';
		});
		output += '
';

		return output;
	};

	Poll.prototype.generateResults = function (ended) {
		var icon = ' ' + (ended ? "Poll ended" : "Poll") + '';
		var totalVotes = '
[Total Votes: ' + this.totalVotes + ']
';
		var output = '

' + icon + ' ' + Tools.escapeHTML(this.question) + '
' + totalVotes;
		var iter = this.options.entries();

		var i = iter.next();
		var c = 0;
		var colors = ['#79A', '#8A8', '#88B'];
		while (!i.done) {
			var percentage = Math.round((i.value[1].votes * 100) / (this.totalVotes || 1));
			output += '
' + i.value[0] + '. ' + Tools.escapeHTML(i.value[1].name) + '  | ' + percentage + '% (' + i.value[1].votes + ' vote' + (i.value[1].votes === 1 ? '' : 's') + ')

';
			i = iter.next();
			c++;
		}
		output += '
';

		return output;
	};

	Poll.prototype.update = function () {
		var results = this.generateResults();

		// Update the poll results for everyone that has voted
		for (var i in this.room.users) {
			var user = this.room.users[i];
			if (this.voters.has(user.latestIp)) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber +  '|' + results);
			}
		}
	};

	Poll.prototype.display = function (user, broadcast) {
		var votes = this.generateVotes();
		var results = this.generateResults();

		var target = {};

		if (broadcast) {
			target = this.room.users;
		} else {
			target[0] = user;
		}

		for (var i in target) {
			var thisUser = target[i];
			if (this.voters.has(thisUser.latestIp)) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber +  '|' + results);
			} else {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber +  '|' + votes);
			}
		}
	};

	Poll.prototype.end = function () {
		var results = this.generateResults(true);

		this.room.send('|uhtmlchange|poll' + this.room.pollNumber +  '|
(The poll has ended – scroll down to see the results)
');
		this.room.send('|html|' + results);
	};

	return Poll;
})();

exports.commands = {
	poll: {
		create: 'new',
		new: function (target, room, user) {
			if (room.type === 'battle') return false;
			if (target.length > 1024) return this.errorReply("Poll too long.");
			var params = target.split(target.includes('|') ? '|' : ',').map(function (param) { return param.trim(); });

			if (!this.can(permission, null, room)) return false;
			if (room.poll) return this.errorReply("There is already a poll in progress in this room.");

			if (params.length < 3) return this.errorReply("Not enough arguments for /poll new.");
			var options = [];

			for (var i = 1; i < params.length; i++) {
				options.push(params[i]);
			}

			if (options.length > 12) {
				return this.errorReply("Too many options for poll (maximum is 12).");
			}

			room.poll = new Poll(room, params[0], options);
			room.poll.display(user, true);
			return this.privateModCommand("(A poll was started by " + user.name + ".)");
		},
		newhelp: ["/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~"],

		vote: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!target) return this.errorReply("Please specify an option.");

			if (target === 'blank') {
				room.poll.blankvote(user);
				return;
			}

			var parsed = parseInt(target);
			if (isNaN(parsed)) return this.errorReply("To vote, specify the number of the option.");

			if (!room.poll.options.has(parsed)) return this.sendReply("Option not in poll.");

			room.poll.vote(user, parsed);
		},
		votehelp: ["/poll vote [number] - Votes for option [number]."],

		timer: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			if (target) {
				if (!this.can(permission, null, room)) return false;
				if (target === 'clear') {
					if (room.poll.timeout) {
						clearTimeout(room.poll.timeout);
						room.poll.timeout = null;
						room.poll.timeoutMins = 0;
						return room.add("The timeout for the poll was cleared.");
					} else {
						return this.errorReply("No timer to clear.");
					}
				}
				var timeout = parseFloat(target);
				if (isNaN(timeout) || timeout <= 0) return this.errorReply("Invalid time given.");
				if (room.poll.timeout) clearTimeout(room.poll.timeout);
				room.poll.timeoutMins = timeout;
				room.poll.timeout = setTimeout((function () {
					room.poll.end();
					delete room.poll;
				}), (timeout * 60000));
				room.add("The timeout for the poll was set to " + timeout + " minutes.");
				return this.privateModCommand("(The poll timeout was set to " + timeout + " minutes by " + user.name + ".)");
			} else {
				if (!this.canBroadcast()) return;
				if (room.poll.timeout) {
					return this.sendReply("The timeout for the poll is " + room.poll.timeoutMins + " minutes.");
				} else {
					return this.sendReply("There's no timer for this poll.");
				}
			}
		},
		timerhelp: ["/poll timer [minutes] - Sets the poll to automatically end after [minutes] minutes. Requires: % @ # & ~", "/poll timer clear - Clears the poll's timer. Requires: % @ # & ~"],

		results: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			return room.poll.blankvote(user);
		},
		resultshelp: ["/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this."],

		close: 'end',
		stop: 'end',
		end: function (target, room, user) {
			if (!this.can(permission, null, room)) return false;
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (room.poll.timeout) clearTimeout(room.poll.timeout);

			room.poll.end();
			delete room.poll;
			return this.privateModCommand("(The poll was ended by " + user.name + ".)");
		},
		endhelp: ["/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],

		timer: function (target, room, user) {
			if (!this.can(permission, null, room)) return false;
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			var timeout = parseFloat(target);
			if (isNaN(timeout)) return this.errorReply("No time given.");
			if (room.poll.timeout) clearTimeout(room.poll.timeout);
			room.poll.timeout = setTimeout((function () {
				room.poll.end();
				delete room.poll;
			}), (timeout * 60000));
			return this.privateModCommand("(The poll timeout was set to " + timeout + " minutes by " + user.name + ".)");
		},
		timerhelp: ["/poll timer [minutes] - Sets the poll to automatically end after [minutes] minutes. Requires: % @ # & ~"],

		pr: 'display',
		pollremind: 'display',
		display: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!this.canBroadcast()) return;
			room.update();

			room.poll.display(user, this.broadcasting);
		},
		displayhelp: ["/poll display - Displays the poll"],

		show: 'display',
		display: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!this.canBroadcast()) return;
			room.update();

			room.poll.display(user, this.broadcasting);
		},
		displayhelp: ["/poll display - Displays the poll"],

		'': function (target, room, user) {
			this.parse('/pollhelp');
		},
	},
	pollhelp: function(target, room, user) {
		if (!this.canBroadcast()) return;
		return this.sendReplyBox(
			"/poll allows rooms to run their own polls. These polls are limited to one poll at a time per room.
" +
			"The poll status is displayed to the users and updated in real time.
" +
			"Accepts the following commands:
" +
			"/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: + % @ # & ~
" +
			"/poll vote [number] - Votes for option [number] (or click the button).
" +
			"/poll timer [minutes] - Sets the poll to automatically end after [minutes]. Requires: + % @ # & ~
" +
			"/poll display - Displays the poll
" +
			"/poll end - Ends a poll and displays the results. Requires: + % @ # & ~"
		);
	},
	votes: function(target, room, user) {
		if (!room.poll) return this.errorReply("There is no poll running in this room.");
		if (!this.canBroadcast()) return;
		var votes = room.poll.totalVotes;
		var lbl = (votes > 1 ? ' VOTES' : ' VOTE');
		return this.sendReplyBox("TOTAL VOTES: " + votes + lbl);
	},
	ep: 'endpoll',
	endpoll: function(target, room, user) {
		this.parse('/poll end');
	},
	pr: 'pollremind',
	pollremind: function(target, room, user) {
		if (!room.poll) return this.errorReply("There is no poll running in this room.");
		if (!this.canBroadcast()) return;
		room.update();

		room.poll.display(user, this.broadcasting);
	},
	oraspoll: function(target, room, user) {
		var tiers = ['Random Battle', 'OU', 'Ubers', 'UU', 'RU', 'NU', 'LC', 'Anything Goes', 'Battle Spot Singles'];
		this.parse('/poll new ORAS Single tier?, ' + tiers);

	},
	randpoll: function(target, room, user) {
		var tiers = ['Random Doubles Battle', 'Random Triples Battle', 'CC1v1', 'Random Monotype', 'Random Sky Battle', 'Random Ubers', 'Random LC', 'Random CAP', 'Random Monogen', 'CC2v2', 'Challenge Cup Metronome', 'Random Haxmons'];
		this.parse('/poll new Random Tier?, ' + tiers);
        },
        ompoll: function(target, room, user) {
		var tiers = ['CAP', 'Balanced Hackmons', '1v1', 'Monotype', 'Tier Shift', 'PU', 'Inverse Battle', 'Almost Any Ability', 'STABmons', 'LC UU', 'Snowy OU'];
		this.parse('/poll new Other Metas Tournament?, ' + tiers);
	},

	easytour: 'etour',
	elimtour: 'etour',
	etour: function (target, room, user) {
		if (!this.can('broadcast', null, room)) return;
		this.parse('/tour new ' + target + ', elimination');
	},

	roundrobintour: 'rtour',
	cancertour: 'rtour',
	rtour: function (target, room, user) {
		if (!this.can('broadcast', null, room)) return;
		this.parse('/tour new ' + target + ', roundrobin');
	},
	vote: function(target, room, user) {
		if (!target) return this.errorReply("Usage: /vote [poll option number] - votes for the [option] in the current poll.");
		this.parse('/poll vote ' + target);
	}
};
<|endoftext|>


<commit_before>/*
* Poll chat plugin
* By bumbadadabum and Zarel.
*/

'use strict';

class Poll {
	constructor(room, question, options) {
		if (room.pollNumber) {
			room.pollNumber++;
		} else {
			room.pollNumber = 1;
		}
		this.room = room;
		this.question = question;
		this.voters = {};
		this.voterIps = {};
		this.totalVotes = 0;
		this.timeout = null;
		this.timeoutMins = 0;

		this.options = new Map();
		for (let i = 0; i < options.length; i++) {
			this.options.set(i + 1, {name: options[i], votes: 0});
		}
	}

	vote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You have already voted for this poll.");
		}

		this.voters[userid] = option;
		this.voterIps[ip] = option;
		this.options.get(option).votes++;
		this.totalVotes++;

		this.update();
	}

	blankvote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You're already looking at the results.");
		} else {
			this.voters[userid] = 0;
			this.voterIps[ip] = 0;
		}

		this.updateTo(user);
	}

	generateVotes() {
		let output = '

 Poll ' + Tools.escapeHTML(this.question) + '
';
		this.options.forEach((option, number) => {
			output += '
<button class="button" style="padding:2px;font-size:11px;text-align:left" value="/poll vote ' + number + '" name="send" title="Vote for ' + number + '. ' + Tools.escapeHTML(option.name) + '">' + number + '. ' + Tools.escapeHTML(option.name) + '</button>
';
		});
		output += '
<button value="/poll results" name="send" title="View results - you will not be able to vote after viewing results">(View results)</button>
';
		output += '
';

		return output;
	}

	generateResults(ended, option) {
		let icon = ' ' + (ended ? "Poll ended" : "Poll") + '';
		let output = '

' + icon + ' ' + Tools.escapeHTML(this.question) + '
';
		let iter = this.options.entries();

		let i = iter.next();
		let c = 0;
		let colors = ['#79A', '#8A8', '#88B'];
		while (!i.done) {
			let percentage = Math.round((i.value[1].votes * 100) / (this.totalVotes || 1));
			output += '
' + i.value[0] + '. ' + (i.value[0] === option ? '' : '') + Tools.escapeHTML(i.value[1].name) + (i.value[0] === option ? '' : '') + ' (' + i.value[1].votes + ' vote' + (i.value[1].votes === 1 ? '' : 's') + ')
 ' + percentage + '%
';
			i = iter.next();
			c++;
		}
		if (option === 0 && !ended) output += '
(You can\'t vote after viewing results)
';
		output += '
';

		return output;
	}

	update() {
		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		// Update the poll results for everyone that has voted
		for (let i in this.room.users) {
			let user = this.room.users[i];
			if (user.userid in this.voters) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voters[user.userid]]);
			} else if (user.latestIp in this.voterIps) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voterIps[user.latestIp]]);
			}
		}
	}

	updateTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	updateFor(user) {
		if (user.userid in this.voters) {
			user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		}
	}

	display() {
		let votes = this.generateVotes();

		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		for (let i in this.room.users) {
			let thisUser = this.room.users[i];
			if (thisUser.userid in this.voters) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voters[thisUser.userid]]);
			} else if (thisUser.latestIp in this.voterIps) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voterIps[thisUser.latestIp]]);
			} else {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + votes);
			}
		}
	}

	displayTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	onConnect(user, connection) {
		this.displayTo(user, connection);
	}

	end() {
		let results = this.generateResults(true);

		this.room.send('|uhtmlchange|poll' + this.room.pollNumber + '|
(The poll has ended – scroll down to see the results)
');
		this.room.add('|html|' + results);
	}
}

exports.commands = {
	poll: {
		create: 'new',
		new: function (target, room, user, connection, cmd, message) {
			if (!target) return this.parse('/help poll new');
			if (target.length > 1024) return this.errorReply("Poll too long.");
			let params = target.split(target.includes('|') ? '|' : ',').map(param => param.trim());

			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (room.poll) return this.errorReply("There is already a poll in progress in this room.");

			if (params.length < 3) return this.errorReply("Not enough arguments for /poll new.");
			let options = [];

			for (let i = 1; i < params.length; i++) {
				options.push(params[i]);
			}

			if (options.length > 8) {
				return this.errorReply("Too many options for poll (maximum is 8).");
			}

			room.poll = new Poll(room, params[0], options);
			room.poll.display();

			this.logEntry("" + user.name + " used " + message);
			return this.privateModCommand("(A poll was started by " + user.name + ".)");
		},
		newhelp: ["/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~"],

		vote: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!target) return this.parse('/help poll vote');

			if (target === 'blank') {
				room.poll.blankvote(user);
				return;
			}

			let parsed = parseInt(target);
			if (isNaN(parsed)) return this.errorReply("To vote, specify the number of the option.");

			if (!room.poll.options.has(parsed)) return this.sendReply("Option not in poll.");

			room.poll.vote(user, parsed);
		},
		votehelp: ["/poll vote [number] - Votes for option [number]."],

		timer: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			if (target) {
				if (!this.can('minigame', null, room)) return false;
				if (target === 'clear') {
					if (!room.poll.timeout) return this.errorReply("There is no timer to clear.");
					clearTimeout(room.poll.timeout);
					room.poll.timeout = null;
					room.poll.timeoutMins = 0;
					return this.add("The poll timer was turned off.");
				}
				let timeout = parseFloat(target);
				if (isNaN(timeout) || timeout <= 0 || timeout > 0x7FFFFFFF) return this.errorReply("Invalid time given.");
				if (room.poll.timeout) clearTimeout(room.poll.timeout);
				room.poll.timeoutMins = timeout;
				room.poll.timeout = setTimeout(() => {
					room.poll.end();
					delete room.poll;
				}, (timeout * 60000));
				room.add("The poll timer was turned on: the poll will end in " + timeout + " minute(s).");
				return this.privateModCommand("(The poll timer was set to " + timeout + " minute(s) by " + user.name + ".)");
			} else {
				if (!this.runBroadcast()) return;
				if (room.poll.timeout) {
					return this.sendReply("The poll timer is on and will end in " + room.poll.timeoutMins + " minute(s).");
				} else {
					return this.sendReply("The poll timer is off.");
				}
			}
		},
		timerhelp: ["/poll timer [minutes] - Sets the poll to automatically end after [minutes] minutes. Requires: % @ # & ~", "/poll timer clear - Clears the poll's timer. Requires: % @ # & ~"],

		results: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			return room.poll.blankvote(user);
		},
		resultshelp: ["/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this."],

		close: 'end',
		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (room.poll.timeout) clearTimeout(room.poll.timeout);

			room.poll.end();
			delete room.poll;
			return this.privateModCommand("(The poll was ended by " + user.name + ".)");
		},
		endhelp: ["/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],

		show: 'display',
		display: function (target, room, user, connection) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!this.runBroadcast()) return;
			room.update();

			if (this.broadcasting) {
				room.poll.display();
			} else {
				room.poll.displayTo(user, connection);
			}
		},
		displayhelp: ["/poll display - Displays the poll"],

		'': function (target, room, user) {
			this.parse('/help poll');
		},
	},
	pollhelp: ["/poll allows rooms to run their own polls. These polls are limited to one poll at a time per room.",
				"Accepts the following commands:",
				"/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~",
				"/poll vote [number] - Votes for option [number].",
				"/poll timer [minutes] - Sets the poll to automatically end after [minutes]. Requires: % @ # & ~",
				"/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this.",
				"/poll display - Displays the poll",
				"/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],
};
<commit_msg>Nicer looking poll buttons<commit_after>/*
* Poll chat plugin
* By bumbadadabum and Zarel.
*/

'use strict';

class Poll {
	constructor(room, question, options) {
		if (room.pollNumber) {
			room.pollNumber++;
		} else {
			room.pollNumber = 1;
		}
		this.room = room;
		this.question = question;
		this.voters = {};
		this.voterIps = {};
		this.totalVotes = 0;
		this.timeout = null;
		this.timeoutMins = 0;

		this.options = new Map();
		for (let i = 0; i < options.length; i++) {
			this.options.set(i + 1, {name: options[i], votes: 0});
		}
	}

	vote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You have already voted for this poll.");
		}

		this.voters[userid] = option;
		this.voterIps[ip] = option;
		this.options.get(option).votes++;
		this.totalVotes++;

		this.update();
	}

	blankvote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You're already looking at the results.");
		} else {
			this.voters[userid] = 0;
			this.voterIps[ip] = 0;
		}

		this.updateTo(user);
	}

	generateVotes() {
		let output = '

 Poll ' + Tools.escapeHTML(this.question) + '
';
		this.options.forEach((option, number) => {
			output += '
<button class="button" style="padding:2px 6px;font-size:11px;text-align:left" value="/poll vote ' + number + '" name="send" title="Vote for ' + number + '. ' + Tools.escapeHTML(option.name) + '">' + number + '. ' + Tools.escapeHTML(option.name) + '</button>
';
		});
		output += '
<button value="/poll results" name="send" title="View results - you will not be able to vote after viewing results">(View results)</button>
';
		output += '
';

		return output;
	}

	generateResults(ended, option) {
		let icon = ' ' + (ended ? "Poll ended" : "Poll") + '';
		let output = '

' + icon + ' ' + Tools.escapeHTML(this.question) + '
';
		let iter = this.options.entries();

		let i = iter.next();
		let c = 0;
		let colors = ['#79A', '#8A8', '#88B'];
		while (!i.done) {
			let percentage = Math.round((i.value[1].votes * 100) / (this.totalVotes || 1));
			output += '
' + i.value[0] + '. ' + (i.value[0] === option ? '' : '') + Tools.escapeHTML(i.value[1].name) + (i.value[0] === option ? '' : '') + ' (' + i.value[1].votes + ' vote' + (i.value[1].votes === 1 ? '' : 's') + ')
 ' + percentage + '%
';
			i = iter.next();
			c++;
		}
		if (option === 0 && !ended) output += '
(You can\'t vote after viewing results)
';
		output += '
';

		return output;
	}

	update() {
		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		// Update the poll results for everyone that has voted
		for (let i in this.room.users) {
			let user = this.room.users[i];
			if (user.userid in this.voters) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voters[user.userid]]);
			} else if (user.latestIp in this.voterIps) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voterIps[user.latestIp]]);
			}
		}
	}

	updateTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	updateFor(user) {
		if (user.userid in this.voters) {
			user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		}
	}

	display() {
		let votes = this.generateVotes();

		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		for (let i in this.room.users) {
			let thisUser = this.room.users[i];
			if (thisUser.userid in this.voters) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voters[thisUser.userid]]);
			} else if (thisUser.latestIp in this.voterIps) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voterIps[thisUser.latestIp]]);
			} else {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + votes);
			}
		}
	}

	displayTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	onConnect(user, connection) {
		this.displayTo(user, connection);
	}

	end() {
		let results = this.generateResults(true);

		this.room.send('|uhtmlchange|poll' + this.room.pollNumber + '|
(The poll has ended – scroll down to see the results)
');
		this.room.add('|html|' + results);
	}
}

exports.commands = {
	poll: {
		create: 'new',
		new: function (target, room, user, connection, cmd, message) {
			if (!target) return this.parse('/help poll new');
			if (target.length > 1024) return this.errorReply("Poll too long.");
			let params = target.split(target.includes('|') ? '|' : ',').map(param => param.trim());

			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (room.poll) return this.errorReply("There is already a poll in progress in this room.");

			if (params.length < 3) return this.errorReply("Not enough arguments for /poll new.");
			let options = [];

			for (let i = 1; i < params.length; i++) {
				options.push(params[i]);
			}

			if (options.length > 8) {
				return this.errorReply("Too many options for poll (maximum is 8).");
			}

			room.poll = new Poll(room, params[0], options);
			room.poll.display();

			this.logEntry("" + user.name + " used " + message);
			return this.privateModCommand("(A poll was started by " + user.name + ".)");
		},
		newhelp: ["/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~"],

		vote: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!target) return this.parse('/help poll vote');

			if (target === 'blank') {
				room.poll.blankvote(user);
				return;
			}

			let parsed = parseInt(target);
			if (isNaN(parsed)) return this.errorReply("To vote, specify the number of the option.");

			if (!room.poll.options.has(parsed)) return this.sendReply("Option not in poll.");

			room.poll.vote(user, parsed);
		},
		votehelp: ["/poll vote [number] - Votes for option [number]."],

		timer: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			if (target) {
				if (!this.can('minigame', null, room)) return false;
				if (target === 'clear') {
					if (!room.poll.timeout) return this.errorReply("There is no timer to clear.");
					clearTimeout(room.poll.timeout);
					room.poll.timeout = null;
					room.poll.timeoutMins = 0;
					return this.add("The poll timer was turned off.");
				}
				let timeout = parseFloat(target);
				if (isNaN(timeout) || timeout <= 0 || timeout > 0x7FFFFFFF) return this.errorReply("Invalid time given.");
				if (room.poll.timeout) clearTimeout(room.poll.timeout);
				room.poll.timeoutMins = timeout;
				room.poll.timeout = setTimeout(() => {
					room.poll.end();
					delete room.poll;
				}, (timeout * 60000));
				room.add("The poll timer was turned on: the poll will end in " + timeout + " minute(s).");
				return this.privateModCommand("(The poll timer was set to " + timeout + " minute(s) by " + user.name + ".)");
			} else {
				if (!this.runBroadcast()) return;
				if (room.poll.timeout) {
					return this.sendReply("The poll timer is on and will end in " + room.poll.timeoutMins + " minute(s).");
				} else {
					return this.sendReply("The poll timer is off.");
				}
			}
		},
		timerhelp: ["/poll timer [minutes] - Sets the poll to automatically end after [minutes] minutes. Requires: % @ # & ~", "/poll timer clear - Clears the poll's timer. Requires: % @ # & ~"],

		results: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			return room.poll.blankvote(user);
		},
		resultshelp: ["/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this."],

		close: 'end',
		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (room.poll.timeout) clearTimeout(room.poll.timeout);

			room.poll.end();
			delete room.poll;
			return this.privateModCommand("(The poll was ended by " + user.name + ".)");
		},
		endhelp: ["/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],

		show: 'display',
		display: function (target, room, user, connection) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!this.runBroadcast()) return;
			room.update();

			if (this.broadcasting) {
				room.poll.display();
			} else {
				room.poll.displayTo(user, connection);
			}
		},
		displayhelp: ["/poll display - Displays the poll"],

		'': function (target, room, user) {
			this.parse('/help poll');
		},
	},
	pollhelp: ["/poll allows rooms to run their own polls. These polls are limited to one poll at a time per room.",
				"Accepts the following commands:",
				"/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~",
				"/poll vote [number] - Votes for option [number].",
				"/poll timer [minutes] - Sets the poll to automatically end after [minutes]. Requires: % @ # & ~",
				"/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this.",
				"/poll display - Displays the poll",
				"/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],
};
<|endoftext|>