/** # Quantumult X 资源解析器 (2020-05-22: 19:59 ) 本资源解析器作者: Shawn(请勿私聊问怎么用),有bug请反馈: @Shawn_KOP_bot 更新请关注tg频道: https://t.me/QuanX_API 主要功能: 将各类服务器订阅解析成 QuantumultX 格式引用(支持 V2RayN/SSR/SS/Trojan/QuanX(conf&list)/Surge(conf&list)格式),并提供 1⃣️ 中的可选参数; 附加功能: rewrite(重写) /filter(分流) 过滤, 可用于解决无法单独禁用远程引用中某(几)条 rewrite/hostname/filter, 以及直接导入 Surge 类型规则 list 的问题 0️⃣ 请在“订阅链接”后加入 "#" 后再加参数, 不同参数间请使用 "&" 来连接, 如: "https://mysub.com#in=香港+台湾&emoji=1&tfo=1" 1️⃣ "节点" 订阅--参数说明: - in, out, 分别为 保留/排除, 多参数用 "+" 连接(逻辑"或"), 逻辑"与"请用"."连接,可直接用中文, 空格用"%20"代替 (如 "in=香港.IPLC.04+台湾&out=香港%20BGP" ); - emoji=1,2 或 -1, 为添加/删除节点名中的 emoji 旗帜 (国行设备请用 emoji=2 ); - udp=1, tfo=1 参数开启 udp-relay 及 fast-open (默认关闭, 此参数对源类型为 QuanX/Surge 的链接无效); - rename 重命名, rename=旧名@新名, 以及 "前缀@", "@后缀", 用 "+" 连接, 如 "rename=香港@HK+[SS]@+@[1X]"; - cert=0,跳过证书验证(vmess/trojan),即强制"tls-verification=false"; - tls13=1, 开启 "tls13=true"(vmess/trojan), 请自行确认服务端是否支持; - sort=1 或 sort=-1, 排序参数,分别根据节点名 正序/逆序 排列; - info=1, 开启通知提示流量信息(前提:原订阅链接有返回该信息),默认关闭 2⃣️ "rewrite(重写)/filter(分流)" 引用--参数说明: - 参数为 "out=xxx", 多个参数用 "+" 连接; - 分流规则额外支持 "policy=xx" 参数, 可用于直接指定策略组,或者为 Surge 格式的 rule-set 生成策略组(默认"Shawn"策略组) ⚠️⚠️ 由于 rewrite/filter 的 UI 中暂时没有提供解析器开关,想使用的请自行去配置文件中的相关行,添加参数"opt-parser=true"以开启,如: https://Advertising.list#policy=MineGroup&out=aweme, tag=🚦去广告,update-interval=86400, opt-parser=true, enabled=true 3⃣️ 通用参数: ntf=1, 用于打开资源解析器的提示通知 (默认关闭), - rewrite/filter 类型则会强制在有 out 参数时开启通知提示被删除(禁用)的内容,以防止规则误删除 */ /** * 使用说明, 0️⃣ 在QuantumultX 配置文件中[general] 部分,加入 resource_parser_url=https://raw.githubusercontent.com/KOP-XIAO/QuantumultX/master/Scripts/resource-parser.js ⚠️⚠️如提示"没有自定义解析器",请长按右下角图标后点击左侧刷新按钮,更新资源,后台退出 app,直到出现解析器说明 1️⃣ 假设原始订阅连接为: https://raw.githubusercontent.com/crossutility/Quantumult-X/master/server-complete.txt , 2️⃣ 假设你想要保留的参数为 in=tls+ss, 想要过滤的参数为 out=http+2, 请注意下面订阅链接后一定要加 ”#“ 符号 3️⃣ 则填入 Quanx 节点引用的的总链接为 https://raw.githubusercontent.com/crossutility/Quantumult-X/master/server-complete.txt#in=tls+ss&out=http+2 4️⃣ 填入上述链接, 并打开的资源解析器开关 ------------------------------ ⚠️⚠️ 由于 rewrite/filter 的 UI 中暂时没有提供解析器开关,想使用的请自行去配置文件中的相关行,添加参数"opt-parser=true"以开启,如: https://Advertising.list#policy=Shawn&out=aweme, tag=🚦去广告,update-interval=86400, opt-parser=true, enabled=true */ var content0=$resource.content; var para=decodeURIComponent($resource.link); var type0=Type_Check(content0); var Pin0=para.indexOf("in=")!=-1? para.split("#")[1].split("in=")[1].split("&")[0].split("+"):null; var Pout0=para.indexOf("out=")!=-1? para.split("#")[1].split("out=")[1].split("&")[0].split("+"):null; var Pemoji=para.indexOf("emoji=")!=-1? para.split("#")[1].split("emoji=")[1].split("&")[0].split("+"):null; var Pudp0=para.indexOf("udp=")!=-1? para.split("#")[1].split("udp=")[1].split("&")[0].split("+"):0; var Ptfo0=para.indexOf("tfo=")!=-1? para.split("#")[1].split("tfo=")[1].split("&")[0].split("+"):0; var Pinfo=para.indexOf("info=")!=-1? para.split("#")[1].split("info=")[1].split("&")[0].split("+"):0; var Prname=para.indexOf("rename=")!=-1? para.split("#")[1].split("rename=")[1].split("&")[0].split("+"):null; var Ppolicy=para.indexOf("policy=")!=-1? para.split("#")[1].split("policy=")[1].split("&")[0].split("+"):"Shawn"; var Pcert0=para.indexOf("cert=")!=-1? para.split("#")[1].split("cert=")[1].split("&")[0].split("+"):1; var Psort0=para.indexOf("sort=")!=-1? para.split("#")[1].split("sort=")[1].split("&")[0].split("+"):0; var PTls13=para.indexOf("tls13=")!=-1? para.split("#")[1].split("tls13=")[1].split("&")[0].split("+"):0; var Pntf0= para.indexOf("ntf=")!=-1? para.split("#")[1].split("ntf=")[1].split("&")[0].split("+"):0; //$notify(type0) //响应头流量处理部分 var subinfo=$resource.info; var subtag=$resource.tag; if(Pinfo==1 && subinfo){ var sinfo=subinfo.replace(/ /g,"").toLowerCase(); var total="总流量: "+(parseFloat(sinfo.split("total=")[1].split(",")[0])/(1024**3)).toFixed(2)+"GB"; var usd="已用流量: "+((parseFloat(sinfo.split("upload=")[1].split(",")[0])+parseFloat(sinfo.split("download=")[1].split(",")[0]))/(1024**3)).toFixed(2)+"GB" var left="剩余流量: "+((parseFloat(sinfo.split("total=")[1].split(",")[0])/(1024**3))-((parseFloat(sinfo.split("upload=")[1].split(",")[0])+parseFloat(sinfo.split("download=")[1].split(",")[0]))/(1024**3))).toFixed(2)+"GB" if(sinfo.indexOf("expire=")!=-1){ var epr= new Date(parseFloat(sinfo.split("expire=")[1].split(",")[0])*1000); var year=epr.getFullYear(); // 获取完整的年份(4位,1970) var mth=epr.getMonth()+1 < 10 ? '0'+(epr.getMonth()+1):(epr.getMonth()+1); // 获取月份(0-11,0代表1月,用的时候记得加上1) var day=epr.getDate()<10 ? "0"+(epr.getDate()):epr.getDate(); epr="过期时间: "+year+"-"+mth+"-"+day } else{ epr="订阅链接: "+para.split("#")[0] //没过期时间的显示订阅链接 } var message=total+"\n"+usd+", "+left; $notify("流量信息: "+subtag, epr, message) } if(type0=="Subs-B64Encode"){ total=SubsEd2QX(content0,Pudp0,Ptfo0,Pcert0,PTls13); flag=1; }else if(type0=="Subs"){ total=Subs2QX(content0,Pudp0,Ptfo0,Pcert0,PTls13); flag=1; }else if(type0=="QuanX"){ total=isQuanX(content0); flag=1; }else if(type0=="Surge"){ total=Surge2QX(content0); flag=1; }else if(type0=="rewrite"){ flag=2; content0=content0.split("\n"); total=Rewrite_Filter(content0,Pout0); }else if(type0=="Rule"){ flag=3; total=content0.split("\n"); total=Rule_Handle(total,Pout0); }else if(content0.trim()==""){ $notify("‼️链接內容为空","⁉️请自行检查原始链接以及过滤参数",para); flag=0; $done({content : ""}) }else { $notify("😭 太难写了", "👻 本解析器 暂未支持/未能识别 该订阅格式", "☠️ 已尝试直接导入Quantumult X"); $done({content : content0}); flag=-1; } if(flag==3){ $done({content : total.join("\n")}); }else if(flag==2){ $done({content:total.join("\n")}); }else if(flag==1){ if(Pin0||Pout0){ if(Pntf0!=0){ $notify("👥 开始转换节点订阅:","🐶 您已添加节点筛选参数,如下","👍️ 保留的关键字:"+Pin0+"\n👎️ 排除的关键字:"+Pout0);} total=filter(total,Pin0,Pout0) } else { if(Pntf0!=0){ $notify("🐷 开始转换节点订阅","🐼️ 如需筛选节点请使用in/out及其他参数,可参考此示范:","👉 https://t.me/QuanXNews/110");} } if(Pemoji){ if(Pntf0!=0){ $notify("🏳️‍🌈 开始更改旗帜 emoji","清除emoji请用参数 -1, 国行设备添加emoji请使用参数 2","你当前所用的参数为 emoji="+Pemoji)}; total=emoji_handle(total,Pemoji); } if(Prname){ if(Pntf0!=0){ $notify("🏳️‍🌈 开始节点重命名","格式为 \"旧名字@新名字\"","你当前所用的参数为"+Prname);} var Prn=Prname; total=total.map(Rename); } if(Psort0==1 || Psort0==-1){ total=QXSort(total,Psort0); } <<<<<<< HEAD total=TagCheck_QX(total) if(total.length==0){ ======= total=TagCheck_QX(total); if(total.length==0){ >>>>>>> 2fc50cc2f6ffcbbbf80aaff57172fb8feecfe39a $notify("‼️无有效节点","⁉️请自行检查原始链接以及过滤参数",para) }; $done({content : total.join("\n")}); } //判断订阅类型 function Type_Check(subs){ var type="" var RuleK=["host","domain","ip-cidr","geoip","user-agent","ip6-cidr"]; var QuanXK=["shadowsocks=","trojan=","vmess=","http="]; var SurgeK=["=ss","=vmess","=trojan","=http","=custom"]; var SubK=["dm1lc3M6Ly","c3NyOi8v","dHJvamFu","c3M6Ly"]; var SubK2=["ss://","vmess://","ssr://","trojan://"]; var subi=subs.replace(/ /g,"") const RuleCheck = (item) => subs.toLowerCase().indexOf(item)!=-1; const QuanXCheck = (item) => subi.toLowerCase().indexOf(item)!=-1; const SurgeCheck = (item) => subi.toLowerCase().indexOf(item)!=-1; const SubCheck = (item) => subs.indexOf(item)!=-1; var subsn=subs.split("\n") if(SubK.some(SubCheck)){ //b64加密的订阅类型 type="Subs-B64Encode" } else if(subsn.length>1 && SubK2.some(SubCheck)){ //未b64加密的多行URI 组合订阅 type="Subs" } else if(subi.indexOf("tag=")!=-1 && QuanXK.some(QuanXCheck)){ type="QuanX" } else if(subs.indexOf("[Proxy]")!=-1){ type="Surge"; } else if(SurgeK.some(SurgeCheck)){ type="Surge" } else if(subs.indexOf("hostname")!=-1){ type="rewrite" } else if(RuleK.some(RuleCheck)){ type="Rule"; } return type } function Trim(item){ return item.trim() } //删除 rewrite 引用中的某部分 function Rewrite_Filter(subs,Pout){ cnt=subs; nlist=[]; drewrite=[]; if(Pout!="" && Pout){ Pout=Pout.map(Trim); for(var i=0;i cc.indexOf(item)!=-1; if(Pout.some(exclude)){ if(cc.indexOf("hostname")!=-1 && cc.indexOf("=")!=-1){ //hostname 部分 nname=[];//保留项 dname=[];//删除项目 hname=cc.split("=")[1].split(","); for(var j=0;j dd.indexOf(item)!=-1; if(!Pout.some(excludehn)){ nname.push(hname[j]) }else{dname.push(hname[j])} } //for j hname="hostname="+nname.join(", "); //console.log(hname) nlist.push(hname) if(dname.length>0){$notify("🤖 您添加的[rewrite]过滤关键词为:"+Pout0.join(", "),"☠️ 主机名 hostname 中已为您删除以下"+dname.length+"个匹配项",dname.join(",") )} } // if cc -hostname else{ drewrite.push(cc); nlist.push(cc.replace(/ url /g," - ")); } }else{ //if Pout.some nlist.push(cc) } //else } }//cnt for if(drewrite.length>0){$notify("🤖 您添加的[rewrite]过滤关键词为:"+Pout0.join(", "),"☠️ 复写 rewrite 中已为您禁用以下"+drewrite.length+"个匹配项",drewrite.join("\n") )}; return nlist }else { // Pout if //$notify("no filter at all") return cnt;} } //分流规则转换及过滤,可用于 surge 及 quanx 的 rule-list function Rule_Handle(subs,Pout){ cnt=subs //.split("\n"); out=Pout; //过滤参数 ply=Ppolicy; //策略组 var nlist=[] var RuleK=["//","#",";"]; if(Pout!="" && Pout!=null){ var dlist=[]; for(var i=0;icc.indexOf(item)!=-1; const RuleCheck = (item) => cc.indexOf(item)!=-1; //无视注释行 if(Pout.some(exclude) && !RuleK.some(RuleCheck)){ dlist.push(cnt[i]) } else if(!RuleK.some(RuleCheck) && cc){ //if Pout.some, 不操作注释项 dd=Rule_Policy(cc); nlist.push(dd); } }//for cnt var no=dlist.length if(dlist.length>0){$notify("🤖 您添加的分流 [filter] 过滤关键词为:"+out,"☠️ 已为您删除以下 "+no+"条匹配规则", dlist.join("\n")) }else{$notify("🤖 您添加的[filter]过滤关键词为:"+out,"☠️ 没有发现任何匹配项",dlist)} return nlist } else{return cnt.map(Rule_Policy)}//if Pout } function Rule_Policy(content){ //增加、替换 policy var cnt=content.split(","); var RuleK=["//","#",";"]; var RuleK1=["host","domain","ip-cidr","geoip","user-agent","ip6-cidr"]; const RuleCheck = (item) => cnt[0].toLowerCase().indexOf(item)!=-1; //无视注释行 const RuleCheck1 = (item) => cnt[0].toLowerCase().indexOf(item)!=-1; //无视 quanx 不支持的规则类别 if(RuleK1.some(RuleCheck1)){ if(cnt.length==3 && cnt.indexOf("no-resolve")==-1){ ply0 = Ppolicy!="Shawn"? Ppolicy:cnt[2] nn=cnt[0]+", "+cnt[1]+", "+ply0 } else if(cnt.length==2){ //Surge rule-set ply0 = Ppolicy!="Shawn"? Ppolicy:"Shawn" nn=cnt[0]+", "+cnt[1]+", "+ply0 }else if(cnt.length==3 && cnt[2].indexOf("no-resolve")!=-1){ ply0 = Ppolicy!="Shawn"? Ppolicy:"Shawn" nn=cnt[0]+", "+cnt[1]+", "+ply0+", "+cnt[2] }else if(cnt.length==4 && cnt[3].indexOf("no-resolve")!=-1){ ply0 = Ppolicy!="Shawn"? Ppolicy:cnt[2] nn=cnt[0]+", "+cnt[1]+", "+ply0+", "+cnt[3] }else if(!RuleK.some(RuleCheck)&& content){ $notify("未能解析其中部分规则",content); return "" }else{return ""} if(cnt[0].indexOf("URL-REGEX")!=-1 || cnt[0].indexOf("PROCESS")!=-1){ nn="" } else {nn=nn.replace("IP-CIDR6","ip6-cidr")} return nn } else{return ""}//if RuleK1 check } //混合订阅类型,用于整体进行了 base64 encode 后的类型 function SubsEd2QX(subs,Pudp,Ptfo,Pcert,Ptls13){ const $base64 = new Base64() var list0=$base64.decode(subs).split("\n"); var QuanXK=["shadowsocks=","trojan=","vmess=","http="]; var SurgeK=["=ss","=vmess","=trojan","=http","=custom"]; var QXlist=[]; var node="" for(i=0;i3){ var type=list0[i].split("://")[0].trim() var listi=list0[i].replace(/ /g,"") const QuanXCheck = (item) => listi.toLowerCase().indexOf(item)!=-1; const SurgeCheck = (item) => listi.toLowerCase().indexOf(item)!=-1; if(type=="vmess"){ node= V2QX(list0[i],Pudp,Ptfo,Pcert,Ptls13) }else if(type=="ssr"){ node= SSR2QX(list0[i],Pudp,Ptfo) }else if(type=="ss"){ node = SS2QX(list0[i],Pudp,Ptfo) }else if(type=="trojan"){ node = TJ2QX(list0[i],Pudp,Ptfo,Pcert,Ptls13) }else if(QuanXK.some(QuanXCheck)){ node = list0[i] }else if(SurgeK.some(SurgeCheck)){ node = Surge2QX(list0[i]) } //$notify("Final","results",node) QXlist.push(node) } } return QXlist } //混合订阅类型,用于未整体进行 base64 encode 的类型 function Subs2QX(subs,Pudp,Ptfo,Pcert,Ptls13){ //const $base64 = new Base64() var list0=subs.split("\n"); var QuanXK=["shadowsocks=","trojan=","vmess=","http="]; var SurgeK=["=ss","=vmess","=trojan","=http"]; var QXlist=[]; var node="" for(i=0;i listi.toLowerCase().indexOf(item)!=-1; const SurgeCheck = (item) => listi.toLowerCase().indexOf(item)!=-1; if(type=="vmess"){ node= V2QX(list0[i],Pudp,Ptfo,Pcert,Ptls13) }else if(type=="ssr"){ node= SSR2QX(list0[i],Pudp,Ptfo) }else if(type=="ss"){ node = SS2QX(list0[i],Pudp,Ptfo) }else if(type=="trojan"){ node = TJ2QX(list0[i],Pudp,Ptfo,Pcert,Ptls13) }else if(QuanXK.some(QuanXCheck)){ node = list0[i] }else if(SurgeK.some(SurgeCheck)){ node = Surge2QX(list0[i]) } QXlist.push(node) } return QXlist } // 检查节点名字(重复以及空名)等QuanX 不允许的情形 function TagCheck_QX(content){ var Olist=content var Nlist=[] var nmlist=[] for(i=0;i name.indexOf(item.toUpperCase()) != -1; // const exclude = (item) => name.indexOf(item.toUpperCase()) != -1; // if(Pin){ // if(Pin.some(include)&&Pout){ // if(!Pout.some(exclude)){ // NList.push(Servers[i]) // } // } else if(Pin.some(include)&&!Pout) {NList.push(Servers[i])} // } else{ // if(!Pout.some(exclude)){ // NList.push(Servers[i]) // } // } // } // } // return NList //} // 判断节点过滤的函数 function Scheck(content,param){ name=content.split("tag=")[1].toUpperCase() if(param){ var flag=0; for(i=0;i name.indexOf(item.toUpperCase()) !=-1; if(params.every(checkpara)){ flag=1 } }//for return flag }else { //if param return 2} } //节点过滤,使用+连接多个关键词(逻辑"或"):in 为保留,out 为排除, "与"逻辑请用符号"."连接 function filter(servers,Pin,Pout){ var Nlist=[]; for(var i=0;itag2? 1:-1 return res } //逆序 function ToTagR(elem1,elem2){ var tag1=emoji_del(elem1.split("tag")[1].split("=")[1].trim()) var tag2=emoji_del(elem2.split("tag")[1].split("=")[1].trim()) res = tag1>tag2? -1:1 return res } //节点重命名 function Rename(str){ var server=str; if(server.indexOf("tag=")!=-1){ hd=server.split("tag=")[0] name=server.split("tag=")[1] for(i=0;i> 2); out += base64EncodeChars.charAt((c1 & 0x3) << 4); out += "=="; break; } c2 = str.charCodeAt(i++); // 当最后剩余两个字节时 if(i == len){ out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); out += base64EncodeChars.charAt((c2 & 0xF) << 2); out += "="; break; } //当剩余字节数大于等于3时 c3 = str.charCodeAt(i++); out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); out += base64EncodeChars.charAt(c3 & 0x3F); } return out; } /** * Base64解码函数 * @param str * @returns {*} */ function base64decode(str){ var c1, c2, c3, c4; var i, len, out; len = str.length; i = 0; out = ""; while(i < len){ /* 得到第一个字符 c1 * 并过虑掉前后所有与Base64编码无关的字符 * */ do{ c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff]; }while(i < len && c1 == -1); // 如果已经到达字符串结尾,并最后还未得到有效的Base64编码字符就结尾循环 if(c1 == -1) break; /* 得到字符 c2 * 并过滤掉所有与Base64编码无关的字符 */ do{ c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff]; }while(i < len && c2 == -1); // 如果已经到达字符串结尾,并最后还未得到有效的Base64编码字符就结尾循环 if(c2 == -1) break; // 根据Base64编码的 c1 和 c2 解码得到一个编码前的字符 out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4)); /* 得到字符 c3 * 并过滤掉所有与Base64编码无关的字符 * 如果获取的 c3 是 '=' 字符则说明已经解码完成,返回解码得到的字符串 */ do{ c3 = str.charCodeAt(i++) & 0xff; if(c3 == 61) return out; c3 = base64DecodeChars[c3]; }while(i < len && c3 == -1); // 如果已经到达字符串结尾,并最后还未得到有效的Base64编码字符就结尾循环 if(c3 == -1) break; // 根据Base64编码的 c2 和 c3 解码得到一个编码前的字符 out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2)); /* 这一步就比较复杂了 * 先是尝试获取第四个Base64 编码的字符 c4 * 如果获取的 c4 是 '=' 字符则说明已经解码完成,返回解码得到的字符串 * */ do{ c4 = str.charCodeAt(i++) & 0xff; if(c4 == 61) return out; c4 = base64DecodeChars[c4]; }while(i < len && c4 == -1); // 如果已经到达字符串结尾,并最后还未得到有效的Base64编码字符就结尾循环 if(c4 == -1) break; // 根据Base64编码的 c3 和 c4 解码得到一个编码前的字符 out += String.fromCharCode(((c3 & 0x03) << 6) | c4); } return out; } /** * 把 unicode 码转换成 utf8 编码 * @param str * @returns {string} */ function unicodeToUtf8(str){ var out, i, len, c; out = ""; len = str.length; for(i = 0; i < len; i++){ c = str.charCodeAt(i); // 兼容 ASCII if((c >= 0x0001) && (c <= 0x007F)){ out += str.charAt(i); }else if(c > 0x07FF){ // 占三个字节的 utf8 out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F)); out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F)); out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); }else{ // 占两个字节的 utf8 out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F)); out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); } } return out; } /** * 把 utf8 编码转换成 unicode 码 * @param str * @returns {string} */ function utf8ToUnicode(str){ var out, i, len, c; var char2, char3; out = ""; len = str.length; i = 0; while(i < len){ c = str.charCodeAt(i++); switch(c >> 4){ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: // 0xxxxxxx ASCII 编码 out += str.charAt(i - 1); break; case 12: case 13: // 110x xxxx 10xx xxxx // 占两个字节的 utf8 char2 = str.charCodeAt(i++); out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); break; case 14: // 1110 xxxx 10xx xxxx 10xx xxxx // 占三个字节的 utf8 char2 = str.charCodeAt(i++); char3 = str.charCodeAt(i++); out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); break; } } return out; } /** * 转成 十六 进制编码 * @param str * @returns {string} * @constructor */ function CharToHex(str){ var out, i, len, c, h; out = ""; len = str.length; i = 0; while(i < len){ c = str.charCodeAt(i++); // 把数据转换成十六进制的字符串 h = c.toString(16); if(h.length < 2) h = "0" + h; out += "\\x" + h + " "; if(i > 0 && i % 8 == 0) out += "\r\n"; } return out; } this.encode=function(str){ // 普通 Base64 编码 return base64encode(unicodeToUtf8(str)); }; this.decode=function(str){ // 普通 Base64 编码 return utf8ToUnicode(base64decode(str)); }; // base64={ // encode:function(str){ // // 普通 Base64 编码 // return base64encode(unicodeToUtf8(str)); // }, // encodeUrl:function(str){ // // 使用 Base64 编码字符串 // return base64encode(unicodeToUtf8(str),1) // }, // decode:function(str){ // // 兼容的 Base64 解码 // return utf8ToUnicode(base64decode(str)); // }, // encodeToHex:function(str){ // // 普通 Base64 编码 以十六进制显示 // return CharToHex(base64encode(unicodeToUtf8(str))); // }, // encodeUrlToHex:function(str){ // // 使用 Base64 编码 url 以十六进制显示 // return CharToHex(base64encode(unicodeToUtf8(str),1)); // } // } };