docs: function signature(obj, sig, classes) {
          if (typeof obj === 'string') {
              obj = list[obj];
          }
          var r;
          var id = obj.id;
          var type = obj.type;
  
          // oh, it's a constructor?
          if (obj.id.indexOf(".new") >= 0) {
              r = '<span class="constructorIdentifier">new </span>'; // if users want to hide or style this via CSS, they can
              r += '<span id="' + id + '" class="member-name ' + (classes || []).join(' ') + '">';
              r += obj.id.substring(0, obj.id.indexOf(".new")) + '</span>';
          }
          else if (type != "callback" && type != "event") { // method or property
              var className = obj.id.substring(0, obj.id.lastIndexOf("."));
              var memberName = obj.id.substring(obj.id.lastIndexOf(".") + 1);
  
              r = '<span id="' + id + '" class="member-name ' + (classes || []).join(' ') + '">'; // users can hide object name via CSS
              r += '<span class="sigClassName">' + className + '.</span><span class="sigMemberName">' + memberName + '</span></span>';
          }
          else if (type == "event") { // bah, an event !
              var parts = obj.id.split("@");
  
              var className = parts[0];
              var eventName = parts[1];
  
              r = '<span class="eventObjName">' + className + '</span><span class="eventListenerStart">.on("</span>';
              r += '<span id="' + id.replace("@", ".event.") + '" class="member-name eventMember ' + (classes || []).join(' ') + '">' + eventName + '</span>';
          }
          else {
              r = obj.id;
          }
  
          // for attributes
          if (obj.defaults !== undefined) {
            r += ' = ' + obj.defaults; 
          }
          else if (sig.arguments) {
              if (type != 'event') r += '(<span class="sigArgList">';
              else r += '<span class="eventListenerClose">", </span><span class="eventFunctionOpen">function(</span>';
  
              sig.arguments.forEach(function (sigArg, sigIdx, sigArgs) {
                  var skip_first, a, value;
                  // skip the first bound argument for prototype methods
                  skip_first = obj.bound && obj.id.indexOf('#') >= 0;
  
                  // turns the argument types into links
                  if (obj.arguments && obj.arguments[sigIdx]) {
                      var argLink = "";
  
                      var currArg = obj.arguments[sigIdx];
  
                      if (currArg.name != sigArg.name) // in the event of, say, multiple signatures
                      {
                          var s;
                          for (s = 0; s < obj.arguments.length; s++) {
                              if (obj.arguments[s].name == sigArg.name) {
                                  //console.log("swaped arg!");
                                  currArg = obj.arguments[s];
                                  break;
                              }
                          }
                          if (s == obj.arguments.length) {
                              //console.error("Couldn't find suitable argument replacement for " + currArg.name + " around " + obj.id);
                          }
                      }
  
                      currArg.types.forEach(function (currArgType, currIdx, currArgs) {
                          if (currArgType === "null") {
                            console.error("Error".red + ": could not determine the argument type for this argument!");
                            console.error(util.inspect(sigArgs));
                            console.error(util.inspect(currArgs));
                          }
                          
                          argLink += link(currArgType, 'argument ' + (classes || []).join(' '));
  
                          if (currIdx < currArgs.length - 1) argLink += " | ";
                      });
                      a = argLink + " " + sigArg.name;
                  }
                  else a = sigArg.name;
  
                  // argument can be callback
                  if (sigArg.arguments) {
                      a = signature({
                          id: a,
                          type: "callback"
                      }, sigArg);
                  }
                  if (!sigIdx && skip_first) {
                      return; //a = '@' + a;
                  }
                  if (typeof sigArg.default_value !== 'undefined') {
                      // apply custom stringifier
                      value = JSON.stringify(sigArg.default_value, function (k, v) {
                          if (v instanceof RegExp) {
                              // FIXME: get rid of quotes, if possible
                              v = v.source;
                          }
                          else if (v === 'null') {
                              v = null;
                          }
                          return v;
                      });
                      a = a + ' = ' + value;
                  }
                  // compensate for possibly skipped first argument
                  if (sigIdx > (skip_first ? 1 : 0)) {
                      a = ', ' + a;
                  }
                  if (sigArg.ellipsis) {
                      a += '...';
                  }
                  if (sigArg.optional) {
                      a = '[' + a + ']';
                  }
                  r += a;
              });
              if (type != "event") r += '</span>)';
              else r += '<span class="eventFunctionClose">))</span>';
          }
  
          else if (type == "event" && !sig.arguments) r += '<span class="eventListenerClose">", </span><span class="eventFunctionOpen">function(</span><span class="eventFunctionClose">))</span>';
          else r += '<span class="emptyArgumentList">()</span>';
  
          return r;
      }