├── .gitignore ├── .ocp-indent ├── LICENSE ├── Makefile ├── README.md ├── _tags ├── factorio.js ├── factorio.ml ├── factoriojs.ml ├── html.ml ├── html.mli ├── index.html ├── recipes.ml └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | /_build/ 2 | -------------------------------------------------------------------------------- /.ocp-indent: -------------------------------------------------------------------------------- 1 | normal 2 | with = 2 3 | match_clause = 4 4 | syntax = mll -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 DooMeeR 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 DooMeeR 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, subject to 9 | # the following conditions: 10 | 11 | # The above copyright notice and this permission notice shall be 12 | # included in all copies or substantial portions of the Software. 13 | 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | OCAMLBUILD := ocamlbuild 23 | OCAMLBUILD := $(OCAMLBUILD) -no-links 24 | OCAMLBUILD := $(OCAMLBUILD) -use-ocamlfind 25 | OCAMLBUILD := $(OCAMLBUILD) -tag annot 26 | 27 | CLIENT = factoriojs.byte 28 | CLIENTJS = factorio.js 29 | 30 | default: $(CLIENTJS) 31 | 32 | $(CLIENTJS): _build/$(CLIENT) 33 | js_of_ocaml _build/$(CLIENT) -o $(CLIENTJS) 34 | 35 | _build/$(CLIENT): factorio.ml factoriojs.ml recipes.ml html.ml html.mli 36 | $(OCAMLBUILD) $(CLIENT) 37 | 38 | clean: 39 | rm -rf _build 40 | 41 | .PHONY: default clean 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Factorio Planner 2 | 3 | Factorio Planner helps you plan your factories by computing how many 4 | resources and machines you need to produce a given throughput of 5 | resources. Try it here: http://doomeer.com/factorio 6 | 7 | ## Current Version 8 | 9 | Recipes for Factorio version 0.17. 10 | 11 | ## Architecture 12 | 13 | Factorio Planner is written in OCaml, but is then compiled to Javascript 14 | using js_of_ocaml. 15 | 16 | * `index.html` is the HTML page which loads the Javascript. 17 | * `factorio.js` is the Javascript compiled from the OCaml code. 18 | It is included in the repository in case you don't want to compile it. 19 | * `factoriojs.ml` is the main file, which implements the Web interface. 20 | * `factorio.ml` contains type definitions and the code to compute 21 | ressource summaries. 22 | * `recipes.ml` contains maker and ressource definitions. 23 | This is the file you want to modify to change ingredients or add 24 | new ressources. 25 | * `html.mli` is the interface to `html.ml`, which provides helpers 26 | for Web interfaces. 27 | 28 | ## Install OCaml 29 | 30 | To compile Factorio Planner you need to install the OCaml compiler 31 | as well as js_of_ocaml. 32 | 33 | ### Debian or Ubuntu: 34 | 35 | sudo apt-get install ocaml ocaml-findlib camlp4 js-of-ocaml 36 | 37 | Alternatively, you can use OPAM (OCaml Package Manager, https://opam.ocaml.org): 38 | 39 | sudo apt-get install opam 40 | opam init 41 | opam switch 4.03.0 42 | opam install js_of_ocaml js_of_ocaml-camlp4 43 | 44 | ### Windows: 45 | 46 | Download OCaml for Windows here: https://fdopen.github.io/opam-repository-mingw/installation 47 | Open the OCaml terminal that the installation produces and run the following: 48 | 49 | opam init 50 | opam switch 4.03.0 51 | opam install js_of_ocaml js_of_ocaml-camlp4 52 | 53 | ## Compile Factorio Planner 54 | 55 | Just run: 56 | 57 | make 58 | 59 | This will compile the project and regenerate `factorio.js`. 60 | 61 | ## License 62 | 63 | Factorio Planner is released under the MIT license. 64 | See the `LICENSE` file. 65 | -------------------------------------------------------------------------------- /_tags: -------------------------------------------------------------------------------- 1 | true: debug 2 | true: package(js_of_ocaml) 3 | true: package(js_of_ocaml-camlp4), syntax(camlp4o) 4 | -------------------------------------------------------------------------------- /factorio.js: -------------------------------------------------------------------------------- 1 | // Generated by js_of_ocaml 2.8.4 2 | (function(ai){"use strict";var 3 | eL="%Li",a3=254,S=255,e3=224,fj=2147483647,fa="Invalid_argument",fu='"',al=16777215,br=616096519,bq=250,ft="<",bS="Basic Oil Processing",bu=1024,j=0.5,c1=65599,eV="jsError",e$="%ni",eK="input",fi="r",fh=246,cS="Solid Fuel",a2=862212904,dd=512,eU="End_of_file",fs=120,eT="Failure",fA="infinity",dc="Steel Furnace",c0="Chemical Plant",fJ="result",db="Advanced Oil Processing + Cracking",f="",fg="Stack_overflow",X=128,eS="Shared Resources",e2="goals",v=100,W="0",Q=248,fz="Offshore Pump",ff="Not_found",e9="Sys_blocked_io",e_="p",ar="outputh1",fy=103,da="oilprocessing",fI="fd ",fH=1023,e1="Match_failure",cQ="Rocket Silo",fG="Centrifuge",E="camlinternalFormat.ml",fr="Division_by_zero",c$=">",aL=1e3,cR="Stone Furnace",fe="setvisible",bW=500,eQ=-34,eR="Sys_error",c3="Advanced Oil Processing",fq="x",az=".",e0="Battery_MK2",aK="+",cZ=3.2,bt=65535,e8=-97,bT=1073741823,cV="timeunit",e7="%u",fp="%d",eZ="%li",e6=110,fd="Energy_shield_MK2",e5="%)",c5=-184774442,fo=57343,fx=785140586,cU=127,fn=200,c4="Burner Mining Drill",bs=997943714,c_="Assembling Machine 2",e4="2",fm=982028505,ak=" ",c6="Assembling Machine 3",fF="1",c9="e",eJ="Undefined_recursive_module",eY=")",cY="Pumpjack",c8="button",a0=-132619923,bR="Electric Mining Drill",eX=-65,aM="-",cT="nan",cX="solidfuel",fl=-48,fc=" \xc3\x97 ",a1="setting",eP=240,cW=" : file already exists",fk=2048,fw=56320,fE="b",fb=32752,eW="/",fv="Assert_failure",eI=3600,eO="0x",bV=114,fD="Power_armor_MK2",eH="%i",fC="Out_of_memory",c2=767175342,bU="Electric Furnace",eM="Assembling Machine 1",eN=32768,fB="index out of bounds",c7="Petroleum Gas";function 4 | dm(d,e,c){var 5 | b=new 6 | Array(c);for(var 7 | a=0;a=b.l||b.t==2&&c>=b.c.length)){b.c=d.t==4?dk(d.c,e,c):e==0&&d.c.length==c?d.c:d.c.substr(e,c);b.t=b.c.length==b.l?0:2}else 18 | if(b.t==2&&f==b.c.length){b.c+=d.t==4?dk(d.c,e,c):e==0&&d.c.length==c?d.c:d.c.substr(e,c);b.t=b.c.length==b.l?0:2}else{if(b.t!=4)bY(b);var 19 | g=d.c,h=b.c;if(d.t==4)if(f<=e)for(var 20 | a=0;a=0;a--)h[f+a]=g[e+a];else{var 23 | i=Math.min(c,g.length-e);for(var 24 | a=0;a>=1;if(b==0)return c;a+=a;d++;if(d==9)a.slice(0,1)}}function 35 | as(a){if(a.t==2)a.c+=a5(a.l-a.c.length,"\0");else 36 | a.c=dk(a.c,0,a.c.length);a.t=0}function 37 | fP(a){if(a.length<24){for(var 38 | b=0;bcU)return false;return true}else 39 | return!/[^\x00-\x7f]/.test(a)}function 40 | r8(e){for(var 41 | k=f,c=f,h,g,i,a,b=0,j=e.length;bdd){c.substr(0,1);k+=c;c=f;k+=e.slice(b,d)}else 43 | c+=e.slice(b,d);if(d==j)break;b=d}a=1;if(++b=55295&&a<57344)a=2}else{a=3;if(++b1114111)a=3}}}}}if(a<4){b-=a;c+="\ufffd"}else 44 | if(a>bt)c+=String.fromCharCode(55232+(a>>10),fw+(a&fH));else 45 | c+=String.fromCharCode(a);if(c.length>bu){c.substr(0,1);k+=c;c=f}}return k+c}function 46 | r7(a){switch(a.t){case 47 | 9:return a.c;default:as(a);case 48 | 0:if(fP(a.c)){a.t=9;return a.c}a.t=8;case 49 | 8:return r8(a.c)}}function 50 | $(c,a,b){this.t=c;this.c=a;this.l=b}$.prototype.toString=function(){return r7(this)};function 51 | a(a){return new 52 | $(0,a,a.length)}function 53 | dj(c,b){rY(c,a(b))}var 54 | G=[0];function 55 | aO(a){dj(G.Invalid_argument,a)}function 56 | rk(){aO(fB)}function 57 | T(a,b){if(b>>>0>=a.length-1)rk();return a}function 58 | rl(a){if(isFinite(a)){if(Math.abs(a)>=2.22507385850720138e-308)return 0;if(a!=0)return 1;return 2}return isNaN(a)?4:3}function 59 | rC(a,b){var 60 | c=a[3]<<16,d=b[3]<<16;if(c>d)return 1;if(cb[2])return 1;if(a[2]b[1])return 1;if(a[1]b.c?1:0}function 63 | bw(a,b,h){var 64 | d=[];for(;;){if(!(h&&a===b))if(a 65 | instanceof 66 | $)if(b 67 | instanceof 68 | $){if(a!==b){var 69 | c=r2(a,b);if(c!=0)return c}}else 70 | return 1;else 71 | if(a 72 | instanceof 73 | Array&&a[0]===(a[0]|0)){var 74 | e=a[0];if(e===a3)e=0;if(e===bq){a=a[1];continue}else 75 | if(b 76 | instanceof 77 | Array&&b[0]===(b[0]|0)){var 78 | f=b[0];if(f===a3)f=0;if(f===bq){b=b[1];continue}else 79 | if(e!=f)return e1)d.push(a,b,1)}}else 86 | return 1}else 87 | if(b 88 | instanceof 89 | $||b 90 | instanceof 91 | Array&&b[0]===(b[0]|0))return-1;else 92 | if(typeof 93 | a!="number"&&a&&a.compare)return a.compare(b,h);else 94 | if(typeof 95 | a=="function")aO("equal: functional value");else{if(ab)return 1;if(a!=b){if(!h)return NaN;if(a==a)return 1;if(b==b)return-1}}if(d.length==0)return 0;var 96 | g=d.pop();b=d.pop();a=d.pop();if(g+10)if(c==0&&(b>=a.l||a.t==2&&b>=a.c.length))if(d==0){a.c=f;a.t=2}else{a.c=a5(b,String.fromCharCode(d));a.t=b==a.l?0:2}else{if(a.t!=4)bY(a);for(b+=c;cb)return 1;if(a===a)return 1;if(b===b)return-1;return 0}function 102 | rn(a){dj(G.Failure,a)}function 103 | bX(a){if((a.t&6)!=0)as(a);return a.c}function 104 | rq(a){var 105 | b;a=bX(a);b=+a;if(a.length>0&&b===b)return b;a=a.replace(/_/g,f);b=+a;if(a.length>0&&b===b||/^[+-]?nan$/i.test(a))return b;var 106 | c=/^ *([+-]?)0x([0-9a-f]+)\.?([0-9a-f]*)p([+-]?[0-9]+)/i.exec(a);if(c){var 107 | d=c[3].replace(/0+$/,f),g=parseInt(c[1]+c[2]+d,16),e=(c[4]|0)-4*d.length;b=g*Math.pow(2,e);return b}if(/^\+?inf(inity)?$/i.test(a))return Infinity;if(/^-inf(inity)?$/i.test(a))return-Infinity;rn("float_of_string")}function 108 | di(d){d=bX(d);var 109 | e=d.length;if(e>31)aO("format_int: format too long");var 110 | a={justify:aK,signstyle:aM,filler:ak,alternate:false,base:0,signedconv:false,width:0,uppercase:false,sign:1,prec:-1,conv:"f"};for(var 111 | c=0;c=0&&b<=9){a.width=a.width*10+b;c++}c--;break;case".":a.prec=0;c++;while(b=d.charCodeAt(c)-48,b>=0&&b<=9){a.prec=a.prec*10+b;c++}c--;case"d":case"i":a.signedconv=true;case"u":a.base=10;break;case"x":a.base=16;break;case"X":a.base=16;a.uppercase=true;break;case"o":a.base=8;break;case"e":case"f":case"g":a.signedconv=true;a.conv=b;break;case"E":case"F":case"G":a.signedconv=true;a.uppercase=true;a.conv=b.toLowerCase();break}}return a}function 113 | de(b,g){if(b.uppercase)g=g.toUpperCase();var 114 | e=g.length;if(b.signedconv&&(b.sign<0||b.signstyle!=aM))e++;if(b.alternate){if(b.base==8)e+=1;if(b.base==16)e+=2}var 115 | c=f;if(b.justify==aK&&b.filler==ak)for(var 116 | d=e;d=1e+21||c.toFixed(0).length>d){var 126 | b=h-1;while(a.charAt(b)==W)b--;if(a.charAt(b)==az)b--;a=a.slice(0,b+1)+a.slice(h);b=a.length;if(a.charAt(b-3)==c9)a=a.slice(0,b-1)+W+a.slice(b-1);break}else{var 127 | f=d;if(g<0){f-=g+1;a=c.toFixed(f)}else 128 | while(a=c.toFixed(f),a.length>d+1)f--;if(f){var 129 | b=a.length-1;while(a.charAt(b)==W)b--;if(a.charAt(b)==az)b--;a=a.slice(0,b+1)}}break}return de(e,a)}function 130 | bZ(e,c){if(bX(e)==fp)return a(f+c);var 131 | b=di(e);if(c<0)if(b.signedconv){b.sign=-1;c=-c}else 132 | c>>>=0;var 133 | d=c.toString(b.base);if(b.prec>=0){b.filler=ak;var 134 | g=b.prec-d.length;if(g>0)d=a5(g,W)+d}return de(b,d)}var 135 | rW=0;function 136 | aB(){return rW++}function 137 | fL(a,b){return+(bw(a,b,false)>=0)}if(!Math.imul)Math.imul=function(b,a){a|=0;return((b>>16)*a<<16)+(b&bt)*a|0};var 138 | by=Math.imul;function 139 | am(b,a){a=by(a,3432918353|0);a=a<<15|a>>>32-15;a=by(a,461845907);b^=a;b=b<<13|b>>>32-13;return(b+(b<<2)|0)+(3864292196|0)|0}function 140 | rw(b,a){var 141 | d=a[1]|a[2]<<24,c=a[2]>>>8|a[3]<<16;b=am(b,c^d);return b}var 142 | sb=Math.log2&&Math.log2(1.12355820928894744e+307)==1020;function 143 | sa(a){if(sb)return Math.floor(Math.log2(a));var 144 | b=0;if(a==0)return-Infinity;if(a>=1)while(a>=2){a/=2;b++}else 145 | while(a<1){a*=2;b--}return b}function 146 | fM(a){if(!isFinite(a)){if(isNaN(a))return[S,1,0,fb];return a>0?[S,0,0,fb]:[S,0,0,65520]}var 147 | f=a==0&&1/a==-Infinity?eN:a>=0?0:eN;if(f)a=-a;var 148 | b=sa(a)+fH;if(b<=0){b=0;a/=Math.pow(2,-1026)}else{a/=Math.pow(2,b-1027);if(a<16){a*=2;b-=1}if(b==0)a/=2}var 149 | d=Math.pow(2,24),c=a|0;a=(a-c)*d;var 150 | e=a|0;a=(a-e)*d;var 151 | g=a|0;c=c&15|f|b<<4;return[S,g,e,c]}function 152 | rv(a,e){var 153 | b=fM(e),d=b[1]|b[2]<<24,c=b[2]>>>8|b[3]<<16;a=am(a,d);a=am(a,c);return a}function 154 | ry(d,b){var 155 | e=b.length,a,c;for(a=0;a+4<=e;a+=4){c=b[a]|b[a+1]<<8|b[a+2]<<16|b[a+3]<<24;d=am(d,c)}c=0;switch(e&3){case 156 | 3:c=b[a+2]<<16;case 157 | 2:c|=b[a+1]<<8;case 158 | 1:c|=b[a];d=am(d,c)}d^=e;return d}function 159 | rz(d,b){var 160 | e=b.length,a,c;for(a=0;a+4<=e;a+=4){c=b.charCodeAt(a)|b.charCodeAt(a+1)<<8|b.charCodeAt(a+2)<<16|b.charCodeAt(a+3)<<24;d=am(d,c)}c=0;switch(e&3){case 161 | 3:c=b.charCodeAt(a+2)<<16;case 162 | 2:c|=b.charCodeAt(a+1)<<8;case 163 | 1:c|=b.charCodeAt(a);d=am(d,c)}d^=e;return d}function 164 | rx(a,b){switch(b.t&6){default:as(b);case 165 | 0:a=rz(a,b.c);break;case 166 | 2:a=ry(a,b.c)}return a}function 167 | ru(a){a^=a>>>16;a=by(a,2246822507|0);a^=a>>>13;a=by(a,3266489909|0);a^=a>>>16;return a}var 168 | fK=256;function 169 | rt(j,k,m,l){var 170 | f,g,h,d,c,b,a,e,i;d=k;if(d<0||d>fK)d=fK;c=j;b=m;f=[l];g=0;h=1;while(g0){a=f[g++];if(a 171 | instanceof 172 | Array&&a[0]===(a[0]|0))switch(a[0]){case 173 | 248:b=am(b,a[2]);c--;break;case 174 | 250:f[--g]=a[1];break;case 175 | 255:b=rw(b,a);c--;break;default:var 176 | n=a.length-1<<10|a[0];b=am(b,n);for(e=1,i=a.length;e=d)break;f[h++]=a[e]}break}else 177 | if(a 178 | instanceof 179 | $){b=rx(b,a);c--}else 180 | if(a===(a|0)){b=am(b,a+a+1);c--}else 181 | if(a===+a){b=rv(b,a);c--}}b=ru(b);return b&bT}function 182 | rK(a){return[a[3]>>8,a[3]&S,a[2]>>16,a[2]>>8&S,a[2]&S,a[1]>>16,a[1]>>8&S,a[1]&S]}function 183 | rA(d,g,a){var 184 | c=0;function 185 | f(a){g--;if(d<0||g<0)return;if(a 186 | instanceof 187 | Array&&a[0]===(a[0]|0))switch(a[0]){case 188 | 248:d--;c=c*c1+a[2]|0;break;case 189 | 250:g++;f(a);break;case 190 | 255:d--;c=c*c1+a[1]+(a[2]<<24)|0;break;default:d--;c=c*19+a[0]|0;for(var 191 | b=a.length-1;b>0;b--)f(a[b])}else 192 | if(a 193 | instanceof 194 | $){d--;switch(a.t&6){default:as(a);case 195 | 0:for(var 196 | i=a.c,e=a.l,b=0;b=0;b--)c=c*19+j[b]|0}}f(a);return c&bT}function 203 | r9(e){for(var 204 | g=f,b=g,a,i,c=0,h=e.length;cdd){b.substr(0,1);g+=b;b=f;g+=e.slice(c,d)}else 206 | b+=e.slice(c,d);if(d==h)break;c=d}if(a>6);b+=String.fromCharCode(X|a&63)}else 207 | if(a<55296||a>=fo)b+=String.fromCharCode(e3|a>>12,X|a>>6&63,X|a&63);else 208 | if(a>=56319||c+1==h||(i=e.charCodeAt(c+1))fo)b+="\xef\xbf\xbd";else{c++;a=(a<<10)+i-56613888;b+=String.fromCharCode(eP|a>>18,X|a>>12&63,X|a>>6&63,X|a&63)}if(b.length>bu){b.substr(0,1);g+=b;b=f}}return g+b}function 209 | at(a){var 210 | b=9;if(!fP(a))b=8,a=r9(a);return new 211 | $(b,a,a.length)}function 212 | rB(a,c,l){if(!isFinite(a)){if(isNaN(a))return at(cT);return at(a>0?fA:"-infinity")}var 213 | j=a==0&&1/a==-Infinity?1:a>=0?0:1;if(j)a=-a;var 214 | d=0;if(a==0);else 215 | if(a<1)while(a<1&&d>-1022){a*=2;d--}else 216 | while(a>=2){a/=2;d++}var 217 | k=d<0?f:aK,e=f;if(j)e=aM;else 218 | switch(l){case 219 | 43:e=aK;break;case 220 | 32:e=ak;break;default:break}if(c>=0&&c<13){var 221 | h=Math.pow(2,c*4);a=Math.round(a*h)/h}var 222 | b=a.toString(16);if(c>=0){var 223 | i=b.indexOf(az);if(i<0)b+=az+a5(c,W);else{var 224 | g=i+1+c;if(b.length>24&al,a>>31&bt]}function 228 | fU(d){var 229 | c=d.length,b=new 230 | Array(c);for(var 231 | a=0;a>24),e=a[3]-b[3]+(d>>24);return[S,c&al,d&al,e&bt]}function 234 | fO(a,b){if(a[3]>b[3])return 1;if(a[3]b[2])return 1;if(a[2]b[1])return 1;if(a[1]>23;a[2]=(a[2]<<1|a[1]>>23)&al;a[1]=a[1]<<1&al}function 236 | rG(a){a[1]=(a[1]>>>1|a[2]<<23)&al;a[2]=(a[2]>>>1|a[3]<<23)&al;a[3]=a[3]>>>1}function 237 | rM(e,f){var 238 | c=0,b=fU(e),a=fU(f),d=[S,0,0,0];while(fO(b,a)>0){c++;fN(a)}while(c>=0){c--;fN(d);if(fO(b,a)>=0){d[1]++;b=rJ(b,a)}rG(a)}return[0,d,b]}function 239 | rL(a){return a[1]|a[2]<<24}function 240 | rE(a){return a[3]<<16<0}function 241 | rH(a){var 242 | b=-a[1],c=-a[2]+(b>>24),d=-a[3]+(c>>24);return[S,b&al,c&al,d&bt]}function 243 | rD(h,c){var 244 | a=di(h);if(a.signedconv&&rE(c)){a.sign=-1;c=rH(c)}var 245 | b=f,i=rI(a.base),g="0123456789abcdef";do{var 246 | e=rM(c,i);c=e[1];b=g.charAt(rL(e[2]))+b}while(!rF(c));if(a.prec>=0){a.filler=ak;var 247 | d=a.prec-b.length;if(d>0)b=a5(d,W)+b}return de(a,b)}var 248 | b0={amp:/&/g,lt:/>>32-b,c)}function 261 | g(c,b,d,e,h,f,g){return a(b&d|~b&e,c,b,h,f,g)}function 262 | h(d,b,e,c,h,f,g){return a(b&c|e&~c,d,b,h,f,g)}function 263 | i(c,b,d,e,h,f,g){return a(b^d^e,c,b,h,f,g)}function 264 | j(c,b,d,e,h,f,g){return a(d^(b|~e),c,b,h,f,g)}function 265 | k(f,n){var 266 | e=n;f[e>>2]|=X<<8*(e&3);for(e=(e&~3)+8;(e&63)<60;e+=4)f[(e>>2)-1]=0;f[(e>>2)-1]=n<<3;f[e>>2]=n>>29&536870911;var 267 | k=[1732584193,4023233417,2562383102,271733878];for(e=0;e>8*m&S;return o}return function(h,g,f){var 273 | e=[];switch(h.t&6){default:as(h);case 274 | 0:var 275 | d=h.c;for(var 276 | a=0;a>2]=d.charCodeAt(b)|d.charCodeAt(b+1)<<8|d.charCodeAt(b+2)<<16|d.charCodeAt(b+3)<<24}for(;a>2]|=d.charCodeAt(a+g)<<8*(a&3);break;case 278 | 4:var 279 | c=h.c;for(var 280 | a=0;a>2]=c[b]|c[b+1]<<8|c[b+2]<<16|c[b+3]<<24}for(;a>2]|=c[a+g]<<8*(a&3)}return f0(k(e,f))}}();function 282 | ah(a){dj(G.Sys_error,a)}function 283 | dh(a){if(!a.opened)ah("Cannot flush a closed channel");if(a.buffer==f)return 0;if(a.output)switch(a.output.length){case 284 | 2:a.output(a,a.buffer);break;default:a.output(a.buffer)}a.buffer=f;return 0}var 285 | f2=0;function 286 | sd(){return new 287 | Date().getTime()/aL}function 288 | dn(){return Math.floor(sd())}function 289 | aA(b){this.data=b;this.inode=f2++;var 290 | a=dn();this.atime=a;this.mtime=a;this.ctime=a}aA.prototype={truncate:function(){this.data=F(0);this.modified()},modified:function(){var 291 | a=dn();this.atime=a;this.mtime=a}};function 292 | i(a){return a.l}function 293 | fX(a){a=a 294 | instanceof 295 | $?a.toString():a;ah(a+": No such file or directory")}var 296 | rm=eW;function 297 | b1(a){a=a 298 | instanceof 299 | $?a.toString():a;if(a.charCodeAt(0)!=47)a=rm+a;var 300 | d=a.split(eW),b=[];for(var 301 | c=0;c1)b.pop();break;case".":break;case"":if(b.length==0)b.push(f);break;default:b.push(d[c]);break}b.orig=a;return b}function 302 | aN(){this.content={};this.inode=f2++;var 303 | a=dn();this.atime=a;this.mtime=a;this.ctime=a}aN.prototype={exists:function(a){return this.content[a]?1:0},mk:function(b,a){this.content[b]=a},get:function(a){return this.content[a]},list:function(){var 304 | a=[];for(var 305 | b 306 | in 307 | this.content)a.push(b);return a},remove:function(a){delete 308 | this.content[a]}};var 309 | b2=new 310 | aN();b2.mk(f,new 311 | aN());function 312 | df(c){var 313 | a=b2;for(var 314 | b=0;b=f){var 383 | d=F(e+c);aa(b.file.data,0,d,0,f);aa(g,0,d,e,c);b.file.data=d}b.offset+=c;b.file.modified();return 0}function 384 | fR(a){var 385 | b;switch(a){case 386 | 1:b=r$;break;case 387 | 2:b=r_;break;default:b=r1}var 388 | d=G.fds[a];if(d.flags.rdonly)ah(fI+a+" is readonly");var 389 | c={file:d.file,offset:d.offset,fd:a,opened:true,buffer:f,output:b};bx[c.fd]=c;return c}function 390 | rS(){var 391 | b=0;for(var 392 | a=0;a=a.c.length)return 0;case 412 | 0:return a.c.charCodeAt(b);case 413 | 4:return a.c[b]}}function 414 | w(b,a){if(a>>>0>=b.l)fZ();return a6(b,a)}function 415 | b3(a,b){return 1-B(a,b)}function 416 | x(a,c,b){b&=S;if(a.t!=4){if(c==a.c.length){a.c+=String.fromCharCode(b);if(c+1==a.l)a.t=0;return 0}bY(a)}a.c[c]=b;return 0}function 417 | au(b,a,c){if(a>>>0>=b.l)fZ();return x(b,a,c)}function 418 | r3(){return fj/4|0}function 419 | rX(){fW(G.Not_found)}function 420 | f1(c){var 421 | a=ai,b=c.toString();if(a.process&&a.process.env&&a.process.env[b]!=undefined)return at(a.process.env[b]);rX()}function 422 | r6(){var 423 | a=new 424 | Date()^4294967295*Math.random();return[0,a]}function 425 | dl(a){var 426 | b=1;while(a&&a.joo_tramp){a=a.joo_tramp.apply(null,a.joo_args);b++}return a}function 427 | J(b,a){return{joo_tramp:b,joo_args:a}}function 428 | fY(a){return a}function 429 | rU(a){return fT[a]}function 430 | ae(a){if(a 431 | instanceof 432 | Array)return a;if(ai.RangeError&&a 433 | instanceof 434 | ai.RangeError&&a.message&&a.message.match(/maximum call stack/i))return fY(G.Stack_overflow);if(ai.InternalError&&a 435 | instanceof 436 | ai.InternalError&&a.message&&a.message.match(/too much recursion/i))return fY(G.Stack_overflow);if(a 437 | instanceof 438 | ai.Error)return[0,rU(eV),a];return[0,G.Failure,at(String(a))]}function 439 | c(a,b){return a.length==1?a(b):a4(a,[b])}function 440 | ac(a,b,c){return a.length==2?a(b,c):a4(a,[b,c])}function 441 | bQ(a,b,c,d){return a.length==3?a(b,c,d):a4(a,[b,c,d])}var 442 | b4=[Q,a(eT),-3],dp=[Q,a(fa),-4],K=[Q,a(ff),-7],y=[Q,a(fv),-11],d4=a("body"),aJ=[a3,0,0];ad(11,[Q,a(eJ),-12],eJ);ad(10,y,fv);ad(9,[Q,a(e9),-10],e9);ad(8,[Q,a(fg),-9],fg);ad(7,[Q,a(e1),-8],e1);ad(6,K,ff);ad(5,[Q,a(fr),-6],fr);ad(4,[Q,a(eU),-5],eU);ad(3,dp,fa);ad(2,b4,eT);ad(1,[Q,a(eR),-2],eR);ad(0,[Q,a(fC),-1],fC);var 443 | f8=a("output_substring"),f4=a("true"),f5=a("false"),ga=[0,a("list.ml"),227,11],gc=a("\\\\"),gd=a("\\'"),ge=a("\\b"),gf=a("\\t"),gg=a("\\n"),gh=a("\\r"),gb=a("Char.chr"),gn=a("String.contains_from / Bytes.contains_from"),gk=a("String.blit / Bytes.blit_string"),gj=a("Bytes.blit"),gi=a("String.sub / Bytes.sub"),go=a(f),gq=a("CamlinternalLazy.Undefined"),gv=a("Buffer.add_substring/add_subbytes"),gu=a("Buffer.add: cannot grow buffer"),gE=a("%c"),gF=a("%s"),gG=a(eH),gH=a(eZ),gI=a(e$),gJ=a(eL),gK=a("%f"),gL=a("%B"),gM=a("%{"),gN=a("%}"),gO=a("%("),gP=a(e5),gQ=a("%a"),gR=a("%t"),gS=a("%?"),gT=a("%r"),gU=a("%_r"),gV=[0,a(E),845,23],g6=[0,a(E),809,21],gY=[0,a(E),810,21],g7=[0,a(E),813,21],gZ=[0,a(E),814,21],g8=[0,a(E),817,19],g0=[0,a(E),818,19],g9=[0,a(E),821,22],g1=[0,a(E),822,22],g_=[0,a(E),826,30],g2=[0,a(E),827,30],g4=[0,a(E),831,26],gW=[0,a(E),832,26],g5=[0,a(E),841,28],gX=[0,a(E),842,28],g3=[0,a(E),846,23],ib=a(e7),h$=[0,a(E),1520,4],ia=a("Printf: bad conversion %["),ic=[0,a(E),1588,39],id=[0,a(E),1611,31],ie=[0,a(E),1612,31],ig=a("Printf: bad conversion %_"),ih=a("@{"),ii=a("@["),h8=a(cT),h9=a(az),h6=a("neg_infinity"),h7=a(fA),h1=a("%.12g"),hO=a("%nd"),hP=a("%+nd"),hQ=a("% nd"),hR=a(e$),hS=a("%+ni"),hT=a("% ni"),hU=a("%nx"),hV=a("%#nx"),hW=a("%nX"),hX=a("%#nX"),hY=a("%no"),hZ=a("%#no"),h0=a("%nu"),hB=a("%ld"),hC=a("%+ld"),hD=a("% ld"),hE=a(eZ),hF=a("%+li"),hG=a("% li"),hH=a("%lx"),hI=a("%#lx"),hJ=a("%lX"),hK=a("%#lX"),hL=a("%lo"),hM=a("%#lo"),hN=a("%lu"),ho=a("%Ld"),hp=a("%+Ld"),hq=a("% Ld"),hr=a(eL),hs=a("%+Li"),ht=a("% Li"),hu=a("%Lx"),hv=a("%#Lx"),hw=a("%LX"),hx=a("%#LX"),hy=a("%Lo"),hz=a("%#Lo"),hA=a("%Lu"),hb=a(fp),hc=a("%+d"),hd=a("% d"),he=a(eH),hf=a("%+i"),hg=a("% i"),hh=a("%x"),hi=a("%#x"),hj=a("%X"),hk=a("%#X"),hl=a("%o"),hm=a("%#o"),hn=a(e7),gw=a("@]"),gx=a("@}"),gy=a("@?"),gz=a("@\n"),gA=a("@."),gB=a("@@"),gC=a("@%"),gD=a("@"),g$=a("CamlinternalFormat.Type_mismatch"),ij=a(fq),ri=a("OCAMLRUNPARAM"),rg=a("CAMLRUNPARAM"),ik=a(f),iC=[3,0,3],iD=a(az),iy=a(c$),iz=a(">>0?1:0:65<=d?0:1;else{if(32===d)var 1193 | f=1;else 1194 | if(43<=d)switch(d-43|0){case 1195 | 5:if(a<(c+2|0))if(1>>0)if(93<=r)var 1217 | s=0,m=0;else 1218 | var 1219 | m=1;else 1220 | if(56<(r-1|0)>>>0)var 1221 | s=1,m=0;else 1222 | var 1223 | m=1;if(m){var 1224 | k=k+1|0;continue}}else 1225 | var 1226 | s=11<=g?13===g?1:0:8<=g?1:0;var 1227 | w=s?1:1}if(w){var 1228 | a=[0,0],t=i(d)-1|0,A=0;if(!(t<0)){var 1229 | j=A;for(;;){var 1230 | f=a6(d,j);if(32<=f){var 1231 | p=f+eQ|0;if(58

>>0)if(93<=p)var 1232 | n=0,o=0;else 1233 | var 1234 | o=1;else 1235 | if(56<(p-1|0)>>>0)var 1236 | n=1,o=0;else 1237 | var 1238 | o=1;if(o)var 1239 | q=1,n=2}else 1240 | var 1241 | n=11<=f?13===f?1:0:8<=f?1:0;switch(n){case 1242 | 0:var 1243 | q=4;break;case 1244 | 1:var 1245 | q=2;break}a[1]=a[1]+q|0;var 1246 | D=j+1|0;if(t!==j){var 1247 | j=D;continue}break}}if(a[1]===i(d))var 1248 | l=b8(d);else{var 1249 | b=F(a[1]);a[1]=0;var 1250 | u=i(d)-1|0,B=0;if(!(u<0)){var 1251 | h=B;for(;;){var 1252 | c=a6(d,h);if(35<=c)var 1253 | e=92===c?1:cU<=c?0:2;else 1254 | if(32<=c)var 1255 | e=34<=c?1:2;else 1256 | if(14<=c)var 1257 | e=0;else 1258 | switch(c){case 1259 | 8:x(b,a[1],92);a[1]++;x(b,a[1],98);var 1260 | e=3;break;case 1261 | 9:x(b,a[1],92);a[1]++;x(b,a[1],116);var 1262 | e=3;break;case 1263 | 10:x(b,a[1],92);a[1]++;x(b,a[1],e6);var 1264 | e=3;break;case 1265 | 13:x(b,a[1],92);a[1]++;x(b,a[1],bV);var 1266 | e=3;break;default:var 1267 | e=0}switch(e){case 1268 | 0:x(b,a[1],92);a[1]++;x(b,a[1],48+(c/v|0)|0);a[1]++;x(b,a[1],48+((c/10|0)%10|0)|0);a[1]++;x(b,a[1],48+(c%10|0)|0);break;case 1269 | 1:x(b,a[1],92);a[1]++;x(b,a[1],c);break;case 1270 | 2:x(b,a[1],c);break}a[1]++;var 1271 | C=h+1|0;if(u!==h){var 1272 | h=C;continue}break}}var 1273 | l=b}}else 1274 | var 1275 | l=d;var 1276 | y=i(l),z=aC(y+2|0,34);aa(l,0,z,1,y);return z}}function 1277 | h2(c,b){switch(c){case 1278 | 0:var 1279 | a=hb;break;case 1280 | 1:var 1281 | a=hc;break;case 1282 | 2:var 1283 | a=hd;break;case 1284 | 3:var 1285 | a=he;break;case 1286 | 4:var 1287 | a=hf;break;case 1288 | 5:var 1289 | a=hg;break;case 1290 | 6:var 1291 | a=hh;break;case 1292 | 7:var 1293 | a=hi;break;case 1294 | 8:var 1295 | a=hj;break;case 1296 | 9:var 1297 | a=hk;break;case 1298 | 10:var 1299 | a=hl;break;case 1300 | 11:var 1301 | a=hm;break;default:var 1302 | a=hn}return bZ(a,b)}function 1303 | h3(c,b){switch(c){case 1304 | 0:var 1305 | a=hB;break;case 1306 | 1:var 1307 | a=hC;break;case 1308 | 2:var 1309 | a=hD;break;case 1310 | 3:var 1311 | a=hE;break;case 1312 | 4:var 1313 | a=hF;break;case 1314 | 5:var 1315 | a=hG;break;case 1316 | 6:var 1317 | a=hH;break;case 1318 | 7:var 1319 | a=hI;break;case 1320 | 8:var 1321 | a=hJ;break;case 1322 | 9:var 1323 | a=hK;break;case 1324 | 10:var 1325 | a=hL;break;case 1326 | 11:var 1327 | a=hM;break;default:var 1328 | a=hN}return bZ(a,b)}function 1329 | h4(c,b){switch(c){case 1330 | 0:var 1331 | a=hO;break;case 1332 | 1:var 1333 | a=hP;break;case 1334 | 2:var 1335 | a=hQ;break;case 1336 | 3:var 1337 | a=hR;break;case 1338 | 4:var 1339 | a=hS;break;case 1340 | 5:var 1341 | a=hT;break;case 1342 | 6:var 1343 | a=hU;break;case 1344 | 7:var 1345 | a=hV;break;case 1346 | 8:var 1347 | a=hW;break;case 1348 | 9:var 1349 | a=hX;break;case 1350 | 10:var 1351 | a=hY;break;case 1352 | 11:var 1353 | a=hZ;break;default:var 1354 | a=h0}return bZ(a,b)}function 1355 | h5(c,b){switch(c){case 1356 | 0:var 1357 | a=ho;break;case 1358 | 1:var 1359 | a=hp;break;case 1360 | 2:var 1361 | a=hq;break;case 1362 | 3:var 1363 | a=hr;break;case 1364 | 4:var 1365 | a=hs;break;case 1366 | 5:var 1367 | a=ht;break;case 1368 | 6:var 1369 | a=hu;break;case 1370 | 7:var 1371 | a=hv;break;case 1372 | 8:var 1373 | a=hw;break;case 1374 | 9:var 1375 | a=hx;break;case 1376 | 10:var 1377 | a=hy;break;case 1378 | 11:var 1379 | a=hz;break;default:var 1380 | a=hA}return rD(a,b)}function 1381 | an(c,u,j){if(16<=c){if(17<=c)switch(c-17|0){case 1382 | 2:var 1383 | m=0;break;case 1384 | 0:case 1385 | 3:var 1386 | o=43,m=1;break;default:var 1387 | o=32,m=1}else 1388 | var 1389 | m=0;if(!m)var 1390 | o=45;var 1391 | k=rB(j,u,o);if(19<=c){var 1392 | n=i(k);if(0===n)return k;var 1393 | r=F(n),s=n-1|0,A=0;if(!(s<0)){var 1394 | e=A;for(;;){var 1395 | h=a6(k,e);if(97<=h)if(122>>0?55===p?1:0:21<(p-1|0)>>>0?1:0;if(!E){var 1454 | l=l+1|0;continue}var 1455 | z=1}return z?g:H(g,h9)}}return g}function 1456 | bB(g,f,e,d,h,c,b,a){if(typeof 1457 | h==="number"){if(typeof 1458 | c==="number")return 0===c?function(c){return m(g,f,[4,e,ac(b,a,c)],d)}:function(h,c){return m(g,f,[4,e,aQ(h,ac(b,a,c))],d)};var 1459 | l=c[1];return function(c){return m(g,f,[4,e,aQ(l,ac(b,a,c))],d)}}else{if(0===h[0]){var 1460 | i=h[2],j=h[1];if(typeof 1461 | c==="number")return 0===c?function(c){return m(g,f,[4,e,_(j,i,ac(b,a,c))],d)}:function(h,c){return m(g,f,[4,e,_(j,i,aQ(h,ac(b,a,c)))],d)};var 1462 | n=c[1];return function(c){return m(g,f,[4,e,_(j,i,aQ(n,ac(b,a,c)))],d)}}var 1463 | k=h[1];if(typeof 1464 | c==="number")return 0===c?function(h,c){return m(g,f,[4,e,_(k,h,ac(b,a,c))],d)}:function(i,h,c){return m(g,f,[4,e,_(k,i,aQ(h,ac(b,a,c)))],d)};var 1465 | o=c[1];return function(h,c){return m(g,f,[4,e,_(k,h,aQ(o,ac(b,a,c)))],d)}}}function 1466 | dE(g,f,e,d,a,b){if(typeof 1467 | a==="number")return function(a){return m(g,f,[4,e,c(b,a)],d)};else{if(0===a[0]){var 1468 | h=a[2],i=a[1];return function(a){return m(g,f,[4,e,_(i,h,c(b,a))],d)}}var 1469 | j=a[1];return function(h,a){return m(g,f,[4,e,_(j,h,c(b,a))],d)}}}function 1470 | bp(f,K,e,I,H){var 1471 | b=K,a=I,d=H;for(;;)if(typeof 1472 | d==="number")return ac(b,e,a);else 1473 | switch(d[0]){case 1474 | 0:var 1475 | L=d[1];return function(c){return m(b,e,[5,a,c],L)};case 1476 | 1:var 1477 | M=d[1];return function(c){if(40<=c)if(92===c)var 1478 | f=gc,d=2;else 1479 | var 1480 | d=cU<=c?0:1;else 1481 | if(32<=c)if(39<=c)var 1482 | f=gd,d=2;else 1483 | var 1484 | d=1;else 1485 | if(14<=c)var 1486 | d=0;else 1487 | switch(c){case 1488 | 8:var 1489 | f=ge,d=2;break;case 1490 | 9:var 1491 | f=gf,d=2;break;case 1492 | 10:var 1493 | f=gg,d=2;break;case 1494 | 13:var 1495 | f=gh,d=2;break;default:var 1496 | d=0}switch(d){case 1497 | 0:var 1498 | g=F(4);x(g,0,92);x(g,1,48+(c/v|0)|0);x(g,2,48+((c/10|0)%10|0)|0);x(g,3,48+(c%10|0)|0);var 1499 | f=g;break;case 1500 | 1:var 1501 | h=F(1);x(h,0,c);var 1502 | f=h;break}var 1503 | j=i(f),k=aC(j+2|0,39);aa(f,0,k,1,j);return m(b,e,[4,a,k],M)};case 1504 | 2:var 1505 | O=d[2],P=d[1];return dE(b,e,a,O,P,function(a){return a});case 1506 | 3:return dE(b,e,a,d[2],d[1],ha);case 1507 | 4:return bB(b,e,a,d[4],d[2],d[3],h2,d[1]);case 1508 | 5:return bB(b,e,a,d[4],d[2],d[3],h3,d[1]);case 1509 | 6:return bB(b,e,a,d[4],d[2],d[3],h4,d[1]);case 1510 | 7:return bB(b,e,a,d[4],d[2],d[3],h5,d[1]);case 1511 | 8:var 1512 | h=d[4],j=d[3],l=d[2],k=d[1];if(typeof 1513 | l==="number"){if(typeof 1514 | j==="number")return 0===j?function(c){return m(b,e,[4,a,an(k,cb,c)],h)}:function(d,c){return m(b,e,[4,a,an(k,d,c)],h)};var 1515 | ai=j[1];return function(c){return m(b,e,[4,a,an(k,ai,c)],h)}}else{if(0===l[0]){var 1516 | p=l[2],u=l[1];if(typeof 1517 | j==="number")return 0===j?function(c){return m(b,e,[4,a,_(u,p,an(k,cb,c))],h)}:function(d,c){return m(b,e,[4,a,_(u,p,an(k,d,c))],h)};var 1518 | aj=j[1];return function(c){return m(b,e,[4,a,_(u,p,an(k,aj,c))],h)}}var 1519 | w=l[1];if(typeof 1520 | j==="number")return 0===j?function(d,c){return m(b,e,[4,a,_(w,d,an(k,cb,c))],h)}:function(f,d,c){return m(b,e,[4,a,_(w,f,an(k,d,c))],h)};var 1521 | ak=j[1];return function(d,c){return m(b,e,[4,a,_(w,d,an(k,ak,c))],h)}}case 1522 | 9:var 1523 | Q=d[1];return function(c){var 1524 | d=c?f4:f5;return m(b,e,[4,a,d],Q)};case 1525 | 10:var 1526 | a=[7,a],d=d[1];continue;case 1527 | 11:var 1528 | a=[2,a,d[1]],d=d[2];continue;case 1529 | 12:var 1530 | a=[3,a,d[1]],d=d[2];continue;case 1531 | 13:var 1532 | R=d[3],S=d[2],A=dB(16);cc(A,S);var 1533 | G=dD(A);return function(c){return m(b,e,[4,a,G],R)};case 1534 | 14:var 1535 | T=d[3],U=d[2];return function(d){var 1536 | f=d[1],c=r(f,t(z(U)));if(typeof 1537 | c[2]==="number")return m(b,e,a,s(c[1],T));throw N};case 1538 | 15:var 1539 | V=d[1];return function(d,c){return m(b,e,[6,a,function(a){return ac(d,a,c)}],V)};case 1540 | 16:var 1541 | W=d[1];return function(c){return m(b,e,[6,a,c],W)};case 1542 | 17:var 1543 | a=[0,a,d[1]],d=d[2];continue;case 1544 | 18:var 1545 | o=d[1];if(0===o[0]){var 1546 | X=d[2],Y=o[1][1],Z=0,b=function(c,d,e){return function(b,a){return m(d,b,[1,c,[0,a]],e)}}(a,b,X),a=Z,d=Y;continue}var 1547 | $=d[2],ab=o[1][1],ad=0,b=function(c,d,e){return function(b,a){return m(d,b,[1,c,[1,a]],e)}}(a,b,$),a=ad,d=ab;continue;case 1548 | 19:throw[0,y,h$];case 1549 | 20:var 1550 | ae=d[3],af=[8,a,ia];return function(a){return m(b,e,af,ae)};case 1551 | 21:var 1552 | ag=d[2];return function(c){return m(b,e,[4,a,bZ(ib,c)],ag)};case 1553 | 22:var 1554 | ah=d[1];return function(c){return m(b,e,[5,a,c],ah)};case 1555 | 23:var 1556 | g=d[2],n=d[1];if(typeof 1557 | n==="number")switch(n){case 1558 | 0:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1559 | 1:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1560 | 2:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1561 | 3:throw[0,y,ic];default:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g])}else 1562 | switch(n[0]){case 1563 | 0:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1564 | 1:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1565 | 2:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1566 | 3:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1567 | 4:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1568 | 5:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1569 | 6:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1570 | 7:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);case 1571 | 8:var 1572 | E=n[2];return f<50?cP(f+1|0,b,e,a,E,g):J(cP,[0,b,e,a,E,g]);case 1573 | 9:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g]);default:return f<50?q(f+1|0,b,e,a,g):J(q,[0,b,e,a,g])}default:var 1574 | B=d[3],C=d[1],D=c(d[2],0);return f<50?cO(f+1|0,b,e,a,B,C,D):J(cO,[0,b,e,a,B,C,D])}}function 1575 | cP(f,e,d,c,a,b){if(typeof 1576 | a==="number")return f<50?q(f+1|0,e,d,c,b):J(q,[0,e,d,c,b]);else 1577 | switch(a[0]){case 1578 | 0:var 1579 | g=a[1];return function(a){return ab(e,d,c,g,b)};case 1580 | 1:var 1581 | h=a[1];return function(a){return ab(e,d,c,h,b)};case 1582 | 2:var 1583 | i=a[1];return function(a){return ab(e,d,c,i,b)};case 1584 | 3:var 1585 | j=a[1];return function(a){return ab(e,d,c,j,b)};case 1586 | 4:var 1587 | k=a[1];return function(a){return ab(e,d,c,k,b)};case 1588 | 5:var 1589 | l=a[1];return function(a){return ab(e,d,c,l,b)};case 1590 | 6:var 1591 | m=a[1];return function(a){return ab(e,d,c,m,b)};case 1592 | 7:var 1593 | n=a[1];return function(a){return ab(e,d,c,n,b)};case 1594 | 8:var 1595 | o=a[2];return function(a){return ab(e,d,c,o,b)};case 1596 | 9:var 1597 | p=a[3],r=a[2],s=C(z(a[1]),r);return function(a){return ab(e,d,c,R(s,p),b)};case 1598 | 10:var 1599 | t=a[1];return function(f,a){return ab(e,d,c,t,b)};case 1600 | 11:var 1601 | u=a[1];return function(a){return ab(e,d,c,u,b)};case 1602 | 12:var 1603 | v=a[1];return function(a){return ab(e,d,c,v,b)};case 1604 | 13:throw[0,y,id];default:throw[0,y,ie]}}function 1605 | q(e,c,b,f,a){var 1606 | d=[8,f,ig];return e<50?bp(e+1|0,c,b,d,a):J(bp,[0,c,b,d,a])}function 1607 | cO(i,d,b,g,a,f,e){if(f){var 1608 | j=f[1];return function(f){return h_(d,b,g,a,j,c(e,f))}}var 1609 | h=[4,g,e];return i<50?bp(i+1|0,d,b,h,a):J(bp,[0,d,b,h,a])}function 1610 | m(a,b,c,d){return dl(bp(0,a,b,c,d))}function 1611 | ab(a,b,c,d,e){return dl(cP(0,a,b,c,d,e))}function 1612 | h_(a,b,c,d,e,f){return dl(cO(0,a,b,c,d,e,f))}function 1613 | ax(b,i){var 1614 | a=i;for(;;)if(typeof 1615 | a==="number")return 0;else 1616 | switch(a[0]){case 1617 | 0:var 1618 | e=a[2],j=a[1];if(typeof 1619 | e==="number")switch(e){case 1620 | 0:var 1621 | d=gw;break;case 1622 | 1:var 1623 | d=gx;break;case 1624 | 2:var 1625 | d=gy;break;case 1626 | 3:var 1627 | d=gz;break;case 1628 | 4:var 1629 | d=gA;break;case 1630 | 5:var 1631 | d=gB;break;default:var 1632 | d=gC}else 1633 | switch(e[0]){case 1634 | 0:var 1635 | d=e[1];break;case 1636 | 1:var 1637 | d=e[1];break;default:var 1638 | d=H(gD,aP(1,e[1]))}ax(b,j);return a_(b,d);case 1639 | 1:var 1640 | g=a[2],h=a[1];if(0===g[0]){var 1641 | k=g[1];ax(b,h);a_(b,ih);var 1642 | a=k;continue}var 1643 | l=g[1];ax(b,h);a_(b,ii);var 1644 | a=l;continue;case 1645 | 6:var 1646 | o=a[2];ax(b,a[1]);return a_(b,c(o,0));case 1647 | 7:var 1648 | a=a[1];continue;case 1649 | 8:var 1650 | p=a[2];ax(b,a[1]);return av(p);case 1651 | 2:case 1652 | 4:var 1653 | m=a[2];ax(b,a[1]);return a_(b,m);default:var 1654 | n=a[2];ax(b,a[1]);var 1655 | f=b[2];if(b[3]<=f)ca(b,1);x(b[1],f,n);b[2]=f+1|0;return 0}}var 1656 | dF=[0,0];function 1657 | dG(a){dF[1]=[0,a,dF[1]];return 0}try{var 1658 | rj=f1(ri),dI=rj}catch(a){a=ae(a);if(a!==K)throw a;try{var 1659 | rh=f1(rg),dH=rh}catch(a){a=ae(a);if(a!==K)throw a;var 1660 | dH=ik}var 1661 | dI=dH}var 1662 | il=gp(dI,82),bC=[fh,function(z){var 1663 | o=r6(0),c=[0,dg(55,0),0],k=0===o.length-1?[0,0]:o,l=k.length-1,b=0;for(;;){T(c[1],b)[b+1]=b;var 1664 | y=b+1|0;if(54!==b){var 1665 | b=y;continue}var 1666 | h=[0,ij],m=54+dr(55,l)|0,t=0;if(!(m<0)){var 1667 | d=t;for(;;){var 1668 | e=d%55|0,n=fS(d,l),u=T(k,n)[n+1],j=H(h[1],a(f+u));h[1]=rQ(j,0,i(j));var 1669 | g=h[1],p=w(g,3)<<24,q=w(g,2)<<16,r=w(g,1)<<8,s=((w(g,0)+r|0)+q|0)+p|0,v=(T(c[1],e)[e+1]^s)&bT;T(c[1],e)[e+1]=v;var 1670 | x=d+1|0;if(m!==d){var 1671 | d=x;continue}break}}c[2]=0;return c}}];function 1672 | ce(h,k){var 1673 | l=h?h[1]:il,b=16;for(;;){if(!(k<=b))if(!(b$<(b*2|0))){var 1674 | b=b*2|0;continue}if(l){var 1675 | i=fV(bC),a=bq===i?bC[1]:fh===i?gt(bC):bC;a[2]=(a[2]+1|0)%55|0;var 1676 | c=a[2],d=T(a[1],c)[c+1],e=(a[2]+24|0)%55|0,f=(T(a[1],e)[e+1]+(d^(d>>>25|0)&31)|0)&bT,g=a[2];T(a[1],g)[g+1]=f;var 1677 | j=f}else 1678 | var 1679 | j=0;return[0,0,dg(b,0),j,b]}}function 1680 | dJ(k,b){var 1681 | d=b[2],e=d.length-1,f=e*2|0,g=f'),k=j.tagName.toLowerCase()===eK?1:0,m=k?j.name===fq?1:0:k,i=m}catch(a){var 1836 | i=0}var 1837 | l=i?fm:-1003883683;dZ[1]=l;continue}if(fm<=h){var 1838 | a=new 1839 | dX();a.push(ft,b.toString());bD(e,function(b){a.push(' type="',fQ(b),fu);return 0});bD(d,function(b){a.push(' name="',fQ(b),fu);return 0});a.push(c$);return c.createElement(a.join(f))}var 1840 | g=bE(c,b);bD(e,function(a){return g.type=a});bD(d,function(a){return g.name=a});return g}}function 1841 | cl(c,b,a){return d0(c,b,a,iM)}aB(0);ay.HTMLElement===iI;function 1842 | I(a,c,b){var 1843 | d=a?a[1]:1;return[0,c,b,d]}function 1844 | b(d,c,b,a,h,g,f,e){var 1845 | i=d?d[1]:1,j=c?c[1]:1,k=b?b[1]:0,l=a?a[1]:0;return[0,h,g,f,e,i,j,k,l]}function 1846 | cm(b,a){if(0===a[6])return[0,b,a[1],0,0];function 1847 | c(c){return[0,b/a[5]*a[3]/c[2]/(c[3]-a[7]),c[1]]}var 1848 | d=O(c,a[2]);function 1849 | e(c){return cm(b/a[5]*c[1],c[2])}var 1850 | f=O(e,a[4]);return[0,b,a[1],d,f]}function 1851 | g(a){return ag.createTextNode(a.toString())}function 1852 | iT(e,d,c,b){var 1853 | f=e?e[1]:iU,g=d?d[1]:b,a=bF(ag,iR);a.src=b.toString();a.alt=g.toString();a.className=f.toString();if(c)a.title=c[1].toString();return a}function 1854 | bG(e,d,b,f){var 1855 | g=e?e[1]:iW,h=d?d[1]:iV,i=b?b[1]:function(a){return 0},a=bF(ag,iQ);Y(function(b){a.appendChild(b);return 0},f);a.className=g.toString();a.href=h.toString();a.onclick=aE(function(a){c(i,0);return aS});return a}function 1856 | d1(d,b,e){var 1857 | f=d?d[1]:iX,g=b?b[1]:function(a){return 0},a=d0(0,0,ag,iN);Y(function(b){a.appendChild(b);return 0},e);a.className=f.toString();a.onclick=aE(function(a){c(g,0);return aS});return a}function 1858 | bH(b,a){b.appendChild(a);return 0}function 1859 | aF(b,d){var 1860 | c=b?b[1]:iY,a=bF(ag,iP);Y(function(b){return bH(a,b)},d);a.className=c.toString();return a}function 1861 | bc(b,a){return aF(b,[0,g(a),0])}function 1862 | cn(a,c){var 1863 | d=a?a[1]:iZ,b=bF(ag,iO);Y(function(a){return bH(b,a)},c);b.className=d.toString();return[0,b,function(i){var 1864 | f=b.childNodes,c=0,a=0,g=f.length;for(;;){if(a>1,u=dt(l,e),v=p(l,e),h=v,g=p(i-l|0,u),f=0;for(;;){if(h){if(g){var 1926 | n=g[1],o=h[1],s=g[2],t=h[2];if(0>1,u=dt(m,e),v=j(m,e),h=v,g=j(i-m|0,u),f=0;for(;;){if(h){if(g){var 1937 | o=g[1],p=h[1],s=g[2],t=h[2];if(0>>0)var 1946 | a=0;else 1947 | switch(f){case 1948 | 0:var 1949 | a=1;break;case 1950 | 1:var 1951 | a=2;break;default:var 1952 | a=3}}else 1953 | if(65<=d)switch(d+eX|0){case 1954 | 0:var 1955 | a=1;break;case 1956 | 1:var 1957 | a=2;break;default:var 1958 | a=3}else 1959 | var 1960 | a=0;switch(a){case 1961 | 0:var 1962 | c=0;break;case 1963 | 1:var 1964 | c=e(0);break;case 1965 | 2:var 1966 | c=e(1);break;default:var 1967 | c=e(2)}if(c)var 1968 | g=c[1],m=g[2],n=g[1],h=aY(b9(b[2],1,i(b[2])-1|0)),l=h*n[2]/b[1][3]*b[1][5],k=[0,[0,m,h]],j=0;else 1969 | var 1970 | l=aY(b[2]),k=0,j=1;return[0,l,b[1],k,j]}return[0,0,b[1],0,0]}var 1971 | e=[0,0,1,0,0,1,0,1,1,a2,a0,aJ,aJ,aJ,aJ,aJ,aJ,aJ,0,0];function 1972 | cN(c,b){function 1973 | m(a){try{var 1974 | b=f$(function(b){return B(b[2],a)},c)}catch(a){a=ae(a);if(a===K)return 0;throw a}return[0,b[1]]}function 1975 | h(b){var 1976 | d=b[3];if(d)var 1977 | n=d[2],o=d[1],i=function(b,a){var 1978 | c=a[2],d=[0,eB(a[1]),0],e=[0,g(oX),d],f=[0,A(c),e],h=b?g(oY):g(oZ);return bI(0,[0,h,f])},p=[0,g(o0),0],q=0,r=aw(O(function(a){return i(q,a)},n),p),s=[0,i(1,o),0],t=[0,g(o1),s],j=aw([0,bI(o3,[0,g(o2),0]),t],r);else{var 1979 | k=m(b[2]);if(k)var 1980 | D=b[1]*v/k[1]|0,E=[0,g(o9),0],F=[0,g(a(f+D)),E],l=[0,bI(o$,[0,g(o_),F]),0];else 1981 | var 1982 | l=0;var 1983 | j=l}switch(e[18]){case 1984 | 0:var 1985 | c=[0,b[1],o4];break;case 1986 | 1:var 1987 | c=[0,b[1]*60,o7];break;default:var 1988 | c=[0,b[1]*eI,o8]}var 1989 | w=c[2],x=c[1],y=aw(j,O(h,b[4])),z=[0,g(w),0],B=[0,eB(x),z],C=[0,g(o5),B];return u(o6,aw([0,A(b[2]),C],y))}return O(h,b)}function 1990 | eF(d){var 1991 | f=[0,0];function 1992 | i(b){var 1993 | a=b[1];return B(a,d6[1])?e[1]:B(a,co[1])?e[2]:B(a,d7[1])?e[3]:B(a,d8[1])?e[4]:B(a,cq[1])?e[5]:B(a,cp[1])?e[6]:B(a,bd[1])?e[7]:B(a,be[1])?e[8]:1}function 1994 | j(c){var 1995 | a=c[1];function 1996 | b(a){f[1]=a[2];return[0,c[1],c[2]*(1+a[1]/v),c[3]]}return B(a,co[1])?b(e[11]):B(a,d_[1])?b(e[12]):B(a,cq[1])?b(e[13]):B(a,bd[1])?b(e[14]):B(a,be[1])?b(e[15]):B(a,d9[1])?b(e[16]):B(a,cr[1])?b(e[17]):c}if(B(d[1],aV[1]))var 1997 | g=e[9],k=a2===g?j7:bs<=g?j1:aV,b=k;else 1998 | var 1999 | b=d;if(B(b[1],bO[1]))var 2000 | h=e[10],l=a0===h?bO:br<=h?nk:nn,a=l;else 2001 | var 2002 | a=b;var 2003 | m=a[2],n=O(j,c(ds(i),m)),o=a[4],p=O(function(a){var 2004 | b=a[1];return[0,b,eF(a[2])]},o),q=a[8]?f[1]:0;return[0,a[1],n,a[3],p,a[5]*(1+q/v),a[6],a[7],a[8]]}function 2005 | eG(k){var 2006 | a=[0,0],q=i(k);function 2007 | b(c){if(0<=a[1])if(!(q<=a[1])){var 2008 | b=w(k,a[1]);a[1]++;return b}return 45}function 2009 | h(e,d,a){var 2010 | f=b(0)+fl|0;if(7>>0)return 0;switch(f){case 2011 | 0:c(e,0);c(d,0);return c(a,0);case 2012 | 1:c(e,0);c(d,0);return c(a,1);case 2013 | 2:c(e,0);c(d,1);return c(a,0);case 2014 | 3:c(e,0);c(d,1);return c(a,1);case 2015 | 4:c(e,1);c(d,0);return c(a,0);case 2016 | 5:c(e,1);c(d,0);return c(a,1);case 2017 | 6:c(e,1);c(d,1);return c(a,0);default:c(e,1);c(d,1);return c(a,1)}}function 2018 | r(e){if(bV===b(0)){var 2019 | g=[0,0],h=[0,0],f=[0,0],k=[0,pA],n=a[1],l=b(0);if(25<(l+eX|0)>>>0)a[1]=n;else 2020 | k[1]=aP(1,l);for(;;){if(f[1]){if(0<=a[1]){var 2021 | o=a8(g[1]),p=1,q=bo(aY(b_(pB,O(function(a){return aP(p,a)},o)))),m=H(k[1],q);e[2]=m;c(e[4],m);var 2022 | r=h[1]?0:1;e[1][6]=r;return c(e[3],h[1])}return 0}var 2023 | d=b(0);if(58<=d)var 2024 | i=fy===d?(h[1]=1,f[1]=1,1):bV===d?(a[1]+=-1,f[1]=1,1):0;else{if(46===d)var 2025 | j=1;else 2026 | if(48<=d)var 2027 | j=1;else 2028 | var 2029 | i=0,j=0;if(j){g[1]=[0,d,g[1]];var 2030 | i=1}}if(!i){a[1]=-1;f[1]=1}continue}}a[1]=-1;return 0}function 2031 | l(j){if(98===b(0)){var 2032 | d=[0,0],e=[0,0];for(;;){if(e[1]){if(0<=a[1]){var 2033 | h=a8(d[1]),i=1;return aY(b_(pC,O(function(a){return aP(i,a)},h)))}return 0}var 2034 | c=b(0);if(58<=c){if(98===c)var 2035 | g=1;else 2036 | if(bV===c)var 2037 | g=1;else 2038 | var 2039 | f=0,g=0;if(g){a[1]+=-1;e[1]=1;var 2040 | f=1}}else 2041 | var 2042 | f=47===c?0:45<=c?(d[1]=[0,c,d[1]],1):0;if(!f){a[1]=-1;e[1]=1}continue}}return 0}function 2043 | d(a){var 2044 | b=l(0);return c(a,[a3,b,l(0)])}var 2045 | m=b(0)-104|0;if(11>>0)var 2046 | j=0;else{switch(m){case 2047 | 0:e[18]=2;var 2048 | f=1;break;case 2049 | 5:e[18]=1;var 2050 | f=1;break;case 2051 | 11:e[18]=0;var 2052 | f=1;break;default:var 2053 | j=0,f=0}if(f)var 2054 | j=1}if(!j)a[1]+=-1;function 2055 | s(a){e[2]=a;return 0}function 2056 | t(a){e[1]=a;return 0}h(function(a){return 0},t,s);function 2057 | u(a){e[5]=a;return 0}function 2058 | v(a){e[4]=a;return 0}h(function(a){e[3]=a;return 0},v,u);function 2059 | x(a){e[8]=a;return 0}function 2060 | y(a){e[7]=a;return 0}h(function(a){e[6]=a;return 0},y,x);var 2061 | n=b(0)+fl|0;if(!(2>>0))switch(n){case 2062 | 0:e[9]=c2;break;case 2063 | 1:e[9]=bs;break;default:e[9]=a2}var 2064 | o=b(0)-72|0;if(8>>0)var 2065 | p=0;else{switch(o){case 2066 | 0:e[10]=br;var 2067 | g=1;break;case 2068 | 4:e[10]=c5;var 2069 | g=1;break;case 2070 | 8:e[10]=a0;var 2071 | g=1;break;default:var 2072 | p=0,g=0}if(g)var 2073 | p=1}d(function(a){e[11]=a;return 0});d(function(a){e[12]=a;return 0});d(function(a){e[13]=a;return 0});d(function(a){e[14]=a;return 0});d(function(a){e[15]=a;return 0});d(function(a){e[16]=a;return 0});d(function(a){e[17]=a;return 0});Y(r,cM);var 2074 | z=e[19];return Y(function(a){return c(a,0)},z)}i4(function(bj){var 2075 | k=[0,0],m=cn(pD,0),n=m[2],r=m[1],j=[0,function(a){return 0}],o=[0,function(a){return 0}];function 2076 | l(a){return c(o[1],0)}function 2077 | d(a,d){function 2078 | f(b){return c(a,0)!==b?(c(d,b),l(0)):0}var 2079 | b=d2(0,[0,f],c(a,0)),g=b[2],h=b[1];function 2080 | i(b){return c(g,c(a,0))}e[19]=[0,i,e[19]];return h}function 2081 | a(d,b,j){function 2082 | g(a){return a?(c(j,0),l(0)):0}var 2083 | k=c(b,0),m=[0,d],h=[0,g]?g:function(a){return 0},i=m?d:i2,a=cl([0,"radio"],[0,i.toString()],ag);a.checked=!!k;a.onclick=aE(function(b){h(a.checked|0);return aS});a.className=f;function 2084 | n(d){return a.checked=!!c(b,0)}e[19]=[0,n,e[19]];return a}function 2085 | p(a,d){function 2086 | f(b){return b3(c(a,0),b)?(c(d,b),l(0)):0}var 2087 | b=d3(0,[0,f],c(a,0)),g=b[2],h=b[1];function 2088 | i(b){return c(g,c(a,0))}e[19]=[0,i,e[19]];return h}function 2089 | h(d,e,a,b){var 2090 | f=d?d[1]:pI,h=[0,g(H(pE,f)),0];function 2091 | i(d){var 2092 | e=c(a,0),f=aY(d);return c(b,[a3,e[1],f])}var 2093 | j=[0,p(function(b){return bo(c(a,0)[2])},i),h],k=[0,g(pF),j];function 2094 | l(d){var 2095 | e=c(a,0)[2];return c(b,[a3,aY(d),e])}var 2096 | m=[0,p(function(b){return bo(c(a,0)[1])},l),k],n=[0,g(pG),m];return u(pH,[0,A(e),n])}var 2097 | s=[0,g(pJ),0];function 2098 | t(a){e[17]=a;return 0}var 2099 | v=[0,h(0,pK,function(a){return e[17]},t),s];function 2100 | w(a){e[16]=a;return 0}var 2101 | x=[0,h(pM,pL,function(a){return e[16]},w),v];function 2102 | y(a){e[15]=a;return 0}var 2103 | z=[0,h(0,pN,function(a){return e[15]},y),x];function 2104 | B(a){e[14]=a;return 0}var 2105 | C=[0,h(0,pO,function(a){return e[14]},B),z];function 2106 | D(a){e[13]=a;return 0}var 2107 | E=[0,h(0,pP,function(a){return e[13]},D),C];function 2108 | F(a){e[12]=a;return 0}var 2109 | G=[0,h(0,pQ,function(a){return e[12]},F),E];function 2110 | I(a){e[11]=a;return 0}var 2111 | J=[0,h(0,pR,function(a){return e[11]},I),G],L=[0,A(pS),0];function 2112 | M(a){e[10]=a0;return 0}var 2113 | N=[0,a(pT,function(a){return a0===e[10]?1:0},M),L],P=[0,A(pU),N];function 2114 | Q(a){e[10]=c5;return 0}var 2115 | R=[0,a(pV,function(a){return c5===e[10]?1:0},Q),P],U=[0,A(pW),R];function 2116 | V(a){e[10]=br;return 0}var 2117 | W=[0,a(pX,function(a){return br===e[10]?1:0},V),U],X=[0,u(pZ,[0,g(pY),W]),J],Z=[0,A(p0),0];function 2118 | _(a){e[9]=a2;return 0}var 2119 | $=[0,a(p1,function(a){return a2===e[9]?1:0},_),Z],aa=[0,A(p2),$];function 2120 | ab(a){e[9]=bs;return 0}var 2121 | ac=[0,a(p3,function(a){return bs===e[9]?1:0},ab),aa],ad=[0,A(p4),ac];function 2122 | af(a){e[9]=c2;return 0}var 2123 | ah=[0,a(p5,function(a){return c2===e[9]?1:0},af),ad],ai=[0,u(p7,[0,g(p6),ah]),X],aj=[0,A(p8),0];function 2124 | ak(a){e[8]=a;return 0}var 2125 | al=[0,d(function(a){return e[8]},ak),aj],am=[0,A(p9),al];function 2126 | an(a){e[7]=a;return 0}var 2127 | ao=[0,d(function(a){return e[7]},an),am],ap=[0,A(p_),ao];function 2128 | aq(a){e[6]=a;return 0}var 2129 | ar=[0,d(function(a){return e[6]},aq),ap],as=[0,u(qa,[0,g(p$),ar]),ai],at=[0,A(qb),0];function 2130 | au(a){e[5]=a;return 0}var 2131 | ax=[0,d(function(a){return e[5]},au),at],az=[0,A(qc),ax];function 2132 | aA(a){e[4]=a;return 0}var 2133 | aB=[0,d(function(a){return e[4]},aA),az],aC=[0,A(qd),aB];function 2134 | aD(a){e[3]=a;return 0}var 2135 | aG=[0,d(function(a){return e[3]},aD),aC],aH=[0,u(qf,[0,g(qe),aG]),as],aI=[0,A(qg),0];function 2136 | aJ(a){e[2]=a;return 0}var 2137 | aK=[0,d(function(a){return e[2]},aJ),aI],aL=[0,A(qh),aK];function 2138 | aM(a){e[1]=a;return 0}var 2139 | aN=[0,d(function(a){return e[1]},aM),aL],aO=[0,u(qj,[0,g(qi),aN]),aH],aQ=[0,g(qk),0];function 2140 | aR(a){e[18]=2;return 0}var 2141 | aT=[0,a(ql,function(a){return 2===e[18]?1:0},aR),aQ],aU=[0,g(qm),aT];function 2142 | aV(a){e[18]=1;return 0}var 2143 | aW=[0,a(qn,function(a){return 1===e[18]?1:0},aV),aU],aX=[0,g(qo),aW];function 2144 | a1(a){e[18]=0;return 0}var 2145 | a4=[0,a(qp,function(a){return 0===e[18]?1:0},a1),aX],a5=[0,u(qr,[0,g(qq),a4]),aO],a6=[0,g(qs),0],a7=[0,d1(qt,[0,function(a){k[1]=0;return c(j[1],0)}],a6),a5],a9=0,a_=[0,g(qu),0],a$=[0,d1(qv,[0,function(a){k[1]=1;return c(j[1],0)}],a_),a9];j[1]=function(a){return k[1]?c(n,a7):c(n,a$)};c(j[1],0);var 2146 | q=cn(qw,0),ba=q[2],bd=q[1];function 2147 | i(aB){var 2148 | z=b(0,0,0,0,qx,0,0,O(function(b){var 2149 | h=b[4],i=b[3],j=b[2],c=eE([0,eF(b[1]),j,i,h]),a=c[1],g=c[2];if(c[4]){switch(e[18]){case 2150 | 0:var 2151 | d=a;break;case 2152 | 1:var 2153 | d=a/60;break;default:var 2154 | d=a/eI}var 2155 | f=d}else 2156 | var 2157 | f=a;return[0,f,g]},cM)),A=ds(function(a){return a[1]!=0?1:0}),a=ce(0,16),aa=1;function 2158 | E(m,c){var 2159 | j=1;try{var 2160 | n=cf(a,c[1])}catch(a){j=0;a=ae(a);if(a!==K)throw a;var 2161 | h=0}if(j)var 2162 | h=n[1];var 2163 | i=[0,h+m,c],d=c[1];function 2164 | e(a){if(a){var 2165 | b=a[3],c=a[1],f=a[2];return 0===bv(c,d)?[0,d,i,b]:[0,c,f,e(b)]}throw K}var 2166 | b=bb(a,d),f=T(a[2],b)[b+1];try{var 2167 | k=e(f),l=T(a[2],b)[b+1]=k;return l}catch(c){c=ae(c);if(c===K){T(a[2],b)[b+1]=[0,d,i,f];a[1]=a[1]+1|0;var 2168 | g=a[2].length-1<<1 79 | let makers = 80 | let make_maker (maker: maker) = 81 | let maker_count = 82 | throughput 83 | /. resource.count 84 | *. resource.time 85 | /. maker.crafting_speed 86 | /. (maker.power -. resource.hardness) 87 | in 88 | maker_count, maker.name 89 | in 90 | List.map make_maker resource.makers 91 | in 92 | let subgoals = 93 | let make_subgoal (count, ingredient) = 94 | summarize_local 95 | (throughput /. resource.count *. count) 96 | ingredient 97 | in 98 | List.map make_subgoal resource.ingredients 99 | in 100 | { 101 | throughput; 102 | goal = resource.name; 103 | makers; 104 | subgoals; 105 | } 106 | | Global -> 107 | { 108 | throughput; 109 | goal = resource.name; 110 | makers = []; 111 | subgoals = []; 112 | } 113 | 114 | (* Detail global resources only. *) 115 | let summarize_global throughput (resource: resource) = 116 | let table = Hashtbl.create 16 in 117 | let add throughput resource = 118 | let previous_throughput = 119 | match Hashtbl.find table resource.name with 120 | | exception Not_found -> 121 | 0. 122 | | tp, _ -> 123 | tp 124 | in 125 | Hashtbl.replace table resource.name 126 | (previous_throughput +. throughput, resource) 127 | in 128 | let rec search throughput resource = 129 | begin 130 | match resource.style with 131 | | Local -> () 132 | | Global -> add throughput resource 133 | end; 134 | List.iter 135 | (fun (count, ingredient) -> 136 | search (throughput /. resource.count *. count) ingredient) 137 | resource.ingredients 138 | in 139 | search throughput resource; 140 | let result = ref [] in 141 | Hashtbl.iter 142 | (fun _ (throughput, resource) -> 143 | result := 144 | summarize_local throughput { resource with style = Local } 145 | :: !result) 146 | table; 147 | List.rev !result 148 | -------------------------------------------------------------------------------- /factoriojs.ml: -------------------------------------------------------------------------------- 1 | (******************************************************************************) 2 | (* Copyright (c) 2016 DooMeeR *) 3 | (* *) 4 | (* Permission is hereby granted, free of charge, to any person obtaining *) 5 | (* a copy of this software and associated documentation files (the *) 6 | (* "Software"), to deal in the Software without restriction, including *) 7 | (* without limitation the rights to use, copy, modify, merge, publish, *) 8 | (* distribute, sublicense, and/or sell copies of the Software, and to *) 9 | (* permit persons to whom the Software is furnished to do so, subject to *) 10 | (* the following conditions: *) 11 | (* *) 12 | (* The above copyright notice and this permission notice shall be *) 13 | (* included in all copies or substantial portions of the Software. *) 14 | (* *) 15 | (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *) 16 | (* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *) 17 | (* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND *) 18 | (* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE *) 19 | (* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION *) 20 | (* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION *) 21 | (* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *) 22 | (******************************************************************************) 23 | 24 | open Factorio 25 | open Recipes 26 | open Html 27 | 28 | type gui_resource = 29 | { 30 | resource: resource; 31 | mutable count: string; 32 | mutable set_gui_global: bool -> unit; 33 | mutable set_gui_count: string -> unit; 34 | } 35 | 36 | let resources_by_category: (string * gui_resource list) list = 37 | let make_resource resource = 38 | { 39 | resource; 40 | count = "0"; 41 | set_gui_global = (fun _ -> ()); 42 | set_gui_count = (fun _ -> ()); 43 | } 44 | in 45 | List.map 46 | (fun (category, resources) -> category, List.map make_resource resources) 47 | resources 48 | 49 | let resources: gui_resource list = 50 | List.map snd resources_by_category |> List.flatten 51 | 52 | let parse_float string = 53 | try 54 | float_of_string string 55 | with Failure _ -> 56 | 0. 57 | 58 | let fs x = 59 | let s = Printf.sprintf "%F" (ceil (x *. 1000.) /. 1000.) in 60 | let len = String.length s in 61 | if len > 0 && s.[len - 1] = '.' then 62 | String.sub s 0 (len - 1) 63 | else 64 | s 65 | 66 | let ft float = text (fs float) 67 | 68 | let special_icons = 69 | let list = 70 | [ 71 | power_armor_mk2.name, "Power_armor_MK2"; 72 | personal_battery.name, "Battery-equipment"; 73 | personal_battery_mk2.name, "Battery_MK2"; 74 | energy_shield_mk2.name, "Energy_shield_MK2"; 75 | ] 76 | in 77 | let table = Hashtbl.create 16 in 78 | List.iter (fun (name, src) -> Hashtbl.add table name src) list; 79 | table 80 | 81 | let special_hrefs = 82 | let list = 83 | [ 84 | power_armor_mk2.name, "Power_armor_MK2"; 85 | personal_battery.name, "Battery_MK1"; 86 | personal_battery_mk2.name, "Battery_MK2"; 87 | energy_shield_mk2.name, "Energy_shield_MK2"; 88 | ] 89 | in 90 | let table = Hashtbl.create 16 in 91 | List.iter (fun (name, src) -> Hashtbl.add table name src) list; 92 | table 93 | 94 | let rec gui_icon alt = 95 | if alt = "Advanced Oil Processing + Cracking" then 96 | span [ 97 | gui_icon "Advanced Oil Processing"; 98 | text "+"; 99 | gui_icon "Heavy Oil Cracking"; 100 | text "+"; 101 | gui_icon "Light Oil Cracking"; 102 | ] 103 | else 104 | let src = 105 | match Hashtbl.find special_icons alt with 106 | | src -> 107 | src 108 | | exception Not_found -> 109 | let src = Bytes.of_string alt in 110 | for i = 0 to Bytes.length src - 1 do 111 | let chr = Bytes.get src i in 112 | if chr = ' ' then 113 | Bytes.set src i '_' 114 | else if i > 0 then 115 | Bytes.set src i (Char.lowercase_ascii chr) 116 | done; 117 | Bytes.to_string src 118 | in 119 | let src = "https://wiki.factorio.com/images/"^src^".png" in 120 | let href = 121 | match Hashtbl.find special_hrefs alt with 122 | | href -> 123 | href 124 | | exception Not_found -> 125 | let href = Bytes.of_string alt in 126 | for i = 0 to Bytes.length href - 1 do 127 | let chr = Bytes.get href i in 128 | if chr = ' ' then 129 | Bytes.set href i '_' 130 | else if i > 0 then 131 | Bytes.set href i (Char.lowercase_ascii chr) 132 | done; 133 | Bytes.to_string href 134 | in 135 | let href = "https://wiki.factorio.com/index.php?title="^href in 136 | a ~href [ img ~class_: "icon" ~alt ~title: alt src ] 137 | 138 | let last_hash = ref "" 139 | 140 | let get_resource_request (resource: gui_resource) = 141 | (* The user can prefix the resource count with a letter, 142 | to automatically convert the count into a number of makers 143 | that should be running at full time. *) 144 | if resource.count <> "" then 145 | let maker = 146 | (* Makers are indexed from best to worst. *) 147 | let get_maker original_index = 148 | let rec find index = function 149 | | [] -> None 150 | | hd :: _ when index = 0 -> Some (hd, original_index) 151 | | _ :: tl -> find (index - 1) tl 152 | in 153 | let makers = 154 | (* Sort the makers by crafting speed, from best to worst. *) 155 | let compare_makers (a: maker) (b: maker) = 156 | compare b.crafting_speed a.crafting_speed 157 | in 158 | List.sort compare_makers resource.resource.makers 159 | in 160 | find original_index makers 161 | in 162 | match resource.count.[0] with 163 | | 'a' | 'A' -> get_maker 0 164 | | 'b' | 'B' -> get_maker 1 165 | | 'c' | 'C' -> get_maker 2 166 | | _ -> None 167 | in 168 | let count, index, should_apply_time_unit = 169 | match maker with 170 | | None -> 171 | parse_float resource.count, None, true 172 | | Some (maker, index) -> 173 | let maker_count = 174 | parse_float 175 | (String.sub resource.count 1 176 | (String.length resource.count - 1)) 177 | in 178 | maker_count *. maker.crafting_speed /. 179 | resource.resource.time *. resource.resource.count, 180 | Some (index, maker_count), 181 | false 182 | in 183 | count, resource.resource, index, should_apply_time_unit 184 | else 185 | 0., resource.resource, None, false 186 | 187 | type module_settings = 188 | { 189 | speed_bonus: float; 190 | productivity_bonus: float; 191 | } 192 | 193 | type time_unit = 194 | | Seconds 195 | | Minutes 196 | | Hours 197 | 198 | (* If you add a setting, don't forget to: 199 | - update [make_hash]; 200 | - update [parse_hash]; 201 | - update [apply_settings]. *) 202 | type settings = 203 | { 204 | mutable drill_burner: bool; 205 | mutable drill_electric: bool; 206 | mutable furnace_stone: bool; 207 | mutable furnace_steel: bool; 208 | mutable furnace_electric: bool; 209 | mutable assembling_machine_1: bool; 210 | mutable assembling_machine_2: bool; 211 | mutable assembling_machine_3: bool; 212 | mutable petroleum_gas: [ `basic | `advanced | `cracking ]; 213 | mutable solid_fuel: [ `heavy | `light | `petroleum ]; 214 | mutable drill_electric_modules: module_settings; 215 | mutable pumpjack_modules: module_settings; 216 | mutable furnace_electric_modules: module_settings; 217 | mutable assembling_machine_2_modules: module_settings; 218 | mutable assembling_machine_3_modules: module_settings; 219 | mutable chemical_plant_modules: module_settings; 220 | mutable rocket_silo_modules: module_settings; 221 | mutable time_unit: time_unit; 222 | (* update_gui: called to copy the values above into the GUI inputs *) 223 | mutable update_gui: (unit -> unit) list; 224 | } 225 | 226 | let no_bonuses = { speed_bonus = 0.; productivity_bonus = 0. } 227 | 228 | let settings = 229 | { 230 | drill_burner = false; 231 | drill_electric = true; 232 | furnace_stone = false; 233 | furnace_steel = false; 234 | furnace_electric = true; 235 | assembling_machine_1 = false; 236 | assembling_machine_2 = true; 237 | assembling_machine_3 = true; 238 | petroleum_gas = `cracking; 239 | solid_fuel = `petroleum; 240 | drill_electric_modules = no_bonuses; 241 | pumpjack_modules = no_bonuses; 242 | furnace_electric_modules = no_bonuses; 243 | assembling_machine_2_modules = no_bonuses; 244 | assembling_machine_3_modules = no_bonuses; 245 | chemical_plant_modules = no_bonuses; 246 | rocket_silo_modules = no_bonuses; 247 | time_unit = Seconds; 248 | update_gui = []; 249 | } 250 | 251 | let gui_goals (global: summary list) (goals: summary list) = 252 | let get_global_goal_throughput goal = 253 | match List.find (fun global_goal -> global_goal.goal = goal) global with 254 | | exception Not_found -> 255 | None 256 | | global_goal -> 257 | Some global_goal.throughput 258 | in 259 | let rec gui_goal (goal: summary) = 260 | let makers = 261 | match goal.makers with 262 | | [] -> 263 | ( 264 | match get_global_goal_throughput goal.goal with 265 | | None -> 266 | [] 267 | | Some global_throughput -> 268 | let percent = 269 | int_of_float 270 | (goal.throughput *. 100. /. global_throughput) 271 | in 272 | [ 273 | span ~class_: "sharedpercent" [ 274 | text "  ("; 275 | text (string_of_int percent); 276 | text "%)"; 277 | ]; 278 | ] 279 | ) 280 | | hd :: tl -> 281 | let gui_maker first (count, name) = 282 | span [ 283 | if first then text "" else text " or "; 284 | gui_icon name; 285 | text " × "; 286 | ft count; 287 | ] 288 | in 289 | [ 290 | span ~class_: "makerspace" [ text " " ]; 291 | text "("; 292 | gui_maker true hd; 293 | ] 294 | @ List.map (gui_maker false) tl 295 | @ [ 296 | text ")"; 297 | ] 298 | in 299 | div ~class_: "goal" ( 300 | let throughput, unit = 301 | match settings.time_unit with 302 | | Seconds -> goal.throughput, "/s" 303 | | Minutes -> goal.throughput *. 60., "/min" 304 | | Hours -> goal.throughput *. 3600., "/h" 305 | in 306 | [ 307 | gui_icon goal.goal; 308 | text " × "; 309 | ft throughput; 310 | text unit; 311 | ] 312 | @ makers 313 | @ List.map gui_goal goal.subgoals 314 | ) 315 | in 316 | List.map gui_goal goals 317 | 318 | let get_resource_request_and_apply_time_unit (resource: gui_resource) = 319 | let count, resource, _, should_apply_time_unit = 320 | get_resource_request resource 321 | in 322 | let count = 323 | if should_apply_time_unit then 324 | match settings.time_unit with 325 | | Seconds -> count 326 | | Minutes -> count /. 60. 327 | | Hours -> count /. 3600. 328 | else 329 | count 330 | in 331 | count, resource 332 | 333 | let rec apply_settings (resource: resource) = 334 | let filter_maker (maker: maker) = 335 | let name = maker.name in 336 | if name = burner_mining_drill.name then 337 | settings.drill_burner 338 | else if name = electric_mining_drill.name then 339 | settings.drill_electric 340 | else if name = stone_furnace.name then 341 | settings.furnace_stone 342 | else if name = steel_furnace.name then 343 | settings.furnace_steel 344 | else if name = electric_furnace.name then 345 | settings.furnace_electric 346 | else if name = assembling_machine_1.name then 347 | settings.assembling_machine_1 348 | else if name = assembling_machine_2.name then 349 | settings.assembling_machine_2 350 | else if name = assembling_machine_3.name then 351 | settings.assembling_machine_3 352 | else 353 | true (* No setting for this maker, keep it. *) 354 | in 355 | let productivity_bonus = ref 0. in 356 | let apply_speed_modules (maker: maker) = 357 | let name = maker.name in 358 | let apply modules = 359 | (* We use the last maker's productivity bonus. *) 360 | productivity_bonus := modules.productivity_bonus; 361 | { 362 | maker with 363 | crafting_speed = 364 | maker.crafting_speed *. 365 | (1. +. modules.speed_bonus /. 100.); 366 | } 367 | in 368 | if name = electric_mining_drill.name then 369 | apply settings.drill_electric_modules 370 | else if name = pumpjack.name then 371 | apply settings.pumpjack_modules 372 | else if name = electric_furnace.name then 373 | apply settings.furnace_electric_modules 374 | else if name = assembling_machine_2.name then 375 | apply settings.assembling_machine_2_modules 376 | else if name = assembling_machine_3.name then 377 | apply settings.assembling_machine_3_modules 378 | else if name = chemical_plant_.name then 379 | apply settings.chemical_plant_modules 380 | else if name = rocket_silo.name then 381 | apply settings.rocket_silo_modules 382 | else 383 | maker 384 | in 385 | let resource = 386 | if resource.name = petroleum_gas.name then 387 | match settings.petroleum_gas with 388 | | `basic -> petroleum_gas_basic 389 | | `advanced -> petroleum_gas_advanced 390 | | `cracking -> petroleum_gas_cracking 391 | else 392 | resource 393 | in 394 | let resource = 395 | if resource.name = solid_fuel.name then 396 | match settings.solid_fuel with 397 | | `heavy -> solid_fuel_from_heavy_oil 398 | | `light -> solid_fuel_from_light_oil 399 | | `petroleum -> solid_fuel_from_petroleum_gas 400 | else 401 | resource 402 | in 403 | (* Need to compute the makers first so that the [productivity_bonus] 404 | reference is updated before we use it. *) 405 | let makers = 406 | List.map 407 | apply_speed_modules 408 | (List.filter filter_maker resource.makers) 409 | in 410 | let ingredients = 411 | List.map 412 | (fun (count, ingredient) -> count, apply_settings ingredient) 413 | resource.ingredients 414 | in 415 | let productivity_bonus = 416 | if resource.allow_productivity then !productivity_bonus else 0. 417 | in 418 | let count = resource.count *. (1. +. productivity_bonus /. 100.) in 419 | { 420 | resource with 421 | makers; 422 | ingredients; 423 | count; 424 | } 425 | 426 | let make_hash () = 427 | let float prefix f = prefix ^ if f = 0. then "" else fs f in 428 | let make_module_hash (modules: module_settings) = 429 | float "b" modules.speed_bonus ^ float "b" modules.productivity_bonus 430 | in 431 | let make_resource_hash (resource: gui_resource) = 432 | let style = 433 | match resource.resource.style with 434 | | Global -> "g" 435 | | Local -> "" 436 | in 437 | let count, _, index, _ = get_resource_request resource in 438 | ( 439 | match index with 440 | | Some (index, maker_count) when index >= 0 && index < 26 -> 441 | let index = String.make 1 (Char.chr (Char.code 'A' + index)) in 442 | "r" ^ index ^ float "" maker_count 443 | | Some _ 444 | | None -> 445 | float "r" count 446 | ) ^ 447 | style 448 | in 449 | let bools a b c = 450 | match a, b, c with 451 | | false, false, false -> "0" 452 | | false, false, true -> "1" 453 | | false, true, false -> "2" 454 | | false, true, true -> "3" 455 | | true, false, false -> "4" 456 | | true, false, true -> "5" 457 | | true, true, false -> "6" 458 | | true, true, true -> "7" 459 | in 460 | ( 461 | ( 462 | match settings.time_unit with 463 | | Seconds -> "s" 464 | | Minutes -> "m" 465 | | Hours -> "h" 466 | ) :: 467 | bools false settings.drill_burner settings.drill_electric :: 468 | bools settings.furnace_stone settings.furnace_steel 469 | settings.furnace_electric :: 470 | bools settings.assembling_machine_1 settings.assembling_machine_2 471 | settings.assembling_machine_3 :: 472 | ( 473 | match settings.petroleum_gas with 474 | | `basic -> "0" 475 | | `advanced -> "1" 476 | | `cracking -> "2" 477 | ) :: 478 | ( 479 | match settings.solid_fuel with 480 | | `heavy -> "H" 481 | | `light -> "L" 482 | | `petroleum -> "P" 483 | ) :: 484 | make_module_hash settings.drill_electric_modules :: 485 | make_module_hash settings.pumpjack_modules :: 486 | make_module_hash settings.furnace_electric_modules :: 487 | make_module_hash settings.assembling_machine_2_modules :: 488 | make_module_hash settings.assembling_machine_3_modules :: 489 | make_module_hash settings.chemical_plant_modules :: 490 | make_module_hash settings.rocket_silo_modules :: 491 | List.map make_resource_hash resources 492 | ) 493 | |> String.concat "" 494 | 495 | let parse_hash hash = 496 | let len = String.length hash in 497 | let i = ref 0 in 498 | let next () = 499 | if !i < 0 || !i >= len then '-' else ( 500 | let c = hash.[!i] in 501 | incr i; 502 | c 503 | ) 504 | in 505 | let parse_bools a b c = 506 | match next () with 507 | | '0' -> a false; b false; c false 508 | | '1' -> a false; b false; c true 509 | | '2' -> a false; b true; c false 510 | | '3' -> a false; b true; c true 511 | | '4' -> a true; b false; c false 512 | | '5' -> a true; b false; c true 513 | | '6' -> a true; b true; c false 514 | | '7' -> a true; b true; c true 515 | | _ -> () 516 | in 517 | let parse_resource_hash (resource: gui_resource) = 518 | if next () = 'r' then ( 519 | let chars = ref [] in 520 | let global = ref false in 521 | let stop = ref false in 522 | let index = ref "" in 523 | ( 524 | (* Try to read the maker index if any. *) 525 | let old_i = !i in 526 | match next () with 527 | | 'A'..'Z' as c -> 528 | index := String.make 1 c 529 | | _ -> 530 | i := old_i 531 | ); 532 | while not !stop do 533 | match next () with 534 | | 'r' -> 535 | (* Go back so that the next resource can parse this 'r'. *) 536 | decr i; 537 | stop := true 538 | | 'g' -> 539 | global := true; 540 | stop := true 541 | | ('0' .. '9' | '.' as c) -> 542 | chars := c :: !chars 543 | | _ -> 544 | i := -1; 545 | stop := true 546 | done; 547 | if !i >= 0 then 548 | let count = 549 | String.concat "" (List.map (String.make 1) (List.rev !chars)) 550 | in 551 | (* We parse the float to avoid security issues. *) 552 | let count = !index ^ (parse_float count |> fs) in 553 | resource.count <- count; 554 | resource.set_gui_count count; 555 | resource.resource.style <- (if !global then Global else Local); 556 | resource.set_gui_global !global 557 | ) else 558 | i := -1; (* next always returns '-' for the next resources *) 559 | (* if !i < 0 then alert ("Failed to parse: "^resource.resource.name); *) 560 | in 561 | let parse_module_bonus () = 562 | if next () = 'b' then ( 563 | let chars = ref [] in 564 | let stop = ref false in 565 | while not !stop do 566 | match next () with 567 | | 'b' | 'r' -> 568 | (* Go back so that the next parser can parse this character. *) 569 | decr i; 570 | stop := true 571 | | ('0' .. '9' | '.' | '-' as c) -> 572 | chars := c :: !chars 573 | | _ -> 574 | i := -1; 575 | stop := true 576 | done; 577 | if !i >= 0 then 578 | let count = 579 | String.concat "" (List.map (String.make 1) (List.rev !chars)) 580 | in 581 | parse_float count 582 | else 583 | 0. 584 | ) 585 | else 586 | 0. 587 | in 588 | let parse_module_bonuses set = 589 | let speed_bonus = parse_module_bonus () in 590 | let productivity_bonus = parse_module_bonus () in 591 | set { speed_bonus; productivity_bonus } 592 | in 593 | ( 594 | match next () with 595 | | 's' -> settings.time_unit <- Seconds 596 | | 'm' -> settings.time_unit <- Minutes 597 | | 'h' -> settings.time_unit <- Hours 598 | | _ -> (* backward compatibility *) decr i 599 | ); 600 | parse_bools 601 | (fun _ -> ()) 602 | (fun x -> settings.drill_burner <- x) 603 | (fun x -> settings.drill_electric <- x); 604 | parse_bools 605 | (fun x -> settings.furnace_stone <- x) 606 | (fun x -> settings.furnace_steel <- x) 607 | (fun x -> settings.furnace_electric <- x); 608 | parse_bools 609 | (fun x -> settings.assembling_machine_1 <- x) 610 | (fun x -> settings.assembling_machine_2 <- x) 611 | (fun x -> settings.assembling_machine_3 <- x); 612 | ( 613 | match next () with 614 | | '0' -> settings.petroleum_gas <- `basic 615 | | '1' -> settings.petroleum_gas <- `advanced 616 | | '2' -> settings.petroleum_gas <- `cracking 617 | | _ -> () 618 | ); 619 | ( 620 | match next () with 621 | | 'H' -> settings.solid_fuel <- `heavy 622 | | 'L' -> settings.solid_fuel <- `light 623 | | 'P' -> settings.solid_fuel <- `petroleum 624 | | _ -> () 625 | ); 626 | parse_module_bonuses 627 | (fun x -> settings.drill_electric_modules <- x); 628 | parse_module_bonuses 629 | (fun x -> settings.pumpjack_modules <- x); 630 | parse_module_bonuses 631 | (fun x -> settings.furnace_electric_modules <- x); 632 | parse_module_bonuses 633 | (fun x -> settings.assembling_machine_2_modules <- x); 634 | parse_module_bonuses 635 | (fun x -> settings.assembling_machine_3_modules <- x); 636 | parse_module_bonuses 637 | (fun x -> settings.chemical_plant_modules <- x); 638 | parse_module_bonuses 639 | (fun x -> settings.rocket_silo_modules <- x); 640 | List.iter parse_resource_hash resources; 641 | List.iter (fun f -> f ()) settings.update_gui 642 | 643 | let () = 644 | Html.run @@ fun () -> 645 | 646 | let settings_visible = ref false in 647 | let settings_div, set_settings_div = div' ~class_: "settings" [] in 648 | 649 | (* [update_settings_div] will be set after [settings] and [show_settings] 650 | are defined. *) 651 | let update_settings_div = ref (fun () -> ()) in 652 | 653 | (* [settings_changed] will be set to [update] once [update] is defined. *) 654 | let settings_changed = ref (fun () -> ()) in 655 | let settings_panel = 656 | let update () = !settings_changed () in 657 | let cb get set = 658 | let on_change value = 659 | if get () <> value then ( 660 | set value; 661 | update () 662 | ) 663 | in 664 | let cb, set_gui = checkbox_input' ~on_change (get ()) in 665 | let new_update_gui () = set_gui (get ()) in 666 | settings.update_gui <- new_update_gui :: settings.update_gui; 667 | cb 668 | in 669 | let rb name get set = 670 | let on_change value = 671 | if value then ( 672 | set (); 673 | update () 674 | ) 675 | in 676 | let rb, set_gui = radio_input' ~name ~on_change (get ()) in 677 | let new_update_gui () = set_gui (get ()) in 678 | settings.update_gui <- new_update_gui :: settings.update_gui; 679 | rb 680 | in 681 | let ti get set = 682 | let on_change value = 683 | if get () <> value then ( 684 | set value; 685 | update () 686 | ) 687 | in 688 | let cb, set_gui = text_input' ~on_change (get ()) in 689 | let new_update_gui () = set_gui (get ()) in 690 | settings.update_gui <- new_update_gui :: settings.update_gui; 691 | cb 692 | in 693 | let module_settings ?(help = "") name get set = 694 | div ~class_: "setting" [ 695 | gui_icon name; 696 | text " speed bonus: "; 697 | ti 698 | (fun () -> fs (get ()).speed_bonus) 699 | (fun x -> set { (get ()) with speed_bonus = parse_float x }); 700 | text "% productivity bonus: "; 701 | ti 702 | (fun () -> fs (get ()).productivity_bonus) 703 | (fun x -> 704 | set { (get ()) with productivity_bonus = parse_float x }); 705 | text ("%"^help); 706 | ] 707 | in 708 | [ 709 | button 710 | ~class_: "setvisible" 711 | ~on_click: 712 | (fun () -> settings_visible := false; !update_settings_div ()) 713 | [ text "Hide Advanced Settings" ]; 714 | div ~class_: "" [ 715 | text "Time Unit: "; 716 | rb "timeunit" 717 | (fun () -> settings.time_unit = Seconds) 718 | (fun () -> settings.time_unit <- Seconds); 719 | text "seconds "; 720 | rb "timeunit" 721 | (fun () -> settings.time_unit = Minutes) 722 | (fun () -> settings.time_unit <- Minutes); 723 | text "minutes "; 724 | rb "timeunit" 725 | (fun () -> settings.time_unit = Hours) 726 | (fun () -> settings.time_unit <- Hours); 727 | text "hours "; 728 | ]; 729 | div ~class_: "setting" [ 730 | text "Drills: "; 731 | cb 732 | (fun () -> settings.drill_burner) 733 | (fun x -> settings.drill_burner <- x); 734 | gui_icon "Burner Mining Drill"; 735 | cb 736 | (fun () -> settings.drill_electric) 737 | (fun x -> settings.drill_electric <- x); 738 | gui_icon "Electric Mining Drill"; 739 | ]; 740 | div ~class_: "setting" [ 741 | text "Furnaces: "; 742 | cb 743 | (fun () -> settings.furnace_stone) 744 | (fun x -> settings.furnace_stone <- x); 745 | gui_icon "Stone Furnace"; 746 | cb 747 | (fun () -> settings.furnace_steel) 748 | (fun x -> settings.furnace_steel <- x); 749 | gui_icon "Steel Furnace"; 750 | cb 751 | (fun () -> settings.furnace_electric) 752 | (fun x -> settings.furnace_electric <- x); 753 | gui_icon "Electric Furnace"; 754 | ]; 755 | div ~class_: "setting" [ 756 | text "Assembling Machines: "; 757 | cb 758 | (fun () -> settings.assembling_machine_1) 759 | (fun x -> settings.assembling_machine_1 <- x); 760 | gui_icon "Assembling Machine 1"; 761 | cb 762 | (fun () -> settings.assembling_machine_2) 763 | (fun x -> settings.assembling_machine_2 <- x); 764 | gui_icon "Assembling Machine 2"; 765 | cb 766 | (fun () -> settings.assembling_machine_3) 767 | (fun x -> settings.assembling_machine_3 <- x); 768 | gui_icon "Assembling Machine 3"; 769 | ]; 770 | div ~class_: "setting" [ 771 | text "Petroleum Gas: "; 772 | rb "oilprocessing" 773 | (fun () -> settings.petroleum_gas = `basic) 774 | (fun () -> settings.petroleum_gas <- `basic); 775 | gui_icon "Basic Oil Processing"; 776 | rb "oilprocessing" 777 | (fun () -> settings.petroleum_gas = `advanced) 778 | (fun () -> settings.petroleum_gas <- `advanced); 779 | gui_icon "Advanced Oil Processing"; 780 | rb "oilprocessing" 781 | (fun () -> settings.petroleum_gas = `cracking) 782 | (fun () -> settings.petroleum_gas <- `cracking); 783 | gui_icon "Advanced Oil Processing + Cracking"; 784 | ]; 785 | div ~class_: "setting" [ 786 | text "Solid fuel: "; 787 | rb "solidfuel" 788 | (fun () -> settings.solid_fuel = `heavy) 789 | (fun () -> settings.solid_fuel <- `heavy); 790 | gui_icon "Solid fuel from heavy oil"; 791 | rb "solidfuel" 792 | (fun () -> settings.solid_fuel = `light) 793 | (fun () -> settings.solid_fuel <- `light); 794 | gui_icon "Solid fuel from light oil"; 795 | rb "solidfuel" 796 | (fun () -> settings.solid_fuel = `petroleum) 797 | (fun () -> settings.solid_fuel <- `petroleum); 798 | gui_icon "Solid fuel from petroleum gas"; 799 | ]; 800 | module_settings "Electric Mining Drill" 801 | (fun () -> settings.drill_electric_modules) 802 | (fun x -> settings.drill_electric_modules <- x); 803 | module_settings "Pumpjack" 804 | (fun () -> settings.pumpjack_modules) 805 | (fun x -> settings.pumpjack_modules <- x); 806 | module_settings "Electric Furnace" 807 | (fun () -> settings.furnace_electric_modules) 808 | (fun x -> settings.furnace_electric_modules <- x); 809 | module_settings "Assembling Machine 2" 810 | (fun () -> settings.assembling_machine_2_modules) 811 | (fun x -> settings.assembling_machine_2_modules <- x); 812 | module_settings "Assembling Machine 3" 813 | (fun () -> settings.assembling_machine_3_modules) 814 | (fun x -> settings.assembling_machine_3_modules <- x); 815 | module_settings "Chemical Plant" 816 | ~help: " (not applied to Petroleum Gas)" 817 | (fun () -> settings.chemical_plant_modules) 818 | (fun x -> settings.chemical_plant_modules <- x); 819 | module_settings "Rocket Silo" 820 | (fun () -> settings.rocket_silo_modules) 821 | (fun x -> settings.rocket_silo_modules <- x); 822 | text "Note: if you use both blue and yellow machines, \ 823 | the productivity bonus which is used is the yellow one."; 824 | ] 825 | in 826 | 827 | let show_settings = 828 | [ 829 | button 830 | ~class_: "setvisible" 831 | ~on_click: 832 | (fun () -> settings_visible := true; !update_settings_div ()) 833 | [ text "Advanced Settings" ]; 834 | ] 835 | in 836 | 837 | update_settings_div := ( 838 | fun () -> 839 | if !settings_visible then 840 | set_settings_div settings_panel 841 | else 842 | set_settings_div show_settings 843 | ); 844 | !update_settings_div (); 845 | 846 | let gui, update = 847 | let result, set_result = Html.div' ~class_: "result" [] in 848 | let update () = 849 | let resources = 850 | List.map 851 | (fun x -> 852 | let a, b = 853 | get_resource_request_and_apply_time_unit 854 | { x with resource = apply_settings x.resource } 855 | in 856 | a, b) 857 | resources 858 | in 859 | let meta_resource = res "" [] 0. resources in 860 | let remove_zero = List.filter (fun goal -> goal.throughput <> 0.) in 861 | let global = summarize_global 1. meta_resource |> remove_zero in 862 | let local = (summarize_local 1. meta_resource).subgoals |> remove_zero in 863 | let result = 864 | match global, local with 865 | | [], [] -> 866 | [ 867 | div ~class_: "outputh1" [ text "Current Version" ]; 868 | p [ 869 | text "Recipes were updated for version 0.17!"; 870 | ]; 871 | div ~class_: "outputh1" [ text "Getting Started" ]; 872 | p_text 873 | "Set the number next to each resource to the \ 874 | requested throughput."; 875 | div ~class_: "outputh1" [ text "Shared Resources" ]; 876 | p_text 877 | "The checkbox controls whether \ 878 | the resource is shared. \ 879 | For instance, to produce Assembling Machine 1, you \ 880 | need iron plates for the machines themselves, but also \ 881 | for gears and for electronic circuits. \ 882 | Not sharing the plates means having \ 883 | three sets of furnaces, one for the circuits, one for \ 884 | the gears and one for the machines. \ 885 | Sharing the plates means having one set of furnaces \ 886 | to produce the plates which you then dispatch to \ 887 | other assemblers."; 888 | p_text 889 | "When shared resources are used, they have a blue percentage \ 890 | next to them which shows how much of the total is used for \ 891 | this particular use."; 892 | div ~class_: "outputh1" [ text "Share Your Settings" ]; 893 | p_text 894 | "See the part after the # \ 895 | in the URL? It encodes your settings and updates \ 896 | automatically. It means you can share your settings \ 897 | easily with other people. You can also bookmark them and \ 898 | even use the Back button."; 899 | div ~class_: "outputh1" [ text "Tips" ]; 900 | p_text 901 | "Instead of entering the requested throughput, \ 902 | you can enter a number of machines that should run \ 903 | at full speed. To this end, simply prefix the number by \ 904 | the letter A to use the fastest machine, B to use the \ 905 | second best or C to use the third best. For instance, \ 906 | if you allow yellow and blue assembly machines in \ 907 | Advanced Settings, \ 908 | requesting b2 laser turrets means that \ 909 | 2 blue assembling machines should run at full speed. \ 910 | If you only allow blue assembly machines, b2 means \ 911 | nothing as there is no second-fastest machine. \ 912 | In this case, a2 would mean that 2 blue assembly machines \ 913 | should run at full speed."; 914 | div ~class_: "outputh1" [ text "Links" ]; 915 | p [ 916 | text "Source code is available on "; 917 | a ~href: "https://github.com/doomeer/factorio" [ 918 | text "GitHub"; 919 | ]; 920 | text "."; 921 | ]; 922 | p [ 923 | text "See also the "; 924 | a ~href: "https://www.reddit.com/r/factorio/comments/4dmxib/my_factorio_planner_now_has_a_web_interface/" [ 925 | text "Reddit post"; 926 | ]; 927 | text "."; 928 | ]; 929 | div ~class_: "outputh1" [ text "Acknowledgments" ]; 930 | p [ 931 | text 932 | "Thanks to Nick Sheffield for the great style sheet! \ 933 | Thanks to s3bash, dwahler and DeCristoforis \ 934 | for 0.15 recipes! \ 935 | Thanks to jab416171 for 0.16 recipes! \ 936 | Thanks to NixonK for patching image links! \ 937 | Thanks to TheOddler for the better CSS for small \ 938 | screens! \ 939 | Thanks to Iidebyo for patching several wrong recipes! \ 940 | Thanks to JuicyJuuce for even more recipes! \ 941 | Thanks to DeCristoforis for the Solid Fuel setting! \ 942 | Thanks to Saintis for the science pack category! \ 943 | Thanks to Lava84flow, SamuelWr, keeshoekzema and s3bash for \ 944 | the 0.17 recipes! \ 945 | And thanks to everyone who helped finding bugs \ 946 | or posted suggestions. This tool greatly improved \ 947 | thanks to you."; 948 | ]; 949 | ] 950 | | _ :: _, [] 951 | | [], _ :: _ -> 952 | gui_goals global (global @ local) 953 | | _ :: _, _ :: _ -> 954 | [ 955 | div ~class_: "outputh1" [ text "Shared Resources" ]; 956 | div ~class_: "goals" (gui_goals global global); 957 | div ~class_: "outputh1" [ text "Non-Shared Resources" ]; 958 | div ~class_: "goals" (gui_goals global local); 959 | ] 960 | in 961 | set_result result; 962 | let hash = make_hash () in 963 | if hash <> !last_hash then ( 964 | last_hash := hash; 965 | Html.set_hash hash 966 | ) 967 | in 968 | let resource_input (resource: gui_resource) = 969 | let global, set_global = 970 | checkbox_input' 971 | ~on_change: 972 | (fun new_value -> 973 | let new_value = if new_value then Global else Local in 974 | resource.resource.style <- new_value; update ()) 975 | (resource.resource.style = Global) 976 | in 977 | resource.set_gui_global <- set_global; 978 | let count, set_count = 979 | text_input' 980 | ~class_: "count" 981 | ~on_change: (fun new_value -> resource.count <- new_value; update ()) 982 | resource.count 983 | in 984 | resource.set_gui_count <- set_count; 985 | div ~class_: "resourceinput" [ 986 | global; 987 | count; 988 | gui_icon resource.resource.name; 989 | ] 990 | in 991 | let resource_input_category (name, resources) = 992 | p ~class_: "resource_category" [ text name ] :: 993 | List.map resource_input resources 994 | in 995 | div ~class_: "main" [ 996 | div ~class_: "leftcolumn" ( 997 | List.flatten (List.map resource_input_category resources_by_category) 998 | @ [ 999 | p ~class_: "button" [ 1000 | a ~class_: "button" ~href: "index.html" [ text "Reset" ]; 1001 | ]; 1002 | ] 1003 | ); 1004 | div ~class_: "rightcolumn" [ 1005 | settings_div; 1006 | div ~class_: "result" [ result ]; 1007 | ]; 1008 | ], 1009 | update 1010 | in 1011 | settings_changed := update; 1012 | last_hash := Html.get_hash (); 1013 | parse_hash !last_hash; 1014 | update (); 1015 | let hash_change () = 1016 | let new_hash = Html.get_hash () in 1017 | if new_hash <> !last_hash then ( 1018 | last_hash := new_hash; 1019 | parse_hash new_hash; 1020 | update (); 1021 | ) 1022 | in 1023 | on_hash_change hash_change; 1024 | gui 1025 | -------------------------------------------------------------------------------- /html.ml: -------------------------------------------------------------------------------- 1 | (******************************************************************************) 2 | (* Copyright (c) 2016 DooMeeR *) 3 | (* *) 4 | (* Permission is hereby granted, free of charge, to any person obtaining *) 5 | (* a copy of this software and associated documentation files (the *) 6 | (* "Software"), to deal in the Software without restriction, including *) 7 | (* without limitation the rights to use, copy, modify, merge, publish, *) 8 | (* distribute, sublicense, and/or sell copies of the Software, and to *) 9 | (* permit persons to whom the Software is furnished to do so, subject to *) 10 | (* the following conditions: *) 11 | (* *) 12 | (* The above copyright notice and this permission notice shall be *) 13 | (* included in all copies or substantial portions of the Software. *) 14 | (* *) 15 | (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *) 16 | (* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *) 17 | (* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND *) 18 | (* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE *) 19 | (* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION *) 20 | (* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION *) 21 | (* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *) 22 | (******************************************************************************) 23 | 24 | type t = Dom.node Js.t 25 | 26 | let alert x = 27 | Dom_html.window##alert(Js.string x) 28 | 29 | let text value = 30 | let text = Dom_html.document##createTextNode(Js.string value) in 31 | (text :> t) 32 | 33 | let text' value = 34 | let text = Dom_html.document##createTextNode(Js.string value) in 35 | let set_text value = text##replaceData(0, text##length, Js.string value) in 36 | (text :> t), set_text 37 | 38 | let img ?(class_ = "") ?alt ?title src = 39 | let alt = match alt with None -> src | Some alt -> alt in 40 | let img = Dom_html.(createImg document) in 41 | img##src <- Js.string src; 42 | img##alt <- Js.string alt; 43 | img##className <- Js.string class_; 44 | (match title with None -> () | Some title -> img##title <- Js.string title); 45 | (img :> t) 46 | 47 | let a ?(class_ = "") ?(href = "") ?(on_click = fun () -> ()) items = 48 | let a = Dom_html.(createA document) in 49 | let append_node node = 50 | let _: Dom.node Js.t = a##appendChild(node) in 51 | () 52 | in 53 | List.iter append_node items; 54 | a##className <- Js.string class_; 55 | a##href <- Js.string href; 56 | let on_click _ = on_click (); Js._true in 57 | a##onclick <- Dom.handler on_click; 58 | (a :> t) 59 | 60 | let button ?(class_ = "") ?(on_click = fun () -> ()) items = 61 | let button = Dom_html.(createButton document) in 62 | let append_node node = 63 | let _: Dom.node Js.t = button##appendChild(node) in 64 | () 65 | in 66 | List.iter append_node items; 67 | button##className <- Js.string class_; 68 | let on_click _ = on_click (); Js._true in 69 | button##onclick <- Dom.handler on_click; 70 | (button :> t) 71 | 72 | let append_node parent node = 73 | let _: Dom.node Js.t = parent##appendChild(node) in 74 | () 75 | 76 | let set_items parent (items: t list) = 77 | List.iter 78 | (fun child -> let _: Dom.node Js.t = parent##removeChild(child) in ()) 79 | (Dom.list_of_nodeList parent##childNodes); 80 | List.iter (append_node parent) items 81 | 82 | let p' ?(class_ = "") items = 83 | let p = Dom_html.(createP document) in 84 | List.iter (append_node p) items; 85 | p##className <- Js.string class_; 86 | (p :> t), set_items p 87 | 88 | let p ?class_ items = 89 | let p, _ = p' ?class_ items in 90 | p 91 | 92 | let p_text ?class_ string = 93 | p ?class_ [ text string ] 94 | 95 | let div' ?(class_ = "") items = 96 | let div = Dom_html.(createDiv document) in 97 | List.iter (append_node div) items; 98 | div##className <- Js.string class_; 99 | (div :> t), set_items div 100 | 101 | let div ?class_ items = 102 | let div, _ = div' ?class_ items in 103 | div 104 | 105 | let span' ?(class_ = "") items = 106 | let span = Dom_html.(createSpan document) in 107 | List.iter (append_node span) items; 108 | span##className <- Js.string class_; 109 | (span :> t), set_items span 110 | 111 | let span ?class_ items = 112 | let span, _ = span' ?class_ items in 113 | span 114 | 115 | let checkbox_input' ?(class_ = "") ?(on_change = fun _ -> ()) checked = 116 | let input = Dom_html.(createInput ~_type: (Js.string "checkbox") document) in 117 | input##checked <- Js.bool checked; 118 | let on_click _ = on_change (Js.to_bool input##checked); Js._true in 119 | input##onclick <- Dom.handler on_click; 120 | input##className <- Js.string class_; 121 | let set_checked checked = input##checked <- Js.bool checked in 122 | (input :> t), set_checked 123 | 124 | let checkbox_input ?class_ ?on_change checked = 125 | let checkbox_input, _ = checkbox_input' ?class_ ?on_change checked in 126 | checkbox_input 127 | 128 | let radio_input' ?(class_ = "") ?(on_change = fun _ -> ()) ?(name = "") 129 | checked = 130 | let input = 131 | Dom_html.( 132 | createInput ~name: (Js.string name) ~_type: (Js.string "radio") 133 | document 134 | ) 135 | in 136 | input##checked <- Js.bool checked; 137 | let on_click _ = on_change (Js.to_bool input##checked); Js._true in 138 | input##onclick <- Dom.handler on_click; 139 | input##className <- Js.string class_; 140 | let set_checked checked = input##checked <- Js.bool checked in 141 | (input :> t), set_checked 142 | 143 | let radio_input ?class_ ?on_change ?name checked = 144 | let radio_input, _ = radio_input' ?class_ ?on_change ?name checked in 145 | radio_input 146 | 147 | let text_input' ?(class_ = "") ?(on_change = fun _ -> ()) value = 148 | let input = Dom_html.(createInput ~_type: (Js.string "text") document) in 149 | input##value <- Js.string value; 150 | let on_input _ = on_change (Js.to_string input##value); Js._true in 151 | input##oninput <- Dom.handler on_input; 152 | input##className <- Js.string class_; 153 | let set_value value = input##value <- Js.string value in 154 | (input :> t), set_value 155 | 156 | let text_input ?class_ ?on_change value = 157 | let text_input, _ = text_input' ?class_ ?on_change value in 158 | text_input 159 | 160 | let run html = 161 | let on_load _ = 162 | let html = html () in 163 | let body = 164 | let find_tag name = 165 | let elements = 166 | Dom_html.window##document##getElementsByTagName(Js.string name) 167 | in 168 | let element = 169 | Js.Opt.get elements##item(0) 170 | (fun () -> failwith ("find_tag("^name^")")) 171 | in 172 | element 173 | in 174 | find_tag "body" 175 | in 176 | let _: t = body##appendChild(html) in 177 | Js._false 178 | in 179 | Dom_html.window##onload <- Dom.handler on_load 180 | 181 | let get_hash () = 182 | let fragment = Dom_html.window##location##hash |> Js.to_string in 183 | if fragment = "" then 184 | "" 185 | else if fragment.[0] = '#' then 186 | String.sub fragment 1 (String.length fragment - 1) 187 | else 188 | fragment 189 | 190 | let set_hash hash = 191 | Dom_html.window##location##hash <- Js.string hash 192 | 193 | let on_hash_change handler = 194 | let handler _ = handler (); Js._true in 195 | Dom_html.window##onhashchange <- Dom.handler handler 196 | -------------------------------------------------------------------------------- /html.mli: -------------------------------------------------------------------------------- 1 | (******************************************************************************) 2 | (* Copyright (c) 2016 DooMeeR *) 3 | (* *) 4 | (* Permission is hereby granted, free of charge, to any person obtaining *) 5 | (* a copy of this software and associated documentation files (the *) 6 | (* "Software"), to deal in the Software without restriction, including *) 7 | (* without limitation the rights to use, copy, modify, merge, publish, *) 8 | (* distribute, sublicense, and/or sell copies of the Software, and to *) 9 | (* permit persons to whom the Software is furnished to do so, subject to *) 10 | (* the following conditions: *) 11 | (* *) 12 | (* The above copyright notice and this permission notice shall be *) 13 | (* included in all copies or substantial portions of the Software. *) 14 | (* *) 15 | (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *) 16 | (* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *) 17 | (* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND *) 18 | (* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE *) 19 | (* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION *) 20 | (* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION *) 21 | (* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *) 22 | (******************************************************************************) 23 | 24 | (** Simple interface to create HTML entities using js_of_ocaml. *) 25 | 26 | type t 27 | 28 | val alert: string -> unit 29 | 30 | val text: string -> t 31 | val text': string -> t * (string -> unit) 32 | val img: ?class_: string -> ?alt: string -> ?title: string -> string -> t 33 | val a: 34 | ?class_: string -> ?href: string -> ?on_click: (unit -> unit) -> t list -> t 35 | val button: ?class_: string -> ?on_click: (unit -> unit) -> t list -> t 36 | val p: ?class_: string -> t list -> t 37 | val p_text: ?class_: string -> string -> t 38 | val p': ?class_: string -> t list -> t * (t list -> unit) 39 | val div: ?class_: string -> t list -> t 40 | val div': ?class_: string -> t list -> t * (t list -> unit) 41 | val span: ?class_: string -> t list -> t 42 | val span': ?class_: string -> t list -> t * (t list -> unit) 43 | val checkbox_input: ?class_: string -> ?on_change: (bool -> unit) -> bool -> t 44 | val checkbox_input': 45 | ?class_: string -> ?on_change: (bool -> unit) -> bool -> t * (bool -> unit) 46 | val radio_input: 47 | ?class_: string -> ?on_change: (bool -> unit) -> ?name: string -> bool -> t 48 | val radio_input': 49 | ?class_: string -> ?on_change: (bool -> unit) -> ?name: string -> 50 | bool -> t * (bool -> unit) 51 | val text_input: ?class_: string -> ?on_change: (string -> unit) -> string -> t 52 | val text_input': 53 | ?class_: string -> ?on_change: (string -> unit) -> string -> 54 | t * (string -> unit) 55 | 56 | val run: (unit -> t) -> unit 57 | 58 | val get_hash: unit -> string 59 | val set_hash: string -> unit 60 | val on_hash_change: (unit -> unit) -> unit 61 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Factorio Planner 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /recipes.ml: -------------------------------------------------------------------------------- 1 | (******************************************************************************) 2 | (* Copyright (c) 2016 DooMeeR *) 3 | (* *) 4 | (* Permission is hereby granted, free of charge, to any person obtaining *) 5 | (* a copy of this software and associated documentation files (the *) 6 | (* "Software"), to deal in the Software without restriction, including *) 7 | (* without limitation the rights to use, copy, modify, merge, publish, *) 8 | (* distribute, sublicense, and/or sell copies of the Software, and to *) 9 | (* permit persons to whom the Software is furnished to do so, subject to *) 10 | (* the following conditions: *) 11 | (* *) 12 | (* The above copyright notice and this permission notice shall be *) 13 | (* included in all copies or substantial portions of the Software. *) 14 | (* *) 15 | (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *) 16 | (* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *) 17 | (* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND *) 18 | (* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE *) 19 | (* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION *) 20 | (* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION *) 21 | (* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *) 22 | (******************************************************************************) 23 | 24 | (* Type definitions and helpful functions are defined in factorio.ml. *) 25 | open Factorio 26 | 27 | (******************************************************************************) 28 | (* Makers *) 29 | (******************************************************************************) 30 | 31 | (* Maker definitions. Makers are items such as assembling machines, 32 | that are used to make recipes. *) 33 | 34 | (* Usage: maker name crafting_speed *) 35 | 36 | (* Lava84flow: miners now have a just mining speed function instead 37 | of needing mining power as well so this might break everything *) 38 | 39 | let burner_mining_drill = maker "Burner Mining Drill" 0.25 40 | let electric_mining_drill = maker "Electric Mining Drill" 0.5 41 | let assembling_machine_1 = maker "Assembling Machine 1" 0.5 42 | let assembling_machine_2 = maker "Assembling Machine 2" 0.75 43 | let assembling_machine_3 = maker "Assembling Machine 3" 1.25 44 | let stone_furnace = maker "Stone Furnace" 1. 45 | let steel_furnace = maker "Steel Furnace" 2. 46 | let electric_furnace = maker "Electric Furnace" 2. 47 | let chemical_plant_ = maker "Chemical Plant" 1. 48 | let centrifuge = maker "Centrifuge" 1. 49 | let pumpjack = maker "Pumpjack" 1. 50 | let rocket_silo = maker "Rocket Silo" 0.001 51 | 52 | (* Shorthands for some commonly-used lists of makers. *) 53 | 54 | let drill = [ burner_mining_drill; electric_mining_drill ] 55 | let am1 = [ assembling_machine_1; assembling_machine_2; assembling_machine_3 ] 56 | let am2 = [ assembling_machine_2; assembling_machine_3 ] 57 | let am3 = [ assembling_machine_3 ] 58 | let furnace = [ stone_furnace; steel_furnace; electric_furnace ] 59 | let chemical_plant = [ chemical_plant_ ] 60 | 61 | (******************************************************************************) 62 | (* Resources *) 63 | (******************************************************************************) 64 | 65 | (* Resource definitions. Resources are craftable items, such as potions. 66 | Note that makers themselves are craftable and are thus also resources. *) 67 | 68 | (* Usage: res name makers crafting_time ingredients 69 | 70 | [makers] is a list of makers. 71 | [ingredients] is a list of (float, resource) pairs, where the float 72 | is the amount of the resource which is required. 73 | 74 | Optional arguments: 75 | - use [~style: Global] (without the brackets) so that 76 | the resource is global by default; 77 | - use [~count: 2.] for recipes which produce 2 items at a time. *) 78 | 79 | (* DON'T FORGET TO ADD THE RESOURCE TO THE LIST AT THE END OF THIS FILE! *) 80 | 81 | (* Coal is only counted to make Plastic Bars, not by Furnaces. 82 | You can change this by adding the Coal ingredient to the plate recipes. *) 83 | 84 | (* I assume 1 Crude Oil per second per pumpjack, but obviously this changes 85 | all the time. *) 86 | 87 | (* I assume Basic Oil Processing is used. 88 | You can change the line: 89 | let petroleum_gas = petroleum_gas_basic 90 | to one of: 91 | let petroleum_gas = petroleum_gas_advanced 92 | let petroleum_gas = petroleum_gas_cracking 93 | to change the oil+water to gas ratio. 94 | 95 | Basically: 96 | - with Basic Oil Processing, you need 2.5 Oil for 1 Gas; 97 | - with Advanced Oil Processing, you need 1.9 Oil (and 1 Water) for 1 Gas; 98 | - with Advanced Oil Processing + Cracking, you need 1.2 Oil (and 1.3 Water) 99 | for 1 Gas. 100 | So you can simply keep the Basic Oil Processing and know that the amount 101 | of oil can be divided by roughly 2 with Advanced Oil Processing and 102 | cracking. (Who cares about Water?) 103 | The amount of refineries is also devided by 2 but you need to add 104 | chemical plants (with 5 refineries you have a perfect ratio 105 | with 1 heavy cracking and 7 light cracking plants) 106 | and water pumps (1 pump for 6 groups of 5 refineries + 8 cracking plants). *) 107 | 108 | (* I use Petroleum Gas for Rocket Fuel but this is obviously non optimal, 109 | it's just for simplification's sake. *) 110 | 111 | (* The order in this file follows the order in the Resources page of 112 | the Wiki, except when this would mess the dependencies. *) 113 | 114 | (* Resources *) 115 | 116 | let wood = 117 | res "Wood" [] 1. ~style: Global 118 | [] 119 | 120 | (* Lava84flow: the mining thing also means that ore hardness is no 121 | longer a thing, so more things to break here *) 122 | 123 | (* Lava84flow: Someone double check the craft times for raws *) 124 | 125 | let coal = 126 | res "Coal" drill 1. ~style: Global ~allow_productivity: true 127 | [] 128 | let iron_ore = 129 | res "Iron Ore" drill 1. ~allow_productivity: true 130 | [] 131 | let copper_ore = 132 | res "Copper Ore" drill 1. ~allow_productivity: true 133 | [] 134 | let stone = 135 | res "Stone" drill 1. ~allow_productivity: true 136 | [] 137 | let water = 138 | res "Water" [ maker "Offshore Pump" 1. ] 1. ~count: 1200. ~style: Global 139 | [] 140 | let crude_oil = 141 | res "Crude Oil" [ pumpjack ] 1. ~count: 10. ~allow_productivity: true 142 | [] 143 | let uranium_ore = 144 | res "Uranium Ore" drill 2. ~allow_productivity: true 145 | [] 146 | (* TODO: This process also produces 0.993 Uranium 238 *) 147 | let uranium_235 = 148 | res "Uranium-235" [ centrifuge ] 12. ~count: 0.007 ~allow_productivity: true 149 | [ 10., uranium_ore ] 150 | 151 | (* Intermediate Products *) 152 | 153 | (* Lava84flow: wood and raw wood have been merged*) 154 | 155 | (* let wood = 156 | res "Wood" am1 0.5 ~count: 2. ~allow_productivity: true 157 | [ 1., raw_wood ] *) 158 | 159 | let iron_plate = 160 | res "Iron Plate" furnace 3.2 ~style: Global ~allow_productivity: true 161 | [ 1., iron_ore ] 162 | let copper_plate = 163 | res "Copper Plate" furnace 3.2 ~style: Global ~allow_productivity: true 164 | [ 1., copper_ore ] 165 | let steel_plate = 166 | res "Steel Plate" furnace 16. ~allow_productivity: true 167 | [ 5., iron_plate ] 168 | let stone_brick = 169 | res "Stone Brick" furnace 3.2 ~allow_productivity: true 170 | [ 2., stone ] 171 | 172 | let petroleum_gas_basic = 173 | res "Petroleum Gas" [ maker "Basic Oil Processing" 1. ] 5. 174 | ~count: 40. ~style: Global ~allow_productivity: true 175 | [ 100., crude_oil ] 176 | let petroleum_gas_advanced = 177 | res "Petroleum Gas" [ maker "Advanced Oil Processing" 1. ] 5. 178 | ~count: 55. ~style: Global ~allow_productivity: true 179 | [ 100., crude_oil; 50., water ] 180 | let petroleum_gas_cracking = 181 | (* 1 Heavy Oil becomes 3/4 = 0.75 Light Oil. 182 | 0.75 + 4.5 = 5.25 Light Oil becomes 5.25 * 2 / 3 = 3.5 Petroleum Gas. 183 | So the end result is 5.5 + 3.5 = 9 Petroleum Gas. 184 | 1 Heavy Oil requires 0.75 Water to be cracked. 185 | 5.25 Light Oil requires 5.25 Water to be cracked. 186 | So the end result is that 0.75 + 5.25 + 5 = 11 Water is required. *) 187 | res "Petroleum Gas" [ maker "Advanced Oil Processing + Cracking" 1. ] 5. 188 | ~count: 90. ~style: Global ~allow_productivity: true 189 | [ 100., crude_oil; 110., water ] 190 | let petroleum_gas = petroleum_gas_basic 191 | let sulfur = 192 | res "Sulfur" chemical_plant 1. ~count: 2. ~allow_productivity: true 193 | [ 30., water; 30., petroleum_gas ] 194 | let sulfuric_acid = 195 | res "Sulfuric Acid" chemical_plant 1. ~count: 50. ~allow_productivity: true 196 | [ 1., iron_plate; 5., sulfur; 100., water ] 197 | let plastic_bar = 198 | res "Plastic Bar" chemical_plant 1. ~count: 2. ~allow_productivity: true 199 | [ 1., coal; 20., petroleum_gas ] 200 | let battery = 201 | res "Battery" chemical_plant 5. ~allow_productivity: true 202 | [ 1., iron_plate; 1., copper_plate; 20., sulfuric_acid ] 203 | let iron_stick = 204 | res "Iron Stick" am1 0.5 ~count: 2. ~allow_productivity: true 205 | [ 1., iron_plate ] 206 | let iron_gear_wheel = 207 | res "Iron Gear Wheel" am1 0.5 ~allow_productivity: true 208 | [ 2., iron_plate ] 209 | let copper_cable = 210 | res "Copper Cable" am1 0.5 ~count: 2. ~allow_productivity: true 211 | [ 1., copper_plate ] 212 | let electronic_circuit = 213 | res "Electronic Circuit" am1 0.5 ~allow_productivity: true 214 | [ 1., iron_plate; 3., copper_cable ] 215 | let advanced_circuit = 216 | res "Advanced Circuit" am1 6. ~allow_productivity: true 217 | [ 2., electronic_circuit; 2., plastic_bar; 4., copper_cable ] 218 | let processing_unit = 219 | res "Processing Unit" am2 10. ~allow_productivity: true 220 | [ 20., electronic_circuit; 2., advanced_circuit; 5., sulfuric_acid ] 221 | let pipe = 222 | res "Pipe" am1 0.5 223 | [ 1., iron_plate ] 224 | let engine_unit = 225 | res "Engine Unit" am1 10. ~allow_productivity: true 226 | [ 1., steel_plate; 1., iron_gear_wheel; 2., pipe ] 227 | let heavy_oil = 228 | res "Heavy Oil" [ maker "Basic Oil Processing" 1. ] 5. 229 | ~count: 3. ~allow_productivity: true 230 | [ 10., crude_oil ] 231 | let light_oil = 232 | res "Light Oil" [ maker "Basic Oil Processing" 1. ] 5. 233 | ~count: 3. ~allow_productivity: true 234 | [ 10., crude_oil ] 235 | let lubricant = 236 | res "Lubricant" chemical_plant 1. ~count: 10. ~allow_productivity: true 237 | [ 10., heavy_oil ] 238 | let electric_engine_unit = 239 | res "Electric Engine Unit" am2 10. ~allow_productivity: true 240 | [ 1., engine_unit; 2., electronic_circuit; 15., lubricant ] 241 | let flying_robot_frame = 242 | res "Flying Robot Frame" am1 20. ~allow_productivity: true 243 | [ 1., electric_engine_unit; 2., battery; 1., steel_plate; 244 | 3., electronic_circuit ] 245 | let transport_belt = 246 | res "Transport Belt" am1 0.5 ~count: 2. 247 | [ 1., iron_plate; 1., iron_gear_wheel ] 248 | let inserter = 249 | res "Inserter" am1 0.5 250 | [ 1., iron_plate; 1., iron_gear_wheel; 1., electronic_circuit ] 251 | let fast_inserter = 252 | res "Fast Inserter" am1 0.5 253 | [ 1., inserter; 2., iron_plate; 2., electronic_circuit ] 254 | let filter_inserter = 255 | res "Filter Inserter" am1 0.5 256 | [ 1., fast_inserter; 4., electronic_circuit ] 257 | let empty_barrel = 258 | res "Empty Barrel" am1 1. ~allow_productivity: true 259 | [ 1., steel_plate ] 260 | let explosives = 261 | res "Explosives" chemical_plant 5. ~count: 2. ~allow_productivity: true 262 | [ 1., sulfur; 1., coal; 10., water ] 263 | 264 | (* Weapons *) 265 | 266 | let land_mine = 267 | res "Land Mine" am1 5. ~count: 4. 268 | [ 1., steel_plate; 2., explosives ] 269 | let grenade = 270 | res "Grenade" am1 8. 271 | [ 10., coal; 5., iron_plate ] 272 | let cluster_grenade = 273 | res "Cluster Grenade" am1 8. 274 | [ 7., grenade; 5., explosives; 5., steel_plate ] 275 | let poison_capsule = 276 | res "Poison Capsule" am1 8. 277 | [ 3., steel_plate; 3., electronic_circuit; 10., coal ] 278 | let slowdown_capsule = 279 | res "Slowdown Capsule" am1 8. 280 | [ 2., steel_plate; 2., electronic_circuit; 5., coal ] 281 | let atomic_bomb = 282 | res "Atomic Bomb" am1 50. 283 | [ 10., explosives; 20., processing_unit; 30., uranium_235 ] 284 | let speed_module = 285 | res "Speed Module" am1 15. 286 | [ 5., advanced_circuit; 5., electronic_circuit ] 287 | 288 | (* Ammo *) 289 | 290 | let firearm_magazine = 291 | res "Firearm Magazine" am1 1. 292 | [ 4., iron_plate ] 293 | let piercing_rounds_magazine = 294 | res "Piercing Rounds Magazine" am1 3. 295 | [ 1., firearm_magazine; 1., steel_plate; 5., copper_plate ] 296 | let defender_capsule = 297 | res "Defender Capsule" am1 8. 298 | [ 3., iron_gear_wheel; 1., flying_robot_frame; 299 | 1., piercing_rounds_magazine ] 300 | let distractor_capsule = 301 | res "Distractor Capsule" am1 15. 302 | [ 3., advanced_circuit; 4., defender_capsule ] 303 | let destroyer_capsule = 304 | res "Destroyer Capsule" am1 15. 305 | [ 1., speed_module; 4., distractor_capsule ] 306 | let shotgun_shells = 307 | res "Shotgun Shells" am1 3. 308 | [ 2., copper_plate; 2., iron_plate ] 309 | let piercing_shotgun_shells = 310 | res "Piercing Shotgun Shells" am1 8. 311 | [ 2., shotgun_shells; 5., copper_plate; 2., steel_plate ] 312 | let rocket = 313 | res "Rocket" am1 8. 314 | [ 1., electronic_circuit; 1., explosives; 2., iron_plate ] 315 | let explosive_rocket = 316 | res "Explosive Rocket" am1 8. 317 | [ 1., rocket; 2., explosives ] 318 | let flamethrower_ammo = 319 | res "Flamethrower Ammo" chemical_plant 6. 320 | [ 5., steel_plate; 100., crude_oil ] 321 | let cannon_shell = 322 | res "Cannon Shell" am1 8. 323 | [ 2., steel_plate; 2., plastic_bar; 1., explosives ] 324 | let explosive_cannon_shell = 325 | res "Explosive Cannon Shell" am1 8. 326 | [ 2., steel_plate; 2., plastic_bar; 2., explosives ] 327 | 328 | (* Modules *) 329 | 330 | let efficiency_module = 331 | res "Efficiency Module" am1 15. 332 | [ 5., advanced_circuit; 5., electronic_circuit ] 333 | let efficiency_module_2 = 334 | res "Efficiency Module 2" am1 30. 335 | [ 5., advanced_circuit; 5., processing_unit; 4., efficiency_module ] 336 | let efficiency_module_3 = 337 | res "Efficiency Module 3" am1 60. 338 | [ 5., advanced_circuit; 5., processing_unit; 5., efficiency_module_2 ] 339 | let productivity_module = 340 | res "Productivity Module" am1 15. 341 | [ 5., advanced_circuit; 5., electronic_circuit ] 342 | let productivity_module_2 = 343 | res "Productivity Module 2" am1 30. 344 | [ 5., advanced_circuit; 5., processing_unit; 4., productivity_module ] 345 | let productivity_module_3 = 346 | res "Productivity Module 3" am1 60. 347 | [ 5., advanced_circuit; 5., processing_unit; 5., productivity_module_2 ] 348 | let speed_module_2 = 349 | res "Speed Module 2" am1 30. 350 | [ 5., advanced_circuit; 5., processing_unit; 4., speed_module ] 351 | let speed_module_3 = 352 | res "Speed Module 3" am1 60. 353 | [ 5., advanced_circuit; 5., processing_unit; 5., speed_module_2 ] 354 | 355 | (* Special *) 356 | 357 | let car = 358 | res "Car" am1 2. 359 | [ 8., engine_unit; 20., iron_plate; 5., steel_plate ] 360 | let tank = 361 | res "Tank" am1 5. 362 | [ 32., engine_unit; 50., steel_plate; 15., iron_gear_wheel; 363 | 10., advanced_circuit ] 364 | let logistic_robot = 365 | res "Logistic Robot" am1 0.5 366 | [ 1., flying_robot_frame; 2., advanced_circuit ] 367 | let construction_robot = 368 | res "Construction Robot" am1 0.5 369 | [ 1., flying_robot_frame; 2., electronic_circuit ] 370 | let roboport = 371 | res "Roboport" am1 5. 372 | [ 45., steel_plate; 45., iron_gear_wheel; 45., advanced_circuit ] 373 | 374 | (* Transport Belts *) 375 | 376 | let underground_belt = 377 | res "Underground Belt" am1 1. ~count: 2. 378 | [ 5., transport_belt; 10., iron_plate ] 379 | let splitter = 380 | res "Splitter" am1 1. 381 | [ 5., electronic_circuit; 5., iron_plate; 4., transport_belt ] 382 | let fast_transport_belt = 383 | res "Fast Transport Belt" am1 0.5 384 | [ 1., transport_belt; 5., iron_gear_wheel ] 385 | let fast_underground_belt = 386 | res "Fast Underground Belt" am1 2. ~count: 2. 387 | [ 40., iron_gear_wheel; 2., underground_belt ] 388 | let fast_splitter = 389 | res "Fast Splitter" am1 2. 390 | [ 1., splitter; 10., iron_gear_wheel; 10., electronic_circuit ] 391 | let express_transport_belt = 392 | res "Express Transport Belt" am2 0.5 393 | [ 1., fast_transport_belt; 10., iron_gear_wheel; 20., lubricant ] 394 | let express_underground_belt = 395 | res "Express Underground Belt" am2 2. ~count: 2. 396 | [ 80., iron_gear_wheel; 2., fast_underground_belt; 40., lubricant ] 397 | let express_splitter = 398 | res "Express Splitter" am2 2. 399 | [ 1., fast_splitter; 10., iron_gear_wheel; 10., advanced_circuit; 400 | 80., lubricant ] 401 | 402 | (* Inserters *) 403 | 404 | let burner_inserter = 405 | res "Burner Inserter" am1 0.5 406 | [ 1., iron_plate; 1., iron_gear_wheel ] 407 | let long_handed_inserter = 408 | res "Long Handed Inserter" am1 0.5 409 | [ 1., inserter; 1., iron_plate; 1., iron_gear_wheel ] 410 | let stack_inserter = 411 | res "Stack Inserter" am1 0.5 412 | [ 15., iron_gear_wheel; 15., electronic_circuit; 1., advanced_circuit; 1., fast_inserter ] 413 | let stack_filter_inserter = 414 | res "Stack Filter Inserter" am1 0.5 415 | [ 1., stack_inserter; 5., electronic_circuit ] 416 | 417 | (* Storage *) 418 | 419 | let wooden_chest = 420 | res "Wooden Chest" am1 0.5 421 | [ 4., wood ] 422 | let iron_chest = 423 | res "Iron Chest" am1 0.5 424 | [ 8., iron_plate ] 425 | let steel_chest = 426 | res "Steel Chest" am1 0.5 427 | [ 8., steel_plate ] 428 | let active_provider_chest = 429 | res "Active Provider Chest" am1 0.5 430 | [ 1., steel_chest; 3., electronic_circuit; 1., advanced_circuit ] 431 | let passive_provider_chest = 432 | res "Passive Provider Chest" am1 0.5 433 | [ 1., steel_chest; 3., electronic_circuit; 1., advanced_circuit ] 434 | let storage_chest = 435 | res "Storage Chest" am1 0.5 436 | [ 1., steel_chest; 3., electronic_circuit; 1., advanced_circuit ] 437 | let requester_chest = 438 | res "Requester Chest" am1 0.5 439 | [ 1., steel_chest; 3., electronic_circuit; 1., advanced_circuit ] 440 | 441 | (* Defensive Structures *) 442 | 443 | let wall = 444 | res "Wall" am1 0.5 445 | [ 5., stone_brick ] 446 | let gate = 447 | res "Gate" am1 0.5 448 | [ 2., electronic_circuit; 2., steel_plate; 1., wall ] 449 | let gun_turret = 450 | res "Gun Turret" am1 8. 451 | [ 20., iron_plate; 10., copper_plate; 10., iron_gear_wheel ] 452 | let flamethrower_turret = 453 | res "Flamethrower Turret" am1 20. 454 | [ 30., steel_plate; 15., iron_gear_wheel; 10., pipe; 5., engine_unit ] 455 | let laser_turret = 456 | res "Laser Turret" am1 20. 457 | [ 20., steel_plate; 20., electronic_circuit; 12., battery ] 458 | 459 | (* Miscellaneous *) 460 | 461 | let concrete = 462 | res "Concrete" am2 10. ~count: 10. 463 | [ 5., stone_brick; 1., iron_ore; 100., water ] 464 | 465 | (* Machines & Furnaces *) 466 | 467 | (* Because makers exist with the same name, makers as resources are 468 | prefixed with "r_". *) 469 | let r_stone_furnace = 470 | res "Stone Furnace" am1 0.5 471 | [ 5., stone ] 472 | let r_burner_mining_drill = 473 | res "Burner Mining Drill" am1 2. 474 | [ 1., r_stone_furnace; 3., iron_plate; 3., iron_gear_wheel ] 475 | let r_electric_mining_drill = 476 | res "Electric Mining Drill" am1 2. 477 | [ 10., iron_plate; 5., iron_gear_wheel; 3., electronic_circuit ] 478 | let r_steel_furnace = 479 | res "Steel Furnace" am1 3. 480 | [ 6., steel_plate; 10., stone_brick ] 481 | let r_electric_furnace = 482 | res "Electric Furnace" am1 5. 483 | [ 10., steel_plate; 10., stone_brick; 5., advanced_circuit ] 484 | let r_assembling_machine_1 = 485 | res assembling_machine_1.name am2 0.5 486 | [ 3., electronic_circuit; 5., iron_gear_wheel; 9., iron_plate ] 487 | let r_assembling_machine_2 = 488 | res assembling_machine_2.name am2 0.5 489 | [ 3., electronic_circuit; 5., iron_gear_wheel; 2., steel_plate; 490 | 1., r_assembling_machine_1 ] 491 | let r_assembling_machine_3 = 492 | res assembling_machine_3.name am1 0.5 493 | [ 2., r_assembling_machine_2; 4., speed_module ] 494 | let r_lab = 495 | res "Lab" am1 2. 496 | [ 4., transport_belt; 10., iron_gear_wheel; 10., electronic_circuit ] 497 | let r_centrifuge = 498 | res "Centrifuge" am1 4. 499 | [ 100., concrete; 50.,steel_plate; 100., advanced_circuit; 500 | 100., iron_gear_wheel] 501 | let r_rocket_silo = 502 | res rocket_silo.name am1 30. 503 | [ 1000., concrete; 200., electric_engine_unit; 100., pipe; 504 | 200., processing_unit; 1000., steel_plate ] 505 | let beacon = 506 | res "Beacon" am1 15. 507 | [ 20., electronic_circuit; 20., advanced_circuit; 10., steel_plate; 508 | 10., copper_cable ] 509 | let radar = 510 | res "Radar" am1 0.5 511 | [ 5., electronic_circuit; 10., iron_plate; 5., iron_gear_wheel ] 512 | 513 | (* Electric Network *) 514 | 515 | let small_electric_pole = 516 | res "Small Electric Pole" am1 0.5 ~count: 2. 517 | [ 1., wood; 2., copper_cable ] 518 | let medium_electric_pole = 519 | res "Medium Electric Pole" am1 0.5 520 | [ 2., steel_plate; 2., copper_plate; 4., iron_stick ] 521 | let big_electric_pole = 522 | res "Big Electric Pole" am1 0.5 523 | [ 5., steel_plate; 5., copper_plate; 8., iron_stick ] 524 | let substation = 525 | res "Substation" am1 0.5 526 | [ 10., steel_plate; 5., advanced_circuit; 5., copper_plate ] 527 | let boiler = 528 | res "Boiler" am1 0.5 529 | [ 1., r_stone_furnace; 4., pipe ] 530 | let steam_engine = 531 | res "Steam Engine" am1 0.5 532 | [ 8., iron_gear_wheel; 5., pipe; 10., iron_plate ] 533 | let solar_panel = 534 | res "Solar Panel" am1 10. 535 | [ 5., steel_plate; 15., electronic_circuit; 5., copper_plate ] 536 | let accumulator = 537 | res "Accumulator" am1 10. 538 | [ 2., iron_plate; 5., battery ] 539 | let lamp = 540 | res "Lamp" am1 0.5 541 | [ 1., electronic_circuit; 3., iron_stick; 1., iron_plate ] 542 | 543 | (* Railway Network *) 544 | 545 | let straight_rail = 546 | res "Straight Rail" am1 0.5 ~count: 2. 547 | [ 1., stone; 1., iron_stick; 1., steel_plate ] 548 | 549 | (* Liquid Network *) 550 | 551 | let pipe_to_ground = 552 | res "Pipe To Ground" am1 0.5 ~count: 2. 553 | [ 10., pipe; 5., iron_plate ] 554 | let r_offshore_pump = 555 | res "Offshore Pump" am1 0.5 556 | [ 2., electronic_circuit; 1., pipe; 1., iron_gear_wheel ] 557 | let storage_tank = 558 | res "Storage Tank" am1 3. 559 | [ 20., iron_plate; 5., steel_plate ] 560 | let r_oil_refinery = 561 | res "Oil Refinery" am1 8. 562 | [ 10., pipe; 15., steel_plate; 10., stone_brick; 10., iron_gear_wheel; 563 | 10., electronic_circuit ] 564 | let r_chemical_plant = 565 | res "Chemical Plant" am1 5. 566 | [ 5., steel_plate; 5., iron_gear_wheel; 5., electronic_circuit; 567 | 5., pipe ] 568 | let r_pumpjack = 569 | res "Pumpjack" am1 5. 570 | [ 5., steel_plate; 10., iron_gear_wheel; 5., electronic_circuit; 571 | 10., pipe ] 572 | let pump = 573 | res "Pump" am1 2. 574 | [ 1., engine_unit; 1., steel_plate; 1., pipe ] 575 | 576 | (* Rocket Compenents *) 577 | 578 | let low_density_structure = 579 | res "Low Density Structure" am1 20. ~allow_productivity: true 580 | [ 2., steel_plate; 20., copper_plate; 5., plastic_bar ] 581 | let rocket_control_unit = 582 | res "Rocket Control Unit" am1 30. ~allow_productivity: true 583 | [ 1., processing_unit; 1., speed_module ] 584 | let solid_fuel_from_heavy_oil = 585 | res "Solid Fuel" chemical_plant 3. ~allow_productivity: true 586 | [ 20., heavy_oil ] 587 | let solid_fuel_from_light_oil = 588 | res "Solid Fuel" chemical_plant 3. ~allow_productivity: true 589 | [ 10., light_oil ] 590 | let solid_fuel_from_petroleum_gas = 591 | res "Solid Fuel" chemical_plant 3. ~allow_productivity: true 592 | [ 20., petroleum_gas ] 593 | let solid_fuel = solid_fuel_from_petroleum_gas 594 | let rocket_fuel = 595 | res "Rocket Fuel" am1 30. ~allow_productivity: true 596 | [ 10., solid_fuel; 10., light_oil ] 597 | let rocket_part = 598 | res "Rocket Part" [ maker "Rocket Silo" 1. ] 3. ~allow_productivity: true 599 | [ 10., low_density_structure; 10., rocket_fuel; 10., rocket_control_unit ] 600 | let satellite = 601 | res "Satellite" am1 5. 602 | [ 100., low_density_structure; 100., solar_panel; 100., accumulator; 603 | 5., radar; 100., processing_unit; 50., rocket_fuel ] 604 | 605 | (* new in 0.15 *) 606 | 607 | let nuclear_reactor = 608 | res "Nuclear reactor" am1 8. 609 | [ 500., concrete; 500., steel_plate; 500., advanced_circuit; 500., copper_plate ] 610 | let heat_pipe = 611 | res "Heat pipe" am1 1. 612 | [ 10., steel_plate; 20., copper_plate ] 613 | let heat_exchanger = 614 | res "Heat exchanger" am1 3. 615 | [ 10., steel_plate; 100., copper_plate; 10., pipe ] 616 | let steam_turbine = 617 | res "Steam turbine" am1 3. 618 | [ 50., iron_gear_wheel; 50., copper_plate; 20., pipe ] 619 | let fluid_wagon = 620 | res "Fluid wagon" am1 1.5 621 | [ 10., iron_gear_wheel; 16., steel_plate; 8., pipe; 3., storage_tank] 622 | 623 | (* Science packs *) 624 | 625 | let automation_science_pack = 626 | res "Automation Science Pack" am1 5. ~allow_productivity: true 627 | [ 1., copper_plate; 1., iron_gear_wheel; ] 628 | 629 | let logistic_science_pack = 630 | res "Logistic Science Pack" am1 6. ~allow_productivity: true 631 | [ 1., inserter; 1., transport_belt; ] 632 | 633 | (* Lava84flow: solid fuel for this one might need some fixing*) 634 | let chemical_science_pack = 635 | res "Chemical Science Pack" am1 24. ~count: 2. ~allow_productivity: true 636 | [ 3., advanced_circuit; 2., engine_unit; 1., sulfur; ] 637 | 638 | let military_science_pack = 639 | res "Military Science Pack" am1 10. ~count: 2. ~allow_productivity: true 640 | [ 1., grenade; 1., piercing_rounds_magazine; 2., wall; ] 641 | 642 | let production_science_pack = 643 | res "Production Science Pack" am1 21. ~count: 3. ~allow_productivity: true 644 | [ 1., r_electric_furnace; 1., productivity_module; 30., straight_rail; ] 645 | 646 | let utility_science_pack = 647 | res "Utility Science Pack" am1 21. ~count: 3. ~allow_productivity: true 648 | [ 1., flying_robot_frame; 3., low_density_structure; 2., processing_unit; ] 649 | 650 | let space_science_pack = 651 | (* TODO: we could replace the 1. by the time it takes to launch a rocket. *) 652 | res "Space Science Pack" [ rocket_silo ] 1. ~count: 1000. 653 | [ 100., rocket_part; 1., satellite ] 654 | 655 | (* Armor *) 656 | 657 | (* iron_armor; *) 658 | (* heavy_armor; *) 659 | let basic_modular_armor = 660 | res "Modular armor" am1 15. 661 | [ 30., advanced_circuit; 50., steel_plate ] 662 | 663 | let power_armor = 664 | res "Power armor" am1 20. 665 | [ 40., processing_unit; 20., electric_engine_unit; 40., steel_plate ] 666 | 667 | let power_armor_mk2 = 668 | res "Power armor MK2" am1 25. 669 | [ 25., efficiency_module_2; 25., speed_module_2; 60., processing_unit; 40., electric_engine_unit; 30., low_density_structure ] 670 | 671 | (* Modular Armor *) 672 | 673 | (* night_vision; *) 674 | let personal_battery = 675 | res "Personal battery" am1 10. 676 | [ 5., battery; 10., steel_plate ] 677 | 678 | let personal_battery_mk2 = 679 | res "Personal battery MK2" am1 10. 680 | [ 10., personal_battery; 15., processing_unit; 5., low_density_structure ] 681 | 682 | let energy_shield = 683 | res "Energy shield" am1 10. 684 | [ 5., advanced_circuit; 10., steel_plate ] 685 | 686 | let energy_shield_mk2 = 687 | res "Energy Shield MK2" am1 10. 688 | [ 10., energy_shield; 10., processing_unit; 5., low_density_structure ] 689 | 690 | let portable_solar_panel = 691 | res "Portable solar panel" am2 10. 692 | [ 1., solar_panel; 2., advanced_circuit; 5., steel_plate ] 693 | 694 | let portable_fusion_reactor = 695 | res "Portable fusion reactor" am1 10. 696 | [ 250., processing_unit; 50., low_density_structure ] 697 | 698 | let personal_laser_defense = 699 | res "Personal laser defense" am1 10. 700 | [ 20., processing_unit; 5., low_density_structure; 5., laser_turret ] 701 | 702 | let discharge_defense = 703 | res "Discharge defense" am1 10. 704 | [ 5., processing_unit; 20., steel_plate; 10., laser_turret ] 705 | 706 | let basic_exoskeleton_equipment = 707 | res "Exoskeleton" am1 10. 708 | [ 10., processing_unit; 30., electric_engine_unit; 20., steel_plate ] 709 | 710 | (* artillery shell *) 711 | let artillery_shell = 712 | res "Artillery Shell" am1 15. 713 | [ 4., explosive_cannon_shell; 8., explosives; 1., radar ] 714 | 715 | 716 | (******************************************************************************) 717 | (* List of Resources *) 718 | (******************************************************************************) 719 | 720 | (* These are the resources that appear in the user interface. *) 721 | 722 | let resources = 723 | [ 724 | "Resources", 725 | [ 726 | wood; 727 | coal; 728 | iron_ore; 729 | copper_ore; 730 | stone; 731 | (* raw_fish; *) 732 | water; 733 | crude_oil; 734 | uranium_ore; 735 | ]; 736 | 737 | "Intermediate Products", 738 | [ 739 | iron_plate; 740 | copper_plate; 741 | steel_plate; 742 | stone_brick; 743 | sulfur; 744 | plastic_bar; 745 | battery; 746 | iron_stick; 747 | iron_gear_wheel; 748 | copper_cable; 749 | electronic_circuit; 750 | advanced_circuit; 751 | processing_unit; 752 | engine_unit; 753 | electric_engine_unit; 754 | flying_robot_frame; 755 | empty_barrel; 756 | explosives; 757 | uranium_235; 758 | 759 | petroleum_gas; 760 | light_oil; 761 | heavy_oil; 762 | sulfuric_acid; 763 | lubricant; 764 | ]; 765 | 766 | "Science Packs", 767 | [ 768 | automation_science_pack; 769 | logistic_science_pack; 770 | chemical_science_pack; 771 | military_science_pack; 772 | production_science_pack; 773 | utility_science_pack; 774 | space_science_pack; 775 | ]; 776 | 777 | "Player Equipment", 778 | [ 779 | (* iron_axe; *) 780 | (* steel_axe; *) 781 | 782 | (* Weapons *) 783 | (* pistol; *) 784 | (* submachine_gun; *) 785 | (* rocket_launcher; *) 786 | (* flamethrower; *) 787 | land_mine; 788 | (* shotgun; *) 789 | (* combat_shotgun; *) 790 | grenade; 791 | cluster_grenade; 792 | defender_capsule; 793 | poison_capsule; 794 | slowdown_capsule; 795 | distractor_capsule; 796 | destroyer_capsule; 797 | (* basic_electric_discharge_defense_remote; *) 798 | atomic_bomb; 799 | 800 | (* Ammo *) 801 | firearm_magazine; 802 | piercing_rounds_magazine; 803 | shotgun_shells; 804 | piercing_shotgun_shells; 805 | rocket; 806 | explosive_rocket; 807 | flamethrower_ammo; 808 | cannon_shell; 809 | explosive_cannon_shell; 810 | artillery_shell; 811 | 812 | (* Armor *) 813 | (* iron_armor; *) 814 | (* heavy_armor; *) 815 | basic_modular_armor; 816 | power_armor; 817 | power_armor_mk2; 818 | 819 | (* Modular Armor *) 820 | (* night_vision; *) 821 | personal_battery; 822 | personal_battery_mk2; 823 | energy_shield; 824 | energy_shield_mk2; 825 | portable_solar_panel; 826 | portable_fusion_reactor; 827 | personal_laser_defense; 828 | discharge_defense; 829 | basic_exoskeleton_equipment; 830 | ]; 831 | 832 | "Modules", 833 | [ 834 | efficiency_module; 835 | efficiency_module_2; 836 | efficiency_module_3; 837 | productivity_module; 838 | productivity_module_2; 839 | productivity_module_3; 840 | speed_module; 841 | speed_module_2; 842 | speed_module_3; 843 | ]; 844 | 845 | "Logistics", 846 | [ 847 | transport_belt; 848 | underground_belt; 849 | splitter; 850 | fast_transport_belt; 851 | fast_underground_belt; 852 | fast_splitter; 853 | express_transport_belt; 854 | express_underground_belt; 855 | express_splitter; 856 | 857 | burner_inserter; 858 | inserter; 859 | long_handed_inserter; 860 | fast_inserter; 861 | filter_inserter; 862 | stack_inserter; 863 | stack_filter_inserter; 864 | 865 | wooden_chest; 866 | iron_chest; 867 | steel_chest; 868 | active_provider_chest; 869 | passive_provider_chest; 870 | storage_chest; 871 | requester_chest; 872 | 873 | straight_rail; 874 | fluid_wagon; 875 | (* train_stop; *) 876 | (* rail_signal; *) 877 | (* rail_chain_signal; *) 878 | (* diesel_locomotive; *) 879 | (* cargo_wagon; *) 880 | 881 | (* Liquid Network *) 882 | pipe; 883 | pipe_to_ground; 884 | storage_tank; 885 | ]; 886 | 887 | "Machines & Furnaces & Pumps", 888 | [ 889 | r_burner_mining_drill; 890 | r_electric_mining_drill; 891 | r_stone_furnace; 892 | r_steel_furnace; 893 | r_electric_furnace; 894 | r_assembling_machine_1; 895 | r_assembling_machine_2; 896 | r_assembling_machine_3; 897 | r_lab; 898 | r_centrifuge; 899 | r_rocket_silo; 900 | r_oil_refinery; 901 | r_chemical_plant; 902 | r_pumpjack; 903 | r_offshore_pump; 904 | pump; 905 | ]; 906 | 907 | "Electric Network", 908 | [ 909 | small_electric_pole; 910 | medium_electric_pole; 911 | big_electric_pole; 912 | substation; 913 | boiler; 914 | steam_engine; 915 | solar_panel; 916 | accumulator; 917 | lamp; 918 | nuclear_reactor; 919 | heat_pipe; 920 | heat_exchanger; 921 | steam_turbine; 922 | ]; 923 | 924 | "Miscellaneous", 925 | [ 926 | concrete; 927 | solid_fuel; 928 | car; 929 | tank; 930 | (* red_wire; *) 931 | (* green_wire; *) 932 | logistic_robot; 933 | construction_robot; 934 | roboport; 935 | (* blueprint; *) 936 | (* deconstruction_planner; *) 937 | 938 | wall; 939 | gate; 940 | gun_turret; 941 | flamethrower_turret; 942 | laser_turret; 943 | (* Rocket Silo *) 944 | 945 | beacon; 946 | radar; 947 | ]; 948 | 949 | "Rocket Components", 950 | [ 951 | low_density_structure; 952 | rocket_fuel; 953 | rocket_part; 954 | rocket_control_unit; 955 | satellite; 956 | ]; 957 | ] 958 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | p { 6 | line-height: 1.5; 7 | } 8 | 9 | p.button { 10 | text-align: center; 11 | } 12 | 13 | button.setvisible, a.button { 14 | font: inherit; 15 | color: #fff; 16 | background-color: #000; 17 | display: block; 18 | margin: 0 auto; 19 | border: 0; 20 | border-radius: 3px; 21 | padding: 0.5em 1em; 22 | cursor: pointer; 23 | } 24 | 25 | a.button { 26 | width: 100px; 27 | text-decoration: none; 28 | line-height: normal; 29 | } 30 | 31 | .count { 32 | width: 3em; 33 | } 34 | 35 | .resourceinput { 36 | display: inline-block; 37 | margin-left: 0.5em; 38 | margin-right: 0.5em; 39 | } 40 | 41 | .leftcolumn { 42 | float: left; 43 | width: 36em; 44 | background-color: #dddddd; 45 | padding: 5px; 46 | border-radius: 3px; 47 | text-align: center; 48 | } 49 | 50 | .rightcolumn { 51 | float: right; 52 | width: calc(100% - 36em - 20px); 53 | margin: 0px; 54 | padding: 0px; 55 | } 56 | 57 | @media (max-width: 72em) { 58 | .leftcolumn, .rightcolumn { 59 | float: unset; 60 | width: unset; 61 | } 62 | } 63 | 64 | .settings { 65 | background-color: #dddddd; 66 | padding: 1em; 67 | border-radius: 3px; 68 | } 69 | 70 | .setting { 71 | margin: 0.5em 0 72 | } 73 | 74 | .setting input[type=checkbox] { 75 | margin-left: 20px; 76 | } 77 | 78 | .setting input[type=radio] { 79 | margin-left: 20px; 80 | } 81 | 82 | .setting input[type=text] { 83 | width: 3em; 84 | } 85 | 86 | .result { 87 | background-color: #eeeeee; 88 | margin-top: 10px; 89 | padding: 5px; 90 | border-radius: 3px; 91 | } 92 | 93 | .goal > .goal { 94 | padding: 0.5em; 95 | border-left: 1px solid #ccc; 96 | margin-left: 2em; 97 | position: relative; 98 | } 99 | 100 | .goal > .goal:before { 101 | content: " "; 102 | display: inline-block; 103 | margin-right: 0.5em; 104 | margin-left: -0.5em; 105 | width: 1em; 106 | border-bottom: 1px solid #ccc; 107 | position: relative; 108 | z-index: 2; 109 | } 110 | 111 | .goal > .goal:last-of-type:after { 112 | content: ''; 113 | display: block; 114 | position: absolute; 115 | width: 5px; 116 | height: 10000px; 117 | background: white; 118 | top: 27px; 119 | left: -3px; 120 | } 121 | 122 | .outputh1 { 123 | margin-bottom: 1em; 124 | font-size: large; 125 | } 126 | 127 | .goals > .goal { 128 | margin-bottom: 1em; 129 | background: #fff; 130 | padding: 0.5em; 131 | border-radius: 3px; 132 | overflow: hidden; 133 | font-size: 0.9em; 134 | } 135 | 136 | .goal img { 137 | margin: 0 0.2em; 138 | } 139 | 140 | .makerspace { 141 | display: inline-block; 142 | width: 2em; 143 | } 144 | 145 | .icon { 146 | vertical-align: middle; 147 | width: 32px; 148 | } 149 | 150 | .warning { 151 | color: red; 152 | } 153 | 154 | .sharedpercent { 155 | color: #80aaff; 156 | } 157 | 158 | .resource_category { 159 | font-weight: bold; 160 | margin-top: 0.2em; 161 | margin-bottom: 0; 162 | } 163 | --------------------------------------------------------------------------------