function UTF8needed(s) {
 var uc;
 for(var i = 0;i < s.length; ++i) {
  uc = s.charCodeAt(i);
  if (uc==0x9d || uc>0xff) return true;
 }
 return false;
}

function str2UTF8str(s) {
 var t,uc,res='';
 for(var i = 0; i < s.length; ++i) {
  uc = s.charCodeAt(i);
  if ((t=nbits(uc)) < 8) 
     res += String.fromCharCode(uc);
  else
   {t=Math.floor((t+3)/5);
    res += String.fromCharCode((-1<<(8-t)&0xff)|(uc>>(6*--t)));
    while (t) res += String.fromCharCode(0x80|(uc>>(6*--t))&0x3f);
   }
 }
 return res;
}

function UTF8str2str(s) {
 var t,ca,cc,j=0,res="";
 while(j < s.length) {
  cc = ca = s.charCodeAt(j++);
  if (ca > 0xff) {alert("not an UTF8 string!"); return null;}
  if (ca & 0x80) {
   cc &= 0x7f; t=0x40;
   if ((cc & t) != t) {alert("Not an UTF8 string (first)!"); return null;}
   while (cc & t && j < s.length) {
    if (((ca=s.charCodeAt(j++)) & 0xc0) != 0x80) {alert("Not an UTF8 string (middle)!"); return null;}
    cc = ((cc & ~t) << 6)|(ca & 0x3f); t <<= 5;
   }
   if (cc & t) {alert("Not an UTF8 string (end)!"); return null;}
  }
  res += String.fromCharCode(cc);
 }
 return res;
}

function UTF8encode(s) {
 if(UTF8needed(s)) return String.fromCharCode(0x9d)+str2UTF8str(s);
 return s;
}

function UTF8decode(s) {
 if (s.charCodeAt(0)==0x9d) return UTF8str2str(s.substring(1));
 return s;
}
