{"size": 8735, "ext": "js", "max_stars_repo_path": "src/PasswordGen.js", "max_stars_repo_name": "zeraphie/passwordGenJS", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "src/PasswordGen.js", "max_issues_repo_name": "zeraphie/passwordGenJS", "max_issues_repo_issues_event_min_datetime": "2018-08-15T10:47:20.000Z", "max_issues_repo_issues_event_max_datetime": "2018-08-15T10:47:20.000Z", "max_forks_repo_path": "src/PasswordGen.js", "max_forks_repo_name": "zeraphie/passwordGenJS", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": 1, "max_forks_count": null, "avg_line_length": 29.4107744108, "max_line_length": 82, "alphanum_fraction": 0.5053234116}
/**
 * Password generator class
 *
 * This is a JavaScript version of my PasswordGen PHP library accessible here:
 * https://github.com/zeraphie/passwordGen
 *
 * This does not rely on any outside libraries apart from the window.crypto
 * object, and has browser support for at least IE11+
 *
 * @license MIT
 * @version 1.0.2
 * @author Izzy Skye
 */
export default class PasswordGen {
    /**
     * Create a new PasswordGen instance and setting the default character
     * groups to be used by this class
     */
    constructor(){
        /*--------------------------------------
         Setup of the length and keyspace
         variables
         --------------------------------------*/
        this.length = this.constructor.default_length;
        this.keyspace = '';
        
        /*--------------------------------------
         Setup the keyspace
         --------------------------------------*/
        this.generateKeyspace();
    }
    
    /**
     * Getter for minimum_length class variable for the minimum length of
     * the password generated
     *
     * @return {number} The minimum password length
     */
    static get minimum_length(){
        return 8;
    }
    
    /**
     * Getter for maximum_random_integer class variable for the maximum limit of
     * random integer
     *
     * @return {number} The maximum random integer
     */
    static get maximum_random_integer(){
        return 256;
    }
    
    /**
     * Getter for default_length class variable used to generate the default
     * password length
     *
     * @return {number} The default password length
     */
    static get default_length(){
        return 16;
    }
    
    /**
     * Getter for default_sets class variable used to generate the keyspace
     *
     * @return {string} The default sets
     */
    static get default_sets(){
        return 'luns';
    }
    
    /**
     * Getter for lowercase_letters set used in generating the keyspace
     *
     * @return {string} All lower case letters
     */
    static get lowercase_letters(){
        return 'abcdefghijklmnopqrstuvwxyz';
    }
    
    /**
     * Getter for uppercase_letters set used in generating the keyspace
     *
     * @return {string} All upper case letters
     */
    static get uppercase_letters(){
        return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    }
    
    /**
     * Getter for numbers set used in generating the keyspace
     *
     * @return {string} All single digits
     */
    static get numbers(){
        return '1234567890';
    }
    
    /**
     * Getter for special_characters set used in generating the keyspace
     *
     * @return {string} All special characters used
     */
    static get special_characters(){
        return '!@#$%&*?,./|[]{}()';
    }
    
    /**
     * Getter for whitespace set used in generating the keyspace
     *
     * @return {string} All whitespace characters used
     */
    static get whitespace(){
        return ' ';
    }
    
    /**
     * Getter for whitespace set used in generating the keyspace
     *
     * @return {object} All whitespace characters used
     */
    static get character_sets(){
        return {
            'l': this.lowercase_letters,
            'u': this.uppercase_letters,
            'n': this.numbers,
            's': this.special_characters,
            'w': this.whitespace
        };
    }
    
    /**
     * Test if any elements of an array exist as keys in another array
     *
     * @param needles {array} The needles to search for
     * @param haystack {object} The haystack to search
     * @returns {boolean} Whether any needles exist as keys in the haystack
     */
    static arrayKeySearch(needles, haystack){
        let i = 0, length = needles.length;
        
        while(i < length){
            for(let item in haystack){
                if(needles[i] === item){
                    return true;
                }
            }
            
            i++;
        }
        
        return false;
    }
    
    /**
     * Generate a cryptographically strong random number between two values
     *
     * @param min {number} The minimum number
     * @param max {number} The maximum number
     * @returns {number|*}
     */
    static randomInteger(min, max){
        if(max < this.maximum_random_integer){
            let crypto = window.crypto || window.msCrypto;
            let byteArray = new Uint8Array(1);
            crypto.getRandomValues(byteArray);
            
            let range = max - min + 1;
            
            if(
                byteArray[0]
                    >=
                Math.floor(this.maximum_random_integer / range) * range
            ){
                return this.randomInteger(min, max);
            }
            
            return min + (byteArray[0] % range);
        } else {
            throw `Sorry the maximum is too large\n` +
            `The maximum size is ${this.maximum_random_integer}\n`;
        }
    }
    
    /**
     * Set the length of the password, checking if it's an integer and
     * higher than the minimum required length
     *
     * @param value {number} Length of the generated password
     * @returns {PasswordGen} The current instance of PasswordGen
     */
    setLength(value = 0){
        if(
            value === parseInt(value)
                &&
            value >= this.constructor.minimum_length
        ){
            this.length = value;
        }
        
        return this;
    }
    
    /**
     * Set the keyspace of the password generator, checking if it's set and not
     * an empty string
     *
     * @param keyspace {string} The keyspace to be used by the generator
     * @returns {PasswordGen} The current instance of PasswordGen
     */
    setKeyspace(keyspace = ''){
        if(typeof keyspace === 'string' && keyspace !== ''){
            if(keyspace.length < this.constructor.maximum_random_integer){
                this.keyspace = keyspace;
            } else {
                console.log(`The keyspace is too long, falling back to default`);
            }
        }
        
        return this;
    }
    
    /**
     * Generate the keyspace of the password generator using the character
     * groups
     *
     * @param sets {string} Sets to be used for the generator
     * @returns {PasswordGen} The current instance of PasswordGen
     */
    generateKeyspace(sets = this.constructor.default_sets){
        this.keyspace = '';
        
        /*--------------------------------------
         Test if the sets variable is a string
         and if any of the characters in it
         are in the character_sets array's keys
         --------------------------------------*/
        if(
            typeof sets === 'string'
                &&
            this.constructor.arrayKeySearch(
                sets.split(''), this.constructor.character_sets
            )
        ){
            /*--------------------------------------
             Split the sets string on every
             character and loop through them
             --------------------------------------*/
            for(let set in sets.split('')){
                this.keyspace += this.constructor.character_sets[
                    sets[set]
                ];
            }
        } else {
            for(let set in this.constructor.default_sets.split('')){
                this.keyspace += this.constructor.character_sets[
                    this.constructor.default_sets[set]
                ];
            }
        }
        
        if(this.keyspace.length > this.constructor.maximum_random_integer){
            console.log(`The keyspace is too long, falling back to default`);
            this.generateKeyspace();
        }
        
        return this;
    }

    /**
     * Generate the password by selecting a random character from
     * the keyspace generated
     *
     * @returns {string} The generated password
     */
    generatePassword(){
        let password = '';
        
        for(let i = 0; i < this.length; i++){
            password += this.keyspace.split('')[
                this.constructor.randomInteger(0, this.keyspace.length - 1)
            ];
        }
        
        return password;
    }
    
    /**
     * The generated password - New one each time this is accessed
     *
     * @returns {string}
     */
    get password(){
        return this.generatePassword();
    }
}
