diff --git a/Scripts/resource-parser.js b/Scripts/resource-parser.js deleted file mode 100644 index 19b3a97..0000000 --- a/Scripts/resource-parser.js +++ /dev/null @@ -1,1106 +0,0 @@ -/** -# 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)); -// } -// } -}; \ No newline at end of file