Gaining Bitwisdom from Keyboard Shortcuts

February 20, 2022

Managing keyboard shortcuts in JavaScript/TypeScript is a real shitshow. Let's say you have the following keyboard shortcuts (assuming you're on macOS):

Here's one way to implement it:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  if (
    event.metaKey &&
    !event.altKey &&
    !event.ctrlKey &&
    !event.shiftKey &&
    event.code === "KeyL"
  ) {
    doThingA();
  }

  if (
    event.metaKey &&
    event.ctrlKey &&
    !event.altKey &&
    !event.shiftKey &&
    event.code === "KeyL"
  ) {
    doThingB();
  }

  if (
    event.metaKey &&
    event.ctrlKey &&
    event.shiftKey &&
    !event.altKey &&
    event.code === "KeyL"
  ) {
    doThingC();
  }
});

Here's another way, which is less code but more confusing (in my opinion):

document.addEventListener("keydown", (event: KeyboardEvent) => {
  if (event.metaKey && !event.altKey && event.code === "KeyL") {
    if (event.ctrlKey) {
      if (event.shiftKey) {
        doThingC();
      } else {
        doThingB();
      }
    } else {
      doThingA();
    }
  }
});

So your choices out-of-the-box are to make it confusing or make it verbose. Good news! It turns out there's a third option: bit flags.

I needed to add quite a few keyboard shortcuts to a side project I'm working on, so I implemented my own little keyboard shortcut library using bit flags. My esteemed colleague, Matt Mayhem (of Bad Shadows and No Tomorrow Boys fame), was reviewing my pull request and wanted to know what the hell was going on with all the & and | operators, so that is why this post exists.

Before I dig into bit shifting, bit flags, and bitwise operators, let's all hop on the trolley to Binary Junction to talk about base-2 numbers.

What's the Deal with Binary?

Most of us operate in the base-10 (or decimal) universe, which I'm too lazy to explain, so I'll let someone else do it. Binary numbers are expressed in the base-2 numeral system, which is just a fancy way of saying it only uses two symbols: 0 and 1.

Note
I'll be using the terms base-10 and decimal interchangeably, but they refer to the same thing.

Counting in binary is a little weird, so let's talk about that weirdness. We're going to use JavaScript's parseInt() function to get the decimal representation of a binary number. The first argument is the binary number string and the second is the radix. To get the decimal value, I'm using a radix of 2 (for base-2 number system).

What do you think will get logged out here?

console.log(parseInt("101", 2));

If you guessed 5, buy yourself a drink! If 101 = 5 sounds like bullshit to you, bear with me. You count binary from right to left. As you move from right to left, the decimal representation of that bit is:

Bit Value * (2 ^ Position Index from Right)

You find the decimal value for each bit and add them all up. So here's how you turn 101 into 5:

Bit Position Math Decimal Value
1 0 1 * 2⁰ 1
0 1 0 * 2¹ 0
1 2 1 * 2² 4
Total 5

That right-most column represents the decimal value of each bit. Since the 0th and 2nd bits are 1, you multiply 1 by 2⁰ (1) and 2² (4) respectively and add them up to get 5.

JavaScript uses 32-bit signed integers for bitwise operations. That means that any bit-twiddling under the hood operates on binary numbers that look like this:

11111111111111111111111111111111

I can save you some time spent counting and assure you that there are 32x 1s in that number. Each 1 represents a bit, which is where "32-bit" comes from. A 32-bit signed integer has a minimum value of -2147483648 and a maximum value of 2147483647. But if you were to run this:

console.log(parseInt("11111111111111111111111111111111", 2));

It logs out 4294967295. How the hell can you get all the way up to 4294967295? Well that's easy, the very first bit is the "sign bit" (and the most significant bit), so flipping it to 1 makes the number unsigned. Since we're no longer dealing with negative numbers, we just shimmy everything over by 2147483648. The minimum of -2147483648 becomes 0, and the maximum of 2147483647 becomes 4294967295.

See for yourself:

console.log(parseInt("11111111111111111111111111111111", 2)); // 4294967295
//                    ^ Unsigned!

console.log(parseInt("01111111111111111111111111111111", 2)); // 2147483647
//                    ^ Signed!

Now that you have a better understanding of binary numbers (hopefully), let's rap about bit shifting and bit flags.

Bit Shifting and Bit Flags

We need to represent the four modifier keys as bit flags. I'm using TypeScript here, so I'm going to use an enum. I'm also using Electron and this application only runs on macOS, so I'm going to represent each modifier in the macOS parlance.

enum Mod {
  Command = 1 << 1, // 2 in base-10
  Control = 1 << 2, // 4 in base-10
  Option  = 1 << 3, // 8 in base-10
  Shift   = 1 << 4, // 16 in base-10
}

The << is the bitwise left shift operator. From the MDN documentation:

The left shift operator (<<) shifts the first operand the specified number of bits to the left. Excess bits shifted off to the left are discarded. Zero bits are shifted in from the right.

So if we were to log out each of those modifiers in their base-2 representation, here's what we'd get:

console.log(
  Mod.Command.toString(2), // "10"
  Mod.Control.toString(2), // "100"
  Mod.Option.toString(2),  // "1000"
  Mod.Shift.toString(2),   // "10000"
);
Note
Like parseInt(), JavaScript's toString() function can take an optional radix argument that converts the number to the specified base.

You can see that the number of 0s directly corresponds to the number to the right of the << operator in the Mod enum.

So what's the point of all this? Well, let's switch to base-10 for a minute. You need to check for multiple true or false conditions. The nice thing about using bit flags is any combination of those Mod values will never add up to the same number:

The list goes on (just trust me). Each possible combination of those flags will never overlap with another combination. For example, you'll never run into an issue where the value could be either (Mod.Command + Mod.Option) or (Mod.Option + Mod.Shift).

Note
There's actually an issue with the amount of bits we're shifting (i.e. 1, 2, 3, and 4), but we'll cover that later.

Now that we've got our modifiers set up, let's cover how to use them with bitwise operators.

Bitwise Operators

JavaScript has four binary bitwise operators: AND (&), OR (|), XOR (^), and NOT (~).

I'm only concerned with the AND, OR, and NOT operators for my purposes, but XOR is there if you need it. Bitwise operators can be described by something called a truth table. If you don't feel like clicking through on that link, here's a quick rundown:

A truth table is a mathematical table used to carry out logical operations in Maths. It includes boolean algebra or boolean functions. It is primarily used to determine whether a compound statement is true or false based on the input values.

The truth table on the linked site is a bit difficult to grasp, so let's go through the truth table for each operator we're concerned with.

Assuming you have two bits a and b. The truth table for the AND bitwise operator looks like this:

a b a & b
0 0 0
0 1 0
1 0 0
1 1 1

If you swapped out 0 for false, 1 for true, and & for &&, it would map to:

console.log(false && false); // false
console.log(false && true);  // false
console.log(true && false);  // false
console.log(true && true);   // true

Here's the truth table for the OR bitwise operator:

a b a | b
0 0 0
0 1 1
1 0 1
1 1 1

If you swapped out 0 for false, 1 for true, and | for ||, it would map to:

console.log(false || false); // false
console.log(false || true);  // true
console.log(true || false);  // true
console.log(true || true);   // true

The truth table for the NOT operator is pretty cut and dried:

a ~a
0 1
1 0

So we have a Mod bit flag enum and some operators we can use to shimmy bits around. Let's apply that to keyboard shortcuts.

Checking for a Modifier with Bits

For the time being, we're going to focus exclusively on modifiers and worry about non-modifier keys (e.g. letters, numbers, symbols) later. Let's create a function called areKeysDown with an event and a combo argument. The event is a KeyboardEvent and the combo is a number that corresponds to our bit flags.

As far as desired functionality goes, we want to check if a specific combination of keys are down. It's important to note that the function returns true if and only if those exact keys are down. So areKeysDown(event, Mod.Command) returns true if the Command key is down, but false if Command + Control is down.

Here's a snippet of what the function looks like:

function areKeysDown(event: KeyboardEvent, combo: number): boolean {
  let keyCode = combo;

  if ((combo & Mod.Command) === Mod.Command) {
    if (!event.metaKey) {
      return false;
    } else {
      keyCode = keyCode & ~Mod.Command;
    }
  } else {
    if (event.metaKey) {
      return false;
    }
  }

  // ... rest of Mod handlers ...

  // This will change when we add non-modifier keys:
  return keyCode === 0;
}
Note
On macOS, event.metaKey indicates the Command key () is down, event.altKey indicates the Option key () is down, event.ctrlKey indicates the Control key () is down, and event.shiftKey indicates the Shift key is down.

And here's how you use that function:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  // Logs out true if event.metaKey = true, other modifiers are false,
  // and no other keys are pressed:
  console.log(areKeysDown(event, Mod.Command));
});

Let's talk this out. On the very first line I create a local variable, keyCode which is assigned the value of combo. The idea is that we step through each modifier and clear the bit flag from the keyCode until we get down to the bottom of the function. After getting rid of all the Mod flags, you're left with the keyCode which represents the letter or number that was pressed.

So after I have my keyCode variable, I perform this check:

if ((combo & Mod.Command) === Mod.Command) {
  // ...
}

Hooray! We've encountered our first bitwise operator, the AND. So what is this line doing? Well, according to the MDN documentation for the AND operator:

The bitwise AND operator (&) returns a 1 in each bit position for which the corresponding bits of both operands are 1s.

Let's use the example that MDN provides to explain this:

const a = 5;        // 00000000000000000000000000000101
const b = 3;        // 00000000000000000000000000000011
//                 Only bit that's a 1 in both values ^

console.log(a & b); // 00000000000000000000000000000001
//                    Which is why it only logs out 1 ^

And applying this to the areKeysDown function:

const combo = Mod.Command;           // 00000000000000000000000000000010
const compare = combo & Mod.Command; // 00000000000000000000000000000010

// This translates to:
// if 00000000000000000000000000000010
// == 00000000000000000000000000000010
// Which is true! So we know the Command modifier was pressed.
if (compare === Mod.Command) {
  // ...
}

So if the Command modifier is down, that condition is true. Let's keep going:

if ((combo & Mod.Command) === Mod.Command) {
  if (!event.metaKey) {
    return false;
  } else {
    keyCode = keyCode & ~Mod.Command;
  }
}

The if (!event.metaKey) statement is pretty obvious. If we're checking for the Command modifier, and it isn't pressed, the function returns false. The else is where things get spicy. I'm using the AND plus the NOT operator to clear Mod.Command from keyCode.

Here's what MDN has to say about NOT:

The bitwise NOT operator (~) inverts the bits of its operand.

Dafuq does that mean? Well for a 32-bit integer, it turns all the 1s to 0s and 0s to 1s.

const combo = Mod.Command;  // 00000000000000000000000000000010
const not   = ~Mod.Command; // 11111111111111111111111111111101
//                                                           ^ Boop

But hang on a second, wouldn't that mean the value would be huge after we NOT it? You are correct, but don't forget that this is a two-step operation:

const combo = Mod.Command;              // 00000000000000000000000000000010
const not   = ~Mod.Command;             // 11111111111111111111111111111101
console.log((combo & not).toString(2)); // 00000000000000000000000000000000
//                 ^ Don't forget about the AND!

Remember the definition for bitwise AND?:

The bitwise AND operator (&) returns a 1 in each bit position for which the corresponding bits of both operands are 1s.

There are no positions in the combo and not variables in which both bits are 1, so the output of that is 0. The final else statement enforces the requirement that the function return true if and only if the exact combo specified is pressed. If you're checking if Mod.Control is down and the user is pressing Control + Command, the function returns false.

function areKeysDown(event: KeyboardEvent, combo: number): boolean {
  let keyCode = combo;

  if ((combo & Mod.Command) === Mod.Command) {
    // ...
  } else {
    // The Command key is depressed, but we're explicitly checking
    // if it is _not_ down, so we return false:
    if (event.metaKey) {
      return false;
    }
  }

  // ... rest of Mod handlers ...

  // This will change when we add non-modifier keys:
  return keyCode === 0;
}

So this is all well and good, but not very useful. How do you check for multiple modifiers? I'm glad you asked!

Handling Multiple Modifiers

Let's add a second modifier if statement to our areKeysDown function that checks for Control:

function areKeysDown(event: KeyboardEvent, combo: number): boolean {
  let keyCode = combo;

  if ((combo & Mod.Command) === Mod.Command) {
    if (!event.metaKey) {
      return false;
    } else {
      keyCode = keyCode & ~Mod.Command;
    }
  } else {
    if (event.metaKey) {
      return false;
    }
  }

  if ((combo & Mod.Control) === Mod.Control) {
    if (!event.ctrlKey) {
      return false;
    } else {
      keyCode = keyCode & ~Mod.Control;
    }
  } else {
    if (event.ctrlKey) {
      return false;
    }
  }

  // ... rest of Mod handlers ...

  // This will change when we add non-modifier keys:
  return keyCode === 0;
}

The only difference from the Mod.Command statement is we're checking for Mod.Control and event.ctrlKey.

In order to check for multiple modifiers, we're going to bring out the final bitwise operator: OR. So our function call looks like this:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  // Logs out true if event.metaKey = true, event.ctrlKey = true,
  // other modifiers are false, and no other keys are pressed:
  console.log(areKeysDown(event, Mod.Command | Mod.Control));
});

Here's the definition of OR from the MDN documentation:

The bitwise OR operator (|) returns a 1 in each bit position for which the corresponding bits of either or both operands are 1s.

Let's use the example that MDN provides to explain this:

const a = 5;        // 00000000000000000000000000000101
const b = 3;        // 00000000000000000000000000000011
//                          These are either 1 or 0 ^^^

console.log(a | b); // 00000000000000000000000000000111
//                     So it turns them all into 1s ^^^

And applying this to the areKeysDown function:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  // Logs out true if event.metaKey = true, event.ctrlKey = true,
  // other modifiers are false, and no other keys are pressed:
  console.log(areKeysDown(event, Mod.Command | Mod.Control));
});

function areKeysDown(event: KeyboardEvent, combo: number): boolean {
  // Mod.Command  = 00000000000000000000000000000010
  // Mod.Control  = 00000000000000000000000000000100
  // combo        = 00000000000000000000000000000110
  //     Set bits with a 1 in either number to 1 ^^

  if ((combo & Mod.Command) === Mod.Command) {
    // Mod.Command         = 00000000000000000000000000000010
    // combo               = 00000000000000000000000000000110
    // combo & Mod.Command = 00000000000000000000000000000010
    //    Only position where Mod.Command _and_ combo is 1 ^

    // Does 00000000000000000000000000000010
    //   == 00000000000000000000000000000010?
    // Yep! So we know that Mod.Command is in the combo argument
  }
}

So what about that keyCode variable? Well, here's how that works:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  console.log(areKeysDown(event, Mod.Command | Mod.Control));
});

function areKeysDown(event: KeyboardEvent, combo: number): boolean {
  // Mod.Command  = 00000000000000000000000000000010
  // Mod.Control  = 00000000000000000000000000000100
  // combo        = 00000000000000000000000000000110
  //     Set bits with a 1 in either number to 1 ^^

  let keyCode = combo;

  if ((combo & Mod.Command) === Mod.Command) {
    // See previous example (we know it's true)...

    if (!event.metaKey) {
      return false;
    } else {
      // ~Mod.Command                    = 11111111111111111111111111111101
      // keyCode is still combo which    = 00000000000000000000000000000110
      keyCode = keyCode & ~Mod.Command; // 00000000000000000000000000000100
      //              Only bit position in both numbers with value of 1 ^
    }
  } else {
    // ...
  }

  if ((combo & Mod.Control) === Mod.Control) {
    // Mod.Control         = 00000000000000000000000000000100
    // combo               = 00000000000000000000000000000110
    // combo & Mod.Control = 00000000000000000000000000000100
    //       Only position where command _and_ combo is 1 ^

    // Does 00000000000000000000000000000100
    //   == 00000000000000000000000000000100?
    // Yep! So we know that Mod.Control is in the combo argument

    if (!event.ctrlKey) {
      return false;
    } else {
      // ~Mod.Control                    = 11111111111111111111111111111011
      // keyCode = combo w/o Mod.Command = 00000000000000000000000000000100
      keyCode = keyCode & ~Mod.Control; // 00000000000000000000000000000000
    }
  }

  // The keyCode is now 0, so we return true!
  return keyCode === 0;
}

So that's how we handle multiple modifiers. But that still doesn't get us all the functionality we need. What about letters, numbers, arrow keys, etc.? Let's cover that next.

Checking for Non-Modifier Keys

This is where things start to get a little hairy. The KeyboardEvent has a keyCode property that has been deprecated for a while and is no longer recommended.

Per the MDN documentation on keyCode:

The deprecated KeyboardEvent.keyCode read-only property represents a system and implementation dependent numerical code identifying the unmodified value of the pressed key.

It's handy for bit flags because it's a number, so the letter A has a keyCode of 65. You could check if Command + A is pressed by doing this:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  // Logs out 65 if "A" is pressed:
  console.log(event.keyCode);

  // Logs out true if event.metaKey = true and "A" is pressed
  // (and only if those keys are pressed):
  console.log(areKeysDown(event, Mod.Command | 65));
});

I imagine the primary reason for the deprecation was due to issues with international keyboard layouts. MDN recommends you use KeyboardEvent.code instead, which is fine, but it just requires a little extra work.

I created an enum with each non-modifier key to make the areKeysDown function more readable. If the keyCode property wasn't deprecated, that would look something like this:

enum Key {
  LetterA = 65,
  LetterB = 66,
  LetterC = 67,
  LetterD = 68,
  // ... and so on ...
}

That would allow us to do the following:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  console.log(areKeysDown(event, Mod.Command | Key.LetterA));
});

function areKeysDown(event: KeyboardEvent, combo: number): boolean {
  let keyCode = combo;

  if ((combo & Mod.Command) === Mod.Command) {
    // ...
    keyCode = keyCode & ~Mod.Command; // keyCode = 65 = Key.LetterA
    // ...
  }

  // event.keyCode = 65 = Key.LetterA = keyCode, so we return true:
  return event.keyCode === keyCode;
}

But since keyCode is deprecated, let's do this instead:

enum Key {
  LetterA = 1,
  LetterB,
  LetterC,
  LetterD,
  // ... and so on ...
}

I started at 1 instead of 0, because 0 would be the result of clearing all the modifiers. There would be no way to differentiate between "only modifiers are pressed" and "some modifiers plus the letter A was pressed".

Now we just need a table to map the Key enum to the corresponding KeyboardEvent.code values:

const codeByKeyTable: Record<Key, string> = {
  [Key.LetterA]: "KeyA",
  [Key.LetterB]: "KeyB",
  [Key.LetterC]: "KeyC",
  [Key.LetterD]: "KeyD",
  // ... and so on ...
}

That "KeyA", "KeyB", etc. is the value of KeyboardEvent.code for each key:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  // Logs out "KeyA" when you press the letter "A":
  console.log(event.code);
});
Note
If you're wondering why I used KeyboardEvent.code instead of KeyboardEvent.key, it's because KeyboardEvent.code returns a value that isn't altered by keyboard layout or the state of the modifier keys. KeyboardEvent.key returns a different value depending on the state of the modifier keys, which would pretty much break all our code.

Let's tweak the previous example to accommodate for the codeByKeyTable lookup table:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  console.log(areKeysDown(event, Mod.Command | Key.LetterA));
});

function areKeysDown(event: KeyboardEvent, combo: number): boolean {
  let keyCode = combo;

  if ((combo & Mod.Command) === Mod.Command) {
    // ...
    keyCode = keyCode & ~Mod.Command;
    // ...
  }

  // keyCode = 1, which is "KeyA" in the lookup table:
  const codeForKeyCode = codeByKeyTable[keyCode];

  // event.code = "KeyA", codeForKeyCode = "KeyA", so return true:
  return event.code === codeForKeyCode;
}

There is, however, a problem that I alluded to earlier in this post. The function will return the wrong value given the current Mod enum values because some of the Key enum values are the same:

enum Mod {
  Command = 1 << 1, // 2  === Key.LetterB
  Control = 1 << 2, // 4  === Key.LetterD
  Option  = 1 << 3, // 8  === Key.LetterH
  Shift   = 1 << 4, // 16 === Key.LetterP
}

So you lose the whole "impossible collision" advantage of bit flags. Fortunately, the fix is super simple. Just increase the number of bits shifted so the base-10 equivalent exceeds the maximum value of the Key enum. You also need to make sure the gap between the Mod enum values is wide enough to accommodate the amount of entries in the Key enum. In true engineering overkill fashion, I opted for the following values:

enum Mod {
  Command = 1 << 12, // 4096 in base-10
  Control = 1 << 16, // 65536 in base-10
  Option  = 1 << 20, // 1048576 in base-10
  Shift   = 1 << 24, // 16777216 in base-10
}

That's a big enough starting point and a more than big enough gap to avoid any collisions. If you were to OR all those together, you end up with:

console.log(
  Mod.Command | Mod.Control | Mod.Option | Mod.Shift
);
// base-2  : 00000001000100010001000000000000
// base-10 : 17895424

Which is still plenty of wiggle room from the maximum 32-bit integer value.

I'm not quite done yet, though. I added a few extra features to really give this library the old razzle-dazzle. Strap in!

The Old Razzle Dazzle

There are circumstances in which I need to check for multiple possible key combinations because I want them to map to the same operation. So I did a little refactoring. I renamed areKeysDown to isComboDown and changed the areKeysDown function to the following:

function areKeysDown(event: KeyboardEvent, ...combos: number[]): boolean {
  for (const combo of combos) {
    if (isComboDown(event, combo)) {
      return true;
    }
  }

  return false;
}

function isComboDown(event: KeyboardEvent, combo: number): boolean {
  // Same implementation as the areKeysDown function from prior examples.
}

I'm treating the combos as an "or" condition, which is why I'm returning true as soon as one of the combos is hit.

I'm using JavaScript's spread syntax, so I can pass a variable amount of key combinations in to the function. Now you can call the function like this:

// Still works as expected the old way:
areKeysDown(event, Mod.Shift | Key.LetterB);

// I can also pass as many additional key combinations as I want:
areKeysDown(
  event,
  Mod.Shift | Key.LetterB,
  Mod.Shift | Key.LetterC,
  Mod.Shift | Key.LetterD,
);

I know what some of you might be thinking:

Why not just make it an array?

Because 80% of the time, I'm only checking for a single key combination.

Why not just make it an array or a single value?

That's a bunch of extra work for zero additional clarity.

I don't agree.

Well, go start your own blog.

But I digress. I added another handy little tidbit to this library. In most cases, you want to handle several keyboard shortcuts in a single event listener. We can leverage JavaScript's square bracket notation to really gussy things up. I created a listenForKeys function that lets you define an object with the key combination OR expression as the key and handler function as the value:

export function listenForKeys(
  event: KeyboardEvent,
  handlers: Record<number, () => void>,
): void {
  for (const [combo, func] of Object.entries(handlers)) {
    // The "+" in front of "combo" converts the value to a number.
    // If we didn't do this, TypeScript would complain that combo is a string:
    if (areKeysDown(event, +combo)) {
      func();
    }
  }
}

In use, that looks like this:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  listenForKeys(event, {
    [Mod.Command | Key.LetterA]() {
      // Do something when Command + A is pressed.
    },

    [Mod.Command | Key.LetterB]() {
      // Do something when Command + B is pressed.
    },

    [Mod.Control | Mod.Shift | Key.LetterC]() {
      // Do something when Control + Shift + C is pressed.
    },
  });
});

If that looks super weird to you, after you evaluate the | and convert it to the decimal representation, it ends up looking like this:

document.addEventListener("keydown", (event: KeyboardEvent) => {
  listenForKeys(event, {
    4097() {
      // Do something when Command + A is pressed.
    },

    4098() {
      // Do something when Command + B is pressed.
    },

    16842755() {
      // Do something when Control + Shift + C is pressed.
    }
  });
});

Pretty cool, eh? Maybe not, but it's better than a slap in the chest with a wet fish.

Whelp, that's all I've got. Thanks for following along on my journey to the weird and wacky world of bits. Hopefully you learned a bit (heh) and have a better understanding of binary numbers, bit flags, bit shifting, and bitwise operators. I also hope I didn't put Matt to sleep. Sayonara!