Create a Password Generator with GTK and JavaScript
Tags: javascript, gnome, gtk
Published: 2021-06-07 05:20:42

## State 0: The Setup

To follow up this tutorial, you'll need this: 
[ ] A [text editor](https://www.codexworld.com/5-best-free-text-editor-web-development/)
[ ] [GTK](https://prognotes.net/2015/12/installing-gtk-3-and-glade-development-tools-in-linux/)
[ ] [glade](http://glade.gnome.org/), and
[ ] [Gjs](https://gjs.guide/)

## Stage 1: The Algorithm

We're going to use a pretty simple algorithm to generate passwords based in some character collections group by type.

First, let's create the collections:
```js
lowerCase = 'qwertyuiopasdfghjklzxcvbnm'.split('');
upperCase = 'QWERTYUIOPASDFGHJKLZXCVBNM'.split('');
numbers = '1234567890'.split('');
specialSymbols = `!@#$%^&*-_+=`.split('');
ambiguousSymbols = `{}[]()/\\'"?,.:;~\`|<>`.split('');
```
Note I'm creating arrays from the strings since it's quicker that way.

Now, lets move to our main problem: Generate a Password.

```js
function generate(
	length = 16, 
	useLower = true, 
	useUpper = true, 
	useNumbers = true, 
	useSpecials = false, 
	useAmbiguous = false
	) {

	const source = [];
	const password = [];

	if (useLower) {
		source.push(...lowerCase);
	}
	if (useUpper) {
		source.push(...upperCase);
	}
	if (useNumbers) {
		source.push(...numbers);
	}
	if (useSpecials) {
		source.push(...specialSymbols);
	}
	if (useAmbiguous) {
		source.push(...ambiguousSymbols);
	}

	for (let i = 0; i < length; i++) {
		const char = source[Math.floor(Math.random() * (source.length - 1))];
		password.push(char);
	}

	return password.join('');
}
```
Done! Yeah! That's it!

You can test the generator in [this codepen](https://codepen.io/kenliten/pen/zYZaGaN).

## Stage 2: The GUI

Now, that we have a working logic for our little tool, let's create the GUI with [glade](http://glade.gnome.org/).

1. Start creating a window and set an ID to it, mine is `MainWindow`:

![Step 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qrs9dec9utvmpprxn5te.png)
![Step 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e2jtmp788l7uji2snqsu.png)

2. Add a `GtkBox` and set the following properties:
- Orientation: Vertical
- Spacing: 0
- Baseline: Center
- Homogeneous => on
- Number of items: 3

![Step 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09casr41lqvn2bi8007f.png)

3. Add a `GtkHeaderBar` and set it up like this:
(General)
- Title: Password Generator
- Reserve space for subtitle => off
- Spacing: 6
- Number of items: 0
(Packing)
- Position: 0
- Padding: 0
- Fill => on
- Expand => on
- Pack type: Start

![Step 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmto4ozsj6n6sb68gpos.png)
 
4. A `GtkGrid` for some checkboxes:

![Step 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mtt7mi5m85v5bkcsz1ug.png)

General Properties:
![Step 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nql6fiu2sr0x0dt4k1ao.png)

Packing Properties:
![Step 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1yhr8aj7x7o0o1tdvtzf.png)
 
5. Another `GtkBox` at the bottom:

![Step 5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/izz9tmf7gd57e2m74ydk.png)

Packing Properties:
![Step 5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/knsn65fgq4cxvh1rp7ih.png)

6. Add, customize 5 `GtkCheckButton` and give a unique ID to each one:

![Step 6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2zdjjukeqvqd48l3vxi8.png)
![Step 6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cu02ev5jpj0j5fdxtoou.png)

Mine have those ID's:
- lowerCaseCheck
- upperCaseCheck
- numbersCheck
- specialSymbolsCheck
- ambiguousCharsCheck
 
7. Add a `GtkLabel` and a `GtkEntry` inside a `GtkBox`:

![Step 7](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eeegqkcushlu3qodt4kd.png)
![Step 7](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9gbkqkpulzcojoqwcca.png)

8. Add A `GtkButton` to call the password generator:

![Step 8](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elndx7hhvem0wj5xfnhv.png)

9. Two more `GtkLabel` to show the generated password:

![Step 9](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2sdr9o7pl0ldgauvr880.png)

Leave the second one without text and make it `selectable` to allow user to copy the password. Don't forget to assign it an ID, in my case is 'generatedPasswordLabel'.

Now just save your new GUI in the project folder. You can save it as a `.ui` or a `.glade` file. Any way remember to call it exactly as it is. In my case is `mainWindow.ui`

## Stage 3: Wrapping it up

Ok! Now all we need to do is make this two things work together.

1. Import the Gtk functions. Create a new file and save it with this content as `main.js`.

```js
#!/usr/bin/gjs

imports.gi.versions.Gtk = '3.0';
const Gtk = imports.gi.Gtk;
```

Here the first line tells the interpreter we want to use to run this program, `Gjs`.
Next we ensure we are working with GTK+ v3.0 and import it as `Gtk`.

2. Using OOP
Now is time to add our logic, you can work this with a functional programming workflow, but we're going to use a OOP approach:

```js
class PasswordGenerator {
  constructor() {
    this.app = new Gtk.Application();
    this.app.connect('activate', this._onActivate.bind(this));
    this.app.connect('startup', this._onStartup.bind(this));
  }

  _onActivate() {
    this._window.show_all();
  }

  _onStartup() {
    const builder = new Gtk.Builder();
    builder.add_from_file('mainWindow.ui');
    this._window = builder.get_object('mainWindow');
    this.app.add_window(this._window);
  }
}

const generator = new PasswordGenerator();

generator.app.run(ARGV);
```
If you run this, you're going to see your previously designed GUI, but no actions except quit.

Ok, But... What's going on here?
Well, here, we create a Gtk Application in the class constructor, and connect two events, (`activate`, and `startup`) to our methods, `_onActivate` and `_onStartup`.

Inside `_onStartup` we just show up our GUI.
Inside `_onActivate` we create a `builder` object, so we can just 'require' our `.ui` file to charge the GUI. Then we get the UI from the file and save it to a property: `_window`. Lastly, we add the window to the app. This method will be called before `_onStartup`, so `_window` will be set just before be used.

3. Linking the Widgets.

Now, we're going to load all the other widgets we neet to collect the info to generate a password.

Let's modify the `constructor` and the `_onActivate` methods:

```js
constructor() {
  this.app = new Gtk.Application();
  this.app.connect('activate', this._onActivate.bind(this));
  this.app.connect('startup', this._onStartup.bind(this));
  this._lowerCase = 'qwertyuiopasdfghjklzxcvbnm'.split('');
  this._upperCase = 'QWERTYUIOPASDFGHJKLZXCVBNM'.split('');
  this._numbers = '1234567890'.split('');
  this._specialSymbols = `!@#$%^&*-_+=`.split('');
  this._ambiguousSymbols = `{}[]()/\\'"?,.:;~\`|<>`.split('');
}

_onStartup() {
  const builder = new Gtk.Builder();
  builder.add_from_file('mainWindow.ui');
  this._window = builder.get_object('mainWindow');
  this._generateButton = builder.get_object('generateButton');
  this._generateButton.connect('clicked', this._generatePassword.bind(this));
  this._lowerCaseCheck = builder.get_object('lowerCaseCheck');
  this._upperCaseCheck = builder.get_object('upperCaseCheck');
  this._numbersCheck = builder.get_object('numbersCheck');
  this._specialSymbolsCheck = builder.get_object('specialSymbolsCheck');
  this._ambiguousCharsCheck = builder.get_object('ambiguousCharsCheck');
  this._passwordLengthEntry = builder.get_object('passwordLengthEntry');
  this._generatedPasswordLabel = builder.get_object('generatedPasswordLabel');
  this.app.add_window(this._window);
}
```

4. Add the logic:

Now, all we need to do is add a new version of our generator:

```js
_generatePassword() {
  const source = [];
  const password = [];
  const length = +this._passwordLengthEntry.text;

  if (this._lowerCaseCheck.active) {
    source.push(...this._lowerCase);
  }
  if (this._upperCaseCheck.active) {
    source.push(...this._upperCase);
  }
  if (this._numbersCheck.active) {
    source.push(...this._numbers);
  }
  if (this._specialSymbolsCheck.active) {
    source.push(...this._specialSymbols);
  }
  if (this._ambiguousCharsCheck.active) {
    source.push(...this._ambiguousSymbols);
  }

  for (let i = 0; i < length; i++) {
    const char = source[Math.floor(Math.random() * (source.length - 1))];
    password.push(char);
  }

  this._generatedPasswordLabel.label = password.join('');
}
```

DONE!

Go to your terminal and run this:
```bash
chmod +x ./main.js # this make the file executable
./main.js # this executes the program
```

## Resources

[Here](https://github.com/kenliten/password-generator) is the full project, including a `.desktop` file and a simple icon. Check it out.