docs: function(str) {
      if(typeof(str)=='undefined') return undefined;
      if(typeof(str)=='undefined') return undefined;
  
      var surrogate_1st = 0;
      var unicode_codes = [];
      for (var i = 0; i < str.length; ++i) {
  	var utf16_code = str.charCodeAt(i);
  	if (surrogate_1st != 0) {
  	    if (utf16_code >= 0xdc00 && utf16_code <= 0xdfff) {
  		var surrogate_2nd = utf16_code;
  		var unicode_code = (surrogate_1st - 0xd800) * (1 << 10) + (1 << 16) +
                      (surrogate_2nd - 0xdc00);
  		unicode_codes.push(unicode_code);
  	    }
  	    surrogate_1st = 0;
  	}
  	else if (utf16_code >= 0xd800 && utf16_code <= 0xdbff) {
  	    surrogate_1st = utf16_code;
  	}
  	else {
  	    unicode_codes.push(utf16_code);
  	}
      }
  
      var utf8_bytes = "";
      var i, unicode_code;
      for(i=0; i<unicode_codes.length; ++i) {
  	unicode_code = unicode_codes[i];
  	if (unicode_code < 0x80) {  // 1-byte
  	    utf8_bytes += String.fromCharCode(unicode_code);
  	}
  	else if (unicode_code < (1 << 11)) {  // 2-byte
  	    utf8_bytes += String.fromCharCode((unicode_code >>> 6) | 0xC0);
  	    utf8_bytes += String.fromCharCode((unicode_code & 0x3F) | 0x80);
  	}
  	else if (unicode_code < (1 << 16)) {  // 3-byte
  	    utf8_bytes += String.fromCharCode((unicode_code >>> 12) | 0xE0);
  	    utf8_bytes += String.fromCharCode(((unicode_code >> 6) & 0x3f) | 0x80);
  	    utf8_bytes += String.fromCharCode((unicode_code & 0x3F) | 0x80);
  	}
  	else if (unicode_code < (1 << 21)) {  // 4-byte
  	    utf8_bytes += String.fromCharCode((unicode_code >>> 18) | 0xF0);
  	    utf8_bytes += String.fromCharCode(((unicode_code >> 12) & 0x3F) | 0x80);
  	    utf8_bytes += String.fromCharCode(((unicode_code >> 6) & 0x3F) | 0x80);
  	    utf8_bytes += String.fromCharCode((unicode_code & 0x3F) | 0x80);
  	}
      }
      return utf8_bytes;
  }