=n){M("invalid-input")}var g=v(t.charCodeAt(c++));if(g>=O||g>V((W-r)/d)){M("overflow")}r+=g*d;var p=u<=s?N:u>=s+G?G:u-s;if(gV(W/m)){M("overflow")}d*=m}var f=i.length+1;s=K(r-h,f,h==0);if(V(r/f)>W-o){M("overflow")}o+=V(r/f);r%=f;i.splice(r++,0,o)}return String.fromCodePoint.apply(String,i)},b=function e(t){var i=[];t=j(t);var n=t.length;var r=F;var o=0;var s=H;var a=true;var l=false;var c=undefined;try{for(var h=t[Symbol.iterator](),d;!(a=(d=h.next()).done);a=true){var u=d.value;if(u<128){i.push(Z(u))}}}catch(e){l=true;c=e}finally{try{if(!a&&h.return){h.return()}}finally{if(l){throw c}}}var g=i.length;var p=g;if(g){i.push(P)}while(p=r&&bV((W-o)/y)){M("overflow")}o+=(m-r)*y;r=m;var w=true;var S=false;var x=undefined;try{for(var k=t[Symbol.iterator](),E;!(w=(E=k.next()).done);w=true){var R=E.value;if(RW){M("overflow")}if(R==r){var T=o;for(var _=O;;_+=O){var $=_<=s?N:_>=s+G?G:_-s;if(T<$){break}var L=T-$;var B=O-$;i.push(Z(D($+L%B,0)));T=V(L/B)}i.push(Z(D(T,0)));s=K(o,y,p==g);o=0;++p}}}catch(e){S=true;x=e}finally{try{if(!w&&k.return){k.return()}}finally{if(S){throw x}}}++o;++r}return i.join("")},w,S,x={version:"2.1.0",ucs2:{decode:j,encode:function e(t){return String.fromCodePoint.apply(String,i(t))}},decode:A,encode:b,toASCII:function e(t){return C(t,function(e){return h.test(e)?"xn--"+b(e):e})},toUnicode:function e(t){return C(t,function(e){return c.test(e)?A(e.slice(4).toLowerCase()):e})}},k={};function E(e){var t=e.charCodeAt(0);var i=void 0;if(t<16)i="%0"+t.toString(16).toUpperCase();else if(t<128)i="%"+t.toString(16).toUpperCase();else if(t<2048)i="%"+(t>>6|192).toString(16).toUpperCase()+"%"+(t&63|128).toString(16).toUpperCase();else i="%"+(t>>12|224).toString(16).toUpperCase()+"%"+(t>>6&63|128).toString(16).toUpperCase()+"%"+(t&63|128).toString(16).toUpperCase();return i}function R(e){var t="";var i=0;var n=e.length;while(i=194&&r<224){if(n-i>=6){var o=parseInt(e.substr(i+4,2),16);t+=String.fromCharCode((r&31)<<6|o&63)}else{t+=e.substr(i,6)}i+=6}else if(r>=224){if(n-i>=9){var s=parseInt(e.substr(i+4,2),16);var a=parseInt(e.substr(i+7,2),16);t+=String.fromCharCode((r&15)<<12|(s&63)<<6|a&63)}else{t+=e.substr(i,9)}i+=9}else{t+=e.substr(i,3);i+=3}}return t}function T(e,i){function t(e){var t=R(e);return!t.match(i.UNRESERVED)?e:t}if(e.scheme)e.scheme=String(e.scheme).replace(i.PCT_ENCODED,t).toLowerCase().replace(i.NOT_SCHEME,"");if(e.userinfo!==undefined)e.userinfo=String(e.userinfo).replace(i.PCT_ENCODED,t).replace(i.NOT_USERINFO,E).replace(i.PCT_ENCODED,p);if(e.host!==undefined)e.host=String(e.host).replace(i.PCT_ENCODED,t).toLowerCase().replace(i.NOT_HOST,E).replace(i.PCT_ENCODED,p);if(e.path!==undefined)e.path=String(e.path).replace(i.PCT_ENCODED,t).replace(e.scheme?i.NOT_PATH:i.NOT_PATH_NOSCHEME,E).replace(i.PCT_ENCODED,p);if(e.query!==undefined)e.query=String(e.query).replace(i.PCT_ENCODED,t).replace(i.NOT_QUERY,E).replace(i.PCT_ENCODED,p);if(e.fragment!==undefined)e.fragment=String(e.fragment).replace(i.PCT_ENCODED,t).replace(i.NOT_FRAGMENT,E).replace(i.PCT_ENCODED,p);return e}function _(e){return e.replace(/^0*(.*)/,"$1")||"0"}function $(e,t){var i=e.match(t.IPV4ADDRESS)||[];var n=y(i,2),r=n[1];if(r){return r.split(".").map(_).join(".")}else{return e}}function L(e,t){var i=e.match(t.IPV6ADDRESS)||[];var n=y(i,3),r=n[1],o=n[2];if(r){var s=r.toLowerCase().split("::").reverse(),a=y(s,2),l=a[0],c=a[1];var h=c?c.split(":").map(_):[];var d=l.split(":").map(_);var u=t.IPV4ADDRESS.test(d[d.length-1]);var g=u?7:8;var p=d.length-g;var m=Array(g);for(var f=0;f1){var A=m.slice(0,I.index);var b=m.slice(I.index+I.length);v=A.join(":")+"::"+b.join(":")}else{v=m.join(":")}if(o){v+="%"+o}return v}else{return e}}var B=/^(?:([^:\/?#]+):)?(?:\/\/((?:([^\/?#@]*)@)?(\[[^\/?#\]]+\]|[^\/?#:]*)(?:\:(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n|\r)*))?/i,z="".match(/(){0}/)[1]===undefined;function X(e){var t=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};var i={};var n=t.iri!==false?l:a;if(t.reference==="suffix")e=(t.scheme?t.scheme+":":"")+"//"+e;var r=e.match(B);if(r){if(z){i.scheme=r[1];i.userinfo=r[3];i.host=r[4];i.port=parseInt(r[5],10);i.path=r[6]||"";i.query=r[7];i.fragment=r[8];if(isNaN(i.port)){i.port=r[5]}}else{i.scheme=r[1]||undefined;i.userinfo=e.indexOf("@")!==-1?r[3]:undefined;i.host=e.indexOf("//")!==-1?r[4]:undefined;i.port=parseInt(r[5],10);i.path=r[6]||"";i.query=e.indexOf("?")!==-1?r[7]:undefined;i.fragment=e.indexOf("#")!==-1?r[8]:undefined;if(isNaN(i.port)){i.port=e.match(/\/\/(?:.|\n)*\:(?:\/|\?|\#|$)/)?r[4]:undefined}}if(i.host){i.host=L($(i.host,n),n)}if(i.scheme===undefined&&i.userinfo===undefined&&i.host===undefined&&i.port===undefined&&!i.path&&i.query===undefined){i.reference="same-document"}else if(i.scheme===undefined){i.reference="relative"}else if(i.fragment===undefined){i.reference="absolute"}else{i.reference="uri"}if(t.reference&&t.reference!=="suffix"&&t.reference!==i.reference){i.error=i.error||"URI is not a "+t.reference+" reference."}var o=k[(t.scheme||i.scheme||"").toLowerCase()];if(!t.unicodeSupport&&(!o||!o.unicodeSupport)){if(i.host&&(t.domainHost||o&&o.domainHost)){try{i.host=x.toASCII(i.host.replace(n.PCT_ENCODED,R).toLowerCase())}catch(e){i.error=i.error||"Host's domain name can not be converted to ASCII via punycode: "+e}}T(i,a)}else{T(i,n)}if(o&&o.parse){o.parse(i,t)}}else{i.error=i.error||"URI can not be parsed."}return i}function J(e,t){var i=t.iri!==false?l:a;var n=[];if(e.userinfo!==undefined){n.push(e.userinfo);n.push("@")}if(e.host!==undefined){n.push(L($(String(e.host),i),i).replace(i.IPV6ADDRESS,function(e,t,i){return"["+t+(i?"%25"+i:"")+"]"}))}if(typeof e.port==="number"||typeof e.port==="string"){n.push(":");n.push(String(e.port))}return n.length?n.join(""):undefined}var Y=/^\.\.?\//,U=/^\/\.(\/|$)/,Q=/^\/\.\.(\/|$)/,q=/^\/?(?:.|\n)*?(?=\/|$)/;function ee(e){var t=[];while(e.length){if(e.match(Y)){e=e.replace(Y,"")}else if(e.match(U)){e=e.replace(U,"/")}else if(e.match(Q)){e=e.replace(Q,"/");t.pop()}else if(e==="."||e===".."){e=""}else{var i=e.match(q);if(i){var n=i[0];e=e.slice(n.length);t.push(n)}else{throw new Error("Unexpected dot segment condition")}}}return t.join("")}function te(t){var i=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};var e=i.iri?l:a;var n=[];var r=k[(i.scheme||t.scheme||"").toLowerCase()];if(r&&r.serialize)r.serialize(t,i);if(t.host){if(e.IPV6ADDRESS.test(t.host)){}else if(i.domainHost||r&&r.domainHost){try{t.host=!i.iri?x.toASCII(t.host.replace(e.PCT_ENCODED,R).toLowerCase()):x.toUnicode(t.host)}catch(e){t.error=t.error||"Host's domain name can not be converted to "+(!i.iri?"ASCII":"Unicode")+" via punycode: "+e}}}T(t,e);if(i.reference!=="suffix"&&t.scheme){n.push(t.scheme);n.push(":")}var o=J(t,i);if(o!==undefined){if(i.reference!=="suffix"){n.push("//")}n.push(o);if(t.path&&t.path.charAt(0)!=="/"){n.push("/")}}if(t.path!==undefined){var s=t.path;if(!i.absolutePath&&(!r||!r.absolutePath)){s=ee(s)}if(o===undefined){s=s.replace(/^\/\//,"/%2F")}n.push(s)}if(t.query!==undefined){n.push("?");n.push(t.query)}if(t.fragment!==undefined){n.push("#");n.push(t.fragment)}return n.join("")}function ie(e,t){var i=arguments.length>2&&arguments[2]!==undefined?arguments[2]:{};var n=arguments[3];var r={};if(!n){e=X(te(e,i),i);t=X(te(t,i),i)}i=i||{};if(!i.tolerant&&t.scheme){r.scheme=t.scheme;r.userinfo=t.userinfo;r.host=t.host;r.port=t.port;r.path=ee(t.path||"");r.query=t.query}else{if(t.userinfo!==undefined||t.host!==undefined||t.port!==undefined){r.userinfo=t.userinfo;r.host=t.host;r.port=t.port;r.path=ee(t.path||"");r.query=t.query}else{if(!t.path){r.path=e.path;if(t.query!==undefined){r.query=t.query}else{r.query=e.query}}else{if(t.path.charAt(0)==="/"){r.path=ee(t.path)}else{if((e.userinfo!==undefined||e.host!==undefined||e.port!==undefined)&&!e.path){r.path="/"+t.path}else if(!e.path){r.path=t.path}else{r.path=e.path.slice(0,e.path.lastIndexOf("/")+1)+t.path}r.path=ee(r.path)}r.query=t.query}r.userinfo=e.userinfo;r.host=e.host;r.port=e.port}r.scheme=e.scheme}r.fragment=t.fragment;return r}function ne(e,t,i){var n=r({scheme:"null"},i);return te(ie(X(e,n),X(t,n),n,true),n)}function re(e,t){if(typeof e==="string"){e=te(X(e,t),t)}else if(n(e)==="object"){e=X(te(e,t),t)}return e}function oe(e,t,i){if(typeof e==="string"){e=te(X(e,i),i)}else if(n(e)==="object"){e=te(e,i)}if(typeof t==="string"){t=te(X(t,i),i)}else if(n(t)==="object"){t=te(t,i)}return e===t}function se(e,t){return e&&e.toString().replace(!t||!t.iri?a.ESCAPE:l.ESCAPE,E)}function ae(e,t){return e&&e.toString().replace(!t||!t.iri?a.PCT_ENCODED:l.PCT_ENCODED,R)}var le={scheme:"http",domainHost:true,parse:function e(t,i){if(!t.host){t.error=t.error||"HTTP URIs must have a host."}return t},serialize:function e(t,i){var n=String(t.scheme).toLowerCase()==="https";if(t.port===(n?443:80)||t.port===""){t.port=undefined}if(!t.path){t.path="/"}return t}},ce={scheme:"https",domainHost:le.domainHost,parse:le.parse,serialize:le.serialize};function ue(e){return typeof e.secure==="boolean"?e.secure:String(e.scheme).toLowerCase()==="wss"}var ge={scheme:"ws",domainHost:true,parse:function e(t,i){var n=t;n.secure=ue(n);n.resourceName=(n.path||"/")+(n.query?"?"+n.query:"");n.path=undefined;n.query=undefined;return n},serialize:function e(t,i){if(t.port===(ue(t)?443:80)||t.port===""){t.port=undefined}if(typeof t.secure==="boolean"){t.scheme=t.secure?"wss":"ws";t.secure=undefined}if(t.resourceName){var n=t.resourceName.split("?"),r=y(n,2),o=r[0],s=r[1];t.path=o&&o!=="/"?o:undefined;t.query=s;t.resourceName=undefined}t.fragment=undefined;return t}},pe={scheme:"wss",domainHost:ge.domainHost,parse:ge.parse,serialize:ge.serialize},me={},fe,Ce="[A-Za-z0-9\\-\\.\\_\\~"+(true?"\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF":"")+"]",Ie="[0-9A-Fa-f]",ve=de(de("%[EFef]"+Ie+"%"+Ie+Ie+"%"+Ie+Ie)+"|"+de("%[89A-Fa-f]"+Ie+"%"+Ie+Ie)+"|"+de("%"+Ie+Ie)),Ae="[A-Za-z0-9\\!\\$\\%\\'\\*\\+\\-\\^\\_\\`\\{\\|\\}\\~]",be,ye=he("[\\!\\$\\%\\'\\(\\)\\*\\+\\,\\-\\.0-9\\<\\>A-Z\\x5E-\\x7E]",'[\\"\\\\]'),we="[\\!\\$\\'\\(\\)\\*\\+\\,\\;\\:\\@]",Se=new RegExp(Ce,"g"),xe=new RegExp(ve,"g"),ke=new RegExp(he("[^]",Ae,"[\\.]",'[\\"]',ye),"g"),Ee=new RegExp(he("[^]",Ce,we),"g"),Re=Ee;function Te(e){var t=R(e);return!t.match(Se)?e:t}var _e={scheme:"mailto",parse:function e(t,i){var n=t;var r=n.to=n.path?n.path.split(","):[];n.path=undefined;if(n.query){var o=false;var s={};var a=n.query.split("&");for(var l=0,c=a.length;l%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i,h=/^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i,d=/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,u=/^(?:\/(?:[^~/]|~0|~1)*)*$/,g=/^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,i=/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;function p(e){return e="full"==e?"full":"fast",n.copy(p[e])}function m(e){var t=e.match(r);if(!t)return!1;var i=+t[1],e=+t[2],t=+t[3];return 1<=e&&e<=12&&1<=t&&t<=(2!=e||((i=i)%4!=0||i%100==0&&i%400!=0)?o[e]:29)}function f(e,t){var i=e.match(s);if(!i)return!1;var n=i[1],r=i[2],e=i[3],i=i[5];return(n<=23&&r<=59&&e<=59||23==n&&59==r&&60==e)&&(!t||i)}(e.exports=p).fast={date:/^\d\d\d\d-[0-1]\d-[0-3]\d$/,time:/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,"date-time":/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,uri:/^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/)?[^\s]*$/i,"uri-reference":/^(?:(?:[a-z][a-z0-9+\-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,"uri-template":c,url:h,email:/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i,hostname:a,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:A,uuid:d,"json-pointer":u,"json-pointer-uri-fragment":g,"relative-json-pointer":i},p.full={date:m,time:f,"date-time":function(e){e=e.split(C);return 2==e.length&&m(e[0])&&f(e[1],!0)},uri:function(e){return I.test(e)&&l.test(e)},"uri-reference":/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,"uri-template":c,url:h,email:/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:a,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:A,uuid:d,"json-pointer":u,"json-pointer-uri-fragment":g,"relative-json-pointer":i};var C=/t|\s/i;var I=/\/|:/;var v=/[^\\]\\Z/;function A(e){if(v.test(e))return!1;try{return new RegExp(e),!0}catch(e){return!1}}},function(e,t,i){"use strict";var o=i(53),s=i(7).toHash;e.exports=function(){var n=[{type:"number",rules:[{maximum:["exclusiveMaximum"]},{minimum:["exclusiveMinimum"]},"multipleOf","format"]},{type:"string",rules:["maxLength","minLength","pattern","format"]},{type:"array",rules:["maxItems","minItems","items","contains","uniqueItems"]},{type:"object",rules:["maxProperties","minProperties","required","dependencies","propertyNames",{properties:["additionalProperties","patternProperties"]}]},{rules:["$ref","const","enum","not","anyOf","oneOf","allOf","if"]}],r=["type","$comment"];return n.all=s(r),n.types=s(["number","integer","string","array","object","boolean","null"]),n.forEach(function(e){e.rules=e.rules.map(function(e){var t,i;return"object"==typeof e&&(i=e[t=Object.keys(e)[0]],e=t,i.forEach(function(e){r.push(e),n.all[e]=!0})),r.push(e),n.all[e]={keyword:e,code:o[e],implements:i}}),n.all.$comment={keyword:"$comment",code:o.$comment},e.type&&(n.types[e.type]=e)}),n.keywords=s(r.concat(["$schema","$id","id","$data","$async","title","description","default","definitions","examples","readOnly","writeOnly","contentMediaType","contentEncoding","additionalItems","then","else"])),n.custom={},n}},function(e,t,i){"use strict";e.exports={$ref:i(54),allOf:i(55),anyOf:i(56),$comment:i(57),const:i(58),contains:i(59),dependencies:i(60),enum:i(61),format:i(62),if:i(63),items:i(64),maximum:i(28),minimum:i(28),maxItems:i(29),minItems:i(29),maxLength:i(30),minLength:i(30),maxProperties:i(31),minProperties:i(31),multipleOf:i(65),not:i(66),oneOf:i(67),pattern:i(68),properties:i(69),propertyNames:i(70),required:i(71),uniqueItems:i(72),validate:i(27)}},function(e,t,i){"use strict";e.exports=function(e,t){var i,n,r=" ",o=e.level,s=e.dataLevel,a=e.schema[t],l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,h="data"+(s||""),t="valid"+o;if("#"==a||"#/"==a)n=e.isRoot?(i=e.async,"validate"):(i=!0===e.root.schema.$async,"root.refVal[0]");else{o=e.resolveRef(e.baseId,a,e.isRoot);if(void 0===o){var d,u=e.MissingRefError.message(e.baseId,a);if("fail"==e.opts.missingRefs){e.logger.error(u),(d=d||[]).push(r),r="",!1!==e.createErrors?(r+=" { keyword: '$ref' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { ref: '"+e.util.escapeQuotes(a)+"' } ",!1!==e.opts.messages&&(r+=" , message: 'can\\'t resolve reference "+e.util.escapeQuotes(a)+"' "),e.opts.verbose&&(r+=" , schema: "+e.util.toQuotedString(a)+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),r+=" } "):r+=" {} ";var g=r,r=d.pop();!e.compositeRule&&c?e.async?r+=" throw new ValidationError(["+g+"]); ":r+=" validate.errors = ["+g+"]; return false; ":r+=" var err = "+g+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",c&&(r+=" if (false) { ")}else{if("ignore"!=e.opts.missingRefs)throw new e.MissingRefError(e.baseId,a,u);e.logger.warn(u),c&&(r+=" if (true) { ")}}else{o.inline?((g=e.util.copy(e)).level++,u="valid"+g.level,g.schema=o.schema,g.schemaPath="",g.errSchemaPath=a,r+=" "+e.validate(g).replace(/validate\.schema/g,o.code)+" ",c&&(r+=" if ("+u+") { ")):(i=!0===o.$async||e.async&&!1!==o.$async,n=o.code)}}if(n){(d=d||[]).push(r),r="",e.opts.passContext?r+=" "+n+".call(this, ":r+=" "+n+"( ",r+=" "+h+", (dataPath || '')",'""'!=e.errorPath&&(r+=" + "+e.errorPath);s=r+=" , "+(s?"data"+(s-1||""):"parentData")+" , "+(s?e.dataPathArr[s]:"parentDataProperty")+", rootData) ";if(r=d.pop(),i){if(!e.async)throw new Error("async schema referenced by sync schema");c&&(r+=" var "+t+"; "),r+=" try { await "+s+"; ",c&&(r+=" "+t+" = true; "),r+=" } catch (e) { if (!(e instanceof ValidationError)) throw e; if (vErrors === null) vErrors = e.errors; else vErrors = vErrors.concat(e.errors); errors = vErrors.length; ",c&&(r+=" "+t+" = false; "),r+=" } ",c&&(r+=" if ("+t+") { ")}else r+=" if (!"+s+") { if (vErrors === null) vErrors = "+n+".errors; else vErrors = vErrors.concat("+n+".errors); errors = vErrors.length; } ",c&&(r+=" else { ")}return r}},function(e,t,i){"use strict";e.exports=function(e,t){var i=" ",n=e.schema[t],r=e.schemaPath+e.util.getProperty(t),o=e.errSchemaPath+"/"+t,s=!e.opts.allErrors,a=e.util.copy(e),l="";a.level++;var c="valid"+a.level,h=a.baseId,d=!0,u=n;if(u)for(var g,p=-1,m=u.length-1;p "+A+") { ",y=c+"["+A+"]",u.schema=S,u.schemaPath=s+"["+A+"]",u.errSchemaPath=a+"/"+A,u.errorPath=e.util.getPathExpr(e.errorPath,A,e.opts.jsonPointers,!0),u.dataPathArr[m]=A,w=e.validate(u),u.baseId=C,e.util.varOccurences(w,f)<2?i+=" "+e.util.varReplace(w,f,y)+" ":i+=" var "+f+" = "+y+"; "+w+" ",i+=" } ",l&&(i+=" if ("+p+") { ",g+="}"))}"object"==typeof r&&(e.opts.strictKeywords?"object"==typeof r&&0 "+o.length+") { for (var "+t+" = "+o.length+"; "+t+" < "+c+".length; "+t+"++) { ",u.errorPath=e.util.getPathExpr(e.errorPath,t,e.opts.jsonPointers,!0),y=c+"["+t+"]",u.dataPathArr[m]=t,w=e.validate(u),u.baseId=C,e.util.varOccurences(w,f)<2?i+=" "+e.util.varReplace(w,f,y)+" ":i+=" var "+f+" = "+y+"; "+w+" ",l&&(i+=" if (!"+p+") break; "),i+=" } } ",l&&(i+=" if ("+p+") { ",g+="}"))}else{(e.opts.strictKeywords?"object"==typeof o&&0 1e-"+e.opts.multipleOfPrecision+" ":i+=" division"+n+" !== parseInt(division"+n+") ",i+=" ) ",h&&(i+=" ) "),i+=" ) { ";var d=d||[];d.push(i),i="",!1!==e.createErrors?(i+=" { keyword: 'multipleOf' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(a)+" , params: { multipleOf: "+r+" } ",!1!==e.opts.messages&&(i+=" , message: 'should be multiple of ",i+=h?"' + "+r:r+"'"),e.opts.verbose&&(i+=" , schema: ",i+=h?"validate.schema"+s:""+o,i+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),i+=" } "):i+=" {} ";c=i,i=d.pop();return!e.compositeRule&&l?e.async?i+=" throw new ValidationError(["+c+"]); ":i+=" validate.errors = ["+c+"]; return false; ":i+=" var err = "+c+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",i+="} ",l&&(i+=" else { "),i}},function(e,t,i){"use strict";e.exports=function(e,t){var i=" ",n=e.level,r=e.dataLevel,o=e.schema[t],s=e.schemaPath+e.util.getProperty(t),a=e.errSchemaPath+"/"+t,l=!e.opts.allErrors,c="data"+(r||""),t="errs__"+n,r=e.util.copy(e);r.level++;var h,d,n="valid"+r.level;return(e.opts.strictKeywords?"object"==typeof o&&0=e.opts.loopRequired,I=e.opts.ownProperties;if(l)if(i+=" var missing"+n+"; ",o){t||(i+=" var "+r+" = validate.schema"+s+"; ");var v="' + "+(x="schema"+n+"["+(y="i"+n)+"]")+" + '";e.opts._errorDataPathProperty&&(e.errorPath=e.util.getPathExpr(C,x,e.opts.jsonPointers)),i+=" var "+h+" = true; ",t&&(i+=" if (schema"+n+" === undefined) "+h+" = true; else if (!Array.isArray(schema"+n+")) "+h+" = false; else {"),i+=" for (var "+y+" = 0; "+y+" < "+r+".length; "+y+"++) { "+h+" = "+c+"["+r+"["+y+"]] !== undefined ",I&&(i+=" && Object.prototype.hasOwnProperty.call("+c+", "+r+"["+y+"]) "),i+="; if (!"+h+") break; } ",t&&(i+=" } "),(S=S||[]).push(i+=" if (!"+h+") { "),i="",!1!==e.createErrors?(i+=" { keyword: 'required' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(a)+" , params: { missingProperty: '"+v+"' } ",!1!==e.opts.messages&&(i+=" , message: '",e.opts._errorDataPathProperty?i+="is a required property":i+="should have required property \\'"+v+"\\'",i+="' "),e.opts.verbose&&(i+=" , schema: validate.schema"+s+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),i+=" } "):i+=" {} ";var A=i,i=S.pop();!e.compositeRule&&l?e.async?i+=" throw new ValidationError(["+A+"]); ":i+=" validate.errors = ["+A+"]; return false; ":i+=" var err = "+A+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",i+=" } else { "}else{i+=" if ( ";var b=d;if(b)for(var y=-1,w=b.length-1;y 1) { ",o=e.schema.items&&e.schema.items.type,r=Array.isArray(o),!o||"object"==o||"array"==o||r&&(0<=o.indexOf("object")||0<=o.indexOf("array"))?n+=" outer: for (;i--;) { for (j = i; j--;) { if (equal("+h+"[i], "+h+"[j])) { "+d+" = false; break outer; } } } ":(n+=" var itemIndices = {}, item; for (;i--;) { var item = "+h+"[i]; ",t="checkDataType"+(r?"s":""),n+=" if ("+e.util[t](o,"item",e.opts.strictNumbers,!0)+") continue; ",r&&(n+=" if (typeof item == 'string') item = '\"' + item; "),n+=" if (typeof itemIndices[item] == 'number') { "+d+" = false; j = itemIndices[item]; break; } itemIndices[item] = i; } "),n+=" } ",u&&(n+=" } "),(i=i||[]).push(n+=" if (!"+d+") { "),n="",!1!==e.createErrors?(n+=" { keyword: 'uniqueItems' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { i: i, j: j } ",!1!==e.opts.messages&&(n+=" , message: 'should NOT have duplicate items (items ## ' + j + ' and ' + i + ' are identical)' "),e.opts.verbose&&(n+=" , schema: ",n+=u?"validate.schema"+a:""+s,n+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+h+" "),n+=" } "):n+=" {} ",h=n,n=i.pop(),!e.compositeRule&&c?e.async?n+=" throw new ValidationError(["+h+"]); ":n+=" validate.errors = ["+h+"]; return false; ":n+=" var err = "+h+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",n+=" } ",c&&(n+=" else { ")):c&&(n+=" if (true) { "),n}},function(e,t,i){"use strict";var l=["multipleOf","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","additionalItems","maxItems","minItems","uniqueItems","maxProperties","minProperties","required","additionalProperties","enum","format","const"];e.exports=function(e,t){for(var i=0;ithis.results.length-1&&(t=0),this._setActiveResult(t,e))}},{key:"previous",value:function(e){var t,i;this.results&&(t=this.results.length-1,(i=null!==this.resultIndex?this.resultIndex-1:t)<0&&(i=t),this._setActiveResult(i,e))}},{key:"_setActiveResult",value:function(e,t){var i;if(this.activeResult&&(i=this.activeResult.node,"field"===this.activeResult.elem?delete i.searchFieldActive:delete i.searchValueActive,i.updateDom()),!this.results||!this.results[e])return this.resultIndex=void 0,void(this.activeResult=void 0);this.resultIndex=e;var n=this.results[this.resultIndex].node,r=this.results[this.resultIndex].elem;"field"===r?n.searchFieldActive=!0:n.searchValueActive=!0,this.activeResult=this.results[this.resultIndex],n.updateDom(),n.scrollTo(function(){t&&n.focus(r)})}},{key:"_clearDelay",value:function(){void 0!==this.timeout&&(clearTimeout(this.timeout),delete this.timeout)}},{key:"_onDelayedSearch",value:function(){this._clearDelay();var t=this;this.timeout=setTimeout(function(e){t._onSearch()},this.delay)}},{key:"_onSearch",value:function(e){this._clearDelay();var t=this.dom.search.value,t=0=e.length;r--)this.removeChild(this.childs[r],!1)}else if("object"===this.type){for(this.childs||(this.childs=[]),r=this.childs.length-1;0<=r;r--)S(e,this.childs[r].field)||this.removeChild(this.childs[r],!1);for(var h in n=0,e){S(e,h)&&(void 0===(s=e[h])||s instanceof Function||((l=this.findChildByProperty(h))?(l.setField(h,!0),l.setValue(s)):(a=new R(this.editor,{field:h,value:s}),l=n=e.childs.length;o--)this.removeChild(this.childs[o],!1)}else if("object"===e.type){for(this.childs||(this.childs=[]),r=0;r=e.childs.length;o--)this.removeChild(this.childs[o],!1)}else this.hideChilds(),delete this.append,delete this.showMore,delete this.expanded,delete this.childs,this.value=e.value;Array.isArray(s)!==Array.isArray(this.childs)&&this.recreateDom(),this.updateDom({updateIndexes:!0}),this.previousValue=this.value}},{key:"recreateDom",value:function(){var e;this.dom&&this.dom.tr&&this.dom.tr.parentNode?(e=this._detachFromDom(),this.clearDom(),this._attachToDom(e)):this.clearDom()}},{key:"getValue",value:function(){if("array"===this.type){var t=[];return this.childs.forEach(function(e){t.push(e.getValue())}),t}if("object"!==this.type)return void 0===this.value&&this._getDomValue(),this.value;var i={};return this.childs.forEach(function(e){i[e.getField()]=e.getValue()}),i}},{key:"getInternalValue",value:function(){return"array"===this.type?{type:this.type,childs:this.childs.map(function(e){return e.getInternalValue()})}:"object"===this.type?{type:this.type,childs:this.childs.map(function(e){return{field:e.getField(),value:e.getInternalValue()}})}:(void 0===this.value&&this._getDomValue(),{type:this.type,value:this.value})}},{key:"getLevel",value:function(){return this.parent?this.parent.getLevel()+1:0}},{key:"getNodePath",value:function(){var e=this.parent?this.parent.getNodePath():[];return e.push(this),e}},{key:"clone",value:function(){var t,i=new R(this.editor);return i.type=this.type,i.field=this.field,i.fieldInnerText=this.fieldInnerText,i.fieldEditable=this.fieldEditable,i.previousField=this.previousField,i.value=this.value,i.valueInnerText=this.valueInnerText,i.previousValue=this.previousValue,i.expanded=this.expanded,i.visibleChilds=this.visibleChilds,this.childs?(t=[],this.childs.forEach(function(e){e=e.clone();e.setParent(i),t.push(e)}),i.childs=t):i.childs=void 0,i}},{key:"expand",value:function(t){this.childs&&(this.expanded=!0,this.dom.expand&&(this.dom.expand.className="jsoneditor-button jsoneditor-expanded"),this.showChilds(),!1!==t&&this.childs.forEach(function(e){e.expand(t)}),this.updateDom({recurse:!1}))}},{key:"collapse",value:function(t){this.childs&&(this.hideChilds(),!1!==t&&this.childs.forEach(function(e){e.collapse(t)}),this.dom.expand&&(this.dom.expand.className="jsoneditor-button jsoneditor-collapsed"),this.expanded=!1,this.updateDom({recurse:!1}))}},{key:"showChilds",value:function(){if(this.childs&&this.expanded){var e=this.dom.tr,t=e?e.parentNode:void 0;if(t){var i=this.getAppendDom();i.parentNode||((r=e.nextSibling)?t.insertBefore(i,r):t.appendChild(i));for(var n=Math.min(this.childs.length,this.visibleChilds),r=this._getNextTr(),o=0;othis.visibleChilds?(o=this.childs[this.visibleChilds-1],this.insertBefore(e,o,i)):this.appendChild(e,!0,i):this.insertBefore(e,t,i),n&&n.removeChild(r))}},{key:"insertBefore",value:function(e,t,i){if(this._hasChilds()){if(this.visibleChilds++,"object"===this.type&&void 0===e.field&&e.setField(""),t===this.append)e.setParent(this),e.fieldEditable="object"===this.type,this.childs.push(e);else{var n=this.childs.indexOf(t);if(-1===n)throw new Error("Node not found");e.setParent(this),e.fieldEditable="object"===this.type,this.childs.splice(n,0,e)}var r;this.expanded&&(r=e.getDom(),t=(n=t.getDom())?n.parentNode:void 0,n&&t&&t.insertBefore(r,n),e.showChilds(),this.showChilds()),!1!==i&&(this.updateDom({updateIndexes:!0}),e.updateDom({recurse:!0}))}}},{key:"insertAfter",value:function(e,t){this._hasChilds()&&(t=this.childs.indexOf(t),(t=this.childs[t+1])?this.insertBefore(e,t):this.appendChild(e))}},{key:"search",value:function(t,i){Array.isArray(i)||(i=[]);var e=t?t.toLowerCase():void 0;return delete this.searchField,delete this.searchValue,void 0!==this.field&&i.length<=this.MAX_SEARCH_RESULTS&&(-1!==String(this.field).toLowerCase().indexOf(e)&&(this.searchField=!0,i.push({node:this,elem:"field"})),this._updateDomField()),this._hasChilds()?this.childs&&this.childs.forEach(function(e){e.search(t,i)}):void 0!==this.value&&i.length<=this.MAX_SEARCH_RESULTS&&(-1!==String(this.value).toLowerCase().indexOf(e)&&(this.searchValue=!0,i.push({node:this,elem:"value"})),this._updateDomValue()),i}},{key:"scrollTo",value:function(e){this.expandPathToNode(),this.dom.tr&&this.dom.tr.parentNode&&this.editor.scrollTo(this.dom.tr.offsetTop,e)}},{key:"expandPathToNode",value:function(){for(var e=this;e&&e.parent;){for(var t="array"===e.parent.type?e.index:e.parent.childs.indexOf(e);e.parent.visibleChilds/g,">").replace(/ {2}/g," ").replace(/^ /," ").replace(/ $/," "),e=JSON.stringify(e),e=e.substring(1,e.length-1);return!0===this.editor.options.escapeUnicode&&(e=Object(T.escapeUnicodeChars)(e)),e}},{key:"_unescapeHTML",value:function(e){e='"'+this._escapeJSON(e)+'"';return Object(T.parse)(e).replace(/</g,"<").replace(/>/g,">").replace(/ |\u00A0/g," ").replace(/&/g,"&")}},{key:"_escapeJSON",value:function(e){for(var t="",i=0;ithis.parent.visibleChilds},E.prototype.onEvent=function(e){"keydown"===e.type&&this.onKeyDown(e)},E);function E(e,t){this.editor=e,this.parent=t,this.dom={}}var R=i(8),L=i(9),B={start:function(e,t){return 0===t.indexOf(e)},contain:function(e,t){return-1this.limit&&1l.d?(i=this,Object(h.addClassName)(i.frame,"busy"),i.dom.busyContent.innerText=t,setTimeout(function(){e(),Object(h.removeClassName)(i.frame,"busy"),i.dom.busyContent.innerText=""},100)):e()},t.validate=i.validate,t._renderErrors=i._renderErrors;var p=[{mode:"preview",mixin:t,data:"json"}]}],r.c=n,r.d=function(e,t,i){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)r.d(i,n,function(e){return t[e]}.bind(null,n));return i},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=36);function r(e){if(n[e])return n[e].exports;var t=n[e]={i:e,l:!1,exports:{}};return i[e].call(t.exports,t,t.exports,r),t.l=!0,t.exports}var i,n});
+//# sourceMappingURL=jsoneditor.map
\ No newline at end of file
diff --git a/app/src/main/java/com/zane/smapiinstaller/MainActivity.java b/app/src/main/java/com/zane/smapiinstaller/MainActivity.java
index 5960343..ade6a7f 100644
--- a/app/src/main/java/com/zane/smapiinstaller/MainActivity.java
+++ b/app/src/main/java/com/zane/smapiinstaller/MainActivity.java
@@ -4,8 +4,10 @@ import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
+import android.provider.Settings;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -32,6 +34,7 @@ import com.zane.smapiinstaller.logic.GameLauncher;
import com.zane.smapiinstaller.logic.ModAssetsManager;
import com.zane.smapiinstaller.utils.ConfigUtils;
import com.zane.smapiinstaller.utils.DialogUtils;
+import com.zane.smapiinstaller.utils.FileUtils;
import com.zane.smapiinstaller.utils.JsonCallback;
import com.zane.smapiinstaller.utils.JsonUtil;
import com.zane.smapiinstaller.utils.TranslateUtil;
@@ -64,6 +67,27 @@ public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private void requestPermissions() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // 先判断有没有权限
+ if (!Environment.isExternalStorageManager()) {
+ DialogUtils.showConfirmDialog(MainActivity.instance, R.string.confirm, R.string.request_all_files_access_permission, ((dialog, dialogAction) -> {
+ if (dialogAction == DialogAction.POSITIVE) {
+ ActivityResultHandler.registerListener(ActivityResultHandler.REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION, (resultCode, data) -> {
+ if (!Environment.isExternalStorageManager()) {
+ this.finish();
+ } else {
+ requestPermissions();
+ }
+ });
+ startActivityForResult(new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION), ActivityResultHandler.REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION);
+ }
+ else {
+ this.finish();
+ }
+ }));
+ return;
+ }
+ }
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
@@ -235,7 +259,7 @@ public class MainActivity extends AppCompatActivity {
DialogUtils.showInputDialog(binding.appBarMain.toolbar, R.string.input, R.string.input_mods_path, Constants.MOD_PATH, Constants.MOD_PATH, (dialog, input) -> {
if (StringUtils.isNoneBlank(input)) {
String pathString = input.toString();
- File file = new File(Environment.getExternalStorageDirectory(), pathString);
+ File file = new File(FileUtils.getStadewValleyBasePath(), pathString);
if (file.exists() && file.isDirectory()) {
Constants.MOD_PATH = pathString;
config.setModsPath(pathString);
diff --git a/app/src/main/java/com/zane/smapiinstaller/dto/ModUpdateCheckRequestDto.java b/app/src/main/java/com/zane/smapiinstaller/dto/ModUpdateCheckRequestDto.java
index bfe6c89..78f8287 100644
--- a/app/src/main/java/com/zane/smapiinstaller/dto/ModUpdateCheckRequestDto.java
+++ b/app/src/main/java/com/zane/smapiinstaller/dto/ModUpdateCheckRequestDto.java
@@ -25,6 +25,11 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class ModUpdateCheckRequestDto {
+ public ModUpdateCheckRequestDto(List mods, SemanticVersion gameVersion) {
+ this.mods = mods;
+ this.gameVersion = gameVersion;
+ }
+
/**
* 待检查MOD列表
*/
@@ -37,7 +42,6 @@ public class ModUpdateCheckRequestDto {
/**
* 游戏版本
*/
- @NonNull
private SemanticVersion gameVersion;
/**
* 平台版本
diff --git a/app/src/main/java/com/zane/smapiinstaller/dto/ModUpdateCheckResponseDto.java b/app/src/main/java/com/zane/smapiinstaller/dto/ModUpdateCheckResponseDto.java
index b652b88..f463c9e 100644
--- a/app/src/main/java/com/zane/smapiinstaller/dto/ModUpdateCheckResponseDto.java
+++ b/app/src/main/java/com/zane/smapiinstaller/dto/ModUpdateCheckResponseDto.java
@@ -2,7 +2,9 @@ package com.zane.smapiinstaller.dto;
import java.util.List;
+import lombok.AllArgsConstructor;
import lombok.Data;
+import lombok.NoArgsConstructor;
/**
* @author Zane
@@ -12,9 +14,22 @@ public class ModUpdateCheckResponseDto {
private String id;
private UpdateInfo suggestedUpdate;
private List errors;
+ private Metadata metadata;
@Data
+ @NoArgsConstructor
+ @AllArgsConstructor
public static class UpdateInfo {
private String version;
private String url;
}
+
+ @Data
+ public static class Metadata {
+ private Main main;
+ @Data
+ public static class Main {
+ private String version;
+ private String url;
+ }
+ }
}
diff --git a/app/src/main/java/com/zane/smapiinstaller/dto/Tuple2.java b/app/src/main/java/com/zane/smapiinstaller/dto/Tuple2.java
new file mode 100644
index 0000000..f7236e1
--- /dev/null
+++ b/app/src/main/java/com/zane/smapiinstaller/dto/Tuple2.java
@@ -0,0 +1,11 @@
+package com.zane.smapiinstaller.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class Tuple2 {
+ private U first;
+ private V second;
+}
diff --git a/app/src/main/java/com/zane/smapiinstaller/dto/WebViewObject.java b/app/src/main/java/com/zane/smapiinstaller/dto/WebViewObject.java
new file mode 100644
index 0000000..2587e8b
--- /dev/null
+++ b/app/src/main/java/com/zane/smapiinstaller/dto/WebViewObject.java
@@ -0,0 +1,49 @@
+package com.zane.smapiinstaller.dto;
+
+import android.webkit.JavascriptInterface;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author Zane
+ */
+@AllArgsConstructor
+public class WebViewObject {
+ private String text;
+ private String mode;
+ private String language;
+ private boolean editable;
+ private int height;
+
+ @JavascriptInterface
+ public String getText() {
+ return text;
+ }
+
+ @JavascriptInterface
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ @JavascriptInterface
+ public boolean isEditable() {
+ return editable;
+ }
+
+ @JavascriptInterface
+ public int getHeight() {
+ return height;
+ }
+
+ @JavascriptInterface
+ public String getMode() {
+ return mode;
+ }
+
+ @JavascriptInterface
+ public String getLanguage() {
+ return language;
+ }
+}
diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/ActivityResultHandler.java b/app/src/main/java/com/zane/smapiinstaller/logic/ActivityResultHandler.java
index 928428a..d853fa0 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/ActivityResultHandler.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/ActivityResultHandler.java
@@ -10,7 +10,8 @@ import java.util.function.BiConsumer;
* @author Zane
*/
public class ActivityResultHandler {
- public static int REQUEST_CODE_APP_INSTALL = 1001;
+ public static final int REQUEST_CODE_APP_INSTALL = 1001;
+ public static final int REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION = 1002;
public static ConcurrentHashMap> listenerMap = new ConcurrentHashMap<>();
diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java b/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java
index 3b19163..f3e5c8b 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java
@@ -7,17 +7,14 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
-import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
import com.android.apksig.ApkSigner;
import com.android.apksig.ApkVerifier;
import com.fasterxml.jackson.core.type.TypeReference;
-import com.google.common.base.Predicate;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Iterables;
-import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import com.zane.smapiinstaller.BuildConfig;
import com.zane.smapiinstaller.MainActivity;
@@ -25,6 +22,7 @@ import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.constant.ManifestPatchConstants;
+import com.zane.smapiinstaller.dto.Tuple2;
import com.zane.smapiinstaller.entity.ApkFilesManifest;
import com.zane.smapiinstaller.entity.ManifestEntry;
import com.zane.smapiinstaller.utils.DialogUtils;
@@ -47,18 +45,17 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import java.util.zip.Deflater;
import androidx.core.content.FileProvider;
-
-import java.util.Objects;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
import pxb.android.axml.NodeVisitor;
/**
@@ -108,46 +105,44 @@ public class ApkPatcher {
String sourceDir = packageInfo.applicationInfo.publicSourceDir;
File apkFile = new File(sourceDir);
- File externalFilesDir = Environment.getExternalStorageDirectory();
- if (externalFilesDir != null) {
- File dest = new File(externalFilesDir.getAbsolutePath() + "/SMAPI Installer/");
- if (!dest.exists()) {
- if (!dest.mkdir()) {
- errorMessage.set(String.format(context.getString(R.string.error_failed_to_create_file), dest.getAbsolutePath()));
- return null;
- }
+ String stadewValleyBasePath = FileUtils.getStadewValleyBasePath();
+ File dest = new File(stadewValleyBasePath + "/SMAPI Installer/");
+ if (!dest.exists()) {
+ if (!dest.mkdir()) {
+ errorMessage.set(String.format(context.getString(R.string.error_failed_to_create_file), dest.getAbsolutePath()));
+ return null;
}
- File distFile = new File(dest, apkFile.getName());
- if (advancedStage == 0) {
- AtomicInteger count = new AtomicInteger();
- ZipUtil.unpack(apkFile, new File(externalFilesDir.getAbsolutePath() + "/StardewValley/"), name -> {
- if (name.startsWith("assets/")) {
- int progress = count.incrementAndGet();
- if (progress % 30 == 0) {
- emitProgress(progress / 30);
- }
- return name.replaceFirst("assets/", "");
- }
- return null;
- });
- return distFile.getAbsolutePath();
- } else if (advancedStage == 1) {
- File contentFolder = new File(externalFilesDir.getAbsolutePath() + "/StardewValley/Content");
- if (contentFolder.exists()) {
- if (!contentFolder.isDirectory()) {
- errorMessage.set(context.getString(R.string.error_directory_exists_with_same_filename, contentFolder.getAbsolutePath()));
- return null;
- }
- } else {
- extract(0);
- }
- ZipUtils.removeEntries(sourceDir, "assets/Content", distFile.getAbsolutePath(), (progress) -> emitProgress((int) (progress * 0.05)));
- } else {
- Files.copy(apkFile, distFile);
- }
- emitProgress(5);
- return distFile.getAbsolutePath();
}
+ File distFile = new File(dest, apkFile.getName());
+ if (advancedStage == 0) {
+ AtomicInteger count = new AtomicInteger();
+ ZipUtil.unpack(apkFile, new File(stadewValleyBasePath + "/StardewValley/"), name -> {
+ if (name.startsWith("assets/")) {
+ int progress = count.incrementAndGet();
+ if (progress % 30 == 0) {
+ emitProgress(progress / 30);
+ }
+ return name.replaceFirst("assets/", "");
+ }
+ return null;
+ });
+ return distFile.getAbsolutePath();
+ } else if (advancedStage == 1) {
+ File contentFolder = new File(stadewValleyBasePath + "/StardewValley/Content");
+ if (contentFolder.exists()) {
+ if (!contentFolder.isDirectory()) {
+ errorMessage.set(context.getString(R.string.error_directory_exists_with_same_filename, contentFolder.getAbsolutePath()));
+ return null;
+ }
+ } else {
+ extract(0);
+ }
+ ZipUtils.removeEntries(sourceDir, "assets/Content", distFile.getAbsolutePath(), (progress) -> emitProgress((int) (progress * 0.05)));
+ } else {
+ Files.copy(apkFile, distFile);
+ }
+ emitProgress(5);
+ return distFile.getAbsolutePath();
} catch (PackageManager.NameNotFoundException ignored) {
} catch (IOException e) {
Log.e(TAG, "Extract error", e);
@@ -250,9 +245,9 @@ public class ApkPatcher {
AtomicReference packageName = new AtomicReference<>();
AtomicReference versionName = new AtomicReference<>();
AtomicLong versionCode = new AtomicLong();
- Predicate processLogic = (attr) -> {
+ Function> attrProcessLogic = (attr) -> {
if (attr == null) {
- return true;
+ return null;
}
if (attr.type == NodeVisitor.TYPE_STRING) {
String strObj = (String) attr.obj;
@@ -276,6 +271,7 @@ public class ApkPatcher {
attr.obj = Constants.PATCHED_APP_NAME;
}
}
+// return Collections.singletonList(new ManifestTagVisitor.AttrArgs(attr.ns, "requestLegacyExternalStorage", -1, NodeVisitor.TYPE_INT_BOOLEAN, true));
break;
case "authorities":
if (strObj.contains(packageName.get())) {
@@ -296,10 +292,22 @@ public class ApkPatcher {
versionCode.set((int) attr.obj);
}
}
- return true;
+ return null;
};
+ AtomicReference permissionAppended = new AtomicReference<>(true);
+ Function> childProcessLogic = (child -> {
+ if (!permissionAppended.get() && StringUtils.equals(child.name, "uses-permission")) {
+ permissionAppended.set(true);
+ return Collections.singletonList(new ManifestTagVisitor.ChildArgs(
+ child.ns, child.name, Collections.singletonList(
+ new ManifestTagVisitor.AttrArgs(
+ "http://schemas.android.com/apk/res/android", "name", -1,
+ NodeVisitor.TYPE_STRING, "android.permission.MANAGE_EXTERNAL_STORAGE"))));
+ }
+ return null;
+ });
try {
- byte[] modifyManifest = CommonLogic.modifyManifest(bytes, processLogic);
+ byte[] modifyManifest = CommonLogic.modifyManifest(bytes, attrProcessLogic, childProcessLogic);
if (StringUtils.endsWith(versionName.get(), ManifestPatchConstants.PATTERN_VERSION_AMAZON)) {
packageName.set(ManifestPatchConstants.APP_PACKAGE_NAME + ManifestPatchConstants.PATTERN_VERSION_AMAZON);
}
@@ -335,56 +343,54 @@ public class ApkPatcher {
*/
public String sign(String apkPath) {
try {
- File externalFilesDir = Environment.getExternalStorageDirectory();
+ String stadewValleyBasePath = FileUtils.getStadewValleyBasePath();
emitProgress(47);
- if (externalFilesDir != null) {
- String signApkPath = externalFilesDir.getAbsolutePath() + "/SMAPI Installer/base_signed.apk";
- KeyStore ks = new KeyStoreFileManager.JksKeyStore();
- try (InputStream fis = context.getAssets().open("debug.keystore.dat")) {
- ks.load(fis, PASSWORD.toCharArray());
- }
- String alias = ks.aliases().nextElement();
- X509Certificate publicKey = (X509Certificate) ks.getCertificate(alias);
- PrivateKey privateKey = (PrivateKey) ks.getKey(alias, "android".toCharArray());
- ApkSigner.SignerConfig signerConfig = new ApkSigner.SignerConfig.Builder("debug", privateKey, Collections.singletonList(publicKey)).build();
- emitProgress(49);
- File outputFile = new File(signApkPath);
- ApkSigner signer = new ApkSigner.Builder(Collections.singletonList(signerConfig))
- .setInputApk(new File(apkPath))
- .setOutputApk(outputFile)
- .setV1SigningEnabled(true)
- .setV2SigningEnabled(true).build();
- long zipOpElapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
- stopwatch.reset();
- Thread thread = new Thread(() -> {
- stopwatch.start();
- while (true) {
- try {
- Thread.sleep(20);
- long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
- double progress = elapsed * 0.98 / zipOpElapsed;
- if (progress < 1.0) {
- emitProgress((int) (49 + 45 * progress));
- }
- } catch (InterruptedException ignored) {
- return;
- }
- }
- });
- thread.start();
- signer.sign();
- FileUtils.forceDelete(new File(apkPath));
- ApkVerifier.Result result = new ApkVerifier.Builder(outputFile).build().verify();
- if (thread.isAlive() && !thread.isInterrupted()) {
- thread.interrupt();
- }
- if (result.containsErrors()) {
- errorMessage.set(result.getErrors().stream().map(ApkVerifier.IssueWithParams::toString).collect(Collectors.joining(",")));
- return null;
- }
- emitProgress(95);
- return signApkPath;
+ String signApkPath = stadewValleyBasePath + "/SMAPI Installer/base_signed.apk";
+ KeyStore ks = new KeyStoreFileManager.JksKeyStore();
+ try (InputStream fis = context.getAssets().open("debug.keystore.dat")) {
+ ks.load(fis, PASSWORD.toCharArray());
}
+ String alias = ks.aliases().nextElement();
+ X509Certificate publicKey = (X509Certificate) ks.getCertificate(alias);
+ PrivateKey privateKey = (PrivateKey) ks.getKey(alias, "android".toCharArray());
+ ApkSigner.SignerConfig signerConfig = new ApkSigner.SignerConfig.Builder("debug", privateKey, Collections.singletonList(publicKey)).build();
+ emitProgress(49);
+ File outputFile = new File(signApkPath);
+ ApkSigner signer = new ApkSigner.Builder(Collections.singletonList(signerConfig))
+ .setInputApk(new File(apkPath))
+ .setOutputApk(outputFile)
+ .setV1SigningEnabled(true)
+ .setV2SigningEnabled(true).build();
+ long zipOpElapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
+ stopwatch.reset();
+ Thread thread = new Thread(() -> {
+ stopwatch.start();
+ while (true) {
+ try {
+ Thread.sleep(20);
+ long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
+ double progress = elapsed * 0.98 / zipOpElapsed;
+ if (progress < 1.0) {
+ emitProgress((int) (49 + 45 * progress));
+ }
+ } catch (InterruptedException ignored) {
+ return;
+ }
+ }
+ });
+ thread.start();
+ signer.sign();
+ FileUtils.forceDelete(new File(apkPath));
+ ApkVerifier.Result result = new ApkVerifier.Builder(outputFile).build().verify();
+ if (thread.isAlive() && !thread.isInterrupted()) {
+ thread.interrupt();
+ }
+ if (result.containsErrors()) {
+ errorMessage.set(result.getErrors().stream().map(ApkVerifier.IssueWithParams::toString).collect(Collectors.joining(",")));
+ return null;
+ }
+ emitProgress(95);
+ return signApkPath;
} catch (Exception e) {
Log.e(TAG, "Sign error", e);
errorMessage.set(e.getLocalizedMessage());
diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java b/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java
index 7006416..1abefbc 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java
@@ -11,7 +11,6 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
-import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
@@ -20,7 +19,6 @@ import android.widget.ImageView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.fasterxml.jackson.core.type.TypeReference;
-import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import com.lmntrx.android.library.livin.missme.ProgressDialog;
@@ -28,6 +26,7 @@ import com.microsoft.appcenter.crashes.Crashes;
import com.zane.smapiinstaller.MainApplication;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.DialogAction;
+import com.zane.smapiinstaller.dto.Tuple2;
import com.zane.smapiinstaller.entity.ApkFilesManifest;
import com.zane.smapiinstaller.entity.ManifestEntry;
import com.zane.smapiinstaller.utils.DialogUtils;
@@ -43,9 +42,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
-
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Function;
+
import pxb.android.axml.AxmlReader;
import pxb.android.axml.AxmlVisitor;
import pxb.android.axml.AxmlWriter;
@@ -196,7 +196,7 @@ public class CommonLogic {
if (manifestEntries == null) {
return false;
}
- File basePath = new File(Environment.getExternalStorageDirectory() + "/StardewValley/");
+ File basePath = new File(FileUtils.getStadewValleyBasePath() + "/StardewValley/");
if (!basePath.exists()) {
if (!basePath.mkdir()) {
return false;
@@ -244,18 +244,18 @@ public class CommonLogic {
* 修改AndroidManifest.xml文件
*
* @param bytes AndroidManifest.xml文件字符数组
- * @param processLogic 处理逻辑
+ * @param attrProcessLogic 处理逻辑
* @return 修改后的AndroidManifest.xml文件字符数组
* @throws IOException 异常
*/
- public static byte[] modifyManifest(byte[] bytes, Predicate processLogic) throws IOException {
+ public static byte[] modifyManifest(byte[] bytes, Function> attrProcessLogic, Function> childProcessLogic) throws IOException {
AxmlReader reader = new AxmlReader(bytes);
AxmlWriter writer = new AxmlWriter();
reader.accept(new AxmlVisitor(writer) {
@Override
public NodeVisitor child(String ns, String name) {
NodeVisitor child = super.child(ns, name);
- return new ManifestTagVisitor(child, processLogic);
+ return new ManifestTagVisitor(child, attrProcessLogic, childProcessLogic);
}
});
return writer.toByteArray();
diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/ConfigManager.java b/app/src/main/java/com/zane/smapiinstaller/logic/ConfigManager.java
index 556c24f..695df49 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/ConfigManager.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/ConfigManager.java
@@ -1,7 +1,5 @@
package com.zane.smapiinstaller.logic;
-import android.os.Environment;
-
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.entity.FrameworkConfig;
import com.zane.smapiinstaller.utils.FileUtils;
@@ -16,7 +14,7 @@ public class ConfigManager {
private FrameworkConfig config;
public ConfigManager() {
- File configFile = new File(Environment.getExternalStorageDirectory(), Constants.CONFIG_PATH);
+ File configFile = new File(FileUtils.getStadewValleyBasePath(), Constants.CONFIG_PATH);
if(configFile.exists()) {
config = FileUtils.getFileJson(configFile, FrameworkConfig.class);
}
@@ -31,7 +29,7 @@ public class ConfigManager {
}
public void flushConfig() {
- File configFile = new File(Environment.getExternalStorageDirectory(), Constants.CONFIG_PATH);
+ File configFile = new File(FileUtils.getStadewValleyBasePath(), Constants.CONFIG_PATH);
FileUtils.writeFileJson(configFile, config);
}
}
diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/ManifestTagVisitor.java b/app/src/main/java/com/zane/smapiinstaller/logic/ManifestTagVisitor.java
index eca3cd2..cdbe547 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/ManifestTagVisitor.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/ManifestTagVisitor.java
@@ -1,7 +1,10 @@
package com.zane.smapiinstaller.logic;
-import com.google.common.base.Predicate;
+import java.util.List;
+import java.util.function.Function;
+import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
import pxb.android.axml.NodeVisitor;
/**
@@ -9,38 +12,56 @@ import pxb.android.axml.NodeVisitor;
*/
class ManifestTagVisitor extends NodeVisitor {
- private final Predicate attrProcessLogic;
+ private final Function> attrProcessLogic;
+ private final Function> childProcessLogic;
- public ManifestTagVisitor(NodeVisitor nv, Predicate attrProcessLogic) {
+ public ManifestTagVisitor(NodeVisitor nv, Function> attrProcessLogic, Function> childProcessLogic) {
super(nv);
this.attrProcessLogic = attrProcessLogic;
+ this.childProcessLogic = childProcessLogic;
}
@Override
public void attr(String ns, String name, int resourceId, int type, Object obj) {
AttrArgs attrArgs = new AttrArgs(ns, name, resourceId, type, obj);
- attrProcessLogic.apply(attrArgs);
+ List appendAttrs = attrProcessLogic.apply(attrArgs);
super.attr(attrArgs.ns, attrArgs.name, attrArgs.resourceId, attrArgs.type, attrArgs.obj);
+ if(appendAttrs != null) {
+ for (AttrArgs attr: appendAttrs) {
+ super.attr(attr.ns, attr.name, attr.resourceId, attr.type, attr.obj);
+ }
+ }
}
@Override
public NodeVisitor child(String ns, String name) {
- return new ManifestTagVisitor(super.child(ns, name), attrProcessLogic);
+ ChildArgs childArgs = new ChildArgs(ns, name, null);
+ List appendChild = childProcessLogic.apply(childArgs);
+ NodeVisitor child = super.child(childArgs.ns, childArgs.name);
+ if(appendChild != null) {
+ for (ChildArgs c: appendChild) {
+ NodeVisitor visitor = super.child(c.ns, c.name);
+ if(c.attrArgs != null) {
+ for (AttrArgs attr: c.attrArgs) {
+ visitor.attr(attr.ns, attr.name, attr.resourceId, attr.type, attr.obj);
+ }
+ }
+ }
+ }
+ return new ManifestTagVisitor(child, attrProcessLogic, childProcessLogic);
}
-
+ @AllArgsConstructor
+ public static class ChildArgs {
+ String ns;
+ String name;
+ List attrArgs;
+ }
+ @AllArgsConstructor
public static class AttrArgs {
String ns;
String name;
int resourceId;
int type;
Object obj;
-
- AttrArgs(String ns, String name, int resourceId, int type, Object obj) {
- this.ns = ns;
- this.name = name;
- this.resourceId = resourceId;
- this.type = type;
- this.obj = obj;
- }
}
}
diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java b/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java
index 51b7321..a56c15e 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java
@@ -2,7 +2,6 @@ package com.zane.smapiinstaller.logic;
import android.app.Activity;
import android.content.pm.PackageInfo;
-import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
@@ -17,16 +16,18 @@ import com.google.common.collect.Queues;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.model.Response;
import com.microsoft.appcenter.crashes.Crashes;
+import com.zane.smapiinstaller.MobileNavigationDirections;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.dto.ModUpdateCheckRequestDto;
import com.zane.smapiinstaller.dto.ModUpdateCheckResponseDto;
+import com.zane.smapiinstaller.dto.Tuple2;
import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.FileUtils;
-import com.zane.smapiinstaller.utils.JsonUtil;
import com.zane.smapiinstaller.utils.JsonCallback;
+import com.zane.smapiinstaller.utils.JsonUtil;
import com.zane.smapiinstaller.utils.VersionUtil;
import org.apache.commons.lang3.StringUtils;
@@ -35,17 +36,22 @@ import org.zeroturnaround.zip.ZipUtil;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
-
-import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import androidx.navigation.NavController;
+import androidx.navigation.Navigation;
/**
* Mod资源管理器
+ *
* @author Zane
*/
public class ModAssetsManager {
@@ -68,7 +74,7 @@ public class ModAssetsManager {
*/
public static ModManifestEntry findFirstModIf(Predicate filter) {
ConcurrentLinkedQueue files = Queues.newConcurrentLinkedQueue();
- files.add(new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH));
+ files.add(new File(FileUtils.getStadewValleyBasePath(), Constants.MOD_PATH));
do {
File currentFile = files.poll();
if (currentFile != null && currentFile.exists()) {
@@ -115,7 +121,7 @@ public class ModAssetsManager {
*/
public static List findAllInstalledMods(boolean ignoreDisabledMod) {
ConcurrentLinkedQueue files = Queues.newConcurrentLinkedQueue();
- files.add(new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH));
+ files.add(new File(FileUtils.getStadewValleyBasePath(), Constants.MOD_PATH));
List mods = new ArrayList<>(30);
do {
File currentFile = files.poll();
@@ -162,7 +168,7 @@ public class ModAssetsManager {
if (modManifestEntries == null) {
return false;
}
- File modFolder = new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH);
+ File modFolder = new File(FileUtils.getStadewValleyBasePath(), Constants.MOD_PATH);
ImmutableListMultimap installedModMap = Multimaps.index(findAllInstalledMods(), ModManifestEntry::getUniqueID);
for (ModManifestEntry mod : modManifestEntries) {
if (installedModMap.containsKey(mod.getUniqueID()) || installedModMap.containsKey(mod.getUniqueID().replace("ZaneYork.CustomLocalization", "SMAPI.CustomLocalization"))) {
@@ -253,18 +259,28 @@ public class ModAssetsManager {
* @param returnCallback 回调函数
*/
private void checkUnsatisfiedDependencies(ImmutableListMultimap installedModMap, Consumer returnCallback) {
- List dependencyErrors = installedModMap.values().stream()
+ List>> dependencyErrors = installedModMap.values().stream()
.map(mod -> checkModDependencyError(mod, installedModMap))
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (dependencyErrors.size() > 0) {
DialogUtils.showConfirmDialog(root, R.string.error,
- Joiner.on(";").join(dependencyErrors),
- R.string.continue_text, R.string.abort,
+ dependencyErrors.stream().map(Tuple2::getFirst).collect(Collectors.joining(";")),
+ R.string.continue_text, R.string.menu_download,
((dialog, which) -> {
if (which == DialogAction.POSITIVE) {
returnCallback.accept(true);
} else {
+ List list = dependencyErrors.stream()
+ .map(Tuple2::getSecond).flatMap(Collection::stream).distinct()
+ .map(item -> {
+ ModUpdateCheckRequestDto.ModInfo modInfo = new ModUpdateCheckRequestDto.ModInfo();
+ modInfo.setId(item);
+ return modInfo;
+ })
+ .distinct()
+ .collect(Collectors.toList());
+ redirectModDownload(list);
returnCallback.accept(false);
}
}));
@@ -280,17 +296,26 @@ public class ModAssetsManager {
* @param returnCallback 回调函数
*/
private void checkContentpacks(ImmutableListMultimap installedModMap, Consumer returnCallback) {
- List dependencyErrors = installedModMap.values().stream()
+ List> dependencyErrors = installedModMap.values().stream()
.map(mod -> checkContentPackDependencyError(mod, installedModMap))
.filter(Objects::nonNull).collect(Collectors.toList());
if (!dependencyErrors.isEmpty()) {
DialogUtils.showConfirmDialog(root, R.string.error,
- Joiner.on(";").join(dependencyErrors),
- R.string.continue_text, R.string.abort,
+ dependencyErrors.stream().map(Tuple2::getFirst).collect(Collectors.joining(";")),
+ R.string.continue_text, R.string.menu_download,
((dialog, which) -> {
if (which == DialogAction.POSITIVE) {
returnCallback.accept(true);
} else {
+ List list = dependencyErrors.stream()
+ .map(item -> {
+ ModUpdateCheckRequestDto.ModInfo modInfo = new ModUpdateCheckRequestDto.ModInfo();
+ modInfo.setId(item.getSecond());
+ return modInfo;
+ })
+ .distinct()
+ .collect(Collectors.toList());
+ redirectModDownload(list);
returnCallback.accept(false);
}
}));
@@ -299,6 +324,45 @@ public class ModAssetsManager {
}
}
+ private void redirectModDownload(List list) {
+ ModUpdateCheckRequestDto requestDto = new ModUpdateCheckRequestDto(list);
+ try {
+ requestDto.setIncludeExtendedMetadata(true);
+ OkGo.>post(Constants.UPDATE_CHECK_SERVICE_URL)
+ .upJson(JsonUtil.toJson(requestDto))
+ .execute(new JsonCallback>(new TypeReference>() {
+ }) {
+ @Override
+ public void onError(Response> response) {
+ super.onError(response);
+ }
+
+ @Override
+ public void onSuccess(Response> response) {
+ List checkResponseDtos = response.body();
+ if (checkResponseDtos != null) {
+ List list = checkResponseDtos.stream()
+ .filter(dto -> dto.getMetadata() != null && dto.getMetadata().getMain() != null && StringUtils.isNoneBlank(dto.getMetadata().getMain().getUrl()))
+ .collect(Collectors.toList());
+ try {
+ if (list.size() > 0) {
+ for (ModUpdateCheckResponseDto dto : list) {
+ dto.setSuggestedUpdate(new ModUpdateCheckResponseDto.UpdateInfo(dto.getMetadata().getMain().getVersion(), dto.getMetadata().getMain().getUrl()));
+ }
+ NavController controller = Navigation.findNavController(CommonLogic.getActivityFromView(root), R.id.nav_host_fragment);
+ controller.navigate(MobileNavigationDirections.actionNavAnyToModUpdateFragment(JsonUtil.toJson(list)));
+ }
+ } catch (Exception e) {
+ Crashes.trackError(e);
+ }
+ }
+ }
+ });
+ } catch (Exception e) {
+ Crashes.trackError(e);
+ }
+ }
+
public void checkModUpdate(Consumer> callback) {
if (checkUpdating.get()) {
return;
@@ -344,13 +408,15 @@ public class ModAssetsManager {
}
}
- private String checkModDependencyError(ModManifestEntry mod, ImmutableListMultimap installedModMap) {
+ private Tuple2> checkModDependencyError(ModManifestEntry mod, ImmutableListMultimap installedModMap) {
if (mod.getDependencies() != null) {
List unsatisfiedDependencies = mod.getDependencies().stream()
.filter(dependency -> isDependencyIsExist(dependency, installedModMap))
.collect(Collectors.toList());
if (unsatisfiedDependencies.size() > 0) {
- return root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), Joiner.on(",").join(Lists.transform(unsatisfiedDependencies, ModManifestEntry::getUniqueID)));
+ return new Tuple2<>(
+ root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), Joiner.on(",").join(Lists.transform(unsatisfiedDependencies, ModManifestEntry::getUniqueID))),
+ unsatisfiedDependencies.stream().map(ModManifestEntry::getUniqueID).collect(Collectors.toList()));
}
}
return null;
@@ -386,7 +452,7 @@ public class ModAssetsManager {
return false;
}
- private String checkContentPackDependencyError(ModManifestEntry mod, ImmutableListMultimap installedModMap) {
+ private Tuple2 checkContentPackDependencyError(ModManifestEntry mod, ImmutableListMultimap installedModMap) {
ModManifestEntry dependency = mod.getContentPackFor();
if (dependency != null) {
if (dependency.getIsRequired() != null && !dependency.getIsRequired()) {
@@ -403,7 +469,7 @@ public class ModAssetsManager {
}
}
if (entries.size() != 1) {
- return root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), dependency.getUniqueID());
+ return new Tuple2<>(root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), dependency.getUniqueID()), dependency.getUniqueID());
}
String version = entries.get(0).getVersion();
if (!StringUtils.isBlank(version)) {
@@ -411,7 +477,7 @@ public class ModAssetsManager {
return null;
}
if (VersionUtil.compareVersion(version, dependency.getMinimumVersion()) < 0) {
- return root.getContext().getString(R.string.error_depends_on_mod_version, mod.getUniqueID(), dependency.getUniqueID(), dependency.getMinimumVersion());
+ return new Tuple2<>(root.getContext().getString(R.string.error_depends_on_mod_version, mod.getUniqueID(), dependency.getUniqueID(), dependency.getMinimumVersion()), dependency.getUniqueID());
}
}
return null;
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigEditFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigEditFragment.java
index 7a08102..662337a 100644
--- a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigEditFragment.java
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigEditFragment.java
@@ -7,12 +7,19 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.webkit.WebChromeClient;
+import android.webkit.WebResourceError;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import com.hjq.language.LanguagesManager;
import com.zane.smapiinstaller.BuildConfig;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.databinding.FragmentConfigEditBinding;
+import com.zane.smapiinstaller.dto.WebViewObject;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.FileUtils;
@@ -21,6 +28,7 @@ import com.zane.smapiinstaller.utils.JsonUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
+import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
@@ -35,30 +43,72 @@ public class ConfigEditFragment extends Fragment {
private String configPath;
private FragmentConfigEditBinding binding;
+ private WebViewObject webObject = null;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentConfigEditBinding.inflate(inflater, container, false);
+ initView();
+ binding.buttonConfigCancel.setOnClickListener(v -> onConfigCancel());
+ binding.buttonConfigSave.setOnClickListener(v -> onConfigSave());
+ binding.buttonLogParser.setOnClickListener(v -> onLogParser());
+ return binding.getRoot();
+ }
+
+ private void initView() {
CommonLogic.doOnNonNull(this.getArguments(), arguments -> {
ConfigEditFragmentArgs args = ConfigEditFragmentArgs.fromBundle(arguments);
editable = args.getEditable();
if (!editable) {
- binding.editTextConfigEdit.setKeyListener(null);
binding.buttonConfigSave.setVisibility(View.INVISIBLE);
binding.buttonConfigCancel.setVisibility(View.INVISIBLE);
binding.buttonLogParser.setVisibility(View.VISIBLE);
}
+ binding.editTextConfigWebview.getSettings().setJavaScriptEnabled(true);
+ binding.editTextConfigWebview.setWebChromeClient(new WebChromeClient());
+ binding.editTextConfigWebview.setWebViewClient(new WebViewClient());
configPath = args.getConfigPath();
File file = new File(configPath);
if (file.exists() && file.length() < Constants.TEXT_FILE_OPEN_SIZE_LIMIT) {
String fileText = FileUtils.getFileText(file);
if (fileText != null) {
- binding.editTextConfigEdit.setText(fileText);
+ binding.scrollView.post(() -> {
+ CommonLogic.doOnNonNull(this.getContext(), (context -> {
+ int height = (int) (binding.scrollView.getMeasuredHeight() / context.getResources().getDisplayMetrics().density * 0.95);
+ String lang = LanguagesManager.getAppLanguage(context).getCountry();
+ switch (lang){
+ case "zh":
+ lang = "zh-CN";
+ break;
+ case "fr":
+ lang = "fr-FR";
+ break;
+ case "pt":
+ lang = "pt-BR";
+ break;
+ default:
+ break;
+ }
+ if (editable) {
+ webObject = new WebViewObject(fileText, "code", lang, true, height);
+ binding.editTextConfigWebview.addJavascriptInterface(webObject, "webObject");
+ } else {
+ webObject = new WebViewObject(fileText, "text-plain", lang, false, height);
+ binding.editTextConfigWebview.addJavascriptInterface(webObject, "webObject");
+ }
+ String assetText = FileUtils.getAssetText(context, "jsoneditor/editor.html");
+ if (assetText != null) {
+ binding.editTextConfigWebview.loadDataWithBaseURL(
+ "file:///android_asset/jsoneditor/",
+ assetText,
+ "text/html",
+ "utf-8", "");
+ }
+ }));
+ });
}
} else {
- binding.editTextConfigEdit.setText("");
- binding.editTextConfigEdit.setKeyListener(null);
DialogUtils.showConfirmDialog(binding.getRoot(), R.string.error, this.getString(R.string.text_too_large), R.string.open_with, R.string.cancel, ((dialog, which) -> {
if (which == DialogAction.POSITIVE) {
Intent intent = new Intent("android.intent.action.VIEW");
@@ -79,9 +129,6 @@ public class ConfigEditFragment extends Fragment {
}));
}
});
- binding.buttonConfigCancel.setOnClickListener(v -> onConfigCancel());
- binding.buttonConfigSave.setOnClickListener(v -> onConfigSave());
- return binding.getRoot();
}
@Override
@@ -92,11 +139,14 @@ public class ConfigEditFragment extends Fragment {
private void onConfigSave() {
try {
- JsonUtil.checkJson(binding.editTextConfigEdit.getText().toString());
- FileOutputStream outputStream = new FileOutputStream(configPath);
- try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) {
- outputStreamWriter.write(binding.editTextConfigEdit.getText().toString());
- outputStreamWriter.flush();
+ if(webObject != null) {
+ binding.editTextConfigWebview.loadUrl("javascript:getJson()");
+ JsonUtil.checkJson(webObject.getText());
+ FileOutputStream outputStream = new FileOutputStream(configPath);
+ try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) {
+ outputStreamWriter.write(webObject.getText());
+ outputStreamWriter.flush();
+ }
}
} catch (Exception e) {
DialogUtils.showAlertDialog(getView(), R.string.error, e.getLocalizedMessage());
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigFragment.java
index 79641b8..b41b5d9 100644
--- a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigFragment.java
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigFragment.java
@@ -39,8 +39,13 @@ public class ConfigFragment extends Fragment {
return false;
});
binding.viewModList.addItemDecoration(new DividerItemDecoration(binding.viewModList.getContext(), DividerItemDecoration.VERTICAL));
- binding.buttonSearch.addTextChangedListener((TextChangedWatcher) (s, start, before, count) -> configViewModel.filter(s));
- binding.buttonSortBy.setOnClickListener(v -> onSortByClick());
+ binding.buttonSearch.addTextChangedListener(new TextChangedWatcher() {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ configViewModel.filter(s);
+ }
+ });
+ binding.buttonSortBy.setOnClickListener(this::onSortByClick);
return binding.getRoot();
}
@@ -50,7 +55,7 @@ public class ConfigFragment extends Fragment {
binding = null;
}
- void onSortByClick() {
+ public void onSortByClick(View v) {
int index = 0;
switch (configViewModel.getSortBy()) {
case "Name asc":
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/help/HelpFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/help/HelpFragment.java
index 51f3f99..d29e8b2 100644
--- a/app/src/main/java/com/zane/smapiinstaller/ui/help/HelpFragment.java
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/help/HelpFragment.java
@@ -1,7 +1,6 @@
package com.zane.smapiinstaller.ui.help;
import android.os.Bundle;
-import android.os.Environment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -12,6 +11,7 @@ import com.zane.smapiinstaller.databinding.FragmentHelpBinding;
import com.zane.smapiinstaller.entity.HelpItemList;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.logic.UpdatableListManager;
+import com.zane.smapiinstaller.utils.FileUtils;
import java.io.File;
@@ -65,7 +65,7 @@ public class HelpFragment extends Fragment {
private void showLog() {
CommonLogic.doOnNonNull(this.getView(), view -> {
NavController controller = Navigation.findNavController(view);
- File logFile = new File(Environment.getExternalStorageDirectory(), Constants.LOG_PATH);
+ File logFile = new File(FileUtils.getStadewValleyBasePath(), Constants.LOG_PATH);
if (logFile.exists()) {
MobileNavigationDirections.ActionNavAnyToConfigEditFragment action = HelpFragmentDirections.actionNavAnyToConfigEditFragment(logFile.getAbsolutePath());
action.setEditable(false);
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/install/InstallFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/install/InstallFragment.java
index 8664f45..28afe6f 100644
--- a/app/src/main/java/com/zane/smapiinstaller/ui/install/InstallFragment.java
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/install/InstallFragment.java
@@ -3,7 +3,6 @@ package com.zane.smapiinstaller.ui.install;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
-import android.os.Environment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -21,6 +20,7 @@ import com.zane.smapiinstaller.logic.ModAssetsManager;
import com.zane.smapiinstaller.ui.main.MainTabsFragmentDirections;
import com.zane.smapiinstaller.utils.ConfigUtils;
import com.zane.smapiinstaller.utils.DialogUtils;
+import com.zane.smapiinstaller.utils.FileUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
@@ -55,7 +55,7 @@ public class InstallFragment extends Fragment {
binding.layoutAdvInstall.setVisibility(View.VISIBLE);
}
try {
- String firstLine = Files.asCharSource(new File(Environment.getExternalStorageDirectory(), Constants.LOG_PATH), StandardCharsets.UTF_8).readFirstLine();
+ String firstLine = Files.asCharSource(new File(FileUtils.getStadewValleyBasePath(), Constants.LOG_PATH), StandardCharsets.UTF_8).readFirstLine();
if (StringUtils.isNoneBlank(firstLine)) {
String versionString = RegExUtils.removePattern(firstLine, "\\[.+\\]\\s+");
versionString = RegExUtils.removePattern(versionString, "\\s+with.+");
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/update/ModUpdateAdapter.java b/app/src/main/java/com/zane/smapiinstaller/ui/update/ModUpdateAdapter.java
index 152ea12..b2a55ae 100644
--- a/app/src/main/java/com/zane/smapiinstaller/ui/update/ModUpdateAdapter.java
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/update/ModUpdateAdapter.java
@@ -70,6 +70,13 @@ public class ModUpdateAdapter extends RecyclerView.Adapter binding.textViewModVersion.setText(
+ activity.getString(R.string.mod_version_update_text, updateInfo.getSuggestedUpdate().getVersion(), updateInfo.getSuggestedUpdate().getVersion())
+ ));
+ }
}
public ViewHolder(View view) {
diff --git a/app/src/main/java/com/zane/smapiinstaller/utils/FileUtils.java b/app/src/main/java/com/zane/smapiinstaller/utils/FileUtils.java
index 251626a..8903d73 100644
--- a/app/src/main/java/com/zane/smapiinstaller/utils/FileUtils.java
+++ b/app/src/main/java/com/zane/smapiinstaller/utils/FileUtils.java
@@ -279,7 +279,7 @@ public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
* @return 移除前缀后的路径
*/
public static String toPrettyPath(String path) {
- return StringUtils.removeStart(path, Environment.getExternalStorageDirectory().getAbsolutePath());
+ return StringUtils.removeStart(path, FileUtils.getStadewValleyBasePath());
}
/**
@@ -310,4 +310,8 @@ public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
}
return null;
}
+
+ public static String getStadewValleyBasePath() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath();
+ }
}
diff --git a/app/src/main/java/com/zane/smapiinstaller/utils/function/TextChangedWatcher.java b/app/src/main/java/com/zane/smapiinstaller/utils/function/TextChangedWatcher.java
index 28ab2bd..1f29592 100644
--- a/app/src/main/java/com/zane/smapiinstaller/utils/function/TextChangedWatcher.java
+++ b/app/src/main/java/com/zane/smapiinstaller/utils/function/TextChangedWatcher.java
@@ -5,8 +5,7 @@ import android.text.Editable;
/**
* @author Zane
*/
-@FunctionalInterface
-public interface TextChangedWatcher extends android.text.TextWatcher {
+public abstract class TextChangedWatcher implements android.text.TextWatcher {
/**
* Do nothing
* @param s origin string
@@ -15,7 +14,7 @@ public interface TextChangedWatcher extends android.text.TextWatcher {
* @param after modified string
*/
@Override
- default void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
/**
* Text changed event
@@ -24,12 +23,12 @@ public interface TextChangedWatcher extends android.text.TextWatcher {
* @param count modify count
*/
@Override
- void onTextChanged(CharSequence s, int start, int before, int count);
+ public abstract void onTextChanged(CharSequence s, int start, int before, int count);
/**
* Do nothing
* @param s target view
*/
@Override
- default void afterTextChanged(Editable s) {}
+ public void afterTextChanged(Editable s) {}
}
diff --git a/app/src/main/res/layout/fragment_config_edit.xml b/app/src/main/res/layout/fragment_config_edit.xml
index 3d38a29..ea958a9 100644
--- a/app/src/main/res/layout/fragment_config_edit.xml
+++ b/app/src/main/res/layout/fragment_config_edit.xml
@@ -4,6 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
+ android:layout_height="wrap_content"/>
重寫增強
設定應用名稱
+ 安卓11及以後版本需要授權文件管理權限以完成框架安裝,是否繼續?
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index 7782b89..6c0983a 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -84,4 +84,5 @@
需要开启未知源权限以安装Mod框架,是否前往设置开启?
重写增强
设置应用名称
+ 安卓11及以后版本需要授权文件管理权限以完成框架安装,是否继续?
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 382e09c..59621dc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -88,4 +88,5 @@
Needs to switch unknown source permission on to install mod framework, open settings?
Rewrite Missing
Patched App Name
+ Need all files access permission on Android Q or above to install mod framework, continue?
diff --git a/build.gradle b/build.gradle
index 7d8d913..81843b9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,7 +8,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.0.1'
+ classpath 'com.android.tools.build:gradle:4.0.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0"
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'