From 650f4ea39571027e3680e515c7108cd44477f63b Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Fri, 22 Mar 2024 08:41:37 +0100 Subject: [PATCH 01/39] test js library --- js/.gitignore | 175 +++++++++++++++++++++++++++++++++++++++++++++++ js/README.md | 15 ++++ js/bun.lockb | Bin 0 -> 23637 bytes js/jsconfig.json | 27 ++++++++ js/package.json | 15 ++++ js/test.js | 12 ++++ 6 files changed, 244 insertions(+) create mode 100644 js/.gitignore create mode 100644 js/README.md create mode 100644 js/bun.lockb create mode 100644 js/jsconfig.json create mode 100644 js/package.json create mode 100644 js/test.js diff --git a/js/.gitignore b/js/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/js/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/js/README.md b/js/README.md new file mode 100644 index 0000000..e77a1f9 --- /dev/null +++ b/js/README.md @@ -0,0 +1,15 @@ +# plutoplotly_helpers + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.js +``` + +This project was created using `bun init` in bun v1.0.34. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/js/bun.lockb b/js/bun.lockb new file mode 100644 index 0000000000000000000000000000000000000000..667d127d6c9b09633204f3aee5a0338bc85e118b GIT binary patch literal 23637 zcmeHv2{@G9`~T3?TOwsGBwD1H!JwijiuO_{lEgF^4WpSMTBK5GS8qytS|k*yC>7fG zq)mzTl%>+9lK9{EGjsI#zJ=cJ|N8%a|Lf{p9%s(-`JDUQXMN7|Ja;#JlMu1U#D^bb z!V3!3^9c!3fQuy%a;N+91w59oP!#AbVTGD1$TJv>U3LRMiXZ8A3+cMHr{9vXp+mc< zw0WnbBTl(+)^udZ`Z4D`AQL!SsmEyjAAY1PeBMU(gYr}vj7WiSnzz`W!RQGwiV%JQ zemn3BWd1quRUph2izVT~JjNwC2BQOnkAdGF{1ot&z|WNBhg<@_n89e(g27OS_?-}s z{#Yi1p#Z)k_-(-FfUgF=9{9+w1U||m;)#WVP##0K6@$?Q!fN0nt`+zwm%q1II5UXB zaQjPI?y;5(Mq7vv_YM?5Q4B?>I`YedkGL}MQQm9dcLtv)5(z~tUf3hZgt!y3^f{0j z^9$e~*2@4Ak%#N=Etn3x2@sZ*&+-*``w{s!Lp=ONW`puj4@&S4d6*E{75plY6ZQHR zfYGmpbj%kEVdQ@S=_p?v=nMJXm8E)k24NittAj9z3k4tb}m_)r(B93KE9&q0}= z06xlN2$>NFI;HX{jA;?%{O0_W+^3;7Tdl_2u5#3x*lt$oqu`-aB5JnB*bQ5{S?TGS z0YxS@E12ECKFG1Ek|?$f|9g#y{hq_En~=6{)1dg&OB$y=M~+wW-K!Fon-*!W>*8?h zb7b|mZemx*uztmFv<|B)_%Uqr%M9%J?PT@ zDwh@Bf7#9=j{etE#ib?DMeNwPs-X1?)D#lBjW|;G_~GQouHvP`Y7g(}J||~J&j%Ir zMyS0?T&8^YZmZzDXY)-*X-vN7aE8mh*7Jkt?;`&!CASBciTqZj*2fn#a^ndEt2)!c2+N{>t3#?*QxD- z9-P9A>H9UqBN8-fyE^2g-dd%YbmN9KbBq6!`<3i5JG7Q+sFsW}j2?E%PA!?Ql{#T| zk8=smJ+%Xvms|BZIns3g$JrHr*P>O*yY9LY7rs5S{2b?b(x9Rxy z9+AdIi+DaTEdMKqvkp3QxcWdpg5in%6vuM29KrJ;w-w-_Ph!7hkocxZNWKq{u|MFk zJ{Z^BdJ#Mu0CYTwZ*KW~zz>#{k7dKKY?dSC6aonKhq!-(Zx2LvfX6~%s`~Bvj|2R` z{{kNacx*o`ALg0gF8>nXVbQ5?f8=S70~qDBL2(v<$Nop+WJ!&BB=1j05tNykl!5yA0R6qmMR8J_sw#o{8+$S10MShUd?p`p9gp+z@z?t#}RxN5PFoX zd~E;bI)a}L_%VS09o=IY1fK`^zOwQ)ARfm+a~#3jz`ns(hHnmz+hh1eP)|=>n zj?e};IaL& z?SID+yeZrS;QUY8y}2?Fd?etp|B${%c$$)h;I9DQURFM?AI+@?!E3^#N%{|#{kwQ) z!23b@*ne^TfP2nnIZ}QW;0MX--`ujX0D@PAn>xf}-}xO!@Xmm@2RzX`@tfiz_-%m4 z{*SUZ7mM5ke-H43Y4Vf!=9aGw_p@aE!1<%OGLUivfFBFxV|gITZ{v>xeiGox{MFnx zB<1VF{Wj{4;8Bj|ID(G`Jno-JxH(wNL-OASyc@0j-_@Tn+_d5S2f@K~+AK%PpAL9( z|AavOW}$z`p9Of_zrZp|&40gJ{u{uf{&C#GRLIc%&GIea9K;UrIPYn}^}F-me87|a z1CHV5_8$}z^OJFpK9-GhAUe8_@z}S~HRfYJJkLQ#_vv~e`uUlUvSa(9-yJS=bRYXj z54g}bfD0Y^SgsKUz|noIlL-~u&_}$fEFOLAOR$Dg4t>NypQIf6C=c9YQsqKhQ;zN< zAM`;g9om9&bRYR)j-ed-m>=$WDM$A)evmYp@-b`&7nVOn7Ekvv-2pD-9|{+Shspfm z;G-ix{>FOYg${kx8@-(|9>W;lSUY1rOvilmcE)%N|7q|44eed4T#Fsz|E2>zIfj&% zPR-2Wx9IR@cI->ltO*OdK64((%D)obw&S*qw_m?ibvK$HI{Ls_TVt2=ohQ4lJePN9 z%9TYKuAN>EGN{w^^AN6HBOF2F#lB1&(`Cz)yDtsR7OGTObv3(@G4{}1%dE5A95X*f z`CROOxiINy?3`{6N5MgyMF({wY8oK zI-54;PhD_(6OEUQC*)@4GXk}$l8*RiZ`a)#uhT-JVjflCzCWZ_!pEym9=P+oTa6ag zO&x&+sz2AS+Dh|^(U9-S-}=#bacmLCjJ9o2+R|OE-(jlb$XT>gYYQfx)L+7n(M-uAy3IcWVu+ZL)_qjz076z3owVcKVGg>hQDAr*at zX}oZ|U+-86p zcYnQRYh2gupiab?H#gZw4=vb|)?z4)7xy2;F%6%z;=I`!w?1&sUXN$l(FbEDuS>qj zw(hlZ*v?|F-LdAI1YP-}8cOTlT1-!kz)!9v$(!ZO0 zSW4B-KD%DIzo^X^lDyDYT z^IPB9tg$=4Xzt4Cx9*O)nc*?yb)qoTi8Eu^qqtKqBa-4npNEtiJ$KOeVPyo&jxwk? zxU?!sj>d~~DsfEfER(W!^Z)W_F*n-W{o@72ibSL7+4WW$S0$Ck3%5V@j;WFxyyyCpmi80m&x~#5BYLVc z;euys>2ZDgOP>4Pcb(m&^Uz59ylS$hkB-9n4d=d<-4X}Ktf~k)v)nU-o#<8LLF2`< zB;uHx=bu&DIEBAs$=9$J?arJlSo6A^c_p23E4=evpMg%bW2z1A#HYj+4P2cwW!wql zX?2cE=k6PNy<7i@VaomFtOIOlykuQMZYC?Mr28|q;rZ%k7o>)q77sGnYp;1i-O*{Z zgj4>sf5|1K1#_>*E47%W+;Pi<;|sR4l5T!B{QPNcfW^?5SMnRycB1jZHoV?3E%x3m z&D`?J@3MHv;tLZGzi#FB@tsZ8Yq^L=A5$$BmW;0G|E+S3YcD1H*v)J9?TytfUUD<) zowHY9|C=`|N(RmCL*s?*alK=XDV%JvwV=g-nOdpF?mCzF6LvXXV^3_~XKI_yLbsCp zOO^BHXU%HavV~sX?VDe>Up!^bS1b1eYM$u_MsBzq6T3B%#tYldddHO0x69h9nCFom z9i3~r@6&3|jR60XdCsrA4eXa0Xxy77~Aci7!W4KO-Cy++Q$IDASvjhE~bkes<=&$a=^0ec=UT6g^XL|2zv zx({N$CbTZ>aiQPUc?&0Bk9=u&S|dd+BKVv8_PDI=&%KoW<}M#8-o5X9=~Bz`tI=0z zy!88J=FVb`t&)=we@%;AqHFVJjm~{bH)Y#R&k9S=?h5aGr*i-CiuMF3-5s<838}<6e8>b}s7{jhFr$iD}Fc+l*tj{}^#)wfW~@iw?yc&zxQioyC(@ zKYy{fvyXz?gYQvk<=xXZmA!FZa{ffJOX;UsW?4HF<^*V8*JsomqVeKAK5@*bSr=DT z2_kN#oY=khzJ=R{{U36)^;GS%Z~7|kh}faL%wXtRjlX=-w;7$XyZY);Maw(6yI1!w z%@ElP9;xhnVCM!JuO=Y{pLtWo<&)m(Y_7SUrn|T37ihCB4ds&~o^x!0MGM_8$u(RlHmm^kL^8Q0E6lzA07miU;S z9~QLH+N|bu2g@@%+MSq?b|rMTLRGPM>8s26>mHtKsn1u~R2%PnSTnod(qSDw@GqIY z%NR!E?LIMR6W`8#pUx4lHM<(&q9 zxbxuNllE3K$9;KK8v8A>_tMlgvj-%ViHqO9uDN-6dPu7+Oy^6v<7N*?9`H6v<5|=J zC*O7}GT%LKMdKy+ZOF~MpdWl$>s8Gf!+G)Xv;7ao#{!p(sn;Pv*em4YoEuhq!Bl2 zp3->r>RCbFJR9qgS0uZvTTDKceJnGhyzhchcZv)bUYZtnB1-Gr)1LP1>HS&@oem!; zYvt*&lRZY+jKAut_Vd3>bPTR0^ohGppPsVwgk1xM+pe{(ionq^p1r7rjjmhP;>f1UXVzt)kB4)8cQ7;#KXNBymyV7~@ z)a*DAb!FY`tnz*trIS?K)o~o#99F-eE4sqk^-TZZ))qBp>kBWPP|fY;ameL`tFz-n z$8(t*OLhbquujw{`iPtk=tB#8ky}vmSop@neGn z2ZedPXuLh>yqtU4^X~Jz7no`;pD;1+`hJVonr@wzzpT;NCz2~<@836B-P_5{GrJFC z$%u~6+vG&6sg!*>zA<<74ABs^ryJJdyFf(0m-&&skc3IeNx@<9^*MOTMm`FOTW0QrW8W&Xe4M>@*-q~S;-hL~mYgWo9w%rfV z?6^i;_BD~l+ndg7SE+X7QL6e4mik}vm3ry$#)-FJd}8Zm_s=dYH$7EZT$FH1PX6PJ zy+zlx+AaRBf1yXD>W93-y!ULyscqBlcxcgh$+-^FGvE5%oE0|ejQ4#r%b{Ui1lm`^I{zE^#|F-kG^5z8~?Thgxy#As_QKm1(@j zba}Z8H-0nX1>By)Sbd_`hRa8@gHn=a@2fccUL*5%j&_lbZf=ZWk9~jV8NIXV@>Tii zZb{5y+tk{3{denhax1W#_>soTqVt|C>A0v>wz7+yk=5HpcJG823eQif96v`8W~wc} z&8lLwsqwwU`-j%{4!hS*wcFR|$mIPqQclI&kM;3%x6&hSdCd+qRzc0r`saW~wu7knZ$*X%@ zy8l{(L!T$fIT_M3J1IrEYdKBKd-X+efKRd)@9#&J>KlC=pGw9~E6L*3w$IGYztHks z!o4L=%l5>6z1%`;#O+=&W~!oBnHzNucJi*K$xF`JDBgj&T={cvc_y)XmaPlihPIJ2 zdAnG(JVR~y+Tm(|RNmQaz$oj!8duMe`xwK7b;gf=U$;u|_?{(sDoR(YEUg@HL!j{J+#nqiReLs*E zV8n6h!UUCjd$@@jce5=m;M9tayv>^TVRvNE4UEYwTPjox? zG+5YWo1@?rKOxG!Cu7=l6RYdTHx_+7UE6+(WA=$zU5-yVR$ew?#{ru&m+QViY?t-S zUNhcv@?24PtC=)jIJ>TQ%>d?O3tqu_{thG<@K7jpjakzJW*YOV{ ztF_hw5`1q)NQO9M z=hT@mqibC4R@G|c=jl~*XEWZ^o*(&HcPEV(-odDM%)%x43RW*$6lI?ttNk&ugG!dJ z)Av4xv%(%Zhi+2YyPTD773b}ajB zaPd5w?AgqYj}Ox4Gd$lWjwuma`|0%9R5HntryTK}|0Std*{Nvxn3jS026aVD&hkac z8ipEECUHheTyoWpL^&xNm?*ED(Ea%C#i>uGhqA}gyXn|M&a#3^>nweBV-qvG7dTTV8Ko|C>-|MQcak?Ou> zv*`O0c&Dr0F?$~>E_%PIb=J4a^LrK;x7ng}tdH<7dB5Pk;c?>@Pt~sJdUcsihs3>> zF0DV;EDZSOs;(NkBz(ZJ@8^wY$17fB>Vmv~-2a>g@JyJzcYPKPM&T?h@_$a6KPCUs z!2h2bAkQj@|8HvXXDa+RTKj|fqk%sf_@jY88u+7uKN|R>fj=7fqk;by8fYzh?a)-_ zD+~~exNLrqSmG@Zu*t{VY!?xaXRF6C)f4k)@r1s{dKP-#0={36(3g6hrM6D$P+~l< z!FvF5o=L(}WhJ=CUT9Pp5Ms`;1)`r*A{E7h_x9xdM!YY<_nz<_C48R<-~Dlb%MmVo ze*mBL<8yg@hK|pr@fk8c@5N`c_#73VdE$E!_^u4Te}eBq4uT6s!sm;6aM{6S57%J0 z@L3Q(x4~yD_&fxkUFgHr6)t>UhIPYpBz*RO&k68MAJ5;r!-dtwvu!+w#YG`(1Q#0hk8W4p>9xT7)Cv#zKQNp=cr%QA?g-&ih9KJXVe+uQJ+MQ zs8iG@w#~gk@DH5*FsZLFz=PArGo&AMne9OkqPQ3`T(rL%xEiQaG6MLY6|llb5AHGZD)N63CLIVKT>{O4zavIzQ_lRIp_^!`*k`0rar`C1NoAllFb~YCN>?& zxBl{S7HkeSDT7!kAYZQorZ3y954$ge*fSvCxl2>}vCaRL-hSP3#BKz!CqQWj`W1{4 z7^c|T#Nq&?n6aTR!We`U7;};2n|?^KkoHkxoq|{`pfD60v0p*#8bFFEu)z@ewRV24 z0}k7gL9A8~YX-p9S1JKHhb49qq!L(x*u(+`v5WvI9JU#B1qQK^L2M^b7*mvuSkWNX z6=W%(17b&m*jJ#~sBuaxYY>YI6owil#8w5dVE`#Iy%8%H#QFiGm_rwVzC-MA5c>>} zBAXA0r4C}j;RhSBdYuu*Lc zVjG0mjF6VYhUt)42O(A?AO%Jo)`!>!A$B7mr7xQUcVhe=Wh>&7n zdNC!|MTpf2Na3)Nj6v*=5W5tRBGdJ+%OSQ%h>Z%s$XekiJs#&?SOkb&6JqZIFqTs7 zKo6lToe&Ec6dP3^V(Wz1zPF zSxt$J6kLbZ=6gIG=>7B!?P=4=bbuiJ-MT_M&u z00R>r)|A*?A@(@1rlxFjs4B6*LM(I0%SCWNI8Xzz(L!u@KuSNDLtq8`CkEGTbi|Ge zvF`yGOE#wuNF5_vm2cFq&h)*fm=_qz6R{n_Bs@`&x4>B<;s^QJ>Y4pmxcfC&xFupP zRbK8lwLPMBF=}X-2;-C=?i%o>^NcKsSj-avqvtKX`Qv>@ehGo874|=oQT;5h%7wM7 zB_G0^^q^>P?*;n=vtMl!7{P)Ni7;3olnBDT{CR?4o`|w9B<70v!ID8}YDjDX29XBw z0)-O3FvtW}L4S!PSZr-#BI5b+#S&3CJ196XK+G13{7j_#1QV9*2g`zO2CZ(!miWzT z1VtwYEEK_#?K==ypX%f7!xOMTU_U-h9&fHcuRaUth7UXXS+p2`9#2xA9wOlj8k4_a zMv#R9uwd4O;nU1Yh_OU~Qa%OcG#g69M>zQn6wqfIXac4L|9Vjap=szf4hiB*1iVI@ zNw$@d>Kfaa1v9j?jOH{GDFO8(D~x5zhK*Ho>nRf%@UmaO2gm9I7CivMRbX#Z^#Q0nY~-4#e0@m(t^Z98k*4(j zGr57}XVK7}u(&iK$v-s%Z2yb|*SN03%CJqc+=xuy4iz^gC^HQB7*jwVy3icL>dE$^@s++Ka0fXxfOB^03;0H~Y zZb=#`tqCMZ(L@fAuIV=TAt@042vFFjdQ!tW1HNGdi`vvTH5OqG0yB63mi_<`?oUCZ zz9B(eTrgSCIMW)@Q-gfK(;y7>z@?}0>44S|08b0C;1*EY^JMEd)ugNdu`ozF`dPlv zs{TJ>DFxzk(1^TvPXNWBhtkPv+X$eZ6j1fQn>E0G2u4AnJ5sWpe=-=#+u?sCZPQhfGRdUJ^W{Zz))ZKPvoMu zzpPN$Ku+_PfOSg#G#)IAzI>i2K+KW|r}G-^Gxd3(l=|Pz>gd$nCn^nXTtb`6)a4x{ z=EE`@j+;3$4^!%Dq$L|>1kQ#LFm~{+zws^$QyF!`iQ1C=gk`5`ZU1Z~1&*IZQ>|y& zgdDW`0}U+%(#U!xr4AU+)E=b~wKT{FWDUX~jFoirD~%3w%XIr{Vtg!CZj4%-rX08FpYy=6;2*%J7Qgn^CJot_Pd>A`08 z^af=DL4z>WDX0aP3r}stdQX7%!C%KXJpBykF!}&^6+Y&PINoTZ%TYljE>8*fNcIl0# zw3||Q|BVPOD@A%pz%rqHDGSxX8VVu1U!WFskwnPy^%hHDTPcJS2MNE?mi}jEsKP%Z zsl8WIBclp?6{1oMM%&zK9}EQMd;aph91OyTOU|{ jP$kr%mh>R25v5XPG;GETr?b^oZ+!-~ayuyB9T8 literal 0 HcmV?d00001 diff --git a/js/jsconfig.json b/js/jsconfig.json new file mode 100644 index 0000000..0fef23a --- /dev/null +++ b/js/jsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/js/package.json b/js/package.json new file mode 100644 index 0000000..c92d0c8 --- /dev/null +++ b/js/package.json @@ -0,0 +1,15 @@ +{ + "name": "plutoplotly_helpers", + "module": "index.js", + "type": "module", + "devDependencies": { + "@types/bun": "^1.0.10" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@emotion/css": "^11.11.2", + "lodash": "^4.17.21" + } +} \ No newline at end of file diff --git a/js/test.js b/js/test.js new file mode 100644 index 0000000..8b0650d --- /dev/null +++ b/js/test.js @@ -0,0 +1,12 @@ +import _ from "lodash" + +// We start by putting all the variable interpolation here at the beginning +// We have to convert all typedarrays in the layout to normal arrays. See Issue #25 +// We use lodash for this for compactness +export function removeTypedArray(o) { + return _.isTypedArray(o) ? Array.from(o) : + _.isPlainObject(o) ? _.mapValues(o, removeTypedArray) : + o +} + +console.log(removeTypedArray([1, 2, 3])) \ No newline at end of file From 521aae88a7c629afac40ceac126c940c37ec32df Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Fri, 22 Mar 2024 08:45:15 +0100 Subject: [PATCH 02/39] index.js --- js/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 js/index.js diff --git a/js/index.js b/js/index.js new file mode 100644 index 0000000..8b0650d --- /dev/null +++ b/js/index.js @@ -0,0 +1,12 @@ +import _ from "lodash" + +// We start by putting all the variable interpolation here at the beginning +// We have to convert all typedarrays in the layout to normal arrays. See Issue #25 +// We use lodash for this for compactness +export function removeTypedArray(o) { + return _.isTypedArray(o) ? Array.from(o) : + _.isPlainObject(o) ? _.mapValues(o, removeTypedArray) : + o +} + +console.log(removeTypedArray([1, 2, 3])) \ No newline at end of file From 3af312114c8d8f179415d7e0a06633b406c116d7 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Fri, 22 Mar 2024 14:14:21 +0100 Subject: [PATCH 03/39] update stuff --- js/.gitignore | 175 -------------------------------------------- js/README.md | 15 ---- js/bun.lockb | Bin 23637 -> 0 bytes js/bundle.js | 20 ++++++ js/deno.jsonc | 5 ++ js/deno.lock | 164 ++++++++++++++++++++++++++++++++++++++++++ js/emotion.js | 3 + js/index.js | 12 ---- js/jsconfig.json | 27 ------- js/package.json | 15 ---- js/prehook.esm.js | 180 ++++++++++++++++++++++++++++++++++++++++++++++ js/prehooks.js | 65 +++++++++++++++++ js/styles.js | 141 ++++++++++++++++++++++++++++++++++++ js/test.js | 12 ---- 14 files changed, 578 insertions(+), 256 deletions(-) delete mode 100644 js/.gitignore delete mode 100644 js/README.md delete mode 100644 js/bun.lockb create mode 100644 js/bundle.js create mode 100644 js/deno.jsonc create mode 100644 js/deno.lock create mode 100644 js/emotion.js delete mode 100644 js/index.js delete mode 100644 js/jsconfig.json delete mode 100644 js/package.json create mode 100644 js/prehook.esm.js create mode 100644 js/prehooks.js create mode 100644 js/styles.js delete mode 100644 js/test.js diff --git a/js/.gitignore b/js/.gitignore deleted file mode 100644 index 9b1ee42..0000000 --- a/js/.gitignore +++ /dev/null @@ -1,175 +0,0 @@ -# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore - -# Logs - -logs -_.log -npm-debug.log_ -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Caches - -.cache - -# Diagnostic reports (https://nodejs.org/api/report.html) - -report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json - -# Runtime data - -pids -_.pid -_.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover - -lib-cov - -# Coverage directory used by tools like istanbul - -coverage -*.lcov - -# nyc test coverage - -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) - -.grunt - -# Bower dependency directory (https://bower.io/) - -bower_components - -# node-waf configuration - -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) - -build/Release - -# Dependency directories - -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) - -web_modules/ - -# TypeScript cache - -*.tsbuildinfo - -# Optional npm cache directory - -.npm - -# Optional eslint cache - -.eslintcache - -# Optional stylelint cache - -.stylelintcache - -# Microbundle cache - -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history - -.node_repl_history - -# Output of 'npm pack' - -*.tgz - -# Yarn Integrity file - -.yarn-integrity - -# dotenv environment variable files - -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) - -.parcel-cache - -# Next.js build output - -.next -out - -# Nuxt.js build / generate output - -.nuxt -dist - -# Gatsby files - -# Comment in the public line in if your project uses Gatsby and not Next.js - -# https://nextjs.org/blog/next-9-1#public-directory-support - -# public - -# vuepress build output - -.vuepress/dist - -# vuepress v2.x temp and cache directory - -.temp - -# Docusaurus cache and generated files - -.docusaurus - -# Serverless directories - -.serverless/ - -# FuseBox cache - -.fusebox/ - -# DynamoDB Local files - -.dynamodb/ - -# TernJS port file - -.tern-port - -# Stores VSCode versions used for testing VSCode extensions - -.vscode-test - -# yarn v2 - -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -# IntelliJ based IDEs -.idea - -# Finder (MacOS) folder config -.DS_Store diff --git a/js/README.md b/js/README.md deleted file mode 100644 index e77a1f9..0000000 --- a/js/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# plutoplotly_helpers - -To install dependencies: - -```bash -bun install -``` - -To run: - -```bash -bun run index.js -``` - -This project was created using `bun init` in bun v1.0.34. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/js/bun.lockb b/js/bun.lockb deleted file mode 100644 index 667d127d6c9b09633204f3aee5a0338bc85e118b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23637 zcmeHv2{@G9`~T3?TOwsGBwD1H!JwijiuO_{lEgF^4WpSMTBK5GS8qytS|k*yC>7fG zq)mzTl%>+9lK9{EGjsI#zJ=cJ|N8%a|Lf{p9%s(-`JDUQXMN7|Ja;#JlMu1U#D^bb z!V3!3^9c!3fQuy%a;N+91w59oP!#AbVTGD1$TJv>U3LRMiXZ8A3+cMHr{9vXp+mc< zw0WnbBTl(+)^udZ`Z4D`AQL!SsmEyjAAY1PeBMU(gYr}vj7WiSnzz`W!RQGwiV%JQ zemn3BWd1quRUph2izVT~JjNwC2BQOnkAdGF{1ot&z|WNBhg<@_n89e(g27OS_?-}s z{#Yi1p#Z)k_-(-FfUgF=9{9+w1U||m;)#WVP##0K6@$?Q!fN0nt`+zwm%q1II5UXB zaQjPI?y;5(Mq7vv_YM?5Q4B?>I`YedkGL}MQQm9dcLtv)5(z~tUf3hZgt!y3^f{0j z^9$e~*2@4Ak%#N=Etn3x2@sZ*&+-*``w{s!Lp=ONW`puj4@&S4d6*E{75plY6ZQHR zfYGmpbj%kEVdQ@S=_p?v=nMJXm8E)k24NittAj9z3k4tb}m_)r(B93KE9&q0}= z06xlN2$>NFI;HX{jA;?%{O0_W+^3;7Tdl_2u5#3x*lt$oqu`-aB5JnB*bQ5{S?TGS z0YxS@E12ECKFG1Ek|?$f|9g#y{hq_En~=6{)1dg&OB$y=M~+wW-K!Fon-*!W>*8?h zb7b|mZemx*uztmFv<|B)_%Uqr%M9%J?PT@ zDwh@Bf7#9=j{etE#ib?DMeNwPs-X1?)D#lBjW|;G_~GQouHvP`Y7g(}J||~J&j%Ir zMyS0?T&8^YZmZzDXY)-*X-vN7aE8mh*7Jkt?;`&!CASBciTqZj*2fn#a^ndEt2)!c2+N{>t3#?*QxD- z9-P9A>H9UqBN8-fyE^2g-dd%YbmN9KbBq6!`<3i5JG7Q+sFsW}j2?E%PA!?Ql{#T| zk8=smJ+%Xvms|BZIns3g$JrHr*P>O*yY9LY7rs5S{2b?b(x9Rxy z9+AdIi+DaTEdMKqvkp3QxcWdpg5in%6vuM29KrJ;w-w-_Ph!7hkocxZNWKq{u|MFk zJ{Z^BdJ#Mu0CYTwZ*KW~zz>#{k7dKKY?dSC6aonKhq!-(Zx2LvfX6~%s`~Bvj|2R` z{{kNacx*o`ALg0gF8>nXVbQ5?f8=S70~qDBL2(v<$Nop+WJ!&BB=1j05tNykl!5yA0R6qmMR8J_sw#o{8+$S10MShUd?p`p9gp+z@z?t#}RxN5PFoX zd~E;bI)a}L_%VS09o=IY1fK`^zOwQ)ARfm+a~#3jz`ns(hHnmz+hh1eP)|=>n zj?e};IaL& z?SID+yeZrS;QUY8y}2?Fd?etp|B${%c$$)h;I9DQURFM?AI+@?!E3^#N%{|#{kwQ) z!23b@*ne^TfP2nnIZ}QW;0MX--`ujX0D@PAn>xf}-}xO!@Xmm@2RzX`@tfiz_-%m4 z{*SUZ7mM5ke-H43Y4Vf!=9aGw_p@aE!1<%OGLUivfFBFxV|gITZ{v>xeiGox{MFnx zB<1VF{Wj{4;8Bj|ID(G`Jno-JxH(wNL-OASyc@0j-_@Tn+_d5S2f@K~+AK%PpAL9( z|AavOW}$z`p9Of_zrZp|&40gJ{u{uf{&C#GRLIc%&GIea9K;UrIPYn}^}F-me87|a z1CHV5_8$}z^OJFpK9-GhAUe8_@z}S~HRfYJJkLQ#_vv~e`uUlUvSa(9-yJS=bRYXj z54g}bfD0Y^SgsKUz|noIlL-~u&_}$fEFOLAOR$Dg4t>NypQIf6C=c9YQsqKhQ;zN< zAM`;g9om9&bRYR)j-ed-m>=$WDM$A)evmYp@-b`&7nVOn7Ekvv-2pD-9|{+Shspfm z;G-ix{>FOYg${kx8@-(|9>W;lSUY1rOvilmcE)%N|7q|44eed4T#Fsz|E2>zIfj&% zPR-2Wx9IR@cI->ltO*OdK64((%D)obw&S*qw_m?ibvK$HI{Ls_TVt2=ohQ4lJePN9 z%9TYKuAN>EGN{w^^AN6HBOF2F#lB1&(`Cz)yDtsR7OGTObv3(@G4{}1%dE5A95X*f z`CROOxiINy?3`{6N5MgyMF({wY8oK zI-54;PhD_(6OEUQC*)@4GXk}$l8*RiZ`a)#uhT-JVjflCzCWZ_!pEym9=P+oTa6ag zO&x&+sz2AS+Dh|^(U9-S-}=#bacmLCjJ9o2+R|OE-(jlb$XT>gYYQfx)L+7n(M-uAy3IcWVu+ZL)_qjz076z3owVcKVGg>hQDAr*at zX}oZ|U+-86p zcYnQRYh2gupiab?H#gZw4=vb|)?z4)7xy2;F%6%z;=I`!w?1&sUXN$l(FbEDuS>qj zw(hlZ*v?|F-LdAI1YP-}8cOTlT1-!kz)!9v$(!ZO0 zSW4B-KD%DIzo^X^lDyDYT z^IPB9tg$=4Xzt4Cx9*O)nc*?yb)qoTi8Eu^qqtKqBa-4npNEtiJ$KOeVPyo&jxwk? zxU?!sj>d~~DsfEfER(W!^Z)W_F*n-W{o@72ibSL7+4WW$S0$Ck3%5V@j;WFxyyyCpmi80m&x~#5BYLVc z;euys>2ZDgOP>4Pcb(m&^Uz59ylS$hkB-9n4d=d<-4X}Ktf~k)v)nU-o#<8LLF2`< zB;uHx=bu&DIEBAs$=9$J?arJlSo6A^c_p23E4=evpMg%bW2z1A#HYj+4P2cwW!wql zX?2cE=k6PNy<7i@VaomFtOIOlykuQMZYC?Mr28|q;rZ%k7o>)q77sGnYp;1i-O*{Z zgj4>sf5|1K1#_>*E47%W+;Pi<;|sR4l5T!B{QPNcfW^?5SMnRycB1jZHoV?3E%x3m z&D`?J@3MHv;tLZGzi#FB@tsZ8Yq^L=A5$$BmW;0G|E+S3YcD1H*v)J9?TytfUUD<) zowHY9|C=`|N(RmCL*s?*alK=XDV%JvwV=g-nOdpF?mCzF6LvXXV^3_~XKI_yLbsCp zOO^BHXU%HavV~sX?VDe>Up!^bS1b1eYM$u_MsBzq6T3B%#tYldddHO0x69h9nCFom z9i3~r@6&3|jR60XdCsrA4eXa0Xxy77~Aci7!W4KO-Cy++Q$IDASvjhE~bkes<=&$a=^0ec=UT6g^XL|2zv zx({N$CbTZ>aiQPUc?&0Bk9=u&S|dd+BKVv8_PDI=&%KoW<}M#8-o5X9=~Bz`tI=0z zy!88J=FVb`t&)=we@%;AqHFVJjm~{bH)Y#R&k9S=?h5aGr*i-CiuMF3-5s<838}<6e8>b}s7{jhFr$iD}Fc+l*tj{}^#)wfW~@iw?yc&zxQioyC(@ zKYy{fvyXz?gYQvk<=xXZmA!FZa{ffJOX;UsW?4HF<^*V8*JsomqVeKAK5@*bSr=DT z2_kN#oY=khzJ=R{{U36)^;GS%Z~7|kh}faL%wXtRjlX=-w;7$XyZY);Maw(6yI1!w z%@ElP9;xhnVCM!JuO=Y{pLtWo<&)m(Y_7SUrn|T37ihCB4ds&~o^x!0MGM_8$u(RlHmm^kL^8Q0E6lzA07miU;S z9~QLH+N|bu2g@@%+MSq?b|rMTLRGPM>8s26>mHtKsn1u~R2%PnSTnod(qSDw@GqIY z%NR!E?LIMR6W`8#pUx4lHM<(&q9 zxbxuNllE3K$9;KK8v8A>_tMlgvj-%ViHqO9uDN-6dPu7+Oy^6v<7N*?9`H6v<5|=J zC*O7}GT%LKMdKy+ZOF~MpdWl$>s8Gf!+G)Xv;7ao#{!p(sn;Pv*em4YoEuhq!Bl2 zp3->r>RCbFJR9qgS0uZvTTDKceJnGhyzhchcZv)bUYZtnB1-Gr)1LP1>HS&@oem!; zYvt*&lRZY+jKAut_Vd3>bPTR0^ohGppPsVwgk1xM+pe{(ionq^p1r7rjjmhP;>f1UXVzt)kB4)8cQ7;#KXNBymyV7~@ z)a*DAb!FY`tnz*trIS?K)o~o#99F-eE4sqk^-TZZ))qBp>kBWPP|fY;ameL`tFz-n z$8(t*OLhbquujw{`iPtk=tB#8ky}vmSop@neGn z2ZedPXuLh>yqtU4^X~Jz7no`;pD;1+`hJVonr@wzzpT;NCz2~<@836B-P_5{GrJFC z$%u~6+vG&6sg!*>zA<<74ABs^ryJJdyFf(0m-&&skc3IeNx@<9^*MOTMm`FOTW0QrW8W&Xe4M>@*-q~S;-hL~mYgWo9w%rfV z?6^i;_BD~l+ndg7SE+X7QL6e4mik}vm3ry$#)-FJd}8Zm_s=dYH$7EZT$FH1PX6PJ zy+zlx+AaRBf1yXD>W93-y!ULyscqBlcxcgh$+-^FGvE5%oE0|ejQ4#r%b{Ui1lm`^I{zE^#|F-kG^5z8~?Thgxy#As_QKm1(@j zba}Z8H-0nX1>By)Sbd_`hRa8@gHn=a@2fccUL*5%j&_lbZf=ZWk9~jV8NIXV@>Tii zZb{5y+tk{3{denhax1W#_>soTqVt|C>A0v>wz7+yk=5HpcJG823eQif96v`8W~wc} z&8lLwsqwwU`-j%{4!hS*wcFR|$mIPqQclI&kM;3%x6&hSdCd+qRzc0r`saW~wu7knZ$*X%@ zy8l{(L!T$fIT_M3J1IrEYdKBKd-X+efKRd)@9#&J>KlC=pGw9~E6L*3w$IGYztHks z!o4L=%l5>6z1%`;#O+=&W~!oBnHzNucJi*K$xF`JDBgj&T={cvc_y)XmaPlihPIJ2 zdAnG(JVR~y+Tm(|RNmQaz$oj!8duMe`xwK7b;gf=U$;u|_?{(sDoR(YEUg@HL!j{J+#nqiReLs*E zV8n6h!UUCjd$@@jce5=m;M9tayv>^TVRvNE4UEYwTPjox? zG+5YWo1@?rKOxG!Cu7=l6RYdTHx_+7UE6+(WA=$zU5-yVR$ew?#{ru&m+QViY?t-S zUNhcv@?24PtC=)jIJ>TQ%>d?O3tqu_{thG<@K7jpjakzJW*YOV{ ztF_hw5`1q)NQO9M z=hT@mqibC4R@G|c=jl~*XEWZ^o*(&HcPEV(-odDM%)%x43RW*$6lI?ttNk&ugG!dJ z)Av4xv%(%Zhi+2YyPTD773b}ajB zaPd5w?AgqYj}Ox4Gd$lWjwuma`|0%9R5HntryTK}|0Std*{Nvxn3jS026aVD&hkac z8ipEECUHheTyoWpL^&xNm?*ED(Ea%C#i>uGhqA}gyXn|M&a#3^>nweBV-qvG7dTTV8Ko|C>-|MQcak?Ou> zv*`O0c&Dr0F?$~>E_%PIb=J4a^LrK;x7ng}tdH<7dB5Pk;c?>@Pt~sJdUcsihs3>> zF0DV;EDZSOs;(NkBz(ZJ@8^wY$17fB>Vmv~-2a>g@JyJzcYPKPM&T?h@_$a6KPCUs z!2h2bAkQj@|8HvXXDa+RTKj|fqk%sf_@jY88u+7uKN|R>fj=7fqk;by8fYzh?a)-_ zD+~~exNLrqSmG@Zu*t{VY!?xaXRF6C)f4k)@r1s{dKP-#0={36(3g6hrM6D$P+~l< z!FvF5o=L(}WhJ=CUT9Pp5Ms`;1)`r*A{E7h_x9xdM!YY<_nz<_C48R<-~Dlb%MmVo ze*mBL<8yg@hK|pr@fk8c@5N`c_#73VdE$E!_^u4Te}eBq4uT6s!sm;6aM{6S57%J0 z@L3Q(x4~yD_&fxkUFgHr6)t>UhIPYpBz*RO&k68MAJ5;r!-dtwvu!+w#YG`(1Q#0hk8W4p>9xT7)Cv#zKQNp=cr%QA?g-&ih9KJXVe+uQJ+MQ zs8iG@w#~gk@DH5*FsZLFz=PArGo&AMne9OkqPQ3`T(rL%xEiQaG6MLY6|llb5AHGZD)N63CLIVKT>{O4zavIzQ_lRIp_^!`*k`0rar`C1NoAllFb~YCN>?& zxBl{S7HkeSDT7!kAYZQorZ3y954$ge*fSvCxl2>}vCaRL-hSP3#BKz!CqQWj`W1{4 z7^c|T#Nq&?n6aTR!We`U7;};2n|?^KkoHkxoq|{`pfD60v0p*#8bFFEu)z@ewRV24 z0}k7gL9A8~YX-p9S1JKHhb49qq!L(x*u(+`v5WvI9JU#B1qQK^L2M^b7*mvuSkWNX z6=W%(17b&m*jJ#~sBuaxYY>YI6owil#8w5dVE`#Iy%8%H#QFiGm_rwVzC-MA5c>>} zBAXA0r4C}j;RhSBdYuu*Lc zVjG0mjF6VYhUt)42O(A?AO%Jo)`!>!A$B7mr7xQUcVhe=Wh>&7n zdNC!|MTpf2Na3)Nj6v*=5W5tRBGdJ+%OSQ%h>Z%s$XekiJs#&?SOkb&6JqZIFqTs7 zKo6lToe&Ec6dP3^V(Wz1zPF zSxt$J6kLbZ=6gIG=>7B!?P=4=bbuiJ-MT_M&u z00R>r)|A*?A@(@1rlxFjs4B6*LM(I0%SCWNI8Xzz(L!u@KuSNDLtq8`CkEGTbi|Ge zvF`yGOE#wuNF5_vm2cFq&h)*fm=_qz6R{n_Bs@`&x4>B<;s^QJ>Y4pmxcfC&xFupP zRbK8lwLPMBF=}X-2;-C=?i%o>^NcKsSj-avqvtKX`Qv>@ehGo874|=oQT;5h%7wM7 zB_G0^^q^>P?*;n=vtMl!7{P)Ni7;3olnBDT{CR?4o`|w9B<70v!ID8}YDjDX29XBw z0)-O3FvtW}L4S!PSZr-#BI5b+#S&3CJ196XK+G13{7j_#1QV9*2g`zO2CZ(!miWzT z1VtwYEEK_#?K==ypX%f7!xOMTU_U-h9&fHcuRaUth7UXXS+p2`9#2xA9wOlj8k4_a zMv#R9uwd4O;nU1Yh_OU~Qa%OcG#g69M>zQn6wqfIXac4L|9Vjap=szf4hiB*1iVI@ zNw$@d>Kfaa1v9j?jOH{GDFO8(D~x5zhK*Ho>nRf%@UmaO2gm9I7CivMRbX#Z^#Q0nY~-4#e0@m(t^Z98k*4(j zGr57}XVK7}u(&iK$v-s%Z2yb|*SN03%CJqc+=xuy4iz^gC^HQB7*jwVy3icL>dE$^@s++Ka0fXxfOB^03;0H~Y zZb=#`tqCMZ(L@fAuIV=TAt@042vFFjdQ!tW1HNGdi`vvTH5OqG0yB63mi_<`?oUCZ zz9B(eTrgSCIMW)@Q-gfK(;y7>z@?}0>44S|08b0C;1*EY^JMEd)ugNdu`ozF`dPlv zs{TJ>DFxzk(1^TvPXNWBhtkPv+X$eZ6j1fQn>E0G2u4AnJ5sWpe=-=#+u?sCZPQhfGRdUJ^W{Zz))ZKPvoMu zzpPN$Ku+_PfOSg#G#)IAzI>i2K+KW|r}G-^Gxd3(l=|Pz>gd$nCn^nXTtb`6)a4x{ z=EE`@j+;3$4^!%Dq$L|>1kQ#LFm~{+zws^$QyF!`iQ1C=gk`5`ZU1Z~1&*IZQ>|y& zgdDW`0}U+%(#U!xr4AU+)E=b~wKT{FWDUX~jFoirD~%3w%XIr{Vtg!CZj4%-rX08FpYy=6;2*%J7Qgn^CJot_Pd>A`08 z^af=DL4z>WDX0aP3r}stdQX7%!C%KXJpBykF!}&^6+Y&PINoTZ%TYljE>8*fNcIl0# zw3||Q|BVPOD@A%pz%rqHDGSxX8VVu1U!WFskwnPy^%hHDTPcJS2MNE?mi}jEsKP%Z zsl8WIBclp?6{1oMM%&zK9}EQMd;aph91OyTOU|{ jP$kr%mh>R25v5XPG;GETr?b^oZ+!-~ayuyB9T8 diff --git a/js/bundle.js b/js/bundle.js new file mode 100644 index 0000000..9a90904 --- /dev/null +++ b/js/bundle.js @@ -0,0 +1,20 @@ +import * as esbuild from "npm:esbuild@0.20.2"; +// Import the WASM build on platforms where running subprocesses is not +// permitted, such as Deno Deploy, or when running without `--allow-run`. +// import * as esbuild from "https://deno.land/x/esbuild@0.20.2/wasm.js"; + +import { denoPlugins } from "jsr:@luca/esbuild-deno-loader@^0.10.3"; + +const result = await esbuild.build({ + plugins: [...denoPlugins()], + entryPoints: ["prehooks.js"], + outfile: "prehook.esm.js", + bundle: true, + format: "esm", + minify: true, + treeShaking: true, +}); + +console.log(result.outputFiles); + +esbuild.stop(); \ No newline at end of file diff --git a/js/deno.jsonc b/js/deno.jsonc new file mode 100644 index 0000000..3106ccf --- /dev/null +++ b/js/deno.jsonc @@ -0,0 +1,5 @@ +{ + "tasks": { + "bundle": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run bundle.js" + } +} \ No newline at end of file diff --git a/js/deno.lock b/js/deno.lock new file mode 100644 index 0000000..9b66982 --- /dev/null +++ b/js/deno.lock @@ -0,0 +1,164 @@ +{ + "version": "3", + "packages": { + "specifiers": { + "jsr:@luca/esbuild-deno-loader@^0.10.3": "jsr:@luca/esbuild-deno-loader@0.10.3", + "jsr:@std/assert@^0.213.1": "jsr:@std/assert@0.213.1", + "jsr:@std/encoding@0.213": "jsr:@std/encoding@0.213.1", + "jsr:@std/jsonc@0.213": "jsr:@std/jsonc@0.213.1", + "jsr:@std/path@0.213": "jsr:@std/path@0.213.1", + "npm:esbuild@0.20.2": "npm:esbuild@0.20.2" + }, + "jsr": { + "@luca/esbuild-deno-loader@0.10.3": { + "integrity": "32fc93f7e7f78060234fd5929a740668aab1c742b808c6048b57f9aaea514921", + "dependencies": [ + "jsr:@std/encoding@0.213", + "jsr:@std/jsonc@0.213", + "jsr:@std/path@0.213" + ] + }, + "@std/assert@0.213.1": { + "integrity": "24c28178b30c8e0782c18e8e94ea72b16282207569cdd10ffb9d1d26f2edebfe" + }, + "@std/encoding@0.213.1": { + "integrity": "fcbb6928713dde941a18ca5db88ca1544d0755ec8fb20fe61e2dc8144b390c62" + }, + "@std/jsonc@0.213.1": { + "integrity": "5578f21aa583b7eb7317eed077ffcde47b294f1056bdbb9aacec407758637bfe", + "dependencies": [ + "jsr:@std/assert@^0.213.1" + ] + }, + "@std/path@0.213.1": { + "integrity": "f187bf278a172752e02fcbacf6bd78a335ed320d080a7ed3a5a59c3e88abc673", + "dependencies": [ + "jsr:@std/assert@^0.213.1" + ] + } + }, + "npm": { + "@esbuild/aix-ppc64@0.20.2": { + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "dependencies": {} + }, + "@esbuild/android-arm64@0.20.2": { + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "dependencies": {} + }, + "@esbuild/android-arm@0.20.2": { + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "dependencies": {} + }, + "@esbuild/android-x64@0.20.2": { + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "dependencies": {} + }, + "@esbuild/darwin-arm64@0.20.2": { + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "dependencies": {} + }, + "@esbuild/darwin-x64@0.20.2": { + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "dependencies": {} + }, + "@esbuild/freebsd-arm64@0.20.2": { + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "dependencies": {} + }, + "@esbuild/freebsd-x64@0.20.2": { + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "dependencies": {} + }, + "@esbuild/linux-arm64@0.20.2": { + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "dependencies": {} + }, + "@esbuild/linux-arm@0.20.2": { + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "dependencies": {} + }, + "@esbuild/linux-ia32@0.20.2": { + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "dependencies": {} + }, + "@esbuild/linux-loong64@0.20.2": { + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "dependencies": {} + }, + "@esbuild/linux-mips64el@0.20.2": { + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "dependencies": {} + }, + "@esbuild/linux-ppc64@0.20.2": { + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "dependencies": {} + }, + "@esbuild/linux-riscv64@0.20.2": { + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "dependencies": {} + }, + "@esbuild/linux-s390x@0.20.2": { + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "dependencies": {} + }, + "@esbuild/linux-x64@0.20.2": { + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "dependencies": {} + }, + "@esbuild/netbsd-x64@0.20.2": { + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "dependencies": {} + }, + "@esbuild/openbsd-x64@0.20.2": { + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "dependencies": {} + }, + "@esbuild/sunos-x64@0.20.2": { + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "dependencies": {} + }, + "@esbuild/win32-arm64@0.20.2": { + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "dependencies": {} + }, + "@esbuild/win32-ia32@0.20.2": { + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "dependencies": {} + }, + "@esbuild/win32-x64@0.20.2": { + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "dependencies": {} + }, + "esbuild@0.20.2": { + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dependencies": { + "@esbuild/aix-ppc64": "@esbuild/aix-ppc64@0.20.2", + "@esbuild/android-arm": "@esbuild/android-arm@0.20.2", + "@esbuild/android-arm64": "@esbuild/android-arm64@0.20.2", + "@esbuild/android-x64": "@esbuild/android-x64@0.20.2", + "@esbuild/darwin-arm64": "@esbuild/darwin-arm64@0.20.2", + "@esbuild/darwin-x64": "@esbuild/darwin-x64@0.20.2", + "@esbuild/freebsd-arm64": "@esbuild/freebsd-arm64@0.20.2", + "@esbuild/freebsd-x64": "@esbuild/freebsd-x64@0.20.2", + "@esbuild/linux-arm": "@esbuild/linux-arm@0.20.2", + "@esbuild/linux-arm64": "@esbuild/linux-arm64@0.20.2", + "@esbuild/linux-ia32": "@esbuild/linux-ia32@0.20.2", + "@esbuild/linux-loong64": "@esbuild/linux-loong64@0.20.2", + "@esbuild/linux-mips64el": "@esbuild/linux-mips64el@0.20.2", + "@esbuild/linux-ppc64": "@esbuild/linux-ppc64@0.20.2", + "@esbuild/linux-riscv64": "@esbuild/linux-riscv64@0.20.2", + "@esbuild/linux-s390x": "@esbuild/linux-s390x@0.20.2", + "@esbuild/linux-x64": "@esbuild/linux-x64@0.20.2", + "@esbuild/netbsd-x64": "@esbuild/netbsd-x64@0.20.2", + "@esbuild/openbsd-x64": "@esbuild/openbsd-x64@0.20.2", + "@esbuild/sunos-x64": "@esbuild/sunos-x64@0.20.2", + "@esbuild/win32-arm64": "@esbuild/win32-arm64@0.20.2", + "@esbuild/win32-ia32": "@esbuild/win32-ia32@0.20.2", + "@esbuild/win32-x64": "@esbuild/win32-x64@0.20.2" + } + } + } + }, + "remote": {} +} diff --git a/js/emotion.js b/js/emotion.js new file mode 100644 index 0000000..074840a --- /dev/null +++ b/js/emotion.js @@ -0,0 +1,3 @@ +import { css } from "https://esm.sh/@emotion/css@11.11.2"; + +export default css \ No newline at end of file diff --git a/js/index.js b/js/index.js deleted file mode 100644 index 8b0650d..0000000 --- a/js/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import _ from "lodash" - -// We start by putting all the variable interpolation here at the beginning -// We have to convert all typedarrays in the layout to normal arrays. See Issue #25 -// We use lodash for this for compactness -export function removeTypedArray(o) { - return _.isTypedArray(o) ? Array.from(o) : - _.isPlainObject(o) ? _.mapValues(o, removeTypedArray) : - o -} - -console.log(removeTypedArray([1, 2, 3])) \ No newline at end of file diff --git a/js/jsconfig.json b/js/jsconfig.json deleted file mode 100644 index 0fef23a..0000000 --- a/js/jsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - // Enable latest features - "lib": ["ESNext"], - "target": "ESNext", - "module": "ESNext", - "moduleDetection": "force", - "jsx": "react-jsx", - "allowJs": true, - - // Bundler mode - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": true, - - // Best practices - "strict": true, - "skipLibCheck": true, - "noFallthroughCasesInSwitch": true, - - // Some stricter flags (disabled by default) - "noUnusedLocals": false, - "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false - } -} diff --git a/js/package.json b/js/package.json deleted file mode 100644 index c92d0c8..0000000 --- a/js/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "plutoplotly_helpers", - "module": "index.js", - "type": "module", - "devDependencies": { - "@types/bun": "^1.0.10" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "dependencies": { - "@emotion/css": "^11.11.2", - "lodash": "^4.17.21" - } -} \ No newline at end of file diff --git a/js/prehook.esm.js b/js/prehook.esm.js new file mode 100644 index 0000000..9700475 --- /dev/null +++ b/js/prehook.esm.js @@ -0,0 +1,180 @@ +var Zn=globalThis||(typeof window<"u"?window:self),xm=Object.create,Ou=Object.defineProperty,Am=Object.getOwnPropertyDescriptor,Em=Object.getOwnPropertyNames,km=Object.getPrototypeOf,jm=Object.prototype.hasOwnProperty,Sm=(t,u)=>()=>(u||t((u={exports:{}}).exports,u),u.exports),$m=(t,u)=>{for(var a in u)Ou(t,a,{get:u[a],enumerable:!0})},Mu=(t,u,a,o)=>{if(u&&typeof u=="object"||typeof u=="function")for(let l of Em(u))!jm.call(t,l)&&l!==a&&Ou(t,l,{get:()=>u[l],enumerable:!(o=Am(u,l))||o.enumerable});return t},Cm=(t,u,a)=>(Mu(t,u,"default"),a&&Mu(a,u,"default")),Vs=(t,u,a)=>(a=t!=null?xm(km(t)):{},Mu(u||!t||!t.__esModule?Ou(a,"default",{value:t,enumerable:!0}):a,t)),Zs=Sm((t,u)=>{(function(){var a,o="4.17.21",l=200,d="Unsupported core-js use. Try https://npms.io/search?q=ponyfill.",p="Expected a function",g="Invalid `variable` option passed into `_.template`",A="__lodash_hash_undefined__",_=500,x="__lodash_placeholder__",w=1,R=2,C=4,$=1,I=2,Z=1,G=2,ae=4,Q=8,Ce=16,ie=32,le=64,U=128,Ke=256,fa=512,jl=30,Sl="...",$l=800,Cl=16,pi=1,Tl=2,Rl=3,Lr=1/0,xr=9007199254740991,Nl=17976931348623157e292,Xt=NaN,ur=4294967295,Ml=ur-1,Ol=ur>>>1,Il=[["ary",U],["bind",Z],["bindKey",G],["curry",Q],["curryRight",Ce],["flip",fa],["partial",ie],["partialRight",le],["rearg",Ke]],Kr="[object Arguments]",en="[object Array]",Ll="[object AsyncFunction]",At="[object Boolean]",Et="[object Date]",Fl="[object DOMException]",rn="[object Error]",tn="[object Function]",hi="[object GeneratorFunction]",Je="[object Map]",kt="[object Number]",Pl="[object Null]",fr="[object Object]",di="[object Promise]",Dl="[object Proxy]",jt="[object RegExp]",Qe="[object Set]",St="[object String]",nn="[object Symbol]",ql="[object Undefined]",$t="[object WeakMap]",Ul="[object WeakSet]",Ct="[object ArrayBuffer]",Jr="[object DataView]",pa="[object Float32Array]",ha="[object Float64Array]",da="[object Int8Array]",va="[object Int16Array]",ma="[object Int32Array]",ga="[object Uint8Array]",ya="[object Uint8ClampedArray]",ba="[object Uint16Array]",wa="[object Uint32Array]",Bl=/\b__p \+= '';/g,zl=/\b(__p \+=) '' \+/g,Wl=/(__e\(.*?\)|\b__t\)) \+\n'';/g,vi=/&(?:amp|lt|gt|quot|#39);/g,mi=/[&<>"']/g,Hl=RegExp(vi.source),Vl=RegExp(mi.source),Zl=/<%-([\s\S]+?)%>/g,Gl=/<%([\s\S]+?)%>/g,gi=/<%=([\s\S]+?)%>/g,Yl=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Kl=/^\w*$/,Jl=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,_a=/[\\^$.*+?()[\]{}|]/g,Ql=RegExp(_a.source),xa=/^\s+/,Xl=/\s/,ef=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,rf=/\{\n\/\* \[wrapped with (.+)\] \*/,tf=/,? & /,nf=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,af=/[()=,{}\[\]\/\s]/,uf=/\\(\\)?/g,of=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,yi=/\w*$/,sf=/^[-+]0x[0-9a-f]+$/i,cf=/^0b[01]+$/i,lf=/^\[object .+?Constructor\]$/,ff=/^0o[0-7]+$/i,pf=/^(?:0|[1-9]\d*)$/,hf=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,an=/($^)/,df=/['\n\r\u2028\u2029\\]/g,un="\\ud800-\\udfff",vf="\\u0300-\\u036f",mf="\\ufe20-\\ufe2f",gf="\\u20d0-\\u20ff",bi=vf+mf+gf,wi="\\u2700-\\u27bf",_i="a-z\\xdf-\\xf6\\xf8-\\xff",yf="\\xac\\xb1\\xd7\\xf7",bf="\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf",wf="\\u2000-\\u206f",_f=" \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",xi="A-Z\\xc0-\\xd6\\xd8-\\xde",Ai="\\ufe0e\\ufe0f",Ei=yf+bf+wf+_f,Aa="['\u2019]",xf="["+un+"]",ki="["+Ei+"]",on="["+bi+"]",ji="\\d+",Af="["+wi+"]",Si="["+_i+"]",$i="[^"+un+Ei+ji+wi+_i+xi+"]",Ea="\\ud83c[\\udffb-\\udfff]",Ef="(?:"+on+"|"+Ea+")",Ci="[^"+un+"]",ka="(?:\\ud83c[\\udde6-\\uddff]){2}",ja="[\\ud800-\\udbff][\\udc00-\\udfff]",Qr="["+xi+"]",Ti="\\u200d",Ri="(?:"+Si+"|"+$i+")",kf="(?:"+Qr+"|"+$i+")",Ni="(?:"+Aa+"(?:d|ll|m|re|s|t|ve))?",Mi="(?:"+Aa+"(?:D|LL|M|RE|S|T|VE))?",Oi=Ef+"?",Ii="["+Ai+"]?",jf="(?:"+Ti+"(?:"+[Ci,ka,ja].join("|")+")"+Ii+Oi+")*",Sf="\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",$f="\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])",Li=Ii+Oi+jf,Cf="(?:"+[Af,ka,ja].join("|")+")"+Li,Tf="(?:"+[Ci+on+"?",on,ka,ja,xf].join("|")+")",Rf=RegExp(Aa,"g"),Nf=RegExp(on,"g"),Sa=RegExp(Ea+"(?="+Ea+")|"+Tf+Li,"g"),Mf=RegExp([Qr+"?"+Si+"+"+Ni+"(?="+[ki,Qr,"$"].join("|")+")",kf+"+"+Mi+"(?="+[ki,Qr+Ri,"$"].join("|")+")",Qr+"?"+Ri+"+"+Ni,Qr+"+"+Mi,$f,Sf,ji,Cf].join("|"),"g"),Of=RegExp("["+Ti+un+bi+Ai+"]"),If=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,Lf=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Ff=-1,ne={};ne[pa]=ne[ha]=ne[da]=ne[va]=ne[ma]=ne[ga]=ne[ya]=ne[ba]=ne[wa]=!0,ne[Kr]=ne[en]=ne[Ct]=ne[At]=ne[Jr]=ne[Et]=ne[rn]=ne[tn]=ne[Je]=ne[kt]=ne[fr]=ne[jt]=ne[Qe]=ne[St]=ne[$t]=!1;var te={};te[Kr]=te[en]=te[Ct]=te[Jr]=te[At]=te[Et]=te[pa]=te[ha]=te[da]=te[va]=te[ma]=te[Je]=te[kt]=te[fr]=te[jt]=te[Qe]=te[St]=te[nn]=te[ga]=te[ya]=te[ba]=te[wa]=!0,te[rn]=te[tn]=te[$t]=!1;var Pf={\u00C0:"A",\u00C1:"A",\u00C2:"A",\u00C3:"A",\u00C4:"A",\u00C5:"A",\u00E0:"a",\u00E1:"a",\u00E2:"a",\u00E3:"a",\u00E4:"a",\u00E5:"a",\u00C7:"C",\u00E7:"c",\u00D0:"D",\u00F0:"d",\u00C8:"E",\u00C9:"E",\u00CA:"E",\u00CB:"E",\u00E8:"e",\u00E9:"e",\u00EA:"e",\u00EB:"e",\u00CC:"I",\u00CD:"I",\u00CE:"I",\u00CF:"I",\u00EC:"i",\u00ED:"i",\u00EE:"i",\u00EF:"i",\u00D1:"N",\u00F1:"n",\u00D2:"O",\u00D3:"O",\u00D4:"O",\u00D5:"O",\u00D6:"O",\u00D8:"O",\u00F2:"o",\u00F3:"o",\u00F4:"o",\u00F5:"o",\u00F6:"o",\u00F8:"o",\u00D9:"U",\u00DA:"U",\u00DB:"U",\u00DC:"U",\u00F9:"u",\u00FA:"u",\u00FB:"u",\u00FC:"u",\u00DD:"Y",\u00FD:"y",\u00FF:"y",\u00C6:"Ae",\u00E6:"ae",\u00DE:"Th",\u00FE:"th",\u00DF:"ss",\u0100:"A",\u0102:"A",\u0104:"A",\u0101:"a",\u0103:"a",\u0105:"a",\u0106:"C",\u0108:"C",\u010A:"C",\u010C:"C",\u0107:"c",\u0109:"c",\u010B:"c",\u010D:"c",\u010E:"D",\u0110:"D",\u010F:"d",\u0111:"d",\u0112:"E",\u0114:"E",\u0116:"E",\u0118:"E",\u011A:"E",\u0113:"e",\u0115:"e",\u0117:"e",\u0119:"e",\u011B:"e",\u011C:"G",\u011E:"G",\u0120:"G",\u0122:"G",\u011D:"g",\u011F:"g",\u0121:"g",\u0123:"g",\u0124:"H",\u0126:"H",\u0125:"h",\u0127:"h",\u0128:"I",\u012A:"I",\u012C:"I",\u012E:"I",\u0130:"I",\u0129:"i",\u012B:"i",\u012D:"i",\u012F:"i",\u0131:"i",\u0134:"J",\u0135:"j",\u0136:"K",\u0137:"k",\u0138:"k",\u0139:"L",\u013B:"L",\u013D:"L",\u013F:"L",\u0141:"L",\u013A:"l",\u013C:"l",\u013E:"l",\u0140:"l",\u0142:"l",\u0143:"N",\u0145:"N",\u0147:"N",\u014A:"N",\u0144:"n",\u0146:"n",\u0148:"n",\u014B:"n",\u014C:"O",\u014E:"O",\u0150:"O",\u014D:"o",\u014F:"o",\u0151:"o",\u0154:"R",\u0156:"R",\u0158:"R",\u0155:"r",\u0157:"r",\u0159:"r",\u015A:"S",\u015C:"S",\u015E:"S",\u0160:"S",\u015B:"s",\u015D:"s",\u015F:"s",\u0161:"s",\u0162:"T",\u0164:"T",\u0166:"T",\u0163:"t",\u0165:"t",\u0167:"t",\u0168:"U",\u016A:"U",\u016C:"U",\u016E:"U",\u0170:"U",\u0172:"U",\u0169:"u",\u016B:"u",\u016D:"u",\u016F:"u",\u0171:"u",\u0173:"u",\u0174:"W",\u0175:"w",\u0176:"Y",\u0177:"y",\u0178:"Y",\u0179:"Z",\u017B:"Z",\u017D:"Z",\u017A:"z",\u017C:"z",\u017E:"z",\u0132:"IJ",\u0133:"ij",\u0152:"Oe",\u0153:"oe",\u0149:"'n",\u017F:"s"},Df={"&":"&","<":"<",">":">",'"':""","'":"'"},qf={"&":"&","<":"<",">":">",""":'"',"'":"'"},Uf={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Bf=parseFloat,zf=parseInt,Fi=typeof Zn=="object"&&Zn&&Zn.Object===Object&&Zn,Wf=typeof self=="object"&&self&&self.Object===Object&&self,ge=Fi||Wf||Function("return this")(),$a=typeof t=="object"&&t&&!t.nodeType&&t,Fr=$a&&typeof u=="object"&&u&&!u.nodeType&&u,Pi=Fr&&Fr.exports===$a,Ca=Pi&&Fi.process,Be=function(){try{var m=Fr&&Fr.require&&Fr.require("util").types;return m||Ca&&Ca.binding&&Ca.binding("util")}catch{}}(),Di=Be&&Be.isArrayBuffer,qi=Be&&Be.isDate,Ui=Be&&Be.isMap,Bi=Be&&Be.isRegExp,zi=Be&&Be.isSet,Wi=Be&&Be.isTypedArray;function Le(m,E,b){switch(b.length){case 0:return m.call(E);case 1:return m.call(E,b[0]);case 2:return m.call(E,b[0],b[1]);case 3:return m.call(E,b[0],b[1],b[2])}return m.apply(E,b)}function Hf(m,E,b,N){for(var P=-1,J=m==null?0:m.length;++P-1}function Ta(m,E,b){for(var N=-1,P=m==null?0:m.length;++N-1;);return b}function Qi(m,E){for(var b=m.length;b--&&Xr(E,m[b],0)>-1;);return b}function ep(m,E){for(var b=m.length,N=0;b--;)m[b]===E&&++N;return N}var rp=Oa(Pf),tp=Oa(Df);function np(m){return"\\"+Uf[m]}function ap(m,E){return m==null?a:m[E]}function et(m){return Of.test(m)}function up(m){return If.test(m)}function ip(m){for(var E,b=[];!(E=m.next()).done;)b.push(E.value);return b}function Pa(m){var E=-1,b=Array(m.size);return m.forEach(function(N,P){b[++E]=[P,N]}),b}function Xi(m,E){return function(b){return m(E(b))}}function kr(m,E){for(var b=-1,N=m.length,P=0,J=[];++b-1}function Zp(e,r){var n=this.__data__,i=kn(n,e);return i<0?(++this.size,n.push([e,r])):n[i][1]=r,this}pr.prototype.clear=zp,pr.prototype.delete=Wp,pr.prototype.get=Hp,pr.prototype.has=Vp,pr.prototype.set=Zp;function hr(e){var r=-1,n=e==null?0:e.length;for(this.clear();++r=r?e:r)),e}function Ve(e,r,n,i,s,f){var h,v=r&w,y=r&R,k=r&C;if(n&&(h=s?n(e,i,s,f):n(e)),h!==a)return h;if(!oe(e))return e;var j=D(e);if(j){if(h=Jh(e),!v)return Te(e,h)}else{var S=Ee(e),T=S==tn||S==hi;if(Nr(e))return Lo(e,v);if(S==fr||S==Kr||T&&!s){if(h=y||T?{}:rs(e),!v)return y?qh(e,ch(h,e)):Dh(e,fo(h,e))}else{if(!te[S])return s?e:{};h=Qh(e,S,v)}}f||(f=new er);var M=f.get(e);if(M)return M;f.set(e,h),Ts(e)?e.forEach(function(F){h.add(Ve(F,r,n,F,e,f))}):$s(e)&&e.forEach(function(F,W){h.set(W,Ve(F,r,n,W,e,f))});var L=k?y?lu:cu:y?Ne:ye,B=j?a:L(e);return ze(B||e,function(F,W){B&&(W=F,F=e[W]),Lt(h,W,Ve(F,r,n,W,e,f))}),h}function lh(e){var r=ye(e);return function(n){return po(n,e,r)}}function po(e,r,n){var i=n.length;if(e==null)return!i;for(e=re(e);i--;){var s=n[i],f=r[s],h=e[s];if(h===a&&!(s in e)||!f(h))return!1}return!0}function ho(e,r,n){if(typeof e!="function")throw new We(p);return zt(function(){e.apply(a,n)},r)}function Ft(e,r,n,i){var s=-1,f=sn,h=!0,v=e.length,y=[],k=r.length;if(!v)return y;n&&(r=ue(r,Fe(n))),i?(f=Ta,h=!1):r.length>=l&&(f=Tt,h=!1,r=new qr(r));e:for(;++ss?0:s+n),i=i===a||i>s?s:q(i),i<0&&(i+=s),i=n>i?0:Ns(i);n0&&n(v)?r>1?we(v,r-1,n,i,s):Er(s,v):i||(s[s.length]=v)}return s}var Ha=Bo(),go=Bo(!0);function ir(e,r){return e&&Ha(e,r,ye)}function Va(e,r){return e&&go(e,r,ye)}function Sn(e,r){return Ar(r,function(n){return yr(e[n])})}function Br(e,r){r=Tr(r,e);for(var n=0,i=r.length;e!=null&&nr}function hh(e,r){return e!=null&&ee.call(e,r)}function dh(e,r){return e!=null&&r in re(e)}function vh(e,r,n){return e>=Ae(r,n)&&e=120&&j.length>=120)?new qr(h&&j):a}j=e[0];var S=-1,T=v[0];e:for(;++S-1;)v!==e&&yn.call(v,y,1),yn.call(e,y,1);return e}function $o(e,r){for(var n=e?r.length:0,i=n-1;n--;){var s=r[n];if(n==i||s!==f){var f=s;gr(s)?yn.call(e,s,1):tu(e,s)}}return e}function Xa(e,r){return e+_n(oo()*(r-e+1))}function $h(e,r,n,i){for(var s=-1,f=me(wn((r-e)/(n||1)),0),h=b(f);f--;)h[i?f:++s]=e,e+=n;return h}function eu(e,r){var n="";if(!e||r<1||r>xr)return n;do r%2&&(n+=e),r=_n(r/2),r&&(e+=e);while(r);return n}function z(e,r){return gu(as(e,r,Me),e+"")}function Ch(e){return lo(lt(e))}function Th(e,r){var n=lt(e);return Pn(n,Ur(r,0,n.length))}function qt(e,r,n,i){if(!oe(e))return e;r=Tr(r,e);for(var s=-1,f=r.length,h=f-1,v=e;v!=null&&++ss?0:s+r),n=n>s?s:n,n<0&&(n+=s),s=r>n?0:n-r>>>0,r>>>=0;for(var f=b(s);++i>>1,h=e[f];h!==null&&!De(h)&&(n?h<=r:h=l){var k=r?null:Wh(e);if(k)return ln(k);h=!1,s=Tt,y=new qr}else y=r?[]:v;e:for(;++i=i?e:Ze(e,r,n)}var Io=_p||function(e){return ge.clearTimeout(e)};function Lo(e,r){if(r)return e.slice();var n=e.length,i=to?to(n):new e.constructor(n);return e.copy(i),i}function iu(e){var r=new e.constructor(e.byteLength);return new mn(r).set(new mn(e)),r}function Ih(e,r){var n=r?iu(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}function Lh(e){var r=new e.constructor(e.source,yi.exec(e));return r.lastIndex=e.lastIndex,r}function Fh(e){return It?re(It.call(e)):{}}function Fo(e,r){var n=r?iu(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function Po(e,r){if(e!==r){var n=e!==a,i=e===null,s=e===e,f=De(e),h=r!==a,v=r===null,y=r===r,k=De(r);if(!v&&!k&&!f&&e>r||f&&h&&y&&!v&&!k||i&&h&&y||!n&&y||!s)return 1;if(!i&&!f&&!k&&e=v)return y;var k=n[i];return y*(k=="desc"?-1:1)}}return e.index-r.index}function Do(e,r,n,i){for(var s=-1,f=e.length,h=n.length,v=-1,y=r.length,k=me(f-h,0),j=b(y+k),S=!i;++v1?n[s-1]:a,h=s>2?n[2]:a;for(f=e.length>3&&typeof f=="function"?(s--,f):a,h&&je(n[0],n[1],h)&&(f=s<3?a:f,s=1),r=re(r);++i-1?s[f?r[h]:h]:a}}function Ho(e){return mr(function(r){var n=r.length,i=n,s=He.prototype.thru;for(e&&r.reverse();i--;){var f=r[i];if(typeof f!="function")throw new We(p);if(s&&!h&&Ln(f)=="wrapper")var h=new He([],!0)}for(i=h?i:n;++i1&&V.reverse(),j&&yv))return!1;var k=f.get(e),j=f.get(r);if(k&&j)return k==r&&j==e;var S=-1,T=!0,M=n&I?new qr:a;for(f.set(e,r),f.set(r,e);++S1?"& ":"")+r[i],r=r.join(n>2?", ":" "),e.replace(ef,`{ +/* [wrapped with `+r+`] */ +`)}function e0(e){return D(e)||Hr(e)||!!(uo&&e&&e[uo])}function gr(e,r){var n=typeof e;return r=r??xr,!!r&&(n=="number"||n!="symbol"&&pf.test(e))&&e>-1&&e%1==0&&e0){if(++r>=$l)return arguments[0]}else r=0;return e.apply(a,arguments)}}function Pn(e,r){var n=-1,i=e.length,s=i-1;for(r=r===a?i:r;++n1?e[r-1]:a;return n=typeof n=="function"?(e.pop(),n):a,ms(e,n)});function gs(e){var r=c(e);return r.__chain__=!0,r}function fd(e,r){return r(e),e}function Dn(e,r){return r(e)}var pd=mr(function(e){var r=e.length,n=r?e[0]:0,i=this.__wrapped__,s=function(f){return Wa(f,e)};return r>1||this.__actions__.length||!(i instanceof H)||!gr(n)?this.thru(s):(i=i.slice(n,+n+(r?1:0)),i.__actions__.push({func:Dn,args:[s],thisArg:a}),new He(i,this.__chain__).thru(function(f){return r&&!f.length&&f.push(a),f}))});function hd(){return gs(this)}function dd(){return new He(this.value(),this.__chain__)}function vd(){this.__values__===a&&(this.__values__=Rs(this.value()));var e=this.__index__>=this.__values__.length,r=e?a:this.__values__[this.__index__++];return{done:e,value:r}}function md(){return this}function gd(e){for(var r,n=this;n instanceof En;){var i=ls(n);i.__index__=0,i.__values__=a,r?s.__wrapped__=i:r=i;var s=i;n=n.__wrapped__}return s.__wrapped__=e,r}function yd(){var e=this.__wrapped__;if(e instanceof H){var r=e;return this.__actions__.length&&(r=new H(this)),r=r.reverse(),r.__actions__.push({func:Dn,args:[yu],thisArg:a}),new He(r,this.__chain__)}return this.thru(yu)}function bd(){return Mo(this.__wrapped__,this.__actions__)}var wd=Rn(function(e,r,n){ee.call(e,n)?++e[n]:dr(e,n,1)});function _d(e,r,n){var i=D(e)?Hi:fh;return n&&je(e,r,n)&&(r=a),i(e,O(r,3))}function xd(e,r){var n=D(e)?Ar:mo;return n(e,O(r,3))}var Ad=Wo(fs),Ed=Wo(ps);function kd(e,r){return we(qn(e,r),1)}function jd(e,r){return we(qn(e,r),Lr)}function Sd(e,r,n){return n=n===a?1:q(n),we(qn(e,r),n)}function ys(e,r){var n=D(e)?ze:$r;return n(e,O(r,3))}function bs(e,r){var n=D(e)?Vf:vo;return n(e,O(r,3))}var $d=Rn(function(e,r,n){ee.call(e,n)?e[n].push(r):dr(e,n,[r])});function Cd(e,r,n,i){e=Re(e)?e:lt(e),n=n&&!i?q(n):0;var s=e.length;return n<0&&(n=me(s+n,0)),Hn(e)?n<=s&&e.indexOf(r,n)>-1:!!s&&Xr(e,r,n)>-1}var Td=z(function(e,r,n){var i=-1,s=typeof r=="function",f=Re(e)?b(e.length):[];return $r(e,function(h){f[++i]=s?Le(r,h,n):Pt(h,r,n)}),f}),Rd=Rn(function(e,r,n){dr(e,n,r)});function qn(e,r){var n=D(e)?ue:xo;return n(e,O(r,3))}function Nd(e,r,n,i){return e==null?[]:(D(r)||(r=r==null?[]:[r]),n=i?a:n,D(n)||(n=n==null?[]:[n]),jo(e,r,n))}var Md=Rn(function(e,r,n){e[n?0:1].push(r)},function(){return[[],[]]});function Od(e,r,n){var i=D(e)?Ra:Yi,s=arguments.length<3;return i(e,O(r,4),n,s,$r)}function Id(e,r,n){var i=D(e)?Zf:Yi,s=arguments.length<3;return i(e,O(r,4),n,s,vo)}function Ld(e,r){var n=D(e)?Ar:mo;return n(e,zn(O(r,3)))}function Fd(e){var r=D(e)?lo:Ch;return r(e)}function Pd(e,r,n){(n?je(e,r,n):r===a)?r=1:r=q(r);var i=D(e)?ih:Th;return i(e,r)}function Dd(e){var r=D(e)?oh:Nh;return r(e)}function qd(e){if(e==null)return 0;if(Re(e))return Hn(e)?rt(e):e.length;var r=Ee(e);return r==Je||r==Qe?e.size:Ka(e).length}function Ud(e,r,n){var i=D(e)?Na:Mh;return n&&je(e,r,n)&&(r=a),i(e,O(r,3))}var Bd=z(function(e,r){if(e==null)return[];var n=r.length;return n>1&&je(e,r[0],r[1])?r=[]:n>2&&je(r[0],r[1],r[2])&&(r=[r[0]]),jo(e,we(r,1),[])}),Un=xp||function(){return ge.Date.now()};function zd(e,r){if(typeof r!="function")throw new We(p);return e=q(e),function(){if(--e<1)return r.apply(this,arguments)}}function ws(e,r,n){return r=n?a:r,r=e&&r==null?e.length:r,vr(e,U,a,a,a,a,r)}function _s(e,r){var n;if(typeof r!="function")throw new We(p);return e=q(e),function(){return--e>0&&(n=r.apply(this,arguments)),e<=1&&(r=a),n}}var wu=z(function(e,r,n){var i=Z;if(n.length){var s=kr(n,st(wu));i|=ie}return vr(e,i,r,n,s)}),xs=z(function(e,r,n){var i=Z|G;if(n.length){var s=kr(n,st(xs));i|=ie}return vr(r,i,e,n,s)});function As(e,r,n){r=n?a:r;var i=vr(e,Q,a,a,a,a,a,r);return i.placeholder=As.placeholder,i}function Es(e,r,n){r=n?a:r;var i=vr(e,Ce,a,a,a,a,a,r);return i.placeholder=Es.placeholder,i}function ks(e,r,n){var i,s,f,h,v,y,k=0,j=!1,S=!1,T=!0;if(typeof e!="function")throw new We(p);r=Ye(r)||0,oe(n)&&(j=!!n.leading,S="maxWait"in n,f=S?me(Ye(n.maxWait)||0,r):f,T="trailing"in n?!!n.trailing:T);function M(he){var tr=i,wr=s;return i=s=a,k=he,h=e.apply(wr,tr),h}function L(he){return k=he,v=zt(W,r),j?M(he):h}function B(he){var tr=he-y,wr=he-k,Ws=r-tr;return S?Ae(Ws,f-wr):Ws}function F(he){var tr=he-y,wr=he-k;return y===a||tr>=r||tr<0||S&&wr>=f}function W(){var he=Un();if(F(he))return V(he);v=zt(W,B(he))}function V(he){return v=a,T&&i?M(he):(i=s=a,h)}function qe(){v!==a&&Io(v),k=0,i=y=s=v=a}function Se(){return v===a?h:V(Un())}function Ue(){var he=Un(),tr=F(he);if(i=arguments,s=this,y=he,tr){if(v===a)return L(y);if(S)return Io(v),v=zt(W,r),M(y)}return v===a&&(v=zt(W,r)),h}return Ue.cancel=qe,Ue.flush=Se,Ue}var Wd=z(function(e,r){return ho(e,1,r)}),Hd=z(function(e,r,n){return ho(e,Ye(r)||0,n)});function Vd(e){return vr(e,fa)}function Bn(e,r){if(typeof e!="function"||r!=null&&typeof r!="function")throw new We(p);var n=function(){var i=arguments,s=r?r.apply(this,i):i[0],f=n.cache;if(f.has(s))return f.get(s);var h=e.apply(this,i);return n.cache=f.set(s,h)||f,h};return n.cache=new(Bn.Cache||hr),n}Bn.Cache=hr;function zn(e){if(typeof e!="function")throw new We(p);return function(){var r=arguments;switch(r.length){case 0:return!e.call(this);case 1:return!e.call(this,r[0]);case 2:return!e.call(this,r[0],r[1]);case 3:return!e.call(this,r[0],r[1],r[2])}return!e.apply(this,r)}}function Zd(e){return _s(2,e)}var Gd=Oh(function(e,r){r=r.length==1&&D(r[0])?ue(r[0],Fe(O())):ue(we(r,1),Fe(O()));var n=r.length;return z(function(i){for(var s=-1,f=Ae(i.length,n);++s=r}),Hr=bo(function(){return arguments}())?bo:function(e){return fe(e)&&ee.call(e,"callee")&&!ao.call(e,"callee")},D=b.isArray,cv=Di?Fe(Di):gh;function Re(e){return e!=null&&Wn(e.length)&&!yr(e)}function pe(e){return fe(e)&&Re(e)}function lv(e){return e===!0||e===!1||fe(e)&&ke(e)==At}var Nr=Ep||Nu,fv=qi?Fe(qi):yh;function pv(e){return fe(e)&&e.nodeType===1&&!Wt(e)}function hv(e){if(e==null)return!0;if(Re(e)&&(D(e)||typeof e=="string"||typeof e.splice=="function"||Nr(e)||ct(e)||Hr(e)))return!e.length;var r=Ee(e);if(r==Je||r==Qe)return!e.size;if(Bt(e))return!Ka(e).length;for(var n in e)if(ee.call(e,n))return!1;return!0}function dv(e,r){return Dt(e,r)}function vv(e,r,n){n=typeof n=="function"?n:a;var i=n?n(e,r):a;return i===a?Dt(e,r,a,n):!!i}function xu(e){if(!fe(e))return!1;var r=ke(e);return r==rn||r==Fl||typeof e.message=="string"&&typeof e.name=="string"&&!Wt(e)}function mv(e){return typeof e=="number"&&io(e)}function yr(e){if(!oe(e))return!1;var r=ke(e);return r==tn||r==hi||r==Ll||r==Dl}function Ss(e){return typeof e=="number"&&e==q(e)}function Wn(e){return typeof e=="number"&&e>-1&&e%1==0&&e<=xr}function oe(e){var r=typeof e;return e!=null&&(r=="object"||r=="function")}function fe(e){return e!=null&&typeof e=="object"}var $s=Ui?Fe(Ui):wh;function gv(e,r){return e===r||Ya(e,r,pu(r))}function yv(e,r,n){return n=typeof n=="function"?n:a,Ya(e,r,pu(r),n)}function bv(e){return Cs(e)&&e!=+e}function wv(e){if(n0(e))throw new P(d);return wo(e)}function _v(e){return e===null}function xv(e){return e==null}function Cs(e){return typeof e=="number"||fe(e)&&ke(e)==kt}function Wt(e){if(!fe(e)||ke(e)!=fr)return!1;var r=gn(e);if(r===null)return!0;var n=ee.call(r,"constructor")&&r.constructor;return typeof n=="function"&&n instanceof n&&hn.call(n)==yp}var Au=Bi?Fe(Bi):_h;function Av(e){return Ss(e)&&e>=-xr&&e<=xr}var Ts=zi?Fe(zi):xh;function Hn(e){return typeof e=="string"||!D(e)&&fe(e)&&ke(e)==St}function De(e){return typeof e=="symbol"||fe(e)&&ke(e)==nn}var ct=Wi?Fe(Wi):Ah;function Ev(e){return e===a}function kv(e){return fe(e)&&Ee(e)==$t}function jv(e){return fe(e)&&ke(e)==Ul}var Sv=In(Ja),$v=In(function(e,r){return e<=r});function Rs(e){if(!e)return[];if(Re(e))return Hn(e)?Xe(e):Te(e);if(Rt&&e[Rt])return ip(e[Rt]());var r=Ee(e),n=r==Je?Pa:r==Qe?ln:lt;return n(e)}function br(e){if(!e)return e===0?e:0;if(e=Ye(e),e===Lr||e===-Lr){var r=e<0?-1:1;return r*Nl}return e===e?e:0}function q(e){var r=br(e),n=r%1;return r===r?n?r-n:r:0}function Ns(e){return e?Ur(q(e),0,ur):0}function Ye(e){if(typeof e=="number")return e;if(De(e))return Xt;if(oe(e)){var r=typeof e.valueOf=="function"?e.valueOf():e;e=oe(r)?r+"":r}if(typeof e!="string")return e===0?e:+e;e=Ki(e);var n=cf.test(e);return n||ff.test(e)?zf(e.slice(2),n?2:8):sf.test(e)?Xt:+e}function Ms(e){return or(e,Ne(e))}function Cv(e){return e?Ur(q(e),-xr,xr):e===0?e:0}function X(e){return e==null?"":Pe(e)}var Tv=it(function(e,r){if(Bt(r)||Re(r)){or(r,ye(r),e);return}for(var n in r)ee.call(r,n)&&Lt(e,n,r[n])}),Os=it(function(e,r){or(r,Ne(r),e)}),Vn=it(function(e,r,n,i){or(r,Ne(r),e,i)}),Rv=it(function(e,r,n,i){or(r,ye(r),e,i)}),Nv=mr(Wa);function Mv(e,r){var n=ut(e);return r==null?n:fo(n,r)}var Ov=z(function(e,r){e=re(e);var n=-1,i=r.length,s=i>2?r[2]:a;for(s&&je(r[0],r[1],s)&&(i=1);++n1),f}),or(e,lu(e),n),i&&(n=Ve(n,w|R|C,Hh));for(var s=r.length;s--;)tu(n,r[s]);return n});function Qv(e,r){return Ls(e,zn(O(r)))}var Xv=mr(function(e,r){return e==null?{}:jh(e,r)});function Ls(e,r){if(e==null)return{};var n=ue(lu(e),function(i){return[i]});return r=O(r),So(e,n,function(i,s){return r(i,s[0])})}function e1(e,r,n){r=Tr(r,e);var i=-1,s=r.length;for(s||(s=1,e=a);++ir){var i=e;e=r,r=i}if(n||e%1||r%1){var s=oo();return Ae(e+s*(r-e+Bf("1e-"+((s+"").length-1))),r)}return Xa(e,r)}var f1=ot(function(e,r,n){return r=r.toLowerCase(),e+(n?Ds(r):r)});function Ds(e){return ju(X(e).toLowerCase())}function qs(e){return e=X(e),e&&e.replace(hf,rp).replace(Nf,"")}function p1(e,r,n){e=X(e),r=Pe(r);var i=e.length;n=n===a?i:Ur(q(n),0,i);var s=n;return n-=r.length,n>=0&&e.slice(n,s)==r}function h1(e){return e=X(e),e&&Vl.test(e)?e.replace(mi,tp):e}function d1(e){return e=X(e),e&&Ql.test(e)?e.replace(_a,"\\$&"):e}var v1=ot(function(e,r,n){return e+(n?"-":"")+r.toLowerCase()}),m1=ot(function(e,r,n){return e+(n?" ":"")+r.toLowerCase()}),g1=zo("toLowerCase");function y1(e,r,n){e=X(e),r=q(r);var i=r?rt(e):0;if(!r||i>=r)return e;var s=(r-i)/2;return On(_n(s),n)+e+On(wn(s),n)}function b1(e,r,n){e=X(e),r=q(r);var i=r?rt(e):0;return r&&i>>0,n?(e=X(e),e&&(typeof r=="string"||r!=null&&!Au(r))&&(r=Pe(r),!r&&et(e))?Rr(Xe(e),0,n):e.split(r,n)):[]}var j1=ot(function(e,r,n){return e+(n?" ":"")+ju(r)});function S1(e,r,n){return e=X(e),n=n==null?0:Ur(q(n),0,e.length),r=Pe(r),e.slice(n,n+r.length)==r}function $1(e,r,n){var i=c.templateSettings;n&&je(e,r,n)&&(r=a),e=X(e),r=Vn({},r,i,Ko);var s=Vn({},r.imports,i.imports,Ko),f=ye(s),h=Fa(s,f),v,y,k=0,j=r.interpolate||an,S="__p += '",T=Da((r.escape||an).source+"|"+j.source+"|"+(j===gi?of:an).source+"|"+(r.evaluate||an).source+"|$","g"),M="//# sourceURL="+(ee.call(r,"sourceURL")?(r.sourceURL+"").replace(/\s/g," "):"lodash.templateSources["+ ++Ff+"]")+` +`;e.replace(T,function(F,W,V,qe,Se,Ue){return V||(V=qe),S+=e.slice(k,Ue).replace(df,np),W&&(v=!0,S+=`' + +__e(`+W+`) + +'`),Se&&(y=!0,S+=`'; +`+Se+`; +__p += '`),V&&(S+=`' + +((__t = (`+V+`)) == null ? '' : __t) + +'`),k=Ue+F.length,F}),S+=`'; +`;var L=ee.call(r,"variable")&&r.variable;if(!L)S=`with (obj) { +`+S+` +} +`;else if(af.test(L))throw new P(g);S=(y?S.replace(Bl,""):S).replace(zl,"$1").replace(Wl,"$1;"),S="function("+(L||"obj")+`) { +`+(L?"":`obj || (obj = {}); +`)+"var __t, __p = ''"+(v?", __e = _.escape":"")+(y?`, __j = Array.prototype.join; +function print() { __p += __j.call(arguments, '') } +`:`; +`)+S+`return __p +}`;var B=Bs(function(){return J(f,M+"return "+S).apply(a,h)});if(B.source=S,xu(B))throw B;return B}function C1(e){return X(e).toLowerCase()}function T1(e){return X(e).toUpperCase()}function R1(e,r,n){if(e=X(e),e&&(n||r===a))return Ki(e);if(!e||!(r=Pe(r)))return e;var i=Xe(e),s=Xe(r),f=Ji(i,s),h=Qi(i,s)+1;return Rr(i,f,h).join("")}function N1(e,r,n){if(e=X(e),e&&(n||r===a))return e.slice(0,eo(e)+1);if(!e||!(r=Pe(r)))return e;var i=Xe(e),s=Qi(i,Xe(r))+1;return Rr(i,0,s).join("")}function M1(e,r,n){if(e=X(e),e&&(n||r===a))return e.replace(xa,"");if(!e||!(r=Pe(r)))return e;var i=Xe(e),s=Ji(i,Xe(r));return Rr(i,s).join("")}function O1(e,r){var n=jl,i=Sl;if(oe(r)){var s="separator"in r?r.separator:s;n="length"in r?q(r.length):n,i="omission"in r?Pe(r.omission):i}e=X(e);var f=e.length;if(et(e)){var h=Xe(e);f=h.length}if(n>=f)return e;var v=n-rt(i);if(v<1)return i;var y=h?Rr(h,0,v).join(""):e.slice(0,v);if(s===a)return y+i;if(h&&(v+=y.length-v),Au(s)){if(e.slice(v).search(s)){var k,j=y;for(s.global||(s=Da(s.source,X(yi.exec(s))+"g")),s.lastIndex=0;k=s.exec(j);)var S=k.index;y=y.slice(0,S===a?v:S)}}else if(e.indexOf(Pe(s),v)!=v){var T=y.lastIndexOf(s);T>-1&&(y=y.slice(0,T))}return y+i}function I1(e){return e=X(e),e&&Hl.test(e)?e.replace(vi,lp):e}var L1=ot(function(e,r,n){return e+(n?" ":"")+r.toUpperCase()}),ju=zo("toUpperCase");function Us(e,r,n){return e=X(e),r=n?a:r,r===a?up(e)?hp(e):Kf(e):e.match(r)||[]}var Bs=z(function(e,r){try{return Le(e,a,r)}catch(n){return xu(n)?n:new P(n)}}),F1=mr(function(e,r){return ze(r,function(n){n=sr(n),dr(e,n,wu(e[n],e))}),e});function P1(e){var r=e==null?0:e.length,n=O();return e=r?ue(e,function(i){if(typeof i[1]!="function")throw new We(p);return[n(i[0]),i[1]]}):[],z(function(i){for(var s=-1;++sxr)return[];var n=ur,i=Ae(e,ur);r=O(r),e-=ur;for(var s=La(i,r);++n0||r<0)?new H(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),r!==a&&(r=q(r),n=r<0?n.dropRight(-r):n.take(r-e)),n)},H.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},H.prototype.toArray=function(){return this.take(ur)},ir(H.prototype,function(e,r){var n=/^(?:filter|find|map|reject)|While$/.test(r),i=/^(?:head|last)$/.test(r),s=c[i?"take"+(r=="last"?"Right":""):r],f=i||/^find/.test(r);s&&(c.prototype[r]=function(){var h=this.__wrapped__,v=i?[1]:arguments,y=h instanceof H,k=v[0],j=y||D(h),S=function(W){var V=s.apply(c,Er([W],v));return i&&T?V[0]:V};j&&n&&typeof k=="function"&&k.length!=1&&(y=j=!1);var T=this.__chain__,M=!!this.__actions__.length,L=f&&!T,B=y&&!M;if(!f&&j){h=B?h:new H(this);var F=e.apply(h,v);return F.__actions__.push({func:Dn,args:[S],thisArg:a}),new He(F,T)}return L&&B?e.apply(this,v):(F=this.thru(S),L?i?F.value()[0]:F.value():F)})}),ze(["pop","push","shift","sort","splice","unshift"],function(e){var r=fn[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",i=/^(?:pop|shift)$/.test(e);c.prototype[e]=function(){var s=arguments;if(i&&!this.__chain__){var f=this.value();return r.apply(D(f)?f:[],s)}return this[n](function(h){return r.apply(D(h)?h:[],s)})}}),ir(H.prototype,function(e,r){var n=c[r];if(n){var i=n.name+"";ee.call(at,i)||(at[i]=[]),at[i].push({name:r,func:n})}}),at[Nn(a,G).name]=[{name:"wrapper",func:a}],H.prototype.clone=Ip,H.prototype.reverse=Lp,H.prototype.value=Fp,c.prototype.at=pd,c.prototype.chain=hd,c.prototype.commit=dd,c.prototype.next=vd,c.prototype.plant=gd,c.prototype.reverse=yd,c.prototype.toJSON=c.prototype.valueOf=c.prototype.value=bd,c.prototype.first=c.prototype.head,Rt&&(c.prototype[Rt]=md),c},jr=dp();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(ge._=jr,define(function(){return jr})):Fr?((Fr.exports=jr)._=jr,$a._=jr):ge._=jr}).call(t)}),Gs={};$m(Gs,{default:()=>Iu});var Tm=Vs(Zs());Cm(Gs,Vs(Zs()));var{default:Hs,...Rm}=Tm,Iu=Hs!==void 0?Hs:Rm;var Gn=new Map,Ht=[],Lu=Ht.map,Nm=Ht.some,Mm=Ht.hasOwnProperty,Om=/^((?:@[^/@]+\/)?[^/@]+)(?:@([^/]+))?(?:\/(.*))?$/,Im=/^\d+\.\d+\.\d+(-[\w-.+]+)?$/,Ys=/(?:\.[^/]*|\/)$/,ft=class extends Error{constructor(t){super(t)}};ft.prototype.name=ft.name;function Ks(t){let u=Om.exec(t);return u&&{name:u[1],version:u[2],path:u[3]}}function Fu(t="https://cdn.jsdelivr.net/npm/",u=["unpkg","jsdelivr","browser","main"]){if(!/\/$/.test(t))throw new Error("origin lacks trailing slash");function a(l){for(let d of u){let p=l[d];if(typeof p=="string")return p.startsWith("./")&&(p=p.slice(2)),Ys.test(p)?p:`${p}.js`}}function o(l){let d=`${t}${l.name}${l.version?`@${l.version}`:""}/package.json`,p=Gn.get(d);return p||Gn.set(d,p=fetch(d).then(g=>{if(!g.ok)throw new ft("unable to load package.json");return g.redirected&&!Gn.has(g.url)&&Gn.set(g.url,p),g.json()})),p}return async function(l,d){if(l.startsWith(t)&&(l=l.substring(t.length)),/^(\w+:)|\/\//i.test(l))return l;if(/^[.]{0,2}\//i.test(l))return new URL(l,d??location).href;if(!l.length||/^[\s._]/.test(l)||/\s$/.test(l))throw new ft("illegal name");let p=Ks(l);if(!p)return`${t}${l}`;if(!p.version&&d!=null&&d.startsWith(t)){let A=await o(Ks(d.substring(t.length)));p.version=A.dependencies&&A.dependencies[p.name]||A.peerDependencies&&A.peerDependencies[p.name]}if(p.path&&!Ys.test(p.path)&&(p.path+=".js"),p.path&&p.version&&Im.test(p.version))return`${t}${p.name}@${p.version}/${p.path}`;let g=await o(p);return`${t}${g.name}@${g.version}/${p.path||a(g)||"index.js"}`}}var Js=pt(Fu());function pt(t){let u=new Map,a=l(null);function o(g){if(typeof g!="string")return g;let A=u.get(g);return A||u.set(g,A=new Promise((_,x)=>{let w=document.createElement("script");w.onload=()=>{try{_(Ht.pop()(l(g)))}catch{x(new ft("invalid module"))}w.remove()},w.onerror=()=>{x(new ft("unable to load module")),w.remove()},w.async=!0,w.src=g,window.define=Qs,document.head.appendChild(w)})),A}function l(g){return A=>Promise.resolve(t(A,g)).then(o)}function d(g){return pt((A,_)=>A in g&&(A=g[A],_=null,typeof A!="string")?A:t(A,_))}function p(g){return arguments.length>1?Promise.all(Lu.call(arguments,a)).then(Lm):a(g)}return p.alias=d,p.resolve=t,p}function Lm(t){let u={};for(let a of t)for(let o in a)Mm.call(a,o)&&(a[o]==null?Object.defineProperty(u,o,{get:Fm(a,o)}):u[o]=a[o]);return u}function Fm(t,u){return()=>t[u]}function Pm(t){return t=t+"",t==="exports"||t==="module"}function Qs(t,u,a){let o=arguments.length;o<2?(a=t,u=[]):o<3&&(a=u,u=typeof t=="string"?[]:t),Ht.push(Nm.call(u,Pm)?l=>{let d={},p={exports:d};return Promise.all(Lu.call(u,g=>(g=g+"",g==="exports"?d:g==="module"?p:l(g)))).then(g=>(a.apply(null,g),p.exports))}:l=>Promise.all(Lu.call(u,l)).then(d=>typeof a=="function"?a.apply(null,d):a))}Qs.amd={};var Xs={},Pu={},Du=34,Vt=10,qu=13;function rc(t){return new Function("d","return {"+t.map(function(u,a){return JSON.stringify(u)+": d["+a+'] || ""'}).join(",")+"}")}function Dm(t,u){var a=rc(t);return function(o,l){return u(a(o),l,t)}}function ec(t){var u=Object.create(null),a=[];return t.forEach(function(o){for(var l in o)l in u||a.push(u[l]=l)}),a}function Oe(t,u){var a=t+"",o=a.length;return o9999?"+"+Oe(t,6):Oe(t,4)}function Um(t){var u=t.getUTCHours(),a=t.getUTCMinutes(),o=t.getUTCSeconds(),l=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":qm(t.getUTCFullYear(),4)+"-"+Oe(t.getUTCMonth()+1,2)+"-"+Oe(t.getUTCDate(),2)+(l?"T"+Oe(u,2)+":"+Oe(a,2)+":"+Oe(o,2)+"."+Oe(l,3)+"Z":o?"T"+Oe(u,2)+":"+Oe(a,2)+":"+Oe(o,2)+"Z":a||u?"T"+Oe(u,2)+":"+Oe(a,2)+"Z":"")}function tc(t){var u=new RegExp('["'+t+` +\r]`),a=t.charCodeAt(0);function o(w,R){var C,$,I=l(w,function(Z,G){if(C)return C(Z,G-1);$=Z,C=R?Dm(Z,R):rc(Z)});return I.columns=$||[],I}function l(w,R){var C=[],$=w.length,I=0,Z=0,G,ae=$<=0,Q=!1;w.charCodeAt($-1)===Vt&&--$,w.charCodeAt($-1)===qu&&--$;function Ce(){if(ae)return Pu;if(Q)return Q=!1,Xs;var le,U=I,Ke;if(w.charCodeAt(U)===Du){for(;I++<$&&w.charCodeAt(I)!==Du||w.charCodeAt(++I)===Du;);return(le=I)>=$?ae=!0:(Ke=w.charCodeAt(I++))===Vt?Q=!0:Ke===qu&&(Q=!0,w.charCodeAt(I)===Vt&&++I),w.slice(U+1,le-1).replace(/""/g,'"')}for(;I<$;){if((Ke=w.charCodeAt(le=I++))===Vt)Q=!0;else if(Ke===qu)Q=!0,w.charCodeAt(I)===Vt&&++I;else if(Ke!==a)continue;return w.slice(U,le)}return ae=!0,w.slice(U,$)}for(;(G=Ce())!==Pu;){for(var ie=[];G!==Xs&&G!==Pu;)ie.push(G),G=Ce();R&&(ie=R(ie,Z++))==null||C.push(ie)}return C}function d(w,R){return w.map(function(C){return R.map(function($){return x(C[$])}).join(t)})}function p(w,R){return R==null&&(R=ec(w)),[R.map(x).join(t)].concat(d(w,R)).join(` +`)}function g(w,R){return R==null&&(R=ec(w)),d(w,R).join(` +`)}function A(w){return w.map(_).join(` +`)}function _(w){return w.map(x).join(t)}function x(w){return w==null?"":w instanceof Date?Um(w):u.test(w+="")?'"'+w.replace(/"/g,'""')+'"':w}return{parse:o,parseRows:l,format:p,formatBody:g,formatRows:A,formatRow:_,formatValue:x}}var Vr=tc(","),nc=Vr.parse,ac=Vr.parseRows,Nb=Vr.format,Mb=Vr.formatBody,Ob=Vr.formatRows,Ib=Vr.formatRow,Lb=Vr.formatValue,Zr=tc(" "),uc=Zr.parse,ic=Zr.parseRows,Fb=Zr.format,Pb=Zr.formatBody,Db=Zr.formatRows,qb=Zr.formatRow,Ub=Zr.formatValue;function oc(t){for(var u in t){var a=t[u].trim(),o,l;if(!a)a=null;else if(a==="true")a=!0;else if(a==="false")a=!1;else if(a==="NaN")a=NaN;else if(!isNaN(o=+a))a=o;else if(l=a.match(/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/))Bm&&l[4]&&!l[7]&&(a=a.replace(/-/g,"/").replace(/T/," ")),a=new Date(a);else continue;t[u]=a}return t}var Bm=new Date("2019-01-01T00:00").getHours()||new Date("2019-07-01T00:00").getHours();function Gr(t,u){return t==null||u==null?NaN:tu?1:t>=u?0:NaN}function zm(t,u){return t==null||u==null?NaN:ut?1:u>=t?0:NaN}function cc(t){let u,a,o;t.length!==2?(u=Gr,a=(g,A)=>Gr(t(g),A),o=(g,A)=>t(g)-A):(u=t===Gr||t===zm?t:Wm,a=t,o=t);function l(g,A,_=0,x=g.length){if(_>>1;a(g[w],A)<0?_=w+1:x=w}while(_>>1;a(g[w],A)<=0?_=w+1:x=w}while(__&&o(g[w-1],A)>-o(g[w],A)?w-1:w}return{left:l,center:p,right:d}}function Wm(){return 0}function Hm(t){return t===null?NaN:+t}var lc=cc(Gr),Wb=lc.right,Hb=lc.left,Vb=cc(Hm).center;var Zb=fc(pc),Gb=fc(Vm);function fc(t){return function(u,a,o=a){if(!((a=+a)>=0))throw new RangeError("invalid rx");if(!((o=+o)>=0))throw new RangeError("invalid ry");let{data:l,width:d,height:p}=u;if(!((d=Math.floor(d))>=0))throw new RangeError("invalid width");if(!((p=Math.floor(p!==void 0?p:l.length/d))>=0))throw new RangeError("invalid height");if(!d||!p||!a&&!o)return u;let g=a&&t(a),A=o&&t(o),_=l.slice();return g&&A?(ht(g,_,l,d,p),ht(g,l,_,d,p),ht(g,_,l,d,p),dt(A,l,_,d,p),dt(A,_,l,d,p),dt(A,l,_,d,p)):g?(ht(g,l,_,d,p),ht(g,_,l,d,p),ht(g,l,_,d,p)):A&&(dt(A,l,_,d,p),dt(A,_,l,d,p),dt(A,l,_,d,p)),u}}function ht(t,u,a,o,l){for(let d=0,p=o*l;d{l<<=2,d<<=2,p<<=2,u(a,o,l+0,d+0,p),u(a,o,l+1,d+1,p),u(a,o,l+2,d+2,p),u(a,o,l+3,d+3,p)}}function pc(t){let u=Math.floor(t);if(u===t)return Zm(t);let a=t-u,o=2*t+1;return(l,d,p,g,A)=>{if(!((g-=A)>=p))return;let _=u*d[p],x=A*u,w=x+A;for(let R=p,C=p+x;R{if(!((d-=p)>=l))return;let g=t*o[l],A=p*t;for(let _=l,x=l+A;_0:Gr(p,p)===0)&&(a=d,l=p,o=!0)}}else for(let l of t)(o?u(l,a)>0:u(l,l)===0)&&(a=l,o=!0);return a}var tw=Gm(Math.random);function Gm(t){return function(u,a=0,o=u.length){let l=o-(a=+a);for(;l;){let d=t()*l--|0,p=u[l+a];u[l+a]=u[d+a],u[d+a]=p}return u}}function vc(t){if(typeof t[Symbol.iterator]!="function")throw new TypeError("values is not iterable");return Array.from(t).reverse()}var Ym=Object.defineProperty,Xn=(t,u)=>{for(var a in u)Ym(t,a,{get:u[a],enumerable:!0})};function ce(t,u,a){return{resolve(o=a){return`${t}@${u}/${o}`}}}var Km=ce("d3","7.8.5","dist/d3.min.js"),Jm=ce("@observablehq/inputs","0.10.6","dist/inputs.min.js"),Qm=ce("@observablehq/plot","0.6.13","dist/plot.umd.min.js"),Xm=ce("@observablehq/graphviz","0.2.1","dist/graphviz.min.js"),Uu=ce("@observablehq/highlight.js","2.0.0","highlight.min.js"),mc=ce("@observablehq/katex","0.11.1","dist/katex.min.js"),eg=ce("lodash","4.17.21","lodash.min.js"),rg=ce("htl","0.3.1","dist/htl.min.js"),tg=ce("jszip","3.10.1","dist/jszip.min.js"),ng=ce("marked","0.3.12","marked.min.js"),gc=ce("sql.js","1.8.0","dist/sql-wasm.js"),ag=ce("vega","5.22.1","build/vega.min.js"),ug=ce("vega-lite","5.6.0","build/vega-lite.min.js"),ig=ce("vega-lite-api","5.0.0","build/vega-lite-api.min.js"),Wu=ce("apache-arrow","4.0.1","Arrow.es2015.min.js"),og=ce("apache-arrow","9.0.0","+esm"),Tc=ce("apache-arrow","11.0.0","+esm"),sg=ce("arquero","4.8.8","dist/arquero.min.js"),cg=ce("topojson-client","3.1.0","dist/topojson-client.min.js"),lg=ce("exceljs","4.3.0","dist/exceljs.min.js"),fg=ce("mermaid","9.2.2","dist/mermaid.min.js"),yc=ce("leaflet","1.9.3","dist/leaflet.js"),Zt=ce("@duckdb/duckdb-wasm","1.24.0","+esm"),Or="https://cdn.observableusercontent.com/npm/",Ir=Js;function pg(t){Ir=t}function hg(t){return t==null?Ir:pt(t)}async function Rc(t){let[u,a]=await Promise.all([t(gc.resolve()),t.resolve(gc.resolve("dist/"))]);return u({locateFile:o=>`${a}${o}`})}var Gu=class Nc{constructor(u){Object.defineProperties(this,{_db:{value:u}})}static async open(u){let[a,o]=await Promise.all([Rc(Ir),Promise.resolve(u).then(Hu)]);return new Nc(new a.Database(o))}async query(u,a){return await vg(this._db,u,a)}async queryRow(u,a){return(await this.query(u,a))[0]||null}async explain(u,a){let o=await this.query(`EXPLAIN QUERY PLAN ${u}`,a);return Mr("pre",{className:"observablehq--inspect"},[Bu(o.map(l=>l.detail).join(` +`))])}async describeTables({schema:u}={}){return this.query(`SELECT NULLIF(schema, 'main') AS schema, name FROM pragma_table_list() WHERE type = 'table'${u==null?"":" AND schema = ?"} AND name NOT LIKE 'sqlite_%' ORDER BY schema, name`,u==null?[]:[u])}async describeColumns({schema:u,table:a}={}){if(a==null)throw new Error("missing table");let o=await this.query(`SELECT name, type, "notnull" FROM pragma_table_info(?${u==null?"":", ?"}) ORDER BY cid`,u==null?[a]:[a,u]);if(!o.length)throw new Error(`table not found: ${a}`);return o.map(({name:l,type:d,notnull:p})=>({name:l,type:dg(d),databaseType:d,nullable:!p}))}async describe(u){let a=await(u===void 0?this.query("SELECT name FROM sqlite_master WHERE type = 'table'"):this.query("SELECT * FROM pragma_table_info(?)",[u]));if(!a.length)throw new Error("Not found");let{columns:o}=a;return Mr("table",{value:a},[Mr("thead",[Mr("tr",o.map(l=>Mr("th",[Bu(l)])))]),Mr("tbody",a.map(l=>Mr("tr",o.map(d=>Mr("td",[Bu(l[d])])))))])}async sql(){return this.query(...this.queryTag.apply(this,arguments))}queryTag(u,...a){return[u.join("?"),a]}};Object.defineProperty(Gu.prototype,"dialect",{value:"sqlite"});function dg(t){switch(t){case"NULL":return"null";case"INT":case"INTEGER":case"TINYINT":case"SMALLINT":case"MEDIUMINT":case"BIGINT":case"UNSIGNED BIG INT":case"INT2":case"INT8":return"integer";case"TEXT":case"CLOB":return"string";case"REAL":case"DOUBLE":case"DOUBLE PRECISION":case"FLOAT":case"NUMERIC":return"number";case"BLOB":return"buffer";case"DATE":case"DATETIME":return"string";default:return/^(?:(?:(?:VARYING|NATIVE) )?CHARACTER|(?:N|VAR|NVAR)CHAR)\(/.test(t)?"string":/^(?:DECIMAL|NUMERIC)\(/.test(t)?"number":"other"}}function Hu(t){return typeof t=="string"?fetch(t).then(Hu):t instanceof Response||t instanceof Blob?t.arrayBuffer().then(Hu):t instanceof ArrayBuffer?new Uint8Array(t):t}async function vg(t,u,a){let[o]=await t.exec(u,a);if(!o)return[];let{columns:l,values:d}=o,p=d.map(g=>Object.fromEntries(g.map((A,_)=>[l[_],A])));return p.columns=l,p}function Mr(t,u,a){arguments.length===2&&(a=u,u=void 0);let o=document.createElement(t);if(u!==void 0)for(let l in u)o[l]=u[l];if(a!==void 0)for(let l of a)o.appendChild(l);return o}function Bu(t){return document.createTextNode(t)}function Yu(t){return t&&typeof t.toArrowBuffer=="function"}function Jn(t){return t&&typeof t.getChild=="function"&&typeof t.toArray=="function"&&t.schema&&Array.isArray(t.schema.fields)}function mg(t){return t.schema.fields.map(gg)}function gg(t){return{name:t.name,type:yg(t.type),nullable:t.nullable,databaseType:String(t.type)}}function yg(t){switch(t.typeId){case 2:return"integer";case 3:case 7:return"number";case 4:case 15:return"buffer";case 5:return"string";case 6:return"boolean";case 8:case 9:case 10:return"date";case 12:case 16:return"array";case 13:case 14:return"object";case 11:case 17:default:return"other"}}async function Ku(){return await import(`${Or}${Tc.resolve()}`)}var zu,Ju=class Mc{constructor(u){Object.defineProperties(this,{_db:{value:u}})}async queryStream(u,a){let o=await this._db.connect(),l,d;try{if(a?.length>0?l=await(await o.prepare(u)).send(...a):l=await o.send(u),d=await l.next(),d.done)throw new Error("missing first batch")}catch(p){throw await o.close(),p}return{schema:mg(d.value),async*readRows(){try{for(;!d.done;)yield d.value.toArray(),d=await l.next()}finally{await o.close()}}}}async query(u,a){let o=await this.queryStream(u,a),l=[];for await(let d of o.readRows())for(let p of d)l.push(p);return l.schema=o.schema,l}async queryRow(u,a){let o=(await this.queryStream(u,a)).readRows();try{let{done:l,value:d}=await o.next();return l||!d.length?null:d[0]}finally{await o.return()}}async sql(u,...a){return await this.query(u.join("?"),a)}queryTag(u,...a){return[u.join("?"),a]}escape(u){return`"${u}"`}async describeTables(){return(await this.query("SHOW TABLES")).map(({name:u})=>({name:u}))}async describeColumns({table:u}={}){return(await this.query(`DESCRIBE ${this.escape(u)}`)).map(({column_name:a,column_type:o,null:l})=>({name:a,type:Ag(o),nullable:l!=="NO",databaseType:o}))}static async of(u={},a={}){let o=await xg();return a.query?.castTimestampToDate===void 0&&(a={...a,query:{...a.query,castTimestampToDate:!0}}),a.query?.castBigIntToDouble===void 0&&(a={...a,query:{...a.query,castBigIntToDouble:!0}}),await o.open(a),await Promise.all(Object.entries(u).map(async([l,d])=>{if(d instanceof _e)await bc(o,l,d);else if(Jn(d))await Qn(o,l,d);else if(Array.isArray(d))await wc(o,l,d);else if(Yu(d))await wg(o,l,d);else if("data"in d){let{data:p,...g}=d;Jn(p)?await Qn(o,l,p,g):await wc(o,l,p,g)}else if("file"in d){let{file:p,...g}=d;await bc(o,l,p,g)}else throw new Error(`invalid source: ${d}`)})),new Mc(o)}};Object.defineProperty(Ju.prototype,"dialect",{value:"duckdb"});async function bc(t,u,a,o){let l=await a.url();if(l.startsWith("blob:")){let p=await a.arrayBuffer();await t.registerFileBuffer(a.name,new Uint8Array(p))}else await t.registerFileURL(a.name,new URL(l,location).href,4);let d=await t.connect();try{switch(a.mimeType){case"text/csv":case"text/tab-separated-values":return await d.insertCSVFromPath(a.name,{name:u,schema:"main",...o}).catch(async p=>{if(p.toString().includes("Could not convert"))return await bg(d,a,u);throw p});case"application/json":return await d.insertJSONFromPath(a.name,{name:u,schema:"main",...o});default:if(/\.arrow$/i.test(a.name)){let p=new Uint8Array(await a.arrayBuffer());return await d.insertArrowFromIPCStream(p,{name:u,schema:"main",...o})}if(/\.parquet$/i.test(a.name))return await d.query(`CREATE VIEW '${u}' AS SELECT * FROM parquet_scan('${a.name}')`);throw new Error(`unknown file type: ${a.mimeType}`)}}finally{await d.close()}}async function bg(t,u,a){return await(await t.prepare(`CREATE TABLE '${a}' AS SELECT * FROM read_csv_auto(?, ALL_VARCHAR=TRUE)`)).send(u.name)}async function Qn(t,u,a,o){let l=await t.connect();try{await l.insertArrowTable(a,{name:u,schema:"main",...o})}finally{await l.close()}}async function wg(t,u,a){let o=(await Ku()).tableFromIPC(a.toArrowBuffer());return await Qn(t,u,o)}async function wc(t,u,a,o){let l=(await Ku()).tableFromJSON(a);return await Qn(t,u,l,o)}async function _g(){let t=await import(`${Or}${Zt.resolve()}`),u=await t.selectBundle({mvp:{mainModule:`${Or}${Zt.resolve("dist/duckdb-mvp.wasm")}`,mainWorker:`${Or}${Zt.resolve("dist/duckdb-browser-mvp.worker.js")}`},eh:{mainModule:`${Or}${Zt.resolve("dist/duckdb-eh.wasm")}`,mainWorker:`${Or}${Zt.resolve("dist/duckdb-browser-eh.worker.js")}`}}),a=new t.ConsoleLogger;return{module:t,bundle:u,logger:a}}async function xg(){zu===void 0&&(zu=_g());let{module:t,bundle:u,logger:a}=await zu,o=await t.createWorker(u.mainWorker),l=new t.AsyncDuckDB(a,o);return await l.instantiate(u.mainModule),l}function Ag(t){switch(t){case"BIGINT":case"HUGEINT":case"UBIGINT":return"bigint";case"DOUBLE":case"REAL":case"FLOAT":return"number";case"INTEGER":case"SMALLINT":case"TINYINT":case"USMALLINT":case"UINTEGER":case"UTINYINT":return"integer";case"BOOLEAN":return"boolean";case"DATE":case"TIMESTAMP":case"TIMESTAMP WITH TIME ZONE":return"date";case"VARCHAR":case"UUID":return"string";default:return/^DECIMAL\(/.test(t)?"integer":"other"}}var Qu=20;function Eg(t,u){return t&&(typeof t.sql=="function"||typeof t.queryTag=="function"&&(typeof t.query=="function"||typeof t.queryStream=="function"))&&(u!=="table"||typeof t.describeColumns=="function")&&t!==qc}function ea(t){return Array.isArray(t)&&(Oc(t.schema)||Ic(t.columns)||kg(t)||Fc(t)||Pc(t))||Dc(t)}function kg(t){let u=Math.min(Qu,t.length);for(let a=0;a0&&jg(t[0])}function jg(t){for(let u in t)return!0;return!1}function Oc(t){return Array.isArray(t)&&t.every(Sg)}function Ic(t){return Array.isArray(t)&&t.every(u=>typeof u=="string")}function Sg(t){return t&&typeof t.name=="string"&&typeof t.type=="string"}function Lc(t){return Dc(t)||Fc(t)||Pc(t)}function Fc(t){let u=Math.min(Qu,t.length);if(!(u>0))return!1;let a,o=!1;for(let l=0;l0))return!1;let a=!1;for(let o=0;o{if(t=await $g(await t,o),Eg(t))return _c(t,Mg(u,t),a);if(ea(t))return Qg(t,u);throw t?new Error("invalid data source"):new Error("missing data source")},{sql(t,u,a){return async function(){return _c(await Cg(await t,a),arguments,u)}}});function Xu(t){let u=new WeakMap;return(a,o)=>{if(!a||typeof a!="object")throw new Error("invalid data source");let l=u.get(a);return(!l||ea(a)&&a.length!==l._numRows)&&(l=t(a,o),l._numRows=a.length,u.set(a,l)),l}}var pw=Xu(async t=>{if(t instanceof _e){switch(t.mimeType){case"text/csv":return t.csv({typed:"auto"});case"text/tab-separated-values":return t.tsv({typed:"auto"});case"application/json":return t.json()}throw new Error(`unsupported file type: ${t.mimeType}`)}return t}),$g=Xu(async(t,u)=>{if(t instanceof _e){switch(t.mimeType){case"text/csv":return t.csv();case"text/tab-separated-values":return t.tsv();case"application/json":return t.json();case"application/x-sqlite3":return t.sqlite()}if(/\.(arrow|parquet)$/i.test(t.name))return mt(t,u);throw new Error(`unsupported file type: ${t.mimeType}`)}return Jn(t)||Yu(t)?mt(t,u):ea(t)&&Lc(t)?Array.from(t,a=>({value:a})):t}),Cg=Xu(async(t,u)=>{if(t instanceof _e){switch(t.mimeType){case"text/csv":case"text/tab-separated-values":case"application/json":return mt(t,u);case"application/x-sqlite3":return t.sqlite()}if(/\.(arrow|parquet)$/i.test(t.name))return mt(t,u);throw new Error(`unsupported file type: ${t.mimeType}`)}return ea(t)?mt(await Tg(t,u),u):Jn(t)||Yu(t)?mt(t,u):t});async function Tg(t,u){let a=await Ku();return Lc(t)?a.tableFromArrays({[u]:t}):a.tableFromJSON(t)}function mt(t,u=t instanceof _e?Rg(t):"__table"){return Ju.of({[u]:t})}function Rg(t){return t.name.replace(/@\d+(?=\.|$)/,"").replace(/\.\w+$/,"")}async function _c(t,u,a){if(!t)throw new Error("missing data source");if(typeof t.queryTag=="function"){let o=new AbortController,l={signal:o.signal};if(a.then(()=>o.abort("invalidated")),typeof t.queryStream=="function")return Ng(t.queryStream(...t.queryTag.apply(t,u),l));if(typeof t.query=="function")return t.query(...t.queryTag.apply(t,u),l)}if(typeof t.sql=="function")return t.sql.apply(t,u);throw new Error("source does not implement query, queryStream, or sql")}async function*Ng(t){let u=performance.now(),a=await t,o=[];o.done=!1,o.error=null,o.schema=a.schema;try{for await(let l of a.readRows()){performance.now()-u>150&&o.length>0&&(yield o,u=performance.now());for(let d of l)o.push(d)}o.done=!0,yield o}catch(l){o.error=l,yield o}}function Mg(t,u){let a=typeof u.escape=="function"?u.escape:x=>x,{select:o,from:l,filter:d,sort:p,slice:g}=t;if(!l.table)throw new Error("missing from table");if(o.columns&&o.columns.length===0)throw new Error("at least one column must be selected");let A=new Map(t.names?.map(({column:x,name:w})=>[x,w])),_=[[`SELECT ${o.columns?o.columns.map(x=>{let w=A.get(x);return w?`${a(x)} AS ${a(w)}`:a(x)}).join(", "):"*"} FROM ${Og(l.table,a)}`]];for(let x=0;x ",a);break;case"gt":se(" > ",a);break;case"lt":se(" < ",a);break;case"gte":se(" >= ",a);break;case"lte":se(" <= ",a);break;default:throw new Error("Invalid filter operation")}vt(u[1],a,o);return}switch(vt(u[0],a,o),t){case"in":se(" IN (",a);break;case"nin":se(" NOT IN (",a);break;default:throw new Error("Invalid filter operation")}Lg(u.slice(1),a),se(")",a)}function vt(t,u,a){t.type==="column"?se(a(t.value),u):(u.push(t.value),u[0].push(""))}function Lg(t,u){let a=!0;for(let o of t)a?a=!1:se(",",u),u.push(o.value),u[0].push("")}function Fg(t){return{...t,value:`%${t.value}%`}}function Uc(t,u){return(t==null||!(t>=t))-(u==null||!(u>=u))}function Pg(t,u){return Uc(t,u)||(tu?1:0)}function Dg(t,u){return Uc(t,u)||(t>u?-1:ttypeof t=="number"&&!Number.isNaN(t),Ug=t=>Number.isInteger(t)&&!Number.isNaN(t),Bg=t=>typeof t=="string",zg=t=>typeof t=="boolean",Wg=t=>typeof t=="bigint",Hg=t=>t instanceof Date&&!isNaN(t),Vg=t=>t instanceof ArrayBuffer,Zg=t=>Array.isArray(t),Gg=t=>typeof t=="object"&&t!==null,Yg=t=>t!=null;function Ac(t){switch(t){case"string":return Bg;case"bigint":return Wg;case"boolean":return zg;case"number":return qg;case"integer":return Ug;case"date":return Hg;case"buffer":return Vg;case"array":return Zg;case"object":return Gg;case"other":default:return Yg}}var Bc=/^(([-+]\d{2})?\d{4}(-\d{2}(-\d{2}))|(\d{1,2})\/(\d{1,2})\/(\d{2,4}))([T ]\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/;function Kg(t,u){switch(u){case"string":return typeof t=="string"||t==null?t:String(t);case"boolean":if(typeof t=="string"){let a=t.trim().toLowerCase();return a==="true"?!0:a==="false"?!1:null}return typeof t=="boolean"||t==null?t:!!t;case"bigint":return typeof t=="bigint"||t==null?t:Number.isInteger(typeof t=="string"&&!t.trim()?NaN:+t)?BigInt(t):void 0;case"integer":case"number":return typeof t=="number"?t:t==null||typeof t=="string"&&!t.trim()?NaN:Number(t);case"date":{if(t instanceof Date||t==null)return t;if(typeof t=="number")return new Date(t);let a=String(t).trim();return typeof t=="string"&&!a?null:new Date(Bc.test(a)?a:NaN)}case"array":case"object":case"buffer":case"other":return t;default:throw new Error(`Unable to coerce to type: ${u}`)}}function Jg(t){let{columns:u}=t,{schema:a}=t;return Oc(a)?{schema:a,inferred:!1}:(a=zc(t,Ic(u)?u:void 0),{schema:a,inferred:!0})}function Ec(t,u){let a=t,{schema:o,inferred:l}=Jg(t),d=new Map(o.map(({name:p,type:g})=>[p,g]));if(u.types){for(let{name:p,type:g}of u.types){d.set(p,g),o===a.schema&&(o=o.slice());let A=o.findIndex(_=>_.name===p);A>-1&&(o[A]={...o[A],type:g})}t=t.map(p=>Vu(p,d,o))}else l&&(t=t.map(p=>Vu(p,d,o)));return{source:t,schema:o}}function kc(t,u){if(!u.names)return t;let a=new Map(u.names.map(o=>[o.column,o]));return t.map(o=>Object.fromEntries(Object.keys(o).map(l=>[a.get(l)?.name??l,o[l]])))}function Qg(t,u){let a=new Map,o=t,l=Ec(t,u);t=l.source;let d=l.schema;if(u.derive){let _=[];u.derive.map(({name:w,value:R})=>{let C=[];kc(t,u).map(($,I)=>{let Z;try{Z=R($)}catch(G){C.push({index:I,error:G}),Z=void 0}_[I]?_[I]={..._[I],[w]:Z}:_.push({[w]:Z})}),C.length&&a.set(w,C)});let x=Ec(_,u);t=t.map((w,R)=>({...w,...x.source[R]})),d=[...d,...x.schema]}for(let{type:_,operands:x}of u.filter){let[{value:w}]=x,R=x.slice(1).map(({value:C})=>C);switch(_){case"v":{let[C]=R,$=Ac(C);t=t.filter(I=>$(I[w]));break}case"nv":{let[C]=R,$=Ac(C);t=t.filter(I=>!$(I[w]));break}case"eq":{let[C]=R;if(C instanceof Date){let $=+C;t=t.filter(I=>+I[w]===$)}else t=t.filter($=>$[w]===C);break}case"ne":{let[C]=R;t=t.filter($=>$[w]!==C);break}case"c":{let[C]=R;t=t.filter($=>typeof $[w]=="string"&&$[w].includes(C));break}case"nc":{let[C]=R;t=t.filter($=>typeof $[w]=="string"&&!$[w].includes(C));break}case"in":{let C=new Set(R);t=t.filter($=>C.has($[w]));break}case"nin":{let C=new Set(R);t=t.filter($=>!C.has($[w]));break}case"n":{t=t.filter(C=>C[w]==null);break}case"nn":{t=t.filter(C=>C[w]!=null);break}case"lt":{let[C]=R;t=t.filter($=>$[w]$[w]<=C);break}case"gt":{let[C]=R;t=t.filter($=>$[w]>C);break}case"gte":{let[C]=R;t=t.filter($=>$[w]>=C);break}default:throw new Error(`unknown filter type: ${_}`)}}for(let{column:_,direction:x}of vc(u.sort)){let w=x==="desc"?Dg:Pg;t===o&&(t=t.slice()),t.sort((R,C)=>w(R[_],C[_]))}let{from:p,to:g}=u.slice;p=p==null?0:Math.max(0,p),g=g==null?1/0:Math.max(0,g),(p>0||g<1/0)&&(t=t.slice(Math.max(0,p),Math.max(0,g)));let A=d.slice();if(u.select.columns){if(d){let _=new Map(d.map(x=>[x.name,x]));d=u.select.columns.map(x=>_.get(x))}t=t.map(_=>Object.fromEntries(u.select.columns.map(x=>[x,_[x]])))}if(u.names){let _=new Map(u.names.map(x=>[x.column,x]));d&&(d=d.map(x=>{let w=_.get(x.name);return{...x,...w?{name:w.name}:null}})),A&&(A=A.map(x=>{let w=_.get(x.name);return{...x,...w?{name:w.name}:null}})),t=kc(t,u)}return t!==o&&d&&(t.schema=d),t.fullSchema=A,t.errors=a,t}function Vu(t,u,a){let o={};for(let l of a){let d=u.get(l.name),p=t[l.name];o[l.name]=d==="raw"?p:Kg(p,d)}return o}function Xg(){return{boolean:0,integer:0,number:0,date:0,string:0,array:0,object:0,bigint:0,buffer:0,defined:0}}var ey=["boolean","integer","number","date","bigint","array","object","buffer"];function ry(t){let u=new Set;for(let a of t)if(a)for(let o in a)Object.prototype.hasOwnProperty.call(a,o)&&u.add(o);return Array.from(u)}function zc(t,u=ry(t)){let a=[],o=t.slice(0,100),l={};for(let d of u){let p=l[d]=Xg();for(let _ of o){let x=_[d];if(x==null)continue;let w=typeof x;if(w!=="string")++p.defined,Array.isArray(x)?++p.array:x instanceof Date?++p.date:x instanceof ArrayBuffer?++p.buffer:w==="number"?(++p.number,Number.isInteger(x)&&++p.integer):w in p&&++p[w];else{if(x=x.trim(),!x)continue;++p.defined,++p.string,/^(true|false)$/i.test(x)?++p.boolean:x&&!isNaN(x)?(++p.number,Number.isInteger(+x)&&++p.integer):Bc.test(x)&&++p.date}}let g=Math.max(1,p.defined*.9),A=dc(ey,_=>p[_]>=g?p[_]:NaN)??(p.string>=g?"string":"other");a.push({name:d,type:A,inferred:A})}return a}var ty=class{constructor(t){Object.defineProperties(this,{_:{value:t},sheetNames:{value:t.worksheets.map(u=>u.name),enumerable:!0}})}sheet(t,u){let a=typeof t=="number"?this.sheetNames[t]:this.sheetNames.includes(t+="")?t:null;if(a==null)throw new Error(`Sheet not found: ${t}`);let o=this._.getWorksheet(a);return ny(o,u)}};function ny(t,{range:u,headers:a}={}){let[[o,l],[d,p]]=ay(u,t),g=a?t._rows[l++]:null,A=new Set(["#"]);for(let x=o;x<=d;x++){let w=g?jc(g.findCell(x+1)):null,R=w&&w+""||uy(x);for(;A.has(R);)R+="_";A.add(R)}A=new Array(o).concat(Array.from(A));let _=new Array(p-l+1);for(let x=l;x<=p;x++){let w=_[x-l]=Object.create(null,{"#":{value:x+1}}),R=t.getRow(x+1);if(R.hasValues)for(let C=o;C<=d;C++){let $=jc(R.findCell(C+1));$!=null&&(w[A[C+1]]=$)}}return _.columns=A.filter(()=>!0),_}function jc(t){if(!t)return;let{value:u}=t;if(u&&typeof u=="object"&&!(u instanceof Date)){if(u.formula||u.sharedFormula)return u.result&&u.result.error?NaN:u.result;if(u.richText)return Sc(u);if(u.text){let{text:a}=u;return a.richText&&(a=Sc(a)),u.hyperlink&&u.hyperlink!==a?`${u.hyperlink} ${a}`:a}return u}return u}function Sc(t){return t.richText.map(u=>u.text).join("")}function ay(t=":",{columnCount:u,rowCount:a}){if(t+="",!t.match(/^[A-Z]*\d*:[A-Z]*\d*$/))throw new Error("Malformed range specifier");let[[o=0,l=0],[d=u-1,p=a-1]]=t.split(":").map(iy);return[[o,l],[d,p]]}function uy(t){let u="";t++;do u=String.fromCharCode(64+(t%26||26))+u;while(t=Math.floor((t-1)/26));return u}function iy(t){let[,u,a]=t.match(/^([A-Z]*)(\d*)$/),o=0;if(u)for(let l=0;l[o,l]));return Object.assign(t.map(o=>Vu(o,a,u)),{schema:u})}async function $c(t,u,{array:a=!1,typed:o=!1}={}){let l=await t.text(),d=u===" "?a?ic:uc:a?ac:nc;if(o==="auto"&&!a){let p=d(l);return oy(p,zc(p,p.columns))}return d(l,o&&oc)}var ei=class{constructor(t,u){Object.defineProperty(this,"name",{value:t,enumerable:!0}),u!==void 0&&Object.defineProperty(this,"mimeType",{value:u+"",enumerable:!0})}async blob(){return(await _r(this)).blob()}async arrayBuffer(){return(await _r(this)).arrayBuffer()}async text(){return(await _r(this)).text()}async json(){return(await _r(this)).json()}async stream(){return(await _r(this)).body}async csv(t){return $c(this,",",t)}async tsv(t){return $c(this," ",t)}async image(t){let u=await this.url();return new Promise((a,o)=>{let l=new Image;new URL(u,document.baseURI).origin!==new URL(location).origin&&(l.crossOrigin="anonymous"),Object.assign(l,t),l.onload=()=>a(l),l.onerror=()=>o(new Error(`Unable to load file: ${this.name}`)),l.src=u})}async arrow({version:t=4}={}){switch(t){case 4:{let[u,a]=await Promise.all([Ir(Wu.resolve()),_r(this)]);return u.Table.from(a)}case 9:{let[u,a]=await Promise.all([import(`${Or}${og.resolve()}`),_r(this)]);return u.tableFromIPC(a)}case 11:{let[u,a]=await Promise.all([import(`${Or}${Tc.resolve()}`),_r(this)]);return u.tableFromIPC(a)}default:throw new Error(`unsupported arrow version: ${t}`)}}async sqlite(){return Gu.open(_r(this))}async zip(){let[t,u]=await Promise.all([Ir(tg.resolve()),this.arrayBuffer()]);return new cy(await t.loadAsync(u))}async xml(t="application/xml"){return new DOMParser().parseFromString(await this.text(),t)}async html(){return this.xml("text/html")}async xlsx(){let[t,u]=await Promise.all([Ir(lg.resolve()),this.arrayBuffer()]);return new ty(await new t.Workbook().xlsx.load(u))}},_e=class extends ei{constructor(t,u,a){super(u,a),Object.defineProperty(this,"_url",{value:t})}async url(){return await this._url+""}};function sy(t){throw new Error(`File not found: ${t}`)}var cy=class{constructor(t){Object.defineProperty(this,"_",{value:t}),this.filenames=Object.keys(t.files).filter(u=>!t.files[u].dir)}file(t){let u=this._.file(t+="");if(!u||u.dir)throw new Error(`file not found: ${t}`);return new ly(u)}},ly=class extends ei{constructor(t){super(t.name),Object.defineProperty(this,"_",{value:t}),Object.defineProperty(this,"_url",{writable:!0})}async url(){return this._url||(this._url=this.blob().then(URL.createObjectURL))}async blob(){return this._.async("blob")}async arrayBuffer(){return this._.async("arraybuffer")}async text(){return this._.async("text")}async json(){return JSON.parse(await this.text())}},Wc={};Xn(Wc,{canvas:()=>fy,context2d:()=>py,download:()=>hy,element:()=>dy,input:()=>vy,range:()=>my,select:()=>gy,svg:()=>yy,text:()=>by,uid:()=>Hc});function fy(t,u){var a=document.createElement("canvas");return a.width=t,a.height=u,a}function py(t,u,a){a==null&&(a=devicePixelRatio);var o=document.createElement("canvas");o.width=t*a,o.height=u*a,o.style.width=t+"px";var l=o.getContext("2d");return l.scale(a,a),l}function hy(t,u="untitled",a="Save"){let o=document.createElement("a"),l=o.appendChild(document.createElement("button"));l.textContent=a,o.download=u;async function d(){await new Promise(requestAnimationFrame),URL.revokeObjectURL(o.href),o.removeAttribute("href"),l.textContent=a,l.disabled=!1}return o.onclick=async p=>{if(l.disabled=!0,o.href)return d();l.textContent="Saving\u2026";try{let g=await(typeof t=="function"?t():t);l.textContent="Download",o.href=URL.createObjectURL(g)}catch{l.textContent=a}if(p.eventPhase)return d();l.disabled=!1},o}var Kn={math:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function dy(t,u){var a=t+="",o=a.indexOf(":"),l;o>=0&&(a=t.slice(0,o))!=="xmlns"&&(t=t.slice(o+1));var d=Kn.hasOwnProperty(a)?document.createElementNS(Kn[a],t):document.createElement(t);if(u)for(var p in u)a=p,o=a.indexOf(":"),l=u[p],o>=0&&(a=p.slice(0,o))!=="xmlns"&&(p=p.slice(o+1)),Kn.hasOwnProperty(a)?d.setAttributeNS(Kn[a],p,l):d.setAttribute(p,l);return d}function vy(t){var u=document.createElement("input");return t!=null&&(u.type=t),u}function my(t,u,a){arguments.length===1&&(u=t,t=null);var o=document.createElement("input");return o.min=t=t==null?0:+t,o.max=u=u==null?1:+u,o.step=a==null?"any":a=+a,o.type="range",o}function gy(t){var u=document.createElement("select");return Array.prototype.forEach.call(t,function(a){var o=document.createElement("option");o.value=o.textContent=a,u.appendChild(o)}),u}function yy(t,u){var a=document.createElementNS("http://www.w3.org/2000/svg","svg");return a.setAttribute("viewBox",[0,0,t,u]),a.setAttribute("width",t),a.setAttribute("height",u),a}function by(t){return document.createTextNode(t)}var wy=0;function Hc(t){return new Vc("O-"+(t==null?"":t+"-")+ ++wy)}function Vc(t){this.id=t,this.href=new URL(`#${t}`,location)+""}Vc.prototype.toString=function(){return"url("+this.href+")"};var Zc={};Xn(Zc,{buffer:()=>_y,text:()=>xy,url:()=>Ay});function _y(t){return new Promise(function(u,a){var o=new FileReader;o.onload=function(){u(o.result)},o.onerror=a,o.readAsArrayBuffer(t)})}function xy(t){return new Promise(function(u,a){var o=new FileReader;o.onload=function(){u(o.result)},o.onerror=a,o.readAsText(t)})}function Ay(t){return new Promise(function(u,a){var o=new FileReader;o.onload=function(){u(o.result)},o.onerror=a,o.readAsDataURL(t)})}var Gc={};Xn(Gc,{disposable:()=>Yc,filter:()=>Ey,input:()=>ky,map:()=>Sy,observe:()=>ra,queue:()=>$y,range:()=>Cy,valueAt:()=>Ty,worker:()=>Ry});function ri(){return this}function Yc(t,u){let a=!1;if(typeof u!="function")throw new Error("dispose is not a function");return{[Symbol.iterator]:ri,next:()=>a?{done:!0}:(a=!0,{done:!1,value:t}),return:()=>(a=!0,u(t),{done:!0}),throw:()=>({done:a=!0})}}function*Ey(t,u){for(var a,o=-1;!(a=t.next()).done;)u(a.value,++o)&&(yield a.value)}function ra(t){let u=!1,a,o,l=t(d);if(l!=null&&typeof l!="function")throw new Error(typeof l.then=="function"?"async initializers are not supported":"initializer returned something, but not a dispose function");function d(g){return o?(o(g),o=null):u=!0,a=g}function p(){return{done:!1,value:u?(u=!1,Promise.resolve(a)):new Promise(g=>o=g)}}return{[Symbol.iterator]:ri,throw:()=>({done:!0}),return:()=>(l?.(),{done:!0}),next:p}}function ky(t){return ra(function(u){var a=jy(t),o=Cc(t);function l(){u(Cc(t))}return t.addEventListener(a,l),o!==void 0&&u(o),function(){t.removeEventListener(a,l)}})}function Cc(t){switch(t.type){case"range":case"number":return t.valueAsNumber;case"date":return t.valueAsDate;case"checkbox":return t.checked;case"file":return t.multiple?t.files:t.files[0];case"select-multiple":return Array.from(t.selectedOptions,u=>u.value);default:return t.value}}function jy(t){switch(t.type){case"button":case"submit":case"checkbox":return"click";case"file":return"change";default:return"input"}}function*Sy(t,u){for(var a,o=-1;!(a=t.next()).done;)yield u(a.value,++o)}function $y(t){let u,a=[],o=t(l);if(o!=null&&typeof o!="function")throw new Error(typeof o.then=="function"?"async initializers are not supported":"initializer returned something, but not a dispose function");function l(p){return a.push(p),u&&(u(a.shift()),u=null),p}function d(){return{done:!1,value:a.length?Promise.resolve(a.shift()):new Promise(p=>u=p)}}return{[Symbol.iterator]:ri,throw:()=>({done:!0}),return:()=>(o?.(),{done:!0}),next:d}}function*Cy(t,u,a){t=+t,u=+u,a=(l=arguments.length)<2?(u=t,t=0,1):l<3?1:+a;for(var o=-1,l=Math.max(0,Math.ceil((u-t)/a))|0;++o{a.terminate(),URL.revokeObjectURL(u)})}function ti(t,u){return function(a){var o=a[0],l=[],d,p=null,g,A,_,x,w,R,C,$=-1;for(x=1,w=arguments.length;x";else if(Array.isArray(d)){for(R=0,C=d.length;R"),p.appendChild(g)):(p=null,o+=g);p=null}else o+=d;o+=a[x]}if(p=t(o),++$>0){for(A=new Array($),_=document.createTreeWalker(p,NodeFilter.SHOW_COMMENT,null,!1);_.nextNode();)g=_.currentNode,/^o:/.test(g.nodeValue)&&(A[+g.nodeValue.slice(2)]=g);for(x=0;x<$;++x)(g=A[x])&&g.parentNode.replaceChild(l[x],g)}return p.childNodes.length===1?p.removeChild(p.firstChild):p.nodeType===11?((g=u()).appendChild(p),g):p}}var Ny=ti(function(t){var u=document.createElement("template");return u.innerHTML=t.trim(),document.importNode(u.content,!0)},function(){return document.createElement("span")});async function My(t){let u=await t(yc.resolve());if(!u._style){let a=document.createElement("link");a.rel="stylesheet",a.href=await t.resolve(yc.resolve("dist/leaflet.css")),u._style=document.head.appendChild(a)}return u}function Oy(t){return t(ng.resolve()).then(function(u){return ti(function(a){var o=document.createElement("div");o.innerHTML=u(a,{langPrefix:""}).trim();var l=o.querySelectorAll("pre code[class]");return l.length>0&&t(Uu.resolve()).then(function(d){l.forEach(function(p){function g(){d.highlightBlock(p),p.parentNode.classList.add("observablehq--md-pre")}d.getLanguage(p.className)?g():t(Uu.resolve("async-languages/index.js")).then(A=>{if(A.has(p.className))return t(Uu.resolve("async-languages/"+A.get(p.className))).then(_=>{d.registerLanguage(p.className,_)})}).then(g,g)})}),o},function(){return document.createElement("div")})})}async function Iy(t){let u=await t(fg.resolve());return u.initialize({securityLevel:"loose",theme:"neutral"}),function(){let a=document.createElement("div");return a.innerHTML=u.render(Hc().id,String.raw.apply(String,arguments)),a.removeChild(a.firstChild)}}function Ly(t){let u;Object.defineProperties(this,{generator:{value:ra(a=>void(u=a))},value:{get:()=>t,set:a=>u(t=a)}}),t!==void 0&&u(t)}function*Fy(){for(;;)yield Date.now()}var Kc={};Xn(Kc,{delay:()=>Py,tick:()=>qy,when:()=>Jc});function Py(t,u){return new Promise(function(a){setTimeout(function(){a(u)},t)})}var Zu=new Map;function Dy(t,u){var a=new Promise(function(o){Zu.delete(u);var l=u-t;if(!(l>0))throw new Error("invalid time");if(l>2147483647)throw new Error("too long to wait");setTimeout(o,l)});return Zu.set(u,a),a}function Jc(t,u){var a;return(a=Zu.get(t=+t))?a.then(()=>u):(a=Date.now())>=t?Promise.resolve(u):Dy(a,t).then(()=>u)}function qy(t,u){return Jc(Math.ceil((Date.now()+1)/t)*t,u)}function Uy(t,u){if(/^(\w+:)|\/\//i.test(t))return t;if(/^[.]{0,2}\//i.test(t))return new URL(t,u??location).href;if(!t.length||/^[\s._]/.test(t)||/\s$/.test(t))throw new Error("illegal name");return"https://unpkg.com/"+t}var By=ti(function(t){var u=document.createElementNS("http://www.w3.org/2000/svg","g");return u.innerHTML=t.trim(),u},function(){return document.createElementNS("http://www.w3.org/2000/svg","g")}),zy=String.raw;function Wy(t){return new Promise(function(u,a){var o=document.createElement("link");o.rel="stylesheet",o.href=t,o.onerror=a,o.onload=u,document.head.appendChild(o)})}function Hy(t){return Promise.all([t(mc.resolve()),t.resolve(mc.resolve("dist/katex.min.css")).then(Wy)]).then(function(u){var a=u[0],o=l();function l(d){return function(){var p=document.createElement("div");return a.render(zy.apply(String,arguments),p,d),p.removeChild(p.firstChild)}}return o.options=l,o.block=l({displayMode:!0}),o})}async function Vy(t){let[u,a,o]=await Promise.all([ag,ug,ig].map(l=>t(l.resolve())));return o.register(u,a)}function Zy(){return ra(function(t){var u=t(document.body.clientWidth);function a(){var o=document.body.clientWidth;o!==u&&t(u=o)}return window.addEventListener("resize",a),function(){window.removeEventListener("resize",a)}})}var Qc=Object.assign(Object.defineProperties(function(t){let u=hg(t);Object.defineProperties(this,Gy({FileAttachment:()=>sy,Mutable:()=>Ly,now:Fy,width:Zy,dot:()=>u(Xm.resolve()),htl:()=>u(rg.resolve()),html:()=>Ny,md:()=>Oy(u),svg:()=>By,tex:()=>Hy(u),_:()=>u(eg.resolve()),aq:()=>u.alias({"apache-arrow":Wu.resolve()})(sg.resolve()),Arrow:()=>u(Wu.resolve()),d3:()=>u(Km.resolve()),DuckDBClient:()=>Ju,Inputs:()=>u(Jm.resolve()).then(a=>({...a,file:a.fileOf(ei)})),L:()=>My(u),mermaid:()=>Iy(u),Plot:()=>u(Qm.resolve()),__query:()=>qc,require:()=>u,resolve:()=>Uy,SQLite:()=>Rc(u),SQLiteDatabaseClient:()=>Gu,topojson:()=>u(cg.resolve()),vl:()=>Vy(u),aapl:()=>new _e("https://static.observableusercontent.com/files/3ccff97fd2d93da734e76829b2b066eafdaac6a1fafdec0faf6ebc443271cfc109d29e80dd217468fcb2aff1e6bffdc73f356cc48feb657f35378e6abbbb63b9").csv({typed:!0}),alphabet:()=>new _e("https://static.observableusercontent.com/files/75d52e6c3130b1cae83cda89305e17b50f33e7420ef205587a135e8562bcfd22e483cf4fa2fb5df6dff66f9c5d19740be1cfaf47406286e2eb6574b49ffc685d").csv({typed:!0}),cars:()=>new _e("https://static.observableusercontent.com/files/048ec3dfd528110c0665dfa363dd28bc516ffb7247231f3ab25005036717f5c4c232a5efc7bb74bc03037155cb72b1abe85a33d86eb9f1a336196030443be4f6").csv({typed:!0}),citywages:()=>new _e("https://static.observableusercontent.com/files/39837ec5121fcc163131dbc2fe8c1a2e0b3423a5d1e96b5ce371e2ac2e20a290d78b71a4fb08b9fa6a0107776e17fb78af313b8ea70f4cc6648fad68ddf06f7a").csv({typed:!0}),diamonds:()=>new _e("https://static.observableusercontent.com/files/87942b1f5d061a21fa4bb8f2162db44e3ef0f7391301f867ab5ba718b225a63091af20675f0bfe7f922db097b217b377135203a7eab34651e21a8d09f4e37252").csv({typed:!0}),flare:()=>new _e("https://static.observableusercontent.com/files/a6b0d94a7f5828fd133765a934f4c9746d2010e2f342d335923991f31b14120de96b5cb4f160d509d8dc627f0107d7f5b5070d2516f01e4c862b5b4867533000").csv({typed:!0}),industries:()=>new _e("https://static.observableusercontent.com/files/76f13741128340cc88798c0a0b7fa5a2df8370f57554000774ab8ee9ae785ffa2903010cad670d4939af3e9c17e5e18e7e05ed2b38b848ac2fc1a0066aa0005f").csv({typed:!0}),miserables:()=>new _e("https://static.observableusercontent.com/files/31d904f6e21d42d4963ece9c8cc4fbd75efcbdc404bf511bc79906f0a1be68b5a01e935f65123670ed04e35ca8cae3c2b943f82bf8db49c5a67c85cbb58db052").json(),olympians:()=>new _e("https://static.observableusercontent.com/files/31ca24545a0603dce099d10ee89ee5ae72d29fa55e8fc7c9ffb5ded87ac83060d80f1d9e21f4ae8eb04c1e8940b7287d179fe8060d887fb1f055f430e210007c").csv({typed:!0}),penguins:()=>new _e("https://static.observableusercontent.com/files/715db1223e067f00500780077febc6cebbdd90c151d3d78317c802732252052ab0e367039872ab9c77d6ef99e5f55a0724b35ddc898a1c99cb14c31a379af80a").csv({typed:!0}),pizza:()=>new _e("https://static.observableusercontent.com/files/c653108ab176088cacbb338eaf2344c4f5781681702bd6afb55697a3f91b511c6686ff469f3e3a27c75400001a2334dbd39a4499fe46b50a8b3c278b7d2f7fb5").csv({typed:!0}),weather:()=>new _e("https://static.observableusercontent.com/files/693a46b22b33db0f042728700e0c73e836fa13d55446df89120682d55339c6db7cc9e574d3d73f24ecc9bc7eb9ac9a1e7e104a1ee52c00aab1e77eb102913c1f").csv({typed:!0}),DOM:Wc,Files:Zc,Generators:Gc,Promises:Kc}))},{resolve:{get:()=>Ir.resolve,enumerable:!0,configurable:!0},require:{get:()=>Ir,set:pg,enumerable:!0,configurable:!0}}),{resolveFrom:Fu,requireFrom:pt});function Gy(t){return Object.fromEntries(Object.entries(t).map(Yy))}function Yy([t,u]){return[t,{value:u,writable:!0,enumerable:!0}]}function Ky(t){if(t.sheet)return t.sheet;for(var u=0;u0?be(bt,--$e):0,gt--,de===10&&(gt=1,sa--),de}function Ie(){return de=$e2||yt(de)>3?"":" "}function ub(t,u){for(;--u&&Ie()&&!(de<48||de>102||de>57&&de<65||de>70&&de<97););return _t(t,na()+(u<6&&ar()==32&&Ie()==32))}function ni(t){for(;Ie();)switch(de){case t:return $e;case 34:case 39:t!==34&&t!==39&&ni(de);break;case 40:t===41&&ni(t);break;case 92:Ie();break}return $e}function ib(t,u){for(;Ie()&&t+de!==57&&!(t+de===84&&ar()===47););return"/*"+_t(u,$e-1)+"*"+Jt(t===47?t:Ie())}function ob(t){for(;!yt(ar());)Ie();return _t(t,$e)}function sl(t){return oi(aa("",null,null,null,[""],t=ii(t),0,[0],t))}function aa(t,u,a,o,l,d,p,g,A){for(var _=0,x=0,w=p,R=0,C=0,$=0,I=1,Z=1,G=1,ae=0,Q="",Ce=l,ie=d,le=o,U=Q;Z;)switch($=ae,ae=Ie()){case 40:if($!=108&&be(U,w-1)==58){oa(U+=Y(Gt(ae),"&","&\f"),"&\f")!=-1&&(G=-1);break}case 34:case 39:case 91:U+=Gt(ae);break;case 9:case 10:case 13:case 32:U+=ab($);break;case 92:U+=ub(na()-1,7);continue;case 47:switch(ar()){case 42:case 47:ta(sb(ib(Ie(),na()),u,a),A);break;default:U+="/"}break;case 123*I:g[_++]=nr(U)*G;case 125*I:case 59:case 0:switch(ae){case 0:case 125:Z=0;case 59+x:G==-1&&(U=Y(U,/\f/g,"")),C>0&&nr(U)-w&&ta(C>32?rl(U+";",o,a,w-1):rl(Y(U," ","")+";",o,a,w-2),A);break;case 59:U+=";";default:if(ta(le=el(U,u,a,_,x,l,g,Q,Ce=[],ie=[],w),d),ae===123)if(x===0)aa(U,u,le,le,Ce,d,w,g,ie);else switch(R===99&&be(U,3)===110?100:R){case 100:case 108:case 109:case 115:aa(t,le,le,o&&ta(el(t,le,le,0,0,l,g,Q,l,Ce=[],w),ie),l,ie,w,g,o?Ce:ie);break;default:aa(U,le,le,le,[""],ie,0,g,ie)}}_=x=C=0,I=G=1,Q=U="",w=p;break;case 58:w=1+nr(U),C=$;default:if(I<1){if(ae==123)--I;else if(ae==125&&I++==0&&nb()==125)continue}switch(U+=Jt(ae),ae*I){case 38:G=x>0?1:(U+="\f",-1);break;case 44:g[_++]=(nr(U)-1)*G,G=1;break;case 64:ar()===45&&(U+=Gt(Ie())),R=ar(),x=w=nr(Q=U+=ob(na())),ae++;break;case 45:$===45&&nr(U)==2&&(I=0)}}return d}function el(t,u,a,o,l,d,p,g,A,_,x){for(var w=l-1,R=l===0?d:[""],C=ui(R),$=0,I=0,Z=0;$0?R[G]+" "+ae:Y(ae,/&\f/g,R[G])))&&(A[Z++]=Q);return ca(t,u,a,l===0?ua:g,A,_,x)}function sb(t,u,a){return ca(t,u,a,tl,Jt(tb()),Yt(t,2,-2),0)}function rl(t,u,a,o){return ca(t,u,a,ia,Yt(t,0,o),Yt(t,o+1,-1),o)}function Yr(t,u){for(var a="",o=ui(t),l=0;l6)switch(be(t,u+1)){case 109:if(be(t,u+4)!==45)break;case 102:return Y(t,/(.+:)(.+)-([^]+)/,"$1"+K+"$2-$3$1"+Kt+(be(t,u+3)==108?"$3":"$2-$3"))+t;case 115:return~oa(t,"stretch")?hl(Y(t,"stretch","fill-available"),u)+t:t}break;case 4949:if(be(t,u+1)!==115)break;case 6444:switch(be(t,nr(t)-3-(~oa(t,"!important")&&10))){case 107:return Y(t,":",":"+K)+t;case 101:return Y(t,/(.+:)([^;!]+)(;|!.+)?/,"$1"+K+(be(t,14)===45?"inline-":"")+"box$3$1"+K+"$2$3$1"+xe+"$2box$3")+t}break;case 5936:switch(be(t,u+11)){case 114:return K+t+xe+Y(t,/[svh]\w+-[tblr]{2}/,"tb")+t;case 108:return K+t+xe+Y(t,/[svh]\w+-[tblr]{2}/,"tb-rl")+t;case 45:return K+t+xe+Y(t,/[svh]\w+-[tblr]{2}/,"lr")+t}return K+t+xe+t+t}return t}var db=function(t,u,a,o){if(t.length>-1&&!t.return)switch(t.type){case ia:t.return=hl(t.value,t.length);break;case ai:return Yr([wt(t,{value:Y(t.value,"@","@"+K)})],o);case ua:if(t.length)return il(t.props,function(l){switch(ul(l,/(::plac\w+|:read-\w+)/)){case":read-only":case":read-write":return Yr([wt(t,{props:[Y(l,/:(read-\w+)/,":"+Kt+"$1")]})],o);case"::placeholder":return Yr([wt(t,{props:[Y(l,/:(plac\w+)/,":"+K+"input-$1")]}),wt(t,{props:[Y(l,/:(plac\w+)/,":"+Kt+"$1")]}),wt(t,{props:[Y(l,/:(plac\w+)/,xe+"input-$1")]})],o)}return""})}},vb=fl(function(){return xt(function(){var t={};return function(u){return t[u]}})}),mb=[db],dl=function(t){var u=t.key,a=t.stylisPlugins||mb,o={},l,d=[],p,g=[pb,hb];{var A=[cl],_=ll(g.concat(a,A)),x=function($){return Yr(sl($),_)},w=vb(a)(u),R=function($,I){var Z=I.name;return w[Z]===void 0&&(w[Z]=x($?$+"{"+I.styles+"}":I.styles)),w[Z]};p=function($,I,Z,G){var ae=I.name,Q=R($,I);if(C.compat===void 0)return G&&(C.inserted[ae]=!0),Q;if(G)C.inserted[ae]=Q;else return Q}}var C={key:u,sheet:new Xc({key:u,container:l,nonce:t.nonce,speedy:t.speedy,prepend:t.prepend,insertionPoint:t.insertionPoint}),nonce:t.nonce,inserted:o,registered:{},insert:p};return C.sheet.hydrate(d),C};function si(t){for(var u=0,a,o=0,l=t.length;l>=4;++o,l-=4)a=t.charCodeAt(o)&255|(t.charCodeAt(++o)&255)<<8|(t.charCodeAt(++o)&255)<<16|(t.charCodeAt(++o)&255)<<24,a=(a&65535)*1540483477+((a>>>16)*59797<<16),a^=a>>>24,u=(a&65535)*1540483477+((a>>>16)*59797<<16)^(u&65535)*1540483477+((u>>>16)*59797<<16);switch(l){case 3:u^=(t.charCodeAt(o+2)&255)<<16;case 2:u^=(t.charCodeAt(o+1)&255)<<8;case 1:u^=t.charCodeAt(o)&255,u=(u&65535)*1540483477+((u>>>16)*59797<<16)}return u^=u>>>13,u=(u&65535)*1540483477+((u>>>16)*59797<<16),((u^u>>>15)>>>0).toString(36)}var ci={animationIterationCount:1,aspectRatio:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,msGridRow:1,msGridRowSpan:1,msGridColumn:1,msGridColumnSpan:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1};var gb=/[A-Z]|^ms/g,yb=/_EMO_([^_]+?)_([^]*?)_EMO_/g,yl=function(t){return t.charCodeAt(1)===45},vl=function(t){return t!=null&&typeof t!="boolean"},li=xt(function(t){return yl(t)?t:t.replace(gb,"-$&").toLowerCase()}),ml=function(t,u){switch(t){case"animation":case"animationName":if(typeof u=="string")return u.replace(yb,function(a,o,l){return cr={name:o,styles:l,next:cr},o})}return ci[t]!==1&&!yl(t)&&typeof u=="number"&&u!==0?u+"px":u};function Qt(t,u,a){if(a==null)return"";if(a.__emotion_styles!==void 0)return a;switch(typeof a){case"boolean":return"";case"object":{if(a.anim===1)return cr={name:a.name,styles:a.styles,next:cr},a.name;if(a.styles!==void 0){var o=a.next;if(o!==void 0)for(;o!==void 0;)cr={name:o.name,styles:o.styles,next:cr},o=o.next;var l=a.styles+";";return l}return bb(t,u,a)}case"function":{if(t!==void 0){var d=cr,p=a(t);return cr=d,Qt(t,u,p)}break}case"string":if(0)var g,A;break}if(u==null)return a;var _=u[a];return _!==void 0?_:a}function bb(t,u,a){var o="";if(Array.isArray(a))for(var l=0;l`;El(u);let a=u.appendChild(kl`
`);u.PLOT=a,u.controller=new AbortController,a.addEventListener("input",o=>{u.value=a.value,!o.bubbles&&u.dispatchEvent(new CustomEvent("input"))},{signal:u.controller.signal})}function __(t){let u=kb(),{PLOT:a}=u;a.layout_size={height:t.layout.height,width:t.layout.width};let o=firstRun,l=original_height??a.container_height??400;u.style.height=l+"px"}export{__ as createPlot}; +/*! Bundled license information: + +lodash/lodash.js: + (** + * @license + * Lodash + * Copyright OpenJS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + *) +*/ diff --git a/js/prehooks.js b/js/prehooks.js new file mode 100644 index 0000000..cc26657 --- /dev/null +++ b/js/prehooks.js @@ -0,0 +1,65 @@ +// This file contains utilities to be executed before calling the plot function. +import { default as _ } from "https://esm.sh/lodash@4.17.21"; +import { Library } from "https://esm.sh/@observablehq/stdlib@5.8.6"; +import { addContainerStyle } from "./styles.js"; + + +const library = new Library() +const html = library.html() + +// We start by putting all the variable interpolation here at the beginning +// We have to convert all typedarrays in the layout to normal arrays. See Issue #25 +// We use lodash for this for compactness +function removeTypedArray(o) { + return _.isTypedArray(o) + ? Array.from(o) + : _.isPlainObject(o) + ? _.mapValues(o, removeTypedArray) + : o; +} + +function makeContainer(caller_this) { + if (caller_this !== undefined) { + // If our input is already the container we just update its flag and return it + caller_this.firstRun = false; + return caller_this; + } + const CONTAINER = html`
`; + // Add the style to it + addContainerStyle(CONTAINER); + // Create the child div that will contain the actual plot + const PLOT = CONTAINER.appendChild( + html`
` + ); + CONTAINER.PLOT = PLOT; + // We use a controller to remove event listeners upon invalidation + CONTAINER.controller = new AbortController(); + // We have to add this to keep supporting @bind with the old API using PLOT + // TO REMOVE NOW WITH JS MIGRATION? WE ARE ANYHOW BREAKING + PLOT.addEventListener( + "input", + (e) => { + CONTAINER.value = PLOT.value; + if (e.bubbles) { + return; + } + CONTAINER.dispatchEvent(new CustomEvent("input")); + }, + { signal: CONTAINER.controller.signal } + ); +} + +export function createPlot(plot_obj) { + const CONTAINER = makeContainer(); + const { PLOT } = CONTAINER; + // Record or update the layout width/height if provided explicitly + PLOT.layout_size = { + height: plot_obj.layout.height, + width: plot_obj.layout.width, + } + // For the height we have to also put a fixed value in case the plot is put on a non-fixed-size container (like the default wrapper) + // We define a variable to check whether we still have to remove the fixed height + let remove_container_size = firstRun + let container_height = original_height ?? PLOT.container_height ?? 400 + CONTAINER.style.height = container_height + 'px' +} \ No newline at end of file diff --git a/js/styles.js b/js/styles.js new file mode 100644 index 0000000..5957b1b --- /dev/null +++ b/js/styles.js @@ -0,0 +1,141 @@ +import { css } from "https://esm.sh/@emotion/css@11.11.2"; + +export function addContainerStyle(CONTAINER) { + CONTAINER.classList.add(css` + .plutoplotly-container { + width: 100%; + height: 100%; + min-height: 0; + min-width: 0; + } + .plutoplotly-container .js-plotly-plot .plotly div { + margin: 0 auto; // This centers the plot + } + .plutoplotly-container.popped-out { + overflow: auto; + z-index: 1000; + position: fixed; + resize: both; + background: var(--main-bg-color); + border: 3px solid var(--kbd-border-color); + border-radius: 12px; + border-top-left-radius: 0px; + border-top-right-radius: 0px; + } + .plutoplotly-clipboard-header { + display: flex; + flex-flow: row wrap; + background: var(--main-bg-color); + border: 3px solid var(--kbd-border-color); + border-top-left-radius: 12px; + border-top-right-radius: 12px; + position: fixed; + z-index: 1001; + cursor: move; + transform: translate(0px, -100%); + padding: 5px; + } + .plutoplotly-clipboard-header span { + display: inline-block; + flex: 1; + } + .plutoplotly-clipboard-header.hidden { + display: none; + } + .clipboard-span { + position: relative; + } + .clipboard-value { + padding-right: 5px; + padding-left: 2px; + cursor: text; + } + .clipboard-span.format { + display: none; + } + .clipboard-span.filename { + flex: 0 0 100%; + text-align: center; + border-top: 3px solid var(--kbd-border-color); + margin-top: 5px; + display: none; + } + .plutoplotly-container.filesave .clipboard-span.filename { + display: inline-block; + } + .clipboard-value.filename { + margin-left: 3px; + text-align: left; + min-width: min(60%, min-content); + } + .plutoplotly-container.filesave .clipboard-span.format { + display: inline-flex; + } + .clipboard-span.format .label { + flex: 0 0 0; + } + .clipboard-value.format { + position: relative; + flex: 1 0 auto; + min-width: 30px; + margin-right: 10px; + } + div.format-options { + display: inline-flex; + flex-flow: column; + position: absolute; + background: var(--main-bg-color); + border-radius: 12px; + padding-left: 3px; + z-index: 2000; + } + div.format-options:hover { + cursor: pointer; + border: 3px solid var(--kbd-border-color); + padding: 3px; + transform: translate(-3px, -6px); + } + div.format-options .format-option { + display: none; + } + div.format-options:hover .format-option { + display: inline-block; + } + .format-option:not(.selected) { + margin-top: 3px; + } + div.format-options .format-option.selected { + order: -1; + display: inline-block; + } + .format-option:hover { + background-color: var(--kbd-border-color); + } + span.config-value { + font-weight: normal; + color: var(--pluto-output-color); + display: none; + position: absolute; + background: var(--main-bg-color); + border: 3px solid var(--kbd-border-color); + border-radius: 12px; + transform: translate(0px, calc(-100% - 10px)); + padding: 5px; + } + .label { + user-select: none; + } + .label:hover span.config-value { + display: inline-block; + min-width: 150px; + } + .clipboard-span.matching-config .label { + color: var(--cm-macro-color); + font-weight: bold; + } + .clipboard-span.different-config .label { + color: var(--cm-tag-color); + font-weight: bold; + } + `); +} \ No newline at end of file diff --git a/js/test.js b/js/test.js deleted file mode 100644 index 8b0650d..0000000 --- a/js/test.js +++ /dev/null @@ -1,12 +0,0 @@ -import _ from "lodash" - -// We start by putting all the variable interpolation here at the beginning -// We have to convert all typedarrays in the layout to normal arrays. See Issue #25 -// We use lodash for this for compactness -export function removeTypedArray(o) { - return _.isTypedArray(o) ? Array.from(o) : - _.isPlainObject(o) ? _.mapValues(o, removeTypedArray) : - o -} - -console.log(removeTypedArray([1, 2, 3])) \ No newline at end of file From ad8ca8bff320dac73804fdbe6758af0f6d25c095 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Fri, 22 Mar 2024 14:57:50 +0100 Subject: [PATCH 04/39] tests --- js/prehook.esm.js | 180 ---------------------------------------------- js/prehook.ts | 61 ++++++++++++++++ js/prehooks.js | 8 +-- 3 files changed, 63 insertions(+), 186 deletions(-) delete mode 100644 js/prehook.esm.js create mode 100644 js/prehook.ts diff --git a/js/prehook.esm.js b/js/prehook.esm.js deleted file mode 100644 index 9700475..0000000 --- a/js/prehook.esm.js +++ /dev/null @@ -1,180 +0,0 @@ -var Zn=globalThis||(typeof window<"u"?window:self),xm=Object.create,Ou=Object.defineProperty,Am=Object.getOwnPropertyDescriptor,Em=Object.getOwnPropertyNames,km=Object.getPrototypeOf,jm=Object.prototype.hasOwnProperty,Sm=(t,u)=>()=>(u||t((u={exports:{}}).exports,u),u.exports),$m=(t,u)=>{for(var a in u)Ou(t,a,{get:u[a],enumerable:!0})},Mu=(t,u,a,o)=>{if(u&&typeof u=="object"||typeof u=="function")for(let l of Em(u))!jm.call(t,l)&&l!==a&&Ou(t,l,{get:()=>u[l],enumerable:!(o=Am(u,l))||o.enumerable});return t},Cm=(t,u,a)=>(Mu(t,u,"default"),a&&Mu(a,u,"default")),Vs=(t,u,a)=>(a=t!=null?xm(km(t)):{},Mu(u||!t||!t.__esModule?Ou(a,"default",{value:t,enumerable:!0}):a,t)),Zs=Sm((t,u)=>{(function(){var a,o="4.17.21",l=200,d="Unsupported core-js use. Try https://npms.io/search?q=ponyfill.",p="Expected a function",g="Invalid `variable` option passed into `_.template`",A="__lodash_hash_undefined__",_=500,x="__lodash_placeholder__",w=1,R=2,C=4,$=1,I=2,Z=1,G=2,ae=4,Q=8,Ce=16,ie=32,le=64,U=128,Ke=256,fa=512,jl=30,Sl="...",$l=800,Cl=16,pi=1,Tl=2,Rl=3,Lr=1/0,xr=9007199254740991,Nl=17976931348623157e292,Xt=NaN,ur=4294967295,Ml=ur-1,Ol=ur>>>1,Il=[["ary",U],["bind",Z],["bindKey",G],["curry",Q],["curryRight",Ce],["flip",fa],["partial",ie],["partialRight",le],["rearg",Ke]],Kr="[object Arguments]",en="[object Array]",Ll="[object AsyncFunction]",At="[object Boolean]",Et="[object Date]",Fl="[object DOMException]",rn="[object Error]",tn="[object Function]",hi="[object GeneratorFunction]",Je="[object Map]",kt="[object Number]",Pl="[object Null]",fr="[object Object]",di="[object Promise]",Dl="[object Proxy]",jt="[object RegExp]",Qe="[object Set]",St="[object String]",nn="[object Symbol]",ql="[object Undefined]",$t="[object WeakMap]",Ul="[object WeakSet]",Ct="[object ArrayBuffer]",Jr="[object DataView]",pa="[object Float32Array]",ha="[object Float64Array]",da="[object Int8Array]",va="[object Int16Array]",ma="[object Int32Array]",ga="[object Uint8Array]",ya="[object Uint8ClampedArray]",ba="[object Uint16Array]",wa="[object Uint32Array]",Bl=/\b__p \+= '';/g,zl=/\b(__p \+=) '' \+/g,Wl=/(__e\(.*?\)|\b__t\)) \+\n'';/g,vi=/&(?:amp|lt|gt|quot|#39);/g,mi=/[&<>"']/g,Hl=RegExp(vi.source),Vl=RegExp(mi.source),Zl=/<%-([\s\S]+?)%>/g,Gl=/<%([\s\S]+?)%>/g,gi=/<%=([\s\S]+?)%>/g,Yl=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Kl=/^\w*$/,Jl=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,_a=/[\\^$.*+?()[\]{}|]/g,Ql=RegExp(_a.source),xa=/^\s+/,Xl=/\s/,ef=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,rf=/\{\n\/\* \[wrapped with (.+)\] \*/,tf=/,? & /,nf=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,af=/[()=,{}\[\]\/\s]/,uf=/\\(\\)?/g,of=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,yi=/\w*$/,sf=/^[-+]0x[0-9a-f]+$/i,cf=/^0b[01]+$/i,lf=/^\[object .+?Constructor\]$/,ff=/^0o[0-7]+$/i,pf=/^(?:0|[1-9]\d*)$/,hf=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,an=/($^)/,df=/['\n\r\u2028\u2029\\]/g,un="\\ud800-\\udfff",vf="\\u0300-\\u036f",mf="\\ufe20-\\ufe2f",gf="\\u20d0-\\u20ff",bi=vf+mf+gf,wi="\\u2700-\\u27bf",_i="a-z\\xdf-\\xf6\\xf8-\\xff",yf="\\xac\\xb1\\xd7\\xf7",bf="\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf",wf="\\u2000-\\u206f",_f=" \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",xi="A-Z\\xc0-\\xd6\\xd8-\\xde",Ai="\\ufe0e\\ufe0f",Ei=yf+bf+wf+_f,Aa="['\u2019]",xf="["+un+"]",ki="["+Ei+"]",on="["+bi+"]",ji="\\d+",Af="["+wi+"]",Si="["+_i+"]",$i="[^"+un+Ei+ji+wi+_i+xi+"]",Ea="\\ud83c[\\udffb-\\udfff]",Ef="(?:"+on+"|"+Ea+")",Ci="[^"+un+"]",ka="(?:\\ud83c[\\udde6-\\uddff]){2}",ja="[\\ud800-\\udbff][\\udc00-\\udfff]",Qr="["+xi+"]",Ti="\\u200d",Ri="(?:"+Si+"|"+$i+")",kf="(?:"+Qr+"|"+$i+")",Ni="(?:"+Aa+"(?:d|ll|m|re|s|t|ve))?",Mi="(?:"+Aa+"(?:D|LL|M|RE|S|T|VE))?",Oi=Ef+"?",Ii="["+Ai+"]?",jf="(?:"+Ti+"(?:"+[Ci,ka,ja].join("|")+")"+Ii+Oi+")*",Sf="\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",$f="\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])",Li=Ii+Oi+jf,Cf="(?:"+[Af,ka,ja].join("|")+")"+Li,Tf="(?:"+[Ci+on+"?",on,ka,ja,xf].join("|")+")",Rf=RegExp(Aa,"g"),Nf=RegExp(on,"g"),Sa=RegExp(Ea+"(?="+Ea+")|"+Tf+Li,"g"),Mf=RegExp([Qr+"?"+Si+"+"+Ni+"(?="+[ki,Qr,"$"].join("|")+")",kf+"+"+Mi+"(?="+[ki,Qr+Ri,"$"].join("|")+")",Qr+"?"+Ri+"+"+Ni,Qr+"+"+Mi,$f,Sf,ji,Cf].join("|"),"g"),Of=RegExp("["+Ti+un+bi+Ai+"]"),If=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,Lf=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Ff=-1,ne={};ne[pa]=ne[ha]=ne[da]=ne[va]=ne[ma]=ne[ga]=ne[ya]=ne[ba]=ne[wa]=!0,ne[Kr]=ne[en]=ne[Ct]=ne[At]=ne[Jr]=ne[Et]=ne[rn]=ne[tn]=ne[Je]=ne[kt]=ne[fr]=ne[jt]=ne[Qe]=ne[St]=ne[$t]=!1;var te={};te[Kr]=te[en]=te[Ct]=te[Jr]=te[At]=te[Et]=te[pa]=te[ha]=te[da]=te[va]=te[ma]=te[Je]=te[kt]=te[fr]=te[jt]=te[Qe]=te[St]=te[nn]=te[ga]=te[ya]=te[ba]=te[wa]=!0,te[rn]=te[tn]=te[$t]=!1;var Pf={\u00C0:"A",\u00C1:"A",\u00C2:"A",\u00C3:"A",\u00C4:"A",\u00C5:"A",\u00E0:"a",\u00E1:"a",\u00E2:"a",\u00E3:"a",\u00E4:"a",\u00E5:"a",\u00C7:"C",\u00E7:"c",\u00D0:"D",\u00F0:"d",\u00C8:"E",\u00C9:"E",\u00CA:"E",\u00CB:"E",\u00E8:"e",\u00E9:"e",\u00EA:"e",\u00EB:"e",\u00CC:"I",\u00CD:"I",\u00CE:"I",\u00CF:"I",\u00EC:"i",\u00ED:"i",\u00EE:"i",\u00EF:"i",\u00D1:"N",\u00F1:"n",\u00D2:"O",\u00D3:"O",\u00D4:"O",\u00D5:"O",\u00D6:"O",\u00D8:"O",\u00F2:"o",\u00F3:"o",\u00F4:"o",\u00F5:"o",\u00F6:"o",\u00F8:"o",\u00D9:"U",\u00DA:"U",\u00DB:"U",\u00DC:"U",\u00F9:"u",\u00FA:"u",\u00FB:"u",\u00FC:"u",\u00DD:"Y",\u00FD:"y",\u00FF:"y",\u00C6:"Ae",\u00E6:"ae",\u00DE:"Th",\u00FE:"th",\u00DF:"ss",\u0100:"A",\u0102:"A",\u0104:"A",\u0101:"a",\u0103:"a",\u0105:"a",\u0106:"C",\u0108:"C",\u010A:"C",\u010C:"C",\u0107:"c",\u0109:"c",\u010B:"c",\u010D:"c",\u010E:"D",\u0110:"D",\u010F:"d",\u0111:"d",\u0112:"E",\u0114:"E",\u0116:"E",\u0118:"E",\u011A:"E",\u0113:"e",\u0115:"e",\u0117:"e",\u0119:"e",\u011B:"e",\u011C:"G",\u011E:"G",\u0120:"G",\u0122:"G",\u011D:"g",\u011F:"g",\u0121:"g",\u0123:"g",\u0124:"H",\u0126:"H",\u0125:"h",\u0127:"h",\u0128:"I",\u012A:"I",\u012C:"I",\u012E:"I",\u0130:"I",\u0129:"i",\u012B:"i",\u012D:"i",\u012F:"i",\u0131:"i",\u0134:"J",\u0135:"j",\u0136:"K",\u0137:"k",\u0138:"k",\u0139:"L",\u013B:"L",\u013D:"L",\u013F:"L",\u0141:"L",\u013A:"l",\u013C:"l",\u013E:"l",\u0140:"l",\u0142:"l",\u0143:"N",\u0145:"N",\u0147:"N",\u014A:"N",\u0144:"n",\u0146:"n",\u0148:"n",\u014B:"n",\u014C:"O",\u014E:"O",\u0150:"O",\u014D:"o",\u014F:"o",\u0151:"o",\u0154:"R",\u0156:"R",\u0158:"R",\u0155:"r",\u0157:"r",\u0159:"r",\u015A:"S",\u015C:"S",\u015E:"S",\u0160:"S",\u015B:"s",\u015D:"s",\u015F:"s",\u0161:"s",\u0162:"T",\u0164:"T",\u0166:"T",\u0163:"t",\u0165:"t",\u0167:"t",\u0168:"U",\u016A:"U",\u016C:"U",\u016E:"U",\u0170:"U",\u0172:"U",\u0169:"u",\u016B:"u",\u016D:"u",\u016F:"u",\u0171:"u",\u0173:"u",\u0174:"W",\u0175:"w",\u0176:"Y",\u0177:"y",\u0178:"Y",\u0179:"Z",\u017B:"Z",\u017D:"Z",\u017A:"z",\u017C:"z",\u017E:"z",\u0132:"IJ",\u0133:"ij",\u0152:"Oe",\u0153:"oe",\u0149:"'n",\u017F:"s"},Df={"&":"&","<":"<",">":">",'"':""","'":"'"},qf={"&":"&","<":"<",">":">",""":'"',"'":"'"},Uf={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Bf=parseFloat,zf=parseInt,Fi=typeof Zn=="object"&&Zn&&Zn.Object===Object&&Zn,Wf=typeof self=="object"&&self&&self.Object===Object&&self,ge=Fi||Wf||Function("return this")(),$a=typeof t=="object"&&t&&!t.nodeType&&t,Fr=$a&&typeof u=="object"&&u&&!u.nodeType&&u,Pi=Fr&&Fr.exports===$a,Ca=Pi&&Fi.process,Be=function(){try{var m=Fr&&Fr.require&&Fr.require("util").types;return m||Ca&&Ca.binding&&Ca.binding("util")}catch{}}(),Di=Be&&Be.isArrayBuffer,qi=Be&&Be.isDate,Ui=Be&&Be.isMap,Bi=Be&&Be.isRegExp,zi=Be&&Be.isSet,Wi=Be&&Be.isTypedArray;function Le(m,E,b){switch(b.length){case 0:return m.call(E);case 1:return m.call(E,b[0]);case 2:return m.call(E,b[0],b[1]);case 3:return m.call(E,b[0],b[1],b[2])}return m.apply(E,b)}function Hf(m,E,b,N){for(var P=-1,J=m==null?0:m.length;++P-1}function Ta(m,E,b){for(var N=-1,P=m==null?0:m.length;++N-1;);return b}function Qi(m,E){for(var b=m.length;b--&&Xr(E,m[b],0)>-1;);return b}function ep(m,E){for(var b=m.length,N=0;b--;)m[b]===E&&++N;return N}var rp=Oa(Pf),tp=Oa(Df);function np(m){return"\\"+Uf[m]}function ap(m,E){return m==null?a:m[E]}function et(m){return Of.test(m)}function up(m){return If.test(m)}function ip(m){for(var E,b=[];!(E=m.next()).done;)b.push(E.value);return b}function Pa(m){var E=-1,b=Array(m.size);return m.forEach(function(N,P){b[++E]=[P,N]}),b}function Xi(m,E){return function(b){return m(E(b))}}function kr(m,E){for(var b=-1,N=m.length,P=0,J=[];++b-1}function Zp(e,r){var n=this.__data__,i=kn(n,e);return i<0?(++this.size,n.push([e,r])):n[i][1]=r,this}pr.prototype.clear=zp,pr.prototype.delete=Wp,pr.prototype.get=Hp,pr.prototype.has=Vp,pr.prototype.set=Zp;function hr(e){var r=-1,n=e==null?0:e.length;for(this.clear();++r=r?e:r)),e}function Ve(e,r,n,i,s,f){var h,v=r&w,y=r&R,k=r&C;if(n&&(h=s?n(e,i,s,f):n(e)),h!==a)return h;if(!oe(e))return e;var j=D(e);if(j){if(h=Jh(e),!v)return Te(e,h)}else{var S=Ee(e),T=S==tn||S==hi;if(Nr(e))return Lo(e,v);if(S==fr||S==Kr||T&&!s){if(h=y||T?{}:rs(e),!v)return y?qh(e,ch(h,e)):Dh(e,fo(h,e))}else{if(!te[S])return s?e:{};h=Qh(e,S,v)}}f||(f=new er);var M=f.get(e);if(M)return M;f.set(e,h),Ts(e)?e.forEach(function(F){h.add(Ve(F,r,n,F,e,f))}):$s(e)&&e.forEach(function(F,W){h.set(W,Ve(F,r,n,W,e,f))});var L=k?y?lu:cu:y?Ne:ye,B=j?a:L(e);return ze(B||e,function(F,W){B&&(W=F,F=e[W]),Lt(h,W,Ve(F,r,n,W,e,f))}),h}function lh(e){var r=ye(e);return function(n){return po(n,e,r)}}function po(e,r,n){var i=n.length;if(e==null)return!i;for(e=re(e);i--;){var s=n[i],f=r[s],h=e[s];if(h===a&&!(s in e)||!f(h))return!1}return!0}function ho(e,r,n){if(typeof e!="function")throw new We(p);return zt(function(){e.apply(a,n)},r)}function Ft(e,r,n,i){var s=-1,f=sn,h=!0,v=e.length,y=[],k=r.length;if(!v)return y;n&&(r=ue(r,Fe(n))),i?(f=Ta,h=!1):r.length>=l&&(f=Tt,h=!1,r=new qr(r));e:for(;++ss?0:s+n),i=i===a||i>s?s:q(i),i<0&&(i+=s),i=n>i?0:Ns(i);n0&&n(v)?r>1?we(v,r-1,n,i,s):Er(s,v):i||(s[s.length]=v)}return s}var Ha=Bo(),go=Bo(!0);function ir(e,r){return e&&Ha(e,r,ye)}function Va(e,r){return e&&go(e,r,ye)}function Sn(e,r){return Ar(r,function(n){return yr(e[n])})}function Br(e,r){r=Tr(r,e);for(var n=0,i=r.length;e!=null&&nr}function hh(e,r){return e!=null&&ee.call(e,r)}function dh(e,r){return e!=null&&r in re(e)}function vh(e,r,n){return e>=Ae(r,n)&&e=120&&j.length>=120)?new qr(h&&j):a}j=e[0];var S=-1,T=v[0];e:for(;++S-1;)v!==e&&yn.call(v,y,1),yn.call(e,y,1);return e}function $o(e,r){for(var n=e?r.length:0,i=n-1;n--;){var s=r[n];if(n==i||s!==f){var f=s;gr(s)?yn.call(e,s,1):tu(e,s)}}return e}function Xa(e,r){return e+_n(oo()*(r-e+1))}function $h(e,r,n,i){for(var s=-1,f=me(wn((r-e)/(n||1)),0),h=b(f);f--;)h[i?f:++s]=e,e+=n;return h}function eu(e,r){var n="";if(!e||r<1||r>xr)return n;do r%2&&(n+=e),r=_n(r/2),r&&(e+=e);while(r);return n}function z(e,r){return gu(as(e,r,Me),e+"")}function Ch(e){return lo(lt(e))}function Th(e,r){var n=lt(e);return Pn(n,Ur(r,0,n.length))}function qt(e,r,n,i){if(!oe(e))return e;r=Tr(r,e);for(var s=-1,f=r.length,h=f-1,v=e;v!=null&&++ss?0:s+r),n=n>s?s:n,n<0&&(n+=s),s=r>n?0:n-r>>>0,r>>>=0;for(var f=b(s);++i>>1,h=e[f];h!==null&&!De(h)&&(n?h<=r:h=l){var k=r?null:Wh(e);if(k)return ln(k);h=!1,s=Tt,y=new qr}else y=r?[]:v;e:for(;++i=i?e:Ze(e,r,n)}var Io=_p||function(e){return ge.clearTimeout(e)};function Lo(e,r){if(r)return e.slice();var n=e.length,i=to?to(n):new e.constructor(n);return e.copy(i),i}function iu(e){var r=new e.constructor(e.byteLength);return new mn(r).set(new mn(e)),r}function Ih(e,r){var n=r?iu(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}function Lh(e){var r=new e.constructor(e.source,yi.exec(e));return r.lastIndex=e.lastIndex,r}function Fh(e){return It?re(It.call(e)):{}}function Fo(e,r){var n=r?iu(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function Po(e,r){if(e!==r){var n=e!==a,i=e===null,s=e===e,f=De(e),h=r!==a,v=r===null,y=r===r,k=De(r);if(!v&&!k&&!f&&e>r||f&&h&&y&&!v&&!k||i&&h&&y||!n&&y||!s)return 1;if(!i&&!f&&!k&&e=v)return y;var k=n[i];return y*(k=="desc"?-1:1)}}return e.index-r.index}function Do(e,r,n,i){for(var s=-1,f=e.length,h=n.length,v=-1,y=r.length,k=me(f-h,0),j=b(y+k),S=!i;++v1?n[s-1]:a,h=s>2?n[2]:a;for(f=e.length>3&&typeof f=="function"?(s--,f):a,h&&je(n[0],n[1],h)&&(f=s<3?a:f,s=1),r=re(r);++i-1?s[f?r[h]:h]:a}}function Ho(e){return mr(function(r){var n=r.length,i=n,s=He.prototype.thru;for(e&&r.reverse();i--;){var f=r[i];if(typeof f!="function")throw new We(p);if(s&&!h&&Ln(f)=="wrapper")var h=new He([],!0)}for(i=h?i:n;++i1&&V.reverse(),j&&yv))return!1;var k=f.get(e),j=f.get(r);if(k&&j)return k==r&&j==e;var S=-1,T=!0,M=n&I?new qr:a;for(f.set(e,r),f.set(r,e);++S1?"& ":"")+r[i],r=r.join(n>2?", ":" "),e.replace(ef,`{ -/* [wrapped with `+r+`] */ -`)}function e0(e){return D(e)||Hr(e)||!!(uo&&e&&e[uo])}function gr(e,r){var n=typeof e;return r=r??xr,!!r&&(n=="number"||n!="symbol"&&pf.test(e))&&e>-1&&e%1==0&&e0){if(++r>=$l)return arguments[0]}else r=0;return e.apply(a,arguments)}}function Pn(e,r){var n=-1,i=e.length,s=i-1;for(r=r===a?i:r;++n1?e[r-1]:a;return n=typeof n=="function"?(e.pop(),n):a,ms(e,n)});function gs(e){var r=c(e);return r.__chain__=!0,r}function fd(e,r){return r(e),e}function Dn(e,r){return r(e)}var pd=mr(function(e){var r=e.length,n=r?e[0]:0,i=this.__wrapped__,s=function(f){return Wa(f,e)};return r>1||this.__actions__.length||!(i instanceof H)||!gr(n)?this.thru(s):(i=i.slice(n,+n+(r?1:0)),i.__actions__.push({func:Dn,args:[s],thisArg:a}),new He(i,this.__chain__).thru(function(f){return r&&!f.length&&f.push(a),f}))});function hd(){return gs(this)}function dd(){return new He(this.value(),this.__chain__)}function vd(){this.__values__===a&&(this.__values__=Rs(this.value()));var e=this.__index__>=this.__values__.length,r=e?a:this.__values__[this.__index__++];return{done:e,value:r}}function md(){return this}function gd(e){for(var r,n=this;n instanceof En;){var i=ls(n);i.__index__=0,i.__values__=a,r?s.__wrapped__=i:r=i;var s=i;n=n.__wrapped__}return s.__wrapped__=e,r}function yd(){var e=this.__wrapped__;if(e instanceof H){var r=e;return this.__actions__.length&&(r=new H(this)),r=r.reverse(),r.__actions__.push({func:Dn,args:[yu],thisArg:a}),new He(r,this.__chain__)}return this.thru(yu)}function bd(){return Mo(this.__wrapped__,this.__actions__)}var wd=Rn(function(e,r,n){ee.call(e,n)?++e[n]:dr(e,n,1)});function _d(e,r,n){var i=D(e)?Hi:fh;return n&&je(e,r,n)&&(r=a),i(e,O(r,3))}function xd(e,r){var n=D(e)?Ar:mo;return n(e,O(r,3))}var Ad=Wo(fs),Ed=Wo(ps);function kd(e,r){return we(qn(e,r),1)}function jd(e,r){return we(qn(e,r),Lr)}function Sd(e,r,n){return n=n===a?1:q(n),we(qn(e,r),n)}function ys(e,r){var n=D(e)?ze:$r;return n(e,O(r,3))}function bs(e,r){var n=D(e)?Vf:vo;return n(e,O(r,3))}var $d=Rn(function(e,r,n){ee.call(e,n)?e[n].push(r):dr(e,n,[r])});function Cd(e,r,n,i){e=Re(e)?e:lt(e),n=n&&!i?q(n):0;var s=e.length;return n<0&&(n=me(s+n,0)),Hn(e)?n<=s&&e.indexOf(r,n)>-1:!!s&&Xr(e,r,n)>-1}var Td=z(function(e,r,n){var i=-1,s=typeof r=="function",f=Re(e)?b(e.length):[];return $r(e,function(h){f[++i]=s?Le(r,h,n):Pt(h,r,n)}),f}),Rd=Rn(function(e,r,n){dr(e,n,r)});function qn(e,r){var n=D(e)?ue:xo;return n(e,O(r,3))}function Nd(e,r,n,i){return e==null?[]:(D(r)||(r=r==null?[]:[r]),n=i?a:n,D(n)||(n=n==null?[]:[n]),jo(e,r,n))}var Md=Rn(function(e,r,n){e[n?0:1].push(r)},function(){return[[],[]]});function Od(e,r,n){var i=D(e)?Ra:Yi,s=arguments.length<3;return i(e,O(r,4),n,s,$r)}function Id(e,r,n){var i=D(e)?Zf:Yi,s=arguments.length<3;return i(e,O(r,4),n,s,vo)}function Ld(e,r){var n=D(e)?Ar:mo;return n(e,zn(O(r,3)))}function Fd(e){var r=D(e)?lo:Ch;return r(e)}function Pd(e,r,n){(n?je(e,r,n):r===a)?r=1:r=q(r);var i=D(e)?ih:Th;return i(e,r)}function Dd(e){var r=D(e)?oh:Nh;return r(e)}function qd(e){if(e==null)return 0;if(Re(e))return Hn(e)?rt(e):e.length;var r=Ee(e);return r==Je||r==Qe?e.size:Ka(e).length}function Ud(e,r,n){var i=D(e)?Na:Mh;return n&&je(e,r,n)&&(r=a),i(e,O(r,3))}var Bd=z(function(e,r){if(e==null)return[];var n=r.length;return n>1&&je(e,r[0],r[1])?r=[]:n>2&&je(r[0],r[1],r[2])&&(r=[r[0]]),jo(e,we(r,1),[])}),Un=xp||function(){return ge.Date.now()};function zd(e,r){if(typeof r!="function")throw new We(p);return e=q(e),function(){if(--e<1)return r.apply(this,arguments)}}function ws(e,r,n){return r=n?a:r,r=e&&r==null?e.length:r,vr(e,U,a,a,a,a,r)}function _s(e,r){var n;if(typeof r!="function")throw new We(p);return e=q(e),function(){return--e>0&&(n=r.apply(this,arguments)),e<=1&&(r=a),n}}var wu=z(function(e,r,n){var i=Z;if(n.length){var s=kr(n,st(wu));i|=ie}return vr(e,i,r,n,s)}),xs=z(function(e,r,n){var i=Z|G;if(n.length){var s=kr(n,st(xs));i|=ie}return vr(r,i,e,n,s)});function As(e,r,n){r=n?a:r;var i=vr(e,Q,a,a,a,a,a,r);return i.placeholder=As.placeholder,i}function Es(e,r,n){r=n?a:r;var i=vr(e,Ce,a,a,a,a,a,r);return i.placeholder=Es.placeholder,i}function ks(e,r,n){var i,s,f,h,v,y,k=0,j=!1,S=!1,T=!0;if(typeof e!="function")throw new We(p);r=Ye(r)||0,oe(n)&&(j=!!n.leading,S="maxWait"in n,f=S?me(Ye(n.maxWait)||0,r):f,T="trailing"in n?!!n.trailing:T);function M(he){var tr=i,wr=s;return i=s=a,k=he,h=e.apply(wr,tr),h}function L(he){return k=he,v=zt(W,r),j?M(he):h}function B(he){var tr=he-y,wr=he-k,Ws=r-tr;return S?Ae(Ws,f-wr):Ws}function F(he){var tr=he-y,wr=he-k;return y===a||tr>=r||tr<0||S&&wr>=f}function W(){var he=Un();if(F(he))return V(he);v=zt(W,B(he))}function V(he){return v=a,T&&i?M(he):(i=s=a,h)}function qe(){v!==a&&Io(v),k=0,i=y=s=v=a}function Se(){return v===a?h:V(Un())}function Ue(){var he=Un(),tr=F(he);if(i=arguments,s=this,y=he,tr){if(v===a)return L(y);if(S)return Io(v),v=zt(W,r),M(y)}return v===a&&(v=zt(W,r)),h}return Ue.cancel=qe,Ue.flush=Se,Ue}var Wd=z(function(e,r){return ho(e,1,r)}),Hd=z(function(e,r,n){return ho(e,Ye(r)||0,n)});function Vd(e){return vr(e,fa)}function Bn(e,r){if(typeof e!="function"||r!=null&&typeof r!="function")throw new We(p);var n=function(){var i=arguments,s=r?r.apply(this,i):i[0],f=n.cache;if(f.has(s))return f.get(s);var h=e.apply(this,i);return n.cache=f.set(s,h)||f,h};return n.cache=new(Bn.Cache||hr),n}Bn.Cache=hr;function zn(e){if(typeof e!="function")throw new We(p);return function(){var r=arguments;switch(r.length){case 0:return!e.call(this);case 1:return!e.call(this,r[0]);case 2:return!e.call(this,r[0],r[1]);case 3:return!e.call(this,r[0],r[1],r[2])}return!e.apply(this,r)}}function Zd(e){return _s(2,e)}var Gd=Oh(function(e,r){r=r.length==1&&D(r[0])?ue(r[0],Fe(O())):ue(we(r,1),Fe(O()));var n=r.length;return z(function(i){for(var s=-1,f=Ae(i.length,n);++s=r}),Hr=bo(function(){return arguments}())?bo:function(e){return fe(e)&&ee.call(e,"callee")&&!ao.call(e,"callee")},D=b.isArray,cv=Di?Fe(Di):gh;function Re(e){return e!=null&&Wn(e.length)&&!yr(e)}function pe(e){return fe(e)&&Re(e)}function lv(e){return e===!0||e===!1||fe(e)&&ke(e)==At}var Nr=Ep||Nu,fv=qi?Fe(qi):yh;function pv(e){return fe(e)&&e.nodeType===1&&!Wt(e)}function hv(e){if(e==null)return!0;if(Re(e)&&(D(e)||typeof e=="string"||typeof e.splice=="function"||Nr(e)||ct(e)||Hr(e)))return!e.length;var r=Ee(e);if(r==Je||r==Qe)return!e.size;if(Bt(e))return!Ka(e).length;for(var n in e)if(ee.call(e,n))return!1;return!0}function dv(e,r){return Dt(e,r)}function vv(e,r,n){n=typeof n=="function"?n:a;var i=n?n(e,r):a;return i===a?Dt(e,r,a,n):!!i}function xu(e){if(!fe(e))return!1;var r=ke(e);return r==rn||r==Fl||typeof e.message=="string"&&typeof e.name=="string"&&!Wt(e)}function mv(e){return typeof e=="number"&&io(e)}function yr(e){if(!oe(e))return!1;var r=ke(e);return r==tn||r==hi||r==Ll||r==Dl}function Ss(e){return typeof e=="number"&&e==q(e)}function Wn(e){return typeof e=="number"&&e>-1&&e%1==0&&e<=xr}function oe(e){var r=typeof e;return e!=null&&(r=="object"||r=="function")}function fe(e){return e!=null&&typeof e=="object"}var $s=Ui?Fe(Ui):wh;function gv(e,r){return e===r||Ya(e,r,pu(r))}function yv(e,r,n){return n=typeof n=="function"?n:a,Ya(e,r,pu(r),n)}function bv(e){return Cs(e)&&e!=+e}function wv(e){if(n0(e))throw new P(d);return wo(e)}function _v(e){return e===null}function xv(e){return e==null}function Cs(e){return typeof e=="number"||fe(e)&&ke(e)==kt}function Wt(e){if(!fe(e)||ke(e)!=fr)return!1;var r=gn(e);if(r===null)return!0;var n=ee.call(r,"constructor")&&r.constructor;return typeof n=="function"&&n instanceof n&&hn.call(n)==yp}var Au=Bi?Fe(Bi):_h;function Av(e){return Ss(e)&&e>=-xr&&e<=xr}var Ts=zi?Fe(zi):xh;function Hn(e){return typeof e=="string"||!D(e)&&fe(e)&&ke(e)==St}function De(e){return typeof e=="symbol"||fe(e)&&ke(e)==nn}var ct=Wi?Fe(Wi):Ah;function Ev(e){return e===a}function kv(e){return fe(e)&&Ee(e)==$t}function jv(e){return fe(e)&&ke(e)==Ul}var Sv=In(Ja),$v=In(function(e,r){return e<=r});function Rs(e){if(!e)return[];if(Re(e))return Hn(e)?Xe(e):Te(e);if(Rt&&e[Rt])return ip(e[Rt]());var r=Ee(e),n=r==Je?Pa:r==Qe?ln:lt;return n(e)}function br(e){if(!e)return e===0?e:0;if(e=Ye(e),e===Lr||e===-Lr){var r=e<0?-1:1;return r*Nl}return e===e?e:0}function q(e){var r=br(e),n=r%1;return r===r?n?r-n:r:0}function Ns(e){return e?Ur(q(e),0,ur):0}function Ye(e){if(typeof e=="number")return e;if(De(e))return Xt;if(oe(e)){var r=typeof e.valueOf=="function"?e.valueOf():e;e=oe(r)?r+"":r}if(typeof e!="string")return e===0?e:+e;e=Ki(e);var n=cf.test(e);return n||ff.test(e)?zf(e.slice(2),n?2:8):sf.test(e)?Xt:+e}function Ms(e){return or(e,Ne(e))}function Cv(e){return e?Ur(q(e),-xr,xr):e===0?e:0}function X(e){return e==null?"":Pe(e)}var Tv=it(function(e,r){if(Bt(r)||Re(r)){or(r,ye(r),e);return}for(var n in r)ee.call(r,n)&&Lt(e,n,r[n])}),Os=it(function(e,r){or(r,Ne(r),e)}),Vn=it(function(e,r,n,i){or(r,Ne(r),e,i)}),Rv=it(function(e,r,n,i){or(r,ye(r),e,i)}),Nv=mr(Wa);function Mv(e,r){var n=ut(e);return r==null?n:fo(n,r)}var Ov=z(function(e,r){e=re(e);var n=-1,i=r.length,s=i>2?r[2]:a;for(s&&je(r[0],r[1],s)&&(i=1);++n1),f}),or(e,lu(e),n),i&&(n=Ve(n,w|R|C,Hh));for(var s=r.length;s--;)tu(n,r[s]);return n});function Qv(e,r){return Ls(e,zn(O(r)))}var Xv=mr(function(e,r){return e==null?{}:jh(e,r)});function Ls(e,r){if(e==null)return{};var n=ue(lu(e),function(i){return[i]});return r=O(r),So(e,n,function(i,s){return r(i,s[0])})}function e1(e,r,n){r=Tr(r,e);var i=-1,s=r.length;for(s||(s=1,e=a);++ir){var i=e;e=r,r=i}if(n||e%1||r%1){var s=oo();return Ae(e+s*(r-e+Bf("1e-"+((s+"").length-1))),r)}return Xa(e,r)}var f1=ot(function(e,r,n){return r=r.toLowerCase(),e+(n?Ds(r):r)});function Ds(e){return ju(X(e).toLowerCase())}function qs(e){return e=X(e),e&&e.replace(hf,rp).replace(Nf,"")}function p1(e,r,n){e=X(e),r=Pe(r);var i=e.length;n=n===a?i:Ur(q(n),0,i);var s=n;return n-=r.length,n>=0&&e.slice(n,s)==r}function h1(e){return e=X(e),e&&Vl.test(e)?e.replace(mi,tp):e}function d1(e){return e=X(e),e&&Ql.test(e)?e.replace(_a,"\\$&"):e}var v1=ot(function(e,r,n){return e+(n?"-":"")+r.toLowerCase()}),m1=ot(function(e,r,n){return e+(n?" ":"")+r.toLowerCase()}),g1=zo("toLowerCase");function y1(e,r,n){e=X(e),r=q(r);var i=r?rt(e):0;if(!r||i>=r)return e;var s=(r-i)/2;return On(_n(s),n)+e+On(wn(s),n)}function b1(e,r,n){e=X(e),r=q(r);var i=r?rt(e):0;return r&&i>>0,n?(e=X(e),e&&(typeof r=="string"||r!=null&&!Au(r))&&(r=Pe(r),!r&&et(e))?Rr(Xe(e),0,n):e.split(r,n)):[]}var j1=ot(function(e,r,n){return e+(n?" ":"")+ju(r)});function S1(e,r,n){return e=X(e),n=n==null?0:Ur(q(n),0,e.length),r=Pe(r),e.slice(n,n+r.length)==r}function $1(e,r,n){var i=c.templateSettings;n&&je(e,r,n)&&(r=a),e=X(e),r=Vn({},r,i,Ko);var s=Vn({},r.imports,i.imports,Ko),f=ye(s),h=Fa(s,f),v,y,k=0,j=r.interpolate||an,S="__p += '",T=Da((r.escape||an).source+"|"+j.source+"|"+(j===gi?of:an).source+"|"+(r.evaluate||an).source+"|$","g"),M="//# sourceURL="+(ee.call(r,"sourceURL")?(r.sourceURL+"").replace(/\s/g," "):"lodash.templateSources["+ ++Ff+"]")+` -`;e.replace(T,function(F,W,V,qe,Se,Ue){return V||(V=qe),S+=e.slice(k,Ue).replace(df,np),W&&(v=!0,S+=`' + -__e(`+W+`) + -'`),Se&&(y=!0,S+=`'; -`+Se+`; -__p += '`),V&&(S+=`' + -((__t = (`+V+`)) == null ? '' : __t) + -'`),k=Ue+F.length,F}),S+=`'; -`;var L=ee.call(r,"variable")&&r.variable;if(!L)S=`with (obj) { -`+S+` -} -`;else if(af.test(L))throw new P(g);S=(y?S.replace(Bl,""):S).replace(zl,"$1").replace(Wl,"$1;"),S="function("+(L||"obj")+`) { -`+(L?"":`obj || (obj = {}); -`)+"var __t, __p = ''"+(v?", __e = _.escape":"")+(y?`, __j = Array.prototype.join; -function print() { __p += __j.call(arguments, '') } -`:`; -`)+S+`return __p -}`;var B=Bs(function(){return J(f,M+"return "+S).apply(a,h)});if(B.source=S,xu(B))throw B;return B}function C1(e){return X(e).toLowerCase()}function T1(e){return X(e).toUpperCase()}function R1(e,r,n){if(e=X(e),e&&(n||r===a))return Ki(e);if(!e||!(r=Pe(r)))return e;var i=Xe(e),s=Xe(r),f=Ji(i,s),h=Qi(i,s)+1;return Rr(i,f,h).join("")}function N1(e,r,n){if(e=X(e),e&&(n||r===a))return e.slice(0,eo(e)+1);if(!e||!(r=Pe(r)))return e;var i=Xe(e),s=Qi(i,Xe(r))+1;return Rr(i,0,s).join("")}function M1(e,r,n){if(e=X(e),e&&(n||r===a))return e.replace(xa,"");if(!e||!(r=Pe(r)))return e;var i=Xe(e),s=Ji(i,Xe(r));return Rr(i,s).join("")}function O1(e,r){var n=jl,i=Sl;if(oe(r)){var s="separator"in r?r.separator:s;n="length"in r?q(r.length):n,i="omission"in r?Pe(r.omission):i}e=X(e);var f=e.length;if(et(e)){var h=Xe(e);f=h.length}if(n>=f)return e;var v=n-rt(i);if(v<1)return i;var y=h?Rr(h,0,v).join(""):e.slice(0,v);if(s===a)return y+i;if(h&&(v+=y.length-v),Au(s)){if(e.slice(v).search(s)){var k,j=y;for(s.global||(s=Da(s.source,X(yi.exec(s))+"g")),s.lastIndex=0;k=s.exec(j);)var S=k.index;y=y.slice(0,S===a?v:S)}}else if(e.indexOf(Pe(s),v)!=v){var T=y.lastIndexOf(s);T>-1&&(y=y.slice(0,T))}return y+i}function I1(e){return e=X(e),e&&Hl.test(e)?e.replace(vi,lp):e}var L1=ot(function(e,r,n){return e+(n?" ":"")+r.toUpperCase()}),ju=zo("toUpperCase");function Us(e,r,n){return e=X(e),r=n?a:r,r===a?up(e)?hp(e):Kf(e):e.match(r)||[]}var Bs=z(function(e,r){try{return Le(e,a,r)}catch(n){return xu(n)?n:new P(n)}}),F1=mr(function(e,r){return ze(r,function(n){n=sr(n),dr(e,n,wu(e[n],e))}),e});function P1(e){var r=e==null?0:e.length,n=O();return e=r?ue(e,function(i){if(typeof i[1]!="function")throw new We(p);return[n(i[0]),i[1]]}):[],z(function(i){for(var s=-1;++sxr)return[];var n=ur,i=Ae(e,ur);r=O(r),e-=ur;for(var s=La(i,r);++n0||r<0)?new H(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),r!==a&&(r=q(r),n=r<0?n.dropRight(-r):n.take(r-e)),n)},H.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},H.prototype.toArray=function(){return this.take(ur)},ir(H.prototype,function(e,r){var n=/^(?:filter|find|map|reject)|While$/.test(r),i=/^(?:head|last)$/.test(r),s=c[i?"take"+(r=="last"?"Right":""):r],f=i||/^find/.test(r);s&&(c.prototype[r]=function(){var h=this.__wrapped__,v=i?[1]:arguments,y=h instanceof H,k=v[0],j=y||D(h),S=function(W){var V=s.apply(c,Er([W],v));return i&&T?V[0]:V};j&&n&&typeof k=="function"&&k.length!=1&&(y=j=!1);var T=this.__chain__,M=!!this.__actions__.length,L=f&&!T,B=y&&!M;if(!f&&j){h=B?h:new H(this);var F=e.apply(h,v);return F.__actions__.push({func:Dn,args:[S],thisArg:a}),new He(F,T)}return L&&B?e.apply(this,v):(F=this.thru(S),L?i?F.value()[0]:F.value():F)})}),ze(["pop","push","shift","sort","splice","unshift"],function(e){var r=fn[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",i=/^(?:pop|shift)$/.test(e);c.prototype[e]=function(){var s=arguments;if(i&&!this.__chain__){var f=this.value();return r.apply(D(f)?f:[],s)}return this[n](function(h){return r.apply(D(h)?h:[],s)})}}),ir(H.prototype,function(e,r){var n=c[r];if(n){var i=n.name+"";ee.call(at,i)||(at[i]=[]),at[i].push({name:r,func:n})}}),at[Nn(a,G).name]=[{name:"wrapper",func:a}],H.prototype.clone=Ip,H.prototype.reverse=Lp,H.prototype.value=Fp,c.prototype.at=pd,c.prototype.chain=hd,c.prototype.commit=dd,c.prototype.next=vd,c.prototype.plant=gd,c.prototype.reverse=yd,c.prototype.toJSON=c.prototype.valueOf=c.prototype.value=bd,c.prototype.first=c.prototype.head,Rt&&(c.prototype[Rt]=md),c},jr=dp();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(ge._=jr,define(function(){return jr})):Fr?((Fr.exports=jr)._=jr,$a._=jr):ge._=jr}).call(t)}),Gs={};$m(Gs,{default:()=>Iu});var Tm=Vs(Zs());Cm(Gs,Vs(Zs()));var{default:Hs,...Rm}=Tm,Iu=Hs!==void 0?Hs:Rm;var Gn=new Map,Ht=[],Lu=Ht.map,Nm=Ht.some,Mm=Ht.hasOwnProperty,Om=/^((?:@[^/@]+\/)?[^/@]+)(?:@([^/]+))?(?:\/(.*))?$/,Im=/^\d+\.\d+\.\d+(-[\w-.+]+)?$/,Ys=/(?:\.[^/]*|\/)$/,ft=class extends Error{constructor(t){super(t)}};ft.prototype.name=ft.name;function Ks(t){let u=Om.exec(t);return u&&{name:u[1],version:u[2],path:u[3]}}function Fu(t="https://cdn.jsdelivr.net/npm/",u=["unpkg","jsdelivr","browser","main"]){if(!/\/$/.test(t))throw new Error("origin lacks trailing slash");function a(l){for(let d of u){let p=l[d];if(typeof p=="string")return p.startsWith("./")&&(p=p.slice(2)),Ys.test(p)?p:`${p}.js`}}function o(l){let d=`${t}${l.name}${l.version?`@${l.version}`:""}/package.json`,p=Gn.get(d);return p||Gn.set(d,p=fetch(d).then(g=>{if(!g.ok)throw new ft("unable to load package.json");return g.redirected&&!Gn.has(g.url)&&Gn.set(g.url,p),g.json()})),p}return async function(l,d){if(l.startsWith(t)&&(l=l.substring(t.length)),/^(\w+:)|\/\//i.test(l))return l;if(/^[.]{0,2}\//i.test(l))return new URL(l,d??location).href;if(!l.length||/^[\s._]/.test(l)||/\s$/.test(l))throw new ft("illegal name");let p=Ks(l);if(!p)return`${t}${l}`;if(!p.version&&d!=null&&d.startsWith(t)){let A=await o(Ks(d.substring(t.length)));p.version=A.dependencies&&A.dependencies[p.name]||A.peerDependencies&&A.peerDependencies[p.name]}if(p.path&&!Ys.test(p.path)&&(p.path+=".js"),p.path&&p.version&&Im.test(p.version))return`${t}${p.name}@${p.version}/${p.path}`;let g=await o(p);return`${t}${g.name}@${g.version}/${p.path||a(g)||"index.js"}`}}var Js=pt(Fu());function pt(t){let u=new Map,a=l(null);function o(g){if(typeof g!="string")return g;let A=u.get(g);return A||u.set(g,A=new Promise((_,x)=>{let w=document.createElement("script");w.onload=()=>{try{_(Ht.pop()(l(g)))}catch{x(new ft("invalid module"))}w.remove()},w.onerror=()=>{x(new ft("unable to load module")),w.remove()},w.async=!0,w.src=g,window.define=Qs,document.head.appendChild(w)})),A}function l(g){return A=>Promise.resolve(t(A,g)).then(o)}function d(g){return pt((A,_)=>A in g&&(A=g[A],_=null,typeof A!="string")?A:t(A,_))}function p(g){return arguments.length>1?Promise.all(Lu.call(arguments,a)).then(Lm):a(g)}return p.alias=d,p.resolve=t,p}function Lm(t){let u={};for(let a of t)for(let o in a)Mm.call(a,o)&&(a[o]==null?Object.defineProperty(u,o,{get:Fm(a,o)}):u[o]=a[o]);return u}function Fm(t,u){return()=>t[u]}function Pm(t){return t=t+"",t==="exports"||t==="module"}function Qs(t,u,a){let o=arguments.length;o<2?(a=t,u=[]):o<3&&(a=u,u=typeof t=="string"?[]:t),Ht.push(Nm.call(u,Pm)?l=>{let d={},p={exports:d};return Promise.all(Lu.call(u,g=>(g=g+"",g==="exports"?d:g==="module"?p:l(g)))).then(g=>(a.apply(null,g),p.exports))}:l=>Promise.all(Lu.call(u,l)).then(d=>typeof a=="function"?a.apply(null,d):a))}Qs.amd={};var Xs={},Pu={},Du=34,Vt=10,qu=13;function rc(t){return new Function("d","return {"+t.map(function(u,a){return JSON.stringify(u)+": d["+a+'] || ""'}).join(",")+"}")}function Dm(t,u){var a=rc(t);return function(o,l){return u(a(o),l,t)}}function ec(t){var u=Object.create(null),a=[];return t.forEach(function(o){for(var l in o)l in u||a.push(u[l]=l)}),a}function Oe(t,u){var a=t+"",o=a.length;return o9999?"+"+Oe(t,6):Oe(t,4)}function Um(t){var u=t.getUTCHours(),a=t.getUTCMinutes(),o=t.getUTCSeconds(),l=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":qm(t.getUTCFullYear(),4)+"-"+Oe(t.getUTCMonth()+1,2)+"-"+Oe(t.getUTCDate(),2)+(l?"T"+Oe(u,2)+":"+Oe(a,2)+":"+Oe(o,2)+"."+Oe(l,3)+"Z":o?"T"+Oe(u,2)+":"+Oe(a,2)+":"+Oe(o,2)+"Z":a||u?"T"+Oe(u,2)+":"+Oe(a,2)+"Z":"")}function tc(t){var u=new RegExp('["'+t+` -\r]`),a=t.charCodeAt(0);function o(w,R){var C,$,I=l(w,function(Z,G){if(C)return C(Z,G-1);$=Z,C=R?Dm(Z,R):rc(Z)});return I.columns=$||[],I}function l(w,R){var C=[],$=w.length,I=0,Z=0,G,ae=$<=0,Q=!1;w.charCodeAt($-1)===Vt&&--$,w.charCodeAt($-1)===qu&&--$;function Ce(){if(ae)return Pu;if(Q)return Q=!1,Xs;var le,U=I,Ke;if(w.charCodeAt(U)===Du){for(;I++<$&&w.charCodeAt(I)!==Du||w.charCodeAt(++I)===Du;);return(le=I)>=$?ae=!0:(Ke=w.charCodeAt(I++))===Vt?Q=!0:Ke===qu&&(Q=!0,w.charCodeAt(I)===Vt&&++I),w.slice(U+1,le-1).replace(/""/g,'"')}for(;I<$;){if((Ke=w.charCodeAt(le=I++))===Vt)Q=!0;else if(Ke===qu)Q=!0,w.charCodeAt(I)===Vt&&++I;else if(Ke!==a)continue;return w.slice(U,le)}return ae=!0,w.slice(U,$)}for(;(G=Ce())!==Pu;){for(var ie=[];G!==Xs&&G!==Pu;)ie.push(G),G=Ce();R&&(ie=R(ie,Z++))==null||C.push(ie)}return C}function d(w,R){return w.map(function(C){return R.map(function($){return x(C[$])}).join(t)})}function p(w,R){return R==null&&(R=ec(w)),[R.map(x).join(t)].concat(d(w,R)).join(` -`)}function g(w,R){return R==null&&(R=ec(w)),d(w,R).join(` -`)}function A(w){return w.map(_).join(` -`)}function _(w){return w.map(x).join(t)}function x(w){return w==null?"":w instanceof Date?Um(w):u.test(w+="")?'"'+w.replace(/"/g,'""')+'"':w}return{parse:o,parseRows:l,format:p,formatBody:g,formatRows:A,formatRow:_,formatValue:x}}var Vr=tc(","),nc=Vr.parse,ac=Vr.parseRows,Nb=Vr.format,Mb=Vr.formatBody,Ob=Vr.formatRows,Ib=Vr.formatRow,Lb=Vr.formatValue,Zr=tc(" "),uc=Zr.parse,ic=Zr.parseRows,Fb=Zr.format,Pb=Zr.formatBody,Db=Zr.formatRows,qb=Zr.formatRow,Ub=Zr.formatValue;function oc(t){for(var u in t){var a=t[u].trim(),o,l;if(!a)a=null;else if(a==="true")a=!0;else if(a==="false")a=!1;else if(a==="NaN")a=NaN;else if(!isNaN(o=+a))a=o;else if(l=a.match(/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/))Bm&&l[4]&&!l[7]&&(a=a.replace(/-/g,"/").replace(/T/," ")),a=new Date(a);else continue;t[u]=a}return t}var Bm=new Date("2019-01-01T00:00").getHours()||new Date("2019-07-01T00:00").getHours();function Gr(t,u){return t==null||u==null?NaN:tu?1:t>=u?0:NaN}function zm(t,u){return t==null||u==null?NaN:ut?1:u>=t?0:NaN}function cc(t){let u,a,o;t.length!==2?(u=Gr,a=(g,A)=>Gr(t(g),A),o=(g,A)=>t(g)-A):(u=t===Gr||t===zm?t:Wm,a=t,o=t);function l(g,A,_=0,x=g.length){if(_>>1;a(g[w],A)<0?_=w+1:x=w}while(_>>1;a(g[w],A)<=0?_=w+1:x=w}while(__&&o(g[w-1],A)>-o(g[w],A)?w-1:w}return{left:l,center:p,right:d}}function Wm(){return 0}function Hm(t){return t===null?NaN:+t}var lc=cc(Gr),Wb=lc.right,Hb=lc.left,Vb=cc(Hm).center;var Zb=fc(pc),Gb=fc(Vm);function fc(t){return function(u,a,o=a){if(!((a=+a)>=0))throw new RangeError("invalid rx");if(!((o=+o)>=0))throw new RangeError("invalid ry");let{data:l,width:d,height:p}=u;if(!((d=Math.floor(d))>=0))throw new RangeError("invalid width");if(!((p=Math.floor(p!==void 0?p:l.length/d))>=0))throw new RangeError("invalid height");if(!d||!p||!a&&!o)return u;let g=a&&t(a),A=o&&t(o),_=l.slice();return g&&A?(ht(g,_,l,d,p),ht(g,l,_,d,p),ht(g,_,l,d,p),dt(A,l,_,d,p),dt(A,_,l,d,p),dt(A,l,_,d,p)):g?(ht(g,l,_,d,p),ht(g,_,l,d,p),ht(g,l,_,d,p)):A&&(dt(A,l,_,d,p),dt(A,_,l,d,p),dt(A,l,_,d,p)),u}}function ht(t,u,a,o,l){for(let d=0,p=o*l;d{l<<=2,d<<=2,p<<=2,u(a,o,l+0,d+0,p),u(a,o,l+1,d+1,p),u(a,o,l+2,d+2,p),u(a,o,l+3,d+3,p)}}function pc(t){let u=Math.floor(t);if(u===t)return Zm(t);let a=t-u,o=2*t+1;return(l,d,p,g,A)=>{if(!((g-=A)>=p))return;let _=u*d[p],x=A*u,w=x+A;for(let R=p,C=p+x;R{if(!((d-=p)>=l))return;let g=t*o[l],A=p*t;for(let _=l,x=l+A;_0:Gr(p,p)===0)&&(a=d,l=p,o=!0)}}else for(let l of t)(o?u(l,a)>0:u(l,l)===0)&&(a=l,o=!0);return a}var tw=Gm(Math.random);function Gm(t){return function(u,a=0,o=u.length){let l=o-(a=+a);for(;l;){let d=t()*l--|0,p=u[l+a];u[l+a]=u[d+a],u[d+a]=p}return u}}function vc(t){if(typeof t[Symbol.iterator]!="function")throw new TypeError("values is not iterable");return Array.from(t).reverse()}var Ym=Object.defineProperty,Xn=(t,u)=>{for(var a in u)Ym(t,a,{get:u[a],enumerable:!0})};function ce(t,u,a){return{resolve(o=a){return`${t}@${u}/${o}`}}}var Km=ce("d3","7.8.5","dist/d3.min.js"),Jm=ce("@observablehq/inputs","0.10.6","dist/inputs.min.js"),Qm=ce("@observablehq/plot","0.6.13","dist/plot.umd.min.js"),Xm=ce("@observablehq/graphviz","0.2.1","dist/graphviz.min.js"),Uu=ce("@observablehq/highlight.js","2.0.0","highlight.min.js"),mc=ce("@observablehq/katex","0.11.1","dist/katex.min.js"),eg=ce("lodash","4.17.21","lodash.min.js"),rg=ce("htl","0.3.1","dist/htl.min.js"),tg=ce("jszip","3.10.1","dist/jszip.min.js"),ng=ce("marked","0.3.12","marked.min.js"),gc=ce("sql.js","1.8.0","dist/sql-wasm.js"),ag=ce("vega","5.22.1","build/vega.min.js"),ug=ce("vega-lite","5.6.0","build/vega-lite.min.js"),ig=ce("vega-lite-api","5.0.0","build/vega-lite-api.min.js"),Wu=ce("apache-arrow","4.0.1","Arrow.es2015.min.js"),og=ce("apache-arrow","9.0.0","+esm"),Tc=ce("apache-arrow","11.0.0","+esm"),sg=ce("arquero","4.8.8","dist/arquero.min.js"),cg=ce("topojson-client","3.1.0","dist/topojson-client.min.js"),lg=ce("exceljs","4.3.0","dist/exceljs.min.js"),fg=ce("mermaid","9.2.2","dist/mermaid.min.js"),yc=ce("leaflet","1.9.3","dist/leaflet.js"),Zt=ce("@duckdb/duckdb-wasm","1.24.0","+esm"),Or="https://cdn.observableusercontent.com/npm/",Ir=Js;function pg(t){Ir=t}function hg(t){return t==null?Ir:pt(t)}async function Rc(t){let[u,a]=await Promise.all([t(gc.resolve()),t.resolve(gc.resolve("dist/"))]);return u({locateFile:o=>`${a}${o}`})}var Gu=class Nc{constructor(u){Object.defineProperties(this,{_db:{value:u}})}static async open(u){let[a,o]=await Promise.all([Rc(Ir),Promise.resolve(u).then(Hu)]);return new Nc(new a.Database(o))}async query(u,a){return await vg(this._db,u,a)}async queryRow(u,a){return(await this.query(u,a))[0]||null}async explain(u,a){let o=await this.query(`EXPLAIN QUERY PLAN ${u}`,a);return Mr("pre",{className:"observablehq--inspect"},[Bu(o.map(l=>l.detail).join(` -`))])}async describeTables({schema:u}={}){return this.query(`SELECT NULLIF(schema, 'main') AS schema, name FROM pragma_table_list() WHERE type = 'table'${u==null?"":" AND schema = ?"} AND name NOT LIKE 'sqlite_%' ORDER BY schema, name`,u==null?[]:[u])}async describeColumns({schema:u,table:a}={}){if(a==null)throw new Error("missing table");let o=await this.query(`SELECT name, type, "notnull" FROM pragma_table_info(?${u==null?"":", ?"}) ORDER BY cid`,u==null?[a]:[a,u]);if(!o.length)throw new Error(`table not found: ${a}`);return o.map(({name:l,type:d,notnull:p})=>({name:l,type:dg(d),databaseType:d,nullable:!p}))}async describe(u){let a=await(u===void 0?this.query("SELECT name FROM sqlite_master WHERE type = 'table'"):this.query("SELECT * FROM pragma_table_info(?)",[u]));if(!a.length)throw new Error("Not found");let{columns:o}=a;return Mr("table",{value:a},[Mr("thead",[Mr("tr",o.map(l=>Mr("th",[Bu(l)])))]),Mr("tbody",a.map(l=>Mr("tr",o.map(d=>Mr("td",[Bu(l[d])])))))])}async sql(){return this.query(...this.queryTag.apply(this,arguments))}queryTag(u,...a){return[u.join("?"),a]}};Object.defineProperty(Gu.prototype,"dialect",{value:"sqlite"});function dg(t){switch(t){case"NULL":return"null";case"INT":case"INTEGER":case"TINYINT":case"SMALLINT":case"MEDIUMINT":case"BIGINT":case"UNSIGNED BIG INT":case"INT2":case"INT8":return"integer";case"TEXT":case"CLOB":return"string";case"REAL":case"DOUBLE":case"DOUBLE PRECISION":case"FLOAT":case"NUMERIC":return"number";case"BLOB":return"buffer";case"DATE":case"DATETIME":return"string";default:return/^(?:(?:(?:VARYING|NATIVE) )?CHARACTER|(?:N|VAR|NVAR)CHAR)\(/.test(t)?"string":/^(?:DECIMAL|NUMERIC)\(/.test(t)?"number":"other"}}function Hu(t){return typeof t=="string"?fetch(t).then(Hu):t instanceof Response||t instanceof Blob?t.arrayBuffer().then(Hu):t instanceof ArrayBuffer?new Uint8Array(t):t}async function vg(t,u,a){let[o]=await t.exec(u,a);if(!o)return[];let{columns:l,values:d}=o,p=d.map(g=>Object.fromEntries(g.map((A,_)=>[l[_],A])));return p.columns=l,p}function Mr(t,u,a){arguments.length===2&&(a=u,u=void 0);let o=document.createElement(t);if(u!==void 0)for(let l in u)o[l]=u[l];if(a!==void 0)for(let l of a)o.appendChild(l);return o}function Bu(t){return document.createTextNode(t)}function Yu(t){return t&&typeof t.toArrowBuffer=="function"}function Jn(t){return t&&typeof t.getChild=="function"&&typeof t.toArray=="function"&&t.schema&&Array.isArray(t.schema.fields)}function mg(t){return t.schema.fields.map(gg)}function gg(t){return{name:t.name,type:yg(t.type),nullable:t.nullable,databaseType:String(t.type)}}function yg(t){switch(t.typeId){case 2:return"integer";case 3:case 7:return"number";case 4:case 15:return"buffer";case 5:return"string";case 6:return"boolean";case 8:case 9:case 10:return"date";case 12:case 16:return"array";case 13:case 14:return"object";case 11:case 17:default:return"other"}}async function Ku(){return await import(`${Or}${Tc.resolve()}`)}var zu,Ju=class Mc{constructor(u){Object.defineProperties(this,{_db:{value:u}})}async queryStream(u,a){let o=await this._db.connect(),l,d;try{if(a?.length>0?l=await(await o.prepare(u)).send(...a):l=await o.send(u),d=await l.next(),d.done)throw new Error("missing first batch")}catch(p){throw await o.close(),p}return{schema:mg(d.value),async*readRows(){try{for(;!d.done;)yield d.value.toArray(),d=await l.next()}finally{await o.close()}}}}async query(u,a){let o=await this.queryStream(u,a),l=[];for await(let d of o.readRows())for(let p of d)l.push(p);return l.schema=o.schema,l}async queryRow(u,a){let o=(await this.queryStream(u,a)).readRows();try{let{done:l,value:d}=await o.next();return l||!d.length?null:d[0]}finally{await o.return()}}async sql(u,...a){return await this.query(u.join("?"),a)}queryTag(u,...a){return[u.join("?"),a]}escape(u){return`"${u}"`}async describeTables(){return(await this.query("SHOW TABLES")).map(({name:u})=>({name:u}))}async describeColumns({table:u}={}){return(await this.query(`DESCRIBE ${this.escape(u)}`)).map(({column_name:a,column_type:o,null:l})=>({name:a,type:Ag(o),nullable:l!=="NO",databaseType:o}))}static async of(u={},a={}){let o=await xg();return a.query?.castTimestampToDate===void 0&&(a={...a,query:{...a.query,castTimestampToDate:!0}}),a.query?.castBigIntToDouble===void 0&&(a={...a,query:{...a.query,castBigIntToDouble:!0}}),await o.open(a),await Promise.all(Object.entries(u).map(async([l,d])=>{if(d instanceof _e)await bc(o,l,d);else if(Jn(d))await Qn(o,l,d);else if(Array.isArray(d))await wc(o,l,d);else if(Yu(d))await wg(o,l,d);else if("data"in d){let{data:p,...g}=d;Jn(p)?await Qn(o,l,p,g):await wc(o,l,p,g)}else if("file"in d){let{file:p,...g}=d;await bc(o,l,p,g)}else throw new Error(`invalid source: ${d}`)})),new Mc(o)}};Object.defineProperty(Ju.prototype,"dialect",{value:"duckdb"});async function bc(t,u,a,o){let l=await a.url();if(l.startsWith("blob:")){let p=await a.arrayBuffer();await t.registerFileBuffer(a.name,new Uint8Array(p))}else await t.registerFileURL(a.name,new URL(l,location).href,4);let d=await t.connect();try{switch(a.mimeType){case"text/csv":case"text/tab-separated-values":return await d.insertCSVFromPath(a.name,{name:u,schema:"main",...o}).catch(async p=>{if(p.toString().includes("Could not convert"))return await bg(d,a,u);throw p});case"application/json":return await d.insertJSONFromPath(a.name,{name:u,schema:"main",...o});default:if(/\.arrow$/i.test(a.name)){let p=new Uint8Array(await a.arrayBuffer());return await d.insertArrowFromIPCStream(p,{name:u,schema:"main",...o})}if(/\.parquet$/i.test(a.name))return await d.query(`CREATE VIEW '${u}' AS SELECT * FROM parquet_scan('${a.name}')`);throw new Error(`unknown file type: ${a.mimeType}`)}}finally{await d.close()}}async function bg(t,u,a){return await(await t.prepare(`CREATE TABLE '${a}' AS SELECT * FROM read_csv_auto(?, ALL_VARCHAR=TRUE)`)).send(u.name)}async function Qn(t,u,a,o){let l=await t.connect();try{await l.insertArrowTable(a,{name:u,schema:"main",...o})}finally{await l.close()}}async function wg(t,u,a){let o=(await Ku()).tableFromIPC(a.toArrowBuffer());return await Qn(t,u,o)}async function wc(t,u,a,o){let l=(await Ku()).tableFromJSON(a);return await Qn(t,u,l,o)}async function _g(){let t=await import(`${Or}${Zt.resolve()}`),u=await t.selectBundle({mvp:{mainModule:`${Or}${Zt.resolve("dist/duckdb-mvp.wasm")}`,mainWorker:`${Or}${Zt.resolve("dist/duckdb-browser-mvp.worker.js")}`},eh:{mainModule:`${Or}${Zt.resolve("dist/duckdb-eh.wasm")}`,mainWorker:`${Or}${Zt.resolve("dist/duckdb-browser-eh.worker.js")}`}}),a=new t.ConsoleLogger;return{module:t,bundle:u,logger:a}}async function xg(){zu===void 0&&(zu=_g());let{module:t,bundle:u,logger:a}=await zu,o=await t.createWorker(u.mainWorker),l=new t.AsyncDuckDB(a,o);return await l.instantiate(u.mainModule),l}function Ag(t){switch(t){case"BIGINT":case"HUGEINT":case"UBIGINT":return"bigint";case"DOUBLE":case"REAL":case"FLOAT":return"number";case"INTEGER":case"SMALLINT":case"TINYINT":case"USMALLINT":case"UINTEGER":case"UTINYINT":return"integer";case"BOOLEAN":return"boolean";case"DATE":case"TIMESTAMP":case"TIMESTAMP WITH TIME ZONE":return"date";case"VARCHAR":case"UUID":return"string";default:return/^DECIMAL\(/.test(t)?"integer":"other"}}var Qu=20;function Eg(t,u){return t&&(typeof t.sql=="function"||typeof t.queryTag=="function"&&(typeof t.query=="function"||typeof t.queryStream=="function"))&&(u!=="table"||typeof t.describeColumns=="function")&&t!==qc}function ea(t){return Array.isArray(t)&&(Oc(t.schema)||Ic(t.columns)||kg(t)||Fc(t)||Pc(t))||Dc(t)}function kg(t){let u=Math.min(Qu,t.length);for(let a=0;a0&&jg(t[0])}function jg(t){for(let u in t)return!0;return!1}function Oc(t){return Array.isArray(t)&&t.every(Sg)}function Ic(t){return Array.isArray(t)&&t.every(u=>typeof u=="string")}function Sg(t){return t&&typeof t.name=="string"&&typeof t.type=="string"}function Lc(t){return Dc(t)||Fc(t)||Pc(t)}function Fc(t){let u=Math.min(Qu,t.length);if(!(u>0))return!1;let a,o=!1;for(let l=0;l0))return!1;let a=!1;for(let o=0;o{if(t=await $g(await t,o),Eg(t))return _c(t,Mg(u,t),a);if(ea(t))return Qg(t,u);throw t?new Error("invalid data source"):new Error("missing data source")},{sql(t,u,a){return async function(){return _c(await Cg(await t,a),arguments,u)}}});function Xu(t){let u=new WeakMap;return(a,o)=>{if(!a||typeof a!="object")throw new Error("invalid data source");let l=u.get(a);return(!l||ea(a)&&a.length!==l._numRows)&&(l=t(a,o),l._numRows=a.length,u.set(a,l)),l}}var pw=Xu(async t=>{if(t instanceof _e){switch(t.mimeType){case"text/csv":return t.csv({typed:"auto"});case"text/tab-separated-values":return t.tsv({typed:"auto"});case"application/json":return t.json()}throw new Error(`unsupported file type: ${t.mimeType}`)}return t}),$g=Xu(async(t,u)=>{if(t instanceof _e){switch(t.mimeType){case"text/csv":return t.csv();case"text/tab-separated-values":return t.tsv();case"application/json":return t.json();case"application/x-sqlite3":return t.sqlite()}if(/\.(arrow|parquet)$/i.test(t.name))return mt(t,u);throw new Error(`unsupported file type: ${t.mimeType}`)}return Jn(t)||Yu(t)?mt(t,u):ea(t)&&Lc(t)?Array.from(t,a=>({value:a})):t}),Cg=Xu(async(t,u)=>{if(t instanceof _e){switch(t.mimeType){case"text/csv":case"text/tab-separated-values":case"application/json":return mt(t,u);case"application/x-sqlite3":return t.sqlite()}if(/\.(arrow|parquet)$/i.test(t.name))return mt(t,u);throw new Error(`unsupported file type: ${t.mimeType}`)}return ea(t)?mt(await Tg(t,u),u):Jn(t)||Yu(t)?mt(t,u):t});async function Tg(t,u){let a=await Ku();return Lc(t)?a.tableFromArrays({[u]:t}):a.tableFromJSON(t)}function mt(t,u=t instanceof _e?Rg(t):"__table"){return Ju.of({[u]:t})}function Rg(t){return t.name.replace(/@\d+(?=\.|$)/,"").replace(/\.\w+$/,"")}async function _c(t,u,a){if(!t)throw new Error("missing data source");if(typeof t.queryTag=="function"){let o=new AbortController,l={signal:o.signal};if(a.then(()=>o.abort("invalidated")),typeof t.queryStream=="function")return Ng(t.queryStream(...t.queryTag.apply(t,u),l));if(typeof t.query=="function")return t.query(...t.queryTag.apply(t,u),l)}if(typeof t.sql=="function")return t.sql.apply(t,u);throw new Error("source does not implement query, queryStream, or sql")}async function*Ng(t){let u=performance.now(),a=await t,o=[];o.done=!1,o.error=null,o.schema=a.schema;try{for await(let l of a.readRows()){performance.now()-u>150&&o.length>0&&(yield o,u=performance.now());for(let d of l)o.push(d)}o.done=!0,yield o}catch(l){o.error=l,yield o}}function Mg(t,u){let a=typeof u.escape=="function"?u.escape:x=>x,{select:o,from:l,filter:d,sort:p,slice:g}=t;if(!l.table)throw new Error("missing from table");if(o.columns&&o.columns.length===0)throw new Error("at least one column must be selected");let A=new Map(t.names?.map(({column:x,name:w})=>[x,w])),_=[[`SELECT ${o.columns?o.columns.map(x=>{let w=A.get(x);return w?`${a(x)} AS ${a(w)}`:a(x)}).join(", "):"*"} FROM ${Og(l.table,a)}`]];for(let x=0;x ",a);break;case"gt":se(" > ",a);break;case"lt":se(" < ",a);break;case"gte":se(" >= ",a);break;case"lte":se(" <= ",a);break;default:throw new Error("Invalid filter operation")}vt(u[1],a,o);return}switch(vt(u[0],a,o),t){case"in":se(" IN (",a);break;case"nin":se(" NOT IN (",a);break;default:throw new Error("Invalid filter operation")}Lg(u.slice(1),a),se(")",a)}function vt(t,u,a){t.type==="column"?se(a(t.value),u):(u.push(t.value),u[0].push(""))}function Lg(t,u){let a=!0;for(let o of t)a?a=!1:se(",",u),u.push(o.value),u[0].push("")}function Fg(t){return{...t,value:`%${t.value}%`}}function Uc(t,u){return(t==null||!(t>=t))-(u==null||!(u>=u))}function Pg(t,u){return Uc(t,u)||(tu?1:0)}function Dg(t,u){return Uc(t,u)||(t>u?-1:ttypeof t=="number"&&!Number.isNaN(t),Ug=t=>Number.isInteger(t)&&!Number.isNaN(t),Bg=t=>typeof t=="string",zg=t=>typeof t=="boolean",Wg=t=>typeof t=="bigint",Hg=t=>t instanceof Date&&!isNaN(t),Vg=t=>t instanceof ArrayBuffer,Zg=t=>Array.isArray(t),Gg=t=>typeof t=="object"&&t!==null,Yg=t=>t!=null;function Ac(t){switch(t){case"string":return Bg;case"bigint":return Wg;case"boolean":return zg;case"number":return qg;case"integer":return Ug;case"date":return Hg;case"buffer":return Vg;case"array":return Zg;case"object":return Gg;case"other":default:return Yg}}var Bc=/^(([-+]\d{2})?\d{4}(-\d{2}(-\d{2}))|(\d{1,2})\/(\d{1,2})\/(\d{2,4}))([T ]\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/;function Kg(t,u){switch(u){case"string":return typeof t=="string"||t==null?t:String(t);case"boolean":if(typeof t=="string"){let a=t.trim().toLowerCase();return a==="true"?!0:a==="false"?!1:null}return typeof t=="boolean"||t==null?t:!!t;case"bigint":return typeof t=="bigint"||t==null?t:Number.isInteger(typeof t=="string"&&!t.trim()?NaN:+t)?BigInt(t):void 0;case"integer":case"number":return typeof t=="number"?t:t==null||typeof t=="string"&&!t.trim()?NaN:Number(t);case"date":{if(t instanceof Date||t==null)return t;if(typeof t=="number")return new Date(t);let a=String(t).trim();return typeof t=="string"&&!a?null:new Date(Bc.test(a)?a:NaN)}case"array":case"object":case"buffer":case"other":return t;default:throw new Error(`Unable to coerce to type: ${u}`)}}function Jg(t){let{columns:u}=t,{schema:a}=t;return Oc(a)?{schema:a,inferred:!1}:(a=zc(t,Ic(u)?u:void 0),{schema:a,inferred:!0})}function Ec(t,u){let a=t,{schema:o,inferred:l}=Jg(t),d=new Map(o.map(({name:p,type:g})=>[p,g]));if(u.types){for(let{name:p,type:g}of u.types){d.set(p,g),o===a.schema&&(o=o.slice());let A=o.findIndex(_=>_.name===p);A>-1&&(o[A]={...o[A],type:g})}t=t.map(p=>Vu(p,d,o))}else l&&(t=t.map(p=>Vu(p,d,o)));return{source:t,schema:o}}function kc(t,u){if(!u.names)return t;let a=new Map(u.names.map(o=>[o.column,o]));return t.map(o=>Object.fromEntries(Object.keys(o).map(l=>[a.get(l)?.name??l,o[l]])))}function Qg(t,u){let a=new Map,o=t,l=Ec(t,u);t=l.source;let d=l.schema;if(u.derive){let _=[];u.derive.map(({name:w,value:R})=>{let C=[];kc(t,u).map(($,I)=>{let Z;try{Z=R($)}catch(G){C.push({index:I,error:G}),Z=void 0}_[I]?_[I]={..._[I],[w]:Z}:_.push({[w]:Z})}),C.length&&a.set(w,C)});let x=Ec(_,u);t=t.map((w,R)=>({...w,...x.source[R]})),d=[...d,...x.schema]}for(let{type:_,operands:x}of u.filter){let[{value:w}]=x,R=x.slice(1).map(({value:C})=>C);switch(_){case"v":{let[C]=R,$=Ac(C);t=t.filter(I=>$(I[w]));break}case"nv":{let[C]=R,$=Ac(C);t=t.filter(I=>!$(I[w]));break}case"eq":{let[C]=R;if(C instanceof Date){let $=+C;t=t.filter(I=>+I[w]===$)}else t=t.filter($=>$[w]===C);break}case"ne":{let[C]=R;t=t.filter($=>$[w]!==C);break}case"c":{let[C]=R;t=t.filter($=>typeof $[w]=="string"&&$[w].includes(C));break}case"nc":{let[C]=R;t=t.filter($=>typeof $[w]=="string"&&!$[w].includes(C));break}case"in":{let C=new Set(R);t=t.filter($=>C.has($[w]));break}case"nin":{let C=new Set(R);t=t.filter($=>!C.has($[w]));break}case"n":{t=t.filter(C=>C[w]==null);break}case"nn":{t=t.filter(C=>C[w]!=null);break}case"lt":{let[C]=R;t=t.filter($=>$[w]$[w]<=C);break}case"gt":{let[C]=R;t=t.filter($=>$[w]>C);break}case"gte":{let[C]=R;t=t.filter($=>$[w]>=C);break}default:throw new Error(`unknown filter type: ${_}`)}}for(let{column:_,direction:x}of vc(u.sort)){let w=x==="desc"?Dg:Pg;t===o&&(t=t.slice()),t.sort((R,C)=>w(R[_],C[_]))}let{from:p,to:g}=u.slice;p=p==null?0:Math.max(0,p),g=g==null?1/0:Math.max(0,g),(p>0||g<1/0)&&(t=t.slice(Math.max(0,p),Math.max(0,g)));let A=d.slice();if(u.select.columns){if(d){let _=new Map(d.map(x=>[x.name,x]));d=u.select.columns.map(x=>_.get(x))}t=t.map(_=>Object.fromEntries(u.select.columns.map(x=>[x,_[x]])))}if(u.names){let _=new Map(u.names.map(x=>[x.column,x]));d&&(d=d.map(x=>{let w=_.get(x.name);return{...x,...w?{name:w.name}:null}})),A&&(A=A.map(x=>{let w=_.get(x.name);return{...x,...w?{name:w.name}:null}})),t=kc(t,u)}return t!==o&&d&&(t.schema=d),t.fullSchema=A,t.errors=a,t}function Vu(t,u,a){let o={};for(let l of a){let d=u.get(l.name),p=t[l.name];o[l.name]=d==="raw"?p:Kg(p,d)}return o}function Xg(){return{boolean:0,integer:0,number:0,date:0,string:0,array:0,object:0,bigint:0,buffer:0,defined:0}}var ey=["boolean","integer","number","date","bigint","array","object","buffer"];function ry(t){let u=new Set;for(let a of t)if(a)for(let o in a)Object.prototype.hasOwnProperty.call(a,o)&&u.add(o);return Array.from(u)}function zc(t,u=ry(t)){let a=[],o=t.slice(0,100),l={};for(let d of u){let p=l[d]=Xg();for(let _ of o){let x=_[d];if(x==null)continue;let w=typeof x;if(w!=="string")++p.defined,Array.isArray(x)?++p.array:x instanceof Date?++p.date:x instanceof ArrayBuffer?++p.buffer:w==="number"?(++p.number,Number.isInteger(x)&&++p.integer):w in p&&++p[w];else{if(x=x.trim(),!x)continue;++p.defined,++p.string,/^(true|false)$/i.test(x)?++p.boolean:x&&!isNaN(x)?(++p.number,Number.isInteger(+x)&&++p.integer):Bc.test(x)&&++p.date}}let g=Math.max(1,p.defined*.9),A=dc(ey,_=>p[_]>=g?p[_]:NaN)??(p.string>=g?"string":"other");a.push({name:d,type:A,inferred:A})}return a}var ty=class{constructor(t){Object.defineProperties(this,{_:{value:t},sheetNames:{value:t.worksheets.map(u=>u.name),enumerable:!0}})}sheet(t,u){let a=typeof t=="number"?this.sheetNames[t]:this.sheetNames.includes(t+="")?t:null;if(a==null)throw new Error(`Sheet not found: ${t}`);let o=this._.getWorksheet(a);return ny(o,u)}};function ny(t,{range:u,headers:a}={}){let[[o,l],[d,p]]=ay(u,t),g=a?t._rows[l++]:null,A=new Set(["#"]);for(let x=o;x<=d;x++){let w=g?jc(g.findCell(x+1)):null,R=w&&w+""||uy(x);for(;A.has(R);)R+="_";A.add(R)}A=new Array(o).concat(Array.from(A));let _=new Array(p-l+1);for(let x=l;x<=p;x++){let w=_[x-l]=Object.create(null,{"#":{value:x+1}}),R=t.getRow(x+1);if(R.hasValues)for(let C=o;C<=d;C++){let $=jc(R.findCell(C+1));$!=null&&(w[A[C+1]]=$)}}return _.columns=A.filter(()=>!0),_}function jc(t){if(!t)return;let{value:u}=t;if(u&&typeof u=="object"&&!(u instanceof Date)){if(u.formula||u.sharedFormula)return u.result&&u.result.error?NaN:u.result;if(u.richText)return Sc(u);if(u.text){let{text:a}=u;return a.richText&&(a=Sc(a)),u.hyperlink&&u.hyperlink!==a?`${u.hyperlink} ${a}`:a}return u}return u}function Sc(t){return t.richText.map(u=>u.text).join("")}function ay(t=":",{columnCount:u,rowCount:a}){if(t+="",!t.match(/^[A-Z]*\d*:[A-Z]*\d*$/))throw new Error("Malformed range specifier");let[[o=0,l=0],[d=u-1,p=a-1]]=t.split(":").map(iy);return[[o,l],[d,p]]}function uy(t){let u="";t++;do u=String.fromCharCode(64+(t%26||26))+u;while(t=Math.floor((t-1)/26));return u}function iy(t){let[,u,a]=t.match(/^([A-Z]*)(\d*)$/),o=0;if(u)for(let l=0;l[o,l]));return Object.assign(t.map(o=>Vu(o,a,u)),{schema:u})}async function $c(t,u,{array:a=!1,typed:o=!1}={}){let l=await t.text(),d=u===" "?a?ic:uc:a?ac:nc;if(o==="auto"&&!a){let p=d(l);return oy(p,zc(p,p.columns))}return d(l,o&&oc)}var ei=class{constructor(t,u){Object.defineProperty(this,"name",{value:t,enumerable:!0}),u!==void 0&&Object.defineProperty(this,"mimeType",{value:u+"",enumerable:!0})}async blob(){return(await _r(this)).blob()}async arrayBuffer(){return(await _r(this)).arrayBuffer()}async text(){return(await _r(this)).text()}async json(){return(await _r(this)).json()}async stream(){return(await _r(this)).body}async csv(t){return $c(this,",",t)}async tsv(t){return $c(this," ",t)}async image(t){let u=await this.url();return new Promise((a,o)=>{let l=new Image;new URL(u,document.baseURI).origin!==new URL(location).origin&&(l.crossOrigin="anonymous"),Object.assign(l,t),l.onload=()=>a(l),l.onerror=()=>o(new Error(`Unable to load file: ${this.name}`)),l.src=u})}async arrow({version:t=4}={}){switch(t){case 4:{let[u,a]=await Promise.all([Ir(Wu.resolve()),_r(this)]);return u.Table.from(a)}case 9:{let[u,a]=await Promise.all([import(`${Or}${og.resolve()}`),_r(this)]);return u.tableFromIPC(a)}case 11:{let[u,a]=await Promise.all([import(`${Or}${Tc.resolve()}`),_r(this)]);return u.tableFromIPC(a)}default:throw new Error(`unsupported arrow version: ${t}`)}}async sqlite(){return Gu.open(_r(this))}async zip(){let[t,u]=await Promise.all([Ir(tg.resolve()),this.arrayBuffer()]);return new cy(await t.loadAsync(u))}async xml(t="application/xml"){return new DOMParser().parseFromString(await this.text(),t)}async html(){return this.xml("text/html")}async xlsx(){let[t,u]=await Promise.all([Ir(lg.resolve()),this.arrayBuffer()]);return new ty(await new t.Workbook().xlsx.load(u))}},_e=class extends ei{constructor(t,u,a){super(u,a),Object.defineProperty(this,"_url",{value:t})}async url(){return await this._url+""}};function sy(t){throw new Error(`File not found: ${t}`)}var cy=class{constructor(t){Object.defineProperty(this,"_",{value:t}),this.filenames=Object.keys(t.files).filter(u=>!t.files[u].dir)}file(t){let u=this._.file(t+="");if(!u||u.dir)throw new Error(`file not found: ${t}`);return new ly(u)}},ly=class extends ei{constructor(t){super(t.name),Object.defineProperty(this,"_",{value:t}),Object.defineProperty(this,"_url",{writable:!0})}async url(){return this._url||(this._url=this.blob().then(URL.createObjectURL))}async blob(){return this._.async("blob")}async arrayBuffer(){return this._.async("arraybuffer")}async text(){return this._.async("text")}async json(){return JSON.parse(await this.text())}},Wc={};Xn(Wc,{canvas:()=>fy,context2d:()=>py,download:()=>hy,element:()=>dy,input:()=>vy,range:()=>my,select:()=>gy,svg:()=>yy,text:()=>by,uid:()=>Hc});function fy(t,u){var a=document.createElement("canvas");return a.width=t,a.height=u,a}function py(t,u,a){a==null&&(a=devicePixelRatio);var o=document.createElement("canvas");o.width=t*a,o.height=u*a,o.style.width=t+"px";var l=o.getContext("2d");return l.scale(a,a),l}function hy(t,u="untitled",a="Save"){let o=document.createElement("a"),l=o.appendChild(document.createElement("button"));l.textContent=a,o.download=u;async function d(){await new Promise(requestAnimationFrame),URL.revokeObjectURL(o.href),o.removeAttribute("href"),l.textContent=a,l.disabled=!1}return o.onclick=async p=>{if(l.disabled=!0,o.href)return d();l.textContent="Saving\u2026";try{let g=await(typeof t=="function"?t():t);l.textContent="Download",o.href=URL.createObjectURL(g)}catch{l.textContent=a}if(p.eventPhase)return d();l.disabled=!1},o}var Kn={math:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function dy(t,u){var a=t+="",o=a.indexOf(":"),l;o>=0&&(a=t.slice(0,o))!=="xmlns"&&(t=t.slice(o+1));var d=Kn.hasOwnProperty(a)?document.createElementNS(Kn[a],t):document.createElement(t);if(u)for(var p in u)a=p,o=a.indexOf(":"),l=u[p],o>=0&&(a=p.slice(0,o))!=="xmlns"&&(p=p.slice(o+1)),Kn.hasOwnProperty(a)?d.setAttributeNS(Kn[a],p,l):d.setAttribute(p,l);return d}function vy(t){var u=document.createElement("input");return t!=null&&(u.type=t),u}function my(t,u,a){arguments.length===1&&(u=t,t=null);var o=document.createElement("input");return o.min=t=t==null?0:+t,o.max=u=u==null?1:+u,o.step=a==null?"any":a=+a,o.type="range",o}function gy(t){var u=document.createElement("select");return Array.prototype.forEach.call(t,function(a){var o=document.createElement("option");o.value=o.textContent=a,u.appendChild(o)}),u}function yy(t,u){var a=document.createElementNS("http://www.w3.org/2000/svg","svg");return a.setAttribute("viewBox",[0,0,t,u]),a.setAttribute("width",t),a.setAttribute("height",u),a}function by(t){return document.createTextNode(t)}var wy=0;function Hc(t){return new Vc("O-"+(t==null?"":t+"-")+ ++wy)}function Vc(t){this.id=t,this.href=new URL(`#${t}`,location)+""}Vc.prototype.toString=function(){return"url("+this.href+")"};var Zc={};Xn(Zc,{buffer:()=>_y,text:()=>xy,url:()=>Ay});function _y(t){return new Promise(function(u,a){var o=new FileReader;o.onload=function(){u(o.result)},o.onerror=a,o.readAsArrayBuffer(t)})}function xy(t){return new Promise(function(u,a){var o=new FileReader;o.onload=function(){u(o.result)},o.onerror=a,o.readAsText(t)})}function Ay(t){return new Promise(function(u,a){var o=new FileReader;o.onload=function(){u(o.result)},o.onerror=a,o.readAsDataURL(t)})}var Gc={};Xn(Gc,{disposable:()=>Yc,filter:()=>Ey,input:()=>ky,map:()=>Sy,observe:()=>ra,queue:()=>$y,range:()=>Cy,valueAt:()=>Ty,worker:()=>Ry});function ri(){return this}function Yc(t,u){let a=!1;if(typeof u!="function")throw new Error("dispose is not a function");return{[Symbol.iterator]:ri,next:()=>a?{done:!0}:(a=!0,{done:!1,value:t}),return:()=>(a=!0,u(t),{done:!0}),throw:()=>({done:a=!0})}}function*Ey(t,u){for(var a,o=-1;!(a=t.next()).done;)u(a.value,++o)&&(yield a.value)}function ra(t){let u=!1,a,o,l=t(d);if(l!=null&&typeof l!="function")throw new Error(typeof l.then=="function"?"async initializers are not supported":"initializer returned something, but not a dispose function");function d(g){return o?(o(g),o=null):u=!0,a=g}function p(){return{done:!1,value:u?(u=!1,Promise.resolve(a)):new Promise(g=>o=g)}}return{[Symbol.iterator]:ri,throw:()=>({done:!0}),return:()=>(l?.(),{done:!0}),next:p}}function ky(t){return ra(function(u){var a=jy(t),o=Cc(t);function l(){u(Cc(t))}return t.addEventListener(a,l),o!==void 0&&u(o),function(){t.removeEventListener(a,l)}})}function Cc(t){switch(t.type){case"range":case"number":return t.valueAsNumber;case"date":return t.valueAsDate;case"checkbox":return t.checked;case"file":return t.multiple?t.files:t.files[0];case"select-multiple":return Array.from(t.selectedOptions,u=>u.value);default:return t.value}}function jy(t){switch(t.type){case"button":case"submit":case"checkbox":return"click";case"file":return"change";default:return"input"}}function*Sy(t,u){for(var a,o=-1;!(a=t.next()).done;)yield u(a.value,++o)}function $y(t){let u,a=[],o=t(l);if(o!=null&&typeof o!="function")throw new Error(typeof o.then=="function"?"async initializers are not supported":"initializer returned something, but not a dispose function");function l(p){return a.push(p),u&&(u(a.shift()),u=null),p}function d(){return{done:!1,value:a.length?Promise.resolve(a.shift()):new Promise(p=>u=p)}}return{[Symbol.iterator]:ri,throw:()=>({done:!0}),return:()=>(o?.(),{done:!0}),next:d}}function*Cy(t,u,a){t=+t,u=+u,a=(l=arguments.length)<2?(u=t,t=0,1):l<3?1:+a;for(var o=-1,l=Math.max(0,Math.ceil((u-t)/a))|0;++o{a.terminate(),URL.revokeObjectURL(u)})}function ti(t,u){return function(a){var o=a[0],l=[],d,p=null,g,A,_,x,w,R,C,$=-1;for(x=1,w=arguments.length;x";else if(Array.isArray(d)){for(R=0,C=d.length;R"),p.appendChild(g)):(p=null,o+=g);p=null}else o+=d;o+=a[x]}if(p=t(o),++$>0){for(A=new Array($),_=document.createTreeWalker(p,NodeFilter.SHOW_COMMENT,null,!1);_.nextNode();)g=_.currentNode,/^o:/.test(g.nodeValue)&&(A[+g.nodeValue.slice(2)]=g);for(x=0;x<$;++x)(g=A[x])&&g.parentNode.replaceChild(l[x],g)}return p.childNodes.length===1?p.removeChild(p.firstChild):p.nodeType===11?((g=u()).appendChild(p),g):p}}var Ny=ti(function(t){var u=document.createElement("template");return u.innerHTML=t.trim(),document.importNode(u.content,!0)},function(){return document.createElement("span")});async function My(t){let u=await t(yc.resolve());if(!u._style){let a=document.createElement("link");a.rel="stylesheet",a.href=await t.resolve(yc.resolve("dist/leaflet.css")),u._style=document.head.appendChild(a)}return u}function Oy(t){return t(ng.resolve()).then(function(u){return ti(function(a){var o=document.createElement("div");o.innerHTML=u(a,{langPrefix:""}).trim();var l=o.querySelectorAll("pre code[class]");return l.length>0&&t(Uu.resolve()).then(function(d){l.forEach(function(p){function g(){d.highlightBlock(p),p.parentNode.classList.add("observablehq--md-pre")}d.getLanguage(p.className)?g():t(Uu.resolve("async-languages/index.js")).then(A=>{if(A.has(p.className))return t(Uu.resolve("async-languages/"+A.get(p.className))).then(_=>{d.registerLanguage(p.className,_)})}).then(g,g)})}),o},function(){return document.createElement("div")})})}async function Iy(t){let u=await t(fg.resolve());return u.initialize({securityLevel:"loose",theme:"neutral"}),function(){let a=document.createElement("div");return a.innerHTML=u.render(Hc().id,String.raw.apply(String,arguments)),a.removeChild(a.firstChild)}}function Ly(t){let u;Object.defineProperties(this,{generator:{value:ra(a=>void(u=a))},value:{get:()=>t,set:a=>u(t=a)}}),t!==void 0&&u(t)}function*Fy(){for(;;)yield Date.now()}var Kc={};Xn(Kc,{delay:()=>Py,tick:()=>qy,when:()=>Jc});function Py(t,u){return new Promise(function(a){setTimeout(function(){a(u)},t)})}var Zu=new Map;function Dy(t,u){var a=new Promise(function(o){Zu.delete(u);var l=u-t;if(!(l>0))throw new Error("invalid time");if(l>2147483647)throw new Error("too long to wait");setTimeout(o,l)});return Zu.set(u,a),a}function Jc(t,u){var a;return(a=Zu.get(t=+t))?a.then(()=>u):(a=Date.now())>=t?Promise.resolve(u):Dy(a,t).then(()=>u)}function qy(t,u){return Jc(Math.ceil((Date.now()+1)/t)*t,u)}function Uy(t,u){if(/^(\w+:)|\/\//i.test(t))return t;if(/^[.]{0,2}\//i.test(t))return new URL(t,u??location).href;if(!t.length||/^[\s._]/.test(t)||/\s$/.test(t))throw new Error("illegal name");return"https://unpkg.com/"+t}var By=ti(function(t){var u=document.createElementNS("http://www.w3.org/2000/svg","g");return u.innerHTML=t.trim(),u},function(){return document.createElementNS("http://www.w3.org/2000/svg","g")}),zy=String.raw;function Wy(t){return new Promise(function(u,a){var o=document.createElement("link");o.rel="stylesheet",o.href=t,o.onerror=a,o.onload=u,document.head.appendChild(o)})}function Hy(t){return Promise.all([t(mc.resolve()),t.resolve(mc.resolve("dist/katex.min.css")).then(Wy)]).then(function(u){var a=u[0],o=l();function l(d){return function(){var p=document.createElement("div");return a.render(zy.apply(String,arguments),p,d),p.removeChild(p.firstChild)}}return o.options=l,o.block=l({displayMode:!0}),o})}async function Vy(t){let[u,a,o]=await Promise.all([ag,ug,ig].map(l=>t(l.resolve())));return o.register(u,a)}function Zy(){return ra(function(t){var u=t(document.body.clientWidth);function a(){var o=document.body.clientWidth;o!==u&&t(u=o)}return window.addEventListener("resize",a),function(){window.removeEventListener("resize",a)}})}var Qc=Object.assign(Object.defineProperties(function(t){let u=hg(t);Object.defineProperties(this,Gy({FileAttachment:()=>sy,Mutable:()=>Ly,now:Fy,width:Zy,dot:()=>u(Xm.resolve()),htl:()=>u(rg.resolve()),html:()=>Ny,md:()=>Oy(u),svg:()=>By,tex:()=>Hy(u),_:()=>u(eg.resolve()),aq:()=>u.alias({"apache-arrow":Wu.resolve()})(sg.resolve()),Arrow:()=>u(Wu.resolve()),d3:()=>u(Km.resolve()),DuckDBClient:()=>Ju,Inputs:()=>u(Jm.resolve()).then(a=>({...a,file:a.fileOf(ei)})),L:()=>My(u),mermaid:()=>Iy(u),Plot:()=>u(Qm.resolve()),__query:()=>qc,require:()=>u,resolve:()=>Uy,SQLite:()=>Rc(u),SQLiteDatabaseClient:()=>Gu,topojson:()=>u(cg.resolve()),vl:()=>Vy(u),aapl:()=>new _e("https://static.observableusercontent.com/files/3ccff97fd2d93da734e76829b2b066eafdaac6a1fafdec0faf6ebc443271cfc109d29e80dd217468fcb2aff1e6bffdc73f356cc48feb657f35378e6abbbb63b9").csv({typed:!0}),alphabet:()=>new _e("https://static.observableusercontent.com/files/75d52e6c3130b1cae83cda89305e17b50f33e7420ef205587a135e8562bcfd22e483cf4fa2fb5df6dff66f9c5d19740be1cfaf47406286e2eb6574b49ffc685d").csv({typed:!0}),cars:()=>new _e("https://static.observableusercontent.com/files/048ec3dfd528110c0665dfa363dd28bc516ffb7247231f3ab25005036717f5c4c232a5efc7bb74bc03037155cb72b1abe85a33d86eb9f1a336196030443be4f6").csv({typed:!0}),citywages:()=>new _e("https://static.observableusercontent.com/files/39837ec5121fcc163131dbc2fe8c1a2e0b3423a5d1e96b5ce371e2ac2e20a290d78b71a4fb08b9fa6a0107776e17fb78af313b8ea70f4cc6648fad68ddf06f7a").csv({typed:!0}),diamonds:()=>new _e("https://static.observableusercontent.com/files/87942b1f5d061a21fa4bb8f2162db44e3ef0f7391301f867ab5ba718b225a63091af20675f0bfe7f922db097b217b377135203a7eab34651e21a8d09f4e37252").csv({typed:!0}),flare:()=>new _e("https://static.observableusercontent.com/files/a6b0d94a7f5828fd133765a934f4c9746d2010e2f342d335923991f31b14120de96b5cb4f160d509d8dc627f0107d7f5b5070d2516f01e4c862b5b4867533000").csv({typed:!0}),industries:()=>new _e("https://static.observableusercontent.com/files/76f13741128340cc88798c0a0b7fa5a2df8370f57554000774ab8ee9ae785ffa2903010cad670d4939af3e9c17e5e18e7e05ed2b38b848ac2fc1a0066aa0005f").csv({typed:!0}),miserables:()=>new _e("https://static.observableusercontent.com/files/31d904f6e21d42d4963ece9c8cc4fbd75efcbdc404bf511bc79906f0a1be68b5a01e935f65123670ed04e35ca8cae3c2b943f82bf8db49c5a67c85cbb58db052").json(),olympians:()=>new _e("https://static.observableusercontent.com/files/31ca24545a0603dce099d10ee89ee5ae72d29fa55e8fc7c9ffb5ded87ac83060d80f1d9e21f4ae8eb04c1e8940b7287d179fe8060d887fb1f055f430e210007c").csv({typed:!0}),penguins:()=>new _e("https://static.observableusercontent.com/files/715db1223e067f00500780077febc6cebbdd90c151d3d78317c802732252052ab0e367039872ab9c77d6ef99e5f55a0724b35ddc898a1c99cb14c31a379af80a").csv({typed:!0}),pizza:()=>new _e("https://static.observableusercontent.com/files/c653108ab176088cacbb338eaf2344c4f5781681702bd6afb55697a3f91b511c6686ff469f3e3a27c75400001a2334dbd39a4499fe46b50a8b3c278b7d2f7fb5").csv({typed:!0}),weather:()=>new _e("https://static.observableusercontent.com/files/693a46b22b33db0f042728700e0c73e836fa13d55446df89120682d55339c6db7cc9e574d3d73f24ecc9bc7eb9ac9a1e7e104a1ee52c00aab1e77eb102913c1f").csv({typed:!0}),DOM:Wc,Files:Zc,Generators:Gc,Promises:Kc}))},{resolve:{get:()=>Ir.resolve,enumerable:!0,configurable:!0},require:{get:()=>Ir,set:pg,enumerable:!0,configurable:!0}}),{resolveFrom:Fu,requireFrom:pt});function Gy(t){return Object.fromEntries(Object.entries(t).map(Yy))}function Yy([t,u]){return[t,{value:u,writable:!0,enumerable:!0}]}function Ky(t){if(t.sheet)return t.sheet;for(var u=0;u0?be(bt,--$e):0,gt--,de===10&&(gt=1,sa--),de}function Ie(){return de=$e2||yt(de)>3?"":" "}function ub(t,u){for(;--u&&Ie()&&!(de<48||de>102||de>57&&de<65||de>70&&de<97););return _t(t,na()+(u<6&&ar()==32&&Ie()==32))}function ni(t){for(;Ie();)switch(de){case t:return $e;case 34:case 39:t!==34&&t!==39&&ni(de);break;case 40:t===41&&ni(t);break;case 92:Ie();break}return $e}function ib(t,u){for(;Ie()&&t+de!==57&&!(t+de===84&&ar()===47););return"/*"+_t(u,$e-1)+"*"+Jt(t===47?t:Ie())}function ob(t){for(;!yt(ar());)Ie();return _t(t,$e)}function sl(t){return oi(aa("",null,null,null,[""],t=ii(t),0,[0],t))}function aa(t,u,a,o,l,d,p,g,A){for(var _=0,x=0,w=p,R=0,C=0,$=0,I=1,Z=1,G=1,ae=0,Q="",Ce=l,ie=d,le=o,U=Q;Z;)switch($=ae,ae=Ie()){case 40:if($!=108&&be(U,w-1)==58){oa(U+=Y(Gt(ae),"&","&\f"),"&\f")!=-1&&(G=-1);break}case 34:case 39:case 91:U+=Gt(ae);break;case 9:case 10:case 13:case 32:U+=ab($);break;case 92:U+=ub(na()-1,7);continue;case 47:switch(ar()){case 42:case 47:ta(sb(ib(Ie(),na()),u,a),A);break;default:U+="/"}break;case 123*I:g[_++]=nr(U)*G;case 125*I:case 59:case 0:switch(ae){case 0:case 125:Z=0;case 59+x:G==-1&&(U=Y(U,/\f/g,"")),C>0&&nr(U)-w&&ta(C>32?rl(U+";",o,a,w-1):rl(Y(U," ","")+";",o,a,w-2),A);break;case 59:U+=";";default:if(ta(le=el(U,u,a,_,x,l,g,Q,Ce=[],ie=[],w),d),ae===123)if(x===0)aa(U,u,le,le,Ce,d,w,g,ie);else switch(R===99&&be(U,3)===110?100:R){case 100:case 108:case 109:case 115:aa(t,le,le,o&&ta(el(t,le,le,0,0,l,g,Q,l,Ce=[],w),ie),l,ie,w,g,o?Ce:ie);break;default:aa(U,le,le,le,[""],ie,0,g,ie)}}_=x=C=0,I=G=1,Q=U="",w=p;break;case 58:w=1+nr(U),C=$;default:if(I<1){if(ae==123)--I;else if(ae==125&&I++==0&&nb()==125)continue}switch(U+=Jt(ae),ae*I){case 38:G=x>0?1:(U+="\f",-1);break;case 44:g[_++]=(nr(U)-1)*G,G=1;break;case 64:ar()===45&&(U+=Gt(Ie())),R=ar(),x=w=nr(Q=U+=ob(na())),ae++;break;case 45:$===45&&nr(U)==2&&(I=0)}}return d}function el(t,u,a,o,l,d,p,g,A,_,x){for(var w=l-1,R=l===0?d:[""],C=ui(R),$=0,I=0,Z=0;$0?R[G]+" "+ae:Y(ae,/&\f/g,R[G])))&&(A[Z++]=Q);return ca(t,u,a,l===0?ua:g,A,_,x)}function sb(t,u,a){return ca(t,u,a,tl,Jt(tb()),Yt(t,2,-2),0)}function rl(t,u,a,o){return ca(t,u,a,ia,Yt(t,0,o),Yt(t,o+1,-1),o)}function Yr(t,u){for(var a="",o=ui(t),l=0;l6)switch(be(t,u+1)){case 109:if(be(t,u+4)!==45)break;case 102:return Y(t,/(.+:)(.+)-([^]+)/,"$1"+K+"$2-$3$1"+Kt+(be(t,u+3)==108?"$3":"$2-$3"))+t;case 115:return~oa(t,"stretch")?hl(Y(t,"stretch","fill-available"),u)+t:t}break;case 4949:if(be(t,u+1)!==115)break;case 6444:switch(be(t,nr(t)-3-(~oa(t,"!important")&&10))){case 107:return Y(t,":",":"+K)+t;case 101:return Y(t,/(.+:)([^;!]+)(;|!.+)?/,"$1"+K+(be(t,14)===45?"inline-":"")+"box$3$1"+K+"$2$3$1"+xe+"$2box$3")+t}break;case 5936:switch(be(t,u+11)){case 114:return K+t+xe+Y(t,/[svh]\w+-[tblr]{2}/,"tb")+t;case 108:return K+t+xe+Y(t,/[svh]\w+-[tblr]{2}/,"tb-rl")+t;case 45:return K+t+xe+Y(t,/[svh]\w+-[tblr]{2}/,"lr")+t}return K+t+xe+t+t}return t}var db=function(t,u,a,o){if(t.length>-1&&!t.return)switch(t.type){case ia:t.return=hl(t.value,t.length);break;case ai:return Yr([wt(t,{value:Y(t.value,"@","@"+K)})],o);case ua:if(t.length)return il(t.props,function(l){switch(ul(l,/(::plac\w+|:read-\w+)/)){case":read-only":case":read-write":return Yr([wt(t,{props:[Y(l,/:(read-\w+)/,":"+Kt+"$1")]})],o);case"::placeholder":return Yr([wt(t,{props:[Y(l,/:(plac\w+)/,":"+K+"input-$1")]}),wt(t,{props:[Y(l,/:(plac\w+)/,":"+Kt+"$1")]}),wt(t,{props:[Y(l,/:(plac\w+)/,xe+"input-$1")]})],o)}return""})}},vb=fl(function(){return xt(function(){var t={};return function(u){return t[u]}})}),mb=[db],dl=function(t){var u=t.key,a=t.stylisPlugins||mb,o={},l,d=[],p,g=[pb,hb];{var A=[cl],_=ll(g.concat(a,A)),x=function($){return Yr(sl($),_)},w=vb(a)(u),R=function($,I){var Z=I.name;return w[Z]===void 0&&(w[Z]=x($?$+"{"+I.styles+"}":I.styles)),w[Z]};p=function($,I,Z,G){var ae=I.name,Q=R($,I);if(C.compat===void 0)return G&&(C.inserted[ae]=!0),Q;if(G)C.inserted[ae]=Q;else return Q}}var C={key:u,sheet:new Xc({key:u,container:l,nonce:t.nonce,speedy:t.speedy,prepend:t.prepend,insertionPoint:t.insertionPoint}),nonce:t.nonce,inserted:o,registered:{},insert:p};return C.sheet.hydrate(d),C};function si(t){for(var u=0,a,o=0,l=t.length;l>=4;++o,l-=4)a=t.charCodeAt(o)&255|(t.charCodeAt(++o)&255)<<8|(t.charCodeAt(++o)&255)<<16|(t.charCodeAt(++o)&255)<<24,a=(a&65535)*1540483477+((a>>>16)*59797<<16),a^=a>>>24,u=(a&65535)*1540483477+((a>>>16)*59797<<16)^(u&65535)*1540483477+((u>>>16)*59797<<16);switch(l){case 3:u^=(t.charCodeAt(o+2)&255)<<16;case 2:u^=(t.charCodeAt(o+1)&255)<<8;case 1:u^=t.charCodeAt(o)&255,u=(u&65535)*1540483477+((u>>>16)*59797<<16)}return u^=u>>>13,u=(u&65535)*1540483477+((u>>>16)*59797<<16),((u^u>>>15)>>>0).toString(36)}var ci={animationIterationCount:1,aspectRatio:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,msGridRow:1,msGridRowSpan:1,msGridColumn:1,msGridColumnSpan:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1};var gb=/[A-Z]|^ms/g,yb=/_EMO_([^_]+?)_([^]*?)_EMO_/g,yl=function(t){return t.charCodeAt(1)===45},vl=function(t){return t!=null&&typeof t!="boolean"},li=xt(function(t){return yl(t)?t:t.replace(gb,"-$&").toLowerCase()}),ml=function(t,u){switch(t){case"animation":case"animationName":if(typeof u=="string")return u.replace(yb,function(a,o,l){return cr={name:o,styles:l,next:cr},o})}return ci[t]!==1&&!yl(t)&&typeof u=="number"&&u!==0?u+"px":u};function Qt(t,u,a){if(a==null)return"";if(a.__emotion_styles!==void 0)return a;switch(typeof a){case"boolean":return"";case"object":{if(a.anim===1)return cr={name:a.name,styles:a.styles,next:cr},a.name;if(a.styles!==void 0){var o=a.next;if(o!==void 0)for(;o!==void 0;)cr={name:o.name,styles:o.styles,next:cr},o=o.next;var l=a.styles+";";return l}return bb(t,u,a)}case"function":{if(t!==void 0){var d=cr,p=a(t);return cr=d,Qt(t,u,p)}break}case"string":if(0)var g,A;break}if(u==null)return a;var _=u[a];return _!==void 0?_:a}function bb(t,u,a){var o="";if(Array.isArray(a))for(var l=0;l`;El(u);let a=u.appendChild(kl`
`);u.PLOT=a,u.controller=new AbortController,a.addEventListener("input",o=>{u.value=a.value,!o.bubbles&&u.dispatchEvent(new CustomEvent("input"))},{signal:u.controller.signal})}function __(t){let u=kb(),{PLOT:a}=u;a.layout_size={height:t.layout.height,width:t.layout.width};let o=firstRun,l=original_height??a.container_height??400;u.style.height=l+"px"}export{__ as createPlot}; -/*! Bundled license information: - -lodash/lodash.js: - (** - * @license - * Lodash - * Copyright OpenJS Foundation and other contributors - * Released under MIT license - * Based on Underscore.js 1.8.3 - * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - *) -*/ diff --git a/js/prehook.ts b/js/prehook.ts new file mode 100644 index 0000000..ed2a673 --- /dev/null +++ b/js/prehook.ts @@ -0,0 +1,61 @@ +// This file contains utilities to be executed before calling the plot function. +import { default as _ } from "https://esm.sh/lodash@4.17.21"; +import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; +import { addContainerStyle } from "./styles.js"; + +// We start by putting all the variable interpolation here at the beginning +// We have to convert all typedarrays in the layout to normal arrays. See Issue #25 +// We use lodash for this for compactness +function removeTypedArray(o) { + return _.isTypedArray(o) + ? Array.from(o) + : _.isPlainObject(o) + ? _.mapValues(o, removeTypedArray) + : o; +} + +export function makeContainer(caller_this) { + if (caller_this !== undefined) { + // If our input is already the container we just update its flag and return it + caller_this.firstRun = false; + return caller_this; + } + const CONTAINER = html`
`; + // Add the style to it + addContainerStyle(CONTAINER); + // Create the child div that will contain the actual plot + const PLOT = CONTAINER.appendChild( + html`
` + ); + CONTAINER.PLOT = PLOT; + // We use a controller to remove event listeners upon invalidation + CONTAINER.controller = new AbortController(); + // We have to add this to keep supporting @bind with the old API using PLOT + // TO REMOVE NOW WITH JS MIGRATION? WE ARE ANYHOW BREAKING + PLOT.addEventListener( + "input", + (e) => { + CONTAINER.value = PLOT.value; + if (e.bubbles) { + return; + } + CONTAINER.dispatchEvent(new CustomEvent("input")); + }, + { signal: CONTAINER.controller.signal } + ); +} + +export function createPlot(plot_obj) { + const CONTAINER = makeContainer(); + const { PLOT } = CONTAINER; + // Record or update the layout width/height if provided explicitly + PLOT.layout_size = { + height: plot_obj.layout.height, + width: plot_obj.layout.width, + } + // For the height we have to also put a fixed value in case the plot is put on a non-fixed-size container (like the default wrapper) + // We define a variable to check whether we still have to remove the fixed height + let remove_container_size = firstRun + let container_height = original_height ?? PLOT.container_height ?? 400 + CONTAINER.style.height = container_height + 'px' +} \ No newline at end of file diff --git a/js/prehooks.js b/js/prehooks.js index cc26657..ed2a673 100644 --- a/js/prehooks.js +++ b/js/prehooks.js @@ -1,12 +1,8 @@ // This file contains utilities to be executed before calling the plot function. import { default as _ } from "https://esm.sh/lodash@4.17.21"; -import { Library } from "https://esm.sh/@observablehq/stdlib@5.8.6"; +import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; import { addContainerStyle } from "./styles.js"; - -const library = new Library() -const html = library.html() - // We start by putting all the variable interpolation here at the beginning // We have to convert all typedarrays in the layout to normal arrays. See Issue #25 // We use lodash for this for compactness @@ -18,7 +14,7 @@ function removeTypedArray(o) { : o; } -function makeContainer(caller_this) { +export function makeContainer(caller_this) { if (caller_this !== undefined) { // If our input is already the container we just update its flag and return it caller_this.firstRun = false; From c4eeba58dbb5fe9a2824a383dd6d55571c248b08 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Fri, 22 Mar 2024 14:59:46 +0100 Subject: [PATCH 05/39] asd --- js/prehook.ts | 61 -------------------------------------------------- js/prehooks.js | 1 + 2 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 js/prehook.ts diff --git a/js/prehook.ts b/js/prehook.ts deleted file mode 100644 index ed2a673..0000000 --- a/js/prehook.ts +++ /dev/null @@ -1,61 +0,0 @@ -// This file contains utilities to be executed before calling the plot function. -import { default as _ } from "https://esm.sh/lodash@4.17.21"; -import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; -import { addContainerStyle } from "./styles.js"; - -// We start by putting all the variable interpolation here at the beginning -// We have to convert all typedarrays in the layout to normal arrays. See Issue #25 -// We use lodash for this for compactness -function removeTypedArray(o) { - return _.isTypedArray(o) - ? Array.from(o) - : _.isPlainObject(o) - ? _.mapValues(o, removeTypedArray) - : o; -} - -export function makeContainer(caller_this) { - if (caller_this !== undefined) { - // If our input is already the container we just update its flag and return it - caller_this.firstRun = false; - return caller_this; - } - const CONTAINER = html`
`; - // Add the style to it - addContainerStyle(CONTAINER); - // Create the child div that will contain the actual plot - const PLOT = CONTAINER.appendChild( - html`
` - ); - CONTAINER.PLOT = PLOT; - // We use a controller to remove event listeners upon invalidation - CONTAINER.controller = new AbortController(); - // We have to add this to keep supporting @bind with the old API using PLOT - // TO REMOVE NOW WITH JS MIGRATION? WE ARE ANYHOW BREAKING - PLOT.addEventListener( - "input", - (e) => { - CONTAINER.value = PLOT.value; - if (e.bubbles) { - return; - } - CONTAINER.dispatchEvent(new CustomEvent("input")); - }, - { signal: CONTAINER.controller.signal } - ); -} - -export function createPlot(plot_obj) { - const CONTAINER = makeContainer(); - const { PLOT } = CONTAINER; - // Record or update the layout width/height if provided explicitly - PLOT.layout_size = { - height: plot_obj.layout.height, - width: plot_obj.layout.width, - } - // For the height we have to also put a fixed value in case the plot is put on a non-fixed-size container (like the default wrapper) - // We define a variable to check whether we still have to remove the fixed height - let remove_container_size = firstRun - let container_height = original_height ?? PLOT.container_height ?? 400 - CONTAINER.style.height = container_height + 'px' -} \ No newline at end of file diff --git a/js/prehooks.js b/js/prehooks.js index ed2a673..d0118a5 100644 --- a/js/prehooks.js +++ b/js/prehooks.js @@ -43,6 +43,7 @@ export function makeContainer(caller_this) { }, { signal: CONTAINER.controller.signal } ); + return CONTAINER } export function createPlot(plot_obj) { From 0586c32d16d27b536adb4ca9eb969afd5d2c4a3e Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Fri, 22 Mar 2024 15:03:16 +0100 Subject: [PATCH 06/39] lol --- js/prehooks.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/js/prehooks.js b/js/prehooks.js index d0118a5..8d98e1a 100644 --- a/js/prehooks.js +++ b/js/prehooks.js @@ -59,4 +59,8 @@ export function createPlot(plot_obj) { let remove_container_size = firstRun let container_height = original_height ?? PLOT.container_height ?? 400 CONTAINER.style.height = container_height + 'px' +} + +export function lol() { + console.log("LOL") } \ No newline at end of file From ab143a28db647f91b378b0eb31bd8e8492bc1308 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Wed, 27 Mar 2024 13:16:55 +0100 Subject: [PATCH 07/39] try importmap --- js/deno.jsonc | 3 +++ js/prehooks.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/js/deno.jsonc b/js/deno.jsonc index 3106ccf..746197f 100644 --- a/js/deno.jsonc +++ b/js/deno.jsonc @@ -1,5 +1,8 @@ { "tasks": { "bundle": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run bundle.js" + }, + "imports": { + "lodash": "https://esm.sh/lodash@4.17.21" } } \ No newline at end of file diff --git a/js/prehooks.js b/js/prehooks.js index 8d98e1a..35030a0 100644 --- a/js/prehooks.js +++ b/js/prehooks.js @@ -1,5 +1,5 @@ // This file contains utilities to be executed before calling the plot function. -import { default as _ } from "https://esm.sh/lodash@4.17.21"; +import { default as _ } from "lodash"; import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; import { addContainerStyle } from "./styles.js"; From 4203f42e0f1265cddcef988ca2a99d11b4721a94 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Wed, 27 Mar 2024 13:21:26 +0100 Subject: [PATCH 08/39] add observable-html importmap --- js/deno.jsonc | 3 ++- js/prehooks.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/js/deno.jsonc b/js/deno.jsonc index 746197f..c74c237 100644 --- a/js/deno.jsonc +++ b/js/deno.jsonc @@ -3,6 +3,7 @@ "bundle": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run bundle.js" }, "imports": { - "lodash": "https://esm.sh/lodash@4.17.21" + "lodash": "https://esm.sh/lodash@4.17.21", + "observable-html": "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js" } } \ No newline at end of file diff --git a/js/prehooks.js b/js/prehooks.js index 35030a0..7289a67 100644 --- a/js/prehooks.js +++ b/js/prehooks.js @@ -1,6 +1,6 @@ // This file contains utilities to be executed before calling the plot function. import { default as _ } from "lodash"; -import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; +import { html } from "observable-html"; import { addContainerStyle } from "./styles.js"; // We start by putting all the variable interpolation here at the beginning From f1802b5387a20ec2e35bf66dd9bad3f0acc66197 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Wed, 27 Mar 2024 14:29:07 +0100 Subject: [PATCH 09/39] revert importmaps --- js/deno.jsonc | 4 ---- js/prehooks.js | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/js/deno.jsonc b/js/deno.jsonc index c74c237..3106ccf 100644 --- a/js/deno.jsonc +++ b/js/deno.jsonc @@ -1,9 +1,5 @@ { "tasks": { "bundle": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run bundle.js" - }, - "imports": { - "lodash": "https://esm.sh/lodash@4.17.21", - "observable-html": "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js" } } \ No newline at end of file diff --git a/js/prehooks.js b/js/prehooks.js index 7289a67..8d98e1a 100644 --- a/js/prehooks.js +++ b/js/prehooks.js @@ -1,6 +1,6 @@ // This file contains utilities to be executed before calling the plot function. -import { default as _ } from "lodash"; -import { html } from "observable-html"; +import { default as _ } from "https://esm.sh/lodash@4.17.21"; +import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; import { addContainerStyle } from "./styles.js"; // We start by putting all the variable interpolation here at the beginning From d5e0fefe0541d3bcbb5e52f12031376e4d2d11d1 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Wed, 27 Mar 2024 14:32:13 +0100 Subject: [PATCH 10/39] try accessing window via globalThis --- js/prehooks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/prehooks.js b/js/prehooks.js index 8d98e1a..fd04b6e 100644 --- a/js/prehooks.js +++ b/js/prehooks.js @@ -61,6 +61,6 @@ export function createPlot(plot_obj) { CONTAINER.style.height = container_height + 'px' } -export function lol() { - console.log("LOL") +export function lol(a = globalThis.asd) { + console.log("LOL: ", a) } \ No newline at end of file From a4ecd88b7697b1faeeedd5be2859d7dd04c58f96 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Wed, 3 Apr 2024 14:55:36 +0200 Subject: [PATCH 11/39] update some code, add some JSDoc, experimenti some Deno. Still not working --- .gitignore | 1 + js/bundle.js | 4 +- js/deno.jsonc | 7 + js/deno.lock | 5 + js/emotion.js | 3 - js/jsconfig.json | 7 + js/prehooks.js | 66 ------ js/src/clipboard.js | 485 +++++++++++++++++++++++++++++++++++++++++ js/src/container.js | 40 ++++ js/src/prehooks.js | 28 +++ js/src/resizer.js | 207 ++++++++++++++++++ js/{ => src}/styles.js | 9 +- js/src/typedef.js | 72 ++++++ js/src/url_imports.js | 7 + js/src/utils.js | 30 +++ 15 files changed, 896 insertions(+), 75 deletions(-) delete mode 100644 js/emotion.js create mode 100644 js/jsconfig.json delete mode 100644 js/prehooks.js create mode 100644 js/src/clipboard.js create mode 100644 js/src/container.js create mode 100644 js/src/prehooks.js create mode 100644 js/src/resizer.js rename js/{ => src}/styles.js (96%) create mode 100644 js/src/typedef.js create mode 100644 js/src/url_imports.js create mode 100644 js/src/utils.js diff --git a/.gitignore b/.gitignore index 7f852aa..7fd7bbc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ Manifest.toml test*.jl .vscode/ .DS_Store +js/dist/ \ No newline at end of file diff --git a/js/bundle.js b/js/bundle.js index 9a90904..3df7cf2 100644 --- a/js/bundle.js +++ b/js/bundle.js @@ -7,8 +7,8 @@ import { denoPlugins } from "jsr:@luca/esbuild-deno-loader@^0.10.3"; const result = await esbuild.build({ plugins: [...denoPlugins()], - entryPoints: ["prehooks.js"], - outfile: "prehook.esm.js", + entryPoints: ["./src/prehooks.js"], + outfile: "./dist/library.esm.min.js", bundle: true, format: "esm", minify: true, diff --git a/js/deno.jsonc b/js/deno.jsonc index 3106ccf..0f7b060 100644 --- a/js/deno.jsonc +++ b/js/deno.jsonc @@ -1,4 +1,11 @@ { + "compilerOptions": { + "lib": [ + "esnext", + "dom" + ], + "checkJs": true + }, "tasks": { "bundle": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run bundle.js" } diff --git a/js/deno.lock b/js/deno.lock index 9b66982..b0d0236 100644 --- a/js/deno.lock +++ b/js/deno.lock @@ -160,5 +160,10 @@ } } }, + "redirects": { + "https://esm.sh/@types/plotly.js": "https://esm.sh/v135/@types/plotly.js@2.29.2/index.d.ts", + "https://esm.sh/v135/@types/lodash-es@~4.17/index.d.ts": "https://esm.sh/v135/@types/lodash-es@4.17.12/index.d.ts", + "https://esm.sh/v135/@types/lodash@4.14.202/index": "https://esm.sh/v135/@types/lodash@4.14.202/index~.d.ts" + }, "remote": {} } diff --git a/js/emotion.js b/js/emotion.js deleted file mode 100644 index 074840a..0000000 --- a/js/emotion.js +++ /dev/null @@ -1,3 +0,0 @@ -import { css } from "https://esm.sh/@emotion/css@11.11.2"; - -export default css \ No newline at end of file diff --git a/js/jsconfig.json b/js/jsconfig.json new file mode 100644 index 0000000..065705d --- /dev/null +++ b/js/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "checkJs": true, + "baseUrl": "./", + "lib": ["esnext", "dom"], + }, +} \ No newline at end of file diff --git a/js/prehooks.js b/js/prehooks.js deleted file mode 100644 index fd04b6e..0000000 --- a/js/prehooks.js +++ /dev/null @@ -1,66 +0,0 @@ -// This file contains utilities to be executed before calling the plot function. -import { default as _ } from "https://esm.sh/lodash@4.17.21"; -import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; -import { addContainerStyle } from "./styles.js"; - -// We start by putting all the variable interpolation here at the beginning -// We have to convert all typedarrays in the layout to normal arrays. See Issue #25 -// We use lodash for this for compactness -function removeTypedArray(o) { - return _.isTypedArray(o) - ? Array.from(o) - : _.isPlainObject(o) - ? _.mapValues(o, removeTypedArray) - : o; -} - -export function makeContainer(caller_this) { - if (caller_this !== undefined) { - // If our input is already the container we just update its flag and return it - caller_this.firstRun = false; - return caller_this; - } - const CONTAINER = html`
`; - // Add the style to it - addContainerStyle(CONTAINER); - // Create the child div that will contain the actual plot - const PLOT = CONTAINER.appendChild( - html`
` - ); - CONTAINER.PLOT = PLOT; - // We use a controller to remove event listeners upon invalidation - CONTAINER.controller = new AbortController(); - // We have to add this to keep supporting @bind with the old API using PLOT - // TO REMOVE NOW WITH JS MIGRATION? WE ARE ANYHOW BREAKING - PLOT.addEventListener( - "input", - (e) => { - CONTAINER.value = PLOT.value; - if (e.bubbles) { - return; - } - CONTAINER.dispatchEvent(new CustomEvent("input")); - }, - { signal: CONTAINER.controller.signal } - ); - return CONTAINER -} - -export function createPlot(plot_obj) { - const CONTAINER = makeContainer(); - const { PLOT } = CONTAINER; - // Record or update the layout width/height if provided explicitly - PLOT.layout_size = { - height: plot_obj.layout.height, - width: plot_obj.layout.width, - } - // For the height we have to also put a fixed value in case the plot is put on a non-fixed-size container (like the default wrapper) - // We define a variable to check whether we still have to remove the fixed height - let remove_container_size = firstRun - let container_height = original_height ?? PLOT.container_height ?? 400 - CONTAINER.style.height = container_height + 'px' -} - -export function lol(a = globalThis.asd) { - console.log("LOL: ", a) -} \ No newline at end of file diff --git a/js/src/clipboard.js b/js/src/clipboard.js new file mode 100644 index 0000000..36ab1fb --- /dev/null +++ b/js/src/clipboard.js @@ -0,0 +1,485 @@ +import { interact, html } from "./url_imports.js" +import { delay, getImageOptions } from "./utils.js" + + +/** + * Adds a clipboard header to the given container. + * + * @param {import("./typedef.js").Container} CONTAINER - The container to which the clipboard header will be added. + * @return {undefined} This function does not return a value. + */ +export function addClipboardHeader(CONTAINER) { + // Return if the CLIPBOARD HEADER has been assigned already + if (CONTAINER.CLIPBOARD_HEADER !== undefined) return +const CLIPBOARD_HEADER = html`` + CLIPBOARD_HEADER.config_spans = {} +} + + +function checkConfigSync(container) { + const valid_classes = [ + "missing-config", + "matching-config", + "different-config", + ]; + function setClass(cl) { + for (const name of valid_classes) { + container.classList.toggle(name, name == cl); + } + } + // We use the custom getters we'll set up in the container + const { ui_value, config_value, config_span, key } = container; + if (config_value === undefined) { + setClass("missing-config"); + config_span.innerHTML = `The key \${key} is not present in the config.`; + } else if (ui_value == config_value) { + setClass("matching-config"); + config_span.innerHTML = `The key \${key} has the same value in the config and in the header.`; + } else { + setClass("different-config"); + config_span.innerHTML = `The key \${key} has a different value (\${config_value}) in the config.`; + } + // Add info about setting and unsetting + config_span.insertAdjacentHTML( + "beforeend", + `
Click on the label once to set the current UI value in the config.` + ); + config_span.insertAdjacentHTML( + "beforeend", + `
Click twice to remove this key from the config.` + ); +} + +const valid_formats = ["png", "svg", "webp", "jpeg", "full-json"]; +function initializeUIValueSpan(span, key, value) { + const container = span.closest(".clipboard-span"); + span.contentEditable = key === "format" ? "false" : "true"; + let parse = (x) => x; + let update = (x) => (span.textContent = x); + if (key === "width" || key === "height") { + parse = (x) => Math.round(parseFloat(x)); + } else if (key === "scale") { + parse = parseFloat; + } else if (key === "format") { + // We remove contentEditable + span.contentEditable = "false"; + // Here we first add the subspans for each option + const opts_div = span.appendChild(html`
`); + for (const fmt of valid_formats) { + const opt = opts_div.appendChild( + html`\${fmt}` + ); + opt.onclick = (e) => { + span.value = opt.textContent; + }; + } + parse = (x) => { + return valid_formats.includes(x) ? x : localValue; + }; + update = (x) => { + for (const opt of opts_div.children) { + opt.classList.toggle("selected", opt.textContent === x); + } + }; + } else { + // We only have filename here + } + let localValue; + Object.defineProperty(span, "value", { + get: () => { + return localValue; + }, + set: (val) => { + if (val !== "") { + localValue = parse(val); + } + update(localValue); + checkConfigSync(container); + }, + }); + // We also assign a listener so that the editable is blurred when enter is pressed + span.onkeydown = (/** @type {KeyboardEvent} */e) => { + if (e.key === 'Enter') { + e.preventDefault(); + span.blur(); + } + }; + span.value = value; +} + +function initializeConfigValueSpan(span, key) { + // Here we mostly want to define the setter and getter + const container = span.closest(".clipboard-span"); + Object.defineProperty(span, "value", { + get: () => { + return plot_obj.config.toImageButtonOptions[key]; + }, + set: (val) => { + // if undefined is passed, we remove the entry from the options + if (val === undefined) { + delete plot_obj.config.toImageButtonOptions[key]; + } else { + plot_obj.config.toImageButtonOptions[key] = val; + } + checkConfigSync(container); + }, + }); +} + +const config_spans = {}; +for (const [key, value] of Object.entries(getImageOptions())) { + const container = CLIPBOARD_HEADER.querySelector(`.clipboard-span.\${key}`); + const label = container.querySelector(".label"); + // We give the label a function that on single click will set the current value and with double click will unset it + label.onclick = DualClick( + () => { + container.config_value = container.ui_value; + }, + (e) => { + console.log("e", e); + e.preventDefault(); + container.config_value = undefined; + } + ); + const ui_value_span = container.querySelector(".clipboard-value"); + const config_value_span = + container.querySelector(".config-value") ?? + label.insertAdjacentElement( + "afterbegin", + html`` + ); + // Assing the two spans as properties of the containing span + container.ui_span = ui_value_span; + container.config_span = config_value_span; + container.key = key; + config_spans[key] = container; + if (firstRun) { + plot_obj.config.toImageButtonOptions = + plot_obj.config.toImageButtonOptions ?? {}; + // We do the initialization of the value span + initializeUIValueSpan(ui_value_span, key, value); + // Then we initialize the config value + initializeConfigValueSpan(config_value_span, key); + // We put some convenience getters/setters + // ui_value forward + Object.defineProperty(container, "ui_value", { + get: () => ui_value_span.value, + set: (val) => { + ui_value_span.value = val; + }, + }); + // config_value forward + Object.defineProperty(container, "config_value", { + get: () => config_value_span.value, + set: (val) => { + config_value_span.value = val; + }, + }); + } +} + +// These objects will contain the default value + +// This code updates the image options in the PLOT config with the provided ones +function setImageOptions(o) { + for (const [key, container] of Object.entries(config_spans)) { + container.config_value = o[key]; + } +} +function unsetImageOptions() { + setImageOptions({}); +} + +const set_button = CLIPBOARD_HEADER.querySelector(".clipboard-span.set"); +const unset_button = CLIPBOARD_HEADER.querySelector(".clipboard-span.unset"); +if (firstRun) { + set_button.onclick = (e) => { + for (const container of Object.values(config_spans)) { + container.config_value = container.ui_value; + } + }; + unset_button.onclick = unsetImageOptions; +} + +// We add a function to check if the clipboard is popped out +CONTAINER.isPoppedOut = () => { + return CONTAINER.classList.contains("popped-out"); +}; + +CLIPBOARD_HEADER.onmousedown = function (event) { + if (event.target.matches("span.clipboard-value")) { + console.log("We don't move!"); + return; + } + const start = { + left: parseFloat(CONTAINER.style.left), + top: parseFloat(CONTAINER.style.top), + X: event.pageX, + Y: event.pageY, + }; + function moveAt(event, start) { + const top = event.pageY - start.Y + start.top + "px"; + const left = event.pageX - start.X + start.left + "px"; + CLIPBOARD_HEADER.style.left = left; + CONTAINER.style.left = left; + CONTAINER.style.top = top; + } + + // move our absolutely positioned ball under the pointer + moveAt(event, start); + function onMouseMove(event) { + moveAt(event, start); + } + + // We use this to remove the mousemove when clicking outside of the container + const controller = new AbortController(); + + // move the container on mousemove + document.addEventListener("mousemove", onMouseMove, { + signal: controller.signal, + }); + document.addEventListener( + "mousedown", + (e) => { + if (e.target.closest(".plutoplotly-container") !== CONTAINER) { + cleanUp(); + controller.abort(); + return; + } + }, + { signal: controller.signal } + ); + + function cleanUp() { + console.log("cleaning up the plot move listener"); + controller.abort(); + CLIPBOARD_HEADER.onmouseup = null; + } + + // (3) drop the ball, remove unneeded handlers + CLIPBOARD_HEADER.onmouseup = cleanUp; +}; + +function sendToClipboard(blob) { + if (!navigator.clipboard) { + alert( + "The Clipboard API does not seem to be available, make sure the Pluto notebook is being used from either localhost or an https source." + ); + } + navigator.clipboard + .write([ + new ClipboardItem({ + // The key is determined dynamically based on the blob's type. + [blob.type]: blob, + }), + ]) + .then( + function () { + console.log("Async: Copying to clipboard was successful!"); + }, + function (err) { + console.error("Async: Could not copy text: ", err); + } + ); +} + +function copyImageToClipboard() { + // We extract the image options from the provided parameters (if they exist) + const config = {}; + for (const [key, container] of Object.entries(config_spans)) { + let val = + container.config_value ?? + (CONTAINER.isPoppedOut() ? container.ui_value : undefined); + // If we have undefined we don't create the key. We also ignore format because the clipboard only supports png. + if (val === undefined || key === "format") { + continue; + } + config[key] = val; + } + Plotly.toImage(PLOT, config).then(function (dataUrl) { + fetch(dataUrl) + .then((res) => res.blob()) + .then((blob) => { + const paste_receiver = document.querySelector('paste-receiver.plutoplotly') + if (paste_receiver) { + paste_receiver.attachImage(dataUrl, CONTAINER) + } + sendToClipboard(blob) + }); + }); +} + +function saveImageToFile() { + const config = {}; + for (const [key, container] of Object.entries(config_spans)) { + let val = + container.config_value ?? + (CONTAINER.isPoppedOut() ? container.ui_value : undefined); + // If we have undefined we don't create the key. + if (val === undefined) { + continue; + } + config[key] = val; + } + Plotly.downloadImage(PLOT, config); +} + +let container_rect = { width: 0, height: 0, top: 0, left: 0 }; +function unpop_container(cl) { + CONTAINER.classList.toggle("popped-out", false); + CONTAINER.classList.toggle(cl, false); + // We fix the height back to the value it had before popout, also setting the flag to signal that upon first resize we remove the fixed inline-style + CONTAINER.style.height = container_rect.height + "px"; + remove_container_size = true; + // We set the other fixed inline-styles to null + CONTAINER.style.width = ""; + CONTAINER.style.top = ""; + CONTAINER.style.left = ""; + // We also remove the CLIPBOARD_HEADER + CLIPBOARD_HEADER.style.width = ""; + CLIPBOARD_HEADER.style.left = ""; + // Finally we remove the hidden class to the header + CLIPBOARD_HEADER.classList.toggle("hidden", true); + return; +} +function popout_container(opts) { + const cl = opts?.cl; + const target_container_size = opts?.target_container_size ?? {}; + const target_plot_size = opts?.target_plot_size ?? {}; + if (CONTAINER.isPoppedOut()) { + return unpop_container(cl); + } + CONTAINER.classList.toggle(cl, cl === undefined ? false : true); + // We extract the current size of the container, save them and fix them + const { width, height, top, left } = CONTAINER.getBoundingClientRect(); + container_rect = { width, height, top, left }; + // We save the current plot size before we pop as it will fill the screen + const current_plot_size = { + width: PLOT._fullLayout.width, + height: PLOT._fullLayout.height, + }; + // We have to save the pad data before popping so we can resize precisely + const pad = {}; + pad.unpopped = getSizeData().container_pad; + CONTAINER.classList.toggle("popped-out", true); + pad.popped = getSizeData().container_pad; + // We do top and left based on the current rect + for (const key of ["top", "left"]) { + const start_val = target_container_size[key] ?? container_rect[key]; + let offset = 0; + for (const kind of ["padding", "border"]) { + offset += pad.popped[kind][key] - pad.unpopped[kind][key]; + } + CONTAINER.style[key] = start_val - offset + "px"; + if (key === "left") { + CLIPBOARD_HEADER.style[key] = CONTAINER.style[key]; + } + } + // We compute the width and height depending on eventual config data + const csz = computeContainerSize({ + width: + target_plot_size.width ?? + config_spans.width.config_value ?? + current_plot_size.width, + height: + target_plot_size.height ?? + config_spans.height.config_value ?? + current_plot_size.height, + }); + for (const key of ["width", "height"]) { + const val = target_container_size[key] ?? csz[key]; + CONTAINER.style[key] = val + "px"; + if (key === "width") { + CLIPBOARD_HEADER.style[key] = CONTAINER.style[key]; + } + } + CLIPBOARD_HEADER.classList.toggle("hidden", false); + const controller = new AbortController(); + + document.addEventListener( + "mousedown", + (e) => { + if (e.target.closest(".plutoplotly-container") !== CONTAINER) { + unpop_container(); + controller.abort(); + return; + } + }, + { signal: controller.signal } + ); +} + +CONTAINER.popOut = popout_container; + +function DualClick(single_func, dbl_func) { + let nclicks = 0; + return function (...args) { + nclicks += 1; + if (nclicks > 1) { + dbl_func(...args); + nclicks = 0; + } else { + delay(300).then(() => { + if (nclicks == 1) { + single_func(...args); + } + nclicks = 0; + }); + } + }; +} + +// We remove the default download image button +plot_obj.config.modeBarButtonsToRemove = _.union( + plot_obj.config.modeBarButtonsToRemove, + ["toImage"] +); +// We add the custom button to the modebar +plot_obj.config.modeBarButtonsToAdd = _.union( + plot_obj.config.modeBarButtonsToAdd, + [ + { + name: "Copy PNG to Clipboard", + icon: { + height: 520, + width: 520, + path: "M280 64h40c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128C0 92.7 28.7 64 64 64h40 9.6C121 27.5 153.3 0 192 0s71 27.5 78.4 64H280zM64 112c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320c8.8 0 16-7.2 16-16V128c0-8.8-7.2-16-16-16H304v24c0 13.3-10.7 24-24 24H192 104c-13.3 0-24-10.7-24-24V112H64zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z", + }, + direction: "up", + click: DualClick(copyImageToClipboard, () => { + popout_container(); + }), + }, + { + name: "Download Image", + icon: Plotly.Icons.camera, + direction: "up", + click: DualClick(saveImageToFile, () => { + popout_container({ cl: "filesave" }); + }), + }, + ] +); \ No newline at end of file diff --git a/js/src/container.js b/js/src/container.js new file mode 100644 index 0000000..d4b97ff --- /dev/null +++ b/js/src/container.js @@ -0,0 +1,40 @@ +//@ts-check +import { html } from "./url_imports.js"; +import { addClipboardHeader } from "./clipboard.js"; +import { addContainerStyle } from "./styles.js"; +/** + * Creates the Container element used by PlutoPlotly to wrap the plotly.js plot and adds additional functionality like resizing and enhanced clipboard. + * + * @param {import("./typedef.js").Plotly} Plotly - The Plotly object. Defaults to globalThis.Plotly. + * @return {import("./typedef.js").Container} The container element for the Plotly plot. + */ +export function makeContainer(Plotly = globalThis.Plotly) { + const /** @type {import("./typedef.js").Container} */ CONTAINER = html`
`; + CONTAINER.Plotly = Plotly + // Add the style to it + addContainerStyle(CONTAINER); + // Add the clipboard header + addClipboardHeader(CONTAINER); + // Create the child div that will contain the actual plot + const PLOT = CONTAINER.PLOT = CONTAINER.appendChild( + html`
` + ); + // We set the flag of remove + CONTAINER.remove_container_size = true + // We use a controller to remove event listeners upon invalidation + CONTAINER.controller = new AbortController(); + // We have to add this to keep supporting @bind with the old API using PLOT + // TO REMOVE NOW WITH JS MIGRATION? WE ARE ANYHOW BREAKING + PLOT.addEventListener( + "input", + (/** @type {Event} */ e) => { + CONTAINER.value = PLOT.value; + if (e.bubbles) { + return; + } + CONTAINER.dispatchEvent(new CustomEvent("input")); + }, + { signal: CONTAINER.controller.signal } + ); + return CONTAINER +} \ No newline at end of file diff --git a/js/src/prehooks.js b/js/src/prehooks.js new file mode 100644 index 0000000..4c53d81 --- /dev/null +++ b/js/src/prehooks.js @@ -0,0 +1,28 @@ +// This file contains utilities to be executed before calling the plot function. +import { html, lodash as _ } from "./url_imports.js" +import { makeContainer } from "./container.js"; + +// We start by putting all the variable interpolation here at the beginning +// We have to convert all typedarrays in the layout to normal arrays. See Issue #25 + +export function createPlot(plot_obj, Plotly = globalThis.Plotly) { + const CONTAINER = makeContainer(); + const { PLOT } = CONTAINER; + // Record or update the layout width/height if provided explicitly + PLOT.layout_size = { + height: plot_obj.layout.height, + width: plot_obj.layout.width, + } + // Removed typed arrays from the layout + const _plot_obj = processPlotObj(plot_obj) + // For the height we have to also put a fixed value in case the plot is put on a non-fixed-size container (like the default wrapper) + // We define a variable to check whether we still have to remove the fixed height + let container_height = PLOT.layout_size.height ?? PLOT.container_height ?? 400 + CONTAINER.style.height = container_height + 'px' + Plotly.react(PLOT, _plot_obj) + return CONTAINER +} + +export function lol(a = globalThis.asd) { + console.log("LOL: ", a) +} \ No newline at end of file diff --git a/js/src/resizer.js b/js/src/resizer.js new file mode 100644 index 0000000..7af1c93 --- /dev/null +++ b/js/src/resizer.js @@ -0,0 +1,207 @@ +//@ts-check +/** + * Retrieves the padding and border information for an HTML element from the compute element style, to be used for resizing the plot container. + * + * @param {HTMLElement} el - The element to retrieve offset data for. + * @return {import("./typedef.js").PaddingBorderData} The object containing padding and border information. + */ +function getOffsetData(el) { + const cs = globalThis.getComputedStyle(el, null); + const odata = { + padding: { + left: parseFloat(cs.paddingLeft), + right: parseFloat(cs.paddingRight), + top: parseFloat(cs.paddingTop), + bottom: parseFloat(cs.paddingBottom), + width: parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight), + height: parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom), + }, + border: { + left: parseFloat(cs.borderLeftWidth), + right: parseFloat(cs.borderRightWidth), + top: parseFloat(cs.borderTopWidth), + bottom: parseFloat(cs.borderBottomWidth), + width: parseFloat(cs.borderLeftWidth) + parseFloat(cs.borderRightWidth), + height: parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth), + }, + }; + return odata; +} +/** + * Retrieves relevant data for the size of the CONTAINER and of the its associated PLOT object + * + * @param {import("./typedef.js").Container} CONTAINER - The container object. + * @return {import("./typedef.js").SizeData} The size data for the container and plot. + */ +export function getSizeData(CONTAINER) { + const { PLOT } = CONTAINER; + const container_pad = getOffsetData(CONTAINER); + const plot_pad = { + ...getOffsetData(PLOT), + offset: { + top: PLOT.offsetParent == CONTAINER ? PLOT.offsetTop : 0, + left: PLOT.offsetParent == CONTAINER ? PLOT.offsetLeft : 0, + }, + }; + const data = { + plot_pad, + plot_rect: PLOT.getBoundingClientRect(), + container_pad, + container_rect: CONTAINER.getBoundingClientRect(), + }; + return data; +} +/** + * Computes the required size of the CONTAINER based on the target size of the PLOT. + * + * @param {import("./typedef.js").Container} CONTAINER - The container element. + * @param {Object} targetSize - The target size object. + * @param {number} [targetSize.width] - The target width of the container. + * @param {number} [targetSize.height] - The target height of the container. + * @param {import("./typedef.js").SizeData} currentSizeData - The current size data object. + * @return {{ + * width: number, + * height: number, + * noChange: boolean + * }} The computed size object. + */ +function computeContainerSize(CONTAINER, targetSize = {}, currentSizeData = getSizeData(CONTAINER)) { + const computed_size = computePlotSize(currentSizeData); + const offsets = computed_size.offsets; + + const width = (targetSize.width ?? computed_size.width) + offsets.width + const height = (targetSize.height ?? computed_size.height) + offsets.height + const noChange = targetSize.width === undefined && targetSize.height === undefined + + return { + width, + height, + noChange, + }; +} + +/** + * Changes the size of the CONTAINER based on the provided target size. + * + * @param {import("./typedef.js").Container} CONTAINER - The container element. + * @param {Object} targetSize - The target size object. + * @param {number} [targetSize.width] - The target width of the container. + * @param {number} [targetSize.height] - The target height of the container. + * @param {import("./typedef.js").SizeData} currentSizeData - The current size data object. + */ +function changeContainerSize( + CONTAINER, + targetSize, + currentSizeData +) { + if (!CONTAINER.isPoppedOut()) { + console.log("Tried to change container size when not popped, ignoring"); + return; + } + + const csz = computeContainerSize(CONTAINER, targetSize, currentSizeData); + + if (csz.noChange) { + console.log("Size is the same as current, ignoring"); + return; + } + // We are now going to set he width and height of the container + for (const key of ["width", "height"]) { + CONTAINER.style.setProperty(key, csz[key] + "px"); + } +} +// We now create the function that will update the plot based on the values specified +function updateFromHeader() { + const header_data = { + height: config_spans.height.ui_value, + width: config_spans.width.ui_value, + }; + changeContainerSize(header_data); +} +// We assign this function to the onblur event of width and height +if (firstRun) { + for (const container of Object.values(config_spans)) { + container.ui_span.onblur = (e) => { + container.ui_value = container.ui_span.textContent; + updateFromHeader(); + }; + } +} +// This function computes +/** + * Computes the plot size to use for relayout as a function of the container size. + * + * @param {import("./typedef.js").SizeData} sizeData - The current size data object. + */ +function computePlotSize(sizeData) { + // Remove Padding + const { container_pad, plot_pad, container_rect } = sizeData; + const offsets = { + width: + plot_pad.padding.width + + plot_pad.border.width + + plot_pad.offset.left + + container_pad.padding.width + + container_pad.border.width, + height: + plot_pad.padding.height + + plot_pad.border.height + + plot_pad.offset.top + + container_pad.padding.height + + container_pad.border.height, + }; + const sz = { + width: Math.round(container_rect.width - offsets.width), + height: Math.round(container_rect.height - offsets.height), + offsets, + }; + return sz; +} + +// Create the resizeObserver to make the plot even more responsive! :magic: +/** + * Computes the required size of the CONTAINER based on the target size of the PLOT. + * + * @param {import("./typedef.js").Container} CONTAINER - The container element. + * @param {import("./typedef.js").Plotly} Plotly - The Plotly library. + */ +export function addResizeObserver(CONTAINER) { +const resizeObserver = new ResizeObserver(() => { + const sizeData = getSizeData(CONTAINER); + const { PLOT, CLIPBOARD_HEADER, Plotly } = CONTAINER + const { container_rect } = sizeData; + const plot_size = computePlotSize(sizeData); + // We save the height in the PLOT object + PLOT.container_height = container_rect.height; + // We deal with some stuff if the container is poppped + CLIPBOARD_HEADER.style.width = container_rect.width + "px"; + CLIPBOARD_HEADER.style.left = container_rect.left + "px"; + config_spans.height.ui_value = plot_size.height; + config_spans.width.ui_value = plot_size.width; + /* + The addition of the invalid argument `plutoresize` seems to fix the problem with calling `relayout` simply with `{autosize: true}` as update breaking mouse relayout events tracking. + See https://github.com/plotly/plotly.js/issues/6156 for details + */ + let config = { + // If this is popped out, we ignore the original width/height + width: + (CONTAINER.isPoppedOut() ? undefined : original_width) ?? plot_size.width, + height: + (CONTAINER.isPoppedOut() ? undefined : original_height) ?? + plot_size.height, + plutoresize: true, + }; + Plotly.relayout(PLOT, config).then(() => { + if (remove_container_size && !CONTAINER.isPoppedOut()) { + // This is needed to avoid the first resize upon plot creation to already be without a fixed height + CONTAINER.style.height = ""; + CONTAINER.style.width = ""; + remove_container_size = false; + } + }); +}); + resizeObserver.observe(CONTAINER); + // Save the resizer inside the CONTAINER + CONTAINER.resizeObserver = resizeObserver; +} + diff --git a/js/styles.js b/js/src/styles.js similarity index 96% rename from js/styles.js rename to js/src/styles.js index 5957b1b..cff905a 100644 --- a/js/styles.js +++ b/js/src/styles.js @@ -1,7 +1,6 @@ -import { css } from "https://esm.sh/@emotion/css@11.11.2"; +import { css } from "./url_imports.js"; -export function addContainerStyle(CONTAINER) { - CONTAINER.classList.add(css` +const CONTAINER_STYLE = css` .plutoplotly-container { width: 100%; height: 100%; @@ -137,5 +136,7 @@ export function addContainerStyle(CONTAINER) { color: var(--cm-tag-color); font-weight: bold; } - `); + ` +export function addContainerStyle(CONTAINER) { + CONTAINER.classList.add(CONTAINER_STYLE); } \ No newline at end of file diff --git a/js/src/typedef.js b/js/src/typedef.js new file mode 100644 index 0000000..fa850f4 --- /dev/null +++ b/js/src/typedef.js @@ -0,0 +1,72 @@ +// The type defined here should be picked up by vscode thanks the jsconfig.json as explained in https://stackoverflow.com/questions/45836847/how-to-get-vs-code-to-understand-jsdocs-typedef-across-multiple-files +// Unfortunately this does not seem to work without the explicit import + +// Plotly type +/** + * @typedef {import('https://esm.sh/@types/plotly.js')} Plotly + */ + +// resizer.js types +/** + * @typedef {Object} BasicSizeData + * @property {number} left - The left offset + * @property {number} right - The right offset + * @property {number} top - The top offset + * @property {number} bottom - The bottom offset + * @property {number} width - The width of the element + * @property {number} height - The height of the element + */ + +/** + * @typedef {Object} PaddingBorderData + * @property {BasicSizeData} padding - The padding data + * @property {BasicSizeData} border - The border data + */ + +/** + * @typedef {Object} PaddingBorderOffsetData + * @property {BasicSizeData} padding - The padding data + * @property {BasicSizeData} border - The border data + * @property {Object} offset - The offset data + * @property {number} offset.top - The top offset + * @property {number} offset.left - The left offset + */ + +/** + * @typedef {Object} SizeData + * @property {PaddingBorderOffsetData} plot_pad - The padding and border data for the plot + * @property {DOMRect} plot_rect - The rect data for the plot + * @property {PaddingBorderData} container_pad - The padding and border data for the container + * @property {DOMRect} container_rect - The rect data for the container + */ + +/** + * @typedef {Object} ComputedSizeData + * @property {number} width - The width of the container element + * @property {number} height - The height of the container element + * @property {boolean} noChange - A flag specifying whether the provided target size was the same as the current plot size + */ + +/** + * The additional properties and methods attached to the HTML element `el` where the plotly.js plot is created (using `Plotly.react(el, ...plot_data)`) + * @typedef {Object} PlotProps + * @property {{ + * width: number, + * height: number, + * }} layout_size + */ + +/** + * @typedef {Object} ContainerProps + * @property {Function} isPoppedOut - Returns true if the container is popped out + * @property {ResizeObserver} resizeObserver - The resizeObserver controlling the resizing of the CONTAINER and PLOT + * @property {AbortController} controller - The AbortController used to stop all listeners and observers tied to the CONTAINER + * @property {Plotly} Plotly - The Plotly library used in this Container + * @property {HTMLElement & PlotProps} PLOT - The child div containint the plotly.js plot + * @property {Element | null} CLIPBOARD_HEADER - The header containing all the clipboard related config spans + * @property {*} [value] - The eventual value of the CONTAINER to be used for `@bind` inside Pluto + */ + +/** + * @typedef {ContainerProps & HTMLElement} Container + */ diff --git a/js/src/url_imports.js b/js/src/url_imports.js new file mode 100644 index 0000000..41d562b --- /dev/null +++ b/js/src/url_imports.js @@ -0,0 +1,7 @@ +// We put all url imports here to have them in a single place +import { css } from "https://esm.sh/@emotion/css@11.11.2"; +import lodash from "https://esm.sh/lodash-es@4.17.21"; +import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; +import { default as interact } from "https://esm.sh/interactjs@1.10.26" + +export { css, html, lodash, interact } \ No newline at end of file diff --git a/js/src/utils.js b/js/src/utils.js new file mode 100644 index 0000000..163cac1 --- /dev/null +++ b/js/src/utils.js @@ -0,0 +1,30 @@ +import { lodash as _ } from "./url_imports.js" + +// We use lodash for this for compactness +function removeTypedArray(o) { + return _.isTypedArray(o) + ? Array.from(o) + : _.isPlainObject(o) + ? _.mapValues(o, removeTypedArray) + : o; +} + +export function processPlotObj(plot_obj) { + return _.update(plot_obj, "layout", removeTypedArray) +} + +export function getImageOptions(plot_obj) { + const o = plot_obj.config.toImageButtonOptions ?? {}; + return { + format: o.format ?? "png", + width: o.width ?? original_width, + height: o.height ?? original_height, + scale: o.scale ?? 1, + filename: o.filename ?? "newplot", + }; +} + +// We create a Promise version of setTimeout +export function delay(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} \ No newline at end of file From 5fa8f297692473d6dfea567324ea8902bd38c67f Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Thu, 4 Apr 2024 14:46:38 +0200 Subject: [PATCH 12/39] status before proable bun migration --- js/src/clipboard.js | 108 ++++++++++++++++++++++++++++-------------- js/src/container.js | 4 +- js/src/prehooks.js | 3 +- js/src/styles.js | 10 ++-- js/src/test.js | 3 ++ js/src/url_imports.js | 13 ++++- js/src/utils.js | 6 +-- 7 files changed, 99 insertions(+), 48 deletions(-) create mode 100644 js/src/test.js diff --git a/js/src/clipboard.js b/js/src/clipboard.js index 36ab1fb..57dfcb5 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -1,6 +1,41 @@ -import { interact, html } from "./url_imports.js" -import { delay, getImageOptions } from "./utils.js" +import { interact, html } from "./url_imports.js"; +import { delay, getImageOptions } from "./utils.js"; +/** + * Function to add a configuration span element. + * + * @param {HTMLElement} CLIPBOARD_HEADER - Clipboard Header element + * @param {string} name - lowercase name of the config span + */ +function addConfigSpan(CLIPBOARD_HEADER, name) { + const container = html` ${name.toUpperCase()}:`; + container.label = container.querySelector(".label"); + container.ui_span = container.label.querySelector(".clipboard-value"); + container.config_span = html`` + container.label.insertAdjacentElement('afterbegin', container.config_span); + container.key = name; + CLIPBOARD_HEADER.config_spans[name] = container + CLIPBOARD_HEADER.insertAdjacentElement('beforeend', container) + // We put some convenience getters/setters + // ui_value forward + Object.defineProperty(container, "ui_value", { + get: () => container.ui_span.value, + set: (val) => { + container.ui_span.value = val; + }, + }); + // config_value forward + Object.defineProperty(container, "config_value", { + get: () => container.config_span.value, + set: (val) => { + container.config_span.value = val; + }, + }); + return +} /** * Adds a clipboard header to the given container. @@ -10,35 +45,36 @@ import { delay, getImageOptions } from "./utils.js" */ export function addClipboardHeader(CONTAINER) { // Return if the CLIPBOARD HEADER has been assigned already - if (CONTAINER.CLIPBOARD_HEADER !== undefined) return -const CLIPBOARD_HEADER = html`` - CLIPBOARD_HEADER.config_spans = {} + if (CONTAINER.CLIPBOARD_HEADER !== undefined) return; + const CLIPBOARD_HEADER = html``; + CLIPBOARD_HEADER.config_spans = {}; } - function checkConfigSync(container) { const valid_classes = [ "missing-config", @@ -121,8 +157,8 @@ function initializeUIValueSpan(span, key, value) { }, }); // We also assign a listener so that the editable is blurred when enter is pressed - span.onkeydown = (/** @type {KeyboardEvent} */e) => { - if (e.key === 'Enter') { + span.onkeydown = (/** @type {KeyboardEvent} */ e) => { + if (e.key === "Enter") { e.preventDefault(); span.blur(); } @@ -323,11 +359,13 @@ function copyImageToClipboard() { fetch(dataUrl) .then((res) => res.blob()) .then((blob) => { - const paste_receiver = document.querySelector('paste-receiver.plutoplotly') + const paste_receiver = document.querySelector( + "paste-receiver.plutoplotly" + ); if (paste_receiver) { - paste_receiver.attachImage(dataUrl, CONTAINER) + paste_receiver.attachImage(dataUrl, CONTAINER); } - sendToClipboard(blob) + sendToClipboard(blob); }); }); } @@ -482,4 +520,4 @@ plot_obj.config.modeBarButtonsToAdd = _.union( }), }, ] -); \ No newline at end of file +); diff --git a/js/src/container.js b/js/src/container.js index d4b97ff..034ceb9 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -1,6 +1,6 @@ //@ts-check import { html } from "./url_imports.js"; -import { addClipboardHeader } from "./clipboard.js"; +// import { addClipboardHeader } from "./clipboard.js"; import { addContainerStyle } from "./styles.js"; /** * Creates the Container element used by PlutoPlotly to wrap the plotly.js plot and adds additional functionality like resizing and enhanced clipboard. @@ -14,7 +14,7 @@ export function makeContainer(Plotly = globalThis.Plotly) { // Add the style to it addContainerStyle(CONTAINER); // Add the clipboard header - addClipboardHeader(CONTAINER); + // addClipboardHeader(CONTAINER); // Create the child div that will contain the actual plot const PLOT = CONTAINER.PLOT = CONTAINER.appendChild( html`
` diff --git a/js/src/prehooks.js b/js/src/prehooks.js index 4c53d81..4a4db3b 100644 --- a/js/src/prehooks.js +++ b/js/src/prehooks.js @@ -1,12 +1,13 @@ // This file contains utilities to be executed before calling the plot function. import { html, lodash as _ } from "./url_imports.js" import { makeContainer } from "./container.js"; +import { processPlotObj } from "./utils.js"; // We start by putting all the variable interpolation here at the beginning // We have to convert all typedarrays in the layout to normal arrays. See Issue #25 export function createPlot(plot_obj, Plotly = globalThis.Plotly) { - const CONTAINER = makeContainer(); + const CONTAINER = makeContainer(Plotly); const { PLOT } = CONTAINER; // Record or update the layout width/height if provided explicitly PLOT.layout_size = { diff --git a/js/src/styles.js b/js/src/styles.js index cff905a..95ddb56 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -1,16 +1,16 @@ import { css } from "./url_imports.js"; const CONTAINER_STYLE = css` - .plutoplotly-container { + & { width: 100%; height: 100%; min-height: 0; min-width: 0; } - .plutoplotly-container .js-plotly-plot .plotly div { + & .js-plotly-plot .plotly div { margin: 0 auto; // This centers the plot } - .plutoplotly-container.popped-out { + &.popped-out { overflow: auto; z-index: 1000; position: fixed; @@ -59,7 +59,7 @@ const CONTAINER_STYLE = css` margin-top: 5px; display: none; } - .plutoplotly-container.filesave .clipboard-span.filename { + &.filesave .clipboard-span.filename { display: inline-block; } .clipboard-value.filename { @@ -67,7 +67,7 @@ const CONTAINER_STYLE = css` text-align: left; min-width: min(60%, min-content); } - .plutoplotly-container.filesave .clipboard-span.format { + &.filesave .clipboard-span.format { display: inline-flex; } .clipboard-span.format .label { diff --git a/js/src/test.js b/js/src/test.js new file mode 100644 index 0000000..75ca522 --- /dev/null +++ b/js/src/test.js @@ -0,0 +1,3 @@ +import createEmotion from "https://esm.sh/@emotion/css@11.11.2/create-instance"; + +export default createEmotion \ No newline at end of file diff --git a/js/src/url_imports.js b/js/src/url_imports.js index 41d562b..6ccd67e 100644 --- a/js/src/url_imports.js +++ b/js/src/url_imports.js @@ -1,7 +1,16 @@ // We put all url imports here to have them in a single place -import { css } from "https://esm.sh/@emotion/css@11.11.2"; +import createEmotion from "https://esm.sh/@emotion/css@11.11.2/create-instance"; import lodash from "https://esm.sh/lodash-es@4.17.21"; import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; import { default as interact } from "https://esm.sh/interactjs@1.10.26" -export { css, html, lodash, interact } \ No newline at end of file +// We need to create our custom emotion instance while loading the module to avoid +const emotion = createEmotion( + { + key: 'css', + container: globalThis.document, + } +); + +export const css = emotion.css +export { createEmotion, emotion, html, lodash, interact } \ No newline at end of file diff --git a/js/src/utils.js b/js/src/utils.js index 163cac1..a88172c 100644 --- a/js/src/utils.js +++ b/js/src/utils.js @@ -14,11 +14,11 @@ export function processPlotObj(plot_obj) { } export function getImageOptions(plot_obj) { - const o = plot_obj.config.toImageButtonOptions ?? {}; + const o = plot_obj?.config?.toImageButtonOptions ?? {}; return { format: o.format ?? "png", - width: o.width ?? original_width, - height: o.height ?? original_height, + width: o.width ?? undefined, + height: o.height ?? undefined, scale: o.scale ?? 1, filename: o.filename ?? "newplot", }; From 555a4e74c0472a6ca96be1269c3a21f6f496c3a9 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Thu, 4 Apr 2024 15:24:31 +0200 Subject: [PATCH 13/39] fix @emotion/css bug with import map --- js/bundle.js | 9 +- js/deno.jsonc | 9 + js/deno.lock | 416 +++++++++++++++++++++++++++++++++++++++++- js/src/url_imports.js | 12 +- 4 files changed, 433 insertions(+), 13 deletions(-) diff --git a/js/bundle.js b/js/bundle.js index 3df7cf2..3cf7d01 100644 --- a/js/bundle.js +++ b/js/bundle.js @@ -3,10 +3,17 @@ import * as esbuild from "npm:esbuild@0.20.2"; // permitted, such as Deno Deploy, or when running without `--allow-run`. // import * as esbuild from "https://deno.land/x/esbuild@0.20.2/wasm.js"; +// the plugin wants an absolute path for the deno config so we have to compute the directory of this file. +// Pointing to the correct config is needed to use the import map linking the @emotion/css url to the npm version of the package to fix a bug with the library not realizing it is inside a browser otherwise. +import * as path from "https://deno.land/std@0.207.0/path/mod.ts"; +const __dirname = path.dirname(path.fromFileUrl(import.meta.url)); +const __config = path.join(__dirname, "deno.jsonc"); + import { denoPlugins } from "jsr:@luca/esbuild-deno-loader@^0.10.3"; +// import { processPlotObj } from "./src/utils.js"; const result = await esbuild.build({ - plugins: [...denoPlugins()], + plugins: [...denoPlugins({configPath: __config})], entryPoints: ["./src/prehooks.js"], outfile: "./dist/library.esm.min.js", bundle: true, diff --git a/js/deno.jsonc b/js/deno.jsonc index 0f7b060..d291248 100644 --- a/js/deno.jsonc +++ b/js/deno.jsonc @@ -8,5 +8,14 @@ }, "tasks": { "bundle": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run bundle.js" + }, + "imports": { + /* We create an import map for the URL as the bundling with the url directly + breaks @emotion/css as it does not recognize it inside a browser. + Strangely enough, when using the package from npm instead of esm.sh + directly it bundles fine. The direct inclusion from esm.sh from the + browser also works fine so it seems to be just a bundling problem probably + tied to how deno-esbuild-loader works? */ + "https://esm.sh/@emotion/css@11.11.2": "npm:@emotion/css@11.11.2", } } \ No newline at end of file diff --git a/js/deno.lock b/js/deno.lock index b0d0236..6d0acf6 100644 --- a/js/deno.lock +++ b/js/deno.lock @@ -5,8 +5,10 @@ "jsr:@luca/esbuild-deno-loader@^0.10.3": "jsr:@luca/esbuild-deno-loader@0.10.3", "jsr:@std/assert@^0.213.1": "jsr:@std/assert@0.213.1", "jsr:@std/encoding@0.213": "jsr:@std/encoding@0.213.1", + "jsr:@std/json@^0.213.1": "jsr:@std/json@0.213.1", "jsr:@std/jsonc@0.213": "jsr:@std/jsonc@0.213.1", "jsr:@std/path@0.213": "jsr:@std/path@0.213.1", + "npm:@emotion/css@11.11.2": "npm:@emotion/css@11.11.2", "npm:esbuild@0.20.2": "npm:esbuild@0.20.2" }, "jsr": { @@ -24,10 +26,14 @@ "@std/encoding@0.213.1": { "integrity": "fcbb6928713dde941a18ca5db88ca1544d0755ec8fb20fe61e2dc8144b390c62" }, + "@std/json@0.213.1": { + "integrity": "f572b1de605d07c4a5602445dac54bfc51b1fb87a3710a17aed2608bfca54e68" + }, "@std/jsonc@0.213.1": { "integrity": "5578f21aa583b7eb7317eed077ffcde47b294f1056bdbb9aacec407758637bfe", "dependencies": [ - "jsr:@std/assert@^0.213.1" + "jsr:@std/assert@^0.213.1", + "jsr:@std/json@^0.213.1" ] }, "@std/path@0.213.1": { @@ -38,6 +44,120 @@ } }, "npm": { + "@babel/code-frame@7.24.2": { + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dependencies": { + "@babel/highlight": "@babel/highlight@7.24.2", + "picocolors": "picocolors@1.0.0" + } + }, + "@babel/helper-module-imports@7.24.3": { + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dependencies": { + "@babel/types": "@babel/types@7.24.0" + } + }, + "@babel/helper-string-parser@7.24.1": { + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dependencies": {} + }, + "@babel/helper-validator-identifier@7.22.20": { + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dependencies": {} + }, + "@babel/highlight@7.24.2": { + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "dependencies": { + "@babel/helper-validator-identifier": "@babel/helper-validator-identifier@7.22.20", + "chalk": "chalk@2.4.2", + "js-tokens": "js-tokens@4.0.0", + "picocolors": "picocolors@1.0.0" + } + }, + "@babel/runtime@7.24.1": { + "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "dependencies": { + "regenerator-runtime": "regenerator-runtime@0.14.1" + } + }, + "@babel/types@7.24.0": { + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "dependencies": { + "@babel/helper-string-parser": "@babel/helper-string-parser@7.24.1", + "@babel/helper-validator-identifier": "@babel/helper-validator-identifier@7.22.20", + "to-fast-properties": "to-fast-properties@2.0.0" + } + }, + "@emotion/babel-plugin@11.11.0": { + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "@babel/helper-module-imports@7.24.3", + "@babel/runtime": "@babel/runtime@7.24.1", + "@emotion/hash": "@emotion/hash@0.9.1", + "@emotion/memoize": "@emotion/memoize@0.8.1", + "@emotion/serialize": "@emotion/serialize@1.1.3", + "babel-plugin-macros": "babel-plugin-macros@3.1.0", + "convert-source-map": "convert-source-map@1.9.0", + "escape-string-regexp": "escape-string-regexp@4.0.0", + "find-root": "find-root@1.1.0", + "source-map": "source-map@0.5.7", + "stylis": "stylis@4.2.0" + } + }, + "@emotion/cache@11.11.0": { + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "@emotion/memoize@0.8.1", + "@emotion/sheet": "@emotion/sheet@1.2.2", + "@emotion/utils": "@emotion/utils@1.2.1", + "@emotion/weak-memoize": "@emotion/weak-memoize@0.3.1", + "stylis": "stylis@4.2.0" + } + }, + "@emotion/css@11.11.2": { + "integrity": "sha512-VJxe1ucoMYMS7DkiMdC2T7PWNbrEI0a39YRiyDvK2qq4lXwjRbVP/z4lpG+odCsRzadlR+1ywwrTzhdm5HNdew==", + "dependencies": { + "@emotion/babel-plugin": "@emotion/babel-plugin@11.11.0", + "@emotion/cache": "@emotion/cache@11.11.0", + "@emotion/serialize": "@emotion/serialize@1.1.3", + "@emotion/sheet": "@emotion/sheet@1.2.2", + "@emotion/utils": "@emotion/utils@1.2.1" + } + }, + "@emotion/hash@0.9.1": { + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==", + "dependencies": {} + }, + "@emotion/memoize@0.8.1": { + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "dependencies": {} + }, + "@emotion/serialize@1.1.3": { + "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", + "dependencies": { + "@emotion/hash": "@emotion/hash@0.9.1", + "@emotion/memoize": "@emotion/memoize@0.8.1", + "@emotion/unitless": "@emotion/unitless@0.8.1", + "@emotion/utils": "@emotion/utils@1.2.1", + "csstype": "csstype@3.1.3" + } + }, + "@emotion/sheet@1.2.2": { + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==", + "dependencies": {} + }, + "@emotion/unitless@0.8.1": { + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "dependencies": {} + }, + "@emotion/utils@1.2.1": { + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==", + "dependencies": {} + }, + "@emotion/weak-memoize@0.3.1": { + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", + "dependencies": {} + }, "@esbuild/aix-ppc64@0.20.2": { "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", "dependencies": {} @@ -130,6 +250,70 @@ "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "dependencies": {} }, + "@types/parse-json@4.0.2": { + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dependencies": {} + }, + "ansi-styles@3.2.1": { + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "color-convert@1.9.3" + } + }, + "babel-plugin-macros@3.1.0": { + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "@babel/runtime@7.24.1", + "cosmiconfig": "cosmiconfig@7.1.0", + "resolve": "resolve@1.22.8" + } + }, + "callsites@3.1.0": { + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dependencies": {} + }, + "chalk@2.4.2": { + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "ansi-styles@3.2.1", + "escape-string-regexp": "escape-string-regexp@1.0.5", + "supports-color": "supports-color@5.5.0" + } + }, + "color-convert@1.9.3": { + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "color-name@1.1.3" + } + }, + "color-name@1.1.3": { + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dependencies": {} + }, + "convert-source-map@1.9.0": { + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dependencies": {} + }, + "cosmiconfig@7.1.0": { + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "@types/parse-json@4.0.2", + "import-fresh": "import-fresh@3.3.0", + "parse-json": "parse-json@5.2.0", + "path-type": "path-type@4.0.0", + "yaml": "yaml@1.10.2" + } + }, + "csstype@3.1.3": { + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dependencies": {} + }, + "error-ex@1.3.2": { + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "is-arrayish@0.2.1" + } + }, "esbuild@0.20.2": { "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "dependencies": { @@ -157,6 +341,130 @@ "@esbuild/win32-ia32": "@esbuild/win32-ia32@0.20.2", "@esbuild/win32-x64": "@esbuild/win32-x64@0.20.2" } + }, + "escape-string-regexp@1.0.5": { + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dependencies": {} + }, + "escape-string-regexp@4.0.0": { + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dependencies": {} + }, + "find-root@1.1.0": { + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dependencies": {} + }, + "function-bind@1.1.2": { + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dependencies": {} + }, + "has-flag@3.0.0": { + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dependencies": {} + }, + "hasown@2.0.2": { + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "function-bind@1.1.2" + } + }, + "import-fresh@3.3.0": { + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "parent-module@1.0.1", + "resolve-from": "resolve-from@4.0.0" + } + }, + "is-arrayish@0.2.1": { + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dependencies": {} + }, + "is-core-module@2.13.1": { + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "hasown@2.0.2" + } + }, + "js-tokens@4.0.0": { + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dependencies": {} + }, + "json-parse-even-better-errors@2.3.1": { + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dependencies": {} + }, + "lines-and-columns@1.2.4": { + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dependencies": {} + }, + "parent-module@1.0.1": { + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "callsites@3.1.0" + } + }, + "parse-json@5.2.0": { + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "@babel/code-frame@7.24.2", + "error-ex": "error-ex@1.3.2", + "json-parse-even-better-errors": "json-parse-even-better-errors@2.3.1", + "lines-and-columns": "lines-and-columns@1.2.4" + } + }, + "path-parse@1.0.7": { + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dependencies": {} + }, + "path-type@4.0.0": { + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dependencies": {} + }, + "picocolors@1.0.0": { + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dependencies": {} + }, + "regenerator-runtime@0.14.1": { + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dependencies": {} + }, + "resolve-from@4.0.0": { + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dependencies": {} + }, + "resolve@1.22.8": { + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "is-core-module@2.13.1", + "path-parse": "path-parse@1.0.7", + "supports-preserve-symlinks-flag": "supports-preserve-symlinks-flag@1.0.0" + } + }, + "source-map@0.5.7": { + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dependencies": {} + }, + "stylis@4.2.0": { + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "dependencies": {} + }, + "supports-color@5.5.0": { + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "has-flag@3.0.0" + } + }, + "supports-preserve-symlinks-flag@1.0.0": { + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dependencies": {} + }, + "to-fast-properties@2.0.0": { + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dependencies": {} + }, + "yaml@1.10.2": { + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dependencies": {} } } }, @@ -165,5 +473,109 @@ "https://esm.sh/v135/@types/lodash-es@~4.17/index.d.ts": "https://esm.sh/v135/@types/lodash-es@4.17.12/index.d.ts", "https://esm.sh/v135/@types/lodash@4.14.202/index": "https://esm.sh/v135/@types/lodash@4.14.202/index~.d.ts" }, - "remote": {} + "remote": { + "https://deno.land/std@0.207.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.207.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.207.0/path/_common/assert_path.ts": "061e4d093d4ba5aebceb2c4da3318bfe3289e868570e9d3a8e327d91c2958946", + "https://deno.land/std@0.207.0/path/_common/basename.ts": "0d978ff818f339cd3b1d09dc914881f4d15617432ae519c1b8fdc09ff8d3789a", + "https://deno.land/std@0.207.0/path/_common/common.ts": "9e4233b2eeb50f8b2ae10ecc2108f58583aea6fd3e8907827020282dc2b76143", + "https://deno.land/std@0.207.0/path/_common/constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.207.0/path/_common/dirname.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.207.0/path/_common/format.ts": "11aa62e316dfbf22c126917f5e03ea5fe2ee707386555a8f513d27ad5756cf96", + "https://deno.land/std@0.207.0/path/_common/from_file_url.ts": "ef1bf3197d2efbf0297a2bdbf3a61d804b18f2bcce45548ae112313ec5be3c22", + "https://deno.land/std@0.207.0/path/_common/glob_to_reg_exp.ts": "5c3c2b79fc2294ec803d102bd9855c451c150021f452046312819fbb6d4dc156", + "https://deno.land/std@0.207.0/path/_common/normalize.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.207.0/path/_common/normalize_string.ts": "88c472f28ae49525f9fe82de8c8816d93442d46a30d6bb5063b07ff8a89ff589", + "https://deno.land/std@0.207.0/path/_common/relative.ts": "1af19d787a2a84b8c534cc487424fe101f614982ae4851382c978ab2216186b4", + "https://deno.land/std@0.207.0/path/_common/strip_trailing_separators.ts": "7ffc7c287e97bdeeee31b155828686967f222cd73f9e5780bfe7dfb1b58c6c65", + "https://deno.land/std@0.207.0/path/_common/to_file_url.ts": "a8cdd1633bc9175b7eebd3613266d7c0b6ae0fb0cff24120b6092ac31662f9ae", + "https://deno.land/std@0.207.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.207.0/path/_os.ts": "30b0c2875f360c9296dbe6b7f2d528f0f9c741cecad2e97f803f5219e91b40a2", + "https://deno.land/std@0.207.0/path/basename.ts": "04bb5ef3e86bba8a35603b8f3b69537112cdd19ce64b77f2522006da2977a5f3", + "https://deno.land/std@0.207.0/path/common.ts": "f4d061c7d0b95a65c2a1a52439edec393e906b40f1caf4604c389fae7caa80f5", + "https://deno.land/std@0.207.0/path/dirname.ts": "88a0a71c21debafc4da7a4cd44fd32e899462df458fbca152390887d41c40361", + "https://deno.land/std@0.207.0/path/extname.ts": "2da4e2490f3b48b7121d19fb4c91681a5e11bd6bd99df4f6f47d7a71bb6ecdf2", + "https://deno.land/std@0.207.0/path/format.ts": "3457530cc85d1b4bab175f9ae73998b34fd456c830d01883169af0681b8894fb", + "https://deno.land/std@0.207.0/path/from_file_url.ts": "e7fa233ea1dff9641e8d566153a24d95010110185a6f418dd2e32320926043f8", + "https://deno.land/std@0.207.0/path/glob_to_regexp.ts": "74d7448c471e293d03f05ccb968df4365fed6aaa508506b6325a8efdc01d8271", + "https://deno.land/std@0.207.0/path/is_absolute.ts": "67232b41b860571c5b7537f4954c88d86ae2ba45e883ee37d3dec27b74909d13", + "https://deno.land/std@0.207.0/path/is_glob.ts": "567dce5c6656bdedfc6b3ee6c0833e1e4db2b8dff6e62148e94a917f289c06ad", + "https://deno.land/std@0.207.0/path/join.ts": "98d3d76c819af4a11a81d5ba2dbb319f1ce9d63fc2b615597d4bcfddd4a89a09", + "https://deno.land/std@0.207.0/path/join_globs.ts": "9b84d5103b63d3dbed4b2cf8b12477b2ad415c7d343f1488505162dc0e5f4db8", + "https://deno.land/std@0.207.0/path/mod.ts": "3defabebc98279e62b392fee7a6937adc932a8f4dcd2471441e36c15b97b00e0", + "https://deno.land/std@0.207.0/path/normalize.ts": "aa95be9a92c7bd4f9dc0ba51e942a1973e2b93d266cd74f5ca751c136d520b66", + "https://deno.land/std@0.207.0/path/normalize_glob.ts": "674baa82e1c00b6cb153bbca36e06f8e0337cb8062db6d905ab5de16076ca46b", + "https://deno.land/std@0.207.0/path/parse.ts": "d87ff0deef3fb495bc0d862278ff96da5a06acf0625ca27769fc52ac0d3d6ece", + "https://deno.land/std@0.207.0/path/posix/_util.ts": "ecf49560fedd7dd376c6156cc5565cad97c1abe9824f4417adebc7acc36c93e5", + "https://deno.land/std@0.207.0/path/posix/basename.ts": "a630aeb8fd8e27356b1823b9dedd505e30085015407caa3396332752f6b8406a", + "https://deno.land/std@0.207.0/path/posix/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", + "https://deno.land/std@0.207.0/path/posix/dirname.ts": "f48c9c42cc670803b505478b7ef162c7cfa9d8e751b59d278b2ec59470531472", + "https://deno.land/std@0.207.0/path/posix/extname.ts": "ee7f6571a9c0a37f9218fbf510c440d1685a7c13082c348d701396cc795e0be0", + "https://deno.land/std@0.207.0/path/posix/format.ts": "b94876f77e61bfe1f147d5ccb46a920636cd3cef8be43df330f0052b03875968", + "https://deno.land/std@0.207.0/path/posix/from_file_url.ts": "b97287a83e6407ac27bdf3ab621db3fccbf1c27df0a1b1f20e1e1b5acf38a379", + "https://deno.land/std@0.207.0/path/posix/glob_to_regexp.ts": "6ed00c71fbfe0ccc35977c35444f94e82200b721905a60bd1278b1b768d68b1a", + "https://deno.land/std@0.207.0/path/posix/is_absolute.ts": "159900a3422d11069d48395568217eb7fc105ceda2683d03d9b7c0f0769e01b8", + "https://deno.land/std@0.207.0/path/posix/is_glob.ts": "ec4fbc604b9db8487f7b56ab0e759b24a971ab6a45f7b0b698bc39b8b9f9680f", + "https://deno.land/std@0.207.0/path/posix/join.ts": "0c0d84bdc344876930126640011ec1b888e6facf74153ffad9ef26813aa2a076", + "https://deno.land/std@0.207.0/path/posix/join_globs.ts": "f4838d54b1f60a34a40625a3293f6e583135348be1b2974341ac04743cb26121", + "https://deno.land/std@0.207.0/path/posix/mod.ts": "f1b08a7f64294b7de87fc37190d63b6ce5b02889af9290c9703afe01951360ae", + "https://deno.land/std@0.207.0/path/posix/normalize.ts": "11de90a94ab7148cc46e5a288f7d732aade1d616bc8c862f5560fa18ff987b4b", + "https://deno.land/std@0.207.0/path/posix/normalize_glob.ts": "10a1840c628ebbab679254d5fa1c20e59106102354fb648a1765aed72eb9f3f9", + "https://deno.land/std@0.207.0/path/posix/parse.ts": "199208f373dd93a792e9c585352bfc73a6293411bed6da6d3bc4f4ef90b04c8e", + "https://deno.land/std@0.207.0/path/posix/relative.ts": "e2f230608b0f083e6deaa06e063943e5accb3320c28aef8d87528fbb7fe6504c", + "https://deno.land/std@0.207.0/path/posix/resolve.ts": "51579d83159d5c719518c9ae50812a63959bbcb7561d79acbdb2c3682236e285", + "https://deno.land/std@0.207.0/path/posix/separator.ts": "0b6573b5f3269a3164d8edc9cefc33a02dd51003731c561008c8bb60220ebac1", + "https://deno.land/std@0.207.0/path/posix/to_file_url.ts": "08d43ea839ee75e9b8b1538376cfe95911070a655cd312bc9a00f88ef14967b6", + "https://deno.land/std@0.207.0/path/posix/to_namespaced_path.ts": "c9228a0e74fd37e76622cd7b142b8416663a9b87db643302fa0926b5a5c83bdc", + "https://deno.land/std@0.207.0/path/relative.ts": "23d45ede8b7ac464a8299663a43488aad6b561414e7cbbe4790775590db6349c", + "https://deno.land/std@0.207.0/path/resolve.ts": "5b184efc87155a0af9fa305ff68a109e28de9aee81fc3e77cd01380f19daf867", + "https://deno.land/std@0.207.0/path/separator.ts": "40a3e9a4ad10bef23bc2cd6c610291b6c502a06237c2c4cd034a15ca78dedc1f", + "https://deno.land/std@0.207.0/path/to_file_url.ts": "edaafa089e0bce386e1b2d47afe7c72e379ff93b28a5829a5885e4b6c626d864", + "https://deno.land/std@0.207.0/path/to_namespaced_path.ts": "cf8734848aac3c7527d1689d2adf82132b1618eff3cc523a775068847416b22a", + "https://deno.land/std@0.207.0/path/windows/_util.ts": "f32b9444554c8863b9b4814025c700492a2b57ff2369d015360970a1b1099d54", + "https://deno.land/std@0.207.0/path/windows/basename.ts": "8a9dbf7353d50afbc5b221af36c02a72c2d1b2b5b9f7c65bf6a5a2a0baf88ad3", + "https://deno.land/std@0.207.0/path/windows/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", + "https://deno.land/std@0.207.0/path/windows/dirname.ts": "5c2aa541384bf0bd9aca821275d2a8690e8238fa846198ef5c7515ce31a01a94", + "https://deno.land/std@0.207.0/path/windows/extname.ts": "07f4fa1b40d06a827446b3e3bcc8d619c5546b079b8ed0c77040bbef716c7614", + "https://deno.land/std@0.207.0/path/windows/format.ts": "343019130d78f172a5c49fdc7e64686a7faf41553268961e7b6c92a6d6548edf", + "https://deno.land/std@0.207.0/path/windows/from_file_url.ts": "d53335c12b0725893d768be3ac6bf0112cc5b639d2deb0171b35988493b46199", + "https://deno.land/std@0.207.0/path/windows/glob_to_regexp.ts": "290755e18ec6c1a4f4d711c3390537358e8e3179581e66261a0cf348b1a13395", + "https://deno.land/std@0.207.0/path/windows/is_absolute.ts": "245b56b5f355ede8664bd7f080c910a97e2169972d23075554ae14d73722c53c", + "https://deno.land/std@0.207.0/path/windows/is_glob.ts": "ec4fbc604b9db8487f7b56ab0e759b24a971ab6a45f7b0b698bc39b8b9f9680f", + "https://deno.land/std@0.207.0/path/windows/join.ts": "e6600bf88edeeef4e2276e155b8de1d5dec0435fd526ba2dc4d37986b2882f16", + "https://deno.land/std@0.207.0/path/windows/join_globs.ts": "f4838d54b1f60a34a40625a3293f6e583135348be1b2974341ac04743cb26121", + "https://deno.land/std@0.207.0/path/windows/mod.ts": "d7040f461465c2c21c1c68fc988ef0bdddd499912138cde3abf6ad60c7fb3814", + "https://deno.land/std@0.207.0/path/windows/normalize.ts": "9deebbf40c81ef540b7b945d4ccd7a6a2c5a5992f791e6d3377043031e164e69", + "https://deno.land/std@0.207.0/path/windows/normalize_glob.ts": "344ff5ed45430495b9a3d695567291e50e00b1b3b04ea56712a2acf07ab5c128", + "https://deno.land/std@0.207.0/path/windows/parse.ts": "120faf778fe1f22056f33ded069b68e12447668fcfa19540c0129561428d3ae5", + "https://deno.land/std@0.207.0/path/windows/relative.ts": "026855cd2c36c8f28f1df3c6fbd8f2449a2aa21f48797a74700c5d872b86d649", + "https://deno.land/std@0.207.0/path/windows/resolve.ts": "5ff441ab18a2346abadf778121128ee71bda4d0898513d4639a6ca04edca366b", + "https://deno.land/std@0.207.0/path/windows/separator.ts": "ae21f27015f10510ed1ac4a0ba9c4c9c967cbdd9d9e776a3e4967553c397bd5d", + "https://deno.land/std@0.207.0/path/windows/to_file_url.ts": "8e9ea9e1ff364aa06fa72999204229952d0a279dbb876b7b838b2b2fea55cce3", + "https://deno.land/std@0.207.0/path/windows/to_namespaced_path.ts": "e0f4d4a5e77f28a5708c1a33ff24360f35637ba6d8f103d19661255ef7bfd50d", + "https://esm.sh/@emotion/css@11.11.2": "864e545ec51febc099b7ce1e2915ace0fad12ee646b587daf13a23d291ea7059", + "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js": "680949c848e5127b5437649159f16c2fb4fcbbd124bc727f059932772a7bac97", + "https://esm.sh/interactjs@1.10.26": "3b379d36d5893e6b157e17d874081e22582dda06859eba240d90c97f2c4c8d16", + "https://esm.sh/lodash-es@4.17.21": "460848e64379750386465df2bfcbe530dc5be584cabb39816e0dec105e5f9af2", + "https://esm.sh/v135/@emotion/cache@11.11.0/denonext/cache.mjs": "c70caee77d4c78bd364bdac8cf8dd7fc7ba997757d9a4025c3e637c1ccdacf9e", + "https://esm.sh/v135/@emotion/css@11.11.2/denonext/create-instance.js": "c740e25eaa362210926807bf5bbe3b795b2e8af0f01ddb2273a618ee01e8572c", + "https://esm.sh/v135/@emotion/css@11.11.2/denonext/css.mjs": "c0ae1f961a61f03ad5f24eb4b3b0b135b07ac7b77549e7ad8129004dc374854e", + "https://esm.sh/v135/@emotion/hash@0.9.1/denonext/hash.mjs": "053b2641767105d296dd83499c10299221f68e4e09d2394bdd53db0080a7ce7d", + "https://esm.sh/v135/@emotion/memoize@0.8.1/denonext/memoize.mjs": "e40cb7a1d1dabc5a5873911b1f3551571e85b9401028540fea356a671a4d7e4f", + "https://esm.sh/v135/@emotion/serialize@1.1.2/denonext/serialize.mjs": "bf052ec1336c2295475a87a9dbc607788dd8d30e6e4cf10dc778a481f9c2631f", + "https://esm.sh/v135/@emotion/serialize@1.1.3/denonext/serialize.mjs": "d15566b7ff44b49eb6158589a2325d92c2afb9ac69e906f3a65b820d48da99d7", + "https://esm.sh/v135/@emotion/sheet@1.2.2/denonext/sheet.mjs": "51feebbf9422e8a55b99d9b9d7424cfe17d8d60a9a5a19e996aa745686f3a805", + "https://esm.sh/v135/@emotion/unitless@0.8.1/denonext/unitless.mjs": "8a91ddf8c10553143e87b383d23d9334637f36cccb930d7d65990f8002a6c091", + "https://esm.sh/v135/@emotion/utils@1.2.1/denonext/utils.mjs": "5f6e36b10f6dd17b64c30a1b49079d5d024cff498e7900e6468b8ee93342004c", + "https://esm.sh/v135/@emotion/weak-memoize@0.3.1/denonext/weak-memoize.mjs": "05e059e6f679168e597ee6b0dace8503031f54678460fc5f366025f40199e18e", + "https://esm.sh/v135/gh/observablehq/stdlib@v5.8.6/denonext/src/html.js": "5389cb3fa08f0e1488d9bdcbe9aef3b7f919b166d60a1b04ed53da43516cc002", + "https://esm.sh/v135/interactjs@1.10.26/denonext/interactjs.mjs": "8aab44525c4b52a1180ba2647f8faa25ea48e05832e0671ed4dab33364cd7193", + "https://esm.sh/v135/lodash-es@4.17.21/denonext/lodash-es.mjs": "ca6d37e6a1271d1bd8920da5ff11f7d05465f5d7961d5d09b44db331838981d7", + "https://esm.sh/v135/stylis@4.2.0/denonext/stylis.mjs": "67596d0aad811e2c7c088875c652feff18b3fe1091df1892c4df71bc090a4f10" + }, + "workspace": { + "dependencies": [ + "npm:@emotion/css@11.11.2" + ] + } } diff --git a/js/src/url_imports.js b/js/src/url_imports.js index 6ccd67e..1350408 100644 --- a/js/src/url_imports.js +++ b/js/src/url_imports.js @@ -1,16 +1,8 @@ // We put all url imports here to have them in a single place -import createEmotion from "https://esm.sh/@emotion/css@11.11.2/create-instance"; +import { css } from "https://esm.sh/@emotion/css@11.11.2"; import lodash from "https://esm.sh/lodash-es@4.17.21"; import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; import { default as interact } from "https://esm.sh/interactjs@1.10.26" // We need to create our custom emotion instance while loading the module to avoid -const emotion = createEmotion( - { - key: 'css', - container: globalThis.document, - } -); - -export const css = emotion.css -export { createEmotion, emotion, html, lodash, interact } \ No newline at end of file +export { css, html, lodash, interact } \ No newline at end of file From 4d7db9d47f13a0132786f4ecabac55b63d9eaa0b Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 7 Apr 2024 20:44:34 +0200 Subject: [PATCH 14/39] add metafile generation in bundle --- js/bundle.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/bundle.js b/js/bundle.js index 3cf7d01..c132bdc 100644 --- a/js/bundle.js +++ b/js/bundle.js @@ -19,9 +19,12 @@ const result = await esbuild.build({ bundle: true, format: "esm", minify: true, + metafile: true, treeShaking: true, }); -console.log(result.outputFiles); +Deno.writeTextFile("./dist/meta.json", JSON.stringify(result.metafile)) + +// console.log(result.metafile); esbuild.stop(); \ No newline at end of file From d89bac31bd5c5e663f83857fb48ae9a7a357dc1f Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 7 Apr 2024 20:44:55 +0200 Subject: [PATCH 15/39] add some deps for development --- js/deno.jsonc | 5 +- js/deno.lock | 992 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 994 insertions(+), 3 deletions(-) diff --git a/js/deno.jsonc b/js/deno.jsonc index d291248..530f940 100644 --- a/js/deno.jsonc +++ b/js/deno.jsonc @@ -2,7 +2,8 @@ "compilerOptions": { "lib": [ "esnext", - "dom" + "dom", + "deno.ns" ], "checkJs": true }, @@ -17,5 +18,7 @@ browser also works fine so it seems to be just a bundling problem probably tied to how deno-esbuild-loader works? */ "https://esm.sh/@emotion/css@11.11.2": "npm:@emotion/css@11.11.2", + "https://esm.sh/lodash-es@4.17.21": "npm:lodash-es@4.17.21", + "https://esm.sh/interactjs@1.10.27": "npm:interactjs@1.10.27", } } \ No newline at end of file diff --git a/js/deno.lock b/js/deno.lock index 6d0acf6..b4caff3 100644 --- a/js/deno.lock +++ b/js/deno.lock @@ -9,7 +9,13 @@ "jsr:@std/jsonc@0.213": "jsr:@std/jsonc@0.213.1", "jsr:@std/path@0.213": "jsr:@std/path@0.213.1", "npm:@emotion/css@11.11.2": "npm:@emotion/css@11.11.2", - "npm:esbuild@0.20.2": "npm:esbuild@0.20.2" + "npm:@interactjs/interactjs@1.10.27": "npm:@interactjs/interactjs@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27_@interactjs+modifiers@1.10.27__@interactjs+core@1.10.27___@interactjs+utils@1.10.27__@interactjs+utils@1.10.27", + "npm:@types/lodash-es": "npm:@types/lodash-es@4.17.12", + "npm:@types/plotly.js": "npm:@types/plotly.js@2.29.2", + "npm:esbuild@0.20.2": "npm:esbuild@0.20.2", + "npm:interactjs": "npm:interactjs@1.10.27", + "npm:interactjs@1.10.27": "npm:interactjs@1.10.27", + "npm:lodash-es@4.17.21": "npm:lodash-es@4.17.21" }, "jsr": { "@luca/esbuild-deno-loader@0.10.3": { @@ -74,6 +80,10 @@ "picocolors": "picocolors@1.0.0" } }, + "@babel/parser@7.24.4": { + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", + "dependencies": {} + }, "@babel/runtime@7.24.1": { "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", "dependencies": { @@ -250,10 +260,219 @@ "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "dependencies": {} }, + "@interactjs/actions@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-FCRg5KwB+stkPcAMx/Cn0fgGP6p4LyMX9S/Upcn/W+hpYme31bPi54PCqmOebzz6myTthN6zFf9jMyLOqtI/gg==", + "dependencies": { + "@interactjs/core": "@interactjs/core@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/auto-scroll@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-zPg5TnVsZv+9Hnt4qnbxLvBMf+rIWHkoJVoSETEbLNaj90C8hIyr0pVwukSUySSgDhCgQ7np0f3pg4INLq9beQ==", + "dependencies": { + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/auto-start@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-ECLBO/nxmaF1knncJKIE5F7la3KKRgEkn0Cu2JTPOYj9xy/LpfYElo3wkRHsodgOqF651nR70GK2/IzPR2lO9A==", + "dependencies": { + "@interactjs/core": "@interactjs/core@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/core@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-SliUr/3ZbLAdED8LokzYzWHWMdCB5Cq+UnpXuRy+BIod1j97m4IUFf/D1iIKUBBjBcucgXbz28z96WnenVCB7Q==", + "dependencies": { + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/dev-tools@1.10.27_@interactjs+modifiers@1.10.27__@interactjs+core@1.10.27___@interactjs+utils@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27": { + "integrity": "sha512-YolmBwRaKH1gWbvyLeV3m5QSwtD38lOZnCBA87PCAlcd9PQAC2gb03fEPeEyD336bE20oLB8f0WZt4Wre+afiw==", + "dependencies": { + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/modifiers": "@interactjs/modifiers@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27", + "vue": "vue@3.4.21" + } + }, + "@interactjs/inertia@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+modifiers@1.10.27__@interactjs+core@1.10.27___@interactjs+utils@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-S/SVj/M0D+wWWPVXHcXN/YUWOK51LFJsEA+CTgVnFhlSU04+1FUvNLwilCZcHgECu1RJxZNKDwZysDATg+r8jQ==", + "dependencies": { + "@interactjs/core": "@interactjs/core@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/modifiers": "@interactjs/modifiers@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/offset": "@interactjs/offset@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-XdH3A2UUzjEFGGJgFuJlhiz99tE8jB8xNh/DmnoMuL6uOQPxNA+sWRnzEVjG0+zY2P3/dbhEpi4Cn3FLPzydwA==", + "dependencies": { + "@interactjs/core": "@interactjs/core@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/interactjs@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27_@interactjs+modifiers@1.10.27__@interactjs+core@1.10.27___@interactjs+utils@1.10.27__@interactjs+utils@1.10.27": { + "integrity": "sha512-UwhfUZMZVXUY72efPABuKSBz1sUY+r+49v8t6Ku9o5Jq76AKg9mwmdGszIlOn3ppnFDDjvtzK/8TL+Sbd0EQEA==", + "dependencies": { + "@interactjs/actions": "@interactjs/actions@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/auto-scroll": "@interactjs/auto-scroll@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/auto-start": "@interactjs/auto-start@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/core": "@interactjs/core@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/dev-tools": "@interactjs/dev-tools@1.10.27_@interactjs+modifiers@1.10.27__@interactjs+core@1.10.27___@interactjs+utils@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27", + "@interactjs/inertia": "@interactjs/inertia@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+modifiers@1.10.27__@interactjs+core@1.10.27___@interactjs+utils@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/modifiers": "@interactjs/modifiers@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/offset": "@interactjs/offset@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/pointer-events": "@interactjs/pointer-events@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/reflow": "@interactjs/reflow@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/modifiers@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-ei/qfoQ+9/8k6WzNzdNqHI6cWkIV576N4Ap16r5CoqOWwhA6Xzj3OMHf1g0t1O4eSq2HdJsVJn3eLNfw9HsbeQ==", + "dependencies": { + "@interactjs/core": "@interactjs/core@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/snappers": "@interactjs/snappers@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/offset@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-AezsLiuK+Qv4jXdYuRa65HJ2pMFMZPlqiAep6ZRLwhP9HE7O75c0EAm+gfx+dpPrHNHs6J9LaiKSZl+B+A2qAw==", + "dependencies": { + "@interactjs/core": "@interactjs/core@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/pointer-events@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-Yo5SS6PhWfC93gHNxnwwW0wvebo5hSYJKGaSnAHO4f9Lh25yibecMnmPBmiEfWVcdMboK/kXrme43mHQaRegVg==", + "dependencies": { + "@interactjs/core": "@interactjs/core@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/reflow@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-Msm0QdYFr40oSsPFxyCR3dHN/pQx34k7QSkdN1uIsUn/drrm+YSFvrvVOu99DFOwr7gTThr5vNe06Sz4vubTSA==", + "dependencies": { + "@interactjs/core": "@interactjs/core@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/snappers@1.10.27_@interactjs+utils@1.10.27": { + "integrity": "sha512-HZLZ0XSi6HI08OmTv/HKG6AltQoaKAALLQ+KDW92utj3XSgw7oren0KsWUKPhaPg3Av7R1jFQd08s+uafqIlLw==", + "dependencies": { + "@interactjs/interact": "@interactjs/interact@1.10.27_@interactjs+utils@1.10.27", + "@interactjs/utils": "@interactjs/utils@1.10.27" + } + }, + "@interactjs/types@1.10.27": { + "integrity": "sha512-BUdv0cvs4H5ODuwft2Xp4eL8Vmi3LcihK42z0Ft/FbVJZoRioBsxH+LlsBdK4tAie7PqlKGy+1oyOncu1nQ6eA==", + "dependencies": {} + }, + "@interactjs/utils@1.10.27": { + "integrity": "sha512-+qfLOio2OxQqg1cXSnRaCl+N8MQDQLDS9w+aOGxH8YLAhIMyt7Asxx/46//sT8orgsi16pmlBPtngPHT9s8zKw==", + "dependencies": {} + }, + "@jridgewell/sourcemap-codec@1.4.15": { + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dependencies": {} + }, + "@types/lodash-es@4.17.12": { + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dependencies": { + "@types/lodash": "@types/lodash@4.17.0" + } + }, + "@types/lodash@4.17.0": { + "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", + "dependencies": {} + }, "@types/parse-json@4.0.2": { "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "dependencies": {} }, + "@types/plotly.js@2.29.2": { + "integrity": "sha512-tJqUVa6CmNHrzTANWO/ZLNmdvjZ6zHOAnM9iQUORVnnLvWmcO9DPdQCsrNumNvHD1aqGNxQJ8pla8CK/NUiZgA==", + "dependencies": {} + }, + "@vue/compiler-core@3.4.21": { + "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", + "dependencies": { + "@babel/parser": "@babel/parser@7.24.4", + "@vue/shared": "@vue/shared@3.4.21", + "entities": "entities@4.5.0", + "estree-walker": "estree-walker@2.0.2", + "source-map-js": "source-map-js@1.2.0" + } + }, + "@vue/compiler-dom@3.4.21": { + "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", + "dependencies": { + "@vue/compiler-core": "@vue/compiler-core@3.4.21", + "@vue/shared": "@vue/shared@3.4.21" + } + }, + "@vue/compiler-sfc@3.4.21": { + "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", + "dependencies": { + "@babel/parser": "@babel/parser@7.24.4", + "@vue/compiler-core": "@vue/compiler-core@3.4.21", + "@vue/compiler-dom": "@vue/compiler-dom@3.4.21", + "@vue/compiler-ssr": "@vue/compiler-ssr@3.4.21", + "@vue/shared": "@vue/shared@3.4.21", + "estree-walker": "estree-walker@2.0.2", + "magic-string": "magic-string@0.30.9", + "postcss": "postcss@8.4.38", + "source-map-js": "source-map-js@1.2.0" + } + }, + "@vue/compiler-ssr@3.4.21": { + "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", + "dependencies": { + "@vue/compiler-dom": "@vue/compiler-dom@3.4.21", + "@vue/shared": "@vue/shared@3.4.21" + } + }, + "@vue/reactivity@3.4.21": { + "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==", + "dependencies": { + "@vue/shared": "@vue/shared@3.4.21" + } + }, + "@vue/runtime-core@3.4.21": { + "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==", + "dependencies": { + "@vue/reactivity": "@vue/reactivity@3.4.21", + "@vue/shared": "@vue/shared@3.4.21" + } + }, + "@vue/runtime-dom@3.4.21": { + "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==", + "dependencies": { + "@vue/runtime-core": "@vue/runtime-core@3.4.21", + "@vue/shared": "@vue/shared@3.4.21", + "csstype": "csstype@3.1.3" + } + }, + "@vue/server-renderer@3.4.21_vue@3.4.21": { + "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==", + "dependencies": { + "@vue/compiler-ssr": "@vue/compiler-ssr@3.4.21", + "@vue/shared": "@vue/shared@3.4.21", + "vue": "vue@3.4.21" + } + }, + "@vue/shared@3.4.21": { + "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==", + "dependencies": {} + }, "ansi-styles@3.2.1": { "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dependencies": { @@ -308,6 +527,10 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dependencies": {} }, + "entities@4.5.0": { + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dependencies": {} + }, "error-ex@1.3.2": { "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dependencies": { @@ -350,6 +573,10 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dependencies": {} }, + "estree-walker@2.0.2": { + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dependencies": {} + }, "find-root@1.1.0": { "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", "dependencies": {} @@ -375,6 +602,12 @@ "resolve-from": "resolve-from@4.0.0" } }, + "interactjs@1.10.27": { + "integrity": "sha512-y/8RcCftGAF24gSp76X2JS3XpHiUvDQyhF8i7ujemBz77hwiHDuJzftHx7thY8cxGogwGiPJ+o97kWB6eAXnsA==", + "dependencies": { + "@interactjs/types": "@interactjs/types@1.10.27" + } + }, "is-arrayish@0.2.1": { "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dependencies": {} @@ -397,6 +630,20 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dependencies": {} }, + "lodash-es@4.17.21": { + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dependencies": {} + }, + "magic-string@0.30.9": { + "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "@jridgewell/sourcemap-codec@1.4.15" + } + }, + "nanoid@3.3.7": { + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dependencies": {} + }, "parent-module@1.0.1": { "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dependencies": { @@ -424,6 +671,14 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dependencies": {} }, + "postcss@8.4.38": { + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dependencies": { + "nanoid": "nanoid@3.3.7", + "picocolors": "picocolors@1.0.0", + "source-map-js": "source-map-js@1.2.0" + } + }, "regenerator-runtime@0.14.1": { "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "dependencies": {} @@ -440,6 +695,10 @@ "supports-preserve-symlinks-flag": "supports-preserve-symlinks-flag@1.0.0" } }, + "source-map-js@1.2.0": { + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dependencies": {} + }, "source-map@0.5.7": { "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dependencies": {} @@ -462,6 +721,16 @@ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dependencies": {} }, + "vue@3.4.21": { + "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==", + "dependencies": { + "@vue/compiler-dom": "@vue/compiler-dom@3.4.21", + "@vue/compiler-sfc": "@vue/compiler-sfc@3.4.21", + "@vue/runtime-dom": "@vue/runtime-dom@3.4.21", + "@vue/server-renderer": "@vue/server-renderer@3.4.21_vue@3.4.21", + "@vue/shared": "@vue/shared@3.4.21" + } + }, "yaml@1.10.2": { "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dependencies": {} @@ -469,11 +738,726 @@ } }, "redirects": { + "https://esm.sh/@types/lodash-es": "https://esm.sh/v135/@types/lodash-es@4.17.12/index.d.ts", "https://esm.sh/@types/plotly.js": "https://esm.sh/v135/@types/plotly.js@2.29.2/index.d.ts", + "https://esm.sh/v135/@types/es6-promisify@latest/index.d.ts": "https://esm.sh/v135/@types/es6-promisify@6.0.4/index.d.ts", "https://esm.sh/v135/@types/lodash-es@~4.17/index.d.ts": "https://esm.sh/v135/@types/lodash-es@4.17.12/index.d.ts", "https://esm.sh/v135/@types/lodash@4.14.202/index": "https://esm.sh/v135/@types/lodash@4.14.202/index~.d.ts" }, "remote": { + "https://cdn.jsdelivr.net/npm/@interactjs/actions/drag/plugin.js": "9b8ac4fc9cb37ff9b3099fd2584e43d44493514536879ee4eabd70a97b1ee349", + "https://cdn.jsdelivr.net/npm/@interactjs/actions/drop/DropEvent.js": "9896add30b396e722593ece8cb9a968d8b9ebd9e491b3a9b6e42907112141faf", + "https://cdn.jsdelivr.net/npm/@interactjs/actions/drop/plugin.js": "b1aa7585d0975f4cfc416eaac095b805c0776cea752632f17336aba9826fb485", + "https://cdn.jsdelivr.net/npm/@interactjs/actions/gesture/plugin.js": "94988fd2824694683e172dc20626fdf1d57e65b42a36ae01036da8bd42037b39", + "https://cdn.jsdelivr.net/npm/@interactjs/actions/plugin.js": "53b6fe98ea689722702c59303ac90785f4e019cc2c9a9f2e80ae44546c9a1bb2", + "https://cdn.jsdelivr.net/npm/@interactjs/actions/resize/plugin.js": "c6770b90cb0b30f93ecff3559e762b98dedb760bc088aa5e7d8c55a03c6723a4", + "https://cdn.jsdelivr.net/npm/@interactjs/auto-scroll/plugin.js": "f19753b241e2ffed5bcee091396c01648f4a85a99fa0b4e51e3cc62743708d67", + "https://cdn.jsdelivr.net/npm/@interactjs/auto-start/InteractableMethods.js": "5f4b4316a97fd12e0066ba2de126edf741f1a322a49a7f62f90afe8a1c9c6855", + "https://cdn.jsdelivr.net/npm/@interactjs/auto-start/base.js": "b6add448c9fca589c3aa3bf966200664638fc84eaa6cf1ef927e8b263e009878", + "https://cdn.jsdelivr.net/npm/@interactjs/auto-start/dragAxis.js": "c5969946efcdd5d70c38c5e5126a50b06db0df69860e5e7c2eaa12ebe2f49190", + "https://cdn.jsdelivr.net/npm/@interactjs/auto-start/hold.js": "9fa5ea5ed5b03aee441638a8c2f422e126247d0db25d7feb5f1db809d74f02a3", + "https://cdn.jsdelivr.net/npm/@interactjs/auto-start/plugin.js": "739fd91f2f02c731e60032079a80d2ccecd34d44f1be88d9d7bff170145570bb", + "https://cdn.jsdelivr.net/npm/@interactjs/core/BaseEvent.js": "3d6f99d05386b27310f9e433a640b4da55b678a69e9239fcc260626cacea5d03", + "https://cdn.jsdelivr.net/npm/@interactjs/core/Eventable.js": "4bb1d50b1718a9d1dbb02b2e9f8de4bc91db4410a8412543ff8e610a16015976", + "https://cdn.jsdelivr.net/npm/@interactjs/core/InteractEvent.js": "ffa2b5be6748da534fbbdbda89e047331cd70502998577709ffa0914e70d9fe7", + "https://cdn.jsdelivr.net/npm/@interactjs/core/InteractStatic.js": "bce746a71b1bbfc1488d1328161b40decb4186c335ac8c0fafe5e076222d82df", + "https://cdn.jsdelivr.net/npm/@interactjs/core/Interactable.js": "be43a0631d55e961ee5164b4f36af9e002a1b6da2e21952bef9234782d69e207", + "https://cdn.jsdelivr.net/npm/@interactjs/core/InteractableSet.js": "f6e91f356a647951b82042f10ae778afccd6b9b1af95333e656a19d3cbeb089e", + "https://cdn.jsdelivr.net/npm/@interactjs/core/Interaction.js": "cc8ca0c672d9087402e222cc7558cea0c8f76c66ed44bf430f23be3ecaf90a5b", + "https://cdn.jsdelivr.net/npm/@interactjs/core/PointerInfo.js": "5ddeeb2ece8da565fb0e084fe5df4f8237d60851dd28dad669421973971dc294", + "https://cdn.jsdelivr.net/npm/@interactjs/core/events.js": "e2370d921f5c2401eefb84e6989f9df2a86b67aef730276080edfec26e8864d2", + "https://cdn.jsdelivr.net/npm/@interactjs/core/interactablePreventDefault.js": "9079e42c6b361992aad718a8c05f5e34a387c2eb96dfa7d3c3a68be55efcc71b", + "https://cdn.jsdelivr.net/npm/@interactjs/core/interactionFinder.js": "3951dae15eca0640ffed1c3a5830dc9605c0b472e3ae2769885ab0e274c49c39", + "https://cdn.jsdelivr.net/npm/@interactjs/core/interactions.js": "392a95eebb18c6acce37958e7968d2a545afd9bedab39f814361e197cea98b1b", + "https://cdn.jsdelivr.net/npm/@interactjs/core/options.js": "d925f074918569dc031a1f119edd5f7d7dd16a45fbdfa5c98fbe0c3d95ae58b9", + "https://cdn.jsdelivr.net/npm/@interactjs/core/scope.js": "7b95175f715063f341902509529c0cc84287163d77db28f1d5b01400c472d946", + "https://cdn.jsdelivr.net/npm/@interactjs/dev-tools/plugin.js": "5ca7e56712f20c8ea6d8684310269e5a0c5185744ffc2f3961fc4935f9402709", + "https://cdn.jsdelivr.net/npm/@interactjs/inertia/plugin.js": "aba63888b95c963070e99610a6ed58efdf57226f0533ad521fb28112418b7224", + "https://cdn.jsdelivr.net/npm/@interactjs/interact/index.js": "282e40bcd45b86a7121758a2bdd01b8cdc1f136b234d6fd84542a5d6f9020ca5", + "https://cdn.jsdelivr.net/npm/@interactjs/interactjs@1.10.27/index.min.js": "3f2cdac2ffbd11a8a70e770faf45c3c87c1d6764aa6379fcc7bd20d91be0ced9", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/Modification.js": "131caf8ebb9bc7b97140d51fb3cdb0dc2971aff779c4d8f2fcdfe4d77fecb93f", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/all.js": "a3f48942922249ead5ef69761f35a1cb0cd571d12caca409f29cc270e9b5e093", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/aspectRatio.js": "8122b75479503022f8d3a9a954c54e38dafe7088d91e5db1cd6cd46a46306a7d", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/base.js": "ded35c9f43f6381307cd1ba89037b06bd2522347eb2f0a2f544ace3fb3ca92dd", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/noop.js": "73741688baa64c6b8fcbdfa3f61c414fe5ac8ab76354d66c2b4f5500882a6ca9", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/plugin.js": "0d54dbd5afc0adca25f185faf6b9ef3bb24d2892d471fe27184837f75ee2f8bc", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/restrict/edges.js": "231f5f6ac3a786457397a38c38fb9244636df359aa47121016dbcd52b9fc4a82", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/restrict/pointer.js": "a0913e4c09c332e3e4e2c175584284b35d9c25717139e3edf9c9876c9f61a3d5", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/restrict/rect.js": "dfe0eee1e7a0c64fc1bdea4b54cfee7a757d50bba17bf01b4cb4873f624a6f25", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/restrict/size.js": "9b059b9c9aa18d2b3219895087de1a3356706d2219cf21d4c35d62d575d6340c", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/snap/edges.js": "3afd0e8079b3a1ac0678d667cb5cefb96e9003b82e1bf8e9108517d54ad30645", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/snap/pointer.js": "569f69e82c4cd0f5b40320dffd0a2da9298836a41723dd289816df454241fdf7", + "https://cdn.jsdelivr.net/npm/@interactjs/modifiers/snap/size.js": "e783dc1862c71e606b87d87b39a61a8f44d34d83900ddb60ffc00ecedd13aee8", + "https://cdn.jsdelivr.net/npm/@interactjs/offset/plugin.js": "e87ed09611d24c063bea9e7686e654188db9cc7ba0bfa39b4256a43e15d845fd", + "https://cdn.jsdelivr.net/npm/@interactjs/pointer-events/PointerEvent.js": "c18d6a3b8cd0868160beb9e1c258a896c8ed84bc06c8be628f78c29b424423c0", + "https://cdn.jsdelivr.net/npm/@interactjs/pointer-events/base-45YfudGV.js": "09c37afd3a34e167a6d427185d4172bd6328746987cec7c9e18edd77a596d5c1", + "https://cdn.jsdelivr.net/npm/@interactjs/pointer-events/holdRepeat.js": "b14afbb8d84974ca18bf83e6a00df762e47a4b7005ec79dbc7cc1151764f0203", + "https://cdn.jsdelivr.net/npm/@interactjs/pointer-events/interactableTargets.js": "b1860c685ed975767438742d82a460748b6c893e1fb24de4dd04e2cf8c1cc035", + "https://cdn.jsdelivr.net/npm/@interactjs/pointer-events/plugin.js": "6b2d8b3aaebe128b2613b05c267cf577bf8f2da8f3baa0265b8a6db27e953b6f", + "https://cdn.jsdelivr.net/npm/@interactjs/reflow/plugin.js": "982a43e63e09dfc3af35c5af2c803d63f6ba3faf6234256f4775e37b288072d8", + "https://cdn.jsdelivr.net/npm/@interactjs/snappers/all-QhkClWVN.js": "a7f34bf34e484896c970a2af32d812b3b713c81c59f4d4ee2ddba15e6470615a", + "https://cdn.jsdelivr.net/npm/@interactjs/snappers/edgeTarget.js": "ff50a19ad36a188c5ce33fce7f05a6659b2c629fdc7f8a8b82c0fc92d53456b3", + "https://cdn.jsdelivr.net/npm/@interactjs/snappers/elements.js": "60be2ce160dfaa7d452079b1c8366118c157c8a8fcfe8390519e06c041e25de1", + "https://cdn.jsdelivr.net/npm/@interactjs/snappers/grid.js": "a78919fd797608e3adfcd8c81321aca1dd2090d1cfa093afbd8a4943cf669bc9", + "https://cdn.jsdelivr.net/npm/@interactjs/snappers/plugin.js": "fd83f6b9f5b7880a7ad3460830f0aa1e0cd61774afd2ad84379f640885cb64c5", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/arr.js": "a07b8658e64bc86ab13443b79ff11a1928dc7c89066e3dc83ce9c99169f39c0a", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/browser.js": "2ca439afdedd6cf449d86ee081ac1d6501b78ba587328557c3d03247c859d277", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/clone.js": "5844aab0d0111a9fd8baec9a706ddaab6e2d633ed6520ca68492dc09f4e3c9de", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/domObjects.js": "8fa7bc4593ab1655010e82c43e6e0be8e30e9a618178bdcad7a7d8fe9dace376", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/domUtils.js": "bc7332b279cb30f2fa8f713f7e6c41bb69d42e72c10837842cb03bb01b6afce0", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/extend.js": "388dbced62cc66d5fa9256a9ebe0a8c1a871b6ddc4ee0b5dd1d9639f5c9dfa3a", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/getOriginXY.js": "840f2027429778ed184c316c8a2743c8cfac17143a1a9128f3665fe77daf926d", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/hypot.js": "b450eeaaf9f9ff395437d4eca2b2e49b2bb044d2cd3861061720bcf3cbd91c63", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/is.js": "3bcdf01e6df5459f0442c4b76aa2aba6429960df220e6c4d516ecc3bd52d33a2", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/isNonNativeEvent.js": "f591e4db3e297db67ae3abbe533740c898a37cc8e433face0cdd40d8c80d8656", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/isWindow.js": "1a3b029e1625399482c8bb429a377588d4b0f89a7311ec83a15bfaa21bf77691", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/misc.js": "afcc2fa2b5ecfe5bbffb14dd9c7f815ea98afe0ee4f688fa1a3ac16354e7dbdf", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/normalizeListeners.js": "668237c1a04dc90ac7211601eb489c9d27b8ac938b6eb91e541d750ba44b0e4f", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/pointerExtend.js": "eb5ae0ed07dbbaf6101dae4911bb437bb27b543f76958908698cffa06af5656b", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/pointerUtils.js": "fe2110e4fb83a84d87d7f840c238b6ee207197581711edb120ef01c73e5328ac", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/raf.js": "77b8e963fb495e0969b7a0aaeffaf525c729400ffdf4e984559c595064ee3545", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/rect.js": "862846230aea60e90bc741f1f437dd6477010c9fa9680d43692cfbbe7b7c456e", + "https://cdn.jsdelivr.net/npm/@interactjs/utils/window.js": "dff0f5790720cfd4018a9882f51f57dfcf51badbdb2e88cbded938fe52db96eb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_DataView.js": "0c4811491cd0c415553118f42f9b31112ed58bdb3941358a99deec9347e7a829", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_Hash.js": "7221393e8931adc88430d3badefee1568ffccf91a6efcd9dda2be498f4a09551", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_LazyWrapper.js": "77e63978b95b1b1dd6d0d8c58eadeab22a58e00e50491b9dccdc285f7a7ab2d0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_ListCache.js": "a8c13a041707d9c96eff21d95a78326b6e2af12b067527bcba22108ce15b600c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_LodashWrapper.js": "b170f31ce90cc1c9b767295c6f32975b7d7f109c332297007bbb5bd1c64e61f8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_Map.js": "014aa839395eedfb215d1a994754db6716ba8c65ff3945b986270a4743b269bb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_MapCache.js": "fd27ffa20196943af9cbb4694555582d3045fff54b6f808269e4d3a645c3d886", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_Promise.js": "55c26a3a24e9f2e6158d36fdeec3b34d59d9f9be08e415a7f50936429595c656", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_Set.js": "84d53490631612489f33e3a4a9199f22c853e58b337aa1e10408e63d5129e216", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_SetCache.js": "0dd677f901228955ec136fb35bb20c4a8dbc4c1ef63649298a29b7b0958b9b07", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_Stack.js": "391b608ace36e0845f917ab7a5bbaab07930f9ad8b570849857ed564a0ec3a76", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_Symbol.js": "631160c3c0cbd05ec8a3d1934ded4a312504a29f04a00be2b29372d00dd6bc6d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_Uint8Array.js": "0181e8a163773e63ca1c366a134df3306c5fec28f487beb326b6bb92113d619d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_WeakMap.js": "1248b893bd82f38179e2ac6df0ff2fa8daf5e5becd11b55d8b2073a5eaab3f47", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_apply.js": "b2e6d04f093a956feef2d826cba78dab35ea1e87e8fdf7f905120adbc0bd134e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayAggregator.js": "2d7486df7ca61784cc1baccb1486f1783e38f75d7b017a941e96e945efdaac2c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayEach.js": "cfbf3667657e0f260600a9aa9359a8bde73a9241df474f77f17c06cd9fe76b4d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayEachRight.js": "0b94862cdcf0a1e25e50133d86be315a22d7c7dbf4170f5215004d95353214a9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayEvery.js": "a755d6c94351ba2979ad532d2ebb4d1d27b1b58b49bd53b9190d580fd00bfe17", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayFilter.js": "46a23fd1969903486b620f5de048c9347f24863f13416db0a632de56cc78b479", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayIncludes.js": "f1ffc64a01e957c2bcec10f0682c73deab068b55b7c55953ec47df32256107bc", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayIncludesWith.js": "0f9eb5b700c04043dc9e9fb752b46386dd02eada3cdf714f65cdbbe55371353f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayLikeKeys.js": "c441c2b158f992a5dd14bae0b35d20c3ec25a8a5b6cd439e1be0b1764e831612", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayMap.js": "ae3521733bd602f0f0443562ca13d3d1c8f42e130d4d4b0deacd931b68b0c60b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayPush.js": "ef2cb71a84885b726882f9664e3c2078279012ba7b220935b7e7390d85d89288", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayReduce.js": "b7fcb5405eac2c287eb90230995a5ba9e13e36933af3ad40dc84a29c364fbb7e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayReduceRight.js": "0c590017c353aa017507a481551f175dd7fe52049116d6501fbad4af0861e112", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arraySample.js": "820e2fc1f8615b8b4e32674082f11ad23228103df96b61a938832fe4ff64562e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arraySampleSize.js": "1afaa5ad8b0370c0c7baf14be4639e1b1f41854bb39d17124b3306fe8ffb5820", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arrayShuffle.js": "94d9119ef856d71e28f5ad65b0ac2e8fa85b01ecbd0a4b2a20e43fcb03dbdfe1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_arraySome.js": "0b50b5e018eabcb2a0b041bc02b5ae0c8d1f16914f94f376ecaf7f887cbd3aa3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_asciiSize.js": "cb2cd50ccfcc10187a4cc383914c0f811f5155532b080209a5fe65041867360c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_asciiToArray.js": "31d8d3eea65375a5b9e280e0a23ef2a4f9eff022119d5b7ed338b925ebd58780", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_asciiWords.js": "598a889963843093438d7453d71e09f759c9fefe1a731bbf87ecadc569272a9a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_assignMergeValue.js": "9018dc11f6fe776f0d6ffa27ec31107552bb1f0ac4f7424db1e1011532bc2c14", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_assignValue.js": "30c56330342d8af0405b9a113f73232fb6fc748a4177787d2b61d1a894cefdf9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_assocIndexOf.js": "b34ff3bbc5cf630e9ea1103eacd7bfcc208347a6b616398e515c7aee821bf8a4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseAggregator.js": "19dd907b3e48d1ab28a893a83d05f0e5133996f5fd18a1301dddb42a01031124", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseAssign.js": "1cce8f5275323ec54e7cc5539e260b8730606c16a14f56de7a815d46ef046297", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseAssignIn.js": "d5e10d50ae24b20e36ecb2c7596ba0ac50431e7a19d75d270b896be3257f1e24", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseAssignValue.js": "fc102066006ab1aa06bf1ed8329c64804b8c61c616840f0739031aafc009fb91", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseAt.js": "d2deadcb558f9024d1871e8d34e8dd6520ded3f738b03d1f4ef85d3798727ca1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseClamp.js": "012ba26308b2ffd9b2f92f20b1894962f6417a98747db968e328c2b76897f3f3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseClone.js": "69d03c7e6fb31a642cd17ae233aa926c4a30538d97562c86d599c6fba95d7603", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseConforms.js": "b0f75057d241513ddfe0ad8391bd87adea5b0fbb40e277effd456893584711bf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseConformsTo.js": "38fbe5fe3b942d5d26b37ef2352e7a619d760f93dffd67b0b9bb77799d0c076c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseCreate.js": "d2298952b6832fe420556a324428d247ae04540a564bf95d30f4e1bd7370a668", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseDelay.js": "38e3669c558a916c724bd4326945a19ecbcc3951c7aef7f76e8b122e78263b8b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseDifference.js": "5bc2ada6f0f5d5f3f3a7a87c6d8878a7fde32768589ea0191b5c36e09e4e4995", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseEach.js": "c18c9d3f90c8ca7f3ede215ea69de5a28dac956f06451fed7cd58876cdbbb6f7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseEachRight.js": "5dd6fcf90252fea5842ad059663d01ad0aaaff972301f9aee5dbe6614ce11573", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseEvery.js": "d1c6ce4ef6c3a179374de1863eb102a406637ece2149616bdd938ffb8462768c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseExtremum.js": "3c590121012aaa9cbbc7de9507fa98d285bd7158cc7e3edb804c35610cffbed7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseFill.js": "ed23def9d8bbfcb3192ea4d54dcaa3f655f3cbf6764da765e9d01025014c239a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseFilter.js": "d80e6bae72ad15a58bcd36a79d783deff5cb6e541f9c377de4a8951e27fabcc0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseFindIndex.js": "0d058c13978446de75c58e8716d59f7c4f886990100534aa40b9b7cf2ae408dd", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseFindKey.js": "f20b3869cf1c3475eb999e591d45ad5d1a7503174315ad8c508642f1a4ca8203", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseFlatten.js": "01a8f6924b9b3ae4765c6e5f2e064c1ef064d2055bff5d95045f0736e1097b81", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseFor.js": "171c18eca21431b2067189679bcfb5087463a64c071087b9511e9fcc67acadff", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseForOwn.js": "18d20c10eae085e80846477a65fa026bac516b447c5446a8e0f48c5a90c8a67e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseForOwnRight.js": "eff6100dde7a64a7a6e804112b6f97cfdb12043076fd86dcf9c8ced6b7d53274", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseForRight.js": "0622c36eb92a1264023940a211535550749f9262e9de27474105803cccf71803", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseFunctions.js": "5af9947df65c62848827a5b6bd6342be6106872356605d77fa5f9ca645d5a34a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseGet.js": "76ec9e50af57d35f9738d3fd9ec1b91dd63e9854381b397b8a41f1462c8e029d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseGetAllKeys.js": "45f92029359aaddc01b6aaf58370ba554430f141d1ff8879f6ef829c03ed4e44", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseGetTag.js": "3a9eebe37ba231eff0380b5f76ac5b5fcee4590665642fd052d77aa51c7d063f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseGt.js": "cc4d6cfd9da71ee5868545c5274806eadb8ca0b266ea7f08587da05f673797e9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseHas.js": "9b08b441a68a0fb9133a056f639219eca13ac301f0f6814ab2a4401a9442cd45", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseHasIn.js": "caff1788e232528fe29da4ceb0e07e064a2ac3d4e893ff0465a4d3013765360f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseInRange.js": "d1b3021ee550521ec85040238584f5a927cd18b4784ffa96a6e9f66cdcc50c6a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIndexOf.js": "2828587465acb419fb570f0241e9f4da2bf031ce20617451648907e6410d320a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIndexOfWith.js": "c564120a31b17efeee1452117e1395a307189ae5cc09997639b23ec8e4ccabfb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIntersection.js": "91185d9c6a2c28c515d452ac0f177a172cdf01da8d3177760921b0b357600207", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseInverter.js": "61040337845c51112eece21c8c3fa65b60dbe96f30aa3a2c6d86ce149fec9fc9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseInvoke.js": "e6247121f0bff0cbd8bbafae3ae2b2760c577deddac35e7b3625e3014eabad1b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsArguments.js": "8cc4e523affa6237c8177cd7cb038ede5e6778deaf3fa9c052f3e416840e0334", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsArrayBuffer.js": "52a3689c985bb974b544992a571efde6b84033e68b715dc091face274f737fe6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsDate.js": "684cef60ed48ab597e1920b30e78a11649c016858b2c8dfeee66be5a9c2d3c05", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsEqual.js": "332f33381628ed8c9849cb3e218d0e7122d47a736fea46f7abe76c1ef6904469", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsEqualDeep.js": "64174049ab03796b0cf775075cadbf74ddbc7d5bb80c10835117417375c16528", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsMap.js": "1c0b6aa98f5a9b4b21c56aaad2e662b8f3c7c483592a964e514443424d1d4493", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsMatch.js": "6820e70ce329033393a30946f5e79b8020afe60eb36399ca76062820eb994058", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsNaN.js": "2b31c10fb13313647e5d4d2e858abdb6bcec88b138669728c5214ca3d8baee87", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsNative.js": "1557c70dbb11eba112c6f5454e3cbaf7a714656da0db17e0dfe83441a70e31b1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsRegExp.js": "d7e58d031a5e88ab8fabf7e170e0859d5afdcb38e54e21bf233a4220d992f848", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsSet.js": "33903d3fdffb6d08dd4beefdb4a2e16c240af87d4ca0c4558aa730b1a87ea55d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIsTypedArray.js": "eb8a844c83bf159a41222acde165ab3f4a9983be77297496550164d10633b076", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseIteratee.js": "682c78be7d5145aac812935eca760bee4941f71e6404fb5c3bd778fdd190dae4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseKeys.js": "5ad980ce937752fa85ce2e48e981eed26ad9b0ee83a0b34491b4c032ab393aee", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseKeysIn.js": "c582f1f38230256bebba36251eccfbf082e7a395bf7b85aecc4496fd798dc83a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseLodash.js": "a2b8a5ec6dc638d34b03037736ee546fbea640643c1a0827afeb0895ce6c451f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseLt.js": "6ebaf722a155d87af068234fe158b4435c54a14c5fc15efb76f4c198237ac18c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseMap.js": "47e4f68c3c160e099028bac2a6c8df64e4285a8a29235ac8a7f44f2c06b54d52", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseMatches.js": "b3cebbc41fe98443bfa4eb119e8e8da42425c5064566d13653028629fc9f267d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseMatchesProperty.js": "1be880b258ae7e0620f184df11dc9c7942cfc4d248a924e6a93cbaccde74a1f6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseMean.js": "ad1081a30035f3926d4a041dbcfce33fd0439dd2471e3c5cd70f7017b42dd98c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseMerge.js": "6d8fada634d2384897e591daaea777b08f0397f953cab3642a541ef9b57c3dac", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseMergeDeep.js": "eec32b72db812c61e62ab0eed2cbc13cab9f75264aa6af9f5c8e7800cf50d3b1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseNth.js": "81a7862b009adca2822495e988a70bc579fd0f373ec6fdad9274e5fd9d89500c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseOrderBy.js": "faae984dd36e898dd406af38f19475fe473c41c4e638293c7122a9173386ebe1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_basePick.js": "be15064b2459865a0dfbdb374372aa4bf25b3fbf08e9552d5db20a665a909e79", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_basePickBy.js": "9ebf3befcd5f6124d7e563e573b68ea6448f834543b42244e45a2b9c9369c087", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseProperty.js": "7f3c9243eb26034d9e01553ba0e3dca6d949724355ad46f69e0e3328301277cc", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_basePropertyDeep.js": "b1c557b398ef34577c5858e8be933a6f04003bcabb42510405e0de5149e4cc8d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_basePropertyOf.js": "a62950da12b7236a1524bc1191d43ed2edc6e93827f1b12e903d71f9f262e15b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_basePullAll.js": "3b8b7c894bd13e4c43b898f7c38e412e9fd8c6e8c8a41dcefee68ff80d34ac6b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_basePullAt.js": "720daedc26a4b1b76f79dc90de66ed772b04ea8865523c4670b780a2ee39964a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseRandom.js": "d74ff1f60fc55cfc35b5dd4eb1bb6dee5a9ef289f366b3ac8df6988a321f3e91", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseRange.js": "c10760efec1cf5ece8a25af30455ba6254c52ea2e74b167547c5499c1b5b977a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseReduce.js": "e85c15d2434fbf5c925f0b277372ab2780cbe80b45905baf873b8ef0f1ef6be7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseRepeat.js": "ca39ea92c9d7507281ba76e1eda345bc51203187c8700652c9f7965aef3a05af", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseRest.js": "f796cefb2c34daf6ad295d2af58abc96ea34348676a23a88e11789bc45f844ea", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSample.js": "032232633ae8c719d596ca7fd2c92d46b736bf58ee6c344eb6c642e8da113edf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSampleSize.js": "9b3a09cc73605e04106f547b3cb8303393ac2155e85a75f29be6ee4a0e255441", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSet.js": "11030a8c8b839be620d2233c1236d04fd08508d357edaf7c3eb3189354ee362e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSetData.js": "3cb4bb352cada1eab70d6c3bb06f90000b9eb0c68a61c3c9b1ddecdae6e04b99", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSetToString.js": "75d18e54921591874d3d54591087f458a423eed3d7042f237753fbca692d1d7f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseShuffle.js": "0da7ac2a693e76ae8b5827d90e88f7c53ba1bb25ee6fa0da4537449a05e42459", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSlice.js": "01913d08224c395783d4be26f1be8db57a0c1f1c53a0e3b8fea60055726b7ced", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSome.js": "4130846547ef5c12ff7756c00e6aad32b2ca75678e6f4c6802bd0180f8067f1b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSortBy.js": "3246812818378a65f35f2d6e3efd358380e4a30bdd143b994b673a60e6fbab76", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSortedIndex.js": "fd14ffcdbf24892594372f69f4c012aa641aed5087afc0159103605430fd3cbf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSortedIndexBy.js": "688adfc367439d1fb62912db83625f41f7101e30207a06ceefc634759b6da561", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSortedUniq.js": "0cb97ac908e675c379134659f8814fd244017ef0c401913c3f6bb6282feb69ef", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseSum.js": "499ebff818e516c4a78e0fd5216bd63d286c48147ad05612d4ec6e9e683a51f2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseTimes.js": "0d71867db092ef76aed4436956c0cf26d0990eb5d19c88f74edbe8c7ae2dfc93", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseToNumber.js": "fa0f9ea95e83fc9e9fc9949009df0b087ca987c43246ce548ac07f7b3f561467", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseToPairs.js": "29bca9551ca9b9b41d42232aff6fff9d0cd6eb83550612e1d49f6f9c531950fe", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseToString.js": "dc45840c564e75b058f8492a207d84a80629facdc33d80c4f05018aacdad675b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseTrim.js": "8cce4af7b95677ab2c74cc39cac7abb359eaab9515427191974f5edd69ad5a71", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseUnary.js": "25d6f22dcecef2d761e3059330425d1c585dfa81d50ba1dcd82d5524bfa78b53", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseUniq.js": "7fd05b3294fa6a78ef86115898480abf6c45a4e0815312c657246bf5fa7c29f2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseUnset.js": "03f96487914730f7462632d4b655ce2033636dbf9c2a63569e57f922830d023f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseUpdate.js": "2fe8901b11eb7e895a37ac0aa49b80bd1e13b83ce83027f1257e7f4a4c01c103", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseValues.js": "5f9486150a50c0f4dc8c5ab04a7a7d58fa9bb3e8e6d6d1a6f1ce9db24349e5a6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseWhile.js": "a92c7d0f081d9e1b763b27ddcf386fcbfa782245105d012e4f6f44565d8b7c9e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseWrapperValue.js": "7e07e072a0cf891d42ef0a2c699320e09948d3e08e03474dbf946da79b7edfea", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseXor.js": "1abd63581ad152a5369b46bfa6cb2a407762f2ee75892bb61263e9faa2ca9ca4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_baseZipObject.js": "55df8ae46ca2d003681a296d1b2b0662e95c9f191f89fdc57af95b48e46b10c4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_cacheHas.js": "142cd8ff86e5e823b1262c6ae0e7843b28c3b0e806c502f7d11daddf941cbc14", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_castArrayLikeObject.js": "15614b9419ef6650e99e6a85690ba6a68fcf71a7ccf3b1841c47badc902d3814", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_castFunction.js": "1da66c213c6fb96a4cd9cd3cb54f8dd5ac103465425bd7bc0282e3aa5d982472", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_castPath.js": "375ca22e1255f6e787cfcb5e6e795f4d68595befd8aab4156b2e7fa5d1c80793", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_castRest.js": "52f39408b59d49b3cb351ec514f9372ddd8123e6545484f626fff98f47552418", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_castSlice.js": "20de417088b7fce8a3c36ed16dace11e4f3104ce25fb38ec5e94dae0974172fe", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_charsEndIndex.js": "140b7d08336cb375b598d7af5846613633f0ee6b61ee64ae79a4b2fd8d7e4bd1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_charsStartIndex.js": "af53bd64f21d7526ffe8aec7dcb8f038ba7d4215612eca7791a14323366f906c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_cloneArrayBuffer.js": "04c06ac0667c7681872950fefa3d82af988cbb2f45e7ac94ad30a1a7fbca760e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_cloneBuffer.js": "879343bd3fe02c37cbd8691dfb7d2b3599a069bc646bc415d49b347b06e7f801", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_cloneDataView.js": "b9a868e7012cd534c7f01d0d65173b005f0252fbd6c5b607423998c2d33f968d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_cloneRegExp.js": "683f8e1bd6e13beb3cc33858eb9d38da573df4aa6a2e02f035a2723ba05c21c4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_cloneSymbol.js": "6be913cb0962aa51be9588bd01e02b3db72443a574b7d65ecf8036c78ddf4d4b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_cloneTypedArray.js": "40396608091618700dc31a89b5e3a70d8a77158134bbe85ca0a33f42061cf90f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_compareAscending.js": "1e22b628978cdcc121df44e8884e4b518f4c8cf20bc8e8b1961248d815bdd4a7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_compareMultiple.js": "25f532223b2f85f82938c245f9c5d5fa1acd918f2e1c088798f0711dd8be6d93", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_composeArgs.js": "521e8326c11e4ff1e25fe82ad7cfebe7924da55ad8dd972ae2431fd203b22d97", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_composeArgsRight.js": "38de3210a1b3246983a106e13e9dc671f00f03e5ff474c7c54231e6df5c46dd5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_copyArray.js": "66e841eb36a87af2b6bd8d7a21651d2f6c65ee11186ba8c0cd8a5f7856aa626c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_copyObject.js": "f348153f6d3fb121b2c2afe79ae0484fa12e5732772720aa44e128badf42b63e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_copySymbols.js": "4895deb1d808c6234457aeab3b06cc9cac80f6b79a0190fef8fd65d78ae91dc2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_copySymbolsIn.js": "589716ac9ef018b9c3ebb65d8f1aa8bb18b1cda819b5309a6374564cc3e370b6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_coreJsData.js": "020778ed390efefeb8ea3cf88873e9e2818784b3e26fa001ec51fa81c71d0392", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_countHolders.js": "eace52eb448ca61b6a6eb44a6f08a7e8a5c85284a9ed0b8cfa42308a7de7e56f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createAggregator.js": "97daf6720a06695fc9df1e6ea4435cee8d6f5374684ad3803bd05fca54fba8dc", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createAssigner.js": "6c238f9cd9247a342da1557fd97d501928cf7ded1d5b1d8646fd279bcc68b921", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createBaseEach.js": "7d72f767d47d7392b3decad9c127e84b1f6b689d501d400a3960e2bad2551ae5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createBaseFor.js": "ce5bfd3ffa87788ae4edf6764fe3abc6cc75c18cddb71fb65d6de562fbebb511", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createBind.js": "2ae658584f4a9b38711a6a8040a63becd360d96fd08b64a1214bcfe35c36da2c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createCaseFirst.js": "26d8ee22d1f47b7028884569c6151b2adc88b71d50178e7fa5e2659d0af37aa3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createCompounder.js": "f8257824694137bf865deaea983c8e746b6b10c11618dcea2ed3ff4318a43586", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createCtor.js": "adb78b514d254baca36870f300b311640b43b407b31a091787c97fbb4bd43727", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createCurry.js": "bb7d9ba55c2c4dd8be56de1c9f2d069523bfe2884bae0e2973dc88a31083d89f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createFind.js": "e0c3a84aa167ae148ad6f7c72713c4905c0f7a3fb4f30b126cc1f1f496c38695", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createFlow.js": "289828ad2da44c6e98907303ffacb39502a4edd2e2fce27066b9d584c3827cf9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createHybrid.js": "0ae49480f4aee16f3409cb1b9c47d61aa766cff35dde323d19e2fa1733c97eff", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createInverter.js": "76c69af2e66b6fb402472541b230033a658f4b2af323759c9c8e026384171fff", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createMathOperation.js": "a93a24069bddedd4b858e9f5392da394d8c60ced87c8ff653144972a0ba7ddd8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createOver.js": "643dff9445a0e6b169a6eb058a6545560ccf9cb2879c4fc30286c538a8edf332", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createPadding.js": "c73c2d0ef0bebe351d2b0ee048ac7273e90a60cec0666c3c298f8c169c57e274", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createPartial.js": "e8c8f8c0c51465d491f238aeda7873dfff377917724a92c022bdca85ee915a8e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createRange.js": "4ff416b91a379f927b8a640b844114ef9622c5ad781f8cd5b2aac6124dba3c40", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createRecurry.js": "edfa939883788e0aa8f0bf12c96a07281c51e6a29dd3f81c52681444bbfef489", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createRelationalOperation.js": "64e620c96989f0bbd9486c21fc5456b75111c191b90114a344e88d5302d05b0f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createRound.js": "374c5b97c9c6d553a82a23679ce36fc3fc8a1f82b81e4a6044d8bba1a0e8e1e5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createSet.js": "01a8a6101f7e424e34dda54fce6a3d2a9f5791a1112512c8c4ae46dfcc3b0a19", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createToPairs.js": "ce8fd24fb7ab8764930686c63f2db3625b40e00b2b30f60097475b874f0cf588", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_createWrap.js": "447640e62372c3dfb5d6b455acf1e99dd25f1ad9eb2a10d078ef97afd6bbd45e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_customDefaultsAssignIn.js": "1ce2ff74a29bbef1fb3efcdb6940d3194189c647d0609bd590c2a52092a71b53", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_customDefaultsMerge.js": "45a5654f3ed4c3281034c96f3b5a8e5b03e8006c3448673c09de460fd1cc8bb8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_customOmitClone.js": "a60feb9a5c28d976f4740c3c22edc2ca8a728d4bb83e0c5bc25586ec1dbf2ac8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_deburrLetter.js": "acc04cf0abe0b871f287a1360daafd3c0201ad8aac7d618576142fc9a989e21a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_defineProperty.js": "08fe1a52a602829f143cf1a1dd4a133533b1a9cda8ef2071563dcb21750c7517", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_equalArrays.js": "ef1ec1ba10240336affff703995642b5f44f9b9278fa81559e4f7ca231877413", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_equalByTag.js": "72a492144060bc60415836aa1f8dc60949a75923f26d47c6007f16da092ef8b0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_equalObjects.js": "c386d0f1a6cb4d2a8b2803ef06cd69199b2e00c7d3062a6ea8ab9b0308bac243", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_escapeHtmlChar.js": "96451046e1ca9a2e38dafdd0635f163976631bea1a32688bfbed105f1c224940", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_escapeStringChar.js": "b6dacd270ee946173123a15ceb308d67b58bf15ec57fba31a7ed70a51f4f4a6a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_flatRest.js": "366430a7a0db893ad22e3412c7bc3d6a27ac71f332cc27303cf9e5cb524c0b24", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_freeGlobal.js": "0b65c37b3c1ce6d1800cb59a330dde73f1009f0d10eeaf8fd45f7607b87db4c5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getAllKeys.js": "01e917e05dd9a3dce40c4b68fe47fe57b6b5a349074e00a4a60768b95fada6e5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getAllKeysIn.js": "9609ec06ed5e1229cc9e6cc156be0806b9108483ae8bb2e45092c764ea011a71", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getData.js": "3548841b9b3df2daa0177abfce4c0a19dab91852b2f8cd05894e690cbc36a6b1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getFuncName.js": "ae247589c0407dbcc62a1ee3911b8561c9dbb2bb0b83dd8b7e15e59714da0eaa", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getHolder.js": "0e3c1a5eaaa7bca69cd5d0f17b452e6e0444a55dacca036ebca04ffdd48fcbb6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getMapData.js": "4ab0307467b45546bd3d8146d67f0e123f2049e6934722653803fe90b3484374", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getMatchData.js": "fbb4e029c9ad497872f30f15691e319807214aaa074b6a272c1d01d0ee319645", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getNative.js": "b93d64fd14cab48b0ad9b0f1b896ee8dcd74d8d5a20ca9cae6f34aa4a68ae9ef", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getPrototype.js": "0d28834baa350bc2ff483720439732052e2695a44b52cce790c99677fc733cf4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getRawTag.js": "0023dec54160317b70d52df1d7918638bc16a2869b1bf355f0a4f3e755aa6faf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getSymbols.js": "34d1bb981fb74a7c1193363e21f8d2634aca05436151bc2297bd9bbf0cd8080c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getSymbolsIn.js": "beb7a439db355b7f133a0460747b072bcf57f5f85f999b0e03262d2b409206c7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getTag.js": "86877c73f440b311c85b96f32a5e05995f0e247ef86091bc3d5f4e585439f8c6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getValue.js": "afff70502a4a536f971f961811013811c177e6f885d1e7e4e41c48c08ea6d6c7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getView.js": "537d39bfe28a9814a513bf28f4df56ef5fe9d01ce0f8ea2e842efbace90cf91d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_getWrapDetails.js": "13ca3f74dc6a6b64d97bcc4fd62fb72968506622c82b70bcdb683c121b94c01f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_hasPath.js": "48f51da88883825ea4598509101fd21666746f95341cd8f488ffa8911d19a8a8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_hasUnicode.js": "1b9ffe8c8b337c09709647874e132c23f98212791c936da6f0f3844c96112a92", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_hasUnicodeWord.js": "fffc30c67e6bcde00590638d4bbbc3abe54f22adc9fba1639697014bbfa53aed", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_hashClear.js": "54d4b21fbc05b32b246c4449e27a30628f9d8b6749c08ae965ff8c81af6dad67", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_hashDelete.js": "eaf93621141b5c23d99a299c6f8620192faf0fcc71f4f00c01ce5de8f98bc92a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_hashGet.js": "5aa4530201b81f25dc5609240b855f11a8bdd85d92c6bfb8cb2566758bfeaac7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_hashHas.js": "92a91d1122b450660d889520e13f185829e296334b1cc708a7b0009a2c568b71", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_hashSet.js": "6535755fba2510b74079e573dc12f8c4f750131793d658ace64cf128fd1e0db2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_initCloneArray.js": "e29650695eb943a391552749678b039dfcd0c1f25294ffa5eee3c5dc4388f990", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_initCloneByTag.js": "e1edeb1cead74091a0ebbdeb8ccc1ca84248afc08294a1732aa984ef630dc0df", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_initCloneObject.js": "18145c5222148da5ee58b43862e8790ac455f1752fa86e881306a335f403ea7f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_insertWrapDetails.js": "e9d266a74bf7ab11b638f718f2d735e2a8072bfae7aa9f3b83e12d6e58cec4a9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isFlattenable.js": "dbf59e7070db64fa694fb2154a9aa45e47658ae4a9ddfa9667c3cc9fc1e8489d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isIndex.js": "33b5bf1805dd076edfc1199e114995a62170f8d6c835e0035ec05099ae29fa97", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isIterateeCall.js": "b03e71d858a91acdfc4b5b1c00b283e9e27ffd03567a06f6e6ce3c666c7b2f75", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isKey.js": "7ed944b05910095ae65d5fde97c1c3c9d81d38c80900ed7b46a5bc5fd501d98a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isKeyable.js": "207b697e802fc7f957d1275f7da727f59c6673e58ee2078fbc1af56f5f08c0de", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isLaziable.js": "5e9740b450ebd0ccbc505399b89d5eb3407ff26949e145eaaef7499d0c0482c2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isMaskable.js": "8e329fc447867493a58d074145f010969226735ab2de0eb98a6d613f4c5fd172", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isMasked.js": "cc641b21108046e4ebe15220dae4f7ad463760596800ebf31d5e53df1b98f152", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isPrototype.js": "df65c802abe765ca4473d3dd632c0b3d13e9f2397dedd7cf9742f0157b2396ed", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_isStrictComparable.js": "afd62e564b991585e12bc0021f1eb0664efd9f690f572554302fa4d5b8f6e52e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_iteratorToArray.js": "35293928814c0426f5663b9e266c2233bb4ebd7719961ca8b19e3ae7d79fddf1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_lazyClone.js": "b20f52dfcaf4d8f8b40288ce6ad68ff4b5b9f1c0024b32c31eeb1b5d1f32adcf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_lazyReverse.js": "eef3dc2edc97d99b8bd7f746cd7533aa67f4bf4e691ee5dfbc98c122f806cad0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_lazyValue.js": "cb3970d156f8cf11270fe82e8e432b67aa633caa9373555c55294cbf716e1381", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_listCacheClear.js": "e0e283b9176131c84766c243101a75d90566b4577e5c70cc6a38ad6266d805f6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_listCacheDelete.js": "4f60c332ff1ea93b47e744cfbeccb8cc65eed33e7bb302d6bad1fb596f70b186", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_listCacheGet.js": "25d578131e42d496bf92590cc2daa636203fc0512eeab29e2add3ac569921e03", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_listCacheHas.js": "c091833394e006cf700731a117a75310786726246843f225de698e94f1d1e798", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_listCacheSet.js": "0696b9f99b4ed89c08725c8acc805b7cc493078ea0fb8d9153bfb920878fa029", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_mapCacheClear.js": "0be01bc64f84e1252f501dbe68d2166eb97ac321bf6638415478ff24a26ac388", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_mapCacheDelete.js": "a52b87a49edecdb1b47793b5bb26d41e4bf379c369a420949302e1b1562b5af7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_mapCacheGet.js": "bb71dbbe31d7fbd56016b622ea28b4c1ec570a8c910030dc927465737b458a4e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_mapCacheHas.js": "5b9c230d5e00aba2d482404cc8dbe327b23ad29325d13a0e49e93cfd0b1bf54d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_mapCacheSet.js": "43318a641896356f4b187bee7aad9be1147b766722108a7e7425d7e1ea322bfa", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_mapToArray.js": "fd24c54b48cbdcb659a15dc013554a473fa7524b7c5fa2a7f8dc2229c7ad2bcc", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_matchesStrictComparable.js": "73a82ac3f13e1d7a625c36cb4c8de3e85f734b13229d5f1ba37d5db443c7355a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_memoizeCapped.js": "1e8b9c9d029c9efa55f74f048564ca8e36d287ea455698235f563e788fccefff", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_mergeData.js": "6354f0f0fe161a30c83e9b2a3cb6f5826d0d7570ee1e4ac21ee5dc6f6a45935b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_metaMap.js": "f96f29b8f4f35592e571e4ccc756f545270d466a959060d8af2f3d427af39eac", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_nativeCreate.js": "59c4a564c25a021902259cb237cfa331daf056423d359258ac968c60f51aa324", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_nativeKeys.js": "df95037fd30b575dc9a1173fea65d68bd8cdb07ba10186b7cc43c4d7c69a92f9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_nativeKeysIn.js": "e63e54e1eaf58287382c811139a5a497512356c0c192f646e00abd0e94bf625b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_nodeUtil.js": "f81d8e969afead3562231a1c368fcf884b51339ee04aff07a2b67f8ba794f03b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_objectToString.js": "8f1bb6043dc27219e71dfae28900a7f214a0856118a628efeb120169fe46fe8c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_overArg.js": "599268d0a0a7da23b421ca0cfefbea1f295e44c33bab99fb87df38f4ef97bb9c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_overRest.js": "2680f289bb332cf28f3a6413929e2afb46886fb1b747e9f6d33aa50c29ba68db", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_parent.js": "4e6b976ff242a252bd2608dcba0657a81ed6f370c0c21f0c13b180f37fcaa1d3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_reEscape.js": "6f8efc8ea0c7988baab4eaae8b35485d9acc2e6aaded4598b232452d359f0d14", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_reEvaluate.js": "622c801212d03c0500e8878a2609f85c516423f1d518e66c8f7db3c67d1327ae", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_reInterpolate.js": "a88ae2c20a0aee81061b5b99e5f94dd942359f8f76f04f4c33f57f6f15ed0969", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_realNames.js": "26176b3fcffcb2c01ca2b2906ccdb9083465f93abc3e467a48b5b70df3d36e47", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_reorder.js": "8270c7427bba48e02722dfa9fad0d5610f1962be05ae1589df2925b0684ce97e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_replaceHolders.js": "d4d3dd88db58d9339141ee09086b3816a24bb9020c4f21f2c49fb071c2115b65", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_root.js": "df58ff96c454ca91ace20d20e6af49b354c70dfba7e737adc00cd23e8ff77168", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_safeGet.js": "5f9023f036ae9eef9a0196bac43db899fce05712ab4594efe195827eba585111", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_setCacheAdd.js": "401386f4cc34e3675ee768b340e521df8b021abb3b9e8fba9b2a49614d54a189", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_setCacheHas.js": "fc24b8606ffad00aab53dd47f42225ca4a97272b7b90df1db7f75a57cb49761e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_setData.js": "5b7349b9da3ae0b351ef558ff54327b2e44e847c755ffcfece80de3b2bcd713e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_setToArray.js": "316f948fca04c5b007fe78c6edc199a69496beef8d395f44bdc0dbfd7a46a1ed", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_setToPairs.js": "44fa058454fa90a360348bfaaae5880c370c5e95f5f53b5b8174711d27fec3cd", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_setToString.js": "2a1ef073af2370f4bf3258d41bcc62f06ae23a41ae13543e5105946b2b07b03b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_setWrapToString.js": "976b5f3fb5ef356abfb7ff9d0bf81f6988c7b033b58bfce1f4195c8984693818", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_shortOut.js": "1b8768641ee4b76051240ec40b2bfe84e6f21682a71ce6f6f6972363f5b9c634", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_shuffleSelf.js": "5b741f17dd16195e529091489d6cff6afc827f48bbb03e11e6bec71acc79caf1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_stackClear.js": "2f18f696265ae6318deb51419e121ab543b4fd9ff240892e8e054d944db8020a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_stackDelete.js": "ae947fc8f29882a437db228bcdac90cd048f94bdd758e508b53987be2e28cdfe", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_stackGet.js": "ae5633328aae57109e1ce84e70a587c2ba90503294f758ab8086d032ebb40897", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_stackHas.js": "06066c99b9909bdc25bb702b579ca29a69ab478d9819506af29aa7e6476eddba", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_stackSet.js": "4f96a1dfb190aae58f59bc26cd1a27fae1d10abc338e1f57a67e69d47b832cbe", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_strictIndexOf.js": "14328ec9997a49f35a4c16caa01444e079c836f030992197d3f1d7bd45a43dab", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_strictLastIndexOf.js": "3283640f66f1e8f1a0feb3df6ebe1678df08e4cd7781235bbe47e03a4d6e8bd1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_stringSize.js": "609aabd3d549c2f4c7842daa82da6e91d2fa20d403185fbaaa491f4090cfc903", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_stringToArray.js": "7d8a6f7807f071c62b9fb5a4b057613e2c9a13d9cb6fdd7df5debc9bfecd0be9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_stringToPath.js": "273b937d9dbb867c6f4ae4fe74b28e92bdb5108c94ef2033625ef61061295477", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_toKey.js": "9ffff48c982394b61f0e19c2864d371b6bb9b9dc0fef05eab7487ac82012d3bc", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_toSource.js": "a2333d6455d5a8802b8e8d64f0de422f04a4603cf477429d2baabb8021ec9032", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_trimmedEndIndex.js": "1ff25e476022aa45aa96dbea87228e3ff709c1f3b8b82561b7f040279575a2ef", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_unescapeHtmlChar.js": "54c3ebcc97e436abf43a7a4f71c0d480a0edb5e2ba7475836ddaacc660ac1bd9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_unicodeSize.js": "2733ca09ddb5aa648ce6082937e5db8fe14fa727e5e6dc4e6efbb9109ebba5e0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_unicodeToArray.js": "806bbf241de7849459a492f859067be4d3379df49358812b41379a966aae7922", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_unicodeWords.js": "f132197f36da541ee8e6d5f5de2bd938f3ccc4ff4cb4aabff444f21a12e64e9a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_updateWrapDetails.js": "23cb06d622f73c9e60da136cc1e30ed79491ccf2d6ce12b19e14c6b828b010e0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/_wrapperClone.js": "7c04a85a5c71d36436dfc95dcb39f4c937a99ae6244f9ec294d0e293ca8fec23", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/add.js": "7f6c2925110e151d391499076cf097f9b1199648674572cc9e328261092ef3fe", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/after.js": "0229081b187c81c079e6bebf93c13e96b05395d1cd9033aa328d8d05a3501a85", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/array.default.js": "3407c3c87ed010ff369fea0b893763be087085e2cd8b3c71004e98d395e73326", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/array.js": "9e9c6fa6e95b2a2769d5116beb02c9345134eac04405eb33965aff6539273cb2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/ary.js": "193cd1078083505cdb6c1a5ef3dc13b4e2c3912711a29e100718091fa96c29a2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/assign.js": "8a353e1f181cf2de8baae4a60dfbd2c3875b956b5a7c2049b6f4a2822be169e4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/assignIn.js": "5bf2c4357338a69baa94efdb1c35814ada504489bae5e9865b27ef1bc12b6ebf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/assignInWith.js": "4ffcb7b4b3ead72d076f8360e3ab913216327a40bcc4eaa0e435f1930d128fb7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/assignWith.js": "1241b647653af775e9b0f0e15d07e9fb0f7ca9c3e538a0a6fd51b3e96c7d5128", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/at.js": "5529d349fc0d92becd247e086a027ff2457c32a6bc9ba1482366b1d357e8ac10", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/attempt.js": "4a702ab485362a62c4bec0b321966cf0c617847ea6268a3459a02655f00cf392", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/before.js": "129ac5e417675d3767db4f83e8ea87676a203cc1cd89efb336739fe426e4ac24", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/bind.js": "7625bdb657c8587f539ff46211b97ce7620f7cd9510e0f5b7dd7fbcaf4f0a48e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/bindAll.js": "2f23342a27c5f58c19292b0a785eacc247c14663ca45fc31d5bd5aaca0ca7a7e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/bindKey.js": "05a152022093decf741c2338a44b86f77b5d23bd735827f482180d3ac81d3bb7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/camelCase.js": "efa9687baf3a7807bc97d842b891ca6442138d1c5251ee823dddd392f7c6f596", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/capitalize.js": "2382093dd0efd7b2846f8a90376de31a2d736b396b2379ee7d51abe1de9363da", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/castArray.js": "be4e7743cc9d27e0a1f61740d852d3f3d1f89da0e6c56d9a0725f623c44fb165", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/ceil.js": "014822d831b2d6b83d37f17a314eaf1be89f9c48f2a48f1aa48508dd7fc4acc3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/chain.js": "c28797bf34f48441a4cbafa3a0ad19170597f68cae59f4ca4f502c486790909f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/chunk.js": "5e6f0d19752929a6fef0afc9bfeddcc44686a9a405a8289d75bce67e6cd6d1a8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/clamp.js": "17a5f0faa6e079daf1a3ee4ead12160724f40764b47243aeff8c24ca54f63692", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/clone.js": "9f7005c7e7d8c46b0c289ec309ffd769db368a414495082be138880b187f2be0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/cloneDeep.js": "1122beed4773529f5b9ef627486f7f5c4d116438f3e651d8ef6041994ae3a88f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/cloneDeepWith.js": "16fea9f19de6c40b3015945d3be35668e55e1766a82f8bec417c95b31a13d0fa", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/cloneWith.js": "0ae4763070c8501d4c1755a730cbd6091a66556eedd991a769f6be005c6745cf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/collection.default.js": "01cb73b5dd8f48cebdc94afdcf5fa628ef49fb6ba5fafb84bb168f354cc33b18", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/collection.js": "977ae4bf5238b87bf70f281603d1498fd9141dbb66799ae725b305a0f47dd3ca", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/commit.js": "7883addf66a93ac95a59c14e28a9b3c03915f108d6eacae2ce741de3ddeeb30f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/compact.js": "1209db306299566c321915b959a68a8ea8af03e8a8f7e53d3f576a90a6a19bbf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/concat.js": "35001637814f7f173b7f0c1ae497375ae94025839866469c25a3547062908a56", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/cond.js": "12f42783deb73d1498ec6f2662535cc9432721de0d5f6cb8d94186ed89ee4f41", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/conforms.js": "b5541dd308015606061ee2f9017e9668d6c5e9fb6ee7dbee3b0c900596699562", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/conformsTo.js": "4016a3038d3044aed0f906da2c50d8ac2db8f5239fcfe0c801f2f488d6fb077b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/constant.js": "cce774605f469fe731a3aa932019b71df84fe6c47741b018a17e69c042f32ca3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/countBy.js": "d83139f2d46f3fd5d35ea9548b1516158624e89566597ab29ff43c5889ae7109", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/create.js": "b788a99d6a2317558273e5330127d25f9fa9042395f84dd25274e7d28edab870", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/curry.js": "858b008d7ebceb48155d14d01a49384c29235ce99f7b6c9b7f3c30c4efaf574b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/curryRight.js": "ecb50ac507bccf91a2f7d9261e9f4804c66fea777f79de1a180a45038f0c4201", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/date.default.js": "59b87d58a2af80bdcbc9d89cb8bc65093167cb49ec8df391df503ce2288e9bab", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/date.js": "57926fd240f5ef8dedf25959e999d1bfc6dcb31589998300a858611559606d26", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/debounce.js": "d5f2c62200af44e14130d0018a27a47b0d6256220ea41b35b8d8fd0807805030", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/deburr.js": "5af1d51fe36a96701dae2db9e5eee2bda272edb59b7e31da687d0dff5a2ca573", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/defaultTo.js": "d52c7dc3b21ab856545f636d92956dfef31df7794453f4683eb1d7f60b80fa44", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/defaults.js": "18bdda11e735027012c1a3394acedb851307fd76dbac77339b37c7066fc6fea1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/defaultsDeep.js": "bdceb5f7d832ecb8d46502a1fe5bc276cd5f8815abd15c253b06051827ff43be", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/defer.js": "d8163c12cf7312db57a3b46a3182c26848100abcea43ecb91fda9f0947e12d4d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/delay.js": "018e6b5cc40ac4a91027b720799af086dd579d3dfe06c24e916476f6c5f5c4ce", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/difference.js": "937ade5f9b13e047421803813de9c1950e6dc78bbbf928819db7b4d0a80b9c7f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/differenceBy.js": "8cad12f4a1d0535dc7b843a5f68c1f732d9bd7531cdae1e0e178e4378633182f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/differenceWith.js": "f1e333001361fb47303190373b1f1302eccb55baaad6306aff4d243eff7e8a23", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/divide.js": "3202cd1b95da01b3c92a8c9fd1b2cb157115a926b51f72ace0201b0f0884ded1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/drop.js": "5e7ab48513361465840081db445e12bce56a82927cecf9a28a3cc67b4938f10d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/dropRight.js": "4cfa3e1085df9fcbd07ee92c01e97d8b20efbecc589afaca4c34e569f2a88de8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/dropRightWhile.js": "3197f06021d759746b24dc1a1f4768f2d6158c2384d99de959fdcb4484e10c7b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/dropWhile.js": "f8ed889a4c56166cb6322a5c5dd6eb29100099299c10c83b0a9f4b6897003227", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/each.js": "9b18959aa9dabdc1111913bd54763d4311f505515050417fd25b951c97010b41", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/eachRight.js": "02d53374092727f3f8e5a829aa728aefcfb540d5a113abd00ed08aba5352c736", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/endsWith.js": "64e7badf7b50f540a6b5f61b02cd15c3dbaf4a163c51897c246d7969f6f19df4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/entries.js": "9d65625c845ca656c7a89271f144bec65d91cac91cf70c4363b9e4ba08f6d89b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/entriesIn.js": "683b6eeb44a3cb21669302ffd1e1f16d58fb7ed63830be757270dad3665bfde2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/eq.js": "3a51729bde3af1d9bacf9b67e966d8c154e22b02f5e1cecc13f53f7f00889c13", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/escape.js": "7babdce893618e4104b3baec5664f7d645b9a18d38e94ed09c4430ebb0f0a0ff", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/escapeRegExp.js": "e606b284899737aad46b97933513629d580ba98199dfc4ed173738ff6c17c8be", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/every.js": "4fd4c643817bd1b59e4b80835987be9a96168fa15257527353355cfbf03c64c9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/extend.js": "f6c78154c8dbb96761dbfec6566217cc944d30e7d4ec337131b8877123dd1035", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/extendWith.js": "efe58da78ae351b6ada34bc9c1dc9bdb47823a34e937ec4b462e70b7d8e5ef07", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/fill.js": "604c72ba8aa0e1c609ae2ea8779328ab5f3d05691ddd858c177c948bac31ec15", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/filter.js": "3db358055789614bf51d5a094e43b39f4c1717a8f2f38848c6ac267a7d469675", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/find.js": "e7191d086b913fe73067b192543bebfdd9903736a8a6a53a65fcd8a6572c028a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/findIndex.js": "befb111ff4df3e8f69c100c304999c99309ebd9ad059247ec9dcc757baaaeb92", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/findKey.js": "6fc490008497da9eb9850c1a25406bb2358bc5627313c7381c1aff264c0b0b44", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/findLast.js": "853dab865ea56e21636c2447f4bfb35ec396cc6bf10f0462ab1afbcb00a0dd6b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/findLastIndex.js": "66561545105b5934ba8fa4bada50954fe88921a6bfef8cad3bb498151a3bbbaf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/findLastKey.js": "6d3742cf880d29ca676c698109c5d3e56e87eea431e1bdba8cdd28698436609d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/first.js": "0fc33df0ab58d79c58523a9ac27254aba3adfab7c872a765dd04326307b28f9d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/flatMap.js": "70cd946ce9ea074bd57c23cd7452202d511d5d1d5d04f73c6289651c2e1f2e6f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/flatMapDeep.js": "0e988d90ae3735b60af7063a5ca09a2be773cf68a69077f59cea9c9948a0434b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/flatMapDepth.js": "c1294f14b4e404ef1664b7d1a6650a9356337fa357fe572efe42853c648dd8c5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/flatten.js": "e36e2b1c043747342573024964a1a776012c083c60f1d4816cbe6c0b1a62b381", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/flattenDeep.js": "c8f092c0b7e37e25650a07c2344ffbdf03f2a99ebf3a8cb9576d9b7ec0454530", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/flattenDepth.js": "287b1266aef1140eec28a51d0ab2124b2e54fb46f94497697318c115e0ff56c6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/flip.js": "e906dd0bd2f4e01834a15b4a178b1b2a9e68d1bdba589377f862f3eea0fd0611", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/floor.js": "d29cfc10b3142b8a39d0eb8eb8f47cded12dc9f9e1b74e46db27685dd8ed010a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/flow.js": "e6915c341a63d1d8e4851aa78bc63d7b4bddd36e5d88f67084f78b3042949d8f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/flowRight.js": "476e9ae3d6af2704725dead0dec7b8f6fe78c07ad84706f46c2365897571305e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/forEach.js": "5d6b87ae960d707fd0df73f943aea6bdba4ebec9ae7dbaece58132b4670ed4ce", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/forEachRight.js": "aba8edea94bcd692d71d2172e53dbf82f3729fd4794a805a3617acc4275011d4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/forIn.js": "2e88343c022e1cb82ae03ce8e3a66570d33397880ef0bf690f1180f373ab438c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/forInRight.js": "5b0468985ec59e8d7a1af9d4ab17ec708fef757a8a32cc55d8b9a9a7d2ab83fd", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/forOwn.js": "954a5725fba018a5bd2d58b2b4a6ca14258e4eb0f6aae5ba42de6f2866c8cb78", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/forOwnRight.js": "dd45eeb010ecc6abdf26fe28cb1ef40b18d99e31d46e7d0780005e079dc464e5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/fromPairs.js": "576133ee2d5c75536c262974e89d7b15f51423e52b62fba28f838ea0144d0222", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/function.default.js": "e74e982647e015ad15f3bc088a5d91226b5a1a2ff1d1b8e82eeb4047547cf660", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/function.js": "f145c82925da0699ad5c20560752947a3343d218bbc363bb68e1b833e7abfb7d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/functions.js": "f4869ccabf7ba9060bbffac047ecf1b4a67b576acd625c221c4cb9433b76d9aa", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/functionsIn.js": "2fc188a926dfa69bea9901c517d43eca38f9898a2810926e3b0cb19aad464813", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/get.js": "da9982ae72d4ec4e0cb7fd30e691e19581848b7eb8a8b28e6331678285615c31", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/groupBy.js": "018a58cd0c4b430e13668bde16e60634b3aa0cb2fea9c59f359e3a232a343730", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/gt.js": "8acd697a721efc1f7cf5d7b5eb8f1920d132f3e19da022f07f122c73f6170d89", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/gte.js": "ab886f1046b0af3a805fc1cffa0c543a8d3be472e8a43776b2ec3bd5a063fb6e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/has.js": "ef180edfe669ba3c31caea4b4cfbc86cbfa0846e648ef982223afcdccbd5f105", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/hasIn.js": "4561bbd438a9e3c8f0922646d4b5939c6bdd7dfb26deadcabcff8403a6df6a8b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/head.js": "99c64e2bbfa17f0605c52e6c52f72e943581183b5a704d4edeeb7b5e43b2e912", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/identity.js": "152c43149691b31cb9bcdf1be4ba07a3963babbd096bcf6313182d3f2597f023", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/inRange.js": "3a9143c4d621797cefe9416edd42432904c25ee5984759010aa2ea907052dcd3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/includes.js": "9807eadfe07818ac4a5416c6e17a320b648322806f59c2347f9f014436e56c47", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/indexOf.js": "a62b0e3826d12eca7d0010a94753905f98cb9e58358ad9f269792c2cfd0612a3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/initial.js": "d0f93b64d79de4f3e3a6fe89fdaebc8a02a9d18ebee9f90cd3bf30d0a40ec28b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/intersection.js": "e85edcdaf87ee1dad1a669536b0f91db3747cd0275d7c2247a4970a02bf39c78", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/intersectionBy.js": "2c05b5d6d524d2cc6fd0d6e29f92d8c2136efcb865608e0a4182e7e47d25bf84", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/intersectionWith.js": "9233dfd36d5f516d4de8d711b7b4b2bf01b90d32acba3c592541f318bd6ccd61", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/invert.js": "885c49447b9a37e5b5120209a3f433f782b40134d1235f1d9d434f6d4892a61c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/invertBy.js": "b7831df7e72645717e812568e9a60fe766a71540602b7a4112aff37236c8bce8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/invoke.js": "c57c9ebdc63812ea8247e0b84736e2aad53a4e25b651e791d060c0e5b6972b36", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/invokeMap.js": "2fbf852927d5d2d18bc87a9f63e7b6dc79cad0da4710e6ef0e51882231884f77", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isArguments.js": "08791025dc5d7ec5183e290aa2aa6a9c4af78703f0d42d1b9421b607c3199fcc", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isArray.js": "0d9bcdfdf53955d9253076ddd68e0b992b2c91fb8364baa2ca971e57cbb74e4b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isArrayBuffer.js": "58ed2a85326d433456316ef13a8bf12764d110c36e7290b8d9f51e0b8a1ce463", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isArrayLike.js": "46956ba526a01c3371fe26a492bf2a247b20d995e6891f2ac99a17b1ab64b49f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isArrayLikeObject.js": "76434e9c893ae3eaccb555792ea5754d42b5765efe5d360d78d2c4abda788ecb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isBoolean.js": "02b75dab099a4ce0a178fe32fe671fb4350133353c29916ecc42d035cf6bb5d1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isBuffer.js": "683ac01368bb0b51e7571332797cdec13cf5fa26a79ad20764045f01b1d249d2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isDate.js": "f58ab84e10e6b7f61aff5badb9f587944bf90d0a267de196ae6fe9a36fc60360", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isElement.js": "f71bd89159ec8f944b9d1325a2b084368e34953d0d1a712e6de31ac66c89cd7d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isEmpty.js": "a2ec3e96e3395946d44bdf0229acc8610c7a4b07e2f3910a424ef4a704eed178", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isEqual.js": "60c66b9d8e67d807bb91d2487f05a5400c14031b4c59aee9920646529d494d17", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isEqualWith.js": "b7a667d8aab3dbde3da485c7b172602a810d5ef152acbb08c04516e10fe43ed6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isError.js": "bcd71260aa1aafae10ea606d6278029a4c8c56ed892ced917a1557e8abff5a05", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isFinite.js": "a0e3d653638a3b4f73b0876a7c55feb44866390aebe1d534f3933a90ff8c3e15", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isFunction.js": "4cf0f598213e142e5d9ad09f17d3611723a9e2e3def000a39edbf38b493b3b08", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isInteger.js": "a2cd7df5c9b4b59e06836cf58b3da67e52f93683f224239de6baafb3944d1042", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isLength.js": "ef8193123dc3fbd12be9893aa39aea7df0a779074e604d853c56da1c09d24e11", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isMap.js": "9cdd2a2b80768554c2d0d88c8fe8460f07d55f9832ca1149a3156d139aff572d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isMatch.js": "7f6553a64aa2c87984ca18035cde4296c856c635b5f697eb4d9036378f7e31ff", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isMatchWith.js": "68d9416b26ea6a90ea06a16119820bb27935afa9360b62806f7b7639f9da36ff", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isNaN.js": "37962fb2480574d2050eeb774638f4b57f9e104c10de55d064e40ae10e1b3d09", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isNative.js": "59cf58e3e1782714ae7d07a3230142efc4e07c54bb725a330ce3533bc79de9c8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isNil.js": "cacf48eaa3b43b80d6912a04f1abb5498ced464e8caecfd08393007f5f0bfc52", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isNull.js": "182b7572104c372bab9ef321111bdc0eebb279447386d649c56e40249fc0c89b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isNumber.js": "4d47a4ebb813272cf997cd008c2ad8934290a1ef7bf8ab31e39c819015dcb86b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isObject.js": "fc44c8f265db1492ea04f777d9e2d8598b44d481f5eb38360cb58c1e45b8cbd9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isObjectLike.js": "e45d69136695e8568c6fb3c7327f96ac1a8ff7284b1b22f26fb4654255fd0c6c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isPlainObject.js": "f790a22fd205b03f11f95fe0657c9244ff48e45363633fa110c3c5688177b192", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isRegExp.js": "427b4185103740e642df855259fa0fd2721c3051485a737e67907df6c0c66607", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isSafeInteger.js": "5deceb0b1e05d89a60cc4f52c635e920d0edf2bc60b6ac60166be8c59463fd83", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isSet.js": "37c903d4dc8656e75a406a2a71577858bc1f61b40aaa610fefb20423fd9dbd4a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isString.js": "91c39c006bbd6924da8a18e9515d273cb1973e632df6b36f713a82f65e864df8", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isSymbol.js": "f0dc1666c3cec2306cd4fd0f63a9a7ab1f25ed21d2db6664b1edc81bd3641a41", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isTypedArray.js": "ea3531a60d17525b79f484377cd4053f32fec478822dd2b51ca63ade12520737", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isUndefined.js": "4a250b584a4809b8410dd5e28271b499ba15e1133a3b63081bba7cec5a455c86", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isWeakMap.js": "af2fba7b4236f498b7fdc8976d4166487d45785d8ecaa894484e99939cc7198f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/isWeakSet.js": "4504ad61c4c35bbdc870ec9fc6be4e8551e1c0853a8f1b6b8f3e8269db4bf5d2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/iteratee.js": "a3750c4ba68ec038bb6faa9d24024ceddc46f804ddae46f41d59faef07f18357", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/join.js": "2f791dd3d33d7033f23af1bcb523df3910f26ed52a4eda7a098e0e30ca55496d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/kebabCase.js": "f9324cf6fe6033abbbbedc636a5a7127968ada7d06f38b5c47efe05bf08a7d66", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/keyBy.js": "9c856fcc32633fb3168f1894cb8bc293f801bb0befee8d67e3450ff0d93fad43", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/keys.js": "cdcc1e10b113af10125daaae5cde3135d581e7f53a22d1190fa12bee62c3315e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/keysIn.js": "fba7db7b7420f0cc87b6636aa748e8c5f44e44cb5408fd33d1fa967b13f0039e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lang.default.js": "9e89d3b5b6f56ff2a98a1c5826b231a99f77dfbb1a8a0e6a8f3ce5eeb7de46cf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lang.js": "b99f39bf01eeef121a1b61b23f2a5fae649fa0265c2b543095a834b75af10ade", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/last.js": "8ce00d6b783b8610a3680ab555a833f65a6bf62aa8304e85e86fb7c48d6428c5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lastIndexOf.js": "f2c4fc32233c698c8735f87606f0ad99a31ffabfeddf989e3f919bd8e4fbb8d9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.default.js": "ac18a136dab11f603f6f0af42ad185c97c6269841895f204d39e100d6e78d8a6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js": "c38ddbba4024239edef4119461cbf48d78be32b8186220a8ce7f0d043cd22a79", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lowerCase.js": "25d608af760b31821ccd0565cab57a0c4034ab9af4ccab196ea86018cf23f9c6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lowerFirst.js": "fc5b5c8c2cd3e5f3359822b4aa1d27bdb08844135c7fe771fa18d467a95b341f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lt.js": "7ea7274ea0c4d91124622475fec6acb7125447829e435a9ab2d46bc6da9d4fd1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lte.js": "55f701715aec867a37e168986be65ce7db931e471f29372fa4d8f5e48741e11b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/map.js": "8ba15cc10575d929729df276ac1515d7087730a3637c277a9ba90ab0b0ccb820", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/mapKeys.js": "bd94b719a21c6a28e1c92303503128392569a4ece207b0f55073c88190000536", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/mapValues.js": "2361d940db5708060af41cf88d2421831a34b5ee1e87b8a8538c27ca3407f5f2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/matches.js": "f019b55723ff2f6e7ca4d2bf35c6cb33dfb40f9e72de432d0645f8327179d158", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/matchesProperty.js": "ed63897c7f1ad105a1c95e1aec94252a069107e7a1b8a3fcf254bc530824edf1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/math.default.js": "c132993e811b4de1d4fd8d5e277a0fbda02fc83b595a94beb170b0383657fe1a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/math.js": "bcb42f38a6d2d037d4ad66d45719ae3c29aa117f3e7c3b9adf6ca172d6b06035", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/max.js": "9cd0724f9afd1e529a3efc3189b290303fe5c48bcd854259be9272a1499ce477", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/maxBy.js": "4477302fc2f050728841f8f0cc177fe83cbe3bf84b6b2b6c76f90f406c34fbdb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/mean.js": "7a075dfb56ce29746e829499e385ed0e2667e88203cae0ea3d7a823f63462beb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/meanBy.js": "24ab29c6acd38be1170fcac1c91eaffb1feb19c5e32976ee557482e1ed3bf58b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/memoize.js": "2a38b3b9b90e1592565de2168467c3daa6a4a95610cd3afd8890208a7c02c782", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/merge.js": "8004f9d4c737e69f5d871ff61e2966460c907036b7a514751d66f8f9b06572b1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/mergeWith.js": "daef90b174930aabe2f79850603ece4931b1df9d592dc3d2d77bd99040623974", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/method.js": "c8bd447c33447d1efc9d85196453631dcb3201c85cbaa3d7237280c843017753", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/methodOf.js": "33754a912fa0b51f5064bf737414524054110793ef70a7a2401a1ce8faeb93b7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/min.js": "7bdb9a3ee63ecc3ddd995a8356b10b8a3c10b4e213415b41caf64885240d2609", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/minBy.js": "24f6b663d3dd388f0353a203bfb3f158503bc2ab9d7efa229a756443ae0a8e0f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/mixin.js": "a0141e5e3fae3c0327708840ead1b34243f3ff4c43bb0dd4a3a3a886c5eeef05", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/multiply.js": "bd25a8797114fc5e00b432f75019df23e6a9b015009ef89b0a0a0d10b97301ce", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/negate.js": "461b37451823d07c678fc33e6d237fbf082922da802ffc756c3024f1c0addffc", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/next.js": "c2cbfd2d4aacebd559d8b2eb8df8747e13480a151ac2c7dcf4114fd9543d3110", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/noop.js": "b0dc31de67593b6fe56a49b13f3d7da222668daf3f8ef7b475903d30a98a7331", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/now.js": "1304c9968c6c069d6529465225da0dbccf18055c0e340eda213ce6c47565a58f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/nth.js": "44200179398b85cd2b2d868899a2251551212eec706a8bdd7c761e6549536cad", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/nthArg.js": "233ec286f68e7128370453608a895259159b4987d63a0a9d188dfba45758b403", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/number.default.js": "f7a5aa9bcd19b5cf1f224cffd61b6c305047fe245b5a8444a1cb70cfcf5054ec", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/number.js": "c2ebc2da8255d315df4a82dff158c62424f3e8917818f59a698828e8e9b34cc1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/object.default.js": "8f062031cd3241c9ff128910bb4f2cd69818b88a84b68ce3b2ab5f21c7da25ac", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/object.js": "5b1a01f3756508d03c794a70c755429f4741a413b40530c83dab7057825e975b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/omit.js": "de577fc677627a1381062f61549754ec88049b759175216708bdeb08984b5784", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/omitBy.js": "4c6d3f52653c623a13967e03d84c5c7a638cb118c80ca5c157ff60d347027701", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/once.js": "b6d9d8f42abf3ec177d6bb3632fbe5db292441c66018ed13072665e4828d3366", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/orderBy.js": "748bf0a440c4fc0200e6ef7158c10c695bf3451ed952242b3556fbf954550fa5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/over.js": "16238cc4efe5a906dffee54298abd504e9bdf306fcd30b44fe7a22e6a9ddfec5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/overArgs.js": "346972ca09cf07271977ec84d9edf6e86e0b5b3b4ac1b683d081336a68bf4fa2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/overEvery.js": "29fbcb5593c6c7daefd81a536537234929625dec99d13df7373b64337afc1585", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/overSome.js": "36d471202c49a36aac229733d6acf61074a4fcc59ef9d63987e5b0e002384011", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/pad.js": "8716d6d74fd135c6a8a6d7e974e2fbb8aada314822c36c30ac559ce0ef6ba796", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/padEnd.js": "7cd63db7530ed1cc4fa79fdbec6d96f5af6b7a6aa5dc6d0347bc2c83bda7521b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/padStart.js": "327dee6206cc7047db081142ddc3ac17b717ee2949a43405e0e20a02feda08d9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/parseInt.js": "8dbdf16a45391777cb57ec9ee2e0e129a8aaa5871c4e2ed32d4efb81520a1cd5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/partial.js": "0851972788dce2f4e0f3a69322a45ccda88bff73640d17dccbdd27e4ad9538f1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/partialRight.js": "ca155c45a83dfeb4ac343b195904504788c51c389d5d61bc869f5d420179b181", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/partition.js": "3c45e0644b7ef63d784f48f953d0e0b735d1a1b2561af343717614c3c8c9340b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/pick.js": "5c84703ee65d5c937b8565c4f55875364cd52e983529f9d984a735eef283c3ff", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/pickBy.js": "9e1d4befd724af1ae594bcf53ca198cac912054f1f62ebe0af94f8e89b7de687", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/plant.js": "9fb5f2532d4482e38d4a6829749e8c3a86fb3849d86aadbd50901ab05a1b9581", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/property.js": "e922d6d26711a4aab2654fd17e0d1e59c4fbbdcca9482ac047f9ca90c2217e2c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/propertyOf.js": "a85c19e48ee98afe897f31d6581f7f145baf9a1a46a2c95ce64068875d655f10", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/pull.js": "53374684e47e67d720aeda1dd69e18453ac333570720d63940db8b04f0ea3e61", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/pullAll.js": "0a7465cdfd73e47dead2472a697ec442895392b094eeb4309d93666a84ff7955", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/pullAllBy.js": "e735efbc19b88c4e40bba4860fb8486d3715ac66bfd24407fb3505aa1d711901", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/pullAllWith.js": "7d140bd317ff00966450e65d6cb43f310ccb681b1b3f0cd1ca2daeee2cf01305", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/pullAt.js": "6e4de44eace5e186d1d623626aa4aee6473e37964c5296081c2404bd6ba01d09", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/random.js": "119b40bf65e8709ab233d6403b9c6fbee7f2a617bdc7f6334e2c755abc66e2e4", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/range.js": "30e4286aca42f47d01e32466f4cc326fdebe5e06f8bed8c87cd9d2753b037915", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/rangeRight.js": "83366149703d4b593ac8ad4c09af0142c3cdea41b905903091b7def7dca910f1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/rearg.js": "7793b212aef31b6963ef91fd56f2c75e0f348c51992d8c87d723ecd7cd95f973", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/reduce.js": "6aae033a085de22c5b6318da0f6142a026183a0828fc5b20c62cc8c3647aa68b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/reduceRight.js": "e93f34bbdb333d9968c40a8c3eb7718c6a86fd0de3ae341079b554bb4cf1be57", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/reject.js": "5db125b64943ca59af8ff7edd7a5ec37c23cf291c16138556924447c87611711", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/remove.js": "250bb03dff4c1c1c1c33545ba1cd04af1bdb4cbfcf950cc0d639e8fcefaafdfb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/repeat.js": "6fa2ff6386de5da11494c9082690d7612bdce435c6e87592988e8d7e21e4d488", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/replace.js": "e8d818b14f93bba6ade97919cac8f5370912f1f430432a2b75f458c195b76cc7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/rest.js": "1428e6fc70b2684561964fe5eb8352ea0364a9ee5b46f5620371f123cfa229f3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/result.js": "5fdecc93f02850527024ad98fb2e098b91eed34923312023c7e4c2a1c2a1ed6d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/reverse.js": "3741dfc2453a170236c4cbea43197d105402df608c2b9336fc372f7943b4f1c6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/round.js": "cb6c0c8b7596aef61bdc145d18c7caa0e07718cb587cd904716bb50753a2d9eb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sample.js": "8b04bf4fe0a86e05894652d22e1ebe96c762ec4c0f1e93cf7e8139ac68250eb2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sampleSize.js": "831f09b947dd21f9bf4fe26944b981df46c198b6ce023af242b3c040fb6c75b0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/seq.default.js": "85d89c5533fc51ab3760b2edd0c1d4bec5503ee60c4f2b98d3cd04f11276a69d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/seq.js": "929986548a10e45b9ac7112cecfeb5b48167430633e50d9e0f4ec67c612467b7", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/set.js": "7503458fc8d205755377bf365b24954bd19b63665068ef3dd1574c6e87ec67e0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/setWith.js": "861cd9425ed707ca61c34ad3caa4b8f97c3f5f42255826760905bdade30ea38e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/shuffle.js": "ef326a20311d872f8d7fedd6e90417a8f828edfc3a10bf0925118ca17082aa7a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/size.js": "1caf9f4be482114ffe1acb122d8566bf5da0581026f67905a0049f3fe3fadb2c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/slice.js": "a85183b29ce9d44f3a040511fb22c7240f9fc9394bc42a192972939629a3e9c2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/snakeCase.js": "055d2713e0c99bcbb0bc56eb309c12c126998faac90ca7287819b5e5b4c9e32f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/some.js": "f4300769ebbf2f71ddcae7e8c8f1a44817699002212a9a25eb6a4271188ec49a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sortBy.js": "24a2d5a13880d6aac426e839248ebcaf401ca6231720abe4e7426a2c06e77c5c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sortedIndex.js": "73e22e1d0b28fdc38325b253151fb4fcfc3d0665c8af71c09eeed4326d6e2a7e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sortedIndexBy.js": "583cb3e8af550fb5e162b2ea1a7ed84cf7655646c39f551fcf763e8b02cf6af5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sortedIndexOf.js": "99d1f781190ca698e18bb20fa286c2b4d43ae0a1fe57c48f130255916aa90d44", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sortedLastIndex.js": "67658f39de67a6a24296a71f91b0cea27952b408a2a78055931232fd6b117806", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sortedLastIndexBy.js": "a4552a970fff174cf62ab30cc17a0a5451e5122184afca0c43049803fec68cf1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sortedLastIndexOf.js": "0e52f8e23cf9c0daf6eb5d793299fe11fbb1b8be1bf22d2e66cf10636adbbe41", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sortedUniq.js": "0b23aa21cf887a1db228d20ecc388f5cc4ad6a0f40a3e8935d4aa44410a69c95", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sortedUniqBy.js": "e2f662094aceab8ac43ffabcf53745bfc9d08fc5e718f60e317429e32f623326", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/split.js": "354bc0faaf242c6ebc8218ea03d9c112272978010c385e4fbfd774d4d8103870", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/spread.js": "e5c22f18dd30065de7cce993793c7536dabba4c82a6c151ea2fab4ebc9bc41e9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/startCase.js": "be0a1c8b49f1433805aa9ac19493128132cb08210515717d5c725af136095667", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/startsWith.js": "0c477f52e8333dfa772e571912590821a980efe013f2d79e0fc9575df17452e9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/string.default.js": "f3485fb9b6529688414d90aa81939d8089833dc0ac17cbcba9dbe3e5c1c3da3c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/string.js": "7a0743b5bafcde641e5c3e80af10f9c6d37a5bf5b662590bafb79a746930d77c", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/stubArray.js": "fbb65461e7df8d72ba98065d2db95b3b781a66c58e396e2c76f8264d427b705e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/stubFalse.js": "fd1d278ab0f9329d2b39c180bb97fcbfb609ec117f89443fc2ed898b633493ea", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/stubObject.js": "a5ffecb629ea2040db6d37e73e3672c349397cf315a2af619e91d115efe41cfa", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/stubString.js": "cfe6783a15bd424881aa87530d01a0a618e35d0c7f2cfe87de3b6ee680a1e2b0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/stubTrue.js": "eda9e41310626951dbdca8c9b52dd440382e3e8bc0c6cd99e8a36658216ec91f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/subtract.js": "29eeabfc41be5aaed5324f567783bbccb7e3ad4a81a911ef6618e9d4c83e7c51", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sum.js": "d34e99934eccd924c73f914986f937929a6df4fe2e8899e8dcc3fcca79647bcf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/sumBy.js": "58f18be82af0409278563c13ad72f2a39ed67b43028a5cf71a434cb6c947b057", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/tail.js": "9c3cc597c96183300338b1d0d69206df264a0dd5bd263613162b3da6174f884d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/take.js": "b2a983f35fc0888464592471fcd9ae72f33071420dbda1d6a60e01c2b8cf0725", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/takeRight.js": "6e427b970813a00c2650c8c40dcf840b64f91e4996363eff620493c859dbec5a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/takeRightWhile.js": "1c85009412c448e2a1cf44a6dab98fe588cc74c1b41be5611bc938a6b07d5d6e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/takeWhile.js": "9e4367f55e8826c2bea398783fe24e4eb00fac7fecea4cafca158f3a29555207", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/tap.js": "adc395245a0aaf4a5635b68441bb5fde116035c1c43738840d9792d7c862c477", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/template.js": "9b2c0d8fff7df15d9b6b8031dc8db392c8d2ca88cb4b5e2f80c715de4cf09229", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/templateSettings.js": "319ee09d1cbd22dab0bc302e3935c33da9a1432888817a88407076156dbeac85", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/throttle.js": "d236ef311beb5042f68688ddc473e473edfe9be3b1ec5806431ad97828f4ac16", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/thru.js": "c7e1b7a237dc3668c88d64b3fc9c2b1d6bc68db50949c8b10d73b2101c8cb55d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/times.js": "e1a725236bfbf7ea2d245694e88f3a4eb2aaf310bb1a115b2c2cb48f0b8d2080", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toArray.js": "9dfc67af6a6b4506ac5ec28840c9026e473637e365cce55dfdeb300e2d4f2280", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toFinite.js": "ba02408b127cd0d31ad263fd21aac6a6a7a66257354b30afe897e6c1da166e94", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toInteger.js": "f727679fc4f778f078e768df837d2a68f8d3430217cf2a3adf536d784d1d921e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toIterator.js": "f9be4cb6620b531ad18eeef95b60fca80d61736e8c7eee9e3c61a05040a6b2c2", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toJSON.js": "1453c4073e4a4329d407b4e53ce8b9974e2f802c8ba07a5d0bc62b857e58cef5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toLength.js": "de08d40fabf2709e153e60f19dd110f1927e6be6c26ba2f1b713fd446a627786", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toLower.js": "94c42e39060ece35a2046d04daac3638d4915447df90190f26e6a391ace2c352", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toNumber.js": "05f8ad055069ca69ac0c91112a70906367e167a088bd4d6b84f15cb0b100a2e0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toPairs.js": "3fa74c8504fbc8d87955c76616fd80bf82b9c1d3fa001f103fc35366cebc111e", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toPairsIn.js": "7dcf13df048e33f74cc8bcbace35961feea077b993d07b901c4508fee90f82b6", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toPath.js": "59aef05ec627bec95468bb333e199e2d2fc996182baf6501534b34373d0deb61", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toPlainObject.js": "9345d80139f01e8768e0a134266ea2fc1196c22afc0f4022de66cb9a68b52a70", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toSafeInteger.js": "ce0e7eb4532d5949eb194fe5404ef558669d57967142e97f3cc3e893a8365023", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toString.js": "3f4c8d942944c3fe622e9fba36275b6291e834113619b266909822becfa94976", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/toUpper.js": "fee2b732b867e3ccaeb69a1c2f98d54300b459841e17e576a2142d237b057435", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/transform.js": "508fbac08dd70532c12dca1e1efeb5e937095994580aa306352785a135cee993", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/trim.js": "251ae2854c76426244485216ffb88b61e8948a39239f57d9a69407b7627e609f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/trimEnd.js": "475f85bcb3ec34b3de8f3e0c323232179988880ad059c45fe23b7574b68f7395", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/trimStart.js": "1c5b3a8e7226a8bde644c0836f1ba2e8c4b2e6debae1aceae08300dee88ed37a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/truncate.js": "b116e6ed31492932587acc457c1ce683cb864ed06707e951514e342d2b22b0c5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/unary.js": "a970bfadd5311ae210819c1c4ee40efb5fd10f7285aa70ef3a18145a50449ea3", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/unescape.js": "3ace360684af3a8e3d537b294ce05ec377cf2fb9c1b9002720a8f9e30fa36b3f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/union.js": "16c4fb976d5485b2ff9b5368dd260696957adb1ffff33377208a20488dba2205", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/unionBy.js": "47f2561ca2ed97fcc07c5823c03b973283a5da0931619fc3cd297ac62fedd404", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/unionWith.js": "7982c75cf58ece511df61df6506e1dee0c82de03db121a4e2d73ea5e74f8de04", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/uniq.js": "aedaa761b16b87507196d94179f7bc4983ba86e0c5dc7efae58f883fa61f8679", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/uniqBy.js": "67f54eed7b2feb663054d029e663de822c34f0f00f91ff28b6884e1f2309f2a5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/uniqWith.js": "0e5b825c82b8f7bba2353dd65d9b88a5544906b8a55562ad8cefca6af655874b", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/uniqueId.js": "f33804ffeaa9f151d528813b65cf4bd1dc3eae001bf3dee63423c009e0c40df0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/unset.js": "c6033f2817b249f7a323ba5bb5a8a6ad47a4a30a41f09c4a69afe13d9fce204a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/unzip.js": "8003e42eac6b1d40d48896ed9428414e4891bbe3a231ada9dbeb1e89e577ccd0", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/unzipWith.js": "a934f1e9f84a55fbbe3d41b2a42d732cca04b279de9542bf44608ae85f496795", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/update.js": "d8ddb4114aa2101e775c1d64d5d475208ee9b9b72753342b9516a6e085217426", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/updateWith.js": "2098b44cb6587176812d65c78cbdc159095ffaf745d8055bba2c1e242af03c5f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/upperCase.js": "70747558f308524e539d53d0c039942f310cdb26f99176ada4d57f1b22dab909", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/upperFirst.js": "1923b5661a65f9efb07814acca2c33d5f31e7e38c9021f7db432f1d03dc2bf05", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/util.default.js": "e2751dc8841c303c27c951703d0f404e75429a42e1e5d273b63c505fdff9a97a", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/util.js": "54ab6bd0ad9847e4d54a5f6ccaaa2d038539ba742516b2b0c542cf820e16dfdf", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/value.js": "1453c4073e4a4329d407b4e53ce8b9974e2f802c8ba07a5d0bc62b857e58cef5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/valueOf.js": "1453c4073e4a4329d407b4e53ce8b9974e2f802c8ba07a5d0bc62b857e58cef5", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/values.js": "e12a6336a23ef03323b05f52df5a04f76c76bfe831dfd7e0143e521d6ec96a6f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/valuesIn.js": "056b75ae09c36b3c628812813c85f1051eda94b664ce02147aff19f79fc98b1d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/without.js": "41af88c983594364a34d470dbf651bca7ffbd1beea207bc6b3ad606d87feca78", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/words.js": "a999d2d5d77affc8fa93047e232fc80183d1b05b3b05c0be58780a3624adf52f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/wrap.js": "a1fb50883c3bbb8165397732b86c382473028d4856721ed161017489c7a90fe1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/wrapperAt.js": "985f9b4a1694e1f389bd9efb3254d8861a16c9ceb683d9ceeef6b90c79b2f267", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/wrapperChain.js": "deebd61a77b4fab8be5464043e04e9ed74cabb8b91138471a0a27dac989e8efb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/wrapperLodash.js": "e274921afa7b72319d09c91a4ac494819ada924e6070c3092b0963feb110b7e1", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/wrapperReverse.js": "9e207c5f34f78d98f6ef1c51d5a2afbe5cf9892b73859f5f4199e4319cc41eab", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/wrapperValue.js": "26a1a5d2d17aace1d97a97f7242193f8de2a4bcd462f2ddcbe048998da01959d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/xor.js": "a67d291736b87537b6018d038517aba170244ca381aadce572ab42051edd6922", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/xorBy.js": "4dee71c05e32209d14d58a50c7019295fac137f3cef63101871e8bd8bc81a3d9", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/xorWith.js": "07193048382dffe5dcf6f8d0fa284299aea90779621f2982c6db4475fa41e41d", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/zip.js": "4c27073c4d77c0233fe9000da62de50991bc6806da0aa446818427664d2fc780", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/zipObject.js": "e069bf473251b19f39587b9bdbfce017d0cd0ec2abe835fedcd1c9ab68a0ac4f", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/zipObjectDeep.js": "5ace286d0c9b25960c1e1ffd11e4d1d9eb10c847c99d2ac6ac56460367262edb", + "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/zipWith.js": "0ff58c20b2716a05db1d0e72b94b4e3c55c6e293e6e6ed8c47abf17706fc9a5f", "https://deno.land/std@0.207.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", "https://deno.land/std@0.207.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", "https://deno.land/std@0.207.0/path/_common/assert_path.ts": "061e4d093d4ba5aebceb2c4da3318bfe3289e868570e9d3a8e327d91c2958946", @@ -554,6 +1538,7 @@ "https://deno.land/std@0.207.0/path/windows/to_file_url.ts": "8e9ea9e1ff364aa06fa72999204229952d0a279dbb876b7b838b2b2fea55cce3", "https://deno.land/std@0.207.0/path/windows/to_namespaced_path.ts": "e0f4d4a5e77f28a5708c1a33ff24360f35637ba6d8f103d19661255ef7bfd50d", "https://esm.sh/@emotion/css@11.11.2": "864e545ec51febc099b7ce1e2915ace0fad12ee646b587daf13a23d291ea7059", + "https://esm.sh/es6-promisify@7.0.0": "d28be4176e805a0000497ca2630b6ef87602bba79405444267d23922fd204749", "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js": "680949c848e5127b5437649159f16c2fb4fcbbd124bc727f059932772a7bac97", "https://esm.sh/interactjs@1.10.26": "3b379d36d5893e6b157e17d874081e22582dda06859eba240d90c97f2c4c8d16", "https://esm.sh/lodash-es@4.17.21": "460848e64379750386465df2bfcbe530dc5be584cabb39816e0dec105e5f9af2", @@ -568,6 +1553,7 @@ "https://esm.sh/v135/@emotion/unitless@0.8.1/denonext/unitless.mjs": "8a91ddf8c10553143e87b383d23d9334637f36cccb930d7d65990f8002a6c091", "https://esm.sh/v135/@emotion/utils@1.2.1/denonext/utils.mjs": "5f6e36b10f6dd17b64c30a1b49079d5d024cff498e7900e6468b8ee93342004c", "https://esm.sh/v135/@emotion/weak-memoize@0.3.1/denonext/weak-memoize.mjs": "05e059e6f679168e597ee6b0dace8503031f54678460fc5f366025f40199e18e", + "https://esm.sh/v135/es6-promisify@7.0.0/denonext/es6-promisify.mjs": "3ed7ac8522f9ce5d845a0a31daf279fde0f8082c258e08bbf256f49f84a22a5b", "https://esm.sh/v135/gh/observablehq/stdlib@v5.8.6/denonext/src/html.js": "5389cb3fa08f0e1488d9bdcbe9aef3b7f919b166d60a1b04ed53da43516cc002", "https://esm.sh/v135/interactjs@1.10.26/denonext/interactjs.mjs": "8aab44525c4b52a1180ba2647f8faa25ea48e05832e0671ed4dab33364cd7193", "https://esm.sh/v135/lodash-es@4.17.21/denonext/lodash-es.mjs": "ca6d37e6a1271d1bd8920da5ff11f7d05465f5d7961d5d09b44db331838981d7", @@ -575,7 +1561,9 @@ }, "workspace": { "dependencies": [ - "npm:@emotion/css@11.11.2" + "npm:@emotion/css@11.11.2", + "npm:interactjs@1.10.27", + "npm:lodash-es@4.17.21" ] } } From a80dc3c2a792a2d086e79bb274fa478d194ccf4d Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 7 Apr 2024 20:45:47 +0200 Subject: [PATCH 16/39] some changes semiworking --- js/index.html | 24 ++ js/index2.html | 16 + js/jsconfig.json | 7 - js/src/clipboard.js | 959 ++++++++++++++++++++++++------------------ js/src/container.js | 10 +- js/src/global_deps.js | 32 ++ js/src/prehooks.js | 6 +- js/src/styles.js | 275 ++++++------ js/src/typedef.js | 88 +++- js/src/url_deps.js | 4 + js/src/url_imports.js | 8 - js/src/utils.js | 59 ++- 12 files changed, 929 insertions(+), 559 deletions(-) create mode 100644 js/index.html create mode 100644 js/index2.html delete mode 100644 js/jsconfig.json create mode 100644 js/src/global_deps.js create mode 100644 js/src/url_deps.js delete mode 100644 js/src/url_imports.js diff --git a/js/index.html b/js/index.html new file mode 100644 index 0000000..142da5a --- /dev/null +++ b/js/index.html @@ -0,0 +1,24 @@ + + + + My Simple HTML Document + + + +

Hello, World!

+

This is a simple HTML document.

+
ASD
+ + \ No newline at end of file diff --git a/js/index2.html b/js/index2.html new file mode 100644 index 0000000..355af83 --- /dev/null +++ b/js/index2.html @@ -0,0 +1,16 @@ + + + + My Simple HTML Document + + + +

Hello, World!

+

This is a simple HTML document.

+
ASD
+ + \ No newline at end of file diff --git a/js/jsconfig.json b/js/jsconfig.json deleted file mode 100644 index 065705d..0000000 --- a/js/jsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "checkJs": true, - "baseUrl": "./", - "lib": ["esnext", "dom"], - }, -} \ No newline at end of file diff --git a/js/src/clipboard.js b/js/src/clipboard.js index 57dfcb5..14f5bfb 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -1,324 +1,458 @@ -import { interact, html } from "./url_imports.js"; -import { delay, getImageOptions } from "./utils.js"; +import { addFormatConfigStyle, addClipboardHeaderStyle } from "./styles.js"; +import { delay, getImageOptions, image_options_defaults } from "./utils.js"; +import { libs } from "./global_deps.js"; + +// Download formats +const valid_download_formats = ["png", "svg", "webp", "jpeg", "full-json"]; +// toImageOption keys +const toImageOptionKeys = ["format", "width", "height", "scale", "filename"]; /** * Function to add a configuration span element. * - * @param {HTMLElement} CLIPBOARD_HEADER - Clipboard Header element - * @param {string} name - lowercase name of the config span + * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER - Clipboard Header element + * @param {keyof import("./typedef.js").ConfigSpansObject} name - lowercase name of the config span */ -function addConfigSpan(CLIPBOARD_HEADER, name) { - const container = html` ${name.toUpperCase()}:`; - container.label = container.querySelector(".label"); - container.ui_span = container.label.querySelector(".clipboard-value"); - container.config_span = html`` - container.label.insertAdjacentElement('afterbegin', container.config_span); +export function addSingleConfigSpan(CLIPBOARD_HEADER, name) { + // Extract the dependencies + const { html, lodash } = libs + + const config_span = html``; + const label = html`${config_span}${lodash.capitalize(name)}:`; + const ui_span = html``; + /** @type {import("./typedef.js").ConfigSpan} */ + const container = html` + ${label} ${ui_span} + `; + // Add custom style if span is the format span + if (name === "format") { + addFormatConfigStyle(container); + } + // Assign the various variables as properties of the container + container.label = label; + container.ui_span = ui_span; + // Initialize the ui_span + initializeUIValueSpan(ui_span, name); + container.config_span = config_span; + // Initialize the config_span + initializeConfigValueSpan(config_span, name); container.key = name; - CLIPBOARD_HEADER.config_spans[name] = container - CLIPBOARD_HEADER.insertAdjacentElement('beforeend', container) - // We put some convenience getters/setters - // ui_value forward - Object.defineProperty(container, "ui_value", { - get: () => container.ui_span.value, - set: (val) => { - container.ui_span.value = val; - }, - }); - // config_value forward - Object.defineProperty(container, "config_value", { - get: () => container.config_span.value, - set: (val) => { - container.config_span.value = val; - }, - }); - return + CLIPBOARD_HEADER.config_spans[name] = container; + // Insert the container as a child of the CLIPBOARD_HEADER + CLIPBOARD_HEADER.appendChild(container); + // We put some convenience getters/setters + // ui_value forward + Object.defineProperty(container, "ui_value", { + get: () => container.ui_span.value, + set: (val) => { + container.ui_span.value = val; + }, + }); + // config_value forward + Object.defineProperty(container, "config_value", { + get: () => container.config_span.value, + set: (val) => { + container.config_span.value = val; + }, + }); + // Add the onclick for setting/unsetting the config + label.onclick = DualClick( + container.setConfigFromUI, + (/** @type {Event} */ e) => { + console.log("e", e); + e.preventDefault(); + container.unsetConfig(); + } + ); + return; +} + + +/** + * Add config spans to the given CLIPBOARD_HEADER. + * + * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER - the clipboard header to add config spans to + * @return {void} + */ +function addConfigSpans(CLIPBOARD_HEADER) { + // Extract the dependencies + const { html } = libs + + // @ts-ignore: Will be populated + CLIPBOARD_HEADER.config_spans = {}; + addSingleConfigSpan(CLIPBOARD_HEADER, "format"); + addSingleConfigSpan(CLIPBOARD_HEADER, "width"); + addSingleConfigSpan(CLIPBOARD_HEADER, "height"); + addSingleConfigSpan(CLIPBOARD_HEADER, "scale"); + // Add set/unset + CLIPBOARD_HEADER.appendChild( + html`` + ); + CLIPBOARD_HEADER.appendChild( + html`` + ); + addSingleConfigSpan(CLIPBOARD_HEADER, "filename"); } /** * Adds a clipboard header to the given container. * * @param {import("./typedef.js").Container} CONTAINER - The container to which the clipboard header will be added. - * @return {undefined} This function does not return a value. + * @return {undefined} span function does not return a value. */ export function addClipboardHeader(CONTAINER) { + // Extract the dependencies + const { html } = libs + // Return if the CLIPBOARD HEADER has been assigned already if (CONTAINER.CLIPBOARD_HEADER !== undefined) return; + /** @type {import("./typedef.js").ClipboardHeader} */ const CLIPBOARD_HEADER = html``; - CLIPBOARD_HEADER.config_spans = {}; -} - -function checkConfigSync(container) { - const valid_classes = [ - "missing-config", - "matching-config", - "different-config", - ]; - function setClass(cl) { - for (const name of valid_classes) { - container.classList.toggle(name, name == cl); + >`; + addClipboardHeaderStyle(CLIPBOARD_HEADER); + CLIPBOARD_HEADER.config_values = toImageOptionsObj({ + target: CLIPBOARD_HEADER, + name: "clipboard-header-change", + type: "config" + }); + CLIPBOARD_HEADER.ui_values = toImageOptionsObj({ + target: CLIPBOARD_HEADER, + name: "clipboard-header-change", + type: "ui" + }); + // Add the various spans for the UI + addConfigSpans(CLIPBOARD_HEADER) + CONTAINER.appendChild(CLIPBOARD_HEADER); + CONTAINER.CLIPBOARD_HEADER = CLIPBOARD_HEADER; + // Add the function that updates config sync + /** @span {import("./typedef.js").ClipboardHeader} */ + CLIPBOARD_HEADER.updateConfigSync = function() { + // @ts-ignore + for (const span of span.config_spans) { + checkConfigSync(span) } } + // Add the listener for imageOptions change + /** @param {CustomEvent} e */ + const listener = (e) => { + e.stopPropagation() + console.log(e.detail) + /** @type {keyof import("./typedef.js").ConfigSpansObject} */ + const key = e.detail.key + const span = CLIPBOARD_HEADER.config_spans[key] + checkConfigSync(span) + }; + // @ts-ignore: addEventListener does not support function with CustomEvent as arguments + CLIPBOARD_HEADER.addEventListener("clipboard-header-change", listener); + return; +} + +/** + * Check if the ui and config value for a given span are in sync and update the label color (indirectly through css) and hover text accordingly. + * @param {import("./typedef.js").ConfigSpan} span + */ +function checkConfigSync(span) { // We use the custom getters we'll set up in the container - const { ui_value, config_value, config_span, key } = container; + const { ui_value, config_value, config_span, key } = span; if (config_value === undefined) { - setClass("missing-config"); - config_span.innerHTML = `The key \${key} is not present in the config.`; + span.setAttribute("config", "missing"); + config_span.setInnerHTML( + `The key ${key} is not present in the config.` + ); } else if (ui_value == config_value) { - setClass("matching-config"); - config_span.innerHTML = `The key \${key} has the same value in the config and in the header.`; + span.setAttribute("config", "matching"); + config_span.setInnerHTML( + `The key ${key} has the same value in the config and in the header.` + ); } else { - setClass("different-config"); - config_span.innerHTML = `The key \${key} has a different value (\${config_value}) in the config.`; + span.setAttribute("config", "different"); + config_span.setInnerHTML( + `The key ${key} has a different value (${config_value}) in the config.` + ); } - // Add info about setting and unsetting - config_span.insertAdjacentHTML( - "beforeend", - `
Click on the label once to set the current UI value in the config.` - ); - config_span.insertAdjacentHTML( - "beforeend", - `
Click twice to remove this key from the config.` - ); } -const valid_formats = ["png", "svg", "webp", "jpeg", "full-json"]; -function initializeUIValueSpan(span, key, value) { - const container = span.closest(".clipboard-span"); - span.contentEditable = key === "format" ? "false" : "true"; - let parse = (x) => x; - let update = (x) => (span.textContent = x); +/** + * Function that returns the parsing function to generate a config value from the UI config span. + * + * @param {string} key - The key to determine the type of function to return. + * @return {function} The function based on the key. + */ +function parseFunction(key) { if (key === "width" || key === "height") { - parse = (x) => Math.round(parseFloat(x)); + return (/** @type {string} */ x) => { + return Math.round(parseFloat(x)); + }; } else if (key === "scale") { - parse = parseFloat; + return parseFloat; + } else if (key === "filename") { + return (/** @type {string} */ x) => { + return x; + }; } else if (key === "format") { - // We remove contentEditable - span.contentEditable = "false"; + return (/** @type {string} */ x) => { + if (!valid_download_formats.includes(x)) { + throw new Error( + `Invalid format: ${x}, only the following ones are supported ${valid_download_formats}` + ); + } + return x; + }; + } else { + throw new Error(`Unknown key: ${key}`); + } +} + +/** + * + * @param {string} key + * @returns {Function} + */ +function updateFunction(key) { + if (key === "format") { + /** + * @this {HTMLElement & {value: string}} + */ + return function () { + this.setAttribute("selected", this.value); + }; + } else { + /** + * @this {HTMLElement & {value: number | string}} + */ + return function () { + this.textContent = `${this.value}`; + }; + } +} + +/** + * + * @param {import("./typedef.js").UIValueSpan} span + * @param {keyof import("./typedef.js").ConfigSpansObject} key + */ +function initializeUIValueSpan(span, key) { + // Extract the dependencies + const { html } = libs + + span.contentEditable = `${key !== "format"}`; + span.updateFromValue = updateFunction(key); + span.parseValue = parseFunction(key); + // For the format key we have to add the format options + if (key === "format") { // Here we first add the subspans for each option - const opts_div = span.appendChild(html`
`); - for (const fmt of valid_formats) { + const opts_div = span.appendChild( + html`
` + ); + for (const fmt of valid_download_formats) { const opt = opts_div.appendChild( - html`\${fmt}` + html`${fmt}` ); - opt.onclick = (e) => { + opt.onclick = () => { span.value = opt.textContent; }; } - parse = (x) => { - return valid_formats.includes(x) ? x : localValue; - }; - update = (x) => { - for (const opt of opts_div.children) { - opt.classList.toggle("selected", opt.textContent === x); - } - }; - } else { - // We only have filename here } - let localValue; Object.defineProperty(span, "value", { - get: () => { - return localValue; + get() { + return this._value; }, - set: (val) => { - if (val !== "") { - localValue = parse(val); + set(val) { + let parsedVal; + try { + parsedVal = this.parseValue(val); + } catch (e) { + console.warn( + `The following error message was thrown while parsing the provided value ${val}:\n\n${e.message}.\n\nIgnoring the provided value` + ); + parsedVal = this.value; } - update(localValue); - checkConfigSync(container); + if (parsedVal != this.value) { + this._value = parsedVal; + this.dispatchEvent( + new CustomEvent("clipboard-header-change", { + bubbles: true, + detail: { key: key, type: "ui" }, + }) + ); + } + this.updateFromValue(); }, }); + // We set the default value while initializing + span.value = image_options_defaults[key]; // We also assign a listener so that the editable is blurred when enter is pressed span.onkeydown = (/** @type {KeyboardEvent} */ e) => { if (e.key === "Enter") { + // We don't want to add a newline in the textarea e.preventDefault(); span.blur(); } }; - span.value = value; } +/** + * Defines a setter and getter for a config value span. + * + * @param {import("./typedef.js").ConfigValueSpan} span - The span element to define the setter and getter on + * @param {keyof import("./typedef.js").toImageOptions} key - The key to access the config value + */ function initializeConfigValueSpan(span, key) { + // Extract the dependencies + const { html } = libs + + // We add the span that will contain the variable text + const variableText = html`

`; + span.appendChild(variableText); + // Text for setting the config + span.appendChild( + html`

+ Click on the label once to set the current UI value in the + config. +

` + ); + // Text for unsetting the config + span.appendChild( + html`

+ Click twice to remove span key from the config. +

` + ); + // We add the function to set the text on the config + span.setInnerHTML = (x) => { + variableText.innerHTML = x; + }; // Here we mostly want to define the setter and getter - const container = span.closest(".clipboard-span"); Object.defineProperty(span, "value", { - get: () => { - return plot_obj.config.toImageButtonOptions[key]; + get() { + return this._value; }, - set: (val) => { - // if undefined is passed, we remove the entry from the options - if (val === undefined) { - delete plot_obj.config.toImageButtonOptions[key]; - } else { - plot_obj.config.toImageButtonOptions[key] = val; - } - checkConfigSync(container); + set(val) { + if (val == this.value) return; + this._value = val; + this.dispatchEvent( + new CustomEvent("clipboard-header-change", { + bubbles: true, + detail: { key: key, type: "config" }, + }) + ); }, }); } -const config_spans = {}; -for (const [key, value] of Object.entries(getImageOptions())) { - const container = CLIPBOARD_HEADER.querySelector(`.clipboard-span.\${key}`); - const label = container.querySelector(".label"); - // We give the label a function that on single click will set the current value and with double click will unset it - label.onclick = DualClick( - () => { - container.config_value = container.ui_value; - }, - (e) => { - console.log("e", e); - e.preventDefault(); - container.config_value = undefined; - } - ); - const ui_value_span = container.querySelector(".clipboard-value"); - const config_value_span = - container.querySelector(".config-value") ?? - label.insertAdjacentElement( - "afterbegin", - html`` - ); - // Assing the two spans as properties of the containing span - container.ui_span = ui_value_span; - container.config_span = config_value_span; - container.key = key; - config_spans[key] = container; - if (firstRun) { - plot_obj.config.toImageButtonOptions = - plot_obj.config.toImageButtonOptions ?? {}; - // We do the initialization of the value span - initializeUIValueSpan(ui_value_span, key, value); - // Then we initialize the config value - initializeConfigValueSpan(config_value_span, key); - // We put some convenience getters/setters - // ui_value forward - Object.defineProperty(container, "ui_value", { - get: () => ui_value_span.value, - set: (val) => { - ui_value_span.value = val; - }, - }); - // config_value forward - Object.defineProperty(container, "config_value", { - get: () => config_value_span.value, - set: (val) => { - config_value_span.value = val; +/** + * + * @param {{ + * target: import("./typedef.js").ClipboardHeader, + * name: string + * type: string + * }} event_details + * @return {import("./typedef.js").toImageOptions} + */ +function toImageOptionsObj({target, name, type}) { + const out = {_options: {}} + for (const key of toImageOptionKeys) { + Object.defineProperty(out, key, { + get() {return this._options[key]}, + set(val) { + this._options[key] = val + target.dispatchEvent( + new CustomEvent(name, { + detail: {key, type} + }) + ) }, - }); + enumerable: true, + }) } -} - -// These objects will contain the default value -// This code updates the image options in the PLOT config with the provided ones -function setImageOptions(o) { - for (const [key, container] of Object.entries(config_spans)) { - container.config_value = o[key]; - } -} -function unsetImageOptions() { - setImageOptions({}); + // @ts-ignore: We did set up the properties as accessors + return out } -const set_button = CLIPBOARD_HEADER.querySelector(".clipboard-span.set"); -const unset_button = CLIPBOARD_HEADER.querySelector(".clipboard-span.unset"); -if (firstRun) { - set_button.onclick = (e) => { - for (const container of Object.values(config_spans)) { - container.config_value = container.ui_value; - } - }; - unset_button.onclick = unsetImageOptions; -} +// // span code updates the image options in the PLOT config with the provided ones +// function setImageOptions(o) { +// for (const [key, container] of Object.entries(config_spans)) { +// container.config_value = o[key]; +// } +// } +// function unsetImageOptions() { +// setImageOptions({}); +// } -// We add a function to check if the clipboard is popped out -CONTAINER.isPoppedOut = () => { - return CONTAINER.classList.contains("popped-out"); -}; +// const set_button = CLIPBOARD_HEADER.querySelector(".clipboard-span.set"); +// const unset_button = CLIPBOARD_HEADER.querySelector(".clipboard-span.unset"); +// if (firstRun) { +// set_button.onclick = (e) => { +// for (const container of Object.values(config_spans)) { +// container.config_value = container.ui_value; +// } +// }; +// unset_button.onclick = unsetImageOptions; +// } -CLIPBOARD_HEADER.onmousedown = function (event) { - if (event.target.matches("span.clipboard-value")) { - console.log("We don't move!"); - return; - } - const start = { - left: parseFloat(CONTAINER.style.left), - top: parseFloat(CONTAINER.style.top), - X: event.pageX, - Y: event.pageY, - }; - function moveAt(event, start) { - const top = event.pageY - start.Y + start.top + "px"; - const left = event.pageX - start.X + start.left + "px"; - CLIPBOARD_HEADER.style.left = left; - CONTAINER.style.left = left; - CONTAINER.style.top = top; - } +// CLIPBOARD_HEADER.onmousedown = function (event) { +// if (event.target.matches("span.clipboard-value")) { +// console.log("We don't move!"); +// return; +// } +// const start = { +// left: parseFloat(CONTAINER.style.left), +// top: parseFloat(CONTAINER.style.top), +// X: event.pageX, +// Y: event.pageY, +// }; +// function moveAt(event, start) { +// const top = event.pageY - start.Y + start.top + "px"; +// const left = event.pageX - start.X + start.left + "px"; +// CLIPBOARD_HEADER.style.left = left; +// CONTAINER.style.left = left; +// CONTAINER.style.top = top; +// } - // move our absolutely positioned ball under the pointer - moveAt(event, start); - function onMouseMove(event) { - moveAt(event, start); - } +// // move our absolutely positioned ball under the pointer +// moveAt(event, start); +// function onMouseMove(event) { +// moveAt(event, start); +// } - // We use this to remove the mousemove when clicking outside of the container - const controller = new AbortController(); +// // We use span to remove the mousemove when clicking outside of the container +// const controller = new AbortController(); - // move the container on mousemove - document.addEventListener("mousemove", onMouseMove, { - signal: controller.signal, - }); - document.addEventListener( - "mousedown", - (e) => { - if (e.target.closest(".plutoplotly-container") !== CONTAINER) { - cleanUp(); - controller.abort(); - return; - } - }, - { signal: controller.signal } - ); +// // move the container on mousemove +// document.addEventListener("mousemove", onMouseMove, { +// signal: controller.signal, +// }); +// document.addEventListener( +// "mousedown", +// (e) => { +// if (e.target.closest(".plutoplotly-container") !== CONTAINER) { +// cleanUp(); +// controller.abort(); +// return; +// } +// }, +// { signal: controller.signal } +// ); - function cleanUp() { - console.log("cleaning up the plot move listener"); - controller.abort(); - CLIPBOARD_HEADER.onmouseup = null; - } +// function cleanUp() { +// console.log("cleaning up the plot move listener"); +// controller.abort(); +// CLIPBOARD_HEADER.onmouseup = null; +// } - // (3) drop the ball, remove unneeded handlers - CLIPBOARD_HEADER.onmouseup = cleanUp; -}; +// // (3) drop the ball, remove unneeded handlers +// CLIPBOARD_HEADER.onmouseup = cleanUp; +// }; +/** + * Function to send the provided blob to the clipboard using the Clipboard API. + * + * @param {Blob} blob - the blob to be copied to the clipboard + * @return {void} + */ function sendToClipboard(blob) { if (!navigator.clipboard) { alert( @@ -342,136 +476,149 @@ function sendToClipboard(blob) { ); } -function copyImageToClipboard() { - // We extract the image options from the provided parameters (if they exist) - const config = {}; - for (const [key, container] of Object.entries(config_spans)) { - let val = - container.config_value ?? - (CONTAINER.isPoppedOut() ? container.ui_value : undefined); - // If we have undefined we don't create the key. We also ignore format because the clipboard only supports png. - if (val === undefined || key === "format") { - continue; - } - config[key] = val; - } - Plotly.toImage(PLOT, config).then(function (dataUrl) { - fetch(dataUrl) - .then((res) => res.blob()) - .then((blob) => { - const paste_receiver = document.querySelector( - "paste-receiver.plutoplotly" - ); - if (paste_receiver) { - paste_receiver.attachImage(dataUrl, CONTAINER); - } - sendToClipboard(blob); - }); - }); -} -function saveImageToFile() { - const config = {}; - for (const [key, container] of Object.entries(config_spans)) { - let val = - container.config_value ?? - (CONTAINER.isPoppedOut() ? container.ui_value : undefined); - // If we have undefined we don't create the key. - if (val === undefined) { - continue; - } - config[key] = val; - } - Plotly.downloadImage(PLOT, config); -} +// /** +// * Function to copy the image from the parent container to the clipboard. +// * @span {import('./typedef.js').Container} +// */ +// export function copyImageToClipboard() { +// // We extract the image options from the provided parameters (if they exist) +// const { Plotly, PLOT } = span +// const config = {}; +// for (const [key, container] of Object.entries(config_spans)) { +// let val = +// container.config_value ?? +// (span.isPoppedOut() ? container.ui_value : undefined); +// // If we have undefined we don't create the key. We also ignore format because the clipboard only supports png. +// if (val === undefined || key === "format") { +// continue; +// } +// config[key] = val; +// } +// Plotly.toImage(PLOT, config).then(function (dataUrl) { +// fetch(dataUrl) +// .then((res) => res.blob()) +// .then((blob) => { +// const paste_receiver = document.querySelector( +// "paste-receiver.plutoplotly" +// ); +// if (paste_receiver) { +// paste_receiver.attachImage(dataUrl, CONTAINER); +// } +// sendToClipboard(blob); +// }); +// }); +// } -let container_rect = { width: 0, height: 0, top: 0, left: 0 }; -function unpop_container(cl) { - CONTAINER.classList.toggle("popped-out", false); - CONTAINER.classList.toggle(cl, false); - // We fix the height back to the value it had before popout, also setting the flag to signal that upon first resize we remove the fixed inline-style - CONTAINER.style.height = container_rect.height + "px"; - remove_container_size = true; - // We set the other fixed inline-styles to null - CONTAINER.style.width = ""; - CONTAINER.style.top = ""; - CONTAINER.style.left = ""; - // We also remove the CLIPBOARD_HEADER - CLIPBOARD_HEADER.style.width = ""; - CLIPBOARD_HEADER.style.left = ""; - // Finally we remove the hidden class to the header - CLIPBOARD_HEADER.classList.toggle("hidden", true); - return; -} -function popout_container(opts) { - const cl = opts?.cl; - const target_container_size = opts?.target_container_size ?? {}; - const target_plot_size = opts?.target_plot_size ?? {}; - if (CONTAINER.isPoppedOut()) { - return unpop_container(cl); - } - CONTAINER.classList.toggle(cl, cl === undefined ? false : true); - // We extract the current size of the container, save them and fix them - const { width, height, top, left } = CONTAINER.getBoundingClientRect(); - container_rect = { width, height, top, left }; - // We save the current plot size before we pop as it will fill the screen - const current_plot_size = { - width: PLOT._fullLayout.width, - height: PLOT._fullLayout.height, - }; - // We have to save the pad data before popping so we can resize precisely - const pad = {}; - pad.unpopped = getSizeData().container_pad; - CONTAINER.classList.toggle("popped-out", true); - pad.popped = getSizeData().container_pad; - // We do top and left based on the current rect - for (const key of ["top", "left"]) { - const start_val = target_container_size[key] ?? container_rect[key]; - let offset = 0; - for (const kind of ["padding", "border"]) { - offset += pad.popped[kind][key] - pad.unpopped[kind][key]; - } - CONTAINER.style[key] = start_val - offset + "px"; - if (key === "left") { - CLIPBOARD_HEADER.style[key] = CONTAINER.style[key]; - } - } - // We compute the width and height depending on eventual config data - const csz = computeContainerSize({ - width: - target_plot_size.width ?? - config_spans.width.config_value ?? - current_plot_size.width, - height: - target_plot_size.height ?? - config_spans.height.config_value ?? - current_plot_size.height, - }); - for (const key of ["width", "height"]) { - const val = target_container_size[key] ?? csz[key]; - CONTAINER.style[key] = val + "px"; - if (key === "width") { - CLIPBOARD_HEADER.style[key] = CONTAINER.style[key]; - } - } - CLIPBOARD_HEADER.classList.toggle("hidden", false); - const controller = new AbortController(); - - document.addEventListener( - "mousedown", - (e) => { - if (e.target.closest(".plutoplotly-container") !== CONTAINER) { - unpop_container(); - controller.abort(); - return; - } - }, - { signal: controller.signal } - ); -} +// function saveImageToFile() { +// const config = {}; +// for (const [key, container] of Object.entries(config_spans)) { +// let val = +// container.config_value ?? +// (CONTAINER.isPoppedOut() ? container.ui_value : undefined); +// // If we have undefined we don't create the key. +// if (val === undefined) { +// continue; +// } +// config[key] = val; +// } +// Plotly.downloadImage(PLOT, config); +// } + +// let container_rect = { width: 0, height: 0, top: 0, left: 0 }; +// function unpop_container(cl) { +// CONTAINER.classList.toggle("popped-out", false); +// CONTAINER.classList.toggle(cl, false); +// // We fix the height back to the value it had before popout, also setting the flag to signal that upon first resize we remove the fixed inline-style +// CONTAINER.style.height = container_rect.height + "px"; +// remove_container_size = true; +// // We set the other fixed inline-styles to null +// CONTAINER.style.width = ""; +// CONTAINER.style.top = ""; +// CONTAINER.style.left = ""; +// // We also remove the CLIPBOARD_HEADER +// CLIPBOARD_HEADER.style.width = ""; +// CLIPBOARD_HEADER.style.left = ""; +// // Finally we remove the hidden class to the header +// CLIPBOARD_HEADER.classList.toggle("hidden", true); +// return; +// } +// function popout_container(opts) { +// const cl = opts?.cl; +// const target_container_size = opts?.target_container_size ?? {}; +// const target_plot_size = opts?.target_plot_size ?? {}; +// if (CONTAINER.isPoppedOut()) { +// return unpop_container(cl); +// } +// CONTAINER.classList.toggle(cl, cl === undefined ? false : true); +// // We extract the current size of the container, save them and fix them +// const { width, height, top, left } = CONTAINER.getBoundingClientRect(); +// container_rect = { width, height, top, left }; +// // We save the current plot size before we pop as it will fill the screen +// const current_plot_size = { +// width: PLOT._fullLayout.width, +// height: PLOT._fullLayout.height, +// }; +// // We have to save the pad data before popping so we can resize precisely +// const pad = {}; +// pad.unpopped = getSizeData().container_pad; +// CONTAINER.classList.toggle("popped-out", true); +// pad.popped = getSizeData().container_pad; +// // We do top and left based on the current rect +// for (const key of ["top", "left"]) { +// const start_val = target_container_size[key] ?? container_rect[key]; +// let offset = 0; +// for (const kind of ["padding", "border"]) { +// offset += pad.popped[kind][key] - pad.unpopped[kind][key]; +// } +// CONTAINER.style[key] = start_val - offset + "px"; +// if (key === "left") { +// CLIPBOARD_HEADER.style[key] = CONTAINER.style[key]; +// } +// } +// // We compute the width and height depending on eventual config data +// const csz = computeContainerSize({ +// width: +// target_plot_size.width ?? +// config_spans.width.config_value ?? +// current_plot_size.width, +// height: +// target_plot_size.height ?? +// config_spans.height.config_value ?? +// current_plot_size.height, +// }); +// for (const key of ["width", "height"]) { +// const val = target_container_size[key] ?? csz[key]; +// CONTAINER.style[key] = val + "px"; +// if (key === "width") { +// CLIPBOARD_HEADER.style[key] = CONTAINER.style[key]; +// } +// } +// CLIPBOARD_HEADER.classList.toggle("hidden", false); +// const controller = new AbortController(); -CONTAINER.popOut = popout_container; +// document.addEventListener( +// "mousedown", +// (e) => { +// if (e.target.closest(".plutoplotly-container") !== CONTAINER) { +// unpop_container(); +// controller.abort(); +// return; +// } +// }, +// { signal: controller.signal } +// ); +// } +// CONTAINER.popOut = popout_container; + +/** + * Creates a function that will execute single_func if clicked once, and dbl_func if clicked twice within a short timeframe. + * + * @param {Function} single_func - The function to be executed on a single click + * @param {Function} dbl_func - The function to be executed on a double click + * @return {Function} A new function that handles single and double clicks + */ function DualClick(single_func, dbl_func) { let nclicks = 0; return function (...args) { @@ -490,34 +637,34 @@ function DualClick(single_func, dbl_func) { }; } -// We remove the default download image button -plot_obj.config.modeBarButtonsToRemove = _.union( - plot_obj.config.modeBarButtonsToRemove, - ["toImage"] -); -// We add the custom button to the modebar -plot_obj.config.modeBarButtonsToAdd = _.union( - plot_obj.config.modeBarButtonsToAdd, - [ - { - name: "Copy PNG to Clipboard", - icon: { - height: 520, - width: 520, - path: "M280 64h40c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128C0 92.7 28.7 64 64 64h40 9.6C121 27.5 153.3 0 192 0s71 27.5 78.4 64H280zM64 112c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320c8.8 0 16-7.2 16-16V128c0-8.8-7.2-16-16-16H304v24c0 13.3-10.7 24-24 24H192 104c-13.3 0-24-10.7-24-24V112H64zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z", - }, - direction: "up", - click: DualClick(copyImageToClipboard, () => { - popout_container(); - }), - }, - { - name: "Download Image", - icon: Plotly.Icons.camera, - direction: "up", - click: DualClick(saveImageToFile, () => { - popout_container({ cl: "filesave" }); - }), - }, - ] -); +// // We remove the default download image button +// plot_obj.config.modeBarButtonsToRemove = _.union( +// plot_obj.config.modeBarButtonsToRemove, +// ["toImage"] +// ); +// // We add the custom button to the modebar +// plot_obj.config.modeBarButtonsToAdd = _.union( +// plot_obj.config.modeBarButtonsToAdd, +// [ +// { +// name: "Copy PNG to Clipboard", +// icon: { +// height: 520, +// width: 520, +// path: "M280 64h40c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128C0 92.7 28.7 64 64 64h40 9.6C121 27.5 153.3 0 192 0s71 27.5 78.4 64H280zM64 112c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320c8.8 0 16-7.2 16-16V128c0-8.8-7.2-16-16-16H304v24c0 13.3-10.7 24-24 24H192 104c-13.3 0-24-10.7-24-24V112H64zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z", +// }, +// direction: "up", +// click: DualClick(copyImageToClipboard, () => { +// popout_container(); +// }), +// }, +// { +// name: "Download Image", +// icon: Plotly.Icons.camera, +// direction: "up", +// click: DualClick(saveImageToFile, () => { +// popout_container({ cl: "filesave" }); +// }), +// }, +// ] +// ); diff --git a/js/src/container.js b/js/src/container.js index 034ceb9..d6686ea 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -1,6 +1,6 @@ //@ts-check -import { html } from "./url_imports.js"; -// import { addClipboardHeader } from "./clipboard.js"; +import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; +import { addClipboardHeader } from "./clipboard.js"; import { addContainerStyle } from "./styles.js"; /** * Creates the Container element used by PlutoPlotly to wrap the plotly.js plot and adds additional functionality like resizing and enhanced clipboard. @@ -14,7 +14,7 @@ export function makeContainer(Plotly = globalThis.Plotly) { // Add the style to it addContainerStyle(CONTAINER); // Add the clipboard header - // addClipboardHeader(CONTAINER); + addClipboardHeader(CONTAINER); // Create the child div that will contain the actual plot const PLOT = CONTAINER.PLOT = CONTAINER.appendChild( html`
` @@ -36,5 +36,9 @@ export function makeContainer(Plotly = globalThis.Plotly) { }, { signal: CONTAINER.controller.signal } ); + // We add a function to check if the clipboard is popped out + CONTAINER.isPoppedOut = function() { + return this.classList.contains("popped-out"); + }; return CONTAINER } \ No newline at end of file diff --git a/js/src/global_deps.js b/js/src/global_deps.js new file mode 100644 index 0000000..1f1129c --- /dev/null +++ b/js/src/global_deps.js @@ -0,0 +1,32 @@ +// This module will contain an object that has to be populated at run time with external JS dependencies + +/** @type {import("./typedef.js").JSDeps} */ +// @ts-ignore: Will be populated at runtime +export const libs = {}; + +/** + * Sets global dependencies for emotion, lodash, interact and html. + * + * @param {import("./typedef.js").JSDeps} params - An object containing emotion, lodash, interact and html dependencies. + * @return {void} + */ +export function setGlobalDeps({ emotion, lodash, interact, html }) { + libs.emotion = emotion; + libs.lodash = lodash; + libs.interact = interact; + libs.html = html; +} + +/** + * Check if the necessary global dependencies are defined. + * + * @return {boolean} Whether all the global dependencies are not undefined + */ +export function validGlobalDeps() { + return ( + libs.emotion != undefined && + libs.lodash != undefined && + libs.interact != undefined && + libs.html != undefined + ); +} \ No newline at end of file diff --git a/js/src/prehooks.js b/js/src/prehooks.js index 4a4db3b..48d43fc 100644 --- a/js/src/prehooks.js +++ b/js/src/prehooks.js @@ -1,13 +1,15 @@ // This file contains utilities to be executed before calling the plot function. -import { html, lodash as _ } from "./url_imports.js" +// import { interact, html, lodash as _ } from "./url_imports.js" import { makeContainer } from "./container.js"; import { processPlotObj } from "./utils.js"; +import { setGlobalDeps, validGlobalDeps } from "./global_deps.js"; +export { setGlobalDeps, validGlobalDeps, makeContainer } // We start by putting all the variable interpolation here at the beginning // We have to convert all typedarrays in the layout to normal arrays. See Issue #25 export function createPlot(plot_obj, Plotly = globalThis.Plotly) { - const CONTAINER = makeContainer(Plotly); + const CONTAINER = makeContainer(); const { PLOT } = CONTAINER; // Record or update the layout width/height if provided explicitly PLOT.layout_size = { diff --git a/js/src/styles.js b/js/src/styles.js index 95ddb56..ba71305 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -1,6 +1,13 @@ -import { css } from "./url_imports.js"; +import { libs } from "./global_deps.js"; -const CONTAINER_STYLE = css` +/** + * Adds a container style to the specified container element. + * + * @param {import("./typedef.js").Container} CONTAINER - The container element to which the style will be added + */ +export function addContainerStyle(CONTAINER) { + const css = libs.emotion.css; + const cl = css` & { width: 100%; height: 100%; @@ -21,122 +28,148 @@ const CONTAINER_STYLE = css` border-top-left-radius: 0px; border-top-right-radius: 0px; } - .plutoplotly-clipboard-header { - display: flex; - flex-flow: row wrap; - background: var(--main-bg-color); - border: 3px solid var(--kbd-border-color); - border-top-left-radius: 12px; - border-top-right-radius: 12px; - position: fixed; - z-index: 1001; - cursor: move; - transform: translate(0px, -100%); - padding: 5px; - } - .plutoplotly-clipboard-header span { - display: inline-block; - flex: 1; - } - .plutoplotly-clipboard-header.hidden { - display: none; - } - .clipboard-span { - position: relative; - } - .clipboard-value { - padding-right: 5px; - padding-left: 2px; - cursor: text; - } - .clipboard-span.format { - display: none; - } - .clipboard-span.filename { - flex: 0 0 100%; - text-align: center; - border-top: 3px solid var(--kbd-border-color); - margin-top: 5px; - display: none; - } - &.filesave .clipboard-span.filename { - display: inline-block; - } - .clipboard-value.filename { - margin-left: 3px; - text-align: left; - min-width: min(60%, min-content); - } - &.filesave .clipboard-span.format { - display: inline-flex; - } - .clipboard-span.format .label { - flex: 0 0 0; - } - .clipboard-value.format { - position: relative; - flex: 1 0 auto; - min-width: 30px; - margin-right: 10px; - } - div.format-options { - display: inline-flex; - flex-flow: column; - position: absolute; - background: var(--main-bg-color); - border-radius: 12px; - padding-left: 3px; - z-index: 2000; - } - div.format-options:hover { - cursor: pointer; - border: 3px solid var(--kbd-border-color); - padding: 3px; - transform: translate(-3px, -6px); - } - div.format-options .format-option { - display: none; - } - div.format-options:hover .format-option { - display: inline-block; - } - .format-option:not(.selected) { - margin-top: 3px; - } - div.format-options .format-option.selected { - order: -1; - display: inline-block; - } - .format-option:hover { - background-color: var(--kbd-border-color); - } - span.config-value { - font-weight: normal; - color: var(--pluto-output-color); - display: none; - position: absolute; - background: var(--main-bg-color); - border: 3px solid var(--kbd-border-color); - border-radius: 12px; - transform: translate(0px, calc(-100% - 10px)); - padding: 5px; - } - .label { - user-select: none; - } - .label:hover span.config-value { - display: inline-block; - min-width: 150px; - } - .clipboard-span.matching-config .label { - color: var(--cm-macro-color); - font-weight: bold; - } - .clipboard-span.different-config .label { - color: var(--cm-tag-color); - font-weight: bold; - } - ` -export function addContainerStyle(CONTAINER) { - CONTAINER.classList.add(CONTAINER_STYLE); -} \ No newline at end of file + `; + CONTAINER.classList.add(cl); +} + + +/** + * Adds the clipboard header style to the given element. + * + * @param {HTMLElement} element - the element to which the clipboard header style will be added + */ +export function addClipboardHeaderStyle(element) { + const css = libs.emotion.css + const cl = css` + & { + display: flex; + flex-flow: row wrap; + background: var(--main-bg-color); + border: 3px solid var(--kbd-border-color); + border-top-left-radius: 12px; + border-top-right-radius: 12px; + position: fixed; + z-index: 1001; + cursor: move; + transform: translate(0px, -100%); + padding: 5px; + } + & span { + display: inline-block; + flex: 1; + } + &.hidden { + display: none; + } + & .clipboard-span { + position: relative; + } + & .clipboard-value { + padding-right: 5px; + padding-left: 2px; + cursor: text; + } + & .config-value { + font-weight: normal; + color: var(--pluto-output-color); + display: none; + position: absolute; + background: var(--main-bg-color); + border: 3px solid var(--kbd-border-color); + border-radius: 12px; + transform: translate(0px, calc(-100% - 10px)); + padding: 5px; + } + & .label { + user-select: none; + } + & .label:hover span.config-value { + display: inline-block; + min-width: 150px; + } + & .clipboard-span.matching-config .label { + color: var(--cm-macro-color); + font-weight: bold; + } + & .clipboard-span.different-config .label { + color: var(--cm-tag-color); + font-weight: bold; + } + & .clipboard-span.format { + display: none; + } + & .clipboard-span.filename { + flex: 0 0 100%; + text-align: center; + border-top: 3px solid var(--kbd-border-color); + margin-top: 5px; + display: none; + } + &.filesave-extras .clipboard-span.filename { + display: inline-block; + } + & .clipboard-value.filename { + margin-left: 3px; + text-align: left; + min-width: min(60%, min-content); + } + &.filesave-extras .clipboard-span.format { + display: inline-flex; + } +`; + element.classList.add(cl); +} + + +/** + * @param {HTMLElement} element - the element to which the clipboard header style will be added + */ +export function addFormatConfigStyle(element) { + const css = libs.emotion.css + const format_config_style = css` + & > .label { + flex: 0 0 0; + } + & .clipboard-value { + position: relative; + flex: 1 0 auto; + min-width: 30px; + margin-right: 10px; + } + & .format-options { + display: inline-flex; + flex-flow: column; + position: absolute; + background: var(--main-bg-color); + border-radius: 12px; + padding-left: 3px; + z-index: 2000; + } + & .format-options:hover { + cursor: pointer; + border: 3px solid var(--kbd-border-color); + padding: 3px; + transform: translate(-3px, -6px); + } + & .format-option { + display: none; + margin-top: 3px; + } + & .format-options:hover .format-option { + display: inline-block; + } + & .format-options[selected="png"] .format-option.png, + & .format-options[selected="svg"] .format-option.svg, + & .format-options[selected="webp"] .format-option.webp, + & .format-options[selected="jpeg"] .format-option.jpeg, + & .format-options[selected="full-json"] .format-option.full-json { + margin-top: 0px; + order: -1; + display: inline-block; + } + & .format-options .format-option:hover { + background-color: var(--kbd-border-color); + } +`; + element.classList.add(format_config_style); +} diff --git a/js/src/typedef.js b/js/src/typedef.js index fa850f4..6b54a83 100644 --- a/js/src/typedef.js +++ b/js/src/typedef.js @@ -1,9 +1,22 @@ // The type defined here should be picked up by vscode thanks the jsconfig.json as explained in https://stackoverflow.com/questions/45836847/how-to-get-vs-code-to-understand-jsdocs-typedef-across-multiple-files // Unfortunately this does not seem to work without the explicit import -// Plotly type +// Helper Dependencies /** - * @typedef {import('https://esm.sh/@types/plotly.js')} Plotly + * @typedef {Object} JSDeps + * @property {import('https://esm.sh/@emotion/css@11.11.2/create-instance').Emotion} emotion + * @property {import('npm:@types/lodash-es')} lodash + * @property {import('npm:interactjs').default} interact + * @property {import('https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js').html} html + */ + +// Plotly types +/** + * @typedef {import('npm:@types/plotly.js')} Plotly + */ + +/** + * @typedef {Plotly.PlotlyDataLayoutConfig & {frames?: Plotly.Frame[]}} PlotObj */ // resizer.js types @@ -47,23 +60,90 @@ * @property {boolean} noChange - A flag specifying whether the provided target size was the same as the current plot size */ +// clipboard.js types +/** + * @typedef {HTMLElement & {value: string | number}} HTMLWithValue + */ + +/** + * @typedef {HTMLWithValue & { + * updateFromValue: Function, + * parseValue: Function, + * }} UIValueSpan + */ + +/** + * @typedef {HTMLWithValue & { + * setInnerHTML: Function, + * }} ConfigValueSpan + */ + +/** + * @typedef {Object} ConfigSpanProps + * @property {HTMLWithValue} label - The label element + * @property {UIValueSpan} ui_span - The span element for the UI value + * @property {ConfigValueSpan} config_span - The span element for the config value + * @property {keyof ConfigSpansObject} key - The key of this configspan + * @property {number | string} ui_value + * @property {number | string} [config_value] + * @property {Function} setConfigFromUI + * @property {Function} unsetConfig + */ + +/** + * @typedef {HTMLElement & ConfigSpanProps} ConfigSpan + */ + +/** + * @typedef {Object} ConfigSpansObject + * @property {ConfigSpan} filename + * @property {ConfigSpan} format + * @property {ConfigSpan} width + * @property {ConfigSpan} height + * @property {ConfigSpan} scale + */ + +/** + * @typedef {Object} toImageOptions + * @property {string | undefined} filename + * @property {string | undefined} format + * @property {number | undefined} width + * @property {number | undefined} height + * @property {number | undefined} scale + */ + +/** + * @typedef {Object} ClipboardHeaderProps + * @property {ConfigSpansObject} config_spans + * @property {toImageOptions} config_values + * @property {toImageOptions} ui_values + * @property {Function} updateConfigSync + */ + +/** + * @typedef {HTMLElement & ClipboardHeaderProps} ClipboardHeader +*/ + +// container.js types /** * The additional properties and methods attached to the HTML element `el` where the plotly.js plot is created (using `Plotly.react(el, ...plot_data)`) * @typedef {Object} PlotProps * @property {{ - * width: number, - * height: number, + * width?: number, + * height?: number, * }} layout_size */ /** * @typedef {Object} ContainerProps + * @property {PlotObj} plot_data - The plot data used for calling Plotly.react * @property {Function} isPoppedOut - Returns true if the container is popped out * @property {ResizeObserver} resizeObserver - The resizeObserver controlling the resizing of the CONTAINER and PLOT * @property {AbortController} controller - The AbortController used to stop all listeners and observers tied to the CONTAINER * @property {Plotly} Plotly - The Plotly library used in this Container * @property {HTMLElement & PlotProps} PLOT - The child div containint the plotly.js plot * @property {Element | null} CLIPBOARD_HEADER - The header containing all the clipboard related config spans + * @property {Function} toImageOptions * @property {*} [value] - The eventual value of the CONTAINER to be used for `@bind` inside Pluto */ diff --git a/js/src/url_deps.js b/js/src/url_deps.js new file mode 100644 index 0000000..e8e0b7d --- /dev/null +++ b/js/src/url_deps.js @@ -0,0 +1,4 @@ +export * as lodash from "https://esm.sh/lodash-es@4.17.21" +export { default as interact } from "https://esm.sh/interactjs@1.10.27" +export { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js" +export * as emotion from "https://esm.sh/@emotion/css@11.11.2" \ No newline at end of file diff --git a/js/src/url_imports.js b/js/src/url_imports.js deleted file mode 100644 index 1350408..0000000 --- a/js/src/url_imports.js +++ /dev/null @@ -1,8 +0,0 @@ -// We put all url imports here to have them in a single place -import { css } from "https://esm.sh/@emotion/css@11.11.2"; -import lodash from "https://esm.sh/lodash-es@4.17.21"; -import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; -import { default as interact } from "https://esm.sh/interactjs@1.10.26" - -// We need to create our custom emotion instance while loading the module to avoid -export { css, html, lodash, interact } \ No newline at end of file diff --git a/js/src/utils.js b/js/src/utils.js index a88172c..3c35501 100644 --- a/js/src/utils.js +++ b/js/src/utils.js @@ -1,6 +1,11 @@ -import { lodash as _ } from "./url_imports.js" +import * as _ from "https://esm.sh/lodash-es@4.17.21"; -// We use lodash for this for compactness +/** + * + * @param {Object} o + * @param {import("./typedef.js").JSDep_lodash} _ + * @returns + */ function removeTypedArray(o) { return _.isTypedArray(o) ? Array.from(o) @@ -9,18 +14,56 @@ function removeTypedArray(o) { : o; } +/** + * Process the plot object by removing typed arrays in the layout. + * + * @param {import("./typedef.js").PlotObj} plot_obj - The plot object to be processed + * @return {import("./typedef.js").PlotObj} The processed plot object + */ export function processPlotObj(plot_obj) { return _.update(plot_obj, "layout", removeTypedArray) } -export function getImageOptions(plot_obj) { +/** + * Extract the image export options from the plot_obj. + * + * @param {import("./typedef.js").PlotObj} plot_obj - The plot object to be processed + * @return {{ + * format: string, + * width?: number, + * height?: number, + * scale: number, + * filename: string + * }} The object containing the export data + */ + +export const image_options_defaults = { + format: "png", + width: 700, + height: 400, + scale: 1, + filename: "newplot" +} +/** + * Returns the image options for a given plot object, with default values if not provided. + * + * @param {import('./typedef.js').PlotObj} [plot_obj] - the plot object for which to get image options + * @return {{ + * format: string, + * width: number, + * height: number, + * scale: number, + * filename: string + * }} the image options with default values if not provided + */ +export function getImageOptions(plot_obj, d = image_options_defaults) { const o = plot_obj?.config?.toImageButtonOptions ?? {}; return { - format: o.format ?? "png", - width: o.width ?? undefined, - height: o.height ?? undefined, - scale: o.scale ?? 1, - filename: o.filename ?? "newplot", + format: o.format ?? d.format, + width: o.width ?? d.width, + height: o.height ?? d.height, + scale: o.scale ?? d.scale, + filename: o.filename ?? d.filename, }; } From 487c00d2e9331b20a58a010c725defd04dcb0a45 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Wed, 24 Apr 2024 10:40:05 +0200 Subject: [PATCH 17/39] draft implementation of global JS deps --- js/index.html | 9 +- js/src/clipboard.js | 139 +++++++++------------ js/src/container.js | 28 ++++- js/src/global_deps.js | 18 +-- js/src/prehooks.js | 15 ++- js/src/styles.js | 275 ++++++++++++++++++++++-------------------- js/src/typedef.js | 24 ++-- 7 files changed, 260 insertions(+), 248 deletions(-) diff --git a/js/index.html b/js/index.html index 142da5a..59e335a 100644 --- a/js/index.html +++ b/js/index.html @@ -1,19 +1,22 @@ + My Simple HTML Document diff --git a/js/src/clipboard.js b/js/src/clipboard.js index 14f5bfb..c2b7c61 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -1,28 +1,28 @@ import { addFormatConfigStyle, addClipboardHeaderStyle } from "./styles.js"; import { delay, getImageOptions, image_options_defaults } from "./utils.js"; -import { libs } from "./global_deps.js"; +import { GlobalDeps } from "./global_deps.js"; // Download formats const valid_download_formats = ["png", "svg", "webp", "jpeg", "full-json"]; // toImageOption keys -const toImageOptionKeys = ["format", "width", "height", "scale", "filename"]; +/** @type {(keyof import("./typedef.js").toImageOptions)[]} */ +export const toImageOptionKeys = ["format", "width", "height", "scale", "filename"]; /** * Function to add a configuration span element. * * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER - Clipboard Header element - * @param {keyof import("./typedef.js").ConfigSpansObject} name - lowercase name of the config span + * @param {keyof import("./typedef.js").OptionSpansObject} name - lowercase name of the config span + * @param {import("./typedef.js").JSDeps} [GlobalDeps] - Global dependencies containing at least html and lodash */ -export function addSingleConfigSpan(CLIPBOARD_HEADER, name) { - // Extract the dependencies - const { html, lodash } = libs +export function addSingleConfigSpan(CLIPBOARD_HEADER, name, { html, lodash } = GlobalDeps) { const config_span = html``; const label = html`${config_span}${lodash.capitalize(name)}:`; const ui_span = html``; - /** @type {import("./typedef.js").ConfigSpan} */ + /** @type {import("./typedef.js").ImageOptionSpan} */ const container = html` ${label} ${ui_span} `; @@ -39,7 +39,7 @@ export function addSingleConfigSpan(CLIPBOARD_HEADER, name) { // Initialize the config_span initializeConfigValueSpan(config_span, name); container.key = name; - CLIPBOARD_HEADER.config_spans[name] = container; + CLIPBOARD_HEADER.option_spans[name] = container; // Insert the container as a child of the CLIPBOARD_HEADER CLIPBOARD_HEADER.appendChild(container); // We put some convenience getters/setters @@ -59,13 +59,21 @@ export function addSingleConfigSpan(CLIPBOARD_HEADER, name) { }); // Add the onclick for setting/unsetting the config label.onclick = DualClick( - container.setConfigFromUI, (/** @type {Event} */ e) => { - console.log("e", e); e.preventDefault(); - container.unsetConfig(); + container.config_value = container.ui_value + }, + (/** @type {Event} */ e) => { + e.preventDefault(); + container.config_value = undefined; } ); + // Add the listener for imageOptions change + const listener = (/** @type {CustomEvent} */ e) => { + checkConfigSync(container) + }; + // @ts-ignore: addEventListener does not support function with CustomEvent as arguments + container.addEventListener("clipboard-header-change", listener); return; } @@ -74,14 +82,13 @@ export function addSingleConfigSpan(CLIPBOARD_HEADER, name) { * Add config spans to the given CLIPBOARD_HEADER. * * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER - the clipboard header to add config spans to + * @param {import("./typedef.js").JSDeps} [GlobalDeps] - Global dependencies containing at least html * @return {void} */ -function addConfigSpans(CLIPBOARD_HEADER) { - // Extract the dependencies - const { html } = libs +function addOptionSpans(CLIPBOARD_HEADER, { html } = GlobalDeps) { // @ts-ignore: Will be populated - CLIPBOARD_HEADER.config_spans = {}; + CLIPBOARD_HEADER.option_spans = {}; addSingleConfigSpan(CLIPBOARD_HEADER, "format"); addSingleConfigSpan(CLIPBOARD_HEADER, "width"); addSingleConfigSpan(CLIPBOARD_HEADER, "height"); @@ -100,11 +107,10 @@ function addConfigSpans(CLIPBOARD_HEADER) { * Adds a clipboard header to the given container. * * @param {import("./typedef.js").Container} CONTAINER - The container to which the clipboard header will be added. + * @param {import("./typedef.js").JSDeps} [GlobalDeps] - Global dependencies containing at least html * @return {undefined} span function does not return a value. */ -export function addClipboardHeader(CONTAINER) { - // Extract the dependencies - const { html } = libs +export function addClipboardHeader(CONTAINER, {html} = GlobalDeps) { // Return if the CLIPBOARD HEADER has been assigned already if (CONTAINER.CLIPBOARD_HEADER !== undefined) return; @@ -113,46 +119,19 @@ export function addClipboardHeader(CONTAINER) { class="plutoplotly-clipboard-header hidden" >`; addClipboardHeaderStyle(CLIPBOARD_HEADER); - CLIPBOARD_HEADER.config_values = toImageOptionsObj({ - target: CLIPBOARD_HEADER, - name: "clipboard-header-change", - type: "config" - }); - CLIPBOARD_HEADER.ui_values = toImageOptionsObj({ - target: CLIPBOARD_HEADER, - name: "clipboard-header-change", - type: "ui" - }); // Add the various spans for the UI - addConfigSpans(CLIPBOARD_HEADER) + addOptionSpans(CLIPBOARD_HEADER) + // Add the objects collecting ui and config options values + addImageOptionsObj(CLIPBOARD_HEADER, "ui"); + addImageOptionsObj(CLIPBOARD_HEADER, "config"); CONTAINER.appendChild(CLIPBOARD_HEADER); CONTAINER.CLIPBOARD_HEADER = CLIPBOARD_HEADER; - // Add the function that updates config sync - /** @span {import("./typedef.js").ClipboardHeader} */ - CLIPBOARD_HEADER.updateConfigSync = function() { - // @ts-ignore - for (const span of span.config_spans) { - checkConfigSync(span) - } - } - // Add the listener for imageOptions change - /** @param {CustomEvent} e */ - const listener = (e) => { - e.stopPropagation() - console.log(e.detail) - /** @type {keyof import("./typedef.js").ConfigSpansObject} */ - const key = e.detail.key - const span = CLIPBOARD_HEADER.config_spans[key] - checkConfigSync(span) - }; - // @ts-ignore: addEventListener does not support function with CustomEvent as arguments - CLIPBOARD_HEADER.addEventListener("clipboard-header-change", listener); return; } /** * Check if the ui and config value for a given span are in sync and update the label color (indirectly through css) and hover text accordingly. - * @param {import("./typedef.js").ConfigSpan} span + * @param {import("./typedef.js").ImageOptionSpan} span */ function checkConfigSync(span) { // We use the custom getters we'll set up in the container @@ -232,12 +211,10 @@ function updateFunction(key) { /** * * @param {import("./typedef.js").UIValueSpan} span - * @param {keyof import("./typedef.js").ConfigSpansObject} key + * @param {keyof import("./typedef.js").OptionSpansObject} key + * @param {import("./typedef.js").JSDeps} [GlobalDeps] - Global dependencies containing at least html */ -function initializeUIValueSpan(span, key) { - // Extract the dependencies - const { html } = libs - +function initializeUIValueSpan(span, key, { html } = GlobalDeps) { span.contentEditable = `${key !== "format"}`; span.updateFromValue = updateFunction(key); span.parseValue = parseFunction(key); @@ -299,11 +276,9 @@ function initializeUIValueSpan(span, key) { * * @param {import("./typedef.js").ConfigValueSpan} span - The span element to define the setter and getter on * @param {keyof import("./typedef.js").toImageOptions} key - The key to access the config value + * @param {import("./typedef.js").JSDeps} [GlobalDeps] - Global dependencies containing at least html */ -function initializeConfigValueSpan(span, key) { - // Extract the dependencies - const { html } = libs - +function initializeConfigValueSpan(span, key, { html } = GlobalDeps) { // We add the span that will contain the variable text const variableText = html`

`; span.appendChild(variableText); @@ -344,37 +319,31 @@ function initializeConfigValueSpan(span, key) { /** * - * @param {{ - * target: import("./typedef.js").ClipboardHeader, - * name: string - * type: string - * }} event_details - * @return {import("./typedef.js").toImageOptions} + * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER + * @param {string} type + * @param {import("./typedef.js").Container} type */ -function toImageOptionsObj({target, name, type}) { - const out = {_options: {}} +function addImageOptionsObj(CLIPBOARD_HEADER, type) { + /** @type {import('./typedef.js').toImageOptions} */ + // @ts-ignore: We populate the keys below + const out = {} + const header_key = type === "ui" ? "ui_values" : "config_values" + const value_key = type === "ui" ? "ui_span" : "config_span" for (const key of toImageOptionKeys) { + const span = CLIPBOARD_HEADER.option_spans[key][value_key] Object.defineProperty(out, key, { - get() {return this._options[key]}, - set(val) { - this._options[key] = val - target.dispatchEvent( - new CustomEvent(name, { - detail: {key, type} - }) - ) - }, + get: () => span.value, + set: (val) => span.value = val, enumerable: true, }) } - - // @ts-ignore: We did set up the properties as accessors - return out + // @ts-ignore: We did put the keys above + CLIPBOARD_HEADER[header_key] = out } // // span code updates the image options in the PLOT config with the provided ones // function setImageOptions(o) { -// for (const [key, container] of Object.entries(config_spans)) { +// for (const [key, container] of Object.entries(option_spans)) { // container.config_value = o[key]; // } // } @@ -386,7 +355,7 @@ function toImageOptionsObj({target, name, type}) { // const unset_button = CLIPBOARD_HEADER.querySelector(".clipboard-span.unset"); // if (firstRun) { // set_button.onclick = (e) => { -// for (const container of Object.values(config_spans)) { +// for (const container of Object.values(option_spans)) { // container.config_value = container.ui_value; // } // }; @@ -485,7 +454,7 @@ function sendToClipboard(blob) { // // We extract the image options from the provided parameters (if they exist) // const { Plotly, PLOT } = span // const config = {}; -// for (const [key, container] of Object.entries(config_spans)) { +// for (const [key, container] of Object.entries(option_spans)) { // let val = // container.config_value ?? // (span.isPoppedOut() ? container.ui_value : undefined); @@ -512,7 +481,7 @@ function sendToClipboard(blob) { // function saveImageToFile() { // const config = {}; -// for (const [key, container] of Object.entries(config_spans)) { +// for (const [key, container] of Object.entries(option_spans)) { // let val = // container.config_value ?? // (CONTAINER.isPoppedOut() ? container.ui_value : undefined); @@ -580,11 +549,11 @@ function sendToClipboard(blob) { // const csz = computeContainerSize({ // width: // target_plot_size.width ?? -// config_spans.width.config_value ?? +// option_spans.width.config_value ?? // current_plot_size.width, // height: // target_plot_size.height ?? -// config_spans.height.config_value ?? +// option_spans.height.config_value ?? // current_plot_size.height, // }); // for (const key of ["width", "height"]) { diff --git a/js/src/container.js b/js/src/container.js index d6686ea..2ca8e72 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -1,6 +1,6 @@ //@ts-check -import { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js"; -import { addClipboardHeader } from "./clipboard.js"; +import { GlobalDeps } from "./global_deps.js"; +import { addClipboardHeader, toImageOptionKeys } from "./clipboard.js"; import { addContainerStyle } from "./styles.js"; /** * Creates the Container element used by PlutoPlotly to wrap the plotly.js plot and adds additional functionality like resizing and enhanced clipboard. @@ -8,7 +8,7 @@ import { addContainerStyle } from "./styles.js"; * @param {import("./typedef.js").Plotly} Plotly - The Plotly object. Defaults to globalThis.Plotly. * @return {import("./typedef.js").Container} The container element for the Plotly plot. */ -export function makeContainer(Plotly = globalThis.Plotly) { +export function makeContainer(Plotly = globalThis.Plotly, { html } = GlobalDeps) { const /** @type {import("./typedef.js").Container} */ CONTAINER = html`
`; CONTAINER.Plotly = Plotly // Add the style to it @@ -40,5 +40,27 @@ export function makeContainer(Plotly = globalThis.Plotly) { CONTAINER.isPoppedOut = function() { return this.classList.contains("popped-out"); }; + // We add the function to extract the image options for saving/copying + CONTAINER.toImageOptions = toImageOptions return CONTAINER +} + +/** + * Extracts the image options from the clipboard header + * @this {import("./typedef.js").Container} + */ +function toImageOptions() { + const { CLIPBOARD_HEADER } = this + /** @type {Partial} */ + const options = {} + for (const key of toImageOptionKeys) { + const config_value = CLIPBOARD_HEADER.config_values[key] + const ui_value = CLIPBOARD_HEADER.ui_values[key] + const isPopped = this.isPoppedOut() + const value = config_value ?? (isPopped ? ui_value : undefined) + if (value !== undefined) { + options[key] = value + } + } + return options } \ No newline at end of file diff --git a/js/src/global_deps.js b/js/src/global_deps.js index 1f1129c..cbd31ee 100644 --- a/js/src/global_deps.js +++ b/js/src/global_deps.js @@ -2,7 +2,7 @@ /** @type {import("./typedef.js").JSDeps} */ // @ts-ignore: Will be populated at runtime -export const libs = {}; +export const GlobalDeps = {}; /** * Sets global dependencies for emotion, lodash, interact and html. @@ -11,10 +11,10 @@ export const libs = {}; * @return {void} */ export function setGlobalDeps({ emotion, lodash, interact, html }) { - libs.emotion = emotion; - libs.lodash = lodash; - libs.interact = interact; - libs.html = html; + GlobalDeps.emotion = emotion; + GlobalDeps.lodash = lodash; + GlobalDeps.interact = interact; + GlobalDeps.html = html; } /** @@ -24,9 +24,9 @@ export function setGlobalDeps({ emotion, lodash, interact, html }) { */ export function validGlobalDeps() { return ( - libs.emotion != undefined && - libs.lodash != undefined && - libs.interact != undefined && - libs.html != undefined + GlobalDeps.emotion != undefined && + GlobalDeps.lodash != undefined && + GlobalDeps.interact != undefined && + GlobalDeps.html != undefined ); } \ No newline at end of file diff --git a/js/src/prehooks.js b/js/src/prehooks.js index 48d43fc..707fb0e 100644 --- a/js/src/prehooks.js +++ b/js/src/prehooks.js @@ -2,19 +2,26 @@ // import { interact, html, lodash as _ } from "./url_imports.js" import { makeContainer } from "./container.js"; import { processPlotObj } from "./utils.js"; -import { setGlobalDeps, validGlobalDeps } from "./global_deps.js"; -export { setGlobalDeps, validGlobalDeps, makeContainer } +import { setGlobalDeps, validGlobalDeps, GlobalDeps} from "./global_deps.js"; +export { setGlobalDeps, validGlobalDeps, makeContainer, GlobalDeps } // We start by putting all the variable interpolation here at the beginning // We have to convert all typedarrays in the layout to normal arrays. See Issue #25 +/** + * Creates a Plotly plot in a container element with the given plot object. + * + * @param {import("./typedef.js").PlotObj} plot_obj - The plot object containing the data, layout, and configuration for the plot. + * @param {import("./typedef.js").Plotly} [Plotly=globalThis.Plotly] - The Plotly library to use for creating the plot. Defaults to the global Plotly object. + * @return {Object} The container element containing the created plot. + */ export function createPlot(plot_obj, Plotly = globalThis.Plotly) { const CONTAINER = makeContainer(); const { PLOT } = CONTAINER; // Record or update the layout width/height if provided explicitly PLOT.layout_size = { - height: plot_obj.layout.height, - width: plot_obj.layout.width, + height: plot_obj.layout?.height, + width: plot_obj.layout?.width, } // Removed typed arrays from the layout const _plot_obj = processPlotObj(plot_obj) diff --git a/js/src/styles.js b/js/src/styles.js index ba71305..83b1216 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -1,12 +1,12 @@ -import { libs } from "./global_deps.js"; +import { GlobalDeps } from "./global_deps.js"; /** * Adds a container style to the specified container element. * * @param {import("./typedef.js").Container} CONTAINER - The container element to which the style will be added + * @param {import("./typedef.js").JSDeps} GlobalDeps - The object containing the emotion library as property */ -export function addContainerStyle(CONTAINER) { - const css = libs.emotion.css; +export function addContainerStyle(CONTAINER, { emotion: {css} } = GlobalDeps) { const cl = css` & { width: 100%; @@ -22,154 +22,167 @@ export function addContainerStyle(CONTAINER) { z-index: 1000; position: fixed; resize: both; - background: var(--main-bg-color); - border: 3px solid var(--kbd-border-color); + background: var(--main-bg-color, var(--bg-color)); + border: 3px solid var(--kbd-border-color, var(--border-color)); border-radius: 12px; border-top-left-radius: 0px; border-top-right-radius: 0px; } + // We add defaults color variables for outside Pluto + @media (prefers-color-scheme: light) { + --border-color: #dfdfdf; + --bg-color: white; + --tag-color: #ef6155; + --macro-color: #5c8c5f; + --output-color: hsl(0, 0%, 25%); + } + @media (prefers-color-scheme: dark) { + --border-color: #222222; + --bg-color: hsl(0deg 0% 12%); + --tag-color: #ef6155; + --macro-color: #82b38b; + --output-color: hsl(0deg 0% 77%); + } `; CONTAINER.classList.add(cl); } - /** * Adds the clipboard header style to the given element. * * @param {HTMLElement} element - the element to which the clipboard header style will be added + * @param {import("./typedef.js").JSDeps} GlobalDeps - The object containing the emotion library as property */ -export function addClipboardHeaderStyle(element) { - const css = libs.emotion.css +export function addClipboardHeaderStyle(element, { emotion: {css} } = GlobalDeps) { const cl = css` - & { - display: flex; - flex-flow: row wrap; - background: var(--main-bg-color); - border: 3px solid var(--kbd-border-color); - border-top-left-radius: 12px; - border-top-right-radius: 12px; - position: fixed; - z-index: 1001; - cursor: move; - transform: translate(0px, -100%); - padding: 5px; - } - & span { - display: inline-block; - flex: 1; - } - &.hidden { - display: none; - } - & .clipboard-span { - position: relative; - } - & .clipboard-value { - padding-right: 5px; - padding-left: 2px; - cursor: text; - } - & .config-value { - font-weight: normal; - color: var(--pluto-output-color); - display: none; - position: absolute; - background: var(--main-bg-color); - border: 3px solid var(--kbd-border-color); - border-radius: 12px; - transform: translate(0px, calc(-100% - 10px)); - padding: 5px; - } - & .label { - user-select: none; - } - & .label:hover span.config-value { - display: inline-block; - min-width: 150px; - } - & .clipboard-span.matching-config .label { - color: var(--cm-macro-color); - font-weight: bold; - } - & .clipboard-span.different-config .label { - color: var(--cm-tag-color); - font-weight: bold; - } - & .clipboard-span.format { - display: none; - } - & .clipboard-span.filename { - flex: 0 0 100%; - text-align: center; - border-top: 3px solid var(--kbd-border-color); - margin-top: 5px; - display: none; - } - &.filesave-extras .clipboard-span.filename { - display: inline-block; - } - & .clipboard-value.filename { - margin-left: 3px; - text-align: left; - min-width: min(60%, min-content); - } - &.filesave-extras .clipboard-span.format { - display: inline-flex; - } -`; + & { + display: flex; + flex-flow: row wrap; + background: var(--main-bg-color, var(--bg-color)); + border: 3px solid var(--kbd-border-color, var(--border-color)); + border-top-left-radius: 12px; + border-top-right-radius: 12px; + position: fixed; + z-index: 1001; + cursor: move; + transform: translate(0px, -100%); + padding: 5px; + } + & span { + display: inline-block; + flex: 1; + } + &.hidden { + display: none; + } + & .clipboard-span { + position: relative; + } + & .clipboard-value { + padding-right: 5px; + padding-left: 2px; + cursor: text; + } + & .config-value { + font-weight: normal; + color: var(--pluto-output-color, var(--output-color)); + display: none; + position: absolute; + background: var(--main-bg-color, var(--bg-color)); + border: 3px solid var(--kbd-border-color, var(--border-color)); + border-radius: 12px; + transform: translate(0px, calc(-100% - 10px)); + padding: 5px; + } + & .label { + user-select: none; + } + & .label:hover span.config-value { + display: inline-block; + min-width: 150px; + } + & .clipboard-span[config="matching"] .label { + color: var(--cm-macro-color, var(--macro-color)); + font-weight: bold; + } + & .clipboard-span[config="different"] .label { + color: var(--cm-tag-color, var(--tag-color)); + font-weight: bold; + } + & .clipboard-span.format { + display: none; + } + & .clipboard-span.filename { + flex: 0 0 100%; + text-align: center; + border-top: 3px solid var(--kbd-border-color, var(--border-color)); + margin-top: 5px; + display: none; + } + &.filesave-extras .clipboard-span.filename { + display: inline-block; + } + & .clipboard-value.filename { + margin-left: 3px; + text-align: left; + min-width: min(60%, min-content); + } + &.filesave-extras .clipboard-span.format { + display: inline-flex; + } + `; element.classList.add(cl); } - /** * @param {HTMLElement} element - the element to which the clipboard header style will be added + * @param {import("./typedef.js").JSDeps} GlobalDeps - The object containing the emotion library as property */ -export function addFormatConfigStyle(element) { - const css = libs.emotion.css +export function addFormatConfigStyle(element, { emotion: {css} } = GlobalDeps) { const format_config_style = css` - & > .label { - flex: 0 0 0; - } - & .clipboard-value { - position: relative; - flex: 1 0 auto; - min-width: 30px; - margin-right: 10px; - } - & .format-options { - display: inline-flex; - flex-flow: column; - position: absolute; - background: var(--main-bg-color); - border-radius: 12px; - padding-left: 3px; - z-index: 2000; - } - & .format-options:hover { - cursor: pointer; - border: 3px solid var(--kbd-border-color); - padding: 3px; - transform: translate(-3px, -6px); - } - & .format-option { - display: none; - margin-top: 3px; - } - & .format-options:hover .format-option { - display: inline-block; - } - & .format-options[selected="png"] .format-option.png, - & .format-options[selected="svg"] .format-option.svg, - & .format-options[selected="webp"] .format-option.webp, - & .format-options[selected="jpeg"] .format-option.jpeg, - & .format-options[selected="full-json"] .format-option.full-json { - margin-top: 0px; - order: -1; - display: inline-block; - } - & .format-options .format-option:hover { - background-color: var(--kbd-border-color); - } -`; + & > .label { + flex: 0 0 0; + } + & .clipboard-value { + position: relative; + flex: 1 0 auto; + min-width: 30px; + margin-right: 10px; + } + & .format-options { + display: inline-flex; + flex-flow: column; + position: absolute; + background: var(--main-bg-color, var(--bg-color)); + border-radius: 12px; + padding-left: 3px; + z-index: 2000; + } + & .format-options:hover { + cursor: pointer; + border: 3px solid var(--kbd-border-color, var(--border-color)); + padding: 3px; + transform: translate(-3px, -6px); + } + & .format-option { + display: none; + margin-top: 3px; + } + & .format-options:hover .format-option { + display: inline-block; + } + & .format-options[selected="png"] .format-option.png, + & .format-options[selected="svg"] .format-option.svg, + & .format-options[selected="webp"] .format-option.webp, + & .format-options[selected="jpeg"] .format-option.jpeg, + & .format-options[selected="full-json"] .format-option.full-json { + margin-top: 0px; + order: -1; + display: inline-block; + } + & .format-options .format-option:hover { + background-color: var(--kbd-border-color, var(--border-color)); + } + `; element.classList.add(format_config_style); } diff --git a/js/src/typedef.js b/js/src/typedef.js index 6b54a83..0ab575e 100644 --- a/js/src/typedef.js +++ b/js/src/typedef.js @@ -79,28 +79,26 @@ */ /** - * @typedef {Object} ConfigSpanProps + * @typedef {Object} ImageOptionSpanProps * @property {HTMLWithValue} label - The label element * @property {UIValueSpan} ui_span - The span element for the UI value * @property {ConfigValueSpan} config_span - The span element for the config value - * @property {keyof ConfigSpansObject} key - The key of this configspan + * @property {keyof OptionSpansObject} key - The key of this ImageOptionSpan * @property {number | string} ui_value * @property {number | string} [config_value] - * @property {Function} setConfigFromUI - * @property {Function} unsetConfig */ /** - * @typedef {HTMLElement & ConfigSpanProps} ConfigSpan + * @typedef {HTMLElement & ImageOptionSpanProps} ImageOptionSpan */ /** - * @typedef {Object} ConfigSpansObject - * @property {ConfigSpan} filename - * @property {ConfigSpan} format - * @property {ConfigSpan} width - * @property {ConfigSpan} height - * @property {ConfigSpan} scale + * @typedef {Object} OptionSpansObject + * @property {ImageOptionSpan} filename + * @property {ImageOptionSpan} format + * @property {ImageOptionSpan} width + * @property {ImageOptionSpan} height + * @property {ImageOptionSpan} scale */ /** @@ -114,7 +112,7 @@ /** * @typedef {Object} ClipboardHeaderProps - * @property {ConfigSpansObject} config_spans + * @property {OptionSpansObject} option_spans * @property {toImageOptions} config_values * @property {toImageOptions} ui_values * @property {Function} updateConfigSync @@ -142,7 +140,7 @@ * @property {AbortController} controller - The AbortController used to stop all listeners and observers tied to the CONTAINER * @property {Plotly} Plotly - The Plotly library used in this Container * @property {HTMLElement & PlotProps} PLOT - The child div containint the plotly.js plot - * @property {Element | null} CLIPBOARD_HEADER - The header containing all the clipboard related config spans + * @property {ClipboardHeader} CLIPBOARD_HEADER - The header containing all the clipboard related config spans * @property {Function} toImageOptions * @property {*} [value] - The eventual value of the CONTAINER to be used for `@bind` inside Pluto */ From 8e1660e3e67ab963e37a1be8b2307c6d6b0f2753 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sat, 20 Jul 2024 16:30:47 +0200 Subject: [PATCH 18/39] add PLOT_PANE to contain the plot and clean some global deps handling --- js/index.html | 34 ++++++++++++++++++++++++++++++-- js/src/clipboard.js | 30 +++++++++++++++-------------- js/src/container.js | 32 ++++++++++++++++++++++++------ js/src/global_deps.js | 10 ++++++++++ js/src/styles.js | 45 ++++++++++++++++++++++++++++++++++--------- js/src/typedef.js | 1 + 6 files changed, 121 insertions(+), 31 deletions(-) diff --git a/js/index.html b/js/index.html index 59e335a..c1f4f3c 100644 --- a/js/index.html +++ b/js/index.html @@ -1,5 +1,6 @@ + My Simple HTML Document @@ -9,19 +10,48 @@ const { makeContainer, setGlobalDeps, GlobalDeps } = asd setGlobalDeps(deps) console.log(asd) - const { emotion: {css}} = GlobalDeps + const { emotion: { css } } = GlobalDeps console.log(css) const dv = document.getElementById('asd'); const container = dv.insertAdjacentElement('afterend', makeContainer()); window.container = container window.option_spans = container.CLIPBOARD_HEADER.option_spans + // container.classList.toggle('popped-out', true) container.CLIPBOARD_HEADER.classList.toggle('hidden') window.width = option_spans['width'] + + // Do the plot + // Sample data + const trace = { + x: [1, 2, 3, 4, 5], + y: [10, 15, 13, 17, 20], + mode: 'markers', + type: 'scatter', + marker: { size: 12 } + }; + + const layout = { + title: 'Sample Scatter Plot', + xaxis: { + title: 'X Axis' + }, + yaxis: { + title: 'Y Axis' + } + }; + + const data = [trace]; + const plot_obj = { data, layout } + + // Render the plot + Plotly.react(container.PLOT, plot_obj); +

Hello, World!

This is a simple HTML document.

-
ASD
+
ASD
+ \ No newline at end of file diff --git a/js/src/clipboard.js b/js/src/clipboard.js index c2b7c61..0c697eb 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -1,6 +1,6 @@ import { addFormatConfigStyle, addClipboardHeaderStyle } from "./styles.js"; import { delay, getImageOptions, image_options_defaults } from "./utils.js"; -import { GlobalDeps } from "./global_deps.js"; +import { mergeDeps } from "./global_deps.js"; // Download formats const valid_download_formats = ["png", "svg", "webp", "jpeg", "full-json"]; @@ -13,10 +13,10 @@ export const toImageOptionKeys = ["format", "width", "height", "scale", "filenam * * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER - Clipboard Header element * @param {keyof import("./typedef.js").OptionSpansObject} name - lowercase name of the config span - * @param {import("./typedef.js").JSDeps} [GlobalDeps] - Global dependencies containing at least html and lodash + * @param {Partial} [deps] - Global dependencies containing at least html and lodash */ -export function addSingleConfigSpan(CLIPBOARD_HEADER, name, { html, lodash } = GlobalDeps) { - +export function addSingleConfigSpan(CLIPBOARD_HEADER, name, deps = {}) { + const { html, lodash } = mergeDeps(deps) const config_span = html``; const label = html`${config_span}${lodash.capitalize(name)}:} [deps] - Global dependencies containing at least html * @return {void} */ -function addOptionSpans(CLIPBOARD_HEADER, { html } = GlobalDeps) { - +function addOptionSpans(CLIPBOARD_HEADER, deps = {}) { + const { html } = mergeDeps(deps) // @ts-ignore: Will be populated CLIPBOARD_HEADER.option_spans = {}; addSingleConfigSpan(CLIPBOARD_HEADER, "format"); @@ -107,11 +107,11 @@ function addOptionSpans(CLIPBOARD_HEADER, { html } = GlobalDeps) { * Adds a clipboard header to the given container. * * @param {import("./typedef.js").Container} CONTAINER - The container to which the clipboard header will be added. - * @param {import("./typedef.js").JSDeps} [GlobalDeps] - Global dependencies containing at least html + * @param {Partial} [deps] - Global dependencies containing at least html * @return {undefined} span function does not return a value. */ -export function addClipboardHeader(CONTAINER, {html} = GlobalDeps) { - +export function addClipboardHeader(CONTAINER, deps = {}) { + const { html } = mergeDeps(deps) // Return if the CLIPBOARD HEADER has been assigned already if (CONTAINER.CLIPBOARD_HEADER !== undefined) return; /** @type {import("./typedef.js").ClipboardHeader} */ @@ -212,9 +212,10 @@ function updateFunction(key) { * * @param {import("./typedef.js").UIValueSpan} span * @param {keyof import("./typedef.js").OptionSpansObject} key - * @param {import("./typedef.js").JSDeps} [GlobalDeps] - Global dependencies containing at least html + * @param {Partial} [deps] - Global dependencies containing at least html */ -function initializeUIValueSpan(span, key, { html } = GlobalDeps) { +function initializeUIValueSpan(span, key, deps = {}) { + const { html } = mergeDeps(deps); span.contentEditable = `${key !== "format"}`; span.updateFromValue = updateFunction(key); span.parseValue = parseFunction(key); @@ -276,9 +277,10 @@ function initializeUIValueSpan(span, key, { html } = GlobalDeps) { * * @param {import("./typedef.js").ConfigValueSpan} span - The span element to define the setter and getter on * @param {keyof import("./typedef.js").toImageOptions} key - The key to access the config value - * @param {import("./typedef.js").JSDeps} [GlobalDeps] - Global dependencies containing at least html + * @param {Partial} [deps] - Global dependencies containing at least html */ -function initializeConfigValueSpan(span, key, { html } = GlobalDeps) { +function initializeConfigValueSpan(span, key, deps = {}) { + const { html } = mergeDeps(deps); // We add the span that will contain the variable text const variableText = html`

`; span.appendChild(variableText); diff --git a/js/src/container.js b/js/src/container.js index 2ca8e72..323a189 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -1,22 +1,26 @@ //@ts-check -import { GlobalDeps } from "./global_deps.js"; +import { mergeDeps } from "./global_deps.js"; import { addClipboardHeader, toImageOptionKeys } from "./clipboard.js"; -import { addContainerStyle } from "./styles.js"; +import { addContainerStyle, addPlotPaneStyle } from "./styles.js"; /** * Creates the Container element used by PlutoPlotly to wrap the plotly.js plot and adds additional functionality like resizing and enhanced clipboard. * * @param {import("./typedef.js").Plotly} Plotly - The Plotly object. Defaults to globalThis.Plotly. + * @param {Partial} [deps] - An optional object containing the JS dependencies to use, which are 'html', 'emotion', 'lodash' and 'interact'. * @return {import("./typedef.js").Container} The container element for the Plotly plot. */ -export function makeContainer(Plotly = globalThis.Plotly, { html } = GlobalDeps) { +export function makeContainer(Plotly = globalThis.Plotly, deps = {}) { + const { html } = mergeDeps(deps) const /** @type {import("./typedef.js").Container} */ CONTAINER = html`
`; CONTAINER.Plotly = Plotly // Add the style to it - addContainerStyle(CONTAINER); + addContainerStyle(CONTAINER, deps); // Add the clipboard header - addClipboardHeader(CONTAINER); + addClipboardHeader(CONTAINER, deps); + // Add the Plot Pane + addPlotPane(CONTAINER, deps); // Create the child div that will contain the actual plot - const PLOT = CONTAINER.PLOT = CONTAINER.appendChild( + const PLOT = CONTAINER.PLOT = CONTAINER.PLOT_PANE.appendChild( html`
` ); // We set the flag of remove @@ -45,6 +49,22 @@ export function makeContainer(Plotly = globalThis.Plotly, { html } = GlobalDeps) return CONTAINER } +/** + * Adds a clipboard header to the given container. + * + * @param {import("./typedef.js").Container} CONTAINER - The container to which the clipboard header will be added. + * @param {Partial} [deps] - Global dependencies containing at least html + * @return {undefined} span function does not return a value. + */ +function addPlotPane(CONTAINER, deps = {}) { + const { html } = mergeDeps(deps) + // Return if the PLOT_PANE has been assigned already + if (CONTAINER.PLOT_PANE !== undefined) return; + const PLOT_PANE = CONTAINER.PLOT_PANE = html`
`; + addPlotPaneStyle(PLOT_PANE, deps); + CONTAINER.appendChild(PLOT_PANE); +} + /** * Extracts the image options from the clipboard header * @this {import("./typedef.js").Container} diff --git a/js/src/global_deps.js b/js/src/global_deps.js index cbd31ee..b787e84 100644 --- a/js/src/global_deps.js +++ b/js/src/global_deps.js @@ -29,4 +29,14 @@ export function validGlobalDeps() { GlobalDeps.interact != undefined && GlobalDeps.html != undefined ); +} + +/** + * Merges the given dependencies with the existing global dependencies. + * + * @param {Partial} deps - An object containing the JS dependencies to override. + * @return {import("./typedef.js").JSDeps} - A new object that contains the defaul global deps with the provided ones overwritten. + */ +export function mergeDeps(deps) { + return {...GlobalDeps, ...deps} } \ No newline at end of file diff --git a/js/src/styles.js b/js/src/styles.js index 83b1216..d10e004 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -1,14 +1,16 @@ -import { GlobalDeps } from "./global_deps.js"; +import { mergeDeps } from "./global_deps.js"; /** * Adds a container style to the specified container element. * * @param {import("./typedef.js").Container} CONTAINER - The container element to which the style will be added - * @param {import("./typedef.js").JSDeps} GlobalDeps - The object containing the emotion library as property + * @param {Partial} [deps] - The object containing the emotion library as property */ -export function addContainerStyle(CONTAINER, { emotion: {css} } = GlobalDeps) { +export function addContainerStyle(CONTAINER, deps = {}) { + const { emotion: {css} } = mergeDeps(deps) const cl = css` & { + position: relative; width: 100%; height: 100%; min-height: 0; @@ -18,7 +20,7 @@ export function addContainerStyle(CONTAINER, { emotion: {css} } = GlobalDeps) { margin: 0 auto; // This centers the plot } &.popped-out { - overflow: auto; + overflow: show; z-index: 1000; position: fixed; resize: both; @@ -51,9 +53,10 @@ export function addContainerStyle(CONTAINER, { emotion: {css} } = GlobalDeps) { * Adds the clipboard header style to the given element. * * @param {HTMLElement} element - the element to which the clipboard header style will be added - * @param {import("./typedef.js").JSDeps} GlobalDeps - The object containing the emotion library as property + * @param {Partial} [deps] - The object containing the emotion library as property */ -export function addClipboardHeaderStyle(element, { emotion: {css} } = GlobalDeps) { +export function addClipboardHeaderStyle(element, deps = {}) { + const { emotion: {css} } = mergeDeps(deps) const cl = css` & { display: flex; @@ -62,11 +65,12 @@ export function addClipboardHeaderStyle(element, { emotion: {css} } = GlobalDeps border: 3px solid var(--kbd-border-color, var(--border-color)); border-top-left-radius: 12px; border-top-right-radius: 12px; - position: fixed; + position: absolute; z-index: 1001; cursor: move; transform: translate(0px, -100%); padding: 5px; + width: 100%; } & span { display: inline-block; @@ -136,9 +140,10 @@ export function addClipboardHeaderStyle(element, { emotion: {css} } = GlobalDeps /** * @param {HTMLElement} element - the element to which the clipboard header style will be added - * @param {import("./typedef.js").JSDeps} GlobalDeps - The object containing the emotion library as property + * @param {Partial} [deps] - The object containing the emotion library as property */ -export function addFormatConfigStyle(element, { emotion: {css} } = GlobalDeps) { +export function addFormatConfigStyle(element, deps = {}) { + const { emotion: {css} } = mergeDeps(deps) const format_config_style = css` & > .label { flex: 0 0 0; @@ -186,3 +191,25 @@ export function addFormatConfigStyle(element, { emotion: {css} } = GlobalDeps) { `; element.classList.add(format_config_style); } + +/** + * Adds a plot_pane style to the specified element. + * + * @param {HTMLElement} PLOT_PANE - The PLOT PANE, which will contain the actual plotlyjs plot + * @param {Partial} [deps] - The object containing the emotion library as property + */ +export function addPlotPaneStyle(PLOT_PANE, deps = {}) { + const { emotion: {css} } = mergeDeps(deps) + const cl = css` + & { + width: 100%; + height: 100%; + min-height: 0; + min-width: 0; + } + & .js-plotly-plot .plotly div { + margin: 0 auto; // This centers the plot + } + `; + PLOT_PANE.classList.add(cl); +} \ No newline at end of file diff --git a/js/src/typedef.js b/js/src/typedef.js index 0ab575e..6b5e645 100644 --- a/js/src/typedef.js +++ b/js/src/typedef.js @@ -141,6 +141,7 @@ * @property {Plotly} Plotly - The Plotly library used in this Container * @property {HTMLElement & PlotProps} PLOT - The child div containint the plotly.js plot * @property {ClipboardHeader} CLIPBOARD_HEADER - The header containing all the clipboard related config spans + * @property {HTMLElement} PLOT_PANE - The container of the actual plot object, only use to control the size of the plot * @property {Function} toImageOptions * @property {*} [value] - The eventual value of the CONTAINER to be used for `@bind` inside Pluto */ From 3cb25a01fb12f70597e022c806063f5c59888a98 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 21 Jul 2024 10:34:39 +0200 Subject: [PATCH 19/39] add function to update plot and draft implementation of popping out --- js/index.html | 6 +++--- js/src/clipboard.js | 25 +++++++++++++++++++++++++ js/src/container.js | 39 +++++++++++++++++++++++++++++++++++++++ js/src/prehooks.js | 4 ++-- js/src/styles.js | 31 +++++++++++-------------------- js/src/typedef.js | 1 + 6 files changed, 81 insertions(+), 25 deletions(-) diff --git a/js/index.html b/js/index.html index c1f4f3c..df32e91 100644 --- a/js/index.html +++ b/js/index.html @@ -7,7 +7,7 @@

Hello, World!

This is a simple HTML document.

-
ASD
+
ASD
\ No newline at end of file diff --git a/js/src/clipboard.js b/js/src/clipboard.js index 0c697eb..9f27fe9 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -1,6 +1,7 @@ import { addFormatConfigStyle, addClipboardHeaderStyle } from "./styles.js"; import { delay, getImageOptions, image_options_defaults } from "./utils.js"; import { mergeDeps } from "./global_deps.js"; +import { updateContainerPosition } from "./container.js"; // Download formats const valid_download_formats = ["png", "svg", "webp", "jpeg", "full-json"]; @@ -496,6 +497,30 @@ function sendToClipboard(blob) { // Plotly.downloadImage(PLOT, config); // } + + +/** + * Function to pop out the container from the current position to a fixed one. + * + * @param {import("./typedef.js").Container} CONTAINER - Main container of the plutoplotly plot + * @param {Partial} [deps] - Global dependencies containing at least html and lodash + */ +export function popContainer(CONTAINER, deps = {}) { + // We update the container position with the current one, also updating the css variables + updateContainerPosition(CONTAINER) + CONTAINER.classList.toggle("popped-out", true); +} + +/** + * Function to unpop the container from the fixed position. + * + * @param {import("./typedef.js").Container} CONTAINER - Main container of the plutoplotly plot + * @param {Partial} [deps] - Global dependencies containing at least html and lodash + */ +export function unpopContainer(CONTAINER, deps = {}) { + CONTAINER.classList.toggle("popped-out", false); +} + // let container_rect = { width: 0, height: 0, top: 0, left: 0 }; // function unpop_container(cl) { // CONTAINER.classList.toggle("popped-out", false); diff --git a/js/src/container.js b/js/src/container.js index 323a189..f56b03b 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -49,6 +49,45 @@ export function makeContainer(Plotly = globalThis.Plotly, deps = {}) { return CONTAINER } +/** + * Add the plot data to the already created CONTAINER and update the plot + * + * @param {import("./typedef.js").Container} CONTAINER - The plutoplotly Container. + * @param {import("./typedef.js").PlotObj} plot_data - An optional object containing the JS dependencies to use, which are 'html', 'emotion', 'lodash' and 'interact'. + * @return {undefined} This function does not return anything. + */ +export function updatePlotData(CONTAINER, plot_data) { + // Extract the plotly library + const { Plotly, PLOT } = CONTAINER + CONTAINER.plot_data = plot_data + Plotly.react(PLOT, plot_data) +} + +/** + * Compute the `position` field of the provided `CONTAINER` using getBoundingClientRect. + * + * @param {import("./typedef.js").Container} CONTAINER - The CONTAINER object to update. + * @return {DOMRect} the function does not return a value. + */ +function computeContainerPosition(CONTAINER) { + CONTAINER.position = CONTAINER.getBoundingClientRect(); + return CONTAINER.position +} + +/** + * Updates the position of the CONTAINER based on the provided left and top values. + * + * @param {import("./typedef.js").Container} CONTAINER - The container element to update. + * @param {DOMRect} [position] } + * @return {undefined} the function does not return a value. + */ +export function updateContainerPosition(CONTAINER, position) { + CONTAINER.position = position ?? computeContainerPosition(CONTAINER) + const { left, top } = CONTAINER.position + CONTAINER.style.setProperty('--element-top', top + 'px') + CONTAINER.style.setProperty('--element-left', left + 'px') +} + /** * Adds a clipboard header to the given container. * diff --git a/js/src/prehooks.js b/js/src/prehooks.js index 707fb0e..b72747d 100644 --- a/js/src/prehooks.js +++ b/js/src/prehooks.js @@ -1,9 +1,9 @@ // This file contains utilities to be executed before calling the plot function. // import { interact, html, lodash as _ } from "./url_imports.js" -import { makeContainer } from "./container.js"; +import { makeContainer, updatePlotData } from "./container.js"; import { processPlotObj } from "./utils.js"; import { setGlobalDeps, validGlobalDeps, GlobalDeps} from "./global_deps.js"; -export { setGlobalDeps, validGlobalDeps, makeContainer, GlobalDeps } +export { setGlobalDeps, validGlobalDeps, makeContainer, GlobalDeps, updatePlotData } // We start by putting all the variable interpolation here at the beginning // We have to convert all typedarrays in the layout to normal arrays. See Issue #25 diff --git a/js/src/styles.js b/js/src/styles.js index d10e004..a73f257 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -9,26 +9,17 @@ import { mergeDeps } from "./global_deps.js"; export function addContainerStyle(CONTAINER, deps = {}) { const { emotion: {css} } = mergeDeps(deps) const cl = css` - & { - position: relative; - width: 100%; - height: 100%; - min-height: 0; - min-width: 0; - } - & .js-plotly-plot .plotly div { - margin: 0 auto; // This centers the plot - } &.popped-out { - overflow: show; + overflow: hidden; z-index: 1000; position: fixed; resize: both; background: var(--main-bg-color, var(--bg-color)); border: 3px solid var(--kbd-border-color, var(--border-color)); border-radius: 12px; - border-top-left-radius: 0px; - border-top-right-radius: 0px; + border-top-left-radius: 12px; + border-top-right-radius: 12px; + box-sizing: border-box; } // We add defaults color variables for outside Pluto @media (prefers-color-scheme: light) { @@ -59,19 +50,17 @@ export function addClipboardHeaderStyle(element, deps = {}) { const { emotion: {css} } = mergeDeps(deps) const cl = css` & { - display: flex; + display: none; flex-flow: row wrap; background: var(--main-bg-color, var(--bg-color)); - border: 3px solid var(--kbd-border-color, var(--border-color)); - border-top-left-radius: 12px; - border-top-right-radius: 12px; - position: absolute; - z-index: 1001; cursor: move; - transform: translate(0px, -100%); padding: 5px; width: 100%; } + .popped-out & { + border-bottom: 3px solid var(--kbd-border-color, var(--border-color)); + display: flex; + } & span { display: inline-block; flex: 1; @@ -206,6 +195,8 @@ export function addPlotPaneStyle(PLOT_PANE, deps = {}) { height: 100%; min-height: 0; min-width: 0; + margin: 0; + padding: 0; } & .js-plotly-plot .plotly div { margin: 0 auto; // This centers the plot diff --git a/js/src/typedef.js b/js/src/typedef.js index 6b5e645..fc736c5 100644 --- a/js/src/typedef.js +++ b/js/src/typedef.js @@ -143,6 +143,7 @@ * @property {ClipboardHeader} CLIPBOARD_HEADER - The header containing all the clipboard related config spans * @property {HTMLElement} PLOT_PANE - The container of the actual plot object, only use to control the size of the plot * @property {Function} toImageOptions + * @property {DOMRect} position - The object containing the latest recorded position of the container via getBoundingClientRect() * @property {*} [value] - The eventual value of the CONTAINER to be used for `@bind` inside Pluto */ From 5531d1a239573222e901bb7a19d7c0e9cd9e9c74 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 21 Jul 2024 12:29:09 +0200 Subject: [PATCH 20/39] add responsive to resizing and popout --- js/index.html | 11 ++++---- js/src/clipboard.js | 35 ++++++++++++++++++------ js/src/container.js | 65 ++++++++++++++++++++++++++------------------- js/src/styles.js | 53 +++++++++++++++++++++++++++++------- js/src/typedef.js | 5 ++-- 5 files changed, 117 insertions(+), 52 deletions(-) diff --git a/js/index.html b/js/index.html index df32e91..7d5cf76 100644 --- a/js/index.html +++ b/js/index.html @@ -14,11 +14,6 @@ console.log(css) const dv = document.getElementById('asd'); const container = dv.insertAdjacentElement('afterend', makeContainer()); - window.container = container - window.option_spans = container.CLIPBOARD_HEADER.option_spans - // container.classList.toggle('popped-out', true) - container.CLIPBOARD_HEADER.classList.toggle('hidden') - window.width = option_spans['width'] // Do the plot // Sample data @@ -45,6 +40,12 @@ // Render the plot updatePlotData(container, plot_obj) + window.container = container + window.option_spans = container.CLIPBOARD_HEADER.option_spans + // container.classList.toggle('popped-out', true) + container.CLIPBOARD_HEADER.classList.toggle('hidden') + window.width = option_spans['width'] + // container.togglePopout() diff --git a/js/src/clipboard.js b/js/src/clipboard.js index 9f27fe9..c8c32d0 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -9,6 +9,25 @@ const valid_download_formats = ["png", "svg", "webp", "jpeg", "full-json"]; /** @type {(keyof import("./typedef.js").toImageOptions)[]} */ export const toImageOptionKeys = ["format", "width", "height", "scale", "filename"]; +/** + * Update the provided container to eventually add a clipboard header and modify the plot_obj. + * + * @param {import("./typedef.js").Container} CONTAINER - The container to which the clipboard header will be added. + * @param {Partial} [deps] - Global dependencies containing at least html + * @return {undefined} span function does not return a value. + */ +export function addClipboardFunctionality(CONTAINER, deps = CONTAINER.js_deps) { + // Try adding the clipboard header if not present + addClipboardHeader(CONTAINER); + CONTAINER.togglePopout = function() { + if (this.classList.contains('popped-out')) { + unpopContainer(this); + } else { + popContainer(this); + } + } +} + /** * Function to add a configuration span element. * @@ -111,7 +130,7 @@ function addOptionSpans(CLIPBOARD_HEADER, deps = {}) { * @param {Partial} [deps] - Global dependencies containing at least html * @return {undefined} span function does not return a value. */ -export function addClipboardHeader(CONTAINER, deps = {}) { +export function addClipboardHeader(CONTAINER, deps = CONTAINER.js_deps) { const { html } = mergeDeps(deps) // Return if the CLIPBOARD HEADER has been assigned already if (CONTAINER.CLIPBOARD_HEADER !== undefined) return; @@ -125,7 +144,7 @@ export function addClipboardHeader(CONTAINER, deps = {}) { // Add the objects collecting ui and config options values addImageOptionsObj(CLIPBOARD_HEADER, "ui"); addImageOptionsObj(CLIPBOARD_HEADER, "config"); - CONTAINER.appendChild(CLIPBOARD_HEADER); + CONTAINER.insertAdjacentElement('afterbegin', CLIPBOARD_HEADER); CONTAINER.CLIPBOARD_HEADER = CLIPBOARD_HEADER; return; } @@ -503,21 +522,21 @@ function sendToClipboard(blob) { * Function to pop out the container from the current position to a fixed one. * * @param {import("./typedef.js").Container} CONTAINER - Main container of the plutoplotly plot - * @param {Partial} [deps] - Global dependencies containing at least html and lodash */ -export function popContainer(CONTAINER, deps = {}) { - // We update the container position with the current one, also updating the css variables - updateContainerPosition(CONTAINER) +export function popContainer(CONTAINER) { + // We save the container position before popping it out (which adds border) + const position = CONTAINER.position = CONTAINER.position ?? CONTAINER.getBoundingClientRect(); CONTAINER.classList.toggle("popped-out", true); + // We update the left/bottom position to make it fixed in the same position it had before popping + updateContainerPosition(CONTAINER, position) } /** * Function to unpop the container from the fixed position. * * @param {import("./typedef.js").Container} CONTAINER - Main container of the plutoplotly plot - * @param {Partial} [deps] - Global dependencies containing at least html and lodash */ -export function unpopContainer(CONTAINER, deps = {}) { +export function unpopContainer(CONTAINER) { CONTAINER.classList.toggle("popped-out", false); } diff --git a/js/src/container.js b/js/src/container.js index f56b03b..e8f3bf8 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -1,6 +1,6 @@ //@ts-check import { mergeDeps } from "./global_deps.js"; -import { addClipboardHeader, toImageOptionKeys } from "./clipboard.js"; +import { addClipboardFunctionality, toImageOptionKeys } from "./clipboard.js"; import { addContainerStyle, addPlotPaneStyle } from "./styles.js"; /** * Creates the Container element used by PlutoPlotly to wrap the plotly.js plot and adds additional functionality like resizing and enhanced clipboard. @@ -10,19 +10,26 @@ import { addContainerStyle, addPlotPaneStyle } from "./styles.js"; * @return {import("./typedef.js").Container} The container element for the Plotly plot. */ export function makeContainer(Plotly = globalThis.Plotly, deps = {}) { - const { html } = mergeDeps(deps) + const js_deps = mergeDeps(deps) + const { html } = js_deps const /** @type {import("./typedef.js").Container} */ CONTAINER = html`
`; CONTAINER.Plotly = Plotly + CONTAINER.js_deps = js_deps // Add the style to it - addContainerStyle(CONTAINER, deps); - // Add the clipboard header - addClipboardHeader(CONTAINER, deps); + addContainerStyle(CONTAINER); // Add the Plot Pane - addPlotPane(CONTAINER, deps); + addPlotPane(CONTAINER); // Create the child div that will contain the actual plot const PLOT = CONTAINER.PLOT = CONTAINER.PLOT_PANE.appendChild( html`
` ); + const resizeObserver = CONTAINER.resizeObserver = new ResizeObserver((entries) => { + if (!PLOT.hasChildNodes()) return // We skip if no plot has been added yet + const lastEntry = entries[entries.length - 1]; + const { width, height } = lastEntry.contentRect; + Plotly.relayout(PLOT, {width, height}) + }) + resizeObserver.observe(CONTAINER.PLOT_PANE) // We set the flag of remove CONTAINER.remove_container_size = true // We use a controller to remove event listeners upon invalidation @@ -53,39 +60,41 @@ export function makeContainer(Plotly = globalThis.Plotly, deps = {}) { * Add the plot data to the already created CONTAINER and update the plot * * @param {import("./typedef.js").Container} CONTAINER - The plutoplotly Container. - * @param {import("./typedef.js").PlotObj} plot_data - An optional object containing the JS dependencies to use, which are 'html', 'emotion', 'lodash' and 'interact'. + * @param {import("./typedef.js").PlotObj} plot_obj - An optional object containing the JS dependencies to use, which are 'html', 'emotion', 'lodash' and 'interact'. * @return {undefined} This function does not return anything. */ -export function updatePlotData(CONTAINER, plot_data) { +export function updatePlotData(CONTAINER, plot_obj) { // Extract the plotly library - const { Plotly, PLOT } = CONTAINER - CONTAINER.plot_data = plot_data - Plotly.react(PLOT, plot_data) -} - -/** - * Compute the `position` field of the provided `CONTAINER` using getBoundingClientRect. - * - * @param {import("./typedef.js").Container} CONTAINER - The CONTAINER object to update. - * @return {DOMRect} the function does not return a value. - */ -function computeContainerPosition(CONTAINER) { - CONTAINER.position = CONTAINER.getBoundingClientRect(); - return CONTAINER.position + const { Plotly, PLOT, js_deps } = CONTAINER + const { lodash } = js_deps + // We make the plot responsive if the plot_data does not contain a specific value for it + if (!lodash.has(plot_obj, "config.responsive")) { + lodash.set(plot_obj, "config.responsive", true); + } + // Add the clipboard header + addClipboardFunctionality(CONTAINER); + CONTAINER.plot_obj = plot_obj + Plotly.react(PLOT, plot_obj) } /** * Updates the position of the CONTAINER based on the provided left and top values. * * @param {import("./typedef.js").Container} CONTAINER - The container element to update. - * @param {DOMRect} [position] } + * @param {DOMRect} position } * @return {undefined} the function does not return a value. */ export function updateContainerPosition(CONTAINER, position) { - CONTAINER.position = position ?? computeContainerPosition(CONTAINER) - const { left, top } = CONTAINER.position - CONTAINER.style.setProperty('--element-top', top + 'px') - CONTAINER.style.setProperty('--element-left', left + 'px') + CONTAINER.position = position + const { left, bottom } = CONTAINER.position + const viewport_bottom = globalThis.innerHeight - bottom + + const computedStyle = getComputedStyle(CONTAINER); + + const borderBottomWidth = parseFloat(computedStyle.borderBottomWidth); + const borderLeftWidth = parseFloat(computedStyle.borderLeftWidth); + CONTAINER.style.setProperty('--element-bottom', viewport_bottom - borderBottomWidth + 'px') + CONTAINER.style.setProperty('--element-left', left - borderLeftWidth + 'px') } /** @@ -95,7 +104,7 @@ export function updateContainerPosition(CONTAINER, position) { * @param {Partial} [deps] - Global dependencies containing at least html * @return {undefined} span function does not return a value. */ -function addPlotPane(CONTAINER, deps = {}) { +function addPlotPane(CONTAINER, deps = CONTAINER.js_deps) { const { html } = mergeDeps(deps) // Return if the PLOT_PANE has been assigned already if (CONTAINER.PLOT_PANE !== undefined) return; diff --git a/js/src/styles.js b/js/src/styles.js index a73f257..def102a 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -6,20 +6,22 @@ import { mergeDeps } from "./global_deps.js"; * @param {import("./typedef.js").Container} CONTAINER - The container element to which the style will be added * @param {Partial} [deps] - The object containing the emotion library as property */ -export function addContainerStyle(CONTAINER, deps = {}) { - const { emotion: {css} } = mergeDeps(deps) +export function addContainerStyle(CONTAINER, deps = CONTAINER.js_deps) { + const { + emotion: { css }, + } = mergeDeps(deps); const cl = css` &.popped-out { overflow: hidden; z-index: 1000; position: fixed; - resize: both; background: var(--main-bg-color, var(--bg-color)); border: 3px solid var(--kbd-border-color, var(--border-color)); border-radius: 12px; border-top-left-radius: 12px; border-top-right-radius: 12px; - box-sizing: border-box; + bottom: var(--element-bottom, 'auto'); + left: var(--element-left, 'auto'); } // We add defaults color variables for outside Pluto @media (prefers-color-scheme: light) { @@ -47,7 +49,9 @@ export function addContainerStyle(CONTAINER, deps = {}) { * @param {Partial} [deps] - The object containing the emotion library as property */ export function addClipboardHeaderStyle(element, deps = {}) { - const { emotion: {css} } = mergeDeps(deps) + const { + emotion: { css }, + } = mergeDeps(deps); const cl = css` & { display: none; @@ -55,7 +59,7 @@ export function addClipboardHeaderStyle(element, deps = {}) { background: var(--main-bg-color, var(--bg-color)); cursor: move; padding: 5px; - width: 100%; + align-items: center; } .popped-out & { border-bottom: 3px solid var(--kbd-border-color, var(--border-color)); @@ -123,6 +127,33 @@ export function addClipboardHeaderStyle(element, deps = {}) { &.filesave-extras .clipboard-span.format { display: inline-flex; } + + & button { + background-color: #4caf50; /* Green background */ + border: none; /* Remove border */ + color: white; /* White text */ + padding: 5px 10px; /* Some padding */ + text-align: center; /* Centered text */ + text-decoration: none; /* Remove underline */ + display: inline-block; /* Make the buttons appear side by side */ + font-size: 13px; /* Increase font size */ + margin: 4px 2px; /* Some margin */ + cursor: pointer; /* Pointer/hand icon on hover */ + border-radius: 12px; /* Rounded corners */ + transition: background-color 0.3s, box-shadow 0.3s; /* Smooth transition for hover effects */ + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */ + } + + & button:hover { + background-color: #45a049; /* Darker green on hover */ + box-shadow: 0 6px 8px rgba(0, 0, 0, 0.2); /* Slightly bigger shadow on hover */ + } + + & button:active { + background-color: #3e8e41; /* Even darker green when clicked */ + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Shadow back to normal when clicked */ + transform: translateY(2px); /* Slight move down effect when clicked */ + } `; element.classList.add(cl); } @@ -132,7 +163,9 @@ export function addClipboardHeaderStyle(element, deps = {}) { * @param {Partial} [deps] - The object containing the emotion library as property */ export function addFormatConfigStyle(element, deps = {}) { - const { emotion: {css} } = mergeDeps(deps) + const { + emotion: { css }, + } = mergeDeps(deps); const format_config_style = css` & > .label { flex: 0 0 0; @@ -188,7 +221,9 @@ export function addFormatConfigStyle(element, deps = {}) { * @param {Partial} [deps] - The object containing the emotion library as property */ export function addPlotPaneStyle(PLOT_PANE, deps = {}) { - const { emotion: {css} } = mergeDeps(deps) + const { + emotion: { css }, + } = mergeDeps(deps); const cl = css` & { width: 100%; @@ -203,4 +238,4 @@ export function addPlotPaneStyle(PLOT_PANE, deps = {}) { } `; PLOT_PANE.classList.add(cl); -} \ No newline at end of file +} diff --git a/js/src/typedef.js b/js/src/typedef.js index fc736c5..07c6fe9 100644 --- a/js/src/typedef.js +++ b/js/src/typedef.js @@ -134,8 +134,9 @@ /** * @typedef {Object} ContainerProps - * @property {PlotObj} plot_data - The plot data used for calling Plotly.react - * @property {Function} isPoppedOut - Returns true if the container is popped out + * @property {PlotObj} plot_obj - The plot data used for calling Plotly.react + * @property {Function} togglePopout - Toggle the popout status of the container + * @property {JSDeps} js_deps - The JS dependency used internally by plutoplotly functions * @property {ResizeObserver} resizeObserver - The resizeObserver controlling the resizing of the CONTAINER and PLOT * @property {AbortController} controller - The AbortController used to stop all listeners and observers tied to the CONTAINER * @property {Plotly} Plotly - The Plotly library used in this Container From da82d32f2affcb3faa966c1a9023d7b763463291 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 21 Jul 2024 13:23:47 +0200 Subject: [PATCH 21/39] add basis for clipboard/save functionality --- js/src/clipboard.js | 187 +++++++++++++++++++++++++------------------- js/src/container.js | 2 +- js/src/typedef.js | 5 +- 3 files changed, 112 insertions(+), 82 deletions(-) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index c8c32d0..b83aa28 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -3,29 +3,39 @@ import { delay, getImageOptions, image_options_defaults } from "./utils.js"; import { mergeDeps } from "./global_deps.js"; import { updateContainerPosition } from "./container.js"; -// Download formats +// Download formats const valid_download_formats = ["png", "svg", "webp", "jpeg", "full-json"]; // toImageOption keys /** @type {(keyof import("./typedef.js").toImageOptions)[]} */ -export const toImageOptionKeys = ["format", "width", "height", "scale", "filename"]; +export const toImageOptionKeys = [ + "format", + "width", + "height", + "scale", + "filename", +]; /** * Update the provided container to eventually add a clipboard header and modify the plot_obj. * * @param {import("./typedef.js").Container} CONTAINER - The container to which the clipboard header will be added. - * @param {Partial} [deps] - Global dependencies containing at least html * @return {undefined} span function does not return a value. */ -export function addClipboardFunctionality(CONTAINER, deps = CONTAINER.js_deps) { +export function addClipboardFunctionality(CONTAINER) { // Try adding the clipboard header if not present addClipboardHeader(CONTAINER); - CONTAINER.togglePopout = function() { - if (this.classList.contains('popped-out')) { - unpopContainer(this); + // Customize the togglePopout function + CONTAINER.togglePopout = function (filesave = false) { + if (CONTAINER.isPoppedOut()) { + unpopContainer(CONTAINER); + CONTAINER.classList.toggle('filesave', false) } else { - popContainer(this); + popContainer(CONTAINER); + CONTAINER.classList.toggle('filesave', filesave) } - } + }; + // Modify the plot object to include the buttons + modifyModebarButtons(CONTAINER); } /** @@ -36,7 +46,7 @@ export function addClipboardFunctionality(CONTAINER, deps = CONTAINER.js_deps) { * @param {Partial} [deps] - Global dependencies containing at least html and lodash */ export function addSingleConfigSpan(CLIPBOARD_HEADER, name, deps = {}) { - const { html, lodash } = mergeDeps(deps) + const { html, lodash } = mergeDeps(deps); const config_span = html``; const label = html`${config_span}${lodash.capitalize(name)}: { e.preventDefault(); - container.config_value = container.ui_value + container.config_value = container.ui_value; }, (/** @type {Event} */ e) => { e.preventDefault(); @@ -90,23 +100,22 @@ export function addSingleConfigSpan(CLIPBOARD_HEADER, name, deps = {}) { ); // Add the listener for imageOptions change const listener = (/** @type {CustomEvent} */ e) => { - checkConfigSync(container) + checkConfigSync(container); }; // @ts-ignore: addEventListener does not support function with CustomEvent as arguments container.addEventListener("clipboard-header-change", listener); return; } - /** * Add config spans to the given CLIPBOARD_HEADER. * * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER - the clipboard header to add config spans to * @param {Partial} [deps] - Global dependencies containing at least html - * @return {void} + * @return {void} */ function addOptionSpans(CLIPBOARD_HEADER, deps = {}) { - const { html } = mergeDeps(deps) + const { html } = mergeDeps(deps); // @ts-ignore: Will be populated CLIPBOARD_HEADER.option_spans = {}; addSingleConfigSpan(CLIPBOARD_HEADER, "format"); @@ -131,7 +140,7 @@ function addOptionSpans(CLIPBOARD_HEADER, deps = {}) { * @return {undefined} span function does not return a value. */ export function addClipboardHeader(CONTAINER, deps = CONTAINER.js_deps) { - const { html } = mergeDeps(deps) + const { html } = mergeDeps(deps); // Return if the CLIPBOARD HEADER has been assigned already if (CONTAINER.CLIPBOARD_HEADER !== undefined) return; /** @type {import("./typedef.js").ClipboardHeader} */ @@ -140,11 +149,11 @@ export function addClipboardHeader(CONTAINER, deps = CONTAINER.js_deps) { >`; addClipboardHeaderStyle(CLIPBOARD_HEADER); // Add the various spans for the UI - addOptionSpans(CLIPBOARD_HEADER) + addOptionSpans(CLIPBOARD_HEADER); // Add the objects collecting ui and config options values addImageOptionsObj(CLIPBOARD_HEADER, "ui"); addImageOptionsObj(CLIPBOARD_HEADER, "config"); - CONTAINER.insertAdjacentElement('afterbegin', CLIPBOARD_HEADER); + CONTAINER.insertAdjacentElement("afterbegin", CLIPBOARD_HEADER); CONTAINER.CLIPBOARD_HEADER = CLIPBOARD_HEADER; return; } @@ -340,27 +349,26 @@ function initializeConfigValueSpan(span, key, deps = {}) { } /** - * + * * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER * @param {string} type * @param {import("./typedef.js").Container} type */ function addImageOptionsObj(CLIPBOARD_HEADER, type) { - /** @type {import('./typedef.js').toImageOptions} */ // @ts-ignore: We populate the keys below - const out = {} - const header_key = type === "ui" ? "ui_values" : "config_values" - const value_key = type === "ui" ? "ui_span" : "config_span" + const out = {}; + const header_key = type === "ui" ? "ui_values" : "config_values"; + const value_key = type === "ui" ? "ui_span" : "config_span"; for (const key of toImageOptionKeys) { - const span = CLIPBOARD_HEADER.option_spans[key][value_key] + const span = CLIPBOARD_HEADER.option_spans[key][value_key]; Object.defineProperty(out, key, { get: () => span.value, - set: (val) => span.value = val, + set: (val) => (span.value = val), enumerable: true, - }) + }); } // @ts-ignore: We did put the keys above - CLIPBOARD_HEADER[header_key] = out + CLIPBOARD_HEADER[header_key] = out; } // // span code updates the image options in the PLOT config with the provided ones @@ -442,7 +450,7 @@ function addImageOptionsObj(CLIPBOARD_HEADER, type) { * Function to send the provided blob to the clipboard using the Clipboard API. * * @param {Blob} blob - the blob to be copied to the clipboard - * @return {void} + * @return {void} */ function sendToClipboard(blob) { if (!navigator.clipboard) { @@ -467,56 +475,39 @@ function sendToClipboard(blob) { ); } +/** + * Function to copy the image from the parent container to the clipboard. + * @param {import('./typedef.js').Container} CONTAINER + */ +export function copyImageToClipboard(CONTAINER) { + // We extract the image options from the provided parameters (if they exist) + const { Plotly, PLOT, CLIPBOARD_HEADER } = CONTAINER + const config = CLIPBOARD_HEADER.ui_values; + Plotly.toImage(PLOT, config).then(function (dataUrl) { + fetch(dataUrl) + .then((res) => res.blob()) + .then((blob) => { + // const paste_receiver = document.querySelector( + // "paste-receiver.plutoplotly" + // ); + // if (paste_receiver) { + // paste_receiver.attachImage(dataUrl, CONTAINER); + // } + sendToClipboard(blob); + }); + }); +} -// /** -// * Function to copy the image from the parent container to the clipboard. -// * @span {import('./typedef.js').Container} -// */ -// export function copyImageToClipboard() { -// // We extract the image options from the provided parameters (if they exist) -// const { Plotly, PLOT } = span -// const config = {}; -// for (const [key, container] of Object.entries(option_spans)) { -// let val = -// container.config_value ?? -// (span.isPoppedOut() ? container.ui_value : undefined); -// // If we have undefined we don't create the key. We also ignore format because the clipboard only supports png. -// if (val === undefined || key === "format") { -// continue; -// } -// config[key] = val; -// } -// Plotly.toImage(PLOT, config).then(function (dataUrl) { -// fetch(dataUrl) -// .then((res) => res.blob()) -// .then((blob) => { -// const paste_receiver = document.querySelector( -// "paste-receiver.plutoplotly" -// ); -// if (paste_receiver) { -// paste_receiver.attachImage(dataUrl, CONTAINER); -// } -// sendToClipboard(blob); -// }); -// }); -// } - -// function saveImageToFile() { -// const config = {}; -// for (const [key, container] of Object.entries(option_spans)) { -// let val = -// container.config_value ?? -// (CONTAINER.isPoppedOut() ? container.ui_value : undefined); -// // If we have undefined we don't create the key. -// if (val === undefined) { -// continue; -// } -// config[key] = val; -// } -// Plotly.downloadImage(PLOT, config); -// } - - +/** + * Function to save the image from the provided container to the disk. + * @param {import('./typedef.js').Container} CONTAINER + */ +function saveImageToFile(CONTAINER) { + // We extract the image options from the provided parameters (if they exist) + const { Plotly, PLOT, CLIPBOARD_HEADER } = CONTAINER + const config = CLIPBOARD_HEADER.ui_values; + Plotly.downloadImage(PLOT, config); +} /** * Function to pop out the container from the current position to a fixed one. @@ -525,10 +516,11 @@ function sendToClipboard(blob) { */ export function popContainer(CONTAINER) { // We save the container position before popping it out (which adds border) - const position = CONTAINER.position = CONTAINER.position ?? CONTAINER.getBoundingClientRect(); + const position = (CONTAINER.position = + CONTAINER.position ?? CONTAINER.getBoundingClientRect()); CONTAINER.classList.toggle("popped-out", true); // We update the left/bottom position to make it fixed in the same position it had before popping - updateContainerPosition(CONTAINER, position) + updateContainerPosition(CONTAINER, position); } /** @@ -683,3 +675,40 @@ function DualClick(single_func, dbl_func) { // }, // ] // ); + +function modifyModebarButtons(CONTAINER) { + const { plot_obj, js_deps, togglePopout, Plotly } = CONTAINER; + const { lodash } = js_deps; + plot_obj.config = plot_obj.config ?? {}; + // We remove the default download image button + plot_obj.config.modeBarButtonsToRemove = lodash.union( + plot_obj.config?.modeBarButtonsToRemove, + ["toImage"] + ); + // We add the custom button to the modebar + plot_obj.config.modeBarButtonsToAdd = lodash.union( + plot_obj.config.modeBarButtonsToAdd, + [ + { + name: "Copy PNG to Clipboard", + icon: { + height: 520, + width: 520, + path: "M280 64h40c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128C0 92.7 28.7 64 64 64h40 9.6C121 27.5 153.3 0 192 0s71 27.5 78.4 64H280zM64 112c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320c8.8 0 16-7.2 16-16V128c0-8.8-7.2-16-16-16H304v24c0 13.3-10.7 24-24 24H192 104c-13.3 0-24-10.7-24-24V112H64zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z", + }, + direction: "up", + click: DualClick(() => copyImageToClipboard(CONTAINER), () => { + togglePopout(); + }), + }, + { + name: "Download Image", + icon: Plotly.Icons.camera, + direction: "up", + click: DualClick(() => saveImageToFile(CONTAINER), () => { + togglePopout(true); + }), + }, + ] + ); +} diff --git a/js/src/container.js b/js/src/container.js index e8f3bf8..58c23dc 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -71,9 +71,9 @@ export function updatePlotData(CONTAINER, plot_obj) { if (!lodash.has(plot_obj, "config.responsive")) { lodash.set(plot_obj, "config.responsive", true); } + CONTAINER.plot_obj = plot_obj // Add the clipboard header addClipboardFunctionality(CONTAINER); - CONTAINER.plot_obj = plot_obj Plotly.react(PLOT, plot_obj) } diff --git a/js/src/typedef.js b/js/src/typedef.js index 07c6fe9..f9c1da4 100644 --- a/js/src/typedef.js +++ b/js/src/typedef.js @@ -113,8 +113,8 @@ /** * @typedef {Object} ClipboardHeaderProps * @property {OptionSpansObject} option_spans - * @property {toImageOptions} config_values - * @property {toImageOptions} ui_values + * @property {Plotly.ToImgopts} config_values + * @property {Plotly.ToImgopts} ui_values * @property {Function} updateConfigSync */ @@ -136,6 +136,7 @@ * @typedef {Object} ContainerProps * @property {PlotObj} plot_obj - The plot data used for calling Plotly.react * @property {Function} togglePopout - Toggle the popout status of the container + * @property {Function} isPoppedOut - Function to check if the container is popped out or not * @property {JSDeps} js_deps - The JS dependency used internally by plutoplotly functions * @property {ResizeObserver} resizeObserver - The resizeObserver controlling the resizing of the CONTAINER and PLOT * @property {AbortController} controller - The AbortController used to stop all listeners and observers tied to the CONTAINER From 71dcaea4548ce4b40cf5b5ed9093651cbd211e8d Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 21 Jul 2024 17:26:58 +0200 Subject: [PATCH 22/39] avoid overflowing on the right and bottom --- js/src/clipboard.js | 8 +-- js/src/container.js | 122 +++++++++++++++++++++++++++----------------- js/src/styles.js | 10 +++- js/src/typedef.js | 4 +- 4 files changed, 90 insertions(+), 54 deletions(-) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index b83aa28..224da31 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -506,6 +506,7 @@ function saveImageToFile(CONTAINER) { // We extract the image options from the provided parameters (if they exist) const { Plotly, PLOT, CLIPBOARD_HEADER } = CONTAINER const config = CLIPBOARD_HEADER.ui_values; + // @ts-ignore config would like downloadImageOpts but we just use our own type Plotly.downloadImage(PLOT, config); } @@ -515,12 +516,11 @@ function saveImageToFile(CONTAINER) { * @param {import("./typedef.js").Container} CONTAINER - Main container of the plutoplotly plot */ export function popContainer(CONTAINER) { - // We save the container position before popping it out (which adds border) - const position = (CONTAINER.position = - CONTAINER.position ?? CONTAINER.getBoundingClientRect()); + // We save the plot pane position before popping it out (which adds border) + const plot_rect = CONTAINER.PLOT_PANE.getBoundingClientRect(); CONTAINER.classList.toggle("popped-out", true); // We update the left/bottom position to make it fixed in the same position it had before popping - updateContainerPosition(CONTAINER, position); + updateContainerPosition(CONTAINER, plot_rect); } /** diff --git a/js/src/container.js b/js/src/container.js index 58c23dc..c246042 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -10,28 +10,32 @@ import { addContainerStyle, addPlotPaneStyle } from "./styles.js"; * @return {import("./typedef.js").Container} The container element for the Plotly plot. */ export function makeContainer(Plotly = globalThis.Plotly, deps = {}) { - const js_deps = mergeDeps(deps) - const { html } = js_deps - const /** @type {import("./typedef.js").Container} */ CONTAINER = html`
`; - CONTAINER.Plotly = Plotly - CONTAINER.js_deps = js_deps + const js_deps = mergeDeps(deps); + const { html } = js_deps; + const /** @type {import("./typedef.js").Container} */ CONTAINER = html`
`; + CONTAINER.Plotly = Plotly; + CONTAINER.js_deps = js_deps; // Add the style to it addContainerStyle(CONTAINER); // Add the Plot Pane addPlotPane(CONTAINER); // Create the child div that will contain the actual plot - const PLOT = CONTAINER.PLOT = CONTAINER.PLOT_PANE.appendChild( + const PLOT = (CONTAINER.PLOT = CONTAINER.PLOT_PANE.appendChild( html`
` - ); - const resizeObserver = CONTAINER.resizeObserver = new ResizeObserver((entries) => { - if (!PLOT.hasChildNodes()) return // We skip if no plot has been added yet - const lastEntry = entries[entries.length - 1]; - const { width, height } = lastEntry.contentRect; - Plotly.relayout(PLOT, {width, height}) - }) - resizeObserver.observe(CONTAINER.PLOT_PANE) + )); + const resizeObserver = (CONTAINER.resizeObserver = new ResizeObserver( + (entries) => { + if (!PLOT.hasChildNodes()) return; // We skip if no plot has been added yet + const lastEntry = entries[entries.length - 1]; + const { width, height } = lastEntry.contentRect; + Plotly.relayout(PLOT, { width, height }); + } + )); + resizeObserver.observe(CONTAINER.PLOT_PANE); // We set the flag of remove - CONTAINER.remove_container_size = true + CONTAINER.remove_container_size = true; // We use a controller to remove event listeners upon invalidation CONTAINER.controller = new AbortController(); // We have to add this to keep supporting @bind with the old API using PLOT @@ -48,12 +52,12 @@ export function makeContainer(Plotly = globalThis.Plotly, deps = {}) { { signal: CONTAINER.controller.signal } ); // We add a function to check if the clipboard is popped out - CONTAINER.isPoppedOut = function() { + CONTAINER.isPoppedOut = function () { return this.classList.contains("popped-out"); }; // We add the function to extract the image options for saving/copying - CONTAINER.toImageOptions = toImageOptions - return CONTAINER + CONTAINER.toImageOptions = toImageOptions; + return CONTAINER; } /** @@ -65,36 +69,55 @@ export function makeContainer(Plotly = globalThis.Plotly, deps = {}) { */ export function updatePlotData(CONTAINER, plot_obj) { // Extract the plotly library - const { Plotly, PLOT, js_deps } = CONTAINER - const { lodash } = js_deps + const { Plotly, PLOT, js_deps } = CONTAINER; + const { lodash } = js_deps; // We make the plot responsive if the plot_data does not contain a specific value for it if (!lodash.has(plot_obj, "config.responsive")) { lodash.set(plot_obj, "config.responsive", true); } - CONTAINER.plot_obj = plot_obj + CONTAINER.plot_obj = plot_obj; // Add the clipboard header addClipboardFunctionality(CONTAINER); - Plotly.react(PLOT, plot_obj) + Plotly.react(PLOT, plot_obj); } /** * Updates the position of the CONTAINER based on the provided left and top values. * * @param {import("./typedef.js").Container} CONTAINER - The container element to update. - * @param {DOMRect} position } + * @param {DOMRect} target_plot_rect } * @return {undefined} the function does not return a value. */ -export function updateContainerPosition(CONTAINER, position) { - CONTAINER.position = position - const { left, bottom } = CONTAINER.position - const viewport_bottom = globalThis.innerHeight - bottom +export function updateContainerPosition(CONTAINER, target_plot_rect) { + const cr = CONTAINER.getBoundingClientRect(); + const tpr = target_plot_rect; + const pr = CONTAINER.PLOT_PANE.getBoundingClientRect(); const computedStyle = getComputedStyle(CONTAINER); + const border = { + top: parseFloat(computedStyle.getPropertyValue("border-top-width")), + right: parseFloat(computedStyle.getPropertyValue("border-right-width")), + bottom: parseFloat(computedStyle.getPropertyValue( + "border-bottom-width" + )), + left: parseFloat(computedStyle.getPropertyValue("border-left-width")) + } + const top_offset = tpr.top - pr.top; + const left_offset = tpr.left - pr.left; + const top = cr.top + top_offset; + const left = cr.left + left_offset; - const borderBottomWidth = parseFloat(computedStyle.borderBottomWidth); - const borderLeftWidth = parseFloat(computedStyle.borderLeftWidth); - CONTAINER.style.setProperty('--element-bottom', viewport_bottom - borderBottomWidth + 'px') - CONTAINER.style.setProperty('--element-left', left - borderLeftWidth + 'px') + CONTAINER.style.setProperty("--element-top", top + "px"); + CONTAINER.style.setProperty("--element-left", left + "px"); + // The 3 belows are to + CONTAINER.style.setProperty( + "--max-width-offset", + tpr.left + border.left + border.right + 3 + "px" + ); + CONTAINER.style.setProperty( + "--max-height-offset", + tpr.top + (pr.top - cr.top) + border.bottom + 3 + "px" + ); } /** @@ -105,12 +128,17 @@ export function updateContainerPosition(CONTAINER, position) { * @return {undefined} span function does not return a value. */ function addPlotPane(CONTAINER, deps = CONTAINER.js_deps) { - const { html } = mergeDeps(deps) + const { html } = mergeDeps(deps); // Return if the PLOT_PANE has been assigned already if (CONTAINER.PLOT_PANE !== undefined) return; - const PLOT_PANE = CONTAINER.PLOT_PANE = html`
`; + const PLOT_PANE_CONTAINER = CONTAINER.appendChild( + html`
` + ); + const PLOT_PANE = (CONTAINER.PLOT_PANE = html`
`); addPlotPaneStyle(PLOT_PANE, deps); - CONTAINER.appendChild(PLOT_PANE); + PLOT_PANE_CONTAINER.appendChild(PLOT_PANE); } /** @@ -118,17 +146,17 @@ function addPlotPane(CONTAINER, deps = CONTAINER.js_deps) { * @this {import("./typedef.js").Container} */ function toImageOptions() { - const { CLIPBOARD_HEADER } = this - /** @type {Partial} */ - const options = {} - for (const key of toImageOptionKeys) { - const config_value = CLIPBOARD_HEADER.config_values[key] - const ui_value = CLIPBOARD_HEADER.ui_values[key] - const isPopped = this.isPoppedOut() - const value = config_value ?? (isPopped ? ui_value : undefined) - if (value !== undefined) { - options[key] = value - } + const { CLIPBOARD_HEADER } = this; + /** @type {Partial} */ + const options = {}; + for (const key of toImageOptionKeys) { + const config_value = CLIPBOARD_HEADER.config_values[key]; + const ui_value = CLIPBOARD_HEADER.ui_values[key]; + const isPopped = this.isPoppedOut(); + const value = config_value ?? (isPopped ? ui_value : undefined); + if (value !== undefined) { + options[key] = value; } - return options -} \ No newline at end of file + } + return options; +} diff --git a/js/src/styles.js b/js/src/styles.js index def102a..de941a7 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -20,8 +20,16 @@ export function addContainerStyle(CONTAINER, deps = CONTAINER.js_deps) { border-radius: 12px; border-top-left-radius: 12px; border-top-right-radius: 12px; - bottom: var(--element-bottom, 'auto'); + top: var(--element-top, 'auto'); left: var(--element-left, 'auto'); + box-sizing: content-box; + max-width: calc(100vw - var(--max-width-offset)); + max-height: calc(100vh - var(--max-height-offset)); + } + &.popped-out .plot-pane-container { + overflow: auto; + max-width: calc(100vw - var(--max-width-offset)); + max-height: calc(100vh - var(--max-height-offset)); } // We add defaults color variables for outside Pluto @media (prefers-color-scheme: light) { diff --git a/js/src/typedef.js b/js/src/typedef.js index f9c1da4..f81f97a 100644 --- a/js/src/typedef.js +++ b/js/src/typedef.js @@ -113,8 +113,8 @@ /** * @typedef {Object} ClipboardHeaderProps * @property {OptionSpansObject} option_spans - * @property {Plotly.ToImgopts} config_values - * @property {Plotly.ToImgopts} ui_values + * @property {toImageOptions} config_values + * @property {toImageOptions} ui_values * @property {Function} updateConfigSync */ From c7c05d6bf3696dae809d0e8d1f22c9c291ee850d Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 21 Jul 2024 17:44:16 +0200 Subject: [PATCH 23/39] fix border-bottom --- js/src/container.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/container.js b/js/src/container.js index c246042..642a29c 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -116,7 +116,7 @@ export function updateContainerPosition(CONTAINER, target_plot_rect) { ); CONTAINER.style.setProperty( "--max-height-offset", - tpr.top + (pr.top - cr.top) + border.bottom + 3 + "px" + tpr.top + border.top + border.bottom + 3 + "px" ); } From 5a6b290276f8bd71149270e372bd80feccd344e3 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 21 Jul 2024 18:13:23 +0200 Subject: [PATCH 24/39] basis for resizing via js variables --- js/src/container.js | 10 ++++++++++ js/src/styles.js | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/js/src/container.js b/js/src/container.js index 642a29c..6310191 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -106,6 +106,8 @@ export function updateContainerPosition(CONTAINER, target_plot_rect) { const left_offset = tpr.left - pr.left; const top = cr.top + top_offset; const left = cr.left + left_offset; + const { PLOT_PANE } = CONTAINER; + const PLOT_PANE_CONTAINER = PLOT_PANE.parentElement CONTAINER.style.setProperty("--element-top", top + "px"); CONTAINER.style.setProperty("--element-left", left + "px"); @@ -118,6 +120,14 @@ export function updateContainerPosition(CONTAINER, target_plot_rect) { "--max-height-offset", tpr.top + border.top + border.bottom + 3 + "px" ); + // @ts-ignore we are sure the container is not null + PLOT_PANE_CONTAINER.style.setProperty( + "--max-height-offset", + tpr.top - (cr.height - pr.height) + border.bottom + 3 + "px" + ); + + PLOT_PANE.style.setProperty("--plot-width", tpr.width + "px"); + PLOT_PANE.style.setProperty("--plot-height", tpr.height + "px"); } /** diff --git a/js/src/styles.js b/js/src/styles.js index de941a7..260a1bb 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -241,6 +241,10 @@ export function addPlotPaneStyle(PLOT_PANE, deps = {}) { margin: 0; padding: 0; } + .popped-out & { + width: var(--plot-width); + height: var(--plot-height); + } & .js-plotly-plot .plotly div { margin: 0 auto; // This centers the plot } From a6e80394a28c8b2d58c1040b43736a21ae6e33cf Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sun, 21 Jul 2024 18:36:03 +0200 Subject: [PATCH 25/39] setting ui values from js change plot pane --- js/src/clipboard.js | 17 +++++++++++++++-- js/src/container.js | 6 ++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index 224da31..2d5cafc 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -155,6 +155,18 @@ export function addClipboardHeader(CONTAINER, deps = CONTAINER.js_deps) { addImageOptionsObj(CLIPBOARD_HEADER, "config"); CONTAINER.insertAdjacentElement("afterbegin", CLIPBOARD_HEADER); CONTAINER.CLIPBOARD_HEADER = CLIPBOARD_HEADER; + // Insert the listener for the change in width/height + function size_listener(/** @type {CustomEvent} */ e) { + console.log("Inside size_listener", e); + const { key, type, value } = e.detail; + if (type !== "ui") return; + if (!(key === "width" || key === "height")) return; + const varname = `--plot-${key}`; + CONTAINER.PLOT_PANE.style.setProperty(varname, value + "px"); + } + CLIPBOARD_HEADER.addEventListener("clipboard-header-change", size_listener, { + signal: CONTAINER.controller.signal + }); return; } @@ -163,6 +175,7 @@ export function addClipboardHeader(CONTAINER, deps = CONTAINER.js_deps) { * @param {import("./typedef.js").ImageOptionSpan} span */ function checkConfigSync(span) { + console.log("inside checkConfigSync", span); // We use the custom getters we'll set up in the container const { ui_value, config_value, config_span, key } = span; if (config_value === undefined) { @@ -282,7 +295,7 @@ function initializeUIValueSpan(span, key, deps = {}) { this.dispatchEvent( new CustomEvent("clipboard-header-change", { bubbles: true, - detail: { key: key, type: "ui" }, + detail: { key: key, type: "ui", value: parsedVal }, }) ); } @@ -341,7 +354,7 @@ function initializeConfigValueSpan(span, key, deps = {}) { this.dispatchEvent( new CustomEvent("clipboard-header-change", { bubbles: true, - detail: { key: key, type: "config" }, + detail: { key: key, type: "config", value: val }, }) ); }, diff --git a/js/src/container.js b/js/src/container.js index 6310191..0a9f6d2 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -93,6 +93,8 @@ export function updateContainerPosition(CONTAINER, target_plot_rect) { const tpr = target_plot_rect; const pr = CONTAINER.PLOT_PANE.getBoundingClientRect(); + const header_height = cr.height - pr.height + const computedStyle = getComputedStyle(CONTAINER); const border = { top: parseFloat(computedStyle.getPropertyValue("border-top-width")), @@ -118,12 +120,12 @@ export function updateContainerPosition(CONTAINER, target_plot_rect) { ); CONTAINER.style.setProperty( "--max-height-offset", - tpr.top + border.top + border.bottom + 3 + "px" + tpr.top - header_height + border.bottom + 3 + "px" ); // @ts-ignore we are sure the container is not null PLOT_PANE_CONTAINER.style.setProperty( "--max-height-offset", - tpr.top - (cr.height - pr.height) + border.bottom + 3 + "px" + tpr.top + border.bottom + 3 + "px" ); PLOT_PANE.style.setProperty("--plot-width", tpr.width + "px"); From 105fd9497e76ebf718caf54b54c61d8102ceb4ee Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sat, 27 Jul 2024 11:34:13 +0200 Subject: [PATCH 26/39] add container resizing when changing values from UI --- js/src/clipboard.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index 2d5cafc..436c3a8 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -312,6 +312,9 @@ function initializeUIValueSpan(span, key, deps = {}) { span.blur(); } }; + span.onblur = function() { + this.value = this.textContent; + } } /** From 9f34a2a18d34185b68d7cc3842a6a342a58025d9 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sat, 27 Jul 2024 11:41:46 +0200 Subject: [PATCH 27/39] fix not appearing filesave stuff Still to make style look nicer, width breaks sometime --- js/src/clipboard.js | 4 ++-- js/src/styles.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index 436c3a8..bbbd322 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -28,10 +28,10 @@ export function addClipboardFunctionality(CONTAINER) { CONTAINER.togglePopout = function (filesave = false) { if (CONTAINER.isPoppedOut()) { unpopContainer(CONTAINER); - CONTAINER.classList.toggle('filesave', false) + CONTAINER.classList.toggle('filesave-extras', false) } else { popContainer(CONTAINER); - CONTAINER.classList.toggle('filesave', filesave) + CONTAINER.classList.toggle('filesave-extras', filesave) } }; // Modify the plot object to include the buttons diff --git a/js/src/styles.js b/js/src/styles.js index 260a1bb..9273ec5 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -124,7 +124,7 @@ export function addClipboardHeaderStyle(element, deps = {}) { margin-top: 5px; display: none; } - &.filesave-extras .clipboard-span.filename { + .filesave-extras & .clipboard-span.filename { display: inline-block; } & .clipboard-value.filename { @@ -132,7 +132,7 @@ export function addClipboardHeaderStyle(element, deps = {}) { text-align: left; min-width: min(60%, min-content); } - &.filesave-extras .clipboard-span.format { + .filesave-extras & .clipboard-span.format { display: inline-flex; } From e01f63f1a02e261a9dbc2291284fc757508ee134 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Sat, 27 Jul 2024 13:56:06 +0200 Subject: [PATCH 28/39] fix setting the format for filesave --- js/src/clipboard.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index bbbd322..f5e4ae5 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -238,7 +238,7 @@ function updateFunction(key) { * @this {HTMLElement & {value: string}} */ return function () { - this.setAttribute("selected", this.value); + this.querySelector(".format-options")?.setAttribute("selected", this.value); }; } else { /** @@ -273,6 +273,7 @@ function initializeUIValueSpan(span, key, deps = {}) { ); opt.onclick = () => { span.value = opt.textContent; + span.updateFromValue(span.value); }; } } @@ -312,8 +313,10 @@ function initializeUIValueSpan(span, key, deps = {}) { span.blur(); } }; - span.onblur = function() { - this.value = this.textContent; + if (key !== "format") { + span.onblur = function() { + this.value = this.textContent; + } } } From 1463c9d039aba9397ce4c5b5b62c6cf9f8378b1a Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Mon, 29 Jul 2024 13:05:53 +0200 Subject: [PATCH 29/39] add drag functionality --- js/src/clipboard.js | 5 +- js/src/container.js | 51 -------- js/src/resizer.js | 289 ++++++++++++++++---------------------------- js/src/styles.js | 1 + 4 files changed, 107 insertions(+), 239 deletions(-) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index f5e4ae5..b84031a 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -1,7 +1,8 @@ import { addFormatConfigStyle, addClipboardHeaderStyle } from "./styles.js"; import { delay, getImageOptions, image_options_defaults } from "./utils.js"; import { mergeDeps } from "./global_deps.js"; -import { updateContainerPosition } from "./container.js"; +import { addDragFunctionality } from "./resizer.js"; +import { updateContainerPosition } from "./resizer.js"; // Download formats const valid_download_formats = ["png", "svg", "webp", "jpeg", "full-json"]; @@ -24,6 +25,8 @@ export const toImageOptionKeys = [ export function addClipboardFunctionality(CONTAINER) { // Try adding the clipboard header if not present addClipboardHeader(CONTAINER); + // Add the drag functionality + addDragFunctionality(CONTAINER); // Customize the togglePopout function CONTAINER.togglePopout = function (filesave = false) { if (CONTAINER.isPoppedOut()) { diff --git a/js/src/container.js b/js/src/container.js index 0a9f6d2..06d6114 100644 --- a/js/src/container.js +++ b/js/src/container.js @@ -81,57 +81,6 @@ export function updatePlotData(CONTAINER, plot_obj) { Plotly.react(PLOT, plot_obj); } -/** - * Updates the position of the CONTAINER based on the provided left and top values. - * - * @param {import("./typedef.js").Container} CONTAINER - The container element to update. - * @param {DOMRect} target_plot_rect } - * @return {undefined} the function does not return a value. - */ -export function updateContainerPosition(CONTAINER, target_plot_rect) { - const cr = CONTAINER.getBoundingClientRect(); - const tpr = target_plot_rect; - const pr = CONTAINER.PLOT_PANE.getBoundingClientRect(); - - const header_height = cr.height - pr.height - - const computedStyle = getComputedStyle(CONTAINER); - const border = { - top: parseFloat(computedStyle.getPropertyValue("border-top-width")), - right: parseFloat(computedStyle.getPropertyValue("border-right-width")), - bottom: parseFloat(computedStyle.getPropertyValue( - "border-bottom-width" - )), - left: parseFloat(computedStyle.getPropertyValue("border-left-width")) - } - const top_offset = tpr.top - pr.top; - const left_offset = tpr.left - pr.left; - const top = cr.top + top_offset; - const left = cr.left + left_offset; - const { PLOT_PANE } = CONTAINER; - const PLOT_PANE_CONTAINER = PLOT_PANE.parentElement - - CONTAINER.style.setProperty("--element-top", top + "px"); - CONTAINER.style.setProperty("--element-left", left + "px"); - // The 3 belows are to - CONTAINER.style.setProperty( - "--max-width-offset", - tpr.left + border.left + border.right + 3 + "px" - ); - CONTAINER.style.setProperty( - "--max-height-offset", - tpr.top - header_height + border.bottom + 3 + "px" - ); - // @ts-ignore we are sure the container is not null - PLOT_PANE_CONTAINER.style.setProperty( - "--max-height-offset", - tpr.top + border.bottom + 3 + "px" - ); - - PLOT_PANE.style.setProperty("--plot-width", tpr.width + "px"); - PLOT_PANE.style.setProperty("--plot-height", tpr.height + "px"); -} - /** * Adds a clipboard header to the given container. * diff --git a/js/src/resizer.js b/js/src/resizer.js index 7af1c93..9668aa9 100644 --- a/js/src/resizer.js +++ b/js/src/resizer.js @@ -1,207 +1,122 @@ //@ts-check + /** - * Retrieves the padding and border information for an HTML element from the compute element style, to be used for resizing the plot container. - * - * @param {HTMLElement} el - The element to retrieve offset data for. - * @return {import("./typedef.js").PaddingBorderData} The object containing padding and border information. - */ -function getOffsetData(el) { - const cs = globalThis.getComputedStyle(el, null); - const odata = { - padding: { - left: parseFloat(cs.paddingLeft), - right: parseFloat(cs.paddingRight), - top: parseFloat(cs.paddingTop), - bottom: parseFloat(cs.paddingBottom), - width: parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight), - height: parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom), - }, - border: { - left: parseFloat(cs.borderLeftWidth), - right: parseFloat(cs.borderRightWidth), - top: parseFloat(cs.borderTopWidth), - bottom: parseFloat(cs.borderBottomWidth), - width: parseFloat(cs.borderLeftWidth) + parseFloat(cs.borderRightWidth), - height: parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth), - }, - }; - return odata; -} -/** - * Retrieves relevant data for the size of the CONTAINER and of the its associated PLOT object + * Function to add Drag functionality to the CLIPBOARD_HEADER * - * @param {import("./typedef.js").Container} CONTAINER - The container object. - * @return {import("./typedef.js").SizeData} The size data for the container and plot. + * @param {import("./typedef.js").Container} CONTAINER - Container element + * @param {Partial} [deps] - Global dependencies containing at least interact */ -export function getSizeData(CONTAINER) { - const { PLOT } = CONTAINER; - const container_pad = getOffsetData(CONTAINER); - const plot_pad = { - ...getOffsetData(PLOT), - offset: { - top: PLOT.offsetParent == CONTAINER ? PLOT.offsetTop : 0, - left: PLOT.offsetParent == CONTAINER ? PLOT.offsetLeft : 0, +export function addDragFunctionality(CONTAINER, deps = CONTAINER.js_deps) { + const { interact } = deps; + const { CLIPBOARD_HEADER, PLOT_PANE } = CONTAINER; + const limits = { top: {min: 0, max: 0}, left: {min: 0, max: 0}}; + let current = { top: 0, left: 0, width: 0, height: 0 }; + let last = { top: 0, left: 0, width: 0, height: 0 }; + // @ts-ignore We assume interact is present + interact(CLIPBOARD_HEADER).draggable({ + listeners: { + start(event) { + console.log(event.type, event.target); + const { pr, border, header_height } = computeContainerPosition(CONTAINER); + const { top, left, width, height } = pr; + // The minus 3 is for allowing some distance to the edge + limits.left = { + min: border.left + 3, + max: globalThis.innerWidth - (width + border.right + 3), + } + limits.top = { + min: border.top + header_height + 3, + max: globalThis.innerHeight - (height + border.bottom + 3), + } + current = { top, left, width, height }; + last = { top, left, width, height }; + CLIPBOARD_HEADER.blur() + CLIPBOARD_HEADER.classList.toggle('dragging', true) + }, + move(event) { + const left = (current.left += event.dx); + const top = (current.top += event.dy); + const new_vals = { + top: Math.min(Math.max(top, limits.top.min), limits.top.max), + left: Math.min(Math.max(left, limits.left.min), limits.left.max), + }; + if (new_vals.top !== last.top || new_vals.left !== last.left) { + last = {...new_vals, width: last.width, height: last.height}; + updateContainerPosition(CONTAINER, last) + } + }, + end(event) { + console.log(event.type, event.target); + CLIPBOARD_HEADER.classList.toggle('dragging', false) + } }, - }; - const data = { - plot_pad, - plot_rect: PLOT.getBoundingClientRect(), - container_pad, - container_rect: CONTAINER.getBoundingClientRect(), - }; - return data; + }); } -/** - * Computes the required size of the CONTAINER based on the target size of the PLOT. - * - * @param {import("./typedef.js").Container} CONTAINER - The container element. - * @param {Object} targetSize - The target size object. - * @param {number} [targetSize.width] - The target width of the container. - * @param {number} [targetSize.height] - The target height of the container. - * @param {import("./typedef.js").SizeData} currentSizeData - The current size data object. - * @return {{ - * width: number, - * height: number, - * noChange: boolean - * }} The computed size object. - */ -function computeContainerSize(CONTAINER, targetSize = {}, currentSizeData = getSizeData(CONTAINER)) { - const computed_size = computePlotSize(currentSizeData); - const offsets = computed_size.offsets; - - const width = (targetSize.width ?? computed_size.width) + offsets.width - const height = (targetSize.height ?? computed_size.height) + offsets.height - const noChange = targetSize.width === undefined && targetSize.height === undefined - return { - width, - height, - noChange, - }; -} /** - * Changes the size of the CONTAINER based on the provided target size. + * Computes the position of the CONTAINER element. * * @param {import("./typedef.js").Container} CONTAINER - The container element. - * @param {Object} targetSize - The target size object. - * @param {number} [targetSize.width] - The target width of the container. - * @param {number} [targetSize.height] - The target height of the container. - * @param {import("./typedef.js").SizeData} currentSizeData - The current size data object. + * @returns {{cr: DOMRect, pr: DOMRect, border: {top: number, right: number, bottom: number, left: number}, header_height: number}} */ -function changeContainerSize( - CONTAINER, - targetSize, - currentSizeData -) { - if (!CONTAINER.isPoppedOut()) { - console.log("Tried to change container size when not popped, ignoring"); - return; - } +function computeContainerPosition(CONTAINER) { + const cr = CONTAINER.getBoundingClientRect(); + const pr = CONTAINER.PLOT_PANE.getBoundingClientRect(); + const header_height = cr.height - pr.height - const csz = computeContainerSize(CONTAINER, targetSize, currentSizeData); - - if (csz.noChange) { - console.log("Size is the same as current, ignoring"); - return; - } - // We are now going to set he width and height of the container - for (const key of ["width", "height"]) { - CONTAINER.style.setProperty(key, csz[key] + "px"); + const computedStyle = getComputedStyle(CONTAINER); + const border = { + top: parseFloat(computedStyle.getPropertyValue("border-top-width")), + right: parseFloat(computedStyle.getPropertyValue("border-right-width")), + bottom: parseFloat(computedStyle.getPropertyValue( + "border-bottom-width" + )), + left: parseFloat(computedStyle.getPropertyValue("border-left-width")) } -} -// We now create the function that will update the plot based on the values specified -function updateFromHeader() { - const header_data = { - height: config_spans.height.ui_value, - width: config_spans.width.ui_value, - }; - changeContainerSize(header_data); -} -// We assign this function to the onblur event of width and height -if (firstRun) { - for (const container of Object.values(config_spans)) { - container.ui_span.onblur = (e) => { - container.ui_value = container.ui_span.textContent; - updateFromHeader(); - }; + + return { cr, pr, border, header_height }; } -} -// This function computes -/** - * Computes the plot size to use for relayout as a function of the container size. - * - * @param {import("./typedef.js").SizeData} sizeData - The current size data object. - */ -function computePlotSize(sizeData) { - // Remove Padding - const { container_pad, plot_pad, container_rect } = sizeData; - const offsets = { - width: - plot_pad.padding.width + - plot_pad.border.width + - plot_pad.offset.left + - container_pad.padding.width + - container_pad.border.width, - height: - plot_pad.padding.height + - plot_pad.border.height + - plot_pad.offset.top + - container_pad.padding.height + - container_pad.border.height, - }; - const sz = { - width: Math.round(container_rect.width - offsets.width), - height: Math.round(container_rect.height - offsets.height), - offsets, - }; - return sz; -} -// Create the resizeObserver to make the plot even more responsive! :magic: + /** - * Computes the required size of the CONTAINER based on the target size of the PLOT. + * Updates the position of the CONTAINER based on the provided left and top values. * - * @param {import("./typedef.js").Container} CONTAINER - The container element. - * @param {import("./typedef.js").Plotly} Plotly - The Plotly library. + * @param {import("./typedef.js").Container} CONTAINER - The container element to update. + * @param {{top: number, left: number, width: number, height: number}} target_plot_position } + * @param {ReturnType} [size_data] + * @return {undefined} the function does not return a value. */ -export function addResizeObserver(CONTAINER) { -const resizeObserver = new ResizeObserver(() => { - const sizeData = getSizeData(CONTAINER); - const { PLOT, CLIPBOARD_HEADER, Plotly } = CONTAINER - const { container_rect } = sizeData; - const plot_size = computePlotSize(sizeData); - // We save the height in the PLOT object - PLOT.container_height = container_rect.height; - // We deal with some stuff if the container is poppped - CLIPBOARD_HEADER.style.width = container_rect.width + "px"; - CLIPBOARD_HEADER.style.left = container_rect.left + "px"; - config_spans.height.ui_value = plot_size.height; - config_spans.width.ui_value = plot_size.width; - /* - The addition of the invalid argument `plutoresize` seems to fix the problem with calling `relayout` simply with `{autosize: true}` as update breaking mouse relayout events tracking. - See https://github.com/plotly/plotly.js/issues/6156 for details - */ - let config = { - // If this is popped out, we ignore the original width/height - width: - (CONTAINER.isPoppedOut() ? undefined : original_width) ?? plot_size.width, - height: - (CONTAINER.isPoppedOut() ? undefined : original_height) ?? - plot_size.height, - plutoresize: true, - }; - Plotly.relayout(PLOT, config).then(() => { - if (remove_container_size && !CONTAINER.isPoppedOut()) { - // This is needed to avoid the first resize upon plot creation to already be without a fixed height - CONTAINER.style.height = ""; - CONTAINER.style.width = ""; - remove_container_size = false; - } - }); -}); - resizeObserver.observe(CONTAINER); - // Save the resizer inside the CONTAINER - CONTAINER.resizeObserver = resizeObserver; -} +export function updateContainerPosition(CONTAINER, target_plot_position, size_data = computeContainerPosition(CONTAINER)) { + const tpr = target_plot_position; + + const { cr, pr, border, header_height } = size_data; + + const { PLOT_PANE } = CONTAINER; + const PLOT_PANE_CONTAINER = PLOT_PANE.parentElement + + const top_offset = tpr.top - pr.top; + const left_offset = tpr.left - pr.left; + const top = cr.top + top_offset; + const left = cr.left + left_offset; + + CONTAINER.style.setProperty("--element-top", top + "px"); + CONTAINER.style.setProperty("--element-left", left + "px"); + // The 3 belows are to + CONTAINER.style.setProperty( + "--max-width-offset", + tpr.left + border.right + 3 + "px" + ); + CONTAINER.style.setProperty( + "--max-height-offset", + tpr.top - header_height + border.bottom + 3 + "px" + ); + // @ts-ignore we are sure the container is not null + PLOT_PANE_CONTAINER.style.setProperty( + "--max-height-offset", + tpr.top + border.bottom + 3 + "px" + ); + PLOT_PANE.style.setProperty("--plot-width", tpr.width + "px"); + PLOT_PANE.style.setProperty("--plot-height", tpr.height + "px"); +} \ No newline at end of file diff --git a/js/src/styles.js b/js/src/styles.js index 9273ec5..0ed1934 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -68,6 +68,7 @@ export function addClipboardHeaderStyle(element, deps = {}) { cursor: move; padding: 5px; align-items: center; + user-select: none; } .popped-out & { border-bottom: 3px solid var(--kbd-border-color, var(--border-color)); From c9b9f6f3ddcf8878c4e674b71bdd42fbdc103db0 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Mon, 29 Jul 2024 16:32:41 +0200 Subject: [PATCH 30/39] add resize functionality --- js/src/clipboard.js | 4 +- js/src/resizer.js | 115 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 94 insertions(+), 25 deletions(-) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index b84031a..9e12766 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -1,7 +1,7 @@ import { addFormatConfigStyle, addClipboardHeaderStyle } from "./styles.js"; import { delay, getImageOptions, image_options_defaults } from "./utils.js"; import { mergeDeps } from "./global_deps.js"; -import { addDragFunctionality } from "./resizer.js"; +import { addDragFunctionality, addResizeFunctionality } from "./resizer.js"; import { updateContainerPosition } from "./resizer.js"; // Download formats @@ -27,6 +27,8 @@ export function addClipboardFunctionality(CONTAINER) { addClipboardHeader(CONTAINER); // Add the drag functionality addDragFunctionality(CONTAINER); + // Add the resize functionality + addResizeFunctionality(CONTAINER); // Customize the togglePopout function CONTAINER.togglePopout = function (filesave = false) { if (CONTAINER.isPoppedOut()) { diff --git a/js/src/resizer.js b/js/src/resizer.js index 9668aa9..7376915 100644 --- a/js/src/resizer.js +++ b/js/src/resizer.js @@ -7,9 +7,9 @@ * @param {Partial} [deps] - Global dependencies containing at least interact */ export function addDragFunctionality(CONTAINER, deps = CONTAINER.js_deps) { - const { interact } = deps; + const { interact, lodash } = deps; const { CLIPBOARD_HEADER, PLOT_PANE } = CONTAINER; - const limits = { top: {min: 0, max: 0}, left: {min: 0, max: 0}}; + const limits = { top: { min: 0, max: 0 }, left: { min: 0, max: 0 } }; let current = { top: 0, left: 0, width: 0, height: 0 }; let last = { top: 0, left: 0, width: 0, height: 0 }; // @ts-ignore We assume interact is present @@ -17,21 +17,21 @@ export function addDragFunctionality(CONTAINER, deps = CONTAINER.js_deps) { listeners: { start(event) { console.log(event.type, event.target); - const { pr, border, header_height } = computeContainerPosition(CONTAINER); + const { pr, border, header_height } = + computeContainerPosition(CONTAINER); const { top, left, width, height } = pr; // The minus 3 is for allowing some distance to the edge limits.left = { min: border.left + 3, max: globalThis.innerWidth - (width + border.right + 3), - } + }; limits.top = { min: border.top + header_height + 3, max: globalThis.innerHeight - (height + border.bottom + 3), - } + }; current = { top, left, width, height }; last = { top, left, width, height }; - CLIPBOARD_HEADER.blur() - CLIPBOARD_HEADER.classList.toggle('dragging', true) + CLIPBOARD_HEADER.classList.toggle("dragging", true); }, move(event) { const left = (current.left += event.dx); @@ -40,22 +40,88 @@ export function addDragFunctionality(CONTAINER, deps = CONTAINER.js_deps) { top: Math.min(Math.max(top, limits.top.min), limits.top.max), left: Math.min(Math.max(left, limits.left.min), limits.left.max), }; - if (new_vals.top !== last.top || new_vals.left !== last.left) { - last = {...new_vals, width: last.width, height: last.height}; - updateContainerPosition(CONTAINER, last) + if (!lodash.isEqual(last, new_vals)) { + last = { ...new_vals, width: last.width, height: last.height }; + updateContainerPosition(CONTAINER, last); } }, end(event) { console.log(event.type, event.target); - CLIPBOARD_HEADER.classList.toggle('dragging', false) - } + CLIPBOARD_HEADER.classList.toggle("dragging", false); + }, }, }); } +export function addResizeFunctionality(CONTAINER) { + const { interact, lodash } = CONTAINER.js_deps; + const { ui_values } = CONTAINER.CLIPBOARD_HEADER; + const limits = { + top: { min: 0, max: 0 }, + left: { min: 0, max: 0 }, + width: { min: 10, max: 0 }, + height: { min: 10, max: 0 }, + }; + let current = { top: 0, left: 0, width: 0, height: 0 }; + let last = { top: 0, left: 0, width: 0, height: 0 }; + interact(CONTAINER).resizable({ + edges: { top: true, left: true, bottom: true, right: true }, + listeners: { + start(event) { + const { edges } = event; + const { pr, border, header_height } = + computeContainerPosition(CONTAINER); + const { top, left, width, height } = pr; + current = { top, left, width, height }; + last = { top, left, width, height }; + limits.left.min = border.left + 3; + limits.left.max = pr.left + width - limits.width.min + limits.top.min = border.top + header_height + 3; + limits.top.max = pr.top + height - limits.height.min + limits.width.max = edges.left + ? pr.right - limits.left.min // We are resizing left + : edges.right + ? globalThis.innerWidth - (pr.left + border.right + 3) // We are resizing right + : width; // This is in case we are not resizing horizontally + limits.height.max = edges.top + ? pr.bottom - limits.top.min + : edges.bottom + ? globalThis.innerHeight - (pr.top + border.bottom + 3) + : height; // This is in case we are not resizing vertically + CONTAINER.classList.toggle("resizing", true); + }, + move(event) { + const { edges, deltaRect } = event; + current.width += deltaRect.width; + current.height += deltaRect.height; + if (edges.left) { + current.left += deltaRect.left; + } + if (edges.top) { + current.top += deltaRect.top; + } + const new_size = { + left: Math.min(Math.max(current.left, limits.left.min), limits.left.max), + top: Math.min(Math.max(current.top, limits.top.min), limits.top.max), + width: Math.min(Math.max(current.width, limits.width.min), limits.width.max), + height: Math.min(Math.max(current.height, limits.height.min), limits.height.max), + } + if (!lodash.isEqual(last, new_size)) { + last = { ...new_size }; + updateContainerPosition(CONTAINER, last); + ui_values.width = last.width; + ui_values.height = last.height; + } + }, + end(event) { + CONTAINER.classList.toggle("resizing", false); + }, + }, + }); +} /** - * Computes the position of the CONTAINER element. + * Computes the position of the CONTAINER and PLOT_PANE elements, as well as current border and header height of the container. * * @param {import("./typedef.js").Container} CONTAINER - The container element. * @returns {{cr: DOMRect, pr: DOMRect, border: {top: number, right: number, bottom: number, left: number}, header_height: number}} @@ -63,21 +129,18 @@ export function addDragFunctionality(CONTAINER, deps = CONTAINER.js_deps) { function computeContainerPosition(CONTAINER) { const cr = CONTAINER.getBoundingClientRect(); const pr = CONTAINER.PLOT_PANE.getBoundingClientRect(); - const header_height = cr.height - pr.height + const header_height = cr.height - pr.height; const computedStyle = getComputedStyle(CONTAINER); const border = { top: parseFloat(computedStyle.getPropertyValue("border-top-width")), right: parseFloat(computedStyle.getPropertyValue("border-right-width")), - bottom: parseFloat(computedStyle.getPropertyValue( - "border-bottom-width" - )), - left: parseFloat(computedStyle.getPropertyValue("border-left-width")) - } + bottom: parseFloat(computedStyle.getPropertyValue("border-bottom-width")), + left: parseFloat(computedStyle.getPropertyValue("border-left-width")), + }; return { cr, pr, border, header_height }; - } - +} /** * Updates the position of the CONTAINER based on the provided left and top values. @@ -87,13 +150,17 @@ function computeContainerPosition(CONTAINER) { * @param {ReturnType} [size_data] * @return {undefined} the function does not return a value. */ -export function updateContainerPosition(CONTAINER, target_plot_position, size_data = computeContainerPosition(CONTAINER)) { +export function updateContainerPosition( + CONTAINER, + target_plot_position, + size_data = computeContainerPosition(CONTAINER) +) { const tpr = target_plot_position; const { cr, pr, border, header_height } = size_data; const { PLOT_PANE } = CONTAINER; - const PLOT_PANE_CONTAINER = PLOT_PANE.parentElement + const PLOT_PANE_CONTAINER = PLOT_PANE.parentElement; const top_offset = tpr.top - pr.top; const left_offset = tpr.left - pr.left; @@ -119,4 +186,4 @@ export function updateContainerPosition(CONTAINER, target_plot_position, size_da PLOT_PANE.style.setProperty("--plot-width", tpr.width + "px"); PLOT_PANE.style.setProperty("--plot-height", tpr.height + "px"); -} \ No newline at end of file +} From 2fe11692cb34bce2337431948e053cfca0e1c5bc Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Mon, 29 Jul 2024 18:05:39 +0200 Subject: [PATCH 31/39] add floatingui to js_deps --- js/deno.jsonc | 1 + js/deno.lock | 20 ++++++++++++++++++++ js/src/global_deps.js | 6 ++++-- js/src/typedef.js | 1 + js/src/url_deps.js | 3 ++- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/js/deno.jsonc b/js/deno.jsonc index 530f940..475327a 100644 --- a/js/deno.jsonc +++ b/js/deno.jsonc @@ -20,5 +20,6 @@ "https://esm.sh/@emotion/css@11.11.2": "npm:@emotion/css@11.11.2", "https://esm.sh/lodash-es@4.17.21": "npm:lodash-es@4.17.21", "https://esm.sh/interactjs@1.10.27": "npm:interactjs@1.10.27", + "https://esm.sh/@floating-ui/dom@1.6.8": "npm:@floating-ui/dom@1.6.8" } } \ No newline at end of file diff --git a/js/deno.lock b/js/deno.lock index b4caff3..2252204 100644 --- a/js/deno.lock +++ b/js/deno.lock @@ -9,6 +9,8 @@ "jsr:@std/jsonc@0.213": "jsr:@std/jsonc@0.213.1", "jsr:@std/path@0.213": "jsr:@std/path@0.213.1", "npm:@emotion/css@11.11.2": "npm:@emotion/css@11.11.2", + "npm:@floating-ui/dom": "npm:@floating-ui/dom@1.6.8", + "npm:@floating-ui/dom@1.6.8": "npm:@floating-ui/dom@1.6.8", "npm:@interactjs/interactjs@1.10.27": "npm:@interactjs/interactjs@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27_@interactjs+modifiers@1.10.27__@interactjs+core@1.10.27___@interactjs+utils@1.10.27__@interactjs+utils@1.10.27", "npm:@types/lodash-es": "npm:@types/lodash-es@4.17.12", "npm:@types/plotly.js": "npm:@types/plotly.js@2.29.2", @@ -260,6 +262,23 @@ "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "dependencies": {} }, + "@floating-ui/core@1.6.5": { + "integrity": "sha512-8GrTWmoFhm5BsMZOTHeGD2/0FLKLQQHvO/ZmQga4tKempYRLz8aqJGqXVuQgisnMObq2YZ2SgkwctN1LOOxcqA==", + "dependencies": { + "@floating-ui/utils": "@floating-ui/utils@0.2.5" + } + }, + "@floating-ui/dom@1.6.8": { + "integrity": "sha512-kx62rP19VZ767Q653wsP1XZCGIirkE09E0QUGNYTM/ttbbQHqcGPdSfWFxUyyNLc/W6aoJRBajOSXhP6GXjC0Q==", + "dependencies": { + "@floating-ui/core": "@floating-ui/core@1.6.5", + "@floating-ui/utils": "@floating-ui/utils@0.2.5" + } + }, + "@floating-ui/utils@0.2.5": { + "integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ==", + "dependencies": {} + }, "@interactjs/actions@1.10.27_@interactjs+core@1.10.27__@interactjs+utils@1.10.27_@interactjs+utils@1.10.27": { "integrity": "sha512-FCRg5KwB+stkPcAMx/Cn0fgGP6p4LyMX9S/Upcn/W+hpYme31bPi54PCqmOebzz6myTthN6zFf9jMyLOqtI/gg==", "dependencies": { @@ -1562,6 +1581,7 @@ "workspace": { "dependencies": [ "npm:@emotion/css@11.11.2", + "npm:@floating-ui/dom@1.6.8", "npm:interactjs@1.10.27", "npm:lodash-es@4.17.21" ] diff --git a/js/src/global_deps.js b/js/src/global_deps.js index b787e84..400819f 100644 --- a/js/src/global_deps.js +++ b/js/src/global_deps.js @@ -10,11 +10,12 @@ export const GlobalDeps = {}; * @param {import("./typedef.js").JSDeps} params - An object containing emotion, lodash, interact and html dependencies. * @return {void} */ -export function setGlobalDeps({ emotion, lodash, interact, html }) { +export function setGlobalDeps({ emotion, lodash, interact, html, floatingUI }) { GlobalDeps.emotion = emotion; GlobalDeps.lodash = lodash; GlobalDeps.interact = interact; GlobalDeps.html = html; + GlobalDeps.floatingUI = floatingUI; } /** @@ -27,7 +28,8 @@ export function validGlobalDeps() { GlobalDeps.emotion != undefined && GlobalDeps.lodash != undefined && GlobalDeps.interact != undefined && - GlobalDeps.html != undefined + GlobalDeps.html != undefined && + GlobalDeps.floatingUI != undefined ); } diff --git a/js/src/typedef.js b/js/src/typedef.js index f81f97a..2adadf5 100644 --- a/js/src/typedef.js +++ b/js/src/typedef.js @@ -8,6 +8,7 @@ * @property {import('npm:@types/lodash-es')} lodash * @property {import('npm:interactjs').default} interact * @property {import('https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js').html} html + * @property {import('https://esm.sh/@floating-ui/dom@1.6.8')} floatingUI */ // Plotly types diff --git a/js/src/url_deps.js b/js/src/url_deps.js index e8e0b7d..9886f82 100644 --- a/js/src/url_deps.js +++ b/js/src/url_deps.js @@ -1,4 +1,5 @@ export * as lodash from "https://esm.sh/lodash-es@4.17.21" export { default as interact } from "https://esm.sh/interactjs@1.10.27" export { html } from "https://esm.sh/gh/observablehq/stdlib@v5.8.6/src/html.js" -export * as emotion from "https://esm.sh/@emotion/css@11.11.2" \ No newline at end of file +export * as emotion from "https://esm.sh/@emotion/css@11.11.2" +export * as floatingUI from "https://esm.sh/@floating-ui/dom@1.6.8" \ No newline at end of file From 9380bfffea75b1a768742a129d11e4394ab5c466 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Tue, 30 Jul 2024 15:24:56 +0200 Subject: [PATCH 32/39] add working tooltips via floating-ui --- js/src/clipboard.js | 66 +++++++++++++++++++++++++++++++-------------- js/src/styles.js | 19 +++++++++---- js/src/typedef.js | 2 +- 3 files changed, 61 insertions(+), 26 deletions(-) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index 9e12766..be5b202 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -43,15 +43,38 @@ export function addClipboardFunctionality(CONTAINER) { modifyModebarButtons(CONTAINER); } +/** + * Adds a tooltip listener to the provided config span element. + * + * @param {import("./typedef.js").ImageOptionSpan} span - The config span element to add the tooltip listener to. + * @param {import("./typedef.js").JSDeps} js_deps - The dependencies object containing the necessary tools for the tooltip listener. + * @return {void} This function does not return a value. + */ +function addTooltipListener(span, js_deps) { + const { floatingUI } = js_deps; + const { computePosition, flip, shift, offset } = floatingUI; + const { label, config_span } = span; + label.onmouseenter = (e) => { + computePosition(label, config_span, { + strategy: "fixed", + placement: "top-start", + middleware: [offset(6), flip(), shift()], + }).then(({x,y}) => { + config_span.style.top = y + "px"; + config_span.style.left = x + "px"; + }) + } +} + /** * Function to add a configuration span element. * - * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER - Clipboard Header element + * @param {import("./typedef.js").Container} CONTAINER - Container element for the plot * @param {keyof import("./typedef.js").OptionSpansObject} name - lowercase name of the config span - * @param {Partial} [deps] - Global dependencies containing at least html and lodash */ -export function addSingleConfigSpan(CLIPBOARD_HEADER, name, deps = {}) { - const { html, lodash } = mergeDeps(deps); +export function addSingleConfigSpan(CONTAINER, name) { + const { CLIPBOARD_HEADER } = CONTAINER; + const { html, lodash } = CONTAINER.js_deps; const config_span = html``; const label = html`${config_span}${lodash.capitalize(name)}: { checkConfigSync(container); }; + checkConfigSync(container) // @ts-ignore: addEventListener does not support function with CustomEvent as arguments container.addEventListener("clipboard-header-change", listener); return; } /** - * Add config spans to the given CLIPBOARD_HEADER. + * Add config spans to the CLIPBOARD_HEADER associated to the given CONTAINER. * - * @param {import("./typedef.js").ClipboardHeader} CLIPBOARD_HEADER - the clipboard header to add config spans to - * @param {Partial} [deps] - Global dependencies containing at least html + * @param {import("./typedef.js").Container} CONTAINER - the Container containing the clipboard header to add config spans to * @return {void} */ -function addOptionSpans(CLIPBOARD_HEADER, deps = {}) { - const { html } = mergeDeps(deps); +function addOptionSpans(CONTAINER) { + const { CLIPBOARD_HEADER } = CONTAINER; + const { html } = CONTAINER.js_deps; // @ts-ignore: Will be populated CLIPBOARD_HEADER.option_spans = {}; - addSingleConfigSpan(CLIPBOARD_HEADER, "format"); - addSingleConfigSpan(CLIPBOARD_HEADER, "width"); - addSingleConfigSpan(CLIPBOARD_HEADER, "height"); - addSingleConfigSpan(CLIPBOARD_HEADER, "scale"); + addSingleConfigSpan(CONTAINER, "format"); + addSingleConfigSpan(CONTAINER, "width"); + addSingleConfigSpan(CONTAINER, "height"); + addSingleConfigSpan(CONTAINER, "scale"); // Add set/unset CLIPBOARD_HEADER.appendChild( html`` @@ -134,7 +160,7 @@ function addOptionSpans(CLIPBOARD_HEADER, deps = {}) { CLIPBOARD_HEADER.appendChild( html`` ); - addSingleConfigSpan(CLIPBOARD_HEADER, "filename"); + addSingleConfigSpan(CONTAINER, "filename"); } /** @@ -152,14 +178,14 @@ export function addClipboardHeader(CONTAINER, deps = CONTAINER.js_deps) { const CLIPBOARD_HEADER = html``; + CONTAINER.CLIPBOARD_HEADER = CLIPBOARD_HEADER; addClipboardHeaderStyle(CLIPBOARD_HEADER); // Add the various spans for the UI - addOptionSpans(CLIPBOARD_HEADER); + addOptionSpans(CONTAINER); // Add the objects collecting ui and config options values addImageOptionsObj(CLIPBOARD_HEADER, "ui"); addImageOptionsObj(CLIPBOARD_HEADER, "config"); CONTAINER.insertAdjacentElement("afterbegin", CLIPBOARD_HEADER); - CONTAINER.CLIPBOARD_HEADER = CLIPBOARD_HEADER; // Insert the listener for the change in width/height function size_listener(/** @type {CustomEvent} */ e) { console.log("Inside size_listener", e); @@ -185,17 +211,17 @@ function checkConfigSync(span) { const { ui_value, config_value, config_span, key } = span; if (config_value === undefined) { span.setAttribute("config", "missing"); - config_span.setInnerHTML( + config_span.setVariableText( `The key ${key} is not present in the config.` ); } else if (ui_value == config_value) { span.setAttribute("config", "matching"); - config_span.setInnerHTML( + config_span.setVariableText( `The key ${key} has the same value in the config and in the header.` ); } else { span.setAttribute("config", "different"); - config_span.setInnerHTML( + config_span.setVariableText( `The key ${key} has a different value (${config_value}) in the config.` ); } @@ -351,7 +377,7 @@ function initializeConfigValueSpan(span, key, deps = {}) {

` ); // We add the function to set the text on the config - span.setInnerHTML = (x) => { + span.setVariableText = (x) => { variableText.innerHTML = x; }; // Here we mostly want to define the setter and getter diff --git a/js/src/styles.js b/js/src/styles.js index 0ed1934..10511dd 100644 --- a/js/src/styles.js +++ b/js/src/styles.js @@ -92,20 +92,29 @@ export function addClipboardHeaderStyle(element, deps = {}) { & .config-value { font-weight: normal; color: var(--pluto-output-color, var(--output-color)); - display: none; - position: absolute; + display: block; + visibility: hidden; + position: fixed; + z-index: 2000; background: var(--main-bg-color, var(--bg-color)); border: 3px solid var(--kbd-border-color, var(--border-color)); border-radius: 12px; - transform: translate(0px, calc(-100% - 10px)); padding: 5px; + max-width: 250px; + transition: visibility 0.25s; + } + & .config-value p:first-child { + margin-block-start: 0; + } + & .config-value p:last-child { + margin-block-end: 0; } & .label { user-select: none; } & .label:hover span.config-value { - display: inline-block; - min-width: 150px; + visibility: visible; + transition: visibility 0s; } & .clipboard-span[config="matching"] .label { color: var(--cm-macro-color, var(--macro-color)); diff --git a/js/src/typedef.js b/js/src/typedef.js index 2adadf5..f6556d5 100644 --- a/js/src/typedef.js +++ b/js/src/typedef.js @@ -75,7 +75,7 @@ /** * @typedef {HTMLWithValue & { - * setInnerHTML: Function, + * setVariableText: Function, * }} ConfigValueSpan */ From f1f8ce11c4e326cc0ab1540b9f56aaf78b8d18f6 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Tue, 30 Jul 2024 15:32:36 +0200 Subject: [PATCH 33/39] prettify and add some comments --- js/src/clipboard.js | 47 ++++++++++++++++++++++++++------------------- js/src/resizer.js | 45 +++++++++++++++++++++++++++++-------------- 2 files changed, 58 insertions(+), 34 deletions(-) diff --git a/js/src/clipboard.js b/js/src/clipboard.js index be5b202..160e3ec 100644 --- a/js/src/clipboard.js +++ b/js/src/clipboard.js @@ -33,10 +33,10 @@ export function addClipboardFunctionality(CONTAINER) { CONTAINER.togglePopout = function (filesave = false) { if (CONTAINER.isPoppedOut()) { unpopContainer(CONTAINER); - CONTAINER.classList.toggle('filesave-extras', false) + CONTAINER.classList.toggle("filesave-extras", false); } else { popContainer(CONTAINER); - CONTAINER.classList.toggle('filesave-extras', filesave) + CONTAINER.classList.toggle("filesave-extras", filesave); } }; // Modify the plot object to include the buttons @@ -59,11 +59,11 @@ function addTooltipListener(span, js_deps) { strategy: "fixed", placement: "top-start", middleware: [offset(6), flip(), shift()], - }).then(({x,y}) => { + }).then(({ x, y }) => { config_span.style.top = y + "px"; config_span.style.left = x + "px"; - }) - } + }); + }; } /** @@ -132,7 +132,7 @@ export function addSingleConfigSpan(CONTAINER, name) { const listener = (/** @type {CustomEvent} */ e) => { checkConfigSync(container); }; - checkConfigSync(container) + checkConfigSync(container); // @ts-ignore: addEventListener does not support function with CustomEvent as arguments container.addEventListener("clipboard-header-change", listener); return; @@ -188,7 +188,6 @@ export function addClipboardHeader(CONTAINER, deps = CONTAINER.js_deps) { CONTAINER.insertAdjacentElement("afterbegin", CLIPBOARD_HEADER); // Insert the listener for the change in width/height function size_listener(/** @type {CustomEvent} */ e) { - console.log("Inside size_listener", e); const { key, type, value } = e.detail; if (type !== "ui") return; if (!(key === "width" || key === "height")) return; @@ -196,7 +195,7 @@ export function addClipboardHeader(CONTAINER, deps = CONTAINER.js_deps) { CONTAINER.PLOT_PANE.style.setProperty(varname, value + "px"); } CLIPBOARD_HEADER.addEventListener("clipboard-header-change", size_listener, { - signal: CONTAINER.controller.signal + signal: CONTAINER.controller.signal, }); return; } @@ -206,7 +205,6 @@ export function addClipboardHeader(CONTAINER, deps = CONTAINER.js_deps) { * @param {import("./typedef.js").ImageOptionSpan} span */ function checkConfigSync(span) { - console.log("inside checkConfigSync", span); // We use the custom getters we'll set up in the container const { ui_value, config_value, config_span, key } = span; if (config_value === undefined) { @@ -269,7 +267,10 @@ function updateFunction(key) { * @this {HTMLElement & {value: string}} */ return function () { - this.querySelector(".format-options")?.setAttribute("selected", this.value); + this.querySelector(".format-options")?.setAttribute( + "selected", + this.value + ); }; } else { /** @@ -345,9 +346,9 @@ function initializeUIValueSpan(span, key, deps = {}) { } }; if (key !== "format") { - span.onblur = function() { + span.onblur = function () { this.value = this.textContent; - } + }; } } @@ -531,7 +532,7 @@ function sendToClipboard(blob) { */ export function copyImageToClipboard(CONTAINER) { // We extract the image options from the provided parameters (if they exist) - const { Plotly, PLOT, CLIPBOARD_HEADER } = CONTAINER + const { Plotly, PLOT, CLIPBOARD_HEADER } = CONTAINER; const config = CLIPBOARD_HEADER.ui_values; Plotly.toImage(PLOT, config).then(function (dataUrl) { fetch(dataUrl) @@ -554,7 +555,7 @@ export function copyImageToClipboard(CONTAINER) { */ function saveImageToFile(CONTAINER) { // We extract the image options from the provided parameters (if they exist) - const { Plotly, PLOT, CLIPBOARD_HEADER } = CONTAINER + const { Plotly, PLOT, CLIPBOARD_HEADER } = CONTAINER; const config = CLIPBOARD_HEADER.ui_values; // @ts-ignore config would like downloadImageOpts but we just use our own type Plotly.downloadImage(PLOT, config); @@ -747,17 +748,23 @@ function modifyModebarButtons(CONTAINER) { path: "M280 64h40c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128C0 92.7 28.7 64 64 64h40 9.6C121 27.5 153.3 0 192 0s71 27.5 78.4 64H280zM64 112c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320c8.8 0 16-7.2 16-16V128c0-8.8-7.2-16-16-16H304v24c0 13.3-10.7 24-24 24H192 104c-13.3 0-24-10.7-24-24V112H64zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z", }, direction: "up", - click: DualClick(() => copyImageToClipboard(CONTAINER), () => { - togglePopout(); - }), + click: DualClick( + () => copyImageToClipboard(CONTAINER), + () => { + togglePopout(); + } + ), }, { name: "Download Image", icon: Plotly.Icons.camera, direction: "up", - click: DualClick(() => saveImageToFile(CONTAINER), () => { - togglePopout(true); - }), + click: DualClick( + () => saveImageToFile(CONTAINER), + () => { + togglePopout(true); + } + ), }, ] ); diff --git a/js/src/resizer.js b/js/src/resizer.js index 7376915..869b07b 100644 --- a/js/src/resizer.js +++ b/js/src/resizer.js @@ -1,22 +1,24 @@ //@ts-check /** - * Function to add Drag functionality to the CLIPBOARD_HEADER + * Function to add Drag functionality to the to plot container when it's + * popped-out, working by dragging from the CLIPBOARD_HEADER. + * This will not allow the container to go outside of the window boundaries. * * @param {import("./typedef.js").Container} CONTAINER - Container element - * @param {Partial} [deps] - Global dependencies containing at least interact */ -export function addDragFunctionality(CONTAINER, deps = CONTAINER.js_deps) { - const { interact, lodash } = deps; - const { CLIPBOARD_HEADER, PLOT_PANE } = CONTAINER; +export function addDragFunctionality(CONTAINER) { + const { interact, lodash } = CONTAINER.js_deps; + const { CLIPBOARD_HEADER } = CONTAINER; + // limits contains the min and max limits for the corresponding css properties const limits = { top: { min: 0, max: 0 }, left: { min: 0, max: 0 } }; + // current and last will hold the current position, modified while dragging, and the last valid position that was enforced let current = { top: 0, left: 0, width: 0, height: 0 }; let last = { top: 0, left: 0, width: 0, height: 0 }; // @ts-ignore We assume interact is present interact(CLIPBOARD_HEADER).draggable({ listeners: { start(event) { - console.log(event.type, event.target); const { pr, border, header_height } = computeContainerPosition(CONTAINER); const { top, left, width, height } = pr; @@ -46,13 +48,19 @@ export function addDragFunctionality(CONTAINER, deps = CONTAINER.js_deps) { } }, end(event) { - console.log(event.type, event.target); CLIPBOARD_HEADER.classList.toggle("dragging", false); }, }, }); } +/** + * Function to add Resize functionality to the to plot container when it's + * popped-out, working by dragging any of the container edges + * This will not allow any the container edges to go outside of the window boundaries. + * + * @param {import("./typedef.js").Container} CONTAINER - Container element + */ export function addResizeFunctionality(CONTAINER) { const { interact, lodash } = CONTAINER.js_deps; const { ui_values } = CONTAINER.CLIPBOARD_HEADER; @@ -75,15 +83,15 @@ export function addResizeFunctionality(CONTAINER) { current = { top, left, width, height }; last = { top, left, width, height }; limits.left.min = border.left + 3; - limits.left.max = pr.left + width - limits.width.min + limits.left.max = pr.left + width - limits.width.min; limits.top.min = border.top + header_height + 3; - limits.top.max = pr.top + height - limits.height.min + limits.top.max = pr.top + height - limits.height.min; limits.width.max = edges.left ? pr.right - limits.left.min // We are resizing left : edges.right ? globalThis.innerWidth - (pr.left + border.right + 3) // We are resizing right : width; // This is in case we are not resizing horizontally - limits.height.max = edges.top + limits.height.max = edges.top ? pr.bottom - limits.top.min : edges.bottom ? globalThis.innerHeight - (pr.top + border.bottom + 3) @@ -101,11 +109,20 @@ export function addResizeFunctionality(CONTAINER) { current.top += deltaRect.top; } const new_size = { - left: Math.min(Math.max(current.left, limits.left.min), limits.left.max), + left: Math.min( + Math.max(current.left, limits.left.min), + limits.left.max + ), top: Math.min(Math.max(current.top, limits.top.min), limits.top.max), - width: Math.min(Math.max(current.width, limits.width.min), limits.width.max), - height: Math.min(Math.max(current.height, limits.height.min), limits.height.max), - } + width: Math.min( + Math.max(current.width, limits.width.min), + limits.width.max + ), + height: Math.min( + Math.max(current.height, limits.height.min), + limits.height.max + ), + }; if (!lodash.isEqual(last, new_size)) { last = { ...new_size }; updateContainerPosition(CONTAINER, last); From f15d5eec17032f610bcf021c3331cf6d192c10bf Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Thu, 1 Aug 2024 09:35:03 +0200 Subject: [PATCH 34/39] improve offset and width/height computations to properly account (or not) for borders --- js/index.html | 3 +- js/src/clipboard.js | 179 ++++++++++++-------------------------------- js/src/container.js | 2 +- js/src/prehooks.js | 3 +- js/src/resizer.js | 79 +++++++++++++------ js/src/styles.js | 13 ++++ 6 files changed, 121 insertions(+), 158 deletions(-) diff --git a/js/index.html b/js/index.html index 7d5cf76..fe202cf 100644 --- a/js/index.html +++ b/js/index.html @@ -7,7 +7,7 @@