l33t Speak translator Please review the code quality of this l33t Speak translator. Improvement suggestions are welcome. Here it is - http://jsbin.com/IHoFuQE/1

(function() {
  "use strict";

  // http://en.wikipedia.org/wiki/Leet
  // http://www.catb.org/jargon/html/crackers.html

  var alphabets = { // common
    "a": "4",
    "b": "8",
    "e": "3",
    "f": "ph",
    "g": "6", // or 9
    "i": "1", // or |
    "o": "0",
    "s": "5",
    "t": "7" // or +
  },
    alphabets2 = { // less common
      "c": "(", // or k or |&lt; or /&lt;
      "d": "&lt;|",
      "h": "|-|",
      "k": "|&lt;", // or /&lt;
      "l": "|", // or 1
      "m": "|\\/|",
      "n": "|\\|",
      "p": "|2",
      "u": "|_|",
      "v": "/", // or \/
      "w": "//", // or \/\/
      "x": "&gt;&lt;",
      "y": "'/"
    };

  var words = {
    "am": "m",
    "are": "r",
    "at": "@",
    "thanks": "thx",
    "your": "ur",

    "cool": "kewl",
    "defeated": "pwned",
    "dude": "d00d",
    "fear": "ph33r", // or ph34r
    "fool": "f00",
    "freak": "phreak",
    "hacker": "h4x0r",
    "lamer": "l4m3r",
    "mad": "m4d",
    "newbie": "n00b",
    "own": "pwn",
    "phone": "fone",
    "porn": "pr0n", // or n0rp
    "rocks": "roxxorz",
    "skill": "sk1llz",
    "sucks": "sux0r",
    "the": "t3h",
    "uber": "ub3r", // or |_|83r
    "yay": "w00t",
    "yo": "j0",
    "you": "j00" // or U
  };

  var elite = document.getElementById("elite"),
    leet = document.getElementById("leet"),
    randomcase = document.getElementById("randomcase"),
    adv = document.getElementById("adv"),
    btn = document.getElementById("btn");

  function changeLetters(text) { // change letters
    text = text || elite.value.toLowerCase();
    for (var i = 0; i &lt; text.length; i++) {
      var alphabet = adv.checked ? alphabets[text[i]] || alphabets2[text[i]] : alphabets[text[i]];
      if (alphabet) {
        text = text.replace(text[i], alphabet);
      }
    }
    return text;
  }

  function changeWords() { // change special words and return text
    return changeLetters().replace(
      /\w+/g,
      function(word) {
        return words[word] ? words[word] : word;
      }
    );
  }

  function randomizeCase() { // RANdOMiZE CAsE
    var text = changeWords();
    for (var i = 0; i &lt; text.length; i++) {
      if (Math.random() &gt; 0.5) {
        text = text.replace(text[i], text[i].toUpperCase());
      } // else keep lower case
    }
    return text;
  }

  (function() { // l33t the words object
    for (var word in words) {
      if (words.hasOwnProperty(word)) {
        words[changeLetters(word)] = words[word];
        delete words[word];
      }
    }
  }());

  function tol33t() {
    leet.value = randomcase.checked ? randomizeCase() : changeWords();
  }

  elite.addEventListener("input", tol33t);
  btn.addEventListener("click", tol33t);
}());
To start with, I'd like to give you credit for using "use strict" and limiting the scope of variables using functions.

Your tol33t() function is weird to me.  If the randomcase button is checked, you call randomizeCase() with no arguments.  What are you randomizing the case of?  It turns out that randomizeCase() calls changeWords(), so it turns out that the two code branches have something in common after all.  That wasn't apparent from looking at tol33t().

There should be clearer separation responsibility between the DOM-interaction and text-translation layers of code.  The text translation code should reside in pure functions (whose results depend solely on their parameters) that you could call from the JavaScript console or from a unit testing engine.  That separation of concerns could be accomplished with:

function onTranslateButtonClicked() {
    var l33tText = toL33t(elite.value, adv.checked);
    if (randomcase.checked) l33tText = randomizeCase(l33tText);
    leet.value = l33tText;
}


Your "l33t the words object" initialization routine lays a trap for anyone trying to  read the code.  When code in changeWords() refers to the words associative array, one would reasonably think that it's using the definition above (i.e., words = { "am": "m", … }) instead of the munged version (words = { "4m": "m" }).  Better to make that munging explicit when you define words (and do so by returning a copy rather than mutating the dictionary as you iterate):

var words = l33tKeys({
    "am": "m",
    …
});

function l33tKeys(dict) {
    var l33tDict = {};
    for (var word in dict) {
        l33tDict[changeWord(word)] = dict[word];
    }
    return l33tDict;
}


I would write changeLetters() as:

 // letterSubsts may be alphabet or alphabet2
function changeLetters(text, letterSubsts) {
    return text.toLowerCase().replace(/[a-z]/g, function(letter) {
        return letterSubsts[letter] || letter;
    });
}


… which is actually analogous to your implementation of changeWords().