diff --git a/dist/build/browserslist_index/browserslist_index.js b/dist/build/browserslist_index/browserslist_index.js index aa207e5c34..3568696c98 100644 --- a/dist/build/browserslist_index/browserslist_index.js +++ b/dist/build/browserslist_index/browserslist_index.js @@ -12,7 +12,7 @@ var hasRequiredDist; function requireDist () { if (hasRequiredDist) return dist; hasRequiredDist = 1; -"function"==typeof SuppressedError&&SuppressedError;const n=(n,a)=>{if(n===a)return 0;const[e=0,u=0]=n.split(".",2).map(Number),[s=0,o=0]=a.split(".",2).map(Number);if(isNaN(e)||isNaN(u))throw new Error(`Invalid version: ${n}`);if(isNaN(s)||isNaN(o))throw new Error(`Invalid version: ${a}`);return e!==s?e>s?1:-1:u!==o?u>o?1:-1:0},a={c:{longName:"chrome"},ca:{longName:"chrome_android"},e:{longName:"edge"},f:{longName:"firefox"},fa:{longName:"firefox_android"},s:{longName:"safari"},si:{longName:"safari_ios"},o:{longName:"opera",engine:"Blink"},oa:{longName:"opera_android",engine:"Blink"},sa:{longName:"samsunginternet_android",engine:"Blink"},wva:{longName:"webview_android",engine:"Blink"},y:{longName:"ya_android",engine:"Blink"},u:{longName:"uc_android",engine:"Blink"},q:{longName:"qq_android",engine:"Blink"},k:{longName:"kai_os",engine:"Gecko"},fb:{longName:"facebook_android",engine:"Blink"},ia:{longName:"instagram_android",engine:"Blink"}},e=n=>6===n.length&&/^\d{6}$/.test(n)?`20${n.slice(0,2)}-${n.slice(2,4)}-${n.slice(4,6)}`:8===n.length&&/^\d{8}$/.test(n)?`${n.slice(0,4)}-${n.slice(4,6)}-${n.slice(6,8)}`:n,u={},s={};let o="",f=false;"pre_baseline\nc,1,081211\nc,2,090521\nc,3,090915\nc,4,100125\nc,5,100525\nc,6,100902\nc,7,101019\nc,8,101202\nc,9,110203\nc,10,110308\nc,11,110427\nc,12,110607\nc,13,110802\nc,14,110916\nc,15,111025\nc,16,111213\nc,17,120208\nc,18,120328\nc,19,120515\nc,20,120626\nc,21,120731\nc,22,120925\nc,23,121106\nc,24,130110\nc,25,130221\nc,26,130326\nc,27,130521\nc,28,130709\nc,29,130820\nc,30,131001\nc,31,131112\nc,32,140114\nc,33,140220\nc,34,140408\nc,35,140520\nc,36,140716\nc,37,140826\nc,38,141007\nc,39,141118\nc,40,150121\nc,41,150303\nc,42,150414\nc,43,150519\nc,44,150721\nca,18,120627\nca,25,130227\nca,26,130403\nca,27,130522\nca,28,130710\nca,29,130821\nca,30,131002\nca,31,131114\nca,32,140115\nca,33,140226\nca,34,140402\nca,35,140520\nca,36,140716\nca,37,140903\nca,38,141008\nca,39,141112\nca,40,150121\nca,41,150311\nca,42,150415\nca,43,150527\nf,1,041109\nf,1.5,051129\nf,2,061024\nf,3,080617\nf,3.5,090630\nf,3.6,100121\nf,4,110322\nf,5,110621\nf,6,110816\nf,7,110927\nf,8,111108\nf,9,111220\nf,10,120131\nf,11,120313\nf,12,120424\nf,13,120605\nf,14,120717\nf,15,120828\nf,16,121009\nf,17,121120\nf,18,130108\nf,19,130219\nf,20,130402\nf,21,130514\nf,22,130625\nf,23,130806\nf,24,130917\nf,25,131029\nf,26,131210\nf,27,140204\nf,28,140318\nf,29,140429\nf,30,140610\nf,31,140722\nf,32,140902\nf,33,141014\nf,34,141201\nf,35,150113\nf,36,150224\nf,37,150331\nf,38,150512\nf,39,150702\nfa,4,110329\nfa,5,110621\nfa,6,110816\nfa,7,110927\nfa,8,111108\nfa,9,111221\nfa,10,120131\nfa,14,120626\nfa,15,120828\nfa,16,121009\nfa,17,121120\nfa,18,130108\nfa,19,130219\nfa,20,130402\nfa,21,130514\nfa,22,130625\nfa,23,130806\nfa,24,130917\nfa,25,131029\nfa,26,131210\nfa,27,140204\nfa,28,140318\nfa,29,140429\nfa,30,140610\nfa,31,140722\nfa,32,140902\nfa,33,141014\nfa,34,141201\nfa,35,150113\nfa,36,150227\nfa,37,150331\nfa,38,150512\nfa,39,150702\ns,1,030623\ns,1.1,031024\ns,1.2,040202\ns,1.3,050415\ns,2,050429\ns,3,071026\ns,3.1,080318\ns,4,090608\ns,5,100607\ns,5.1,110720\ns,6,120725\ns,7,131022\ns,8,141016\nsi,1,070629\nsi,2,080711\nsi,3,090617\nsi,3.2,100403\nsi,4,100621\nsi,4.2,101122\nsi,5,111012\nsi,6,120910\nsi,7,130918\nsi,8,140917\n20150729\nc,38,141007\nca,38,141008\ne,12,150729\nf,38,150512\nfa,38,150512\ns,11,170919\nsi,11,170919\no,25,141015,38\noa,25,141016,38\nsa,3.0,150410,38\nwva,38,141008,38\ny,14.12,u,38\nu,11.1,u,40\nq,8.1,u,57\nk,2.0,170701,48\nfb,66,u,48\nia,23,u,62\n20150922\nf,41,150922\nfa,41,150922\n20150930\nc,44,150721\nca,44,150729\no,31,150804,44\noa,32,150923,45\nsa,4.0,160311,44\nwva,44,150729,44\ny,15.9,u,44\nu,11.6,u,57\n20151112\ne,13,151112\n20151231\nc,44,150721\nca,44,150729\ne,13,151112\nf,41,150922\nfa,41,150922\ns,11,170919\nsi,11,170919\no,31,150804,44\noa,32,150923,45\nsa,4.0,160311,44\nwva,44,150729,44\ny,15.9,u,44\nu,11.6,u,57\nq,8.1,u,57\nk,2.0,170701,48\nfb,66,u,48\nia,23,u,62\n20160308\nf,45,160308\nfa,45,160308\n20160607\nf,47,160607\nfa,47,160607\n20160802\ne,14,160802\n20160907\nc,53,160831\nca,53,160907\no,40,160920,53\noa,41,161025,54\nsa,6.0,170823,56\nwva,53,160907,53\ny,16.10,u,53\nfb,95,u,53\n20160920\nf,49,160920\nfa,49,160920\nk,3.0,210901,84\n20161231\nc,53,160831\nca,53,160907\ne,14,160802\nf,49,160920\nfa,49,160920\ns,11,170919\nsi,11,170919\no,40,160920,53\noa,41,161025,54\nsa,6.0,170823,56\nwva,53,160907,53\ny,16.10,u,53\nu,11.6,u,57\nq,8.1,u,57\nk,3.0,210901,84\nfb,95,u,53\nia,23,u,62\n20170201\nc,56,170125\nca,56,170201\nf,50,161115\nfa,50,161115\no,43,170207,56\noa,43,170927,59\nwva,56,170201,56\ny,17.3,u,56\nfb,112,u,56\n20170307\nf,52,170307\nfa,52,170307\n20170316\nca,57,170316\n20170405\nc,57,170309\ne,15,170405\no,44,170321,57\nsa,7.0,180316,59\nwva,57,170316,57\ny,17.4,u,57\nfb,117,u,57\n20170419\nf,53,170419\nfa,53,170419\n20170928\nfa,56,170928\n20171017\ne,16,171017\n20171024\nc,62,171017\nca,62,171024\no,49,171108,62\noa,46,180514,63\nsa,8.0,180718,63\nwva,62,171024,62\ny,17.11,u,62\nu,13.3,200909,78\nq,9.6,u,66\nfb,149,u,62\n20171128\nfa,57,171128\n20171231\nc,62,171017\nca,62,171024\ne,16,171017\nf,53,170419\nfa,57,171128\ns,11,170919\nsi,11,170919\no,49,171108,62\noa,46,180514,63\nsa,8.0,180718,63\nwva,62,171024,62\ny,17.11,u,62\nu,13.3,200909,78\nq,9.6,u,66\nk,3.0,210901,84\nfb,149,u,62\nia,23,u,62\n20180412\ns,11.1,180412\nsi,11.3,180329\n20180430\ne,17,180430\n20180509\nc,66,180417\nca,66,180417\nf,60,180509\nfa,60,180509\no,53,180510,66\noa,47,180723,66\nsa,9.0,180915,67\nwva,66,180417,66\ny,18.6,u,66\nfb,169,u,66\nia,42,u,66\n20180905\nf,62,180905\nfa,62,180905\n20181002\ne,18,181002\n20181023\nf,63,181023\nfa,63,181023\n20181211\nf,64,181211\nfa,64,181211\n20181231\nc,66,180417\nca,66,180417\ne,18,181002\nf,64,181211\nfa,64,181211\ns,11.1,180412\nsi,11.3,180329\no,53,180510,66\noa,47,180723,66\nsa,9.0,180915,67\nwva,66,180417,66\ny,18.6,u,66\nu,13.3,200909,78\nq,9.6,u,66\nk,3.0,210901,84\nfb,169,u,66\nia,42,u,66\n20190129\nf,65,190129\nfa,65,190129\n20190325\ns,12.1,190325\nsi,12.2,190325\n20190919\ns,13,190919\nsi,13,190919\n20191231\nc,66,180417\nca,66,180417\ne,18,181002\nf,65,190129\nfa,65,190129\ns,13,190919\nsi,13,190919\no,53,180510,66\noa,47,180723,66\nsa,9.0,180915,67\nwva,66,180417,66\ny,18.6,u,66\nu,13.3,200909,78\nq,9.6,u,66\nk,3.0,210901,84\nfb,169,u,66\nia,42,u,66\n20200115\nc,76,190730\nca,76,190730\ne,79,200115\nf,68,190709\nfa,68,190709\no,63,190820,76\noa,54,191018,76\nsa,12.0,200619,79\nwva,76,190730,76\ny,19.9,u,76\nq,10.9,201122,77\nfb,233,u,76\nia,96,u,80\n20200204\nc,80,200204\nca,80,200204\no,67,200303,80\noa,57,200330,80\nsa,13.0,201202,83\nwva,80,200204,80\ny,20.3,u,80\nu,15.3,230317,100\nq,11.7,211102,89\nfb,256,u,80\n20200207\ne,80,200207\n20200324\ns,13.1,200324\nsi,13.4,200324\n20200413\nc,81,200407\nca,81,200407\ne,81,200413\no,68,200422,81\noa,58,200513,81\nwva,81,200407,81\ny,20.4,u,81\nfb,266,u,81\nia,137,u,81\n20200727\nc,84,200727\nca,84,200727\ne,84,200716\no,70,200727,84\noa,60,200923,85\nsa,14.0,210417,87\nwva,84,200727,84\ny,20.8,200902,84\nfb,297,201202,86\nia,153,u,84\n20200728\nf,78,200630\nfa,79,200728\n20200827\nc,85,200825\nca,85,200825\ne,85,200827\no,71,200915,85\nwva,85,200825,85\ny,20.9,200927,85\nia,163,u,92\n20200916\nf,79,200728\ns,14,200916\nsi,14,200916\n20201020\nc,86,201020\nca,86,201020\ne,86,201009\no,72,201021,86\noa,61,201207,86\nwva,86,201020,86\ny,20.11,201111,86\n20201117\nf,83,201117\nfa,83,201117\n20201119\nc,87,201117\nca,87,201117\ne,87,201119\no,73,201209,87\noa,62,210216,87\nwva,87,201117,87\ny,20.12,201220,87\nfb,348,211219,96\n20201231\nc,87,201117\nca,87,201117\ne,87,201119\nf,83,201117\nfa,83,201117\ns,14,200916\nsi,14,200916\no,73,201209,87\noa,62,210216,87\nsa,14.0,210417,87\nwva,87,201117,87\ny,20.12,201220,87\nu,15.3,230317,100\nq,11.7,211102,89\nk,3.0,210901,84\nfb,348,211219,96\nia,163,u,92\n20210121\nc,88,210119\nca,88,210119\ne,88,210121\nf,84,201215\nfa,84,201215\no,74,210202,88\noa,63,210416,89\nsa,15.0,210813,90\nwva,88,210119,88\ny,21.1,211231,88\n20210126\nf,85,210126\nfa,85,210126\nk,4.0,250501,123\n20210426\nc,89,210302\nca,89,210302\ne,89,210304\nf,86,210223\nfa,86,210223\ns,14.1,210426\nsi,14.5,210426\no,75,210324,89\nwva,89,210302,89\ny,21.3,210404,89\n20210810\nf,91,210810\nfa,91,210810\n20210902\ne,93,210902\n20210907\nf,92,210907\nfa,92,210907\n20210920\nc,93,210831\nca,93,210831\ns,15,210920\nsi,15,210920\no,79,210914,93\noa,66,211215,94\nsa,17.0,220504,96\nwva,93,210831,93\ny,21.9,210929,93\nq,13.4,230426,98\nia,258,221104,106\n20211005\nf,93,211005\nfa,93,211005\n20211025\ns,15.1,211025\nsi,15.1,211025\n20211102\nf,94,211102\nfa,94,211102\n20211119\nc,96,211115\nca,96,211115\ne,96,211119\no,82,211202,96\noa,67,220131,96\nwva,96,211115,96\ny,22.1,211231,96\n20211207\nf,95,211207\n20211213\ns,15.2,211213\nsi,15.2,211213\n20211231\nc,96,211115\nca,96,211115\ne,96,211119\nf,95,211207\nfa,94,211102\ns,15.2,211213\nsi,15.2,211213\no,82,211202,96\noa,67,220131,96\nsa,17.0,220504,96\nwva,96,211115,96\ny,22.1,211231,96\nu,15.3,230317,100\nq,13.4,230426,98\nk,4.0,250501,123\nfb,348,211219,96\nia,258,221104,106\n20220106\nc,97,220104\nca,97,220104\ne,97,220106\no,83,220119,97\noa,68,220330,99\nsa,18.0,220808,99\nwva,97,220104,97\ny,22.3,220325,98\nfb,399,230204,109\n20220203\nc,98,220201\nca,98,220201\ne,98,220203\nf,96,220111\nfa,96,220111\no,84,220216,98\nwva,98,220201,98\n20220303\nc,99,220301\nca,99,220301\ne,99,220303\no,85,220323,99\nwva,99,220301,99\ny,22.5,220520,100\nq,14.2,231014,109\n20220314\nf,98,220308\nfa,98,220308\ns,15.4,220314\nsi,15.4,220314\n20220428\nc,101,220426\nca,101,220426\ne,101,220428\no,87,220517,101\noa,70,220629,102\nsa,19.0,221101,102\nwva,101,220426,101\ny,22.7,220707,102\nu,17.6,250515,123\n20220503\nf,100,220503\nfa,100,220503\n20220531\nf,101,220531\nfa,101,220531\n20220628\nf,102,220628\nfa,102,220628\n20220726\nf,103,220726\nfa,103,220726\n20220805\nc,104,220802\nca,104,220802\ne,104,220805\no,90,220818,104\noa,71,220916,104\nsa,20.0,230210,106\nwva,104,220802,104\ny,22.8,u,104\n20220823\nf,104,220823\nfa,104,220823\n20220902\nc,105,220902\nca,105,220902\ne,105,220901\ns,15.6,220720\nsi,15.6,220720\no,91,220914,105\noa,72,221021,106\nwva,105,220902,105\ny,22.11,221111,106\n20220912\ns,16,220912\nsi,16,220912\n20221003\nc,106,220927\nca,106,220927\ne,106,221003\no,92,221019,106\nwva,106,220927,106\n20221027\nc,107,221025\nca,107,221025\ne,107,221027\no,93,221117,107\noa,73,230117,108\nsa,21.0,230519,110\nwva,107,221025,107\ny,23.1,230110,108\nia,279,231231,109\n20221115\nf,107,221115\nfa,107,221115\n20221205\nc,108,221129\nca,108,221129\ne,108,221205\no,94,221215,108\nwva,108,221129,108\n20221213\nf,108,221213\nfa,108,221213\n20221231\nc,108,221129\nca,108,221129\ne,108,221205\nf,108,221213\nfa,108,221213\ns,16,220912\nsi,16,220912\no,94,221215,108\noa,73,230117,108\nsa,21.0,230519,110\nwva,108,221129,108\ny,23.1,230110,108\nu,17.6,250515,123\nq,14.2,231014,109\nk,4.0,250501,123\nfb,399,230204,109\nia,279,231231,109\n20230112\nc,109,230110\nca,109,230110\ne,109,230112\no,95,230201,109\noa,74,230313,110\nwva,109,230110,109\ny,23.3,230326,110\n20230214\nf,110,230214\nfa,110,230214\n20230313\nc,111,230307\nca,111,230307\ne,111,230313\no,97,230322,111\noa,75,230517,112\nsa,22.0,230714,111\nwva,111,230301,111\ny,23.5,230519,112\nq,19.1,250708,121\nfb,420,230628,114\nia,288,u,114\n20230314\nf,111,230314\nfa,111,230314\n20230327\ns,16.4,230327\nsi,16.4,230327\n20230411\nf,112,230411\nfa,112,230411\n20230509\nf,113,230509\nfa,113,230509\n20230606\nf,114,230606\nfa,114,230606\n20230704\nc,112,230404\nca,112,230404\ne,112,230406\nf,115,230704\nfa,115,230704\no,98,230420,112\nsa,23.0,231018,115\nwva,112,230404,112\n20230721\nc,115,230718\nca,115,230721\ne,115,230721\no,101,230726,115\noa,77,230831,115\nwva,115,230721,115\ny,23.9,230913,116\nfb,430,230903,116\nia,292,u,115\n20230801\nf,116,230801\nfa,116,230801\n20230915\nc,117,230912\nca,117,230912\ne,117,230915\no,103,231003,117\noa,78,231023,117\nsa,24.0,240125,117\nwva,117,230912,117\ny,23.11,231115,118\nfb,434,231005,117\nia,302,u,117\n20230918\ns,17,230918\nsi,17,230918\n20231013\nc,118,231010\nca,118,231010\ne,118,231013\nf,118,230926\nfa,118,230926\no,104,231023,118\noa,79,231206,119\nsa,25.0,240424,121\nwva,118,231010,118\nfb,437,u,118\nia,306,240117,118\n20231024\nf,119,231024\nfa,119,231024\n20231102\nc,119,231031\nca,119,231031\ne,119,231102\no,105,231114,119\nwva,119,231031,119\ny,24.1,240118,120\nfb,439,231111,119\nia,309,u,119\n20231121\nf,120,231121\nfa,120,231121\n20231205\nca,120,231205\n20231207\nc,120,231205\ne,120,231207\no,106,231219,120\noa,80,240125,120\nwva,120,231205,120\nfb,443,231207,120\nia,311,u,120\n20231211\ns,17.2,231211\nsi,17.2,231211\n20231219\nf,121,231219\nfa,121,231219\n20231231\nc,120,231205\nca,120,231205\ne,120,231207\nf,121,231219\nfa,121,231219\ns,17.2,231211\nsi,17.2,231211\no,106,231219,120\noa,80,240125,120\nsa,25.0,240424,121\nwva,120,231205,120\ny,24.1,240118,120\nu,17.6,250515,123\nq,19.1,250708,121\nk,4.0,250501,123\nfb,443,231207,120\nia,311,u,120\n20240123\nf,122,240123\nfa,122,240123\n20240125\nc,121,240123\nca,121,240123\ne,121,240125\no,107,240207,121\noa,81,240314,122\nwva,121,240123,121\ny,24.4,240327,122\nfb,448,240129,121\nia,317,240203,121\n20240220\nf,123,240220\nfa,123,240220\n20240305\ns,17.4,240305\nsi,17.4,240305\n20240319\nf,124,240319\nfa,124,240319\n20240322\nc,123,240319\nca,123,240319\ne,123,240322\no,109,240327,123\noa,82,240502,124\nsa,27.0,241106,125\nwva,123,240319,123\ny,24.6,240604,124\nfb,465,240707,126\nia,338,240706,126\n20240416\nf,125,240416\nfa,125,240416\n20240418\nc,124,240416\nca,124,240416\ne,124,240418\no,110,240514,124\nwva,124,240416,124\n20240513\ns,17.5,240513\nsi,17.5,240513\n20240514\nf,126,240514\nfa,126,240514\n20240517\nc,125,240514\nca,125,240514\ne,125,240517\no,111,240612,125\noa,83,240625,126\nwva,125,240514,125\ny,24.7,240718,126\n20240611\nf,127,240611\nfa,127,240611\n20240709\nf,128,240709\nfa,128,240709\n20240725\nc,127,240723\nca,127,240723\ne,127,240725\no,113,240822,127\noa,84,240826,127\nsa,28.0,250402,130\nwva,127,240723,127\ny,24.10,241011,128\nfb,474,240730,127\nia,346,240901,127\n20240806\nf,129,240806\nfa,129,240806\n20240903\nf,130,240903\nfa,130,240903\n20240916\ns,18,240916\nsi,18,240916\n20241001\nf,131,241001\nfa,131,241001\n20241017\nc,130,241015\nca,130,241015\ne,130,241017\no,115,241127,130\noa,86,241202,130\nwva,130,241015,130\ny,24.12,241130,130\nfb,486,241018,130\nia,355,241106,130\n20241029\nf,132,241029\nfa,132,241029\n20241211\ns,18.2,241211\nsi,18.2,241211\n20241231\nc,130,241015\nca,130,241015\ne,130,241017\nf,132,241029\nfa,132,241029\ns,18.2,241211\nsi,18.2,241211\no,115,241127,130\noa,86,241202,130\nsa,28.0,250402,130\nwva,130,241015,130\ny,24.12,241130,130\nfb,486,241018,130\nia,355,241106,130\n20250107\nf,134,250107\nfa,134,250107\n20250127\nsi,18.3,250127\n20250304\nf,136,250304\nfa,136,250304\n20250331\ns,18.4,250331\nsi,18.4,250331\n20250401\nc,133,250204\nca,133,250204\ne,133,250206\nf,137,250401\nfa,137,250401\no,118,250415,133\noa,88,250319,134\nsa,29.0,251025,136\nwva,133,250204,133\ny,25.4,250423,134\nfb,503,250312,134\nia,378,250503,135\n20250404\nc,135,250401\nca,135,250401\ne,135,250404\no,120,250702,135\noa,89,250429,135\nwva,135,250401,135\ny,25.6,250904,136\nfb,514,250528,136\n20250429\nf,138,250429\nfa,138,250429\n20250501\nc,136,250429\nca,136,250429\ne,136,250501\no,121,250827,137\noa,90,250618,137\nwva,136,250429,136\nia,381,250619,137\n20250527\nf,139,250527\nfa,139,250527\n20250626\nc,138,250624\nca,138,250624\ne,138,250626\no,122,250911,138\noa,91,250819,139\nwva,138,250624,138\ny,25.8,250830,138\nia,390,250726,138\n20250819\nf,142,250819\nfa,142,250819\n20250905\nc,140,250902\nca,140,250902\ne,140,250905\no,124,251113,140\noa,92,251008,140\nwva,140,250902,140\ny,25.10,251009,140\nia,399,250928,140\n20250915\ns,26,250915\nsi,26,250915\n20250916\nf,143,250916\nfa,143,250916\n20251003\nc,141,250930\nca,141,250930\ne,141,251003\no,125,251204,141\noa,93,251125,142\nwva,141,250930,141\ny,25.12,251207,142\nia,400,251006,141\n20251014\nf,144,251014\nfa,144,251014\n20251111\nf,145,251111\nfa,145,251111\n20251212\nc,143,251202\nca,143,251202\ne,143,251205\nf,146,251209\nfa,146,251209\ns,26.2,251212\nsi,26.2,251212\no,127,260202,143\noa,94,260113,143\nwva,143,251202,143\ny,26.3,260304,144\nia,409,251216,143\n20251231\nc,143,251202\nca,143,251202\ne,143,251205\nf,146,251209\nfa,146,251209\ns,26.2,251212\nsi,26.2,251212\no,127,260202,143\noa,94,260113,143\nwva,143,251202,143\ny,26.3,260304,144\nia,409,251216,143\n20260113\nf,147,260113\nfa,147,260113\n20260211\ns,26.3,260211\nsi,26.3,260211\n20260214\nc,145,260210\nca,145,260210\ne,145,260214\no,129,260318,145\noa,96,260310,145\nwva,145,260210,145\ny,26.4,260425,146\nia,423,260405,146\n20260224\nf,148,260224\nfa,148,260224\n20260313\nc,146,260310\nca,146,260310\ne,146,260313\no,130,260408,146\noa,97,260416,146\nwva,146,260310,146\n20260324\nf,149,260324\nfa,149,260324\ns,26.4,260324\nsi,26.4,260324\n20260410\nc,147,260407\nca,147,260407\ne,147,260410\no,131,260429,147\noa,98,260505,147\nwva,147,260407,147\ny,26.6,260608,148\nia,430,260530,148\n20260505\nca,148,260505\n20260507\nc,148,260505\ne,148,260507\no,132,260528,148\noa,99,260610,148\nwva,148,260505,148\n20260511\ns,26.5,260511\nsi,26.5,260511\n20260519\nf,151,260519\nfa,151,260519\n20260616\nf,152,260616\nfa,152,260616\n20260721\nf,153,260721\nfa,153,260721\nreleases\nc,45,150901\nc,46,151013\nc,47,151201\nc,48,160120\nc,49,160302\nc,50,160413\nc,51,160525\nc,52,160720\nc,54,161012\nc,55,161201\nc,58,170419\nc,59,170605\nc,60,170725\nc,61,170905\nc,63,171206\nc,64,180123\nc,65,180306\nc,67,180529\nc,68,180724\nc,69,180904\nc,70,181016\nc,71,181204\nc,72,190129\nc,73,190312\nc,74,190423\nc,75,190604\nc,77,190910\nc,78,191022\nc,79,191210\nc,83,200519\nc,90,210413\nc,91,210525\nc,92,210720\nc,94,210921\nc,95,211019\nc,100,220329\nc,102,220524\nc,103,220621\nc,110,230207\nc,113,230502\nc,114,230530\nc,116,230815\nc,122,240220\nc,126,240611\nc,128,240820\nc,129,240917\nc,131,241112\nc,132,250114\nc,134,250304\nc,137,250527\nc,139,250805\nc,142,251028\nc,144,260113\nc,149,260602\nc,150,260630\nc,151,260728\nc,152,260825\nc,153,260908\nca,45,150901\nca,46,151014\nca,47,151202\nca,48,160126\nca,49,160309\nca,50,160413\nca,51,160608\nca,52,160727\nca,54,161019\nca,55,161206\nca,58,170425\nca,59,170606\nca,60,170801\nca,61,170905\nca,63,171205\nca,64,180123\nca,65,180306\nca,67,180531\nca,68,180724\nca,69,180904\nca,70,181017\nca,71,181204\nca,72,190129\nca,73,190312\nca,74,190424\nca,75,190604\nca,77,190910\nca,78,191022\nca,79,191217\nca,83,200519\nca,90,210413\nca,91,210525\nca,92,210720\nca,94,210921\nca,95,211019\nca,100,220329\nca,102,220524\nca,103,220621\nca,110,230207\nca,113,230502\nca,114,230530\nca,116,230815\nca,122,240220\nca,126,240611\nca,128,240820\nca,129,240917\nca,131,241112\nca,132,250114\nca,134,250304\nca,137,250527\nca,139,250805\nca,142,251028\nca,144,260113\nca,149,260602\nca,150,260630\nca,151,260728\nca,152,260825\nca,153,260908\ne,83,200521\ne,90,210415\ne,91,210527\ne,92,210722\ne,94,210924\ne,95,211021\ne,100,220401\ne,102,220531\ne,103,220623\ne,110,230209\ne,113,230505\ne,114,230602\ne,116,230821\ne,122,240223\ne,126,240613\ne,128,240822\ne,129,240919\ne,131,241114\ne,132,250117\ne,134,250306\ne,137,250529\ne,139,250807\ne,142,251031\ne,144,260121\ne,149,260604\ne,150,260702\ne,151,260731\ne,152,260827\ne,153,260910\nf,40,150811\nf,42,151103\nf,43,151215\nf,44,160126\nf,46,160426\nf,48,160802\nf,51,170124\nf,54,170613\nf,55,170808\nf,56,170928\nf,57,171114\nf,58,180123\nf,59,180313\nf,61,180626\nf,66,190319\nf,67,190521\nf,69,190903\nf,70,191022\nf,71,191210\nf,72,200107\nf,73,200211\nf,74,200310\nf,75,200407\nf,76,200505\nf,77,200602\nf,80,200825\nf,81,200922\nf,82,201020\nf,87,210323\nf,88,210419\nf,89,210601\nf,90,210713\nf,97,220208\nf,99,220405\nf,105,220920\nf,106,221018\nf,109,230117\nf,117,230829\nf,133,241126\nf,135,250204\nf,140,250624\nf,141,250722\nf,150,260421\nf,154,260818\nf,155,260901\nfa,40,150811\nfa,42,151103\nfa,43,151215\nfa,44,160126\nfa,46,160426\nfa,48,160802\nfa,51,170124\nfa,54,170613\nfa,55,170808\nfa,58,180122\nfa,59,180313\nfa,61,180626\nfa,66,190319\nfa,67,190521\nfa,80,200831\nfa,81,200922\nfa,82,201020\nfa,87,210323\nfa,88,210419\nfa,89,210601\nfa,90,210713\nfa,95,211207\nfa,97,220208\nfa,99,220405\nfa,105,220920\nfa,106,221018\nfa,109,230117\nfa,117,230829\nfa,133,241126\nfa,135,250204\nfa,140,250624\nfa,141,250722\nfa,150,260421\nfa,154,260818\nfa,155,260901\ns,9,150930\ns,9.1,160321\ns,10,160920\ns,10.1,170327\ns,12,180917\ns,15.3,220126\ns,15.5,220516\ns,16.1,221024\ns,16.2,221213\ns,16.3,230123\ns,16.5,230518\ns,16.6,230724\ns,17.1,231025\ns,17.3,240122\ns,17.6,240729\ns,18.1,241028\ns,18.3,250127\ns,18.5,250512\ns,18.6,250729\ns,26.1,251103\ns,26.6,260727\ns,27,u\nsi,9,150916\nsi,9.3,160321\nsi,10,160913\nsi,10.3,170327\nsi,12,180917\nsi,15.3,220126\nsi,15.5,220516\nsi,16.1,221024\nsi,16.2,221213\nsi,16.3,230123\nsi,16.5,230518\nsi,16.6,230724\nsi,17.1,231025\nsi,17.3,240122\nsi,17.6,240729\nsi,18.1,241028\nsi,18.5,250512\nsi,18.6,250729\nsi,26.1,251103\nsi,26.6,260727\nsi,27,u\no,15,130702,28\no,16,130827,29\no,17,131008,30\no,18,131119,31\no,19,140128,32\no,20,140304,33\no,21,140506,34\no,22,140603,35\no,23,140722,36\no,24,140902,37\no,26,141203,39\no,27,150127,40\no,28,150310,41\no,29,150428,42\no,30,150609,43\no,32,150915,45\no,33,151027,46\no,34,151208,47\no,35,160202,48\no,36,160315,49\no,37,160504,50\no,38,160608,51\no,39,160802,52\no,41,161025,54\no,42,161213,55\no,45,170510,58\no,46,170622,59\no,47,170809,60\no,48,170927,61\no,50,180104,63\no,51,180207,64\no,52,180322,65\no,54,180628,67\no,55,180816,68\no,56,180925,69\no,57,181128,70\no,58,190123,71\no,60,190409,73\no,62,190627,75\no,64,191007,77\no,65,191113,78\no,66,200107,79\no,69,200624,83\no,76,210428,90\no,77,210609,91\no,78,210803,92\no,80,211005,94\no,81,211104,95\no,86,220420,100\no,88,220608,102\no,89,220707,103\no,96,230222,110\no,99,230516,113\no,100,230629,114\no,102,230823,116\no,108,240305,122\no,112,240711,126\no,114,240925,128\no,116,250108,131\no,117,250213,132\no,119,250513,134\no,123,251028,139\no,126,260108,142\no,128,260226,144\no,133,260629,149\no,134,u,150\no,135,u,151\noa,15,130708,28\noa,16,130918,29\noa,18,131120,31\noa,19,140128,32\noa,20,140306,33\noa,21,140422,34\noa,22,140617,35\noa,24,140910,37\noa,26,141202,39\noa,27,150129,40\noa,28,150310,41\noa,29,150428,42\noa,30,150610,43\noa,33,151103,46\noa,34,151216,47\noa,35,160204,48\noa,36,160331,49\noa,37,160616,50\noa,42,170121,55\noa,44,171211,60\noa,45,180215,61\noa,48,181108,69\noa,49,181207,70\noa,50,190218,71\noa,51,190321,72\noa,52,190517,73\noa,53,190711,74\noa,55,191203,77\noa,56,200206,78\noa,59,200630,83\noa,64,210525,91\noa,65,211020,92\noa,69,220509,100\noa,76,230626,114\noa,85,241029,128\noa,87,250122,132\noa,95,260211,144\noa,100,260701,149\nsa,1.5,130925,28\nsa,1.6,140411,28\nsa,2.0,141017,34\nsa,2.1,150107,34\nsa,3.2,150824,38\nsa,4.2,160802,44\nsa,5.0,161215,51\nsa,5.2,170421,51\nsa,5.4,170517,51\nsa,6.2,171026,56\nsa,6.4,180219,56\nsa,7.2,180620,59\nsa,7.4,180912,59\nsa,8.2,181221,63\nsa,9.2,190402,67\nsa,9.4,190725,67\nsa,10.0,190822,71\nsa,10.2,191009,71\nsa,11.0,191205,75\nsa,11.2,200322,75\nsa,12.1,200707,79\nsa,13.2,210120,83\nsa,14.2,210625,87\nsa,16.0,211125,92\nsa,16.2,220306,92\nsa,18.1,220909,99\nsa,19.1,221108,102\nsa,26.0,240607,122\nwva,4.4,131209,30\nwva,4.4.3,140602,33\nwva,37,140903,37\nwva,39,141112,39\nwva,40,150121,40\nwva,41,150311,41\nwva,42,150415,42\nwva,43,150527,43\nwva,45,150901,45\nwva,46,151014,46\nwva,47,151202,47\nwva,48,160126,48\nwva,49,160309,49\nwva,50,160413,50\nwva,51,160608,51\nwva,52,160727,52\nwva,54,161019,54\nwva,55,161206,55\nwva,58,170425,58\nwva,59,170606,59\nwva,60,170801,60\nwva,61,170905,61\nwva,63,171205,63\nwva,64,180123,64\nwva,65,180306,65\nwva,67,180531,67\nwva,68,180724,68\nwva,69,180904,69\nwva,70,181017,70\nwva,71,181204,71\nwva,72,190129,72\nwva,73,190312,73\nwva,74,190424,74\nwva,75,190604,75\nwva,77,190910,77\nwva,78,191022,78\nwva,79,191217,79\nwva,83,200519,83\nwva,90,210413,90\nwva,91,210525,91\nwva,92,210720,92\nwva,94,210921,94\nwva,95,211019,95\nwva,100,220329,100\nwva,102,220524,102\nwva,103,220621,103\nwva,110,230207,110\nwva,113,230502,113\nwva,114,230530,114\nwva,116,230815,116\nwva,122,240220,122\nwva,126,240611,126\nwva,128,240820,128\nwva,129,240917,129\nwva,131,241112,131\nwva,132,250114,132\nwva,134,250304,134\nwva,137,250527,137\nwva,139,250805,139\nwva,142,251028,142\nwva,144,260113,144\nwva,149,260602,149\nwva,150,260630,150\nwva,151,260728,151\nwva,152,260825,152\nwva,153,260908,153\ny,1.0,u,25\ny,1.5,u,22\ny,1.6,u,25\ny,1.7,u,25\ny,1.20,u,25\ny,2.5,u,25\ny,3.2,u,25\ny,4.6,u,25\ny,5.3,u,25\ny,5.4,u,25\ny,7.4,u,25\ny,9.6,u,25\ny,10.5,u,25\ny,11.4,u,25\ny,11.5,u,25\ny,12.7,u,25\ny,13.9,u,28\ny,13.10,u,28\ny,13.11,u,28\ny,13.12,u,30\ny,14.2,u,32\ny,14.4,u,33\ny,14.5,u,34\ny,14.7,u,35\ny,14.8,u,36\ny,14.10,u,37\ny,15.2,u,40\ny,15.4,u,41\ny,15.6,u,42\ny,15.7,u,43\ny,15.10,u,45\ny,15.12,u,46\ny,16.2,u,47\ny,16.3,u,47\ny,16.4,u,49\ny,16.6,u,50\ny,16.7,u,51\ny,16.9,u,52\ny,16.11,u,54\ny,17.1,u,55\ny,17.6,u,58\ny,17.7,u,59\ny,17.9,u,60\ny,17.10,u,61\ny,18.1,u,63\ny,18.2,u,63\ny,18.3,u,64\ny,18.4,u,65\ny,18.7,u,67\ny,18.9,u,68\ny,18.10,u,69\ny,18.11,u,70\ny,19.1,u,71\ny,19.3,u,72\ny,19.4,u,73\ny,19.5,u,75\ny,19.6,u,75\ny,19.7,u,75\ny,19.10,u,77\ny,19.11,u,78\ny,19.12,u,78\ny,20.2,u,79\ny,20.6,u,81\ny,20.7,u,83\ny,21.2,u,88\ny,21.5,u,90\ny,21.6,210928,91\ny,21.8,210928,92\ny,21.11,211029,94\ny,22.4,u,92\ny,22.9,220827,104\ny,23.7,230706,114\ny,24.2,240325,120\ny,24.9,241001,126\ny,25.2,250424,132\ny,25.3,250423,132\ny,26.7,260723,149\ny,26.8,260801,150\nu,10.5,u,31\nu,10.7,u,31\nu,10.8,u,31\nu,10.10,u,31\nu,11.0,u,31\nu,11.2,u,40\nu,11.3,u,40\nu,11.4,u,40\nu,11.5,u,40\nu,11.8,u,57\nu,11.9,u,57\nu,12.0,u,57\nu,12.1,u,57\nu,12.2,u,57\nu,12.3,u,57\nu,12.4,u,57\nu,12.5,u,57\nu,12.6,u,57\nu,12.7,u,57\nu,12.8,u,57\nu,12.9,u,57\nu,12.10,u,57\nu,12.11,u,57\nu,12.12,u,57\nu,12.13,u,57\nu,12.14,u,57\nu,13.0,u,57\nu,13.1,u,57\nu,13.2,u,57\nu,13.4,210928,78\nu,13.5,230825,78\nu,13.6,231217,78\nu,13.7,230624,78\nu,13.8,220430,78\nu,13.9,220518,78\nu,15.0,220824,78\nu,15.1,221111,78\nu,15.2,230423,78\nu,15.4,231025,100\nu,15.5,230822,100\nu,16.0,230824,100\nu,16.1,231015,100\nu,16.2,231209,100\nu,16.3,240308,100\nu,16.4,241003,100\nu,16.5,240530,100\nu,16.6,240723,100\nu,17.0,240824,100\nu,17.1,240926,100\nu,17.2,241129,100\nu,17.3,250107,100\nu,17.4,250226,100\nu,17.5,250408,100\nu,17.7,250611,123\nu,17.8,250730,123\nu,18.0,250817,123\nu,18.1,251004,123\nu,18.2,251104,123\nu,18.3,251212,123\nu,18.4,260109,123\nu,18.5,260128,123\nu,18.6,260321,123\nu,18.8,260503,123\nu,18.9,260702,123\nu,18.10,260808,123\nq,6.0,u,37\nq,6.1,u,37\nq,6.2,u,37\nq,6.3,u,37\nq,6.4,u,37\nq,6.6,u,37\nq,6.7,u,37\nq,6.8,u,37\nq,6.9,u,37\nq,7.0,u,37\nq,7.1,u,37\nq,7.2,u,37\nq,7.3,u,37\nq,7.4,u,37\nq,7.5,u,37\nq,7.6,u,37\nq,7.7,u,37\nq,7.8,u,37\nq,7.9,u,37\nq,8.0,u,37\nq,8.2,u,57\nq,8.3,u,57\nq,8.4,u,57\nq,8.5,u,57\nq,8.6,u,57\nq,8.7,u,57\nq,8.8,u,57\nq,8.9,u,57\nq,9.1,u,57\nq,9.7,u,66\nq,9.8,u,66\nq,10.0,u,66\nq,10.1,u,66\nq,10.2,u,66\nq,10.3,u,66\nq,10.4,u,66\nq,10.5,u,66\nq,10.7,200909,66\nq,11.0,u,77\nq,11.2,210130,77\nq,11.3,210331,77\nq,11.9,u,89\nq,12.0,211104,89\nq,12.1,211105,89\nq,12.2,211207,89\nq,12.5,220407,89\nq,12.7,220521,89\nq,12.8,220630,89\nq,12.9,220726,89\nq,13.0,220815,89\nq,13.1,220910,89\nq,13.2,221026,89\nq,13.3,221109,89\nq,13.5,230206,98\nq,13.6,230209,98\nq,13.7,230421,98\nq,13.8,230421,98\nq,14.0,231212,98\nq,14.1,230716,98\nq,14.3,230913,109\nq,14.4,231031,109\nq,14.5,231112,109\nq,14.6,231224,109\nq,14.7,240118,109\nq,14.8,240304,109\nq,14.9,240409,109\nq,15.0,240417,109\nq,15.1,240518,109\nq,15.2,241024,109\nq,15.3,240728,109\nq,15.4,240907,109\nq,15.5,240924,109\nq,15.6,241024,109\nq,15.7,241203,109\nq,15.8,241211,109\nq,15.9,250201,109\nq,19.2,250715,121\nq,19.3,250831,121\nq,19.4,250920,121\nq,19.5,251023,121\nq,19.6,251117,121\nq,19.7,251218,121\nq,19.8,260120,121\nq,19.9,260309,121\nq,20.0,260406,121\nq,20.1,260430,121\nq,20.2,260610,121\nq,20.3,260617,121\nq,20.4,260728,121\nk,1.0,170301,37\nk,2.5,170701,48\nk,3.1,220301,84\nfb,68,u,48\nfb,74,u,50\nfb,75,u,50\nfb,76,u,50\nfb,77,u,50\nfb,78,u,50\nfb,79,u,50\nfb,80,u,51\nfb,81,u,51\nfb,82,u,51\nfb,83,u,51\nfb,84,u,51\nfb,86,u,51\nfb,87,u,52\nfb,88,u,52\nfb,89,u,52\nfb,90,u,52\nfb,91,u,52\nfb,92,u,52\nfb,93,u,52\nfb,94,u,52\nfb,96,u,53\nfb,97,u,53\nfb,98,u,53\nfb,99,u,53\nfb,100,u,54\nfb,101,u,54\nfb,103,u,54\nfb,104,u,54\nfb,105,u,54\nfb,106,u,55\nfb,107,u,55\nfb,108,u,55\nfb,109,u,55\nfb,110,u,55\nfb,111,u,55\nfb,113,u,56\nfb,114,u,56\nfb,115,u,56\nfb,116,u,56\nfb,118,u,57\nfb,119,u,57\nfb,120,u,57\nfb,121,u,57\nfb,122,u,58\nfb,123,u,58\nfb,124,u,58\nfb,125,u,58\nfb,126,u,58\nfb,127,u,58\nfb,128,u,58\nfb,129,u,58\nfb,130,u,59\nfb,131,u,59\nfb,132,u,59\nfb,133,u,59\nfb,134,u,59\nfb,135,u,59\nfb,136,u,59\nfb,137,u,59\nfb,138,u,60\nfb,140,u,60\nfb,142,u,61\nfb,143,u,61\nfb,144,u,61\nfb,145,u,61\nfb,146,u,61\nfb,147,u,61\nfb,148,u,61\nfb,150,u,62\nfb,151,u,62\nfb,152,u,62\nfb,153,u,63\nfb,154,u,63\nfb,155,u,63\nfb,156,u,63\nfb,157,u,64\nfb,158,u,64\nfb,159,u,64\nfb,160,u,64\nfb,161,u,64\nfb,162,u,64\nfb,163,u,65\nfb,164,u,65\nfb,165,u,65\nfb,166,u,65\nfb,167,u,65\nfb,168,u,65\nfb,170,u,66\nfb,171,u,66\nfb,172,u,66\nfb,173,u,66\nfb,174,u,66\nfb,175,u,67\nfb,176,u,67\nfb,177,u,67\nfb,178,u,67\nfb,180,u,67\nfb,181,u,67\nfb,182,u,67\nfb,183,u,68\nfb,184,u,68\nfb,185,u,68\nfb,186,u,68\nfb,187,u,68\nfb,188,u,68\nfb,202,u,71\nfb,227,u,75\nfb,228,u,75\nfb,229,u,75\nfb,230,u,75\nfb,231,u,75\nfb,235,u,76\nfb,236,u,76\nfb,237,u,76\nfb,238,u,76\nfb,240,u,77\nfb,241,u,77\nfb,242,u,77\nfb,243,u,77\nfb,244,u,78\nfb,245,u,78\nfb,246,u,78\nfb,247,u,78\nfb,248,u,78\nfb,249,u,78\nfb,250,u,78\nfb,251,u,79\nfb,252,u,79\nfb,253,u,79\nfb,254,u,79\nfb,255,u,79\nfb,257,u,80\nfb,258,u,80\nfb,259,u,80\nfb,260,u,80\nfb,261,u,80\nfb,262,u,80\nfb,263,u,80\nfb,264,u,80\nfb,265,u,80\nfb,267,u,81\nfb,268,u,81\nfb,269,u,81\nfb,270,u,81\nfb,271,u,81\nfb,272,u,83\nfb,273,u,83\nfb,274,u,83\nfb,275,u,83\nfb,400,230210,109\nfb,436,231013,117\nfb,438,231028,118\nfb,440,231112,119\nfb,441,231120,119\nfb,442,231129,119\nfb,444,231213,120\nfb,445,231221,120\nfb,446,240106,120\nfb,447,240112,120\nfb,449,240202,121\nfb,450,240205,121\nfb,451,240217,121\nfb,452,240225,122\nfb,453,240228,122\nfb,454,240304,122\nfb,466,u,126\nfb,469,u,126\nfb,471,240710,126\nfb,472,240711,126\nfb,475,240801,127\nfb,476,240809,127\nfb,477,240816,127\nfb,478,240821,128\nfb,479,240831,128\nfb,480,240907,128\nfb,481,240914,128\nfb,482,240920,129\nfb,483,240927,129\nfb,484,241004,129\nfb,485,241011,129\nfb,487,241026,130\nfb,488,241102,130\nfb,489,241109,130\nfb,494,241226,131\nfb,497,250126,132\nfb,515,250531,137\nia,24,u,62\nia,25,u,62\nia,26,u,63\nia,27,u,63\nia,28,u,63\nia,29,u,63\nia,30,u,63\nia,31,u,64\nia,32,u,64\nia,33,u,64\nia,34,u,64\nia,35,u,65\nia,36,u,65\nia,37,u,65\nia,38,u,65\nia,39,u,65\nia,40,u,65\nia,41,u,65\nia,43,u,66\nia,44,u,66\nia,45,u,66\nia,46,u,66\nia,47,u,66\nia,48,u,67\nia,49,u,67\nia,50,u,67\nia,51,u,67\nia,52,u,67\nia,53,u,67\nia,54,u,67\nia,55,u,67\nia,56,u,68\nia,57,u,68\nia,58,u,68\nia,59,u,68\nia,60,u,68\nia,61,u,68\nia,65,u,69\nia,66,u,69\nia,68,u,69\nia,72,u,70\nia,74,u,71\nia,75,u,71\nia,79,u,71\nia,81,u,72\nia,82,u,72\nia,83,u,72\nia,84,u,73\nia,86,u,73\nia,95,u,74\nia,97,u,80\nia,98,u,80\nia,103,u,80\nia,104,u,80\nia,117,u,80\nia,118,u,80\nia,119,u,80\nia,120,u,80\nia,121,u,80\nia,127,u,80\nia,128,u,80\nia,129,u,80\nia,130,u,80\nia,131,u,80\nia,132,u,80\nia,133,u,80\nia,134,u,80\nia,135,u,80\nia,136,u,80\nia,138,u,81\nia,139,u,81\nia,140,u,81\nia,141,u,81\nia,142,u,81\nia,143,u,83\nia,144,u,83\nia,145,u,83\nia,146,u,83\nia,164,u,92\nia,230,u,92\nia,259,221104,106\nia,281,u,109\nia,289,231221,114\nia,290,231230,114\nia,295,u,115\nia,296,u,115\nia,297,u,115\nia,298,240111,115\nia,299,u,115\nia,300,u,116\nia,301,240112,116\nia,303,u,117\nia,304,u,117\nia,305,u,117\nia,307,u,118\nia,308,240119,118\nia,310,u,119\nia,312,u,120\nia,313,u,120\nia,314,u,120\nia,315,240119,120\nia,316,240125,120\nia,318,240216,121\nia,320,240304,121\nia,321,240307,122\nia,347,240911,127\nia,349,240920,128\nia,366,u,132\nia,367,250215,132\nia,382,250619,137\nia,383,250618,137\nia,384,250616,137\nia,385,250627,137\nia,387,250709,137\nia,392,250812,138\nia,394,250826,139\nia,395,250913,139\nia,396,250920,139\nia,397,250919,139\nia,401,251008,141\nia,404,251031,141\nia,406,251116,141\nia,407,251123,142\nia,408,251128,142\nia,410,251217,143\nia,411,260107,143\nia,438,260722,150\n".split("\n").forEach(n=>{if(n=n.trim())if("releases"!==n){if(f){const a=n.split(",");if(a.length>=3){const[n,u,o,f]=a,i=[n,u,e(o.trim())];f&&i.push(f.trim()),s[n]||(s[n]=[]),s[n].push(i);}return}if(n.startsWith("20")||n.startsWith("pre_baseline"))return o=e(n),void(u[o]=[]);{const a=n.split(",");if(a.length>=3){const[n,s,f,i]=a,r=[n,s,e(f.trim())];if(i&&r.push(i.trim()),!u[o])throw new Error(`Timeline entry for date ${o} is undefined. This should not happen.`);u[o].push(r);}}}else f=true;});const i={};Object.keys(a).forEach(n=>{var a;i[n]=[...null!==(a=s[n])&&void 0!==a?a:[]];}),Object.keys(u).forEach(n=>{u[n].forEach(n=>{const a=n[0];i[a]&&(i[a].some(a=>a[1]===n[1])||i[a].push(n));});}),Object.keys(i).forEach(a=>{i[a].sort((a,e)=>n(a[1],e[1]));});let r=false;const c=Object.keys(a).filter(n=>void 0===a[n].engine).map(n=>{const{longName:e}=a[n];return {shortName:n,longName:e}}),t=["webview_android","samsunginternet_android","opera_android","opera","ya_android","uc_android","qq_android","kai_os","facebook_android","instagram_android"],b=a=>{const e=[],u=[];return a.forEach(n=>{c.some(a=>a.longName===n.browser)?e.push(n):u.push(n);}),e.sort((a,e)=>a.browsere.browser?1:n(a.version,e.version)),u.sort((a,e)=>{const u=t.indexOf(a.browser),s=t.indexOf(e.browser);return u!==s?u-s:n(a.version,e.version)}),[...e,...u]};let l=false;const w=n=>{if(false===n.includeDownstreamBrowsers&&true===n.includeKaiOS)throw new Error("KaiOS is a downstream browser and can only be included if you include other downstream browsers. Please ensure you use `includeDownstreamBrowsers: true`.")},v=(n,e,u,s,o)=>{var f;const i={browser:e,version:u,release_date:"u"===s?"unknown":s};return o&&(i.engine_version=o,i.engine=null===(f=a[n])||void 0===f?void 0:f.engine),i};function y(e){var s,o,f,t,y,p,d;let g=null!=e?e:{},q={listAllCompatibleVersions:null!==(s=g.listAllCompatibleVersions)&&void 0!==s&&s,includeDownstreamBrowsers:null!==(o=g.includeDownstreamBrowsers)&&void 0!==o&&o,widelyAvailableOnDate:null!==(f=g.widelyAvailableOnDate)&&void 0!==f?f:void 0,targetYear:null!==(t=g.targetYear)&&void 0!==t?t:void 0,includeKaiOS:null!==(y=g.includeKaiOS)&&void 0!==y&&y,overrideLastUpdated:null!==(p=g.overrideLastUpdated)&&void 0!==p?p:void 0,suppressWarnings:null!==(d=g.suppressWarnings)&&void 0!==d&&d},m=new Date;if(q.widelyAvailableOnDate||q.targetYear){if(q.targetYear&&q.widelyAvailableOnDate)throw new Error("You cannot use targetYear and widelyAvailableOnDate at the same time. Please remove one of these options and try again.");q.widelyAvailableOnDate?m=new Date(q.widelyAvailableOnDate):q.targetYear&&(m=new Date(`${q.targetYear}-12-31`));}else m=new Date;if((q.widelyAvailableOnDate||void 0===q.targetYear)&&m.setMonth(m.getMonth()-30),!q.suppressWarnings){if(((n,a)=>{if(r||"undefined"!=typeof process&&process.env&&(process.env.BROWSERSLIST_IGNORE_OLD_DATA||process.env.BASELINE_BROWSER_MAPPING_IGNORE_OLD_DATA))return;const e=new Date;e.setMonth(e.getMonth()-2),n>e&&(null!=a?a:1786199540278)(new Date).getFullYear())throw new Error("There are no browser versions compatible with Baseline in the future")}const h=m{_[n]=void 0;}),Object.keys(u).forEach(n=>{const a=u[n];let e=false;e="pre_baseline"===n||new Date(n)<=m,e&&a.forEach(a=>{const e=a[0];"pre_baseline"!==n&&(_[e]=a);});});const O=[];return Object.keys(a).forEach(e=>{var u,s;const{longName:o}=a[e];if(!q.includeKaiOS&&"k"===e)return;const f=c.some(n=>n.shortName===e);if(q.includeDownstreamBrowsers||f)if(h)if(q.listAllCompatibleVersions){O.push({browser:o,version:"0",release_date:""});(null!==(u=i[e])&&void 0!==u?u:[]).forEach(n=>{O.push(v(e,o,n[1],n[2],n[3]));});}else O.push({browser:o,version:"0",release_date:""});else {const a=_[e];if(!a)return;const u=a[1];if(q.listAllCompatibleVersions){(null!==(s=i[e])&&void 0!==s?s:[]).forEach(a=>{const s=a[1];n(s,u)>=0&&O.push(v(e,o,s,a[2],a[3]));});}else O.push(v(e,o,u,a[2],a[3]));}}),b(O)}dist._resetHasWarned=function(){r=false;},dist.getAllVersions=function(a){var e,u,s,o,f;l=true;let i=null!=a?a:{},r={outputFormat:null!==(e=i.outputFormat)&&void 0!==e?e:"array",includeDownstreamBrowsers:null!==(u=i.includeDownstreamBrowsers)&&void 0!==u&&u,useSupports:null!==(s=i.useSupports)&&void 0!==s&&s,includeKaiOS:null!==(o=i.includeKaiOS)&&void 0!==o&&o,suppressWarnings:null!==(f=i.suppressWarnings)&&void 0!==f&&f};w(r);let t=(new Date).getFullYear()+1;const b=[...Array(t).keys()].slice(2015),v={};b.forEach(n=>{v[n]={},y({targetYear:n,suppressWarnings:r.suppressWarnings}).forEach(a=>{v[n]&&(v[n][a.browser]=a);});});const p=y({suppressWarnings:r.suppressWarnings}),d={};p.forEach(n=>{d[n.browser]=n;});const g=new Date;g.setMonth(g.getMonth()+30);const q=y({widelyAvailableOnDate:g.toISOString().slice(0,10),suppressWarnings:r.suppressWarnings}),m={};q.forEach(n=>{m[n.browser]=n;});const h=y({targetYear:2002,listAllCompatibleVersions:true,suppressWarnings:r.suppressWarnings}),_=[];if(c.map(n=>n.longName).forEach(a=>{var e,u,s,o;let f=h.filter(n=>n.browser==a).sort((a,e)=>n(a.version,e.version)),i=null!==(u=null===(e=d[a])||void 0===e?void 0:e.version)&&void 0!==u?u:"0",c=null!==(o=null===(s=m[a])||void 0===s?void 0:s.version)&&void 0!==o?o:"0";b.forEach(e=>{var u;if(v[e]){let s=(null!==(u=v[e][a])&&void 0!==u?u:{version:"0"}).version,o=f.findIndex(a=>0===n(a.version,s));(e===t-1?f:f.slice(0,o)).forEach(a=>{let u=n(a.version,i)>=0,s=n(a.version,c)>=0,o=Object.assign(Object.assign({},a),{year:e<=2015?"pre_baseline":e-1});r.useSupports?(u&&(o.supports="widely"),s&&(o.supports="newly")):o=Object.assign(Object.assign({},o),{wa_compatible:u}),_.push(o);}),f=f.slice(o,f.length);}});}),r.includeDownstreamBrowsers){y({targetYear:2002,listAllCompatibleVersions:true,includeDownstreamBrowsers:true,includeKaiOS:r.includeKaiOS,suppressWarnings:r.suppressWarnings}).filter(n=>!c.map(n=>n.longName).includes(n.browser)).forEach(n=>{const a="Gecko"===n.engine?"firefox":"chrome";let e=_.find(e=>e.browser===a&&e.version===n.engine_version);e&&(r.useSupports?_.push(Object.assign(Object.assign({},n),{year:e.year,supports:e.supports})):_.push(Object.assign(Object.assign({},n),{year:e.year,wa_compatible:e.wa_compatible})));});}if(_.sort((a,e)=>{if("pre_baseline"===a.year&&"pre_baseline"!==e.year)return -1;if("pre_baseline"===e.year&&"pre_baseline"!==a.year)return 1;if("pre_baseline"!==a.year&&"pre_baseline"!==e.year){if(a.yeare.year)return 1}return a.browsere.browser?1:n(a.version,e.version)}),"object"===r.outputFormat){const n={};return _.forEach(a=>{n[a.browser]||(n[a.browser]={});let e={year:a.year,release_date:a.release_date,engine:a.engine,engine_version:a.engine_version};n[a.browser][a.version]=r.useSupports?a.supports?Object.assign(Object.assign({},e),{supports:a.supports}):e:Object.assign(Object.assign({},e),{wa_compatible:a.wa_compatible});}),null!=n?n:{}}if("csv"===r.outputFormat){let n=`"browser","version","year","${r.useSupports?"supports":"wa_compatible"}","release_date","engine","engine_version"`;return _.forEach(a=>{var e,u,s,o;let f={browser:a.browser,version:a.version,year:a.year,release_date:null!==(e=a.release_date)&&void 0!==e?e:"NULL",engine:null!==(u=a.engine)&&void 0!==u?u:"NULL",engine_version:null!==(s=a.engine_version)&&void 0!==s?s:"NULL"};f=r.useSupports?Object.assign(Object.assign({},f),{supports:null!==(o=a.supports)&&void 0!==o?o:""}):Object.assign(Object.assign({},f),{wa_compatible:a.wa_compatible}),n+=`\n"${f.browser}","${f.version}","${f.year}","${r.useSupports?f.supports:f.wa_compatible}","${f.release_date}","${f.engine}","${f.engine_version}"`;}),n}return _},dist.getCompatibleVersions=y,dist.getTimeline=function(n){var e,s,o,f;const i=null!=n?n:{},r=null!==(e=i.groupBy)&&void 0!==e?e:"date",t=null!==(s=i.listAllBrowsers)&&void 0!==s&&s,l=null!==(o=i.includeDownstreamBrowsers)&&void 0!==o&&o,w=null!==(f=i.includeKaiOS)&&void 0!==f&&f;if(false===l&&true===w)throw new Error("KaiOS is a downstream browser and can only be included if you include other downstream browsers. Please ensure you use `includeDownstreamBrowsers: true`.");const y={},p=[];if(Object.keys(u).forEach(n=>{const e=u[n];if("pre_baseline"===n)return;const s=new Set;e.forEach(n=>{const a=n[0],e=y[a],u=n[1];e&&e[1]===u||s.add(a),y[a]=n;});const o=[];if(Object.keys(a).forEach(n=>{const{longName:e}=a[n];if(!w&&"k"===n)return;const u=c.some(a=>a.shortName===n);if(!l&&!u)return;const f=s.has(n);if(t||f){const a=y[n];a&&o.push(v(n,e,a[1],a[2],a[3]));}}),o.length>0){const a=b(o);p.push({date:n,browsers:a});}}),"browser"===r){const n={};return Object.keys(a).forEach(e=>{const{longName:u}=a[e];if(!w&&"k"===e)return;const s=c.some(n=>n.shortName===e);(l||s)&&(n[u]=[]);}),p.forEach(a=>{a.browsers.forEach(e=>{const{browser:u}=e,s=function(n,a){var e={};for(var u in n)Object.prototype.hasOwnProperty.call(n,u)&&a.indexOf(u)<0&&(e[u]=n[u]);if(null!=n&&"function"==typeof Object.getOwnPropertySymbols){var s=0;for(u=Object.getOwnPropertySymbols(n);s{if(n===a)return 0;const[e=0,u=0]=n.split(".",2).map(Number),[s=0,o=0]=a.split(".",2).map(Number);if(isNaN(e)||isNaN(u))throw new Error(`Invalid version: ${n}`);if(isNaN(s)||isNaN(o))throw new Error(`Invalid version: ${a}`);return e!==s?e>s?1:-1:u!==o?u>o?1:-1:0},a={c:{longName:"chrome"},ca:{longName:"chrome_android"},e:{longName:"edge"},f:{longName:"firefox"},fa:{longName:"firefox_android"},s:{longName:"safari"},si:{longName:"safari_ios"},o:{longName:"opera",engine:"Blink"},oa:{longName:"opera_android",engine:"Blink"},sa:{longName:"samsunginternet_android",engine:"Blink"},wva:{longName:"webview_android",engine:"Blink"},y:{longName:"ya_android",engine:"Blink"},u:{longName:"uc_android",engine:"Blink"},q:{longName:"qq_android",engine:"Blink"},k:{longName:"kai_os",engine:"Gecko"},fb:{longName:"facebook_android",engine:"Blink"},ia:{longName:"instagram_android",engine:"Blink"}},e=n=>6===n.length&&/^\d{6}$/.test(n)?`20${n.slice(0,2)}-${n.slice(2,4)}-${n.slice(4,6)}`:8===n.length&&/^\d{8}$/.test(n)?`${n.slice(0,4)}-${n.slice(4,6)}-${n.slice(6,8)}`:n,u={},s={};let o="",f=false;"pre_baseline\nc,1,081211\nc,2,090521\nc,3,090915\nc,4,100125\nc,5,100525\nc,6,100902\nc,7,101019\nc,8,101202\nc,9,110203\nc,10,110308\nc,11,110427\nc,12,110607\nc,13,110802\nc,14,110916\nc,15,111025\nc,16,111213\nc,17,120208\nc,18,120328\nc,19,120515\nc,20,120626\nc,21,120731\nc,22,120925\nc,23,121106\nc,24,130110\nc,25,130221\nc,26,130326\nc,27,130521\nc,28,130709\nc,29,130820\nc,30,131001\nc,31,131112\nc,32,140114\nc,33,140220\nc,34,140408\nc,35,140520\nc,36,140716\nc,37,140826\nc,38,141007\nc,39,141118\nc,40,150121\nc,41,150303\nc,42,150414\nc,43,150519\nc,44,150721\nca,18,120627\nca,25,130227\nca,26,130403\nca,27,130522\nca,28,130710\nca,29,130821\nca,30,131002\nca,31,131114\nca,32,140115\nca,33,140226\nca,34,140402\nca,35,140520\nca,36,140716\nca,37,140903\nca,38,141008\nca,39,141112\nca,40,150121\nca,41,150311\nca,42,150415\nca,43,150527\nf,1,041109\nf,1.5,051129\nf,2,061024\nf,3,080617\nf,3.5,090630\nf,3.6,100121\nf,4,110322\nf,5,110621\nf,6,110816\nf,7,110927\nf,8,111108\nf,9,111220\nf,10,120131\nf,11,120313\nf,12,120424\nf,13,120605\nf,14,120717\nf,15,120828\nf,16,121009\nf,17,121120\nf,18,130108\nf,19,130219\nf,20,130402\nf,21,130514\nf,22,130625\nf,23,130806\nf,24,130917\nf,25,131029\nf,26,131210\nf,27,140204\nf,28,140318\nf,29,140429\nf,30,140610\nf,31,140722\nf,32,140902\nf,33,141014\nf,34,141201\nf,35,150113\nf,36,150224\nf,37,150331\nf,38,150512\nf,39,150702\nfa,4,110329\nfa,5,110621\nfa,6,110816\nfa,7,110927\nfa,8,111108\nfa,9,111221\nfa,10,120131\nfa,14,120626\nfa,15,120828\nfa,16,121009\nfa,17,121120\nfa,18,130108\nfa,19,130219\nfa,20,130402\nfa,21,130514\nfa,22,130625\nfa,23,130806\nfa,24,130917\nfa,25,131029\nfa,26,131210\nfa,27,140204\nfa,28,140318\nfa,29,140429\nfa,30,140610\nfa,31,140722\nfa,32,140902\nfa,33,141014\nfa,34,141201\nfa,35,150113\nfa,36,150227\nfa,37,150331\nfa,38,150512\nfa,39,150702\ns,1,030623\ns,1.1,031024\ns,1.2,040202\ns,1.3,050415\ns,2,050429\ns,3,071026\ns,3.1,080318\ns,4,090608\ns,5,100607\ns,5.1,110720\ns,6,120725\ns,7,131022\ns,8,141016\nsi,1,070629\nsi,2,080711\nsi,3,090617\nsi,3.2,100403\nsi,4,100621\nsi,4.2,101122\nsi,5,111012\nsi,6,120910\nsi,7,130918\nsi,8,140917\n20150729\nc,38,141007\nca,38,141008\ne,12,150729\nf,38,150512\nfa,38,150512\ns,11,170919\nsi,11,170919\no,25,141015,38\noa,25,141016,38\nsa,3.0,150410,38\nwva,38,141008,38\ny,14.12,u,38\nu,11.1,u,40\nq,8.1,u,57\nk,2.0,170701,48\nfb,66,u,48\nia,23,u,62\n20150922\nf,41,150922\nfa,41,150922\n20150930\nc,44,150721\nca,44,150729\no,31,150804,44\noa,32,150923,45\nsa,4.0,160311,44\nwva,44,150729,44\ny,15.9,u,44\nu,11.6,u,57\n20151112\ne,13,151112\n20151231\nc,44,150721\nca,44,150729\ne,13,151112\nf,41,150922\nfa,41,150922\ns,11,170919\nsi,11,170919\no,31,150804,44\noa,32,150923,45\nsa,4.0,160311,44\nwva,44,150729,44\ny,15.9,u,44\nu,11.6,u,57\nq,8.1,u,57\nk,2.0,170701,48\nfb,66,u,48\nia,23,u,62\n20160308\nf,45,160308\nfa,45,160308\n20160607\nf,47,160607\nfa,47,160607\n20160802\ne,14,160802\n20160907\nc,53,160831\nca,53,160907\no,40,160920,53\noa,41,161025,54\nsa,6.0,170823,56\nwva,53,160907,53\ny,16.10,u,53\nfb,95,u,53\n20160920\nf,49,160920\nfa,49,160920\nk,3.0,210901,84\n20161231\nc,53,160831\nca,53,160907\ne,14,160802\nf,49,160920\nfa,49,160920\ns,11,170919\nsi,11,170919\no,40,160920,53\noa,41,161025,54\nsa,6.0,170823,56\nwva,53,160907,53\ny,16.10,u,53\nu,11.6,u,57\nq,8.1,u,57\nk,3.0,210901,84\nfb,95,u,53\nia,23,u,62\n20170201\nc,56,170125\nca,56,170201\nf,50,161115\nfa,50,161115\no,43,170207,56\noa,43,170927,59\nwva,56,170201,56\ny,17.3,u,56\nfb,112,u,56\n20170307\nf,52,170307\nfa,52,170307\n20170316\nca,57,170316\n20170405\nc,57,170309\ne,15,170405\no,44,170321,57\nsa,7.0,180316,59\nwva,57,170316,57\ny,17.4,u,57\nfb,117,u,57\n20170419\nf,53,170419\nfa,53,170419\n20170928\nfa,56,170928\n20171017\ne,16,171017\n20171024\nc,62,171017\nca,62,171024\no,49,171108,62\noa,46,180514,63\nsa,8.0,180718,63\nwva,62,171024,62\ny,17.11,u,62\nu,13.3,200909,78\nq,9.6,u,66\nfb,149,u,62\n20171128\nfa,57,171128\n20171231\nc,62,171017\nca,62,171024\ne,16,171017\nf,53,170419\nfa,57,171128\ns,11,170919\nsi,11,170919\no,49,171108,62\noa,46,180514,63\nsa,8.0,180718,63\nwva,62,171024,62\ny,17.11,u,62\nu,13.3,200909,78\nq,9.6,u,66\nk,3.0,210901,84\nfb,149,u,62\nia,23,u,62\n20180412\ns,11.1,180412\nsi,11.3,180329\n20180430\ne,17,180430\n20180509\nc,66,180417\nca,66,180417\nf,60,180509\nfa,60,180509\no,53,180510,66\noa,47,180723,66\nsa,9.0,180915,67\nwva,66,180417,66\ny,18.6,u,66\nfb,169,u,66\nia,42,u,66\n20180905\nf,62,180905\nfa,62,180905\n20181002\ne,18,181002\n20181023\nf,63,181023\nfa,63,181023\n20181211\nf,64,181211\nfa,64,181211\n20181231\nc,66,180417\nca,66,180417\ne,18,181002\nf,64,181211\nfa,64,181211\ns,11.1,180412\nsi,11.3,180329\no,53,180510,66\noa,47,180723,66\nsa,9.0,180915,67\nwva,66,180417,66\ny,18.6,u,66\nu,13.3,200909,78\nq,9.6,u,66\nk,3.0,210901,84\nfb,169,u,66\nia,42,u,66\n20190129\nf,65,190129\nfa,65,190129\n20190325\ns,12.1,190325\nsi,12.2,190325\n20190919\ns,13,190919\nsi,13,190919\n20191231\nc,66,180417\nca,66,180417\ne,18,181002\nf,65,190129\nfa,65,190129\ns,13,190919\nsi,13,190919\no,53,180510,66\noa,47,180723,66\nsa,9.0,180915,67\nwva,66,180417,66\ny,18.6,u,66\nu,13.3,200909,78\nq,9.6,u,66\nk,3.0,210901,84\nfb,169,u,66\nia,42,u,66\n20200115\nc,76,190730\nca,76,190730\ne,79,200115\nf,68,190709\nfa,68,190709\no,63,190820,76\noa,54,191018,76\nsa,12.0,200619,79\nwva,76,190730,76\ny,19.9,u,76\nq,10.9,201122,77\nfb,233,u,76\nia,96,u,80\n20200204\nc,80,200204\nca,80,200204\no,67,200303,80\noa,57,200330,80\nsa,13.0,201202,83\nwva,80,200204,80\ny,20.3,u,80\nu,15.3,230317,100\nq,11.7,211102,89\nfb,256,u,80\n20200207\ne,80,200207\n20200324\ns,13.1,200324\nsi,13.4,200324\n20200413\nc,81,200407\nca,81,200407\ne,81,200413\no,68,200422,81\noa,58,200513,81\nwva,81,200407,81\ny,20.4,u,81\nfb,266,u,81\nia,137,u,81\n20200727\nc,84,200727\nca,84,200727\ne,84,200716\no,70,200727,84\noa,60,200923,85\nsa,14.0,210417,87\nwva,84,200727,84\ny,20.8,200902,84\nfb,297,201202,86\nia,153,u,84\n20200728\nf,78,200630\nfa,79,200728\n20200827\nc,85,200825\nca,85,200825\ne,85,200827\no,71,200915,85\nwva,85,200825,85\ny,20.9,200927,85\nia,163,u,92\n20200916\nf,79,200728\ns,14,200916\nsi,14,200916\n20201020\nc,86,201020\nca,86,201020\ne,86,201009\no,72,201021,86\noa,61,201207,86\nwva,86,201020,86\ny,20.11,201111,86\n20201117\nf,83,201117\nfa,83,201117\n20201119\nc,87,201117\nca,87,201117\ne,87,201119\no,73,201209,87\noa,62,210216,87\nwva,87,201117,87\ny,20.12,201220,87\nfb,348,211219,96\n20201231\nc,87,201117\nca,87,201117\ne,87,201119\nf,83,201117\nfa,83,201117\ns,14,200916\nsi,14,200916\no,73,201209,87\noa,62,210216,87\nsa,14.0,210417,87\nwva,87,201117,87\ny,20.12,201220,87\nu,15.3,230317,100\nq,11.7,211102,89\nk,3.0,210901,84\nfb,348,211219,96\nia,163,u,92\n20210121\nc,88,210119\nca,88,210119\ne,88,210121\nf,84,201215\nfa,84,201215\no,74,210202,88\noa,63,210416,89\nsa,15.0,210813,90\nwva,88,210119,88\ny,21.1,211231,88\n20210126\nf,85,210126\nfa,85,210126\nk,4.0,250501,123\n20210426\nc,89,210302\nca,89,210302\ne,89,210304\nf,86,210223\nfa,86,210223\ns,14.1,210426\nsi,14.5,210426\no,75,210324,89\nwva,89,210302,89\ny,21.3,210404,89\n20210810\nf,91,210810\nfa,91,210810\n20210902\ne,93,210902\n20210907\nf,92,210907\nfa,92,210907\n20210920\nc,93,210831\nca,93,210831\ns,15,210920\nsi,15,210920\no,79,210914,93\noa,66,211215,94\nsa,17.0,220504,96\nwva,93,210831,93\ny,21.9,210929,93\nq,13.4,230426,98\nia,258,221104,106\n20211005\nf,93,211005\nfa,93,211005\n20211025\ns,15.1,211025\nsi,15.1,211025\n20211102\nf,94,211102\nfa,94,211102\n20211119\nc,96,211115\nca,96,211115\ne,96,211119\no,82,211202,96\noa,67,220131,96\nwva,96,211115,96\ny,22.1,211231,96\n20211207\nf,95,211207\n20211213\ns,15.2,211213\nsi,15.2,211213\n20211231\nc,96,211115\nca,96,211115\ne,96,211119\nf,95,211207\nfa,94,211102\ns,15.2,211213\nsi,15.2,211213\no,82,211202,96\noa,67,220131,96\nsa,17.0,220504,96\nwva,96,211115,96\ny,22.1,211231,96\nu,15.3,230317,100\nq,13.4,230426,98\nk,4.0,250501,123\nfb,348,211219,96\nia,258,221104,106\n20220106\nc,97,220104\nca,97,220104\ne,97,220106\no,83,220119,97\noa,68,220330,99\nsa,18.0,220808,99\nwva,97,220104,97\ny,22.3,220325,98\nfb,399,230204,109\n20220203\nc,98,220201\nca,98,220201\ne,98,220203\nf,96,220111\nfa,96,220111\no,84,220216,98\nwva,98,220201,98\n20220303\nc,99,220301\nca,99,220301\ne,99,220303\no,85,220323,99\nwva,99,220301,99\ny,22.5,220520,100\nq,14.2,231014,109\n20220314\nf,98,220308\nfa,98,220308\ns,15.4,220314\nsi,15.4,220314\n20220428\nc,101,220426\nca,101,220426\ne,101,220428\no,87,220517,101\noa,70,220629,102\nsa,19.0,221101,102\nwva,101,220426,101\ny,22.7,220707,102\nu,17.6,250515,123\n20220503\nf,100,220503\nfa,100,220503\n20220531\nf,101,220531\nfa,101,220531\n20220628\nf,102,220628\nfa,102,220628\n20220726\nf,103,220726\nfa,103,220726\n20220805\nc,104,220802\nca,104,220802\ne,104,220805\no,90,220818,104\noa,71,220916,104\nsa,20.0,230210,106\nwva,104,220802,104\ny,22.8,u,104\n20220823\nf,104,220823\nfa,104,220823\n20220902\nc,105,220902\nca,105,220902\ne,105,220901\ns,15.6,220720\nsi,15.6,220720\no,91,220914,105\noa,72,221021,106\nwva,105,220902,105\ny,22.11,221111,106\n20220912\ns,16,220912\nsi,16,220912\n20221003\nc,106,220927\nca,106,220927\ne,106,221003\no,92,221019,106\nwva,106,220927,106\n20221027\nc,107,221025\nca,107,221025\ne,107,221027\no,93,221117,107\noa,73,230117,108\nsa,21.0,230519,110\nwva,107,221025,107\ny,23.1,230110,108\nia,279,231231,109\n20221115\nf,107,221115\nfa,107,221115\n20221205\nc,108,221129\nca,108,221129\ne,108,221205\no,94,221215,108\nwva,108,221129,108\n20221213\nf,108,221213\nfa,108,221213\n20221231\nc,108,221129\nca,108,221129\ne,108,221205\nf,108,221213\nfa,108,221213\ns,16,220912\nsi,16,220912\no,94,221215,108\noa,73,230117,108\nsa,21.0,230519,110\nwva,108,221129,108\ny,23.1,230110,108\nu,17.6,250515,123\nq,14.2,231014,109\nk,4.0,250501,123\nfb,399,230204,109\nia,279,231231,109\n20230112\nc,109,230110\nca,109,230110\ne,109,230112\no,95,230201,109\noa,74,230313,110\nwva,109,230110,109\ny,23.3,230326,110\n20230214\nf,110,230214\nfa,110,230214\n20230313\nc,111,230307\nca,111,230307\ne,111,230313\no,97,230322,111\noa,75,230517,112\nsa,22.0,230714,111\nwva,111,230301,111\ny,23.5,230519,112\nq,19.1,250708,121\nfb,420,230628,114\nia,288,u,114\n20230314\nf,111,230314\nfa,111,230314\n20230327\ns,16.4,230327\nsi,16.4,230327\n20230411\nf,112,230411\nfa,112,230411\n20230509\nf,113,230509\nfa,113,230509\n20230606\nf,114,230606\nfa,114,230606\n20230704\nc,112,230404\nca,112,230404\ne,112,230406\nf,115,230704\nfa,115,230704\no,98,230420,112\nsa,23.0,231018,115\nwva,112,230404,112\n20230721\nc,115,230718\nca,115,230721\ne,115,230721\no,101,230726,115\noa,77,230831,115\nwva,115,230721,115\ny,23.9,230913,116\nfb,430,230903,116\nia,292,u,115\n20230801\nf,116,230801\nfa,116,230801\n20230915\nc,117,230912\nca,117,230912\ne,117,230915\no,103,231003,117\noa,78,231023,117\nsa,24.0,240125,117\nwva,117,230912,117\ny,23.11,231115,118\nfb,434,231005,117\nia,302,u,117\n20230918\ns,17,230918\nsi,17,230918\n20231013\nc,118,231010\nca,118,231010\ne,118,231013\nf,118,230926\nfa,118,230926\no,104,231023,118\noa,79,231206,119\nsa,25.0,240424,121\nwva,118,231010,118\nfb,437,u,118\nia,306,240117,118\n20231024\nf,119,231024\nfa,119,231024\n20231102\nc,119,231031\nca,119,231031\ne,119,231102\no,105,231114,119\nwva,119,231031,119\ny,24.1,240118,120\nfb,439,231111,119\nia,309,u,119\n20231121\nf,120,231121\nfa,120,231121\n20231205\nca,120,231205\n20231207\nc,120,231205\ne,120,231207\no,106,231219,120\noa,80,240125,120\nwva,120,231205,120\nfb,443,231207,120\nia,311,u,120\n20231211\ns,17.2,231211\nsi,17.2,231211\n20231219\nf,121,231219\nfa,121,231219\n20231231\nc,120,231205\nca,120,231205\ne,120,231207\nf,121,231219\nfa,121,231219\ns,17.2,231211\nsi,17.2,231211\no,106,231219,120\noa,80,240125,120\nsa,25.0,240424,121\nwva,120,231205,120\ny,24.1,240118,120\nu,17.6,250515,123\nq,19.1,250708,121\nk,4.0,250501,123\nfb,443,231207,120\nia,311,u,120\n20240123\nf,122,240123\nfa,122,240123\n20240125\nc,121,240123\nca,121,240123\ne,121,240125\no,107,240207,121\noa,81,240314,122\nwva,121,240123,121\ny,24.4,240327,122\nfb,448,240129,121\nia,317,240203,121\n20240220\nf,123,240220\nfa,123,240220\n20240305\ns,17.4,240305\nsi,17.4,240305\n20240319\nf,124,240319\nfa,124,240319\n20240322\nc,123,240319\nca,123,240319\ne,123,240322\no,109,240327,123\noa,82,240502,124\nsa,27.0,241106,125\nwva,123,240319,123\ny,24.6,240604,124\nfb,465,240707,126\nia,338,240706,126\n20240416\nf,125,240416\nfa,125,240416\n20240418\nc,124,240416\nca,124,240416\ne,124,240418\no,110,240514,124\nwva,124,240416,124\n20240513\ns,17.5,240513\nsi,17.5,240513\n20240514\nf,126,240514\nfa,126,240514\n20240517\nc,125,240514\nca,125,240514\ne,125,240517\no,111,240612,125\noa,83,240625,126\nwva,125,240514,125\ny,24.7,240718,126\n20240611\nf,127,240611\nfa,127,240611\n20240709\nf,128,240709\nfa,128,240709\n20240725\nc,127,240723\nca,127,240723\ne,127,240725\no,113,240822,127\noa,84,240826,127\nsa,28.0,250402,130\nwva,127,240723,127\ny,24.10,241011,128\nfb,474,240730,127\nia,346,240901,127\n20240806\nf,129,240806\nfa,129,240806\n20240903\nf,130,240903\nfa,130,240903\n20240916\ns,18,240916\nsi,18,240916\n20241001\nf,131,241001\nfa,131,241001\n20241017\nc,130,241015\nca,130,241015\ne,130,241017\no,115,241127,130\noa,86,241202,130\nwva,130,241015,130\ny,24.12,241130,130\nfb,486,241018,130\nia,355,241106,130\n20241029\nf,132,241029\nfa,132,241029\n20241211\ns,18.2,241211\nsi,18.2,241211\n20241231\nc,130,241015\nca,130,241015\ne,130,241017\nf,132,241029\nfa,132,241029\ns,18.2,241211\nsi,18.2,241211\no,115,241127,130\noa,86,241202,130\nsa,28.0,250402,130\nwva,130,241015,130\ny,24.12,241130,130\nfb,486,241018,130\nia,355,241106,130\n20250107\nf,134,250107\nfa,134,250107\n20250127\nsi,18.3,250127\n20250304\nf,136,250304\nfa,136,250304\n20250331\ns,18.4,250331\nsi,18.4,250331\n20250401\nc,133,250204\nca,133,250204\ne,133,250206\nf,137,250401\nfa,137,250401\no,118,250415,133\noa,88,250319,134\nsa,29.0,251025,136\nwva,133,250204,133\ny,25.4,250423,134\nfb,503,250312,134\nia,378,250503,135\n20250404\nc,135,250401\nca,135,250401\ne,135,250404\no,120,250702,135\noa,89,250429,135\nwva,135,250401,135\ny,25.6,250904,136\nfb,514,250528,136\n20250429\nf,138,250429\nfa,138,250429\n20250501\nc,136,250429\nca,136,250429\ne,136,250501\no,121,250827,137\noa,90,250618,137\nwva,136,250429,136\nia,381,250619,137\n20250512\nsi,18.5,250512\n20250527\nf,139,250527\nfa,139,250527\n20250626\nc,138,250624\nca,138,250624\ne,138,250626\no,122,250911,138\noa,91,250819,139\nwva,138,250624,138\ny,25.8,250830,138\nia,390,250726,138\n20250819\nf,142,250819\nfa,142,250819\n20250905\nc,140,250902\nca,140,250902\ne,140,250905\no,124,251113,140\noa,92,251008,140\nwva,140,250902,140\ny,25.10,251009,140\nia,399,250928,140\n20250915\ns,26,250915\nsi,26,250915\n20250916\nf,143,250916\nfa,143,250916\n20251003\nc,141,250930\nca,141,250930\ne,141,251003\no,125,251204,141\noa,93,251125,142\nwva,141,250930,141\ny,25.12,251207,142\nia,400,251006,141\n20251014\nf,144,251014\nfa,144,251014\n20251111\nf,145,251111\nfa,145,251111\n20251212\ns,26.2,251212\nsi,26.2,251212\n20251231\nc,141,250930\nca,141,250930\ne,141,251003\nf,145,251111\nfa,145,251111\ns,26.2,251212\nsi,26.2,251212\no,125,251204,141\noa,93,251125,142\nwva,141,250930,141\ny,25.12,251207,142\nia,400,251006,141\n20260113\nf,147,260113\nfa,147,260113\n20260211\ns,26.3,260211\nsi,26.3,260211\n20260214\nc,145,260210\nca,145,260210\ne,145,260214\no,129,260318,145\noa,96,260310,145\nwva,145,260210,145\ny,26.4,260425,146\nia,423,260405,146\n20260224\nf,148,260224\nfa,148,260224\n20260313\nc,146,260310\nca,146,260310\ne,146,260313\no,130,260408,146\noa,97,260416,146\nwva,146,260310,146\n20260324\nf,149,260324\nfa,149,260324\ns,26.4,260324\nsi,26.4,260324\n20260410\nc,147,260407\nca,147,260407\ne,147,260410\no,131,260429,147\noa,98,260505,147\nwva,147,260407,147\ny,26.6,260608,148\nia,430,260530,148\n20260505\nca,148,260505\n20260507\nc,148,260505\ne,148,260507\no,132,260528,148\noa,99,260610,148\nwva,148,260505,148\n20260511\ns,26.5,260511\nsi,26.5,260511\n20260519\nf,151,260519\nfa,151,260519\n20260616\nf,152,260616\nfa,152,260616\n20260721\nf,153,260721\nfa,153,260721\nreleases\nc,45,150901\nc,46,151013\nc,47,151201\nc,48,160120\nc,49,160302\nc,50,160413\nc,51,160525\nc,52,160720\nc,54,161012\nc,55,161201\nc,58,170419\nc,59,170605\nc,60,170725\nc,61,170905\nc,63,171206\nc,64,180123\nc,65,180306\nc,67,180529\nc,68,180724\nc,69,180904\nc,70,181016\nc,71,181204\nc,72,190129\nc,73,190312\nc,74,190423\nc,75,190604\nc,77,190910\nc,78,191022\nc,79,191210\nc,83,200519\nc,90,210413\nc,91,210525\nc,92,210720\nc,94,210921\nc,95,211019\nc,100,220329\nc,102,220524\nc,103,220621\nc,110,230207\nc,113,230502\nc,114,230530\nc,116,230815\nc,122,240220\nc,126,240611\nc,128,240820\nc,129,240917\nc,131,241112\nc,132,250114\nc,134,250304\nc,137,250527\nc,139,250805\nc,142,251028\nc,143,251202\nc,144,260113\nc,149,260602\nc,150,260630\nc,151,260728\nc,152,260825\nc,153,260908\nca,45,150901\nca,46,151014\nca,47,151202\nca,48,160126\nca,49,160309\nca,50,160413\nca,51,160608\nca,52,160727\nca,54,161019\nca,55,161206\nca,58,170425\nca,59,170606\nca,60,170801\nca,61,170905\nca,63,171205\nca,64,180123\nca,65,180306\nca,67,180531\nca,68,180724\nca,69,180904\nca,70,181017\nca,71,181204\nca,72,190129\nca,73,190312\nca,74,190424\nca,75,190604\nca,77,190910\nca,78,191022\nca,79,191217\nca,83,200519\nca,90,210413\nca,91,210525\nca,92,210720\nca,94,210921\nca,95,211019\nca,100,220329\nca,102,220524\nca,103,220621\nca,110,230207\nca,113,230502\nca,114,230530\nca,116,230815\nca,122,240220\nca,126,240611\nca,128,240820\nca,129,240917\nca,131,241112\nca,132,250114\nca,134,250304\nca,137,250527\nca,139,250805\nca,142,251028\nca,143,251202\nca,144,260113\nca,149,260602\nca,150,260630\nca,151,260728\nca,152,260825\nca,153,260908\ne,83,200521\ne,90,210415\ne,91,210527\ne,92,210722\ne,94,210924\ne,95,211021\ne,100,220401\ne,102,220531\ne,103,220623\ne,110,230209\ne,113,230505\ne,114,230602\ne,116,230821\ne,122,240223\ne,126,240613\ne,128,240822\ne,129,240919\ne,131,241114\ne,132,250117\ne,134,250306\ne,137,250529\ne,139,250807\ne,142,251031\ne,143,251205\ne,144,260121\ne,149,260604\ne,150,260702\ne,151,260731\ne,152,260827\ne,153,260910\nf,40,150811\nf,42,151103\nf,43,151215\nf,44,160126\nf,46,160426\nf,48,160802\nf,51,170124\nf,54,170613\nf,55,170808\nf,56,170928\nf,57,171114\nf,58,180123\nf,59,180313\nf,61,180626\nf,66,190319\nf,67,190521\nf,69,190903\nf,70,191022\nf,71,191210\nf,72,200107\nf,73,200211\nf,74,200310\nf,75,200407\nf,76,200505\nf,77,200602\nf,80,200825\nf,81,200922\nf,82,201020\nf,87,210323\nf,88,210419\nf,89,210601\nf,90,210713\nf,97,220208\nf,99,220405\nf,105,220920\nf,106,221018\nf,109,230117\nf,117,230829\nf,133,241126\nf,135,250204\nf,140,250624\nf,141,250722\nf,146,251209\nf,150,260421\nf,154,260818\nf,155,260901\nfa,40,150811\nfa,42,151103\nfa,43,151215\nfa,44,160126\nfa,46,160426\nfa,48,160802\nfa,51,170124\nfa,54,170613\nfa,55,170808\nfa,58,180122\nfa,59,180313\nfa,61,180626\nfa,66,190319\nfa,67,190521\nfa,80,200831\nfa,81,200922\nfa,82,201020\nfa,87,210323\nfa,88,210419\nfa,89,210601\nfa,90,210713\nfa,95,211207\nfa,97,220208\nfa,99,220405\nfa,105,220920\nfa,106,221018\nfa,109,230117\nfa,117,230829\nfa,133,241126\nfa,135,250204\nfa,140,250624\nfa,141,250722\nfa,146,251209\nfa,150,260421\nfa,154,260818\nfa,155,260901\ns,9,150930\ns,9.1,160321\ns,10,160920\ns,10.1,170327\ns,12,180917\ns,15.3,220126\ns,15.5,220516\ns,16.1,221024\ns,16.2,221213\ns,16.3,230123\ns,16.5,230518\ns,16.6,230724\ns,17.1,231025\ns,17.3,240122\ns,17.6,240729\ns,18.1,241028\ns,18.3,250127\ns,18.5,250512\ns,18.6,250729\ns,26.1,251103\ns,26.6,260727\ns,27,u\nsi,9,150916\nsi,9.3,160321\nsi,10,160913\nsi,10.3,170327\nsi,12,180917\nsi,15.3,220126\nsi,15.5,220516\nsi,16.1,221024\nsi,16.2,221213\nsi,16.3,230123\nsi,16.5,230518\nsi,16.6,230724\nsi,17.1,231025\nsi,17.3,240122\nsi,17.6,240729\nsi,18.1,241028\nsi,18.6,250729\nsi,26.1,251103\nsi,26.6,260727\nsi,27,u\no,15,130702,28\no,16,130827,29\no,17,131008,30\no,18,131119,31\no,19,140128,32\no,20,140304,33\no,21,140506,34\no,22,140603,35\no,23,140722,36\no,24,140902,37\no,26,141203,39\no,27,150127,40\no,28,150310,41\no,29,150428,42\no,30,150609,43\no,32,150915,45\no,33,151027,46\no,34,151208,47\no,35,160202,48\no,36,160315,49\no,37,160504,50\no,38,160608,51\no,39,160802,52\no,41,161025,54\no,42,161213,55\no,45,170510,58\no,46,170622,59\no,47,170809,60\no,48,170927,61\no,50,180104,63\no,51,180207,64\no,52,180322,65\no,54,180628,67\no,55,180816,68\no,56,180925,69\no,57,181128,70\no,58,190123,71\no,60,190409,73\no,62,190627,75\no,64,191007,77\no,65,191113,78\no,66,200107,79\no,69,200624,83\no,76,210428,90\no,77,210609,91\no,78,210803,92\no,80,211005,94\no,81,211104,95\no,86,220420,100\no,88,220608,102\no,89,220707,103\no,96,230222,110\no,99,230516,113\no,100,230629,114\no,102,230823,116\no,108,240305,122\no,112,240711,126\no,114,240925,128\no,116,250108,131\no,117,250213,132\no,119,250513,134\no,123,251028,139\no,126,260108,142\no,127,260202,143\no,128,260226,144\no,133,260629,149\no,134,260806,150\no,135,u,151\no,136,u,152\noa,15,130708,28\noa,16,130918,29\noa,18,131120,31\noa,19,140128,32\noa,20,140306,33\noa,21,140422,34\noa,22,140617,35\noa,24,140910,37\noa,26,141202,39\noa,27,150129,40\noa,28,150310,41\noa,29,150428,42\noa,30,150610,43\noa,33,151103,46\noa,34,151216,47\noa,35,160204,48\noa,36,160331,49\noa,37,160616,50\noa,42,170121,55\noa,44,171211,60\noa,45,180215,61\noa,48,181108,69\noa,49,181207,70\noa,50,190218,71\noa,51,190321,72\noa,52,190517,73\noa,53,190711,74\noa,55,191203,77\noa,56,200206,78\noa,59,200630,83\noa,64,210525,91\noa,65,211020,92\noa,69,220509,100\noa,76,230626,114\noa,85,241029,128\noa,87,250122,132\noa,94,260113,143\noa,95,260211,144\noa,100,260701,149\nsa,1.5,130925,28\nsa,1.6,140411,28\nsa,2.0,141017,34\nsa,2.1,150107,34\nsa,3.2,150824,38\nsa,4.2,160802,44\nsa,5.0,161215,51\nsa,5.2,170421,51\nsa,5.4,170517,51\nsa,6.2,171026,56\nsa,6.4,180219,56\nsa,7.2,180620,59\nsa,7.4,180912,59\nsa,8.2,181221,63\nsa,9.2,190402,67\nsa,9.4,190725,67\nsa,10.0,190822,71\nsa,10.2,191009,71\nsa,11.0,191205,75\nsa,11.2,200322,75\nsa,12.1,200707,79\nsa,13.2,210120,83\nsa,14.2,210625,87\nsa,16.0,211125,92\nsa,16.2,220306,92\nsa,18.1,220909,99\nsa,19.1,221108,102\nsa,26.0,240607,122\nwva,4.4,131209,30\nwva,4.4.3,140602,33\nwva,37,140903,37\nwva,39,141112,39\nwva,40,150121,40\nwva,41,150311,41\nwva,42,150415,42\nwva,43,150527,43\nwva,45,150901,45\nwva,46,151014,46\nwva,47,151202,47\nwva,48,160126,48\nwva,49,160309,49\nwva,50,160413,50\nwva,51,160608,51\nwva,52,160727,52\nwva,54,161019,54\nwva,55,161206,55\nwva,58,170425,58\nwva,59,170606,59\nwva,60,170801,60\nwva,61,170905,61\nwva,63,171205,63\nwva,64,180123,64\nwva,65,180306,65\nwva,67,180531,67\nwva,68,180724,68\nwva,69,180904,69\nwva,70,181017,70\nwva,71,181204,71\nwva,72,190129,72\nwva,73,190312,73\nwva,74,190424,74\nwva,75,190604,75\nwva,77,190910,77\nwva,78,191022,78\nwva,79,191217,79\nwva,83,200519,83\nwva,90,210413,90\nwva,91,210525,91\nwva,92,210720,92\nwva,94,210921,94\nwva,95,211019,95\nwva,100,220329,100\nwva,102,220524,102\nwva,103,220621,103\nwva,110,230207,110\nwva,113,230502,113\nwva,114,230530,114\nwva,116,230815,116\nwva,122,240220,122\nwva,126,240611,126\nwva,128,240820,128\nwva,129,240917,129\nwva,131,241112,131\nwva,132,250114,132\nwva,134,250304,134\nwva,137,250527,137\nwva,139,250805,139\nwva,142,251028,142\nwva,143,251202,143\nwva,144,260113,144\nwva,149,260602,149\nwva,150,260630,150\nwva,151,260728,151\nwva,152,260825,152\nwva,153,260908,153\ny,1.0,u,25\ny,1.5,u,22\ny,1.6,u,25\ny,1.7,u,25\ny,1.20,u,25\ny,2.5,u,25\ny,3.2,u,25\ny,4.6,u,25\ny,5.3,u,25\ny,5.4,u,25\ny,7.4,u,25\ny,9.6,u,25\ny,10.5,u,25\ny,11.4,u,25\ny,11.5,u,25\ny,12.7,u,25\ny,13.9,u,28\ny,13.10,u,28\ny,13.11,u,28\ny,13.12,u,30\ny,14.2,u,32\ny,14.4,u,33\ny,14.5,u,34\ny,14.7,u,35\ny,14.8,u,36\ny,14.10,u,37\ny,15.2,u,40\ny,15.4,u,41\ny,15.6,u,42\ny,15.7,u,43\ny,15.10,u,45\ny,15.12,u,46\ny,16.2,u,47\ny,16.3,u,47\ny,16.4,u,49\ny,16.6,u,50\ny,16.7,u,51\ny,16.9,u,52\ny,16.11,u,54\ny,17.1,u,55\ny,17.6,u,58\ny,17.7,u,59\ny,17.9,u,60\ny,17.10,u,61\ny,18.1,u,63\ny,18.2,u,63\ny,18.3,u,64\ny,18.4,u,65\ny,18.7,u,67\ny,18.9,u,68\ny,18.10,u,69\ny,18.11,u,70\ny,19.1,u,71\ny,19.3,u,72\ny,19.4,u,73\ny,19.5,u,75\ny,19.6,u,75\ny,19.7,u,75\ny,19.10,u,77\ny,19.11,u,78\ny,19.12,u,78\ny,20.2,u,79\ny,20.6,u,81\ny,20.7,u,83\ny,21.2,u,88\ny,21.5,u,90\ny,21.6,210928,91\ny,21.8,210928,92\ny,21.11,211029,94\ny,22.4,u,92\ny,22.9,220827,104\ny,23.7,230706,114\ny,24.2,240325,120\ny,24.9,241001,126\ny,25.2,250424,132\ny,25.3,250423,132\ny,26.3,260304,144\ny,26.7,260723,149\ny,26.8,260801,150\nu,10.5,u,31\nu,10.7,u,31\nu,10.8,u,31\nu,10.10,u,31\nu,11.0,u,31\nu,11.2,u,40\nu,11.3,u,40\nu,11.4,u,40\nu,11.5,u,40\nu,11.8,u,57\nu,11.9,u,57\nu,12.0,u,57\nu,12.1,u,57\nu,12.2,u,57\nu,12.3,u,57\nu,12.4,u,57\nu,12.5,u,57\nu,12.6,u,57\nu,12.7,u,57\nu,12.8,u,57\nu,12.9,u,57\nu,12.10,u,57\nu,12.11,u,57\nu,12.12,u,57\nu,12.13,u,57\nu,12.14,u,57\nu,13.0,u,57\nu,13.1,u,57\nu,13.2,u,57\nu,13.4,210928,78\nu,13.5,230825,78\nu,13.6,231217,78\nu,13.7,230624,78\nu,13.8,220430,78\nu,13.9,220518,78\nu,15.0,220824,78\nu,15.1,221111,78\nu,15.2,230423,78\nu,15.4,231025,100\nu,15.5,230822,100\nu,16.0,230824,100\nu,16.1,231015,100\nu,16.2,231209,100\nu,16.3,240308,100\nu,16.4,241003,100\nu,16.5,240530,100\nu,16.6,240723,100\nu,17.0,240824,100\nu,17.1,240926,100\nu,17.2,241129,100\nu,17.3,250107,100\nu,17.4,250226,100\nu,17.5,250408,100\nu,17.7,250611,123\nu,17.8,250730,123\nu,18.0,250817,123\nu,18.1,251004,123\nu,18.2,251104,123\nu,18.3,251212,123\nu,18.4,260109,123\nu,18.5,260128,123\nu,18.6,260321,123\nu,18.8,260503,123\nu,18.9,260702,123\nu,18.10,260808,123\nq,6.0,u,37\nq,6.1,u,37\nq,6.2,u,37\nq,6.3,u,37\nq,6.4,u,37\nq,6.6,u,37\nq,6.7,u,37\nq,6.8,u,37\nq,6.9,u,37\nq,7.0,u,37\nq,7.1,u,37\nq,7.2,u,37\nq,7.3,u,37\nq,7.4,u,37\nq,7.5,u,37\nq,7.6,u,37\nq,7.7,u,37\nq,7.8,u,37\nq,7.9,u,37\nq,8.0,u,37\nq,8.2,u,57\nq,8.3,u,57\nq,8.4,u,57\nq,8.5,u,57\nq,8.6,u,57\nq,8.7,u,57\nq,8.8,u,57\nq,8.9,u,57\nq,9.1,u,57\nq,9.7,u,66\nq,9.8,u,66\nq,10.0,u,66\nq,10.1,u,66\nq,10.2,u,66\nq,10.3,u,66\nq,10.4,u,66\nq,10.5,u,66\nq,10.7,200909,66\nq,11.0,u,77\nq,11.2,210130,77\nq,11.3,210331,77\nq,11.9,u,89\nq,12.0,211104,89\nq,12.1,211105,89\nq,12.2,211207,89\nq,12.5,220407,89\nq,12.7,220521,89\nq,12.8,220630,89\nq,12.9,220726,89\nq,13.0,220815,89\nq,13.1,220910,89\nq,13.2,221026,89\nq,13.3,221109,89\nq,13.5,230206,98\nq,13.6,230209,98\nq,13.7,230421,98\nq,13.8,230421,98\nq,14.0,231212,98\nq,14.1,230716,98\nq,14.3,230913,109\nq,14.4,231031,109\nq,14.5,231112,109\nq,14.6,231224,109\nq,14.7,240118,109\nq,14.8,240304,109\nq,14.9,240409,109\nq,15.0,240417,109\nq,15.1,240518,109\nq,15.2,241024,109\nq,15.3,240728,109\nq,15.4,240907,109\nq,15.5,240924,109\nq,15.6,241024,109\nq,15.7,241203,109\nq,15.8,241211,109\nq,15.9,250201,109\nq,19.2,250715,121\nq,19.3,250831,121\nq,19.4,250920,121\nq,19.5,251023,121\nq,19.6,251117,121\nq,19.7,251218,121\nq,19.8,260120,121\nq,19.9,260309,121\nq,20.0,260406,121\nq,20.1,260430,121\nq,20.2,260610,121\nq,20.3,260617,121\nq,20.4,260728,121\nq,20.5,260817,121\nk,1.0,170301,37\nk,2.5,170701,48\nk,3.1,220301,84\nfb,68,u,48\nfb,74,u,50\nfb,75,u,50\nfb,76,u,50\nfb,77,u,50\nfb,78,u,50\nfb,79,u,50\nfb,80,u,51\nfb,81,u,51\nfb,82,u,51\nfb,83,u,51\nfb,84,u,51\nfb,86,u,51\nfb,87,u,52\nfb,88,u,52\nfb,89,u,52\nfb,90,u,52\nfb,91,u,52\nfb,92,u,52\nfb,93,u,52\nfb,94,u,52\nfb,96,u,53\nfb,97,u,53\nfb,98,u,53\nfb,99,u,53\nfb,100,u,54\nfb,101,u,54\nfb,103,u,54\nfb,104,u,54\nfb,105,u,54\nfb,106,u,55\nfb,107,u,55\nfb,108,u,55\nfb,109,u,55\nfb,110,u,55\nfb,111,u,55\nfb,113,u,56\nfb,114,u,56\nfb,115,u,56\nfb,116,u,56\nfb,118,u,57\nfb,119,u,57\nfb,120,u,57\nfb,121,u,57\nfb,122,u,58\nfb,123,u,58\nfb,124,u,58\nfb,125,u,58\nfb,126,u,58\nfb,127,u,58\nfb,128,u,58\nfb,129,u,58\nfb,130,u,59\nfb,131,u,59\nfb,132,u,59\nfb,133,u,59\nfb,134,u,59\nfb,135,u,59\nfb,136,u,59\nfb,137,u,59\nfb,138,u,60\nfb,140,u,60\nfb,142,u,61\nfb,143,u,61\nfb,144,u,61\nfb,145,u,61\nfb,146,u,61\nfb,147,u,61\nfb,148,u,61\nfb,150,u,62\nfb,151,u,62\nfb,152,u,62\nfb,153,u,63\nfb,154,u,63\nfb,155,u,63\nfb,156,u,63\nfb,157,u,64\nfb,158,u,64\nfb,159,u,64\nfb,160,u,64\nfb,161,u,64\nfb,162,u,64\nfb,163,u,65\nfb,164,u,65\nfb,165,u,65\nfb,166,u,65\nfb,167,u,65\nfb,168,u,65\nfb,170,u,66\nfb,171,u,66\nfb,172,u,66\nfb,173,u,66\nfb,174,u,66\nfb,175,u,67\nfb,176,u,67\nfb,177,u,67\nfb,178,u,67\nfb,180,u,67\nfb,181,u,67\nfb,182,u,67\nfb,183,u,68\nfb,184,u,68\nfb,185,u,68\nfb,186,u,68\nfb,187,u,68\nfb,188,u,68\nfb,202,u,71\nfb,227,u,75\nfb,228,u,75\nfb,229,u,75\nfb,230,u,75\nfb,231,u,75\nfb,235,u,76\nfb,236,u,76\nfb,237,u,76\nfb,238,u,76\nfb,240,u,77\nfb,241,u,77\nfb,242,u,77\nfb,243,u,77\nfb,244,u,78\nfb,245,u,78\nfb,246,u,78\nfb,247,u,78\nfb,248,u,78\nfb,249,u,78\nfb,250,u,78\nfb,251,u,79\nfb,252,u,79\nfb,253,u,79\nfb,254,u,79\nfb,255,u,79\nfb,257,u,80\nfb,258,u,80\nfb,259,u,80\nfb,260,u,80\nfb,261,u,80\nfb,262,u,80\nfb,263,u,80\nfb,264,u,80\nfb,265,u,80\nfb,267,u,81\nfb,268,u,81\nfb,269,u,81\nfb,270,u,81\nfb,271,u,81\nfb,272,u,83\nfb,273,u,83\nfb,274,u,83\nfb,275,u,83\nfb,400,230210,109\nfb,436,231013,117\nfb,438,231028,118\nfb,440,231112,119\nfb,441,231120,119\nfb,442,231129,119\nfb,444,231213,120\nfb,445,231221,120\nfb,446,240106,120\nfb,447,240112,120\nfb,449,240202,121\nfb,450,240205,121\nfb,451,240217,121\nfb,452,240225,122\nfb,453,240228,122\nfb,454,240304,122\nfb,466,u,126\nfb,469,u,126\nfb,471,240710,126\nfb,472,240711,126\nfb,475,240801,127\nfb,476,240809,127\nfb,477,240816,127\nfb,478,240821,128\nfb,479,240831,128\nfb,480,240907,128\nfb,481,240914,128\nfb,482,240920,129\nfb,483,240927,129\nfb,484,241004,129\nfb,485,241011,129\nfb,487,241026,130\nfb,488,241102,130\nfb,489,241109,130\nfb,494,241226,131\nfb,497,250126,132\nfb,515,250531,137\nia,24,u,62\nia,25,u,62\nia,26,u,63\nia,27,u,63\nia,28,u,63\nia,29,u,63\nia,30,u,63\nia,31,u,64\nia,32,u,64\nia,33,u,64\nia,34,u,64\nia,35,u,65\nia,36,u,65\nia,37,u,65\nia,38,u,65\nia,39,u,65\nia,40,u,65\nia,41,u,65\nia,43,u,66\nia,44,u,66\nia,45,u,66\nia,46,u,66\nia,47,u,66\nia,48,u,67\nia,49,u,67\nia,50,u,67\nia,51,u,67\nia,52,u,67\nia,53,u,67\nia,54,u,67\nia,55,u,67\nia,56,u,68\nia,57,u,68\nia,58,u,68\nia,59,u,68\nia,60,u,68\nia,61,u,68\nia,65,u,69\nia,66,u,69\nia,68,u,69\nia,72,u,70\nia,74,u,71\nia,75,u,71\nia,79,u,71\nia,81,u,72\nia,82,u,72\nia,83,u,72\nia,84,u,73\nia,86,u,73\nia,95,u,74\nia,97,u,80\nia,98,u,80\nia,103,u,80\nia,104,u,80\nia,117,u,80\nia,118,u,80\nia,119,u,80\nia,120,u,80\nia,121,u,80\nia,127,u,80\nia,128,u,80\nia,129,u,80\nia,130,u,80\nia,131,u,80\nia,132,u,80\nia,133,u,80\nia,134,u,80\nia,135,u,80\nia,136,u,80\nia,138,u,81\nia,139,u,81\nia,140,u,81\nia,141,u,81\nia,142,u,81\nia,143,u,83\nia,144,u,83\nia,145,u,83\nia,146,u,83\nia,164,u,92\nia,230,u,92\nia,259,221104,106\nia,281,u,109\nia,289,231221,114\nia,290,231230,114\nia,295,u,115\nia,296,u,115\nia,297,u,115\nia,298,240111,115\nia,299,u,115\nia,300,u,116\nia,301,240112,116\nia,303,u,117\nia,304,u,117\nia,305,u,117\nia,307,u,118\nia,308,240119,118\nia,310,u,119\nia,312,u,120\nia,313,u,120\nia,314,u,120\nia,315,240119,120\nia,316,240125,120\nia,318,240216,121\nia,320,240304,121\nia,321,240307,122\nia,347,240911,127\nia,349,240920,128\nia,366,u,132\nia,367,250215,132\nia,382,250619,137\nia,383,250618,137\nia,384,250616,137\nia,385,250627,137\nia,387,250709,137\nia,392,250812,138\nia,394,250826,139\nia,395,250913,139\nia,396,250920,139\nia,397,250919,139\nia,401,251008,141\nia,404,251031,141\nia,406,251116,141\nia,407,251123,142\nia,408,251128,142\nia,409,251216,143\nia,410,251217,143\nia,411,260107,143\nia,438,260722,150\n".split("\n").forEach(n=>{if(n=n.trim())if("releases"!==n){if(f){const a=n.split(",");if(a.length>=3){const[n,u,o,f]=a,i=[n,u,e(o.trim())];f&&i.push(f.trim()),s[n]||(s[n]=[]),s[n].push(i);}return}if(n.startsWith("20")||n.startsWith("pre_baseline"))return o=e(n),void(u[o]=[]);{const a=n.split(",");if(a.length>=3){const[n,s,f,i]=a,r=[n,s,e(f.trim())];if(i&&r.push(i.trim()),!u[o])throw new Error(`Timeline entry for date ${o} is undefined. This should not happen.`);u[o].push(r);}}}else f=true;});const i={};Object.keys(a).forEach(n=>{var a;i[n]=[...null!==(a=s[n])&&void 0!==a?a:[]];}),Object.keys(u).forEach(n=>{u[n].forEach(n=>{const a=n[0];i[a]&&(i[a].some(a=>a[1]===n[1])||i[a].push(n));});}),Object.keys(i).forEach(a=>{i[a].sort((a,e)=>n(a[1],e[1]));});let r=false;const c=Object.keys(a).filter(n=>void 0===a[n].engine).map(n=>{const{longName:e}=a[n];return {shortName:n,longName:e}}),t=["webview_android","samsunginternet_android","opera_android","opera","ya_android","uc_android","qq_android","kai_os","facebook_android","instagram_android"],b=a=>{const e=[],u=[];return a.forEach(n=>{c.some(a=>a.longName===n.browser)?e.push(n):u.push(n);}),e.sort((a,e)=>a.browsere.browser?1:n(a.version,e.version)),u.sort((a,e)=>{const u=t.indexOf(a.browser),s=t.indexOf(e.browser);return u!==s?u-s:n(a.version,e.version)}),[...e,...u]};let l=false;const w=n=>{if(false===n.includeDownstreamBrowsers&&true===n.includeKaiOS)throw new Error("KaiOS is a downstream browser and can only be included if you include other downstream browsers. Please ensure you use `includeDownstreamBrowsers: true`.")},v=(n,e,u,s,o)=>{var f;const i={browser:e,version:u,release_date:"u"===s?"unknown":s};return o&&(i.engine_version=o,i.engine=null===(f=a[n])||void 0===f?void 0:f.engine),i};function y(e){var s,o,f,t,y,p,d;let g=null!=e?e:{},q={listAllCompatibleVersions:null!==(s=g.listAllCompatibleVersions)&&void 0!==s&&s,includeDownstreamBrowsers:null!==(o=g.includeDownstreamBrowsers)&&void 0!==o&&o,widelyAvailableOnDate:null!==(f=g.widelyAvailableOnDate)&&void 0!==f?f:void 0,targetYear:null!==(t=g.targetYear)&&void 0!==t?t:void 0,includeKaiOS:null!==(y=g.includeKaiOS)&&void 0!==y&&y,overrideLastUpdated:null!==(p=g.overrideLastUpdated)&&void 0!==p?p:void 0,suppressWarnings:null!==(d=g.suppressWarnings)&&void 0!==d&&d},m=new Date;if(q.widelyAvailableOnDate||q.targetYear){if(q.targetYear&&q.widelyAvailableOnDate)throw new Error("You cannot use targetYear and widelyAvailableOnDate at the same time. Please remove one of these options and try again.");q.widelyAvailableOnDate?m=new Date(q.widelyAvailableOnDate):q.targetYear&&(m=new Date(`${q.targetYear}-12-31`));}else m=new Date;if((q.widelyAvailableOnDate||void 0===q.targetYear)&&m.setMonth(m.getMonth()-30),!q.suppressWarnings){if(((n,a)=>{if(r||"undefined"!=typeof process&&process.env&&(process.env.BROWSERSLIST_IGNORE_OLD_DATA||process.env.BASELINE_BROWSER_MAPPING_IGNORE_OLD_DATA))return;const e=new Date;e.setMonth(e.getMonth()-2),n>e&&(null!=a?a:1786976913541)(new Date).getFullYear())throw new Error("There are no browser versions compatible with Baseline in the future")}const h=m{_[n]=void 0;}),Object.keys(u).forEach(n=>{const a=u[n];let e=false;e="pre_baseline"===n||new Date(n)<=m,e&&a.forEach(a=>{const e=a[0];"pre_baseline"!==n&&(_[e]=a);});});const O=[];return Object.keys(a).forEach(e=>{var u,s;const{longName:o}=a[e];if(!q.includeKaiOS&&"k"===e)return;const f=c.some(n=>n.shortName===e);if(q.includeDownstreamBrowsers||f)if(h)if(q.listAllCompatibleVersions){O.push({browser:o,version:"0",release_date:""});(null!==(u=i[e])&&void 0!==u?u:[]).forEach(n=>{O.push(v(e,o,n[1],n[2],n[3]));});}else O.push({browser:o,version:"0",release_date:""});else {const a=_[e];if(!a)return;const u=a[1];if(q.listAllCompatibleVersions){(null!==(s=i[e])&&void 0!==s?s:[]).forEach(a=>{const s=a[1];n(s,u)>=0&&O.push(v(e,o,s,a[2],a[3]));});}else O.push(v(e,o,u,a[2],a[3]));}}),b(O)}dist._resetHasWarned=function(){r=false;},dist.getAllVersions=function(a){var e,u,s,o,f;l=true;let i=null!=a?a:{},r={outputFormat:null!==(e=i.outputFormat)&&void 0!==e?e:"array",includeDownstreamBrowsers:null!==(u=i.includeDownstreamBrowsers)&&void 0!==u&&u,useSupports:null!==(s=i.useSupports)&&void 0!==s&&s,includeKaiOS:null!==(o=i.includeKaiOS)&&void 0!==o&&o,suppressWarnings:null!==(f=i.suppressWarnings)&&void 0!==f&&f};w(r);let t=(new Date).getFullYear()+1;const b=[...Array(t).keys()].slice(2015),v={};b.forEach(n=>{v[n]={},y({targetYear:n,suppressWarnings:r.suppressWarnings}).forEach(a=>{v[n]&&(v[n][a.browser]=a);});});const p=y({suppressWarnings:r.suppressWarnings}),d={};p.forEach(n=>{d[n.browser]=n;});const g=new Date;g.setMonth(g.getMonth()+30);const q=y({widelyAvailableOnDate:g.toISOString().slice(0,10),suppressWarnings:r.suppressWarnings}),m={};q.forEach(n=>{m[n.browser]=n;});const h=y({targetYear:2002,listAllCompatibleVersions:true,suppressWarnings:r.suppressWarnings}),_=[];if(c.map(n=>n.longName).forEach(a=>{var e,u,s,o;let f=h.filter(n=>n.browser==a).sort((a,e)=>n(a.version,e.version)),i=null!==(u=null===(e=d[a])||void 0===e?void 0:e.version)&&void 0!==u?u:"0",c=null!==(o=null===(s=m[a])||void 0===s?void 0:s.version)&&void 0!==o?o:"0";b.forEach(e=>{var u;if(v[e]){let s=(null!==(u=v[e][a])&&void 0!==u?u:{version:"0"}).version,o=f.findIndex(a=>0===n(a.version,s));(e===t-1?f:f.slice(0,o)).forEach(a=>{let u=n(a.version,i)>=0,s=n(a.version,c)>=0,o=Object.assign(Object.assign({},a),{year:e<=2015?"pre_baseline":e-1});r.useSupports?(u&&(o.supports="widely"),s&&(o.supports="newly")):o=Object.assign(Object.assign({},o),{wa_compatible:u}),_.push(o);}),f=f.slice(o,f.length);}});}),r.includeDownstreamBrowsers){y({targetYear:2002,listAllCompatibleVersions:true,includeDownstreamBrowsers:true,includeKaiOS:r.includeKaiOS,suppressWarnings:r.suppressWarnings}).filter(n=>!c.map(n=>n.longName).includes(n.browser)).forEach(n=>{const a="Gecko"===n.engine?"firefox":"chrome";let e=_.find(e=>e.browser===a&&e.version===n.engine_version);e&&(r.useSupports?_.push(Object.assign(Object.assign({},n),{year:e.year,supports:e.supports})):_.push(Object.assign(Object.assign({},n),{year:e.year,wa_compatible:e.wa_compatible})));});}if(_.sort((a,e)=>{if("pre_baseline"===a.year&&"pre_baseline"!==e.year)return -1;if("pre_baseline"===e.year&&"pre_baseline"!==a.year)return 1;if("pre_baseline"!==a.year&&"pre_baseline"!==e.year){if(a.yeare.year)return 1}return a.browsere.browser?1:n(a.version,e.version)}),"object"===r.outputFormat){const n={};return _.forEach(a=>{n[a.browser]||(n[a.browser]={});let e={year:a.year,release_date:a.release_date,engine:a.engine,engine_version:a.engine_version};n[a.browser][a.version]=r.useSupports?a.supports?Object.assign(Object.assign({},e),{supports:a.supports}):e:Object.assign(Object.assign({},e),{wa_compatible:a.wa_compatible});}),null!=n?n:{}}if("csv"===r.outputFormat){let n=`"browser","version","year","${r.useSupports?"supports":"wa_compatible"}","release_date","engine","engine_version"`;return _.forEach(a=>{var e,u,s,o;let f={browser:a.browser,version:a.version,year:a.year,release_date:null!==(e=a.release_date)&&void 0!==e?e:"NULL",engine:null!==(u=a.engine)&&void 0!==u?u:"NULL",engine_version:null!==(s=a.engine_version)&&void 0!==s?s:"NULL"};f=r.useSupports?Object.assign(Object.assign({},f),{supports:null!==(o=a.supports)&&void 0!==o?o:""}):Object.assign(Object.assign({},f),{wa_compatible:a.wa_compatible}),n+=`\n"${f.browser}","${f.version}","${f.year}","${r.useSupports?f.supports:f.wa_compatible}","${f.release_date}","${f.engine}","${f.engine_version}"`;}),n}return _},dist.getCompatibleVersions=y,dist.getTimeline=function(n){var e,s,o,f;const i=null!=n?n:{},r=null!==(e=i.groupBy)&&void 0!==e?e:"date",t=null!==(s=i.listAllBrowsers)&&void 0!==s&&s,l=null!==(o=i.includeDownstreamBrowsers)&&void 0!==o&&o,w=null!==(f=i.includeKaiOS)&&void 0!==f&&f;if(false===l&&true===w)throw new Error("KaiOS is a downstream browser and can only be included if you include other downstream browsers. Please ensure you use `includeDownstreamBrowsers: true`.");const y={},p=[];if(Object.keys(u).forEach(n=>{const e=u[n];if("pre_baseline"===n)return;const s=new Set;e.forEach(n=>{const a=n[0],e=y[a],u=n[1];e&&e[1]===u||s.add(a),y[a]=n;});const o=[];if(Object.keys(a).forEach(n=>{const{longName:e}=a[n];if(!w&&"k"===n)return;const u=c.some(a=>a.shortName===n);if(!l&&!u)return;const f=s.has(n);if(t||f){const a=y[n];a&&o.push(v(n,e,a[1],a[2],a[3]));}}),o.length>0){const a=b(o);p.push({date:n,browsers:a});}}),"browser"===r){const n={};return Object.keys(a).forEach(e=>{const{longName:u}=a[e];if(!w&&"k"===e)return;const s=c.some(n=>n.shortName===e);(l||s)&&(n[u]=[]);}),p.forEach(a=>{a.browsers.forEach(e=>{const{browser:u}=e,s=function(n,a){var e={};for(var u in n)Object.prototype.hasOwnProperty.call(n,u)&&a.indexOf(u)<0&&(e[u]=n[u]);if(null!=n&&"function"==typeof Object.getOwnPropertySymbols){var s=0;for(u=Object.getOwnPropertySymbols(n);s { + let fullReloadRequested = false; const reloader = { urlHotMetas, status: { @@ -51,6 +52,14 @@ const initAutoreload = ({ mainFilePath }) => { }, currentExecution: null, reload: () => { + if (fullReloadRequested) { + // The document is already being replaced; anything we would do here + // dies with the current page. Calling window.location.reload() again + // would abort the navigation already in flight (net::ERR_ABORTED). + // This happens for a single file edit: the change is sent first, then + // the prune of the urls it no longer references arrives a few ms later. + return; + } const someEffectIsFullReload = reloader.changes.value.some( (reloadMessage) => { if (reloadMessage.type === "full") { @@ -77,6 +86,7 @@ const initAutoreload = ({ mainFilePath }) => { }, ); if (someEffectIsFullReload) { + fullReloadRequested = true; dispatchBeforeFullReload(); reloadHtmlPage(); return; diff --git a/package.json b/package.json index 569184a39a..d70c26969f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/core", - "version": "41.4.12", + "version": "41.4.13", "type": "module", "description": "Tool to develop, test and build js projects", "repository": { @@ -72,21 +72,21 @@ "test:snapshot_clear": "npx @jsenv/filesystem clear **/tests/**/side_effects/" }, "dependencies": { - "@jsenv/ast": "6.9.0", - "@jsenv/js-module-fallback": "1.4.39", + "@jsenv/ast": "6.9.1", + "@jsenv/js-module-fallback": "1.5.0", "@jsenv/plugin-bundling": "2.10.16", - "@jsenv/plugin-minification": "1.7.7", - "@jsenv/plugin-supervisor": "1.8.10", - "@jsenv/plugin-transpilation": "1.5.80", - "@jsenv/server": "17.6.0", + "@jsenv/plugin-minification": "1.7.8", + "@jsenv/plugin-supervisor": "1.8.11", + "@jsenv/plugin-transpilation": "1.6.0", + "@jsenv/server": "17.6.1", "@jsenv/sourcemap": "1.4.2", "react-table": "7.8.0" }, "devDependencies": { - "@babel/eslint-parser": "7.28.6", - "@babel/plugin-syntax-decorators": "7.28.6", + "@babel/eslint-parser": "7.29.7", + "@babel/plugin-syntax-decorators": "7.29.7", "@babel/plugin-syntax-import-attributes": "7.29.7", - "@babel/plugin-syntax-optional-chaining-assign": "7.28.6", + "@babel/plugin-syntax-optional-chaining-assign": "7.29.7", "@jsenv/abort": "workspace:*", "@jsenv/assert": "workspace:*", "@jsenv/cli": "workspace:*", @@ -114,17 +114,17 @@ "@jsenv/url-meta": "workspace:*", "@jsenv/urls": "workspace:*", "@jsenv/utils": "workspace:*", - "@playwright/browser-chromium": "1.61.1", - "@playwright/browser-firefox": "1.61.1", - "@playwright/browser-webkit": "1.61.1", + "@playwright/browser-chromium": "1.62.1", + "@playwright/browser-firefox": "1.62.1", + "@playwright/browser-webkit": "1.62.1", "babel-plugin-transform-async-to-promises": "0.8.18", "eslint": "9.39.2", "eslint-plugin-import-x": "4.17.1", "eslint-plugin-react": "7.37.5", - "eslint-plugin-regexp": "3.1.1", - "globals": "17.7.0", - "open": "11.0.0", - "playwright": "1.61.1", + "eslint-plugin-regexp": "3.2.0", + "globals": "17.11.0", + "open": "11.0.1", + "playwright": "1.62.1", "preact": "11.0.0-beta.2", "prettier": "3.9.6", "prettier-plugin-css-order": "git+https://github.com/dmail-fork/prettier-plugin-css-order.git", diff --git a/packages/backend/database-manager/package.json b/packages/backend/database-manager/package.json index 66a6070720..d863861bd0 100644 --- a/packages/backend/database-manager/package.json +++ b/packages/backend/database-manager/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@jsenv/database": "0.0.13", - "@jsenv/server": "17.6.0", + "@jsenv/server": "17.6.1", "@jsenv/urls": "2.9.10", "@tanstack/table-core": "8.21.3", "jsonwebtoken": "9.0.3", @@ -42,7 +42,7 @@ "@jsenv/navi": "../../frontend/navi", "@jsenv/plugin-preact": "../../related/plugin-preact", "@jsenv/server": "../../server", - "@preact/signals": "2.9.4", + "@preact/signals": "2.11.1", "preact": "11.0.0-beta.2" }, "publishConfig": { diff --git a/packages/backend/server/dist/ws/ws.js b/packages/backend/server/dist/ws/ws.js index 6eb5c53dd2..ccab272f81 100644 --- a/packages/backend/server/dist/ws/ws.js +++ b/packages/backend/server/dist/ws/ws.js @@ -418,7 +418,9 @@ function requirePermessageDeflate () { (typeof opts.serverMaxWindowBits === 'number' && opts.serverMaxWindowBits > params.server_max_window_bits))) || (typeof opts.clientMaxWindowBits === 'number' && - !params.client_max_window_bits) + (typeof params.client_max_window_bits === 'number' + ? opts.clientMaxWindowBits > params.client_max_window_bits + : !params.client_max_window_bits)) ) { return false; } diff --git a/packages/backend/server/package.json b/packages/backend/server/package.json index 03e1843ed2..cfa823c501 100644 --- a/packages/backend/server/package.json +++ b/packages/backend/server/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/server", - "version": "17.6.0", + "version": "17.6.1", "type": "module", "description": "A modern Node.js HTTP server with declarative routing, content negotiation, and WebSocket support", "repository": { @@ -54,11 +54,11 @@ "@jsenv/url-pattern": "../../tooling/url-pattern", "@jsenv/urls": "../../tooling/urls", "@jsenv/utils": "../../tooling/utils", - "eventsource": "4.1.0", + "eventsource": "5.1.0", "formidable": "3.5.4", - "playwright": "1.61.1", - "undici": "8.8.0", - "ws": "8.21.1" + "playwright": "1.62.1", + "undici": "8.10.0", + "ws": "8.21.3" }, "publishConfig": { "access": "public" diff --git a/packages/frontend/dom/dist/jsenv_dom.js b/packages/frontend/dom/dist/jsenv_dom.js index ed68e534e1..9fcb806f7a 100644 --- a/packages/frontend/dom/dist/jsenv_dom.js +++ b/packages/frontend/dom/dist/jsenv_dom.js @@ -4257,6 +4257,187 @@ const createPreviousNodeIterator = (fromNode, rootNode, skipRoot = null) => { }; }; +/** + * The click a gesture leaves behind. + * + * A press that turned into something else — an object carried, a screen swiped, + * a menu opened by holding still — still ends with a `pointerup`, and the + * browser follows that with a `click` on whatever the pointer was over. On a + * link or a button that click means "follow me", which is not what the hand + * asked for: the press was already answered, by the gesture. + * + * So it is swallowed, once, in capture on the document — before any handler an + * element may have, and without anyone having to know which element that is. + */ + +/** + * Swallows the next click, for a gesture that has just answered the press. + * + * @returns {() => void} the gesture is over. The suppressor cannot be taken + * down with it — the click is dispatched AFTER the pointerup that ends the + * gesture, so it would be gone one event too early, and the drag would end on + * the link it started from being followed. It goes once it has swallowed a + * click, or at the next press if the gesture produced none: a click is always + * preceded by a press, so a suppressor that outlives one press can never + * reach the click of another. + */ +const suppressClickAfterGesture = () => { + const suppressClick = (clickEvent) => { + clickEvent.stopPropagation(); + clickEvent.preventDefault(); + stopSuppressing(); + }; + const stopSuppressing = () => { + document.removeEventListener("click", suppressClick, { capture: true }); + document.removeEventListener("pointerdown", stopSuppressing, { + capture: true, + }); + }; + document.addEventListener("click", suppressClick, { capture: true }); + return () => { + document.addEventListener("pointerdown", stopSuppressing, { + capture: true, + }); + }; +}; + +/** + * A press that says something by NOT moving. + * + * A finger landing on an element is ambiguous — it may be a tap, a scroll, a + * swipe — and the one unambiguous signal a finger can give is staying still: + * travel is exactly what a scroll looks like, so it cannot be the sign. This + * owns that wait, and only that: what the press then means (an object picked + * up, a menu opened) belongs to whoever asked for it. + * + * The wait also has to hold off the system's own answer to the same gesture: a + * FINGER held long enough IS the context-menu gesture, and Android's menu (around + * 500ms) or iOS's callout lands a tenth of a second after the press was answered + * here. The half of that which is an event is refused below; the half that is not + * (iOS selecting the word under the finger) is a stylesheet the caller writes on + * its own elements — `-webkit-touch-callout: none` has to be true before the + * finger lands, so it cannot be set from here. + * + * A mouse is a different matter and is left alone: its context menu comes from + * the other button, not from this press, and refusing it would take the browser's + * menu away from an element for no reason. + */ + +/** + * Waits for a press to be held still, then tells the caller. + * + * @param {PointerEvent} pressEvent The `pointerdown` that may become a hold. + * @param {object} options + * @param {number} [options.delay=400] How long (ms) the pointer must stay down. + * Kept under the system context-menu delay so the press is answered before + * the menu would have opened. + * @param {number} [options.slop=8] How far (px) the pointer may drift during + * the wait — beyond it the finger is going somewhere, and a press answered in + * passing is a press nobody made. + * @param {function} [options.onPressStart] The wait began (a cue that the press + * counts). + * @param {function} [options.onPressCancel] The pointer moved or lifted before + * the wait was over. + * @param {(pressEvent: PointerEvent, handle: {endPress: () => void}) => void} options.onPressHeld + * The wait completed. Whatever the press now means outlives this call — an + * object is being carried, a menu is open under the finger — so the caller + * owns the end of it and says when with `endPress`, which is what gives the + * context menu back. + * @returns {{ cancel: () => void }} + */ +const waitForPressHeld = ( + pressEvent, + { delay = 400, slop = 8, onPressStart, onPressCancel, onPressHeld }, +) => { + const { pointerId, clientX, clientY } = pressEvent; + + const pressCleanupCallbacks = []; + const endPress = () => { + for (const pressCleanupCallback of pressCleanupCallbacks) { + pressCleanupCallback(); + } + pressCleanupCallbacks.length = 0; + }; + + /* A FINGER held down is the system's own context-menu gesture, and the menu it + raises lands on top of the answer this press was already given. A MOUSE is + not: its context menu comes from the other button, has nothing to do with + this press, and is the user asking for the browser's menu — so it is left + alone, and only a touch press refuses it. + The listener goes on window, in capture: what answers the press may cover the + page (a drag backdrop, a popup), and the contextmenu event is then aimed at + that instead of at the element pressed. */ + if (pressEvent.pointerType === "touch") { + const preventContextMenu = (contextMenuEvent) => { + contextMenuEvent.preventDefault(); + }; + window.addEventListener("contextmenu", preventContextMenu, true); + pressCleanupCallbacks.push(() => { + window.removeEventListener("contextmenu", preventContextMenu, true); + }); + } + + const countdownCleanupCallbacks = []; + const endCountdown = () => { + for (const countdownCleanupCallback of countdownCleanupCallbacks) { + countdownCleanupCallback(); + } + countdownCleanupCallbacks.length = 0; + }; + + const timeout = setTimeout(() => { + endCountdown(); + onPressHeld(pressEvent, { endPress }); + }, delay); + countdownCleanupCallbacks.push(() => { + clearTimeout(timeout); + }); + + const cancelPress = (pointerEvent) => { + endCountdown(); + endPress(); + onPressCancel?.(pointerEvent); + }; + const onPointerMove = (pointerMoveEvent) => { + if (pointerMoveEvent.pointerId !== pointerId) { + return; + } + const xDrift = Math.abs(pointerMoveEvent.clientX - clientX); + const yDrift = Math.abs(pointerMoveEvent.clientY - clientY); + if (xDrift < slop && yDrift < slop) { + return; + } + // The finger is going somewhere: it is scrolling the page, or running down + // the list. Letting the countdown survive would answer a press in passing. + cancelPress(pointerMoveEvent); + }; + const onPointerEnd = (pointerEndEvent) => { + if (pointerEndEvent.pointerId !== pointerId) { + return; + } + cancelPress(pointerEndEvent); + }; + // On window rather than on the element: the finger can leave it, and the + // element itself can be taken out of the document while the press is waiting. + window.addEventListener("pointermove", onPointerMove); + window.addEventListener("pointerup", onPointerEnd); + window.addEventListener("pointercancel", onPointerEnd); + countdownCleanupCallbacks.push(() => { + window.removeEventListener("pointermove", onPointerMove); + window.removeEventListener("pointerup", onPointerEnd); + window.removeEventListener("pointercancel", onPointerEnd); + }); + + onPressStart?.(pressEvent); + + return { + cancel: () => { + endCountdown(); + endPress(); + }, + }; +}; + const activeElementSignal = signal( typeof document === "object" ? document.activeElement : undefined, ); @@ -8296,36 +8477,8 @@ const createDragGestureController = (options = {}) => { return dragData; }; const markAsStarted = () => { - // Suppress the click that the browser fires after pointerup following a real drag. - // The capture phase runs before any element onClick handler. - const suppressClick = clickEvent => { - clickEvent.stopPropagation(); - clickEvent.preventDefault(); - stopSuppressingClick(); - }; - // That click is dispatched AFTER the pointerup that ends the drag, so - // this cannot be taken down with the gesture — it would be gone one event - // too early, and the drag would end on the link it started from being - // followed. It goes once it has swallowed the click, or at the next press - // if the drag produced none: a click is always preceded by a press, so a - // suppressor that outlives one press can never reach the click of - // another. - const stopSuppressingClick = () => { - document.removeEventListener("click", suppressClick, { - capture: true - }); - document.removeEventListener("pointerdown", stopSuppressingClick, { - capture: true - }); - }; - document.addEventListener("click", suppressClick, { - capture: true - }); - addReleaseCallback(() => { - document.addEventListener("pointerdown", stopSuppressingClick, { - capture: true - }); - }); + const clickSuppressionIsOver = suppressClickAfterGesture(); + addReleaseCallback(clickSuppressionIsOver); // Everything this gesture puts on the document is in place, and undoable, // BEFORE anybody is told it started: a listener may end the gesture from // inside this very notification — that is how a press becomes a drag (see @@ -8668,8 +8821,9 @@ installImportMetaCssBuild(import.meta);/** * * A pointer going down on a draggable element is ambiguous — it may be a click, * a text selection, a scroll, or a drag — and starting the gesture right away - * would steal all the others. This module owns the wait that resolves the - * ambiguity, and only then hands over to the real gesture. + * would steal all the others. This module picks which signal resolves the + * ambiguity for the pointer at hand, and only then hands over to the real + * gesture. * * There is one gesture, with a trigger per pointer: * - a dedicated handle ([data-drag-handle]) says it outright: drag on contact @@ -8822,94 +8976,26 @@ const dragAfterLongPress = (grabEvent, dragGestureInitializer, { onPressCancel, onPress }) => { - const { - pointerId, - clientX, - clientY - } = grabEvent; - const pressCleanupCallbacks = []; - const endPress = () => { - for (const pressCleanupCallback of pressCleanupCallbacks) { - pressCleanupCallback(); - } - pressCleanupCallbacks.length = 0; - }; - - /* - * A press held long enough IS the system's context-menu gesture: Android opens - * its menu around 500ms, iOS its callout — both a tenth of a second after the - * object has been picked up, landing on top of something the finger is already - * carrying. - * The listener goes on window, in capture: once the gesture runs, the drag - * backdrop covers the page, so the contextmenu event is aimed at the backdrop - * and never reaches the element being dragged. - * It is removed on release — a right click with a mouse remains a right click. - */ - const preventContextMenu = contextMenuEvent => { - contextMenuEvent.preventDefault(); - }; - window.addEventListener("contextmenu", preventContextMenu, true); - pressCleanupCallbacks.push(() => { - window.removeEventListener("contextmenu", preventContextMenu, true); - }); - const countdownCleanupCallbacks = []; - const endCountdown = () => { - for (const countdownCleanupCallback of countdownCleanupCallbacks) { - countdownCleanupCallback(); - } - countdownCleanupCallbacks.length = 0; - }; - const timeout = setTimeout(() => { - endCountdown(); - onPress?.(grabEvent); - // Scrolling is taken away by the gesture itself, from the moment it starts - // (see markAsStarted in drag_gesture.js) — one place refuses the touchmove, - // for every way a drag can begin. - const dragGesture = startDragGesture(dragGestureInitializer); - if (!dragGesture) { - endPress(); - return; - } - dragGesture.addReleaseCallback(endPress); - }, longPressDelay); - countdownCleanupCallbacks.push(() => { - clearTimeout(timeout); - }); - const cancelPress = pointerEvent => { - endCountdown(); - endPress(); - onPressCancel?.(pointerEvent); - }; - const onPointerMove = pointerMoveEvent => { - if (pointerMoveEvent.pointerId !== pointerId) { - return; - } - const xDrift = Math.abs(pointerMoveEvent.clientX - clientX); - const yDrift = Math.abs(pointerMoveEvent.clientY - clientY); - if (xDrift < longPressSlop && yDrift < longPressSlop) { - return; - } - // The finger is going somewhere: it is scrolling the page, or running down - // the list. Letting the countdown survive would unhook an object in passing. - cancelPress(pointerMoveEvent); - }; - const onPointerEnd = pointerEndEvent => { - if (pointerEndEvent.pointerId !== pointerId) { - return; + waitForPressHeld(grabEvent, { + delay: longPressDelay, + slop: longPressSlop, + onPressStart, + onPressCancel, + onPressHeld: (pressEvent, { + endPress + }) => { + onPress?.(pressEvent); + // Scrolling is taken away by the gesture itself, from the moment it starts + // (see markAsStarted in drag_gesture.js) — one place refuses the touchmove, + // for every way a drag can begin. + const dragGesture = startDragGesture(dragGestureInitializer); + if (!dragGesture) { + endPress(); + return; + } + dragGesture.addReleaseCallback(endPress); } - cancelPress(pointerEndEvent); - }; - // On window rather than on the element: the finger can leave it, and the - // element itself can be taken out of the document while the press is waiting. - window.addEventListener("pointermove", onPointerMove); - window.addEventListener("pointerup", onPointerEnd); - window.addEventListener("pointercancel", onPointerEnd); - countdownCleanupCallbacks.push(() => { - window.removeEventListener("pointermove", onPointerMove); - window.removeEventListener("pointerup", onPointerEnd); - window.removeEventListener("pointercancel", onPointerEnd); }); - onPressStart?.(grabEvent); }; /** @@ -10609,773 +10695,413 @@ const roundForConstraints = (value) => { return Math.round(value * 100) / 100; }; -const applyStickyFrontiersToAutoScrollArea = ( - autoScrollArea, - { direction, scrollContainer, dragName }, +/** + * Detects the drop target based on what element is actually under the mouse cursor. + * Uses document.elementsFromPoint() to respect visual stacking order naturally. + * + * @param {Object} gestureInfo - Gesture information + * @param {Element[]} targetElements - Array of potential drop target elements + * @param {object} [options] + * @param {Element} [options.dragElement] - The element being dragged. When provided and + * `fallbackToEdge` is true, used to compute the fallback rect. + * @param {boolean} [options.fallbackToEdge=false] - When true and the drag element does + * not intersect any target, falls back to the first item (if above all items) or the + * last item (if below all items) so there is always a valid drop target at list edges. + * @returns {Object|null} Drop target info with elementSide or null if no valid target found + */ +const getDropTargetInfo = ( + gestureInfo, + targetElements, + { fallbackToEdge = false } = {}, ) => { - let { left, right, top, bottom } = autoScrollArea; - - if (direction.x) { - const horizontalStickyFrontiers = createStickyFrontierOnAxis( - scrollContainer, - { - name: dragName, - scrollContainer, - primarySide: "left", - oppositeSide: "right", - }, - ); - for (const horizontalStickyFrontier of horizontalStickyFrontiers) { - const { side, bounds, element } = horizontalStickyFrontier; - if (side === "left") { - if (bounds.right <= left) { - continue; - } - left = bounds.right; - continue; - } - // right - if (bounds.left >= right) { - continue; - } - right = bounds.left; + const dragElement = gestureInfo.elementImpacted || gestureInfo.element; + const dragElementRect = dragElement.getBoundingClientRect(); + const intersectingTargets = []; + let someTargetIsCol; + let someTargetIsTr; + for (const targetElement of targetElements) { + const targetRect = targetElement.getBoundingClientRect(); + if (!rectangleAreIntersecting(dragElementRect, targetRect)) { continue; } - } - - if (direction.y) { - const verticalStickyFrontiers = createStickyFrontierOnAxis( - scrollContainer, - { - name: dragName, - scrollContainer, - primarySide: "top", - oppositeSide: "bottom", - }, - ); - for (const verticalStickyFrontier of verticalStickyFrontiers) { - const { side, bounds, element } = verticalStickyFrontier; - - // Frontier acts as a top barrier - constrains from the bottom edge of the frontier - if (side === "top") { - if (bounds.bottom <= top) { - continue; - } - top = bounds.bottom; - continue; - } - - // Frontier acts as a bottom barrier - constrains from the top edge of the frontier - if (bounds.top >= bottom) { - continue; - } - bottom = bounds.top; - continue; + if (!someTargetIsCol && targetElement.tagName === "COL") { + someTargetIsCol = true; + } + if (!someTargetIsTr && targetElement.tagName === "TR") { + someTargetIsTr = true; } + intersectingTargets.push(targetElement); } - return { left, right, top, bottom }; -}; + if (intersectingTargets.length === 0) { + if (fallbackToEdge) { + const dragElement = gestureInfo.elementImpacted || gestureInfo.element; + const dragElementRect = dragElement.getBoundingClientRect(); + const firstItem = targetElements[0]; + const lastItem = targetElements[targetElements.length - 1]; + if ( + firstItem && + dragElementRect.bottom < firstItem.getBoundingClientRect().top + ) { + // Drag element is above all items → treat as hovering the first item from the top. + return { + element: firstItem, + elementSide: { x: "start", y: "start" }, + index: 0, + intersectingIndex: 0, + intersecting: [firstItem], + }; + } + if ( + lastItem && + dragElementRect.top > lastItem.getBoundingClientRect().bottom + ) { + // Drag element is below all items → treat as hovering the last item from the bottom. + return { + element: lastItem, + elementSide: { x: "start", y: "end" }, + index: targetElements.length - 1, + intersectingIndex: 0, + intersecting: [lastItem], + }; + } + } + return null; + } -const createStickyFrontierOnAxis = ( - element, - { name, scrollContainer, primarySide, oppositeSide }, -) => { - const primaryAttrName = `data-drag-sticky-${primarySide}-frontier`; - const oppositeAttrName = `data-drag-sticky-${oppositeSide}-frontier`; - const frontiers = element.querySelectorAll( - `[${primaryAttrName}], [${oppositeAttrName}]`, - ); - const matchingStickyFrontiers = []; - for (const frontier of frontiers) { - if (frontier.closest("[data-drag-ignore]")) { - continue; + const dragElementCenterX = dragElementRect.left + dragElementRect.width / 2; + const dragElementCenterY = dragElementRect.top + dragElementRect.height / 2; + // Clamp coordinates to viewport to avoid issues with elementsFromPoint + const viewportWidth = document.documentElement.clientWidth; + const viewportHeight = document.documentElement.clientHeight; + const clientX = + dragElementCenterX < 0 + ? 0 + : dragElementCenterX > viewportWidth + ? viewportWidth - 1 + : dragElementCenterX; + const clientY = + dragElementCenterY < 0 + ? 0 + : dragElementCenterY > viewportHeight + ? viewportHeight - 1 + : dragElementCenterY; + + // Find the first target element in the stack (topmost visible target) + const elementsUnderDragElement = document.elementsFromPoint(clientX, clientY); + let targetElement = null; + let targetIndex = -1; + let intersectingIndex = -1; + for (const element of elementsUnderDragElement) { + // First, check if the element itself is a target + const directIndex = intersectingTargets.indexOf(element); + if (directIndex !== -1) { + targetElement = element; + intersectingIndex = directIndex; + break; } - const hasPrimary = frontier.hasAttribute(primaryAttrName); - const hasOpposite = frontier.hasAttribute(oppositeAttrName); - // Check if element has both sides (invalid) - if (hasPrimary && hasOpposite) { - const elementSignature = getElementSignature(frontier); - console.warn( - `Sticky frontier element (${elementSignature}) has both ${primarySide} and ${oppositeSide} attributes. - A sticky frontier should only have one side attribute.`, - ); + // Special case: if element is or and not in targets, + // try to find its corresponding element + if (!isTableCell(element)) { continue; } - const attrName = hasPrimary ? primaryAttrName : oppositeAttrName; - const attributeValue = frontier.getAttribute(attrName); - if (attributeValue && name) { - const frontierNames = attributeValue.split(","); - const isMatching = frontierNames.some( - (frontierName) => - frontierName.trim().toLowerCase() === name.toLowerCase(), - ); - if (!isMatching) { - continue; + try_col: { + if (!someTargetIsCol) { + break try_col; + } + const tableCellCol = findTableCellCol(element); + if (!tableCellCol) { + break try_col; + } + const colIndex = intersectingTargets.indexOf(tableCellCol); + if (colIndex === -1) { + break try_col; } + targetElement = tableCellCol; + intersectingIndex = colIndex; + break; + } + try_tr: { + if (!someTargetIsTr) { + break try_tr; + } + const tableRow = element.closest("tr"); + const rowIndex = targetElements.indexOf(tableRow); + if (rowIndex === -1) { + break try_tr; + } + targetElement = tableRow; + intersectingIndex = intersectingTargets.indexOf(tableRow); + break; } - const frontierBounds = getScrollRelativeRect(frontier, scrollContainer); - const stickyFrontierObject = { - type: "sticky-frontier", - element: frontier, - side: hasPrimary ? primarySide : oppositeSide, - bounds: frontierBounds, - name: `sticky_frontier_${hasPrimary ? primarySide : oppositeSide} (${getElementSignature(frontier)})`, - }; - matchingStickyFrontiers.push(stickyFrontierObject); } - return matchingStickyFrontiers; + if (!targetElement) { + targetElement = intersectingTargets[0]; + intersectingIndex = 0; + } + targetIndex = targetElements.indexOf(targetElement); + + // Determine position within the target for both axes. + // + // Use the leading edge of the dragged element (in the direction of movement) + // compared against the target's center: + // - Dragging down: "after" as soon as the bottom crosses the target center. + // - Dragging up: "before" as soon as the top crosses the target center. + // - Not moving: center-vs-center fallback. + // + // This gives consistent, predictable thresholds regardless of element size. + const targetRect = targetElement.getBoundingClientRect(); + const targetCenterX = targetRect.left + targetRect.width / 2; + const targetCenterY = targetRect.top + targetRect.height / 2; + const { intentGoingDown, intentGoingUp, intentGoingRight, intentGoingLeft } = + gestureInfo; + let sideY; + if (intentGoingDown) { + sideY = dragElementRect.bottom > targetCenterY ? "end" : "start"; + } else if (intentGoingUp) { + sideY = dragElementRect.top < targetCenterY ? "start" : "end"; + } else { + sideY = dragElementCenterY < targetCenterY ? "start" : "end"; + } + let sideX; + if (intentGoingRight) { + sideX = dragElementRect.right > targetCenterX ? "end" : "start"; + } else if (intentGoingLeft) { + sideX = dragElementRect.left < targetCenterX ? "start" : "end"; + } else { + sideX = dragElementCenterX < targetCenterX ? "start" : "end"; + } + const result = { + // NOTE: avoid relying on `index` in application code. The targetElements + // array may be dynamically filtered (e.g. excluding the grabbed element), + // making this index inconsistent with the full list. Use `element` instead + // and look up its position yourself from your own data source. + index: targetIndex, + element: targetElement, + elementSide: { + x: sideX, + y: sideY, + }, + // Index within the intersecting subset — could be useful to know how many + // elements were overlapping, but rarely needed in practice + intersectingIndex, + intersecting: intersectingTargets, + }; + return result; }; -const dragStyleController = createStyleController("drag_to_move"); +const rectangleAreIntersecting = (r1, r2) => { + return !( + r2.left > r1.right || + r2.right < r1.left || + r2.top > r1.bottom || + r2.bottom < r1.top + ); +}; + +const isTableCell = (el) => { + return el.tagName === "TD" || el.tagName === "TH"; +}; /** - * Creates a gesture controller that moves elements via drag. - * - * Wraps `createDragGestureController` and adds: - * - Element translation via CSS transform (translate only; other existing transforms are preserved) - * - Auto-scroll while dragging near scroll-container edges - * - Constraints (area boundaries, obstacle elements) - * - * The returned controller exposes a `grab(options)` / `grabViaPointer(event, options)` method. - * Key grab options: - * - `element`: the element whose position drives layout calculations (scroll-container detection, - * constraints, auto-scroll). Sets `data-grabbed` during the drag. - * - `referenceElement`: optional sticky-frontier / obstacle reference, defaults to `element`. - * - `elementToMove`: optional different element to actually translate (e.g. a drag clone). - * If omitted, `element` is translated. The translate is read from `dragStyleController` - * at grab time so any pre-existing translate is accumulated rather than reset. - * - * A `transform` already on the moved element (rotate, scale…) is preserved and does - * not disturb the movement. `rotate` and `scale` set as individual CSS properties do: - * they apply outside `transform`, where nothing the gesture writes can reach them — - * put those on a child element instead (a warning says so in dev). - * - * @param {object} [options] - * @param {boolean} [options.stickyFrontiers=true] - * Shrinks the auto-scroll area at sticky boundaries (elements with `data-sticky-left` / - * `data-sticky-top`). - * @param {number} [options.autoScrollAreaPadding=0] - * Extra padding (px) subtracted from each edge of the auto-scroll trigger area. - * @param {string|object|function} [options.areaConstraint="scroll"] - * Constrains where the element can be dragged. - * `"scroll"` — bounded by the full scroll area. - * `"scrollport"` — bounded by the visible viewport of the scroll container. - * `"none"` — no area constraint. - * `{left, top, right, bottom}` — fixed bounds (values may be functions receiving context). - * `function` — called each drag frame, must return a `{left,top,right,bottom}` object. - * @param {Element} [options.obstaclesContainer] - * Container to look for obstacle elements in. Defaults to the scroll container. - * @param {string} [options.obstacleAttributeName="data-drag-obstacle"] - * Attribute that marks obstacle elements. - * @param {boolean} [options.showConstraintFeedbackLine=false] - * Renders a visual line when the pointer deviates from the element due to constraints. - * @param {boolean} [options.showDebugMarkers=false] - * Renders debug markers for constraint regions. - * @param {"commit"|"cancel"|"cancel-animated"|"manual"} [options.releasePositionEffect="commit"] - * Controls what happens to the translated position on release. - * - `"commit"`: bakes the translate into inline styles so the element stays put (default). - * - `"cancel"`: discards the translate so the element snaps back to its original position. - * - `"cancel-animated"`: same, travelling back to it over `cancelAnimationDuration`. - * - `"manual"`: does nothing — the caller is responsible for clearing or committing - * the transform via `dragStyleController`. - * @param {number} [options.cancelAnimationDuration=200] - * Duration (ms) of the way back for `"cancel-animated"`. - * @param {string} [options.cancelAnimationEasing="ease-out"] - * Easing of the way back for `"cancel-animated"`. - * @returns {object} Drag gesture controller with augmented `grab()` / `grabViaPointer()` methods. - * - * `gestureInfo` gains `cancelPosition()`, `commitPosition()` and - * `cancelPositionAnimated({duration, easing})` — the last returns the `Animation` - * playing the way back (`null` when the element was already home), so a caller - * on `"manual"` can decide between thrown and put back, and still await the - * landing. + * Find the corresponding element for a given or cell + * @param {Element} cellElement - The or element + * @param {Element[]} targetColElements - Array of elements to search in + * @returns {Element|null} The corresponding element or null if not found */ -const createDragToMoveGestureController = ({ - stickyFrontiers = true, - autoScrollAreaPadding = 0, - areaConstraint = "scroll", - obstaclesContainer, - obstacleAttributeName = "data-drag-obstacle", - showConstraintFeedbackLine = false, - showDebugMarkers = false, - releasePositionEffect = "commit", - cancelAnimationDuration = 200, - cancelAnimationEasing = "ease-out", - ...options -} = {}) => { - const initGrabToMoveElement = ( - dragGesture, - { element, referenceElement, elementToMove, convertScrollablePosition }, - ) => { - const scrollContainer = dragGesture.gestureInfo.scrollContainer; - - const direction = dragGesture.gestureInfo.direction; - // elementImpacted is either an externally provided elementToMove (e.g. a drag clone) - const elementImpacted = elementToMove || element; - // elementImpacted is either an externally provided elementToMove - // (e.g. a drag clone passed by the caller) or the element itself. - // Capture any pre-existing translate so we can accumulate on top of it - // rather than resetting it to zero on the first drag event. - const transformAtGrab = dragStyleController.getUnderlyingValue( - elementImpacted, - "transform", - ); - const translateXAtGrab = transformAtGrab.translateX; - const translateYAtGrab = transformAtGrab.translateY; - - const cancelPosition = () => { - dragStyleController.clear(elementImpacted); - }; - // Reading the transform on either side of the clear is what lets this work - // without knowing anything about the element: how it looked while held and - // how it looks once let go are both just computed transforms, and the - // animation has only to bridge the two. - const cancelPositionAnimated = ({ - duration = cancelAnimationDuration, - easing = cancelAnimationEasing, - } = {}) => { - const transformWhileHeld = getComputedStyle(elementImpacted).transform; - cancelPosition(); - const transformAtRest = getComputedStyle(elementImpacted).transform; - if (transformWhileHeld === transformAtRest) { - return null; - } - // No fill: the element already sits at its resting transform, the - // animation only replays the way back to it. - return elementImpacted.animate( - [{ transform: transformWhileHeld }, { transform: transformAtRest }], - { duration, easing }, - ); - }; - const commitPosition = () => { - dragStyleController.commit(elementImpacted); - }; - dragGesture.gestureInfo.cancelPosition = cancelPosition; - dragGesture.gestureInfo.cancelPositionAnimated = cancelPositionAnimated; - dragGesture.gestureInfo.commitPosition = commitPosition; - - dragGesture.addReleaseCallback(() => { - if (releasePositionEffect === "cancel") { - cancelPosition(); - } else if (releasePositionEffect === "cancel-animated") { - cancelPositionAnimated(); - } else if (releasePositionEffect === "commit") { - commitPosition(); - } - // "manual": caller handles cleanup, do nothing. - }); - - let elementWidth; - let elementHeight; - { - const updateElementDimension = () => { - const elementRect = element.getBoundingClientRect(); - elementWidth = elementRect.width; - elementHeight = elementRect.height; - }; - updateElementDimension(); - dragGesture.addBeforeDragCallback(updateElementDimension); - } +const findTableCellCol = (cellElement) => { + const table = cellElement.closest("table"); + const colgroup = table.querySelector("colgroup"); + if (!colgroup) { + return null; + } + const cols = colgroup.querySelectorAll("col"); + const columnIndex = cellElement.cellIndex; + const correspondingCol = cols[columnIndex]; + return correspondingCol; +}; - let scrollArea; - { - // Snapshot at grab time so that DOM mutations during dragging - // (e.g. items shifting) don't change the scrollable boundary mid-drag. - scrollArea = { - left: 0, - top: 0, - right: scrollContainer.scrollWidth, - bottom: scrollContainer.scrollHeight, - }; +// Temporarily attach to the element so inherited CSS vars resolve correctly, +// then snapshot all drop-hint custom properties onto the scroll container +// so they survive once the element moves to the scroll container. +const moveCSSVars = (vars, fromEl, toEl) => { + const fromComputedStyle = getComputedStyle(fromEl); + const savedVars = {}; + for (const varName of vars) { + const value = fromComputedStyle.getPropertyValue(varName).trim(); + if (value) { + savedVars[varName] = toEl.style.getPropertyValue(varName); + toEl.style.setProperty(varName, value); } + } - let scrollport; - let autoScrollArea; - { - // scrollBox is the fixed bounding rect of the scroll container viewport. - // scrollport is recomputed before each drag event to account for scrolling. - const scrollBox = getScrollBox(scrollContainer); - const updateScrollportAndAutoScrollArea = () => { - scrollport = getScrollport(scrollBox, scrollContainer); - autoScrollArea = scrollport; - if (stickyFrontiers) { - autoScrollArea = applyStickyFrontiersToAutoScrollArea( - autoScrollArea, - { - scrollContainer, - direction, - // dragGestureName, - }, - ); - } - if (autoScrollAreaPadding > 0) { - autoScrollArea = { - paddingLeft: autoScrollAreaPadding, - paddingTop: autoScrollAreaPadding, - paddingRight: autoScrollAreaPadding, - paddingBottom: autoScrollAreaPadding, - left: autoScrollArea.left + autoScrollAreaPadding, - top: autoScrollArea.top + autoScrollAreaPadding, - right: autoScrollArea.right - autoScrollAreaPadding, - bottom: autoScrollArea.bottom - autoScrollAreaPadding, - }; + return () => { + for (const varName of vars) { + if (varName in savedVars) { + if (savedVars[varName]) { + toEl.style.setProperty(varName, savedVars[varName]); + } else { + toEl.style.removeProperty(varName); } - }; - updateScrollportAndAutoScrollArea(); - dragGesture.addBeforeDragCallback(updateScrollportAndAutoScrollArea); + } } + }; +}; - // Set up dragging attribute - element.setAttribute("data-grabbed", ""); - dragGesture.addReleaseCallback(() => { - element.removeAttribute("data-grabbed"); - }); +const applyStickyFrontiersToAutoScrollArea = ( + autoScrollArea, + { direction, scrollContainer, dragName }, +) => { + let { left, right, top, bottom } = autoScrollArea; - // Will be used for dynamic constraints on sticky elements - let hasCrossedScrollportLeftOnce = false; - let hasCrossedScrollportTopOnce = false; - const dragConstraints = initDragConstraints(dragGesture, { - areaConstraint, - obstaclesContainer: obstaclesContainer || scrollContainer, - obstacleAttributeName, - showConstraintFeedbackLine, - showDebugMarkers, - referenceElement, - }); - dragGesture.addBeforeDragCallback( - (layoutRequested, currentLayout, limitLayout, { dragEvent }) => { - dragConstraints.applyConstraints( - layoutRequested, - currentLayout, - limitLayout, - { - elementWidth, - elementHeight, - scrollArea, - scrollport, - hasCrossedScrollportLeftOnce, - hasCrossedScrollportTopOnce, - autoScrollArea, - dragEvent, - }, - ); + if (direction.x) { + const horizontalStickyFrontiers = createStickyFrontierOnAxis( + scrollContainer, + { + name: dragName, + scrollContainer, + primarySide: "left", + oppositeSide: "right", }, ); + for (const horizontalStickyFrontier of horizontalStickyFrontiers) { + const { side, bounds, element } = horizontalStickyFrontier; + if (side === "left") { + if (bounds.right <= left) { + continue; + } + left = bounds.right; + continue; + } + // right + if (bounds.left >= right) { + continue; + } + right = bounds.left; + continue; + } + } - const dragToMove = (gestureInfo) => { - const { isGoingDown, isGoingUp, isGoingLeft, isGoingRight, layout } = - gestureInfo; - const left = layout.left; - const top = layout.top; - const right = left + elementWidth; - const bottom = top + elementHeight; - + if (direction.y) { + const verticalStickyFrontiers = createStickyFrontierOnAxis( + scrollContainer, { - hasCrossedScrollportLeftOnce = - hasCrossedScrollportLeftOnce || left < scrollport.left; - hasCrossedScrollportTopOnce = - hasCrossedScrollportTopOnce || top < scrollport.top; + name: dragName, + scrollContainer, + primarySide: "top", + oppositeSide: "bottom", + }, + ); + for (const verticalStickyFrontier of verticalStickyFrontiers) { + const { side, bounds, element } = verticalStickyFrontier; - const getScrollMove = (axis) => { - const isGoingPositive = axis === "x" ? isGoingRight : isGoingDown; - if (isGoingPositive) { - const elementEnd = axis === "x" ? right : bottom; - const autoScrollAreaEnd = - axis === "x" ? autoScrollArea.right : autoScrollArea.bottom; + // Frontier acts as a top barrier - constrains from the bottom edge of the frontier + if (side === "top") { + if (bounds.bottom <= top) { + continue; + } + top = bounds.bottom; + continue; + } - if (elementEnd <= autoScrollAreaEnd) { - return 0; - } - const scrollAmountNeeded = elementEnd - autoScrollAreaEnd; - return scrollAmountNeeded; - } + // Frontier acts as a bottom barrier - constrains from the top edge of the frontier + if (bounds.top >= bottom) { + continue; + } + bottom = bounds.top; + continue; + } + } - const isGoingNegative = axis === "x" ? isGoingLeft : isGoingUp; - if (!isGoingNegative) { - return 0; - } + return { left, right, top, bottom }; +}; - const referenceOrEl = referenceElement || element; - const canAutoScrollNegative = - axis === "x" - ? !referenceOrEl.hasAttribute("data-sticky-left") || - hasCrossedScrollportLeftOnce - : !referenceOrEl.hasAttribute("data-sticky-top") || - hasCrossedScrollportTopOnce; - if (!canAutoScrollNegative) { - return 0; - } - - const elementStart = axis === "x" ? left : top; - const autoScrollAreaStart = - axis === "x" ? autoScrollArea.left : autoScrollArea.top; - if (elementStart >= autoScrollAreaStart) { - return 0; - } - - const scrollAmountNeeded = autoScrollAreaStart - elementStart; - return -scrollAmountNeeded; - }; - - let scrollLeftTarget; - let scrollTopTarget; - if (direction.x) { - const containerScrollLeftMove = getScrollMove("x"); - if (containerScrollLeftMove) { - scrollLeftTarget = - scrollContainer.scrollLeft + containerScrollLeftMove; - } - } - if (direction.y) { - const containerScrollTopMove = getScrollMove("y"); - if (containerScrollTopMove) { - scrollTopTarget = - scrollContainer.scrollTop + containerScrollTopMove; - } - } - // now we know what to do, do it - if (scrollLeftTarget !== undefined) { - scrollContainer.scrollLeft = scrollLeftTarget; - } - if (scrollTopTarget !== undefined) { - scrollContainer.scrollTop = scrollTopTarget; - } - } - - { - const { scrollableLeft, scrollableTop } = layout; - const [positionedLeft, positionedTop] = convertScrollablePosition( - scrollableLeft, - scrollableTop, - ); - // Build the transform to apply, preserving any transforms that were - // already on the element before the grab (e.g. rotate from another - // controller), and accumulating from the pre-grab translate baseline. - // The translate keys are seeded HERE, before the spread, and not merely - // assigned below: a transform object is serialized in key order, and in a - // transform list every function transforms the frame of the ones after it. - // A translate written after a rotate or a scale therefore travels rotated - // and scaled — the element drifts away from the pointer, proportionally to - // the distance covered. Dragging moves things on screen, so its translate - // has to come first, whatever else the element carries. The spread still - // wins on the value when the element already had a translate of its own. - const transform = { translateX: 0, translateY: 0, ...transformAtGrab }; - if (direction.x) { - const leftTarget = positionedLeft; - const leftAtGrab = dragGesture.gestureInfo.leftAtGrab; - const leftDelta = leftTarget - leftAtGrab; - const translateX = translateXAtGrab - ? translateXAtGrab + leftDelta - : leftDelta; - transform.translateX = translateX; - } - if (direction.y) { - const topTarget = positionedTop; - const topAtGrab = dragGesture.gestureInfo.topAtGrab; - const topDelta = topTarget - topAtGrab; - const translateY = translateYAtGrab - ? translateYAtGrab + topDelta - : topDelta; - transform.translateY = translateY; - } - dragStyleController.set(elementImpacted, { - transform, - }); - } - }; - dragGesture.addDragCallback(dragToMove); - }; - - const dragGestureController = createDragGestureController(options); - const grab = dragGestureController.grab; - dragGestureController.grab = ({ - element, - referenceElement, - elementToMove, - event, - ...rest - } = {}) => { - const scrollContainer = getScrollContainer(referenceElement || element); - const [ - elementScrollableLeft, - elementScrollableTop, - convertScrollablePosition, - ] = createDragElementPositioner(element, referenceElement, elementToMove); - const dragGesture = grab({ - element, - scrollContainer, - layoutScrollableLeft: elementScrollableLeft, - layoutScrollableTop: elementScrollableTop, - event, - ...rest, - }); - initGrabToMoveElement(dragGesture, { - element, - referenceElement, - elementToMove, - convertScrollablePosition, - }); - return dragGesture; - }; - - return dragGestureController; -}; - -/** - * Detects the drop target based on what element is actually under the mouse cursor. - * Uses document.elementsFromPoint() to respect visual stacking order naturally. - * - * @param {Object} gestureInfo - Gesture information - * @param {Element[]} targetElements - Array of potential drop target elements - * @param {object} [options] - * @param {Element} [options.dragElement] - The element being dragged. When provided and - * `fallbackToEdge` is true, used to compute the fallback rect. - * @param {boolean} [options.fallbackToEdge=false] - When true and the drag element does - * not intersect any target, falls back to the first item (if above all items) or the - * last item (if below all items) so there is always a valid drop target at list edges. - * @returns {Object|null} Drop target info with elementSide or null if no valid target found - */ -const getDropTargetInfo = ( - gestureInfo, - targetElements, - { fallbackToEdge = false } = {}, +const createStickyFrontierOnAxis = ( + element, + { name, scrollContainer, primarySide, oppositeSide }, ) => { - const dragElement = gestureInfo.elementImpacted || gestureInfo.element; - const dragElementRect = dragElement.getBoundingClientRect(); - const intersectingTargets = []; - let someTargetIsCol; - let someTargetIsTr; - for (const targetElement of targetElements) { - const targetRect = targetElement.getBoundingClientRect(); - if (!rectangleAreIntersecting(dragElementRect, targetRect)) { + const primaryAttrName = `data-drag-sticky-${primarySide}-frontier`; + const oppositeAttrName = `data-drag-sticky-${oppositeSide}-frontier`; + const frontiers = element.querySelectorAll( + `[${primaryAttrName}], [${oppositeAttrName}]`, + ); + const matchingStickyFrontiers = []; + for (const frontier of frontiers) { + if (frontier.closest("[data-drag-ignore]")) { continue; } - if (!someTargetIsCol && targetElement.tagName === "COL") { - someTargetIsCol = true; - } - if (!someTargetIsTr && targetElement.tagName === "TR") { - someTargetIsTr = true; + const hasPrimary = frontier.hasAttribute(primaryAttrName); + const hasOpposite = frontier.hasAttribute(oppositeAttrName); + // Check if element has both sides (invalid) + if (hasPrimary && hasOpposite) { + const elementSignature = getElementSignature(frontier); + console.warn( + `Sticky frontier element (${elementSignature}) has both ${primarySide} and ${oppositeSide} attributes. + A sticky frontier should only have one side attribute.`, + ); + continue; } - intersectingTargets.push(targetElement); - } - - if (intersectingTargets.length === 0) { - if (fallbackToEdge) { - const dragElement = gestureInfo.elementImpacted || gestureInfo.element; - const dragElementRect = dragElement.getBoundingClientRect(); - const firstItem = targetElements[0]; - const lastItem = targetElements[targetElements.length - 1]; - if ( - firstItem && - dragElementRect.bottom < firstItem.getBoundingClientRect().top - ) { - // Drag element is above all items → treat as hovering the first item from the top. - return { - element: firstItem, - elementSide: { x: "start", y: "start" }, - index: 0, - intersectingIndex: 0, - intersecting: [firstItem], - }; - } - if ( - lastItem && - dragElementRect.top > lastItem.getBoundingClientRect().bottom - ) { - // Drag element is below all items → treat as hovering the last item from the bottom. - return { - element: lastItem, - elementSide: { x: "start", y: "end" }, - index: targetElements.length - 1, - intersectingIndex: 0, - intersecting: [lastItem], - }; + const attrName = hasPrimary ? primaryAttrName : oppositeAttrName; + const attributeValue = frontier.getAttribute(attrName); + if (attributeValue && name) { + const frontierNames = attributeValue.split(","); + const isMatching = frontierNames.some( + (frontierName) => + frontierName.trim().toLowerCase() === name.toLowerCase(), + ); + if (!isMatching) { + continue; } } - return null; + const frontierBounds = getScrollRelativeRect(frontier, scrollContainer); + const stickyFrontierObject = { + type: "sticky-frontier", + element: frontier, + side: hasPrimary ? primarySide : oppositeSide, + bounds: frontierBounds, + name: `sticky_frontier_${hasPrimary ? primarySide : oppositeSide} (${getElementSignature(frontier)})`, + }; + matchingStickyFrontiers.push(stickyFrontierObject); } + return matchingStickyFrontiers; +}; - const dragElementCenterX = dragElementRect.left + dragElementRect.width / 2; - const dragElementCenterY = dragElementRect.top + dragElementRect.height / 2; - // Clamp coordinates to viewport to avoid issues with elementsFromPoint - const viewportWidth = document.documentElement.clientWidth; - const viewportHeight = document.documentElement.clientHeight; - const clientX = - dragElementCenterX < 0 - ? 0 - : dragElementCenterX > viewportWidth - ? viewportWidth - 1 - : dragElementCenterX; - const clientY = - dragElementCenterY < 0 - ? 0 - : dragElementCenterY > viewportHeight - ? viewportHeight - 1 - : dragElementCenterY; +installImportMetaCssBuild(import.meta);/** + * A drag, and what it is FOR. + * + * What a hand does is always the same — pick the thing up, carry it, let go — so + * the gesture is not what distinguishes these. What distinguishes them is the + * outcome the caller asked for, and that is what `startDragTo` takes: + * + * - **move**: it stays where it was put. The element ITSELF travels and keeps the + * place the hand gave it. + * - **reorder**: it takes a place in a list. A COPY travels while the original + * keeps its place in the layout, which is what makes the gesture possible at + * all — nothing else moves while the hand looks for a place, so there is a + * stable row of items to look between. + * - **toss**: it is gotten rid of. The same copy, for the opposite reason: the + * original stays until the answer says it is really gone. + * + * The caller lists which outcomes ITS element can answer, and only the machinery + * those need runs: no copy for a move, no drop hint for something that can only be + * thrown away, no landing looked for where nothing lands. `reorder` and `toss` + * combine (dropped on a row, or thrown off the screen); `move` and `reorder` cannot + * both be true of one release, and the caller is the one who must not ask for both. + * + * `createDragToMoveGestureController` below is the layer under all of that — the + * translation, the auto-scroll, the constraints — and stays usable on its own for + * anything that is none of the three (a table column being dragged, a sticky + * frontier being moved). + */ +const dragStyleController = createStyleController("drag_to_move"); - // Find the first target element in the stack (topmost visible target) - const elementsUnderDragElement = document.elementsFromPoint(clientX, clientY); - let targetElement = null; - let targetIndex = -1; - let intersectingIndex = -1; - for (const element of elementsUnderDragElement) { - // First, check if the element itself is a target - const directIndex = intersectingTargets.indexOf(element); - if (directIndex !== -1) { - targetElement = element; - intersectingIndex = directIndex; - break; - } - // Special case: if element is or and not in targets, - // try to find its corresponding element - if (!isTableCell(element)) { - continue; - } - try_col: { - if (!someTargetIsCol) { - break try_col; - } - const tableCellCol = findTableCellCol(element); - if (!tableCellCol) { - break try_col; - } - const colIndex = intersectingTargets.indexOf(tableCellCol); - if (colIndex === -1) { - break try_col; - } - targetElement = tableCellCol; - intersectingIndex = colIndex; - break; - } - try_tr: { - if (!someTargetIsTr) { - break try_tr; - } - const tableRow = element.closest("tr"); - const rowIndex = targetElements.indexOf(tableRow); - if (rowIndex === -1) { - break try_tr; - } - targetElement = tableRow; - intersectingIndex = intersectingTargets.indexOf(tableRow); - break; - } - } - if (!targetElement) { - targetElement = intersectingTargets[0]; - intersectingIndex = 0; - } - targetIndex = targetElements.indexOf(targetElement); - - // Determine position within the target for both axes. - // - // Use the leading edge of the dragged element (in the direction of movement) - // compared against the target's center: - // - Dragging down: "after" as soon as the bottom crosses the target center. - // - Dragging up: "before" as soon as the top crosses the target center. - // - Not moving: center-vs-center fallback. - // - // This gives consistent, predictable thresholds regardless of element size. - const targetRect = targetElement.getBoundingClientRect(); - const targetCenterX = targetRect.left + targetRect.width / 2; - const targetCenterY = targetRect.top + targetRect.height / 2; - const { intentGoingDown, intentGoingUp, intentGoingRight, intentGoingLeft } = - gestureInfo; - let sideY; - if (intentGoingDown) { - sideY = dragElementRect.bottom > targetCenterY ? "end" : "start"; - } else if (intentGoingUp) { - sideY = dragElementRect.top < targetCenterY ? "start" : "end"; - } else { - sideY = dragElementCenterY < targetCenterY ? "start" : "end"; - } - let sideX; - if (intentGoingRight) { - sideX = dragElementRect.right > targetCenterX ? "end" : "start"; - } else if (intentGoingLeft) { - sideX = dragElementRect.left < targetCenterX ? "start" : "end"; - } else { - sideX = dragElementCenterX < targetCenterX ? "start" : "end"; - } - const result = { - // NOTE: avoid relying on `index` in application code. The targetElements - // array may be dynamically filtered (e.g. excluding the grabbed element), - // making this index inconsistent with the full list. Use `element` instead - // and look up its position yourself from your own data source. - index: targetIndex, - element: targetElement, - elementSide: { - x: sideX, - y: sideY, - }, - // Index within the intersecting subset — could be useful to know how many - // elements were overlapping, but rarely needed in practice - intersectingIndex, - intersecting: intersectingTargets, - }; - return result; -}; - -const rectangleAreIntersecting = (r1, r2) => { - return !( - r2.left > r1.right || - r2.right < r1.left || - r2.top > r1.bottom || - r2.bottom < r1.top - ); -}; - -const isTableCell = (el) => { - return el.tagName === "TD" || el.tagName === "TH"; -}; - -/** - * Find the corresponding element for a given or cell - * @param {Element} cellElement - The or element - * @param {Element[]} targetColElements - Array of elements to search in - * @returns {Element|null} The corresponding element or null if not found - */ -const findTableCellCol = (cellElement) => { - const table = cellElement.closest("table"); - const colgroup = table.querySelector("colgroup"); - if (!colgroup) { - return null; - } - const cols = colgroup.querySelectorAll("col"); - const columnIndex = cellElement.cellIndex; - const correspondingCol = cols[columnIndex]; - return correspondingCol; -}; - -// Temporarily attach to the element so inherited CSS vars resolve correctly, -// then snapshot all drop-hint custom properties onto the scroll container -// so they survive once the element moves to the scroll container. -const moveCSSVars = (vars, fromEl, toEl) => { - const fromComputedStyle = getComputedStyle(fromEl); - const savedVars = {}; - for (const varName of vars) { - const value = fromComputedStyle.getPropertyValue(varName).trim(); - if (value) { - savedVars[varName] = toEl.style.getPropertyValue(varName); - toEl.style.setProperty(varName, value); - } - } - - return () => { - for (const varName of vars) { - if (varName in savedVars) { - if (savedVars[varName]) { - toEl.style.setProperty(varName, savedVars[varName]); - } else { - toEl.style.removeProperty(varName); - } - } - } - }; -}; - -installImportMetaCssBuild(import.meta);const css$1 = /* css */` +// How long the copy takes to leave the screen, and to come back. Written into the +// CSS below from here: the flight has to be waited for, and a duration living only +// in a stylesheet is a timing JS cannot read reliably. +const TOSS_DURATION_MS = 320; +// Far enough to be off any screen, in the direction the hand was going. +const TOSS_DISTANCE = 900; +const css$1 = /* css */` /* IN THE PAGE, NOT IN THE LIST: the hint lands on the edge of a row, which for the last one is the very bottom of the scroll area — drawn inside it, the line would push the scrollable area a few pixels further and make a @@ -11453,7 +11179,7 @@ installImportMetaCssBuild(import.meta);const css$1 = /* css */` the pointer), so an I-beam over it would promise something that does not happen: it reads as a plain surface instead. An opted-out area keeps both its cursor and its selection, and never starts a drag (see the check in - startDragToReorder). + startDragTo). Controls inside a source keep their own cursor: cursor is inherited, and anything setting its own (a button's pointer) wins on itself. Only the resting cursor is set here: what it becomes once a drag is under @@ -11471,133 +11197,713 @@ installImportMetaCssBuild(import.meta);const css$1 = /* css */` user-select: auto; } - [navi-drag-clone-source] { - visibility: hidden; + [navi-drag-clone-source] { + visibility: hidden; + } + + [navi-drag-clone-wrapper] { + /* Also a popover (see .navi_drop_hint): in the top layer it is over the + page whatever the page's own stacking is, and the coordinates it is + given are viewport ones — which is what the pointer carrying it works + in. Same UA-style reset as the hint. */ + position: fixed; + inset: auto; + top: var(--clone-top); + left: var(--clone-left); + box-sizing: border-box; + width: var(--clone-width); + height: var(--clone-height); + margin: 0; + padding: 0; + color: inherit; + background: transparent; + border: none; + /* A var, and read from the dragged element (see dragCSSVars): what being + carried LOOKS like belongs to whoever owns the thing — a row lifted off a + list wants this shadow, a sheet of paper leaving a board wants none, and its + shade is a theme's business either way. */ + box-shadow: var(--drag-clone-shadow, 0 12px 28px rgba(0, 0, 0, 0.22)); + opacity: 0.95; + transition: box-shadow 0.15s ease; + pointer-events: none; + /* Nothing in a copy being carried by a pointer is text to select: the + selection belongs to the original, which is still in the page. This is the + one place the rule is unconditional — an element that can be dragged is + usually selectable too (a link is both), and forcing it there would take + away a selection made from outside the element. */ + user-select: none; + overflow: visible; + } + + /* Ce qui a été lancé: il continue dans la direction du geste jusqu'à sortir de + l'écran, et revient par le même chemin si la réponse refuse. */ + [navi-drag-clone-wrapper][data-tossed] { + transition: + translate ${TOSS_DURATION_MS}ms ease-out, + opacity ${TOSS_DURATION_MS}ms ease-out; + } + [navi-drag-clone-wrapper][data-tossed="away"] { + opacity: 0; + } + + [navi-drag-clone] { + transform: scale(var(--drag-clone-scale, 1.03)); + transform-origin: var(--drag-origin); + transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1); + } + + @starting-style { + [navi-drag-clone-wrapper] { + box-shadow: none; + } + + [navi-drag-clone] { + transform: scale(1); + } + } +`; +// At module scope, not inside startDragTo: the cursor rules above say who +// can start a drag, and they have to be true BEFORE anyone drags anything. +import.meta.css = [css$1, "@jsenv/dom/src/interaction/drag/drag_to.js"]; +const dragCSSVars = ["--drop-hint-size", "--drop-hint-background-color", "--drop-hint-border-radius", "--drop-hint-margin-x", "--drop-hint-margin-y", "--drop-hint-arrow-size", "--drag-clone-scale", "--drag-clone-shadow"]; + +/** + * Starts a drag-to-reorder interaction on a list item. + * + * Handles the full reorder UX: + * - Activates only once the intent is established — a short movement with a mouse, a long + * press with a finger (see `dragAfterIntent`), so that neither a click nor a scroll + * reorders anything by accident. + * - Clones the grabbed element and moves the clone while the original stays hidden in place + * (keeps the layout intact so other items don't shift during the drag). + * - CSS vars (`--drop-hint-size`, `--drop-hint-background-color`, etc.) are read from the + * dragged element and moved to `document.documentElement` for the duration of the drag so + * the drop-hint and clone — both in `document.body` — can inherit them. + * - Shows a drop-hint line indicating where the item will land. + * - Drop-target detection is intersection-based: the clone's bounding rect is compared + * against every item that matches `itemSelector` in the scroll container. + * - No-ops are filtered: releasing on the grabbed element itself, or in a position that + * would leave it at exactly the same index, never triggers `onReorder`. + * - On a valid drop, the clone animates to the drop position via the View Transitions API, + * `onReorder` is called inside the transition callback so the DOM update and the animation + * are captured together, then the clone is removed. + * - On a cancelled drop (pointer released with no valid target), the clone is removed + * immediately without calling `onReorder`. + * + * IDs are used as the bridge between DOM elements and JS state because: + * - Not all DOM elements matching `itemSelector` may be valid drop targets + * (holes in the structure), so DOM indices don't reliably map to state indices. + * - Virtual lists render fewer DOM nodes than the total item count, so + * DOM-index-based counting would be wrong. + * + * Any option not listed below is forwarded to `createDragToMoveGestureController` + * (`areaConstraint`, `autoScrollAreaPadding`, `stickyFrontiers`…), except + * `releasePositionEffect`, always `"manual"` here: what moves is the clone, and it + * is removed on release, so there is no position to commit or cancel. + * + * @param {PointerEvent} event + * The `pointerdown` event that may become a reorder. + * @param {object} options + * @param {Element} [options.draggedElement=event.currentTarget] + * The list item to drag. + * @param {Element} [options.containerElement=draggedElement.parentElement] + * Element searched with `itemSelector` to find the items to drop between. + * @param {string} [options.itemSelector] + * CSS selector that matches all list items inside `containerElement`. + * Used for drop-target detection and no-op filtering. Left out, nothing is a + * drop target: no hint is drawn and no reorder can be answered — which is what + * a drag that only ever throws the thing away asks for. + * @param {function} options.getItemId + * Returns the stable ID for a given DOM element. + * Signature: `getItemId(element) → id`. + * @param {function} options.onReorder + * Called when the user drops the item in a new position. + * Signature: `onReorder(fromId, toId, syncCloneWithDropTarget)`. + * - `fromId`: stable ID of the dragged item. + * - `toId`: stable ID of the item to insert before, or `null` to append at the end. + * - `syncCloneWithDropTarget`: call it synchronously inside a + * `document.startViewTransition` callback, next to the DOM mutation, so the + * clone is captured at its landing position. + * @param {(detail: {gestureInfo: object, dropTarget: Element|null}) => "reorder"|"toss"|"cancel"} [options.resolveDrop] + * What THIS release means, when the answer is not simply "a target was found or + * not": the same grab can be meant to reorder or to get rid of the thing, and + * only the caller knows which — far and fast is a throw, over a row is a move. + * Left out, a drop target reorders and anything else is cancelled. + * @param {(detail: {gestureInfo: object}) => Promise|void} [options.onToss] + * The release was a throw. The clone leaves the screen the way it was thrown + * while this runs; it comes back if the promise rejects, because the thing still + * exists and the screen has to say so. + * @param {object} [options.direction={ x: false, y: true }] + * Axes along which dragging is allowed. Passed to `createDragToMoveGestureController`. + * @param {number} [options.threshold=5] + * Distance (px) a mouse must travel before the press becomes a drag. + * @param {boolean|"if-touch"} [options.longPress="if-touch"] + * Which pointers start the drag by holding still instead of by travelling. + * @param {number} [options.longPressDelay=400] + * How long (ms) such a pointer must stay down. + * @param {number} [options.longPressSlop=8] + * How far (px) it may drift during that wait before the press is abandoned. + * @param {function} [options.onPressStart] + * The pointer went down and the wait began (a cue that the press counts). + * @param {function} [options.onPressCancel] + * The pointer moved or lifted before the wait was over. + * @param {function} [options.onPress] + * The wait completed and the item is now held (haptics, scale…). + */ + +/** + * Creates a gesture controller that moves elements via drag. + * + * Wraps `createDragGestureController` and adds: + * - Element translation via CSS transform (translate only; other existing transforms are preserved) + * - Auto-scroll while dragging near scroll-container edges + * - Constraints (area boundaries, obstacle elements) + * + * The returned controller exposes a `grab(options)` / `grabViaPointer(event, options)` method. + * Key grab options: + * - `element`: the element whose position drives layout calculations (scroll-container detection, + * constraints, auto-scroll). Sets `data-grabbed` during the drag. + * - `referenceElement`: optional sticky-frontier / obstacle reference, defaults to `element`. + * - `elementToMove`: optional different element to actually translate (e.g. a drag clone). + * If omitted, `element` is translated. The translate is read from `dragStyleController` + * at grab time so any pre-existing translate is accumulated rather than reset. + * + * A `transform` already on the moved element (rotate, scale…) is preserved and does + * not disturb the movement. `rotate` and `scale` set as individual CSS properties do: + * they apply outside `transform`, where nothing the gesture writes can reach them — + * put those on a child element instead (a warning says so in dev). + * + * @param {object} [options] + * @param {boolean} [options.stickyFrontiers=true] + * Shrinks the auto-scroll area at sticky boundaries (elements with `data-sticky-left` / + * `data-sticky-top`). + * @param {number} [options.autoScrollAreaPadding=0] + * Extra padding (px) subtracted from each edge of the auto-scroll trigger area. + * @param {string|object|function} [options.areaConstraint="scroll"] + * Constrains where the element can be dragged. + * `"scroll"` — bounded by the full scroll area. + * `"scrollport"` — bounded by the visible viewport of the scroll container. + * `"none"` — no area constraint. + * `{left, top, right, bottom}` — fixed bounds (values may be functions receiving context). + * `function` — called each drag frame, must return a `{left,top,right,bottom}` object. + * @param {Element} [options.obstaclesContainer] + * Container to look for obstacle elements in. Defaults to the scroll container. + * @param {string} [options.obstacleAttributeName="data-drag-obstacle"] + * Attribute that marks obstacle elements. + * @param {boolean} [options.showConstraintFeedbackLine=false] + * Renders a visual line when the pointer deviates from the element due to constraints. + * @param {boolean} [options.showDebugMarkers=false] + * Renders debug markers for constraint regions. + * @param {"commit"|"cancel"|"cancel-animated"|"manual"} [options.releasePositionEffect="commit"] + * Controls what happens to the translated position on release. + * - `"commit"`: bakes the translate into inline styles so the element stays put (default). + * - `"cancel"`: discards the translate so the element snaps back to its original position. + * - `"cancel-animated"`: same, travelling back to it over `cancelAnimationDuration`. + * - `"manual"`: does nothing — the caller is responsible for clearing or committing + * the transform via `dragStyleController`. + * @param {number} [options.cancelAnimationDuration=200] + * Duration (ms) of the way back for `"cancel-animated"`. + * @param {string} [options.cancelAnimationEasing="ease-out"] + * Easing of the way back for `"cancel-animated"`. + * @returns {object} Drag gesture controller with augmented `grab()` / `grabViaPointer()` methods. + * + * `gestureInfo` gains `cancelPosition()`, `commitPosition()` and + * `cancelPositionAnimated({duration, easing})` — the last returns the `Animation` + * playing the way back (`null` when the element was already home), so a caller + * on `"manual"` can decide between thrown and put back, and still await the + * landing. + */ +const createDragToMoveGestureController = ({ + stickyFrontiers = true, + autoScrollAreaPadding = 0, + areaConstraint = "scroll", + obstaclesContainer, + obstacleAttributeName = "data-drag-obstacle", + showConstraintFeedbackLine = false, + showDebugMarkers = false, + releasePositionEffect = "commit", + cancelAnimationDuration = 200, + cancelAnimationEasing = "ease-out", + ...options +} = {}) => { + const initGrabToMoveElement = (dragGesture, { + element, + referenceElement, + elementToMove, + convertScrollablePosition + }) => { + const scrollContainer = dragGesture.gestureInfo.scrollContainer; + const direction = dragGesture.gestureInfo.direction; + // elementImpacted is either an externally provided elementToMove (e.g. a drag clone) + const elementImpacted = elementToMove || element; + // elementImpacted is either an externally provided elementToMove + // (e.g. a drag clone passed by the caller) or the element itself. + // Capture any pre-existing translate so we can accumulate on top of it + // rather than resetting it to zero on the first drag event. + const transformAtGrab = dragStyleController.getUnderlyingValue(elementImpacted, "transform"); + const translateXAtGrab = transformAtGrab.translateX; + const translateYAtGrab = transformAtGrab.translateY; + const cancelPosition = () => { + dragStyleController.clear(elementImpacted); + }; + // Reading the transform on either side of the clear is what lets this work + // without knowing anything about the element: how it looked while held and + // how it looks once let go are both just computed transforms, and the + // animation has only to bridge the two. + const cancelPositionAnimated = ({ + duration = cancelAnimationDuration, + easing = cancelAnimationEasing + } = {}) => { + const transformWhileHeld = getComputedStyle(elementImpacted).transform; + cancelPosition(); + const transformAtRest = getComputedStyle(elementImpacted).transform; + if (transformWhileHeld === transformAtRest) { + return null; + } + // No fill: the element already sits at its resting transform, the + // animation only replays the way back to it. + return elementImpacted.animate([{ + transform: transformWhileHeld + }, { + transform: transformAtRest + }], { + duration, + easing + }); + }; + const commitPosition = () => { + dragStyleController.commit(elementImpacted); + }; + dragGesture.gestureInfo.cancelPosition = cancelPosition; + dragGesture.gestureInfo.cancelPositionAnimated = cancelPositionAnimated; + dragGesture.gestureInfo.commitPosition = commitPosition; + dragGesture.addReleaseCallback(() => { + if (releasePositionEffect === "cancel") { + cancelPosition(); + } else if (releasePositionEffect === "cancel-animated") { + cancelPositionAnimated(); + } else if (releasePositionEffect === "commit") { + commitPosition(); + } + // "manual": caller handles cleanup, do nothing. + }); + let elementWidth; + let elementHeight; + { + const updateElementDimension = () => { + const elementRect = element.getBoundingClientRect(); + elementWidth = elementRect.width; + elementHeight = elementRect.height; + }; + updateElementDimension(); + dragGesture.addBeforeDragCallback(updateElementDimension); + } + let scrollArea; + { + // Snapshot at grab time so that DOM mutations during dragging + // (e.g. items shifting) don't change the scrollable boundary mid-drag. + scrollArea = { + left: 0, + top: 0, + right: scrollContainer.scrollWidth, + bottom: scrollContainer.scrollHeight + }; + } + let scrollport; + let autoScrollArea; + { + // scrollBox is the fixed bounding rect of the scroll container viewport. + // scrollport is recomputed before each drag event to account for scrolling. + const scrollBox = getScrollBox(scrollContainer); + const updateScrollportAndAutoScrollArea = () => { + scrollport = getScrollport(scrollBox, scrollContainer); + autoScrollArea = scrollport; + if (stickyFrontiers) { + autoScrollArea = applyStickyFrontiersToAutoScrollArea(autoScrollArea, { + scrollContainer, + direction + // dragGestureName, + }); + } + if (autoScrollAreaPadding > 0) { + autoScrollArea = { + paddingLeft: autoScrollAreaPadding, + paddingTop: autoScrollAreaPadding, + paddingRight: autoScrollAreaPadding, + paddingBottom: autoScrollAreaPadding, + left: autoScrollArea.left + autoScrollAreaPadding, + top: autoScrollArea.top + autoScrollAreaPadding, + right: autoScrollArea.right - autoScrollAreaPadding, + bottom: autoScrollArea.bottom - autoScrollAreaPadding + }; + } + }; + updateScrollportAndAutoScrollArea(); + dragGesture.addBeforeDragCallback(updateScrollportAndAutoScrollArea); + } + + // Set up dragging attribute + element.setAttribute("data-grabbed", ""); + dragGesture.addReleaseCallback(() => { + element.removeAttribute("data-grabbed"); + }); + + // Will be used for dynamic constraints on sticky elements + let hasCrossedScrollportLeftOnce = false; + let hasCrossedScrollportTopOnce = false; + const dragConstraints = initDragConstraints(dragGesture, { + areaConstraint, + obstaclesContainer: obstaclesContainer || scrollContainer, + obstacleAttributeName, + showConstraintFeedbackLine, + showDebugMarkers, + referenceElement + }); + dragGesture.addBeforeDragCallback((layoutRequested, currentLayout, limitLayout, { + dragEvent + }) => { + dragConstraints.applyConstraints(layoutRequested, currentLayout, limitLayout, { + elementWidth, + elementHeight, + scrollArea, + scrollport, + hasCrossedScrollportLeftOnce, + hasCrossedScrollportTopOnce, + autoScrollArea, + dragEvent + }); + }); + const dragToMove = gestureInfo => { + const { + isGoingDown, + isGoingUp, + isGoingLeft, + isGoingRight, + layout + } = gestureInfo; + const left = layout.left; + const top = layout.top; + const right = left + elementWidth; + const bottom = top + elementHeight; + { + hasCrossedScrollportLeftOnce = hasCrossedScrollportLeftOnce || left < scrollport.left; + hasCrossedScrollportTopOnce = hasCrossedScrollportTopOnce || top < scrollport.top; + const getScrollMove = axis => { + const isGoingPositive = axis === "x" ? isGoingRight : isGoingDown; + if (isGoingPositive) { + const elementEnd = axis === "x" ? right : bottom; + const autoScrollAreaEnd = axis === "x" ? autoScrollArea.right : autoScrollArea.bottom; + if (elementEnd <= autoScrollAreaEnd) { + return 0; + } + const scrollAmountNeeded = elementEnd - autoScrollAreaEnd; + return scrollAmountNeeded; + } + const isGoingNegative = axis === "x" ? isGoingLeft : isGoingUp; + if (!isGoingNegative) { + return 0; + } + const referenceOrEl = referenceElement || element; + const canAutoScrollNegative = axis === "x" ? !referenceOrEl.hasAttribute("data-sticky-left") || hasCrossedScrollportLeftOnce : !referenceOrEl.hasAttribute("data-sticky-top") || hasCrossedScrollportTopOnce; + if (!canAutoScrollNegative) { + return 0; + } + const elementStart = axis === "x" ? left : top; + const autoScrollAreaStart = axis === "x" ? autoScrollArea.left : autoScrollArea.top; + if (elementStart >= autoScrollAreaStart) { + return 0; + } + const scrollAmountNeeded = autoScrollAreaStart - elementStart; + return -scrollAmountNeeded; + }; + let scrollLeftTarget; + let scrollTopTarget; + if (direction.x) { + const containerScrollLeftMove = getScrollMove("x"); + if (containerScrollLeftMove) { + scrollLeftTarget = scrollContainer.scrollLeft + containerScrollLeftMove; + } + } + if (direction.y) { + const containerScrollTopMove = getScrollMove("y"); + if (containerScrollTopMove) { + scrollTopTarget = scrollContainer.scrollTop + containerScrollTopMove; + } + } + // now we know what to do, do it + if (scrollLeftTarget !== undefined) { + scrollContainer.scrollLeft = scrollLeftTarget; + } + if (scrollTopTarget !== undefined) { + scrollContainer.scrollTop = scrollTopTarget; + } + } + { + const { + scrollableLeft, + scrollableTop + } = layout; + const [positionedLeft, positionedTop] = convertScrollablePosition(scrollableLeft, scrollableTop); + // Build the transform to apply, preserving any transforms that were + // already on the element before the grab (e.g. rotate from another + // controller), and accumulating from the pre-grab translate baseline. + // The translate keys are seeded HERE, before the spread, and not merely + // assigned below: a transform object is serialized in key order, and in a + // transform list every function transforms the frame of the ones after it. + // A translate written after a rotate or a scale therefore travels rotated + // and scaled — the element drifts away from the pointer, proportionally to + // the distance covered. Dragging moves things on screen, so its translate + // has to come first, whatever else the element carries. The spread still + // wins on the value when the element already had a translate of its own. + const transform = { + translateX: 0, + translateY: 0, + ...transformAtGrab + }; + if (direction.x) { + const leftTarget = positionedLeft; + const leftAtGrab = dragGesture.gestureInfo.leftAtGrab; + const leftDelta = leftTarget - leftAtGrab; + const translateX = translateXAtGrab ? translateXAtGrab + leftDelta : leftDelta; + transform.translateX = translateX; + } + if (direction.y) { + const topTarget = positionedTop; + const topAtGrab = dragGesture.gestureInfo.topAtGrab; + const topDelta = topTarget - topAtGrab; + const translateY = translateYAtGrab ? translateYAtGrab + topDelta : topDelta; + transform.translateY = translateY; + } + dragStyleController.set(elementImpacted, { + transform + }); + } + }; + dragGesture.addDragCallback(dragToMove); + }; + const dragGestureController = createDragGestureController(options); + const grab = dragGestureController.grab; + dragGestureController.grab = ({ + element, + referenceElement, + elementToMove, + event, + ...rest + } = {}) => { + const scrollContainer = getScrollContainer(referenceElement || element); + const [elementScrollableLeft, elementScrollableTop, convertScrollablePosition] = createDragElementPositioner(element, referenceElement, elementToMove); + const dragGesture = grab({ + element, + scrollContainer, + layoutScrollableLeft: elementScrollableLeft, + layoutScrollableTop: elementScrollableTop, + event, + ...rest + }); + initGrabToMoveElement(dragGesture, { + element, + referenceElement, + elementToMove, + convertScrollablePosition + }); + return dragGesture; + }; + return dragGestureController; +}; + +/** + * Starts a drag, for one or more of the outcomes listed. + * + * @param {PointerEvent} event The `pointerdown` that may become a drag. + * @param {("move"|"reorder"|"toss")[]} effects + * What letting go of this element can mean. `reorder` and `toss` carry a copy; + * `move` carries the element itself. Asking for `move` and `reorder` together is + * asking one release to mean two things. + * @param {object} [options] + * @param {Element} [options.draggedElement=event.currentTarget] + * @param {(detail: {gestureInfo: object, x: number, y: number}) => Promise|void} [options.onMove] + * It was put somewhere. The position is already committed when this runs — the + * hand let go of it there — and travels back if the promise rejects. + * @param {Element} [options.containerElement=draggedElement.parentElement] + * Searched with `itemSelector` for the items to drop between. + * @param {string} [options.itemSelector] What matches the items of the list. + * @param {function} [options.getItemId] `getItemId(element) → id`. + * @param {function} [options.onReorder] + * `onReorder(fromId, toId, syncCloneWithDropTarget)` — see its own note below. + * @param {(detail: {gestureInfo: object}) => Promise|void} [options.onToss] + * It was thrown away. The copy leaves the screen while this runs and comes back + * if the promise rejects, because the thing still exists and the screen has to + * say so. + * @param {number} [options.tossDistance=110] How far a throw goes, in px. + * @param {number} [options.tossSpeed=0.45] And how fast, in px/ms. BOTH are asked + * for: one without the other is moving the thing while hesitating, and nothing is + * thrown away on a hesitation. + * + * Everything else is forwarded to `createDragToMoveGestureController` + * (`areaConstraint`, `autoScrollAreaPadding`, `direction`…) and to `dragAfterIntent` + * (`threshold`, `longPress`, `longPressDelay`, `longPressSlop`, `onPressStart`, + * `onPressCancel`, `onPress`). + * + * About `onReorder`: + * - `fromId`: id of the item that moved. + * - `toId`: id of the item to insert before, or `null` to append at the end. + * - `syncCloneWithDropTarget`: call it synchronously inside a + * `document.startViewTransition` callback, next to the DOM mutation, so the copy + * is captured at its landing position. + * The gesture holds its copy until what `onReorder` returns settles, so returning + * the transition is what makes the landing continuous. + */ +const startDragTo = (event, effects, { + draggedElement = event.currentTarget, + ...options +} = {}) => { + // An area that opted out of dragging (a text one wants to select, a control that + // owns the gesture): the press there is none of our business. + if (event.target.closest && event.target.closest("[data-drag-ignore]")) { + return undefined; } - - [navi-drag-clone-wrapper] { - /* Also a popover (see .navi_drop_hint): in the top layer it is over the - page whatever the page's own stacking is, and the coordinates it is - given are viewport ones — which is what the pointer carrying it works - in. Same UA-style reset as the hint. */ - position: fixed; - inset: auto; - top: var(--clone-top); - left: var(--clone-left); - box-sizing: border-box; - width: var(--clone-width); - height: var(--clone-height); - margin: 0; - padding: 0; - color: inherit; - background: transparent; - border: none; - box-shadow: 0 12px 28px rgba(0, 0, 0, 0.22); - opacity: 0.95; - transition: box-shadow 0.15s ease; - pointer-events: none; - overflow: visible; + // A secondary button (right click and friends) is a context menu, not a grab. + if (!isPrimaryButtonEvent(event)) { + return undefined; } - - [navi-drag-clone] { - transform: scale(var(--drag-clone-scale, 1.03)); - transform-origin: var(--drag-origin); - transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1); + const canReorder = effects.includes("reorder"); + const canToss = effects.includes("toss"); + if (canReorder || canToss) { + return startDragToCarryCopy(event, { + draggedElement, + canReorder, + canToss, + ...options + }); } + return startDragToMoveElement(event, { + draggedElement, + ...options + }); +}; - @starting-style { - [navi-drag-clone-wrapper] { - box-shadow: none; +/** + * The element ITSELF is carried, and keeps the place the hand gave it. + * + * No copy, unlike the two others: what is being moved is the thing and not a + * stand-in for it, so there is nothing to put back and nothing to reveal. + */ +const startDragToMoveElement = (event, { + draggedElement, + onMove, + threshold, + longPress, + longPressDelay, + longPressSlop, + onPressStart, + onPressCancel, + onPress, + ...options +}) => { + event.preventDefault(); + return dragAfterIntent(event, () => { + const gestureController = createDragToMoveGestureController({ + releasePositionEffect: "manual", + ...options + }); + const dragGesture = gestureController.grabViaPointer(event, { + element: draggedElement + }); + if (!dragGesture) { + return null; } + dragGesture.addReleaseCallback(async gestureInfo => { + const { + xDelta, + yDelta + } = gestureInfo.layout; + if (!xDelta && !yDelta) { + // Picked up and put back down: nothing moved, so nobody is told. + gestureInfo.cancelPosition(); + return; + } + // Committed before the answer rather than after: the hand let go of it + // there, and a thing that snaps home while a request is in flight says the + // gesture was not understood. + gestureInfo.commitPosition(); + try { + await onMove?.({ + gestureInfo, + x: xDelta, + y: yDelta + }); + } catch { + gestureInfo.cancelPositionAnimated(); + } + }); + return dragGesture; + }, { + threshold, + longPress, + longPressDelay, + longPressSlop, + onPressStart, + onPressCancel, + onPress + }); +}; - [navi-drag-clone] { - transform: scale(1); +// Far and fast, both at once: one without the other is moving the thing while +// hesitating, and nothing is thrown away on a hesitation — it comes back. +const TOSS_DISTANCE_TO_COMMIT = 110; +const TOSS_SPEED_TO_COMMIT = 0.45; +const resolveDropMeaning = ({ + gestureInfo, + hasDropTarget, + canReorder, + canToss, + tossDistance = TOSS_DISTANCE_TO_COMMIT, + tossSpeed = TOSS_SPEED_TO_COMMIT +}) => { + if (canToss) { + const { + xDelta, + yDelta + } = gestureInfo.layout; + const distance = Math.hypot(xDelta, yDelta); + if (distance > tossDistance && gestureInfo.velocity > tossSpeed) { + return "toss"; } } -`; -// At module scope, not inside startDragToReorder: the cursor rules above say who -// can start a drag, and they have to be true BEFORE anyone drags anything. -import.meta.css = [css$1, "@jsenv/dom/src/interaction/drag/drag_to_reorder.js"]; -const dragCSSVars = ["--drop-hint-size", "--drop-hint-background-color", "--drop-hint-border-radius", "--drop-hint-margin-x", "--drop-hint-margin-y", "--drop-hint-arrow-size", "--drag-clone-scale"]; + if (canReorder && hasDropTarget) { + return "reorder"; + } + return "cancel"; +}; /** - * Starts a drag-to-reorder interaction on a list item. - * - * Handles the full reorder UX: - * - Activates only once the intent is established — a short movement with a mouse, a long - * press with a finger (see `dragAfterIntent`), so that neither a click nor a scroll - * reorders anything by accident. - * - Clones the grabbed element and moves the clone while the original stays hidden in place - * (keeps the layout intact so other items don't shift during the drag). - * - CSS vars (`--drop-hint-size`, `--drop-hint-background-color`, etc.) are read from the - * dragged element and moved to `document.documentElement` for the duration of the drag so - * the drop-hint and clone — both in `document.body` — can inherit them. - * - Shows a drop-hint line indicating where the item will land. - * - Drop-target detection is intersection-based: the clone's bounding rect is compared - * against every item that matches `itemSelector` in the scroll container. - * - No-ops are filtered: releasing on the grabbed element itself, or in a position that - * would leave it at exactly the same index, never triggers `onReorder`. - * - On a valid drop, the clone animates to the drop position via the View Transitions API, - * `onReorder` is called inside the transition callback so the DOM update and the animation - * are captured together, then the clone is removed. - * - On a cancelled drop (pointer released with no valid target), the clone is removed - * immediately without calling `onReorder`. - * - * IDs are used as the bridge between DOM elements and JS state because: - * - Not all DOM elements matching `itemSelector` may be valid drop targets - * (holes in the structure), so DOM indices don't reliably map to state indices. - * - Virtual lists render fewer DOM nodes than the total item count, so - * DOM-index-based counting would be wrong. - * - * Any option not listed below is forwarded to `createDragToMoveGestureController` - * (`areaConstraint`, `autoScrollAreaPadding`, `stickyFrontiers`…), except - * `releasePositionEffect`, always `"manual"` here: what moves is the clone, and it - * is removed on release, so there is no position to commit or cancel. - * - * @param {PointerEvent} event - * The `pointerdown` event that may become a reorder. - * @param {object} options - * @param {Element} [options.draggedElement=event.currentTarget] - * The list item to drag. - * @param {Element} [options.containerElement=draggedElement.parentElement] - * Element searched with `itemSelector` to find the items to drop between. - * @param {string} options.itemSelector - * CSS selector that matches all list items inside `containerElement`. - * Used for drop-target detection and no-op filtering. - * @param {function} options.getItemId - * Returns the stable ID for a given DOM element. - * Signature: `getItemId(element) → id`. - * @param {function} options.onReorder - * Called when the user drops the item in a new position. - * Signature: `onReorder(fromId, toId, syncCloneWithDropTarget)`. - * - `fromId`: stable ID of the dragged item. - * - `toId`: stable ID of the item to insert before, or `null` to append at the end. - * - `syncCloneWithDropTarget`: call it synchronously inside a - * `document.startViewTransition` callback, next to the DOM mutation, so the - * clone is captured at its landing position. - * @param {object} [options.direction={ x: false, y: true }] - * Axes along which dragging is allowed. Passed to `createDragToMoveGestureController`. - * @param {number} [options.threshold=5] - * Distance (px) a mouse must travel before the press becomes a drag. - * @param {boolean|"if-touch"} [options.longPress="if-touch"] - * Which pointers start the drag by holding still instead of by travelling. - * @param {number} [options.longPressDelay=400] - * How long (ms) such a pointer must stay down. - * @param {number} [options.longPressSlop=8] - * How far (px) it may drift during that wait before the press is abandoned. - * @param {function} [options.onPressStart] - * The pointer went down and the wait began (a cue that the press counts). - * @param {function} [options.onPressCancel] - * The pointer moved or lifted before the wait was over. - * @param {function} [options.onPress] - * The wait completed and the item is now held (haptics, scale…). + * A COPY of the element is carried, and the original keeps its place in the + * layout — which is what makes a reorder possible at all: nothing else moves + * while the hand looks for a place, so there is a stable row of items to look + * between. A throw uses the same copy for the opposite reason: the original stays + * until the answer says it is really gone. */ -const startDragToReorder = (event, { - draggedElement = event.currentTarget, +const startDragToCarryCopy = (event, { + draggedElement, + canReorder, + canToss, + // Something that can be thrown away has to be able to LEAVE. The default of + // the layer below keeps what is dragged inside its scroll area, which is right + // for a reorder (a row belongs to its list) and makes a throw impossible — the + // copy hits the edge of the list and no distance is ever covered, so no throw + // ever happens and no sideways movement is even visible. + // Destructured with the default here rather than written at the call below: a + // caller passing `areaConstraint: undefined` (which is what saying nothing + // through an options object looks like) would otherwise put the layer below + // back on its own default and undo this. + areaConstraint = canToss ? "none" : undefined, containerElement = draggedElement.parentElement, itemSelector, getItemId, onReorder, + onToss, + tossDistance, + tossSpeed, direction = { x: false, y: true @@ -11630,6 +11936,7 @@ const startDragToReorder = (event, { const gestureController = createDragToMoveGestureController({ direction, releasePositionEffect: "manual", + areaConstraint, ...options }); const dragGesture = gestureController.grabViaPointer(event, { @@ -11639,11 +11946,16 @@ const startDragToReorder = (event, { // getDropTargetInfo uses gestureInfo.elementImpacted to compute the dragged rect. // Point it at the clone so drop detection tracks the clone's current position. dragGesture.gestureInfo.elementImpacted = cloneWrapper; - const dropHintEl = createDropHint(); - document.body.appendChild(dropHintEl); + + // No place to land, no hint: an element that can only be thrown away has + // nowhere to be put. + const dropHintEl = canReorder ? createDropHint() : null; + if (dropHintEl) { + document.body.appendChild(dropHintEl); + } // The hint first, the clone second: that order is what stacks them in the // top layer. - dropHintEl.showPopover(); + dropHintEl?.showPopover(); cloneWrapper.showPopover(); // currentBeforeElement: element before which the grabbed item will be inserted (null = end) @@ -11651,6 +11963,9 @@ const startDragToReorder = (event, { let currentBeforeElement; let currentReleaseElement; const clearDropHintDOM = () => { + if (!dropHintEl) { + return; + } dropHintEl.removeAttribute("data-drop-edge"); dropHintEl.style.removeProperty("--drop-target-top"); dropHintEl.style.removeProperty("--drop-target-bottom"); @@ -11663,6 +11978,9 @@ const startDragToReorder = (event, { clearDropHintDOM(); }; dragGesture.addDragCallback(gestureInfo => { + if (!dropHintEl) { + return; + } const allItems = []; const items = []; for (const el of containerElement.querySelectorAll(itemSelector)) { @@ -11718,9 +12036,35 @@ const startDragToReorder = (event, { }); dragGesture.addReleaseCallback(async gestureInfo => { clearDropHintDOM(); - dropHintEl.remove(); + dropHintEl?.remove(); restoreCSSVars(); - if (currentBeforeElement !== undefined) { + + // What THIS release means, from what the element said it can answer. A + // throw is asked about first: it is the more insistent of the two, and a + // hand that sent the thing across the screen has not asked for it to swap + // places with whatever it happened to fly over. + const hasDropTarget = currentBeforeElement !== undefined; + const dropMeans = resolveDropMeaning({ + gestureInfo, + hasDropTarget, + canReorder, + canToss, + tossDistance, + tossSpeed + }); + if (dropMeans === "toss") { + // Bake the position the hand left it at, so the flight starts from + // there rather than from where the clone was declared. + setCloneViewportRect(cloneWrapper, cloneWrapper); + gestureInfo.cancelPosition(); + const gone = await tossCloneAway(cloneWrapper, gestureInfo, onToss); + if (!gone) { + // It still exists, so the screen has to say so: the copy comes back + // over the original, and taking it away then reveals the row in + // place. + await settleCloneBack(cloneWrapper, draggedElement); + } + } else if (dropMeans === "reorder" && hasDropTarget) { const clone = cloneWrapper.firstElementChild; // Bake the current visual position (transform included) into the CSS vars // so the clone stays where the user released it when we clear the transform. @@ -11780,6 +12124,7 @@ const setCloneViewportRect = (cloneWrapper, el) => { // so the element expands naturally from where the user clicked. // On release, the `navi-drag-clone` attribute is removed inside // startViewTransition to drop the scale back to 1 as the "new" state. + // The chevron is the one the table's column drop preview uses, rotated by the // CSS above so each cap points into the line. const dropHintTemplate = /* html */` @@ -11808,6 +12153,51 @@ const createDropHint = () => { div.innerHTML = dropHintTemplate.trim(); return div.firstElementChild; }; + +/** + * The copy leaves the screen the way it was thrown, and the caller says what that + * meant. Resolves true when it is really gone. + * + * The answer is asked for WHILE it flies rather than after: the thing is already + * far away by the time the request lands, which is the whole point of a gesture + * that means "get rid of this" — nobody waits to watch it go. + */ +const tossCloneAway = async (cloneWrapper, gestureInfo, onToss) => { + const { + xDelta, + yDelta + } = gestureInfo.layout; + const distance = Math.hypot(xDelta, yDelta) || 1; + cloneWrapper.dataset.tossed = "away"; + cloneWrapper.style.translate = `${xDelta / distance * TOSS_DISTANCE}px ${yDelta / distance * TOSS_DISTANCE}px`; + try { + await onToss?.({ + gestureInfo + }); + return true; + } catch { + return false; + } +}; + +/** + * It comes back where it came from, and only then is taken away — which is what + * makes the original reappear in place rather than blink back into it. + * + * Flown home on `translate` rather than by rewriting the position vars: the vars + * hold where the hand let go, the transition is on translate, and moving the vars + * would put the copy there instantly instead of taking it there. + */ +const settleCloneBack = (cloneWrapper, sourceElement) => { + const sourceRect = sourceElement.getBoundingClientRect(); + const releaseLeft = parseFloat(cloneWrapper.style.getPropertyValue("--clone-left")); + const releaseTop = parseFloat(cloneWrapper.style.getPropertyValue("--clone-top")); + cloneWrapper.dataset.tossed = "back"; + cloneWrapper.style.translate = `${sourceRect.left - releaseLeft}px ${sourceRect.top - releaseTop}px`; + return new Promise(resolve => { + setTimeout(resolve, TOSS_DURATION_MS); + }); +}; const createDragClone = (element, pointerEvent) => { const rect = element.getBoundingClientRect(); const wrapper = document.createElement("div"); @@ -11829,7 +12219,20 @@ const createDragClone = (element, pointerEvent) => { wrapper.style.setProperty(property, computedStyle.getPropertyValue(property)); } const elementClone = element.cloneNode(true); + // A deep copy copies the ids too, and two elements answering to one id is a + // document that lies: getElementById picks whichever comes first, an anchor + // resolves to the wrong one, a view-transition-name is claimed twice and the + // transition is dropped. The copy is a picture of the thing, not another one of + // it — so it answers to no name at all. + elementClone.removeAttribute("id"); + for (const descendantWithId of elementClone.querySelectorAll("[id]")) { + descendantWithId.removeAttribute("id"); + } elementClone.setAttribute("navi-drag-clone", ""); + // What is held is the copy, so it is the copy that must LOOK held: the caller + // dresses `[data-grabbed]` on its own element once, and the copy is that element. + // (The original wears it too, but it is hidden — see navi-drag-clone-source.) + elementClone.setAttribute("data-grabbed", ""); elementClone.style.viewTransitionName = "navi-drag-clone"; wrapper.appendChild(elementClone); document.body.appendChild(wrapper); @@ -12004,9 +12407,9 @@ import.meta.css = [/* css */` // a press that wandered a pixel is still a press, and nothing budges. const DRAG_START_THRESHOLD = 10; // How much of a box has to be pulled for letting go to carry on rather than put -// things back. Under half, because a gesture that has clearly begun is an -// intention: asking for the box to be dragged all the way across turns a travel -// into work. +// things back, when the caller does not say. Under half, because a gesture that +// has clearly begun is an intention: asking for the box to be dragged all the +// way across turns a travel into work. const DRAG_COMMIT_RATIO = 0.3; // A flick travels whatever the distance: the hand said "away" quickly, which is // the whole gesture — px/ms of pointer, and a few pixels to tell it from a tap @@ -12136,7 +12539,8 @@ const travelsAfter = ({ slack, size, velocity, - towardsSomething + towardsSomething, + commitRatio }) => { if (!towardsSomething) { return false; @@ -12164,7 +12568,7 @@ const travelsAfter = ({ // …and going towards it travels whatever the distance: the hand said "away" // quickly, which is the whole gesture. const flicked = goingFast && Math.abs(pulled) > DRAG_FLICK_DISTANCE; - return flicked || Math.abs(pulled) > size * DRAG_COMMIT_RATIO; + return flicked || Math.abs(pulled) > size * commitRatio; }; /** @@ -12196,6 +12600,11 @@ const travelsAfter = ({ * pixel since the grab is owed to the hand. The axis comes from the caller * rather than from the movement, because there is nothing to decide — what * was caught is travelling on one already. + * @param {number} [options.commitRatio=0.3] - what fraction of the box has to + * be pulled for letting go to carry on rather than put things back. A + * fraction and never a distance, so the same gesture asks for the same thing + * on a phone and on a wide screen. Speed still answers on its own (see + * travelsAfter), whatever this says. * @param {(detail: {axis: string, sign: number, target: Element, event: PointerEvent}) => false|{size: number, slack?: number, travelBack?: boolean, travelOn?: boolean}} options.onStart * - the finger has picked its axis. Answer `false` to give the gesture up, or * with the geometry it walks: `size` (one box along that axis), `slack` (how @@ -12226,6 +12635,7 @@ const startDragToTravel = (pointerDownEvent, { element, axes = "xy", immediate = false, + commitRatio = DRAG_COMMIT_RATIO, onStart, onPull, onEnd, @@ -12497,7 +12907,8 @@ const startDragToTravel = (pointerDownEvent, { slack, size, velocity, - towardsSomething + towardsSomething, + commitRatio }), cancelled, event: releaseEvent @@ -18312,4 +18723,4 @@ const useResizeStatus = (elementRef, { as = "number" } = {}) => { }; }; -export { EASING, ELEMENT_SIZE_CHANGE, activeElementSignal, addActiveElementEffect, addAttributeEffect, allowWheelThrough, appendStyles, applyNewPosition, canScroll, captureScrollState, chainEvent, claimWheelGesture, closestOpenableAncestor, contrastColor, createBackgroundColorTransition, createBackgroundTransition, createBorderRadiusTransition, createBorderTransition, createDragGestureController, createDragToMoveGestureController, createEventGroupLogger, createGroupTransitionController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dispatchCustomEvent, dispatchInternalCustomEvent, dispatchPublicCustomEvent, dragAfterIntent, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findEvent, findFocusDelegateTarget, findFocusable, findSelfOrAncestorFixedPosition, formatEventSideEffect, getAncestorOpenType, getAvailableHeight, getAvailableWidth, getBackground, getBackgroundColor, getBorder, getBorderRadius, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getHeightWithoutTransition, getInnerHeight, getInnerWidth, getKeyboardEventDefaultAction, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getOpacityWithoutTransition, getPaddingSizes, getPositionedParent, getPositioningScrollOffset, getPreferedColorScheme, getScrollBox, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateXWithoutTransition, getTranslateY, getVisuallyVisibleInfo, getWidth, getWidthWithoutTransition, hasCSSSizeUnit, initFlexDetailsSet, initFocusGroup, initPositionSticky, isAncestorOpen, isPrimaryButtonEvent, isSameColor, isScrollable, measureLongestVisualLineWidth, measureScrollbar, measureWidestChildRow, mergeOneStyle, mergeTwoStyles, normalizeKeyboardKey, normalizeStyle, normalizeStyles, observeAncestorOpenState, onAncestorReopen, parsePositionArea, parseStyle, performTabNavigation, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, preventIntermediateScrollbar, releaseWheelGesture, resolveCSSColor, resolveCSSSize, resolveColorLuminance, resolveOklchLightness, scrollIntoViewScoped, scrollIntoViewWithStickyAwareness, scrollRoomTowards, setAttribute, setAttributes, setStyles, snapToPixel, startDragToReorder, startDragToResizeGesture, startDragToTravel, stickyAsRelativeCoords, stringifyStyle, subscribeVisualViewportResizeSettled, subscribeWindowResizeSettled, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect, watchWheelTravel, wheelGestureIsTakenFrom }; +export { EASING, ELEMENT_SIZE_CHANGE, activeElementSignal, addActiveElementEffect, addAttributeEffect, allowWheelThrough, appendStyles, applyNewPosition, canScroll, captureScrollState, chainEvent, claimWheelGesture, closestOpenableAncestor, contrastColor, createBackgroundColorTransition, createBackgroundTransition, createBorderRadiusTransition, createBorderTransition, createDragGestureController, createDragToMoveGestureController, createEventGroupLogger, createGroupTransitionController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dispatchCustomEvent, dispatchInternalCustomEvent, dispatchPublicCustomEvent, dragAfterIntent, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findEvent, findFocusDelegateTarget, findFocusable, findSelfOrAncestorFixedPosition, formatEventSideEffect, getAncestorOpenType, getAvailableHeight, getAvailableWidth, getBackground, getBackgroundColor, getBorder, getBorderRadius, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getHeightWithoutTransition, getInnerHeight, getInnerWidth, getKeyboardEventDefaultAction, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getOpacityWithoutTransition, getPaddingSizes, getPositionedParent, getPositioningScrollOffset, getPreferedColorScheme, getScrollBox, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateXWithoutTransition, getTranslateY, getVisuallyVisibleInfo, getWidth, getWidthWithoutTransition, hasCSSSizeUnit, initFlexDetailsSet, initFocusGroup, initPositionSticky, isAncestorOpen, isPrimaryButtonEvent, isSameColor, isScrollable, measureLongestVisualLineWidth, measureScrollbar, measureWidestChildRow, mergeOneStyle, mergeTwoStyles, normalizeKeyboardKey, normalizeStyle, normalizeStyles, observeAncestorOpenState, onAncestorReopen, parsePositionArea, parseStyle, performTabNavigation, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, preventIntermediateScrollbar, releaseWheelGesture, resolveCSSColor, resolveCSSSize, resolveColorLuminance, resolveOklchLightness, scrollIntoViewScoped, scrollIntoViewWithStickyAwareness, scrollRoomTowards, setAttribute, setAttributes, setStyles, snapToPixel, startDragTo, startDragToResizeGesture, startDragToTravel, stickyAsRelativeCoords, stringifyStyle, subscribeVisualViewportResizeSettled, subscribeWindowResizeSettled, suppressClickAfterGesture, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect, waitForPressHeld, watchWheelTravel, wheelGestureIsTakenFrom }; diff --git a/packages/frontend/dom/index.js b/packages/frontend/dom/index.js index 70af21d272..47a8291c60 100644 --- a/packages/frontend/dom/index.js +++ b/packages/frontend/dom/index.js @@ -68,6 +68,9 @@ export { findDescendant, } from "./src/traversal.js"; +// interaction +export { suppressClickAfterGesture } from "./src/interaction/click_suppression.js"; +export { waitForPressHeld } from "./src/interaction/press_held.js"; // interaction/focus export { activeElementSignal, @@ -123,8 +126,10 @@ export { createDragGestureController, isPrimaryButtonEvent, } from "./src/interaction/drag/drag_gesture.js"; -export { createDragToMoveGestureController } from "./src/interaction/drag/drag_to_move.js"; -export { startDragToReorder } from "./src/interaction/drag/drag_to_reorder.js"; +export { + createDragToMoveGestureController, + startDragTo, +} from "./src/interaction/drag/drag_to.js"; export { startDragToResizeGesture } from "./src/interaction/drag/drag_to_resize_gesture.js"; export { scrollRoomTowards, diff --git a/packages/frontend/dom/package.json b/packages/frontend/dom/package.json index 431304edf7..0b7122cd27 100644 --- a/packages/frontend/dom/package.json +++ b/packages/frontend/dom/package.json @@ -40,7 +40,7 @@ "@jsenv/core": "../../../", "@jsenv/navi": "../navi", "@jsenv/snapshot": "../../tooling/snapshot", - "@preact/signals": "2.9.4", + "@preact/signals": "2.11.1", "preact": "11.0.0-beta.2" }, "publishConfig": { diff --git a/packages/frontend/dom/src/demos/vanilla_reorderable_list_demo.html b/packages/frontend/dom/src/demos/vanilla_reorderable_list_demo.html index a06c378d3a..9a61bfc66f 100644 --- a/packages/frontend/dom/src/demos/vanilla_reorderable_list_demo.html +++ b/packages/frontend/dom/src/demos/vanilla_reorderable_list_demo.html @@ -134,7 +134,7 @@ + + diff --git a/packages/frontend/navi/src/control/demos/many/4_reorderable_list_demo.html b/packages/frontend/navi/src/control/demos/many/4_reorderable_list_demo.html index e1b48d765e..57cf23950d 100644 --- a/packages/frontend/navi/src/control/demos/many/4_reorderable_list_demo.html +++ b/packages/frontend/navi/src/control/demos/many/4_reorderable_list_demo.html @@ -42,11 +42,7 @@ + + diff --git a/packages/frontend/navi/src/nav/url_target/url_target.js b/packages/frontend/navi/src/nav/url_target/url_target.js new file mode 100644 index 0000000000..d5b2f9e403 --- /dev/null +++ b/packages/frontend/navi/src/nav/url_target/url_target.js @@ -0,0 +1,250 @@ +/** + * The element the URL designates — the one whose id is the hash — shows itself + * when it renders, not when the URL changes. + * + * The browser answers a fragment at two moments only: the end of the document + * load, and each fragment navigation. In an app whose content comes from a + * request, both are too early — the element does not exist yet, there is + * nothing to scroll to, and the moment passes. + * + * What is already acquired, and what this file therefore does not redo: + * `:target` is live — as soon as an element carries the hash's id it matches, + * even if it arrives a second later. The durable "this is the one" state is + * there for free, and an app styles it in CSS. What is lost is the one-shot + * action tied to a moment: bringing the target under the reader's eyes, and + * saying it just arrived. That is all of what follows. + * + * Two decisions worth knowing before reading: + * + * - **navi places the target itself, every time.** Where the browser does + * answer a fragment it puts the element against the top edge, where it reads + * as the first thing on the page rather than as the one that was pointed at; + * the alignment below is applied after, so one rule holds whether the target + * was there all along or arrived late. A page with nothing to scroll simply + * does not move, which is the whole of the "the list already fits on screen" + * case — the transient mark alone then says which one was meant. + * + * - **Wait, but not forever.** As long as the document is working (routes, + * actions) the target may still arrive; once it has been idle for a moment, a + * month-old link to a deleted element simply brings nothing and the reader + * lands on the page — the right degradation. + * + * The case where none of this is needed is worth naming: when a list's skeleton + * already knows the ids of its slice (they are in cache, or they come from the + * URL), putting them on the placeholders is enough — the target then exists on + * the very first render and the browser does everything on its own. + */ + +import { elementIsFocusable } from "@jsenv/dom"; +import { computed, effect } from "@preact/signals"; + +import { documentIsBusySignal } from "../browser_integration/document_loading_signal.js"; +import { documentUrlSignal } from "../browser_integration/document_url_signal.js"; + +const URL_TARGET_ATTRIBUTE = "data-url-target"; + +const css = /* css */ ` + @layer navi { + [${URL_TARGET_ATTRIBUTE}] { + animation: navi_url_target var(--navi-url-target-duration, 2000ms) + ease-out; + } + + @keyframes navi_url_target { + from { + box-shadow: 0 0 0 3px + var(--navi-url-target-color, light-dark(#4476ff, #3b82f6)); + } + to { + box-shadow: 0 0 0 3px transparent; + } + } + } +`; +import.meta.css = css; + +let urlTargetOptions = { + block: "center", + behavior: "smooth", + markDuration: 2000, + graceAfterIdle: 1000, + maxWait: 10_000, +}; + +/** + * Adjusts how navi answers the element designated by the URL hash. + * + * @param {object} options + * @param {"start"|"center"|"end"|"nearest"} [options.block="center"] + * Vertical alignment of the scroll. "center" by default: an element stuck to + * the top of the screen reads as the first one of the page rather than as the + * one that was pointed at. + * @param {ScrollBehavior} [options.behavior="smooth"] + * Overridden with "instant" under `prefers-reduced-motion: reduce`. + * @param {number} [options.markDuration=2000] + * How long, in ms, the element carries `data-url-target`. Published to CSS as + * `--navi-url-target-duration`. + * @param {number} [options.graceAfterIdle=1000] + * How long, in ms, to keep waiting for a target that has not arrived, counted + * from the moment the document stops working. + * @param {number} [options.maxWait=10000] + * Longest wait, in ms, for a document that never stops working. + */ +export const setUrlTargetOptions = (options) => { + urlTargetOptions = { ...urlTargetOptions, ...options }; + if (options.markDuration !== undefined) { + document.documentElement.style.setProperty( + "--navi-url-target-duration", + `${options.markDuration}ms`, + ); + } +}; + +const urlTargetIdSignal = computed(() => { + const documentUrl = documentUrlSignal.value; + return urlToTargetId(documentUrl); +}); +/** + * The id the URL hash designates, or "" when the URL designates none. + * Reactive: a component reading it re-renders when the target changes. + */ +export const useUrlTargetId = () => { + return urlTargetIdSignal.value; +}; + +let stopWaitingForCurrentTarget = null; +let currentTargetKey; + +/** + * Answers the URL's target again, as if it had just been designated. + * + * Clicking the very link one is already on moves nothing — same pathname, same + * hash, no history entry, no event — so nothing downstream would notice. The + * reader did ask, again, to be taken to that element. + */ +export const rearmUrlTarget = () => { + currentTargetKey = undefined; + armUrlTarget(documentUrlSignal.peek()); +}; + +const armUrlTarget = (documentUrl) => { + const targetKey = urlToTargetKey(documentUrl); + if (targetKey === currentTargetKey) { + return; + } + currentTargetKey = targetKey; + if (stopWaitingForCurrentTarget) { + stopWaitingForCurrentTarget(); + stopWaitingForCurrentTarget = null; + } + const targetId = urlToTargetId(documentUrl); + if (!targetId) { + return; + } + stopWaitingForCurrentTarget = waitForElementWithId(targetId, (element) => { + stopWaitingForCurrentTarget = null; + revealUrlTarget(element); + }); +}; + +// The pathname and the hash, not the whole URL: a search param changing (a +// filter, a page) is still the same page and the same target, and must not make +// it answer a second time. +const urlToTargetKey = (url) => { + const { pathname, hash } = new URL(url); + return `${pathname}${hash}`; +}; +const urlToTargetId = (url) => { + const { hash } = new URL(url); + return hash ? decodeURIComponent(hash.slice(1)) : ""; +}; + +const waitForElementWithId = (id, onFound) => { + let mutationObserver = null; + let stopWatchingBusy = null; + let idleTimeout = null; + let maxWaitTimeout = null; + let found = false; + + const stopWaiting = () => { + if (mutationObserver) { + mutationObserver.disconnect(); + mutationObserver = null; + } + if (stopWatchingBusy) { + stopWatchingBusy(); + stopWatchingBusy = null; + } + clearTimeout(idleTimeout); + clearTimeout(maxWaitTimeout); + }; + + const checkForElement = () => { + const element = document.getElementById(id); + if (!element) { + return; + } + // Rendered inside a closed tab, a folded details, a view that is not the + // one on screen: the element exists but would show nothing. Keep waiting — + // it is the same wait, for the same reason. + if (element.checkVisibility && !element.checkVisibility()) { + return; + } + found = true; + stopWaiting(); + onFound(element); + }; + + checkForElement(); + if (found) { + return stopWaiting; + } + + mutationObserver = new MutationObserver(checkForElement); + mutationObserver.observe(document.documentElement, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ["id"], + }); + const { graceAfterIdle, maxWait } = urlTargetOptions; + stopWatchingBusy = effect(() => { + const documentIsBusy = documentIsBusySignal.value; + clearTimeout(idleTimeout); + if (!documentIsBusy) { + idleTimeout = setTimeout(stopWaiting, graceAfterIdle); + } + }); + maxWaitTimeout = setTimeout(stopWaiting, maxWait); + + return stopWaiting; +}; + +const revealUrlTarget = (element) => { + const { block, behavior, markDuration } = urlTargetOptions; + // The element just entered the DOM: where it sits is only known once layout + // has run. + requestAnimationFrame(() => { + const prefersReducedMotion = window.matchMedia( + "(prefers-reduced-motion: reduce)", + ).matches; + element.scrollIntoView({ + block, + behavior: prefersReducedMotion ? "instant" : behavior, + }); + // What the browser does when it handles a fragment itself: keyboard + // navigation resumes from the target, not from the top of the document. + if (elementIsFocusable(element)) { + element.focus({ preventScroll: true }); + } + element.setAttribute(URL_TARGET_ATTRIBUTE, ""); + setTimeout(() => { + element.removeAttribute(URL_TARGET_ATTRIBUTE); + }, markDuration); + }); +}; + +effect(() => { + const documentUrl = documentUrlSignal.value; + armUrlTarget(documentUrl); +}); diff --git a/packages/frontend/pwa/package.json b/packages/frontend/pwa/package.json index 8b50852d87..e607c85771 100644 --- a/packages/frontend/pwa/package.json +++ b/packages/frontend/pwa/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/pwa", - "version": "6.2.0", + "version": "6.2.1", "type": "module", "description": "Service worker and other progressive web application helpers", "repository": { @@ -28,14 +28,14 @@ "/src/" ], "dependencies": { - "@jsenv/sigi": "0.1.10" + "@jsenv/sigi": "0.1.11" }, "devDependencies": { "@jsenv/assert": "../../tooling/assert", "@jsenv/core": "../../../", "@jsenv/https-local": "../../tooling/https-local", "@jsenv/plugin-explorer": "../../related/plugin-explorer", - "playwright": "1.61.1" + "playwright": "1.62.1" }, "publishConfig": { "access": "public" diff --git a/packages/frontend/service-worker/package.json b/packages/frontend/service-worker/package.json index bca3c29aa2..d1fba2cfd6 100644 --- a/packages/frontend/service-worker/package.json +++ b/packages/frontend/service-worker/package.json @@ -36,7 +36,7 @@ "@jsenv/pwa": "../pwa", "@jsenv/service-worker": "./", "@jsenv/snapshot": "../../tooling/snapshot", - "playwright": "1.61.1" + "playwright": "1.62.1" }, "publishConfig": { "access": "public" diff --git a/packages/frontend/sigi/package.json b/packages/frontend/sigi/package.json index f96e3fc317..ff8d470903 100644 --- a/packages/frontend/sigi/package.json +++ b/packages/frontend/sigi/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/sigi", - "version": "0.1.10", + "version": "0.1.11", "type": "module", "description": "State management", "repository": { @@ -27,7 +27,7 @@ "/src/" ], "dependencies": { - "@preact/signals": "2.9.4" + "@preact/signals": "2.11.1" }, "devDependencies": { "@jsenv/assert": "../../tooling/assert", diff --git a/packages/internal/ast/package.json b/packages/internal/ast/package.json index bb04fc0614..fa1b67465d 100644 --- a/packages/internal/ast/package.json +++ b/packages/internal/ast/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/ast", - "version": "6.9.0", + "version": "6.9.1", "type": "module", "description": "Parse and transform HTML, CSS and JS", "repository": { @@ -26,9 +26,9 @@ "/src/" ], "dependencies": { - "@babel/core": "7.29.0", - "@babel/helper-module-imports": "7.28.6", - "@babel/parser": "7.29.3", + "@babel/core": "7.29.7", + "@babel/helper-module-imports": "7.29.7", + "@babel/parser": "7.29.8", "@jsenv/humanize": "1.7.8", "@jsenv/urls": "2.9.10", "@jsenv/utils": "2.3.1", diff --git a/packages/internal/js-module-fallback/package.json b/packages/internal/js-module-fallback/package.json index bd2d359460..1ca36387d3 100644 --- a/packages/internal/js-module-fallback/package.json +++ b/packages/internal/js-module-fallback/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/js-module-fallback", - "version": "1.4.39", + "version": "1.5.0", "type": "module", "repository": { "type": "git", @@ -28,11 +28,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@babel/parser": "7.29.0", - "@babel/plugin-transform-dynamic-import": "7.27.1", - "@babel/plugin-transform-modules-systemjs": "7.29.7", - "@babel/plugin-transform-modules-umd": "7.27.1", - "@jsenv/ast": "6.9.0", + "@babel/parser": "7.29.8", + "@babel/plugin-transform-dynamic-import": "7.29.7", + "@babel/plugin-transform-modules-systemjs": "7.29.8", + "@babel/plugin-transform-modules-umd": "7.29.7", + "@jsenv/ast": "6.9.1", "@jsenv/sourcemap": "1.4.2", "@jsenv/urls": "2.9.10", "babel-plugin-transform-async-to-promises": "0.8.18" diff --git a/packages/internal/plugin-minification/package.json b/packages/internal/plugin-minification/package.json index 0b3e3f925a..157f830ad2 100644 --- a/packages/internal/plugin-minification/package.json +++ b/packages/internal/plugin-minification/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/plugin-minification", - "version": "1.7.7", + "version": "1.7.8", "type": "module", "repository": { "type": "git", @@ -21,7 +21,7 @@ "@jsenv/core": "*" }, "dependencies": { - "@jsenv/ast": "6.9.0", + "@jsenv/ast": "6.9.1", "@jsenv/urls": "2.9.10", "html-minifier": "4.0.0", "lightningcss": "1.33.0", diff --git a/packages/internal/plugin-supervisor/package.json b/packages/internal/plugin-supervisor/package.json index 46daf3839c..2871d34386 100644 --- a/packages/internal/plugin-supervisor/package.json +++ b/packages/internal/plugin-supervisor/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/plugin-supervisor", - "version": "1.8.10", + "version": "1.8.11", "type": "module", "repository": { "type": "git", @@ -27,7 +27,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@jsenv/ast": "6.9.0", + "@jsenv/ast": "6.9.1", "@jsenv/sourcemap": "1.4.2" }, "devDependencies": { diff --git a/packages/internal/plugin-transpilation/package.json b/packages/internal/plugin-transpilation/package.json index 0fc8453460..cc15cde516 100644 --- a/packages/internal/plugin-transpilation/package.json +++ b/packages/internal/plugin-transpilation/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/plugin-transpilation", - "version": "1.5.80", + "version": "1.6.0", "type": "module", "description": "TODO", "repository": { @@ -17,39 +17,39 @@ "/src/" ], "dependencies": { - "@babel/plugin-syntax-import-assertions": "7.28.6", + "@babel/plugin-syntax-import-assertions": "7.29.7", "@babel/plugin-syntax-object-rest-spread": "7.8.3", "@babel/plugin-syntax-optional-catch-binding": "7.8.3", - "@babel/plugin-transform-arrow-functions": "7.27.1", - "@babel/plugin-transform-block-scoped-functions": "7.27.1", - "@babel/plugin-transform-block-scoping": "7.28.6", - "@babel/plugin-transform-classes": "7.28.6", - "@babel/plugin-transform-computed-properties": "7.28.6", - "@babel/plugin-transform-destructuring": "7.28.5", - "@babel/plugin-transform-dotall-regex": "7.28.6", - "@babel/plugin-transform-duplicate-keys": "7.27.1", - "@babel/plugin-transform-exponentiation-operator": "7.28.6", - "@babel/plugin-transform-for-of": "7.27.1", - "@babel/plugin-transform-function-name": "7.27.1", - "@babel/plugin-transform-json-strings": "7.28.6", - "@babel/plugin-transform-literals": "7.27.1", - "@babel/plugin-transform-new-target": "7.27.1", - "@babel/plugin-transform-numeric-separator": "7.28.6", - "@babel/plugin-transform-object-rest-spread": "7.28.6", - "@babel/plugin-transform-object-super": "7.27.1", - "@babel/plugin-transform-optional-catch-binding": "7.28.6", - "@babel/plugin-transform-optional-chaining": "7.28.6", - "@babel/plugin-transform-parameters": "7.27.7", - "@babel/plugin-transform-regenerator": "7.29.0", - "@babel/plugin-transform-shorthand-properties": "7.27.1", - "@babel/plugin-transform-spread": "7.28.6", - "@babel/plugin-transform-sticky-regex": "7.27.1", - "@babel/plugin-transform-template-literals": "7.27.1", - "@babel/plugin-transform-typeof-symbol": "7.27.1", - "@babel/plugin-transform-unicode-property-regex": "7.28.6", - "@babel/plugin-transform-unicode-regex": "7.27.1", - "@jsenv/ast": "6.9.0", - "@jsenv/js-module-fallback": "1.4.39", + "@babel/plugin-transform-arrow-functions": "7.29.7", + "@babel/plugin-transform-block-scoped-functions": "7.29.7", + "@babel/plugin-transform-block-scoping": "7.29.7", + "@babel/plugin-transform-classes": "7.29.7", + "@babel/plugin-transform-computed-properties": "7.29.7", + "@babel/plugin-transform-destructuring": "7.29.7", + "@babel/plugin-transform-dotall-regex": "7.29.7", + "@babel/plugin-transform-duplicate-keys": "7.29.7", + "@babel/plugin-transform-exponentiation-operator": "7.29.7", + "@babel/plugin-transform-for-of": "7.29.7", + "@babel/plugin-transform-function-name": "7.29.7", + "@babel/plugin-transform-json-strings": "7.29.7", + "@babel/plugin-transform-literals": "7.29.7", + "@babel/plugin-transform-new-target": "7.29.7", + "@babel/plugin-transform-numeric-separator": "7.29.7", + "@babel/plugin-transform-object-rest-spread": "7.29.7", + "@babel/plugin-transform-object-super": "7.29.7", + "@babel/plugin-transform-optional-catch-binding": "7.29.7", + "@babel/plugin-transform-optional-chaining": "7.29.7", + "@babel/plugin-transform-parameters": "7.29.7", + "@babel/plugin-transform-regenerator": "7.29.8", + "@babel/plugin-transform-shorthand-properties": "7.29.7", + "@babel/plugin-transform-spread": "7.29.8", + "@babel/plugin-transform-sticky-regex": "7.29.7", + "@babel/plugin-transform-template-literals": "7.29.7", + "@babel/plugin-transform-typeof-symbol": "7.29.7", + "@babel/plugin-transform-unicode-property-regex": "7.29.7", + "@babel/plugin-transform-unicode-regex": "7.29.7", + "@jsenv/ast": "6.9.1", + "@jsenv/js-module-fallback": "1.5.0", "@jsenv/sourcemap": "1.4.2", "@jsenv/urls": "2.9.10", "@jsenv/utils": "2.3.1", diff --git a/packages/private/oto/package.json b/packages/private/oto/package.json index 32c2fd868d..615717eae5 100644 --- a/packages/private/oto/package.json +++ b/packages/private/oto/package.json @@ -1,6 +1,6 @@ { "name": "oto", - "version": "0.0.3", + "version": "0.0.4", "type": "module", "private": true, "workspaces": [ @@ -23,24 +23,24 @@ "dependencies": { "@jsenv/dom": "./packages/dom", "@jsenv/navi": "./packages/navi", - "@preact/signals": "2.9.4", + "@preact/signals": "2.11.1", "dom": "./packages/dom", "hooks": "./packages/hooks", "matrix": "./packages/matrix", "preact": "11.0.0-beta.0" }, "devDependencies": { - "@babel/plugin-syntax-import-attributes": "7.28.6", - "@babel/plugin-transform-react-jsx": "7.28.6", + "@babel/plugin-syntax-import-attributes": "7.29.7", + "@babel/plugin-transform-react-jsx": "7.29.7", "@jsenv/assert": "../../tooling/assert", "@jsenv/core": "../../../", "@jsenv/custom-elements-redefine": "../../frontend/custom-elements-redefine", "@jsenv/eslint-config-relax": "../../tooling/eslint-config-relax", "@jsenv/plugin-preact": "../../related/plugin-preact", "@jsenv/test": "../../related/test", - "@playwright/browser-chromium": "1.61.1", + "@playwright/browser-chromium": "1.62.1", "eslint": "9.39.2", - "open": "11.0.0", + "open": "11.0.1", "prettier": "3.9.6", "prettier-plugin-organize-imports": "4.3.0" }, diff --git a/packages/private/oto/packages/canvas_editor/package.json b/packages/private/oto/packages/canvas_editor/package.json index 02f27a5ecd..40c05c89e7 100644 --- a/packages/private/oto/packages/canvas_editor/package.json +++ b/packages/private/oto/packages/canvas_editor/package.json @@ -2,7 +2,7 @@ "name": "canvas-editor", "private": true, "dependencies": { - "@preact/signals": "2.9.4", + "@preact/signals": "2.11.1", "preact": "11.0.0-beta.0" }, "devDependencies": { diff --git a/packages/related/cli/package.json b/packages/related/cli/package.json index e1916b1b2a..1886d2749d 100644 --- a/packages/related/cli/package.json +++ b/packages/related/cli/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/cli", - "version": "0.3.138", + "version": "0.3.139", "type": "module", "description": "Command Line Interface for jsenv", "repository": { diff --git a/packages/related/cli/template-node-package/package.json b/packages/related/cli/template-node-package/package.json index 627e3661c7..27d3fb2093 100644 --- a/packages/related/cli/template-node-package/package.json +++ b/packages/related/cli/template-node-package/package.json @@ -13,9 +13,9 @@ }, "devDependencies": { "@jsenv/assert": "4.5.7", - "@jsenv/core": "41.4.12", - "@jsenv/eslint-config-relax": "1.8.9", - "@jsenv/test": "3.7.31", + "@jsenv/core": "41.4.13", + "@jsenv/eslint-config-relax": "1.9.0", + "@jsenv/test": "3.7.32", "eslint": "9.39.2", "prettier": "3.9.6" } diff --git a/packages/related/cli/template-web-components/package.json b/packages/related/cli/template-web-components/package.json index a2af65bebd..37834faa1d 100644 --- a/packages/related/cli/template-web-components/package.json +++ b/packages/related/cli/template-web-components/package.json @@ -15,18 +15,18 @@ "playwright:install": "npx playwright install-deps && npx playwright install" }, "devDependencies": { - "@babel/plugin-syntax-import-attributes": "7.28.6", + "@babel/plugin-syntax-import-attributes": "7.29.7", "@jsenv/custom-elements-redefine": "0.1.0", "@jsenv/assert": "4.5.7", - "@jsenv/core": "41.4.12", + "@jsenv/core": "41.4.13", "@jsenv/plugin-bundling": "2.10.16", - "@jsenv/plugin-minification": "1.7.7", - "@jsenv/eslint-config-relax": "1.8.9", - "@jsenv/test": "3.7.31", + "@jsenv/plugin-minification": "1.7.8", + "@jsenv/eslint-config-relax": "1.9.0", + "@jsenv/test": "3.7.32", "eslint": "9.39.2", - "open": "11.0.0", - "@playwright/browser-chromium": "1.61.1", + "open": "11.0.1", + "@playwright/browser-chromium": "1.62.1", "prettier": "3.9.6", - "playwright": "1.61.1" + "playwright": "1.62.1" } } diff --git a/packages/related/cli/template-web-preact/package.json b/packages/related/cli/template-web-preact/package.json index 8b5dc2ed82..e127717361 100644 --- a/packages/related/cli/template-web-preact/package.json +++ b/packages/related/cli/template-web-preact/package.json @@ -18,19 +18,19 @@ "preact": "11.0.0-beta.0" }, "devDependencies": { - "@babel/plugin-syntax-import-attributes": "7.28.6", - "@babel/plugin-transform-react-jsx": "7.28.6", + "@babel/plugin-syntax-import-attributes": "7.29.7", + "@babel/plugin-transform-react-jsx": "7.29.7", "@jsenv/assert": "4.5.7", - "@jsenv/core": "41.4.12", - "@jsenv/plugin-preact": "1.7.41", + "@jsenv/core": "41.4.13", + "@jsenv/plugin-preact": "1.8.0", "@jsenv/plugin-bundling": "2.10.16", - "@jsenv/plugin-minification": "1.7.7", - "@jsenv/eslint-config-relax": "1.8.9", - "@jsenv/test": "3.7.31", + "@jsenv/plugin-minification": "1.7.8", + "@jsenv/eslint-config-relax": "1.9.0", + "@jsenv/test": "3.7.32", "eslint": "9.39.2", - "open": "11.0.0", - "@playwright/browser-chromium": "1.61.1", + "open": "11.0.1", + "@playwright/browser-chromium": "1.62.1", "prettier": "3.9.6", - "playwright": "1.61.1" + "playwright": "1.62.1" } } diff --git a/packages/related/cli/template-web-react/package.json b/packages/related/cli/template-web-react/package.json index 1fee629b64..b6d5fa25aa 100644 --- a/packages/related/cli/template-web-react/package.json +++ b/packages/related/cli/template-web-react/package.json @@ -19,19 +19,19 @@ "react-dom": "19.2.8" }, "devDependencies": { - "@babel/plugin-syntax-import-attributes": "7.28.6", - "@babel/plugin-transform-react-jsx": "7.28.6", + "@babel/plugin-syntax-import-attributes": "7.29.7", + "@babel/plugin-transform-react-jsx": "7.29.7", "@jsenv/assert": "4.5.7", - "@jsenv/core": "41.4.12", - "@jsenv/plugin-react": "1.7.46", + "@jsenv/core": "41.4.13", + "@jsenv/plugin-react": "1.8.0", "@jsenv/plugin-bundling": "2.10.16", - "@jsenv/plugin-minification": "1.7.7", - "@jsenv/eslint-config-relax": "1.8.9", - "@jsenv/test": "3.7.31", + "@jsenv/plugin-minification": "1.7.8", + "@jsenv/eslint-config-relax": "1.9.0", + "@jsenv/test": "3.7.32", "eslint": "9.39.2", - "open": "11.0.0", - "@playwright/browser-chromium": "1.61.1", + "open": "11.0.1", + "@playwright/browser-chromium": "1.62.1", "prettier": "3.9.6", - "playwright": "1.61.1" + "playwright": "1.62.1" } } diff --git a/packages/related/cli/template-web/package.json b/packages/related/cli/template-web/package.json index eac817b7b5..d4f8782626 100644 --- a/packages/related/cli/template-web/package.json +++ b/packages/related/cli/template-web/package.json @@ -14,17 +14,17 @@ "prettier": "prettier --write ." }, "devDependencies": { - "@babel/plugin-syntax-import-attributes": "7.28.6", + "@babel/plugin-syntax-import-attributes": "7.29.7", "@jsenv/assert": "4.5.7", - "@jsenv/core": "41.4.12", - "@jsenv/eslint-config-relax": "1.8.9", + "@jsenv/core": "41.4.13", + "@jsenv/eslint-config-relax": "1.9.0", "@jsenv/plugin-bundling": "2.10.16", - "@jsenv/plugin-minification": "1.7.7", - "@jsenv/test": "3.7.31", + "@jsenv/plugin-minification": "1.7.8", + "@jsenv/test": "3.7.32", "eslint": "9.39.2", - "open": "11.0.0", - "@playwright/browser-chromium": "1.61.1", + "open": "11.0.1", + "@playwright/browser-chromium": "1.62.1", "prettier": "3.9.6", - "playwright": "1.61.1" + "playwright": "1.62.1" } } diff --git a/packages/related/plugin-as-js-classic/package.json b/packages/related/plugin-as-js-classic/package.json index 3b836314da..46c28d603e 100644 --- a/packages/related/plugin-as-js-classic/package.json +++ b/packages/related/plugin-as-js-classic/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/plugin-as-js-classic", - "version": "1.6.43", + "version": "1.6.44", "type": "module", "repository": { "type": "git", @@ -18,7 +18,7 @@ "/src/" ], "dependencies": { - "@jsenv/js-module-fallback": "1.4.39", + "@jsenv/js-module-fallback": "1.5.0", "@jsenv/plugin-bundling": "2.10.16", "@jsenv/urls": "2.9.10" }, @@ -29,7 +29,7 @@ "@jsenv/plugin-as-js-classic": "./", "@jsenv/plugin-preact": "../plugin-preact", "@jsenv/snapshot": "../../tooling/snapshot", - "playwright": "1.61.1" + "playwright": "1.62.1" }, "publishConfig": { "access": "public" diff --git a/packages/related/plugin-commonjs/package.json b/packages/related/plugin-commonjs/package.json index 5d49b41799..c291e1b692 100644 --- a/packages/related/plugin-commonjs/package.json +++ b/packages/related/plugin-commonjs/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/plugin-commonjs", - "version": "2.11.19", + "version": "2.11.20", "type": "module", "repository": { "type": "git", @@ -27,16 +27,16 @@ "@rollup/plugin-json": "6.1.0", "@rollup/plugin-node-resolve": "16.0.3", "@rollup/plugin-replace": "6.0.3", - "cjs-module-lexer": "2.2.0", + "cjs-module-lexer": "2.2.1", "is-valid-identifier": "2.0.2", "resolve": "1.22.12", - "rollup": "4.62.2", + "rollup": "4.62.4", "rollup-plugin-polyfill-node": "0.13.0" }, "devDependencies": { "@jsenv/assert": "../tooling/assert", "@jsenv/core": "../../", - "playwright": "1.61.1" + "playwright": "1.62.1" }, "publishConfig": { "access": "public" diff --git a/packages/related/plugin-preact/package.json b/packages/related/plugin-preact/package.json index 718a22e849..7bfbb2f4eb 100644 --- a/packages/related/plugin-preact/package.json +++ b/packages/related/plugin-preact/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/plugin-preact", - "version": "1.7.41", + "version": "1.8.0", "type": "module", "repository": { "type": "git", @@ -21,10 +21,10 @@ "@jsenv/core": "*" }, "dependencies": { - "@babel/plugin-transform-react-jsx": "7.28.6", - "@babel/plugin-transform-react-jsx-development": "7.27.1", - "@babel/plugin-transform-react-jsx-source": "7.27.1", - "@jsenv/ast": "6.9.0", + "@babel/plugin-transform-react-jsx": "7.29.7", + "@babel/plugin-transform-react-jsx-development": "7.29.7", + "@babel/plugin-transform-react-jsx-source": "7.29.7", + "@jsenv/ast": "6.9.1", "@jsenv/sourcemap": "1.4.2", "@jsenv/url-meta": "8.5.7", "@prefresh/babel-plugin": "0.5.3", @@ -37,7 +37,7 @@ "@jsenv/plugin-commonjs": "../plugin-commonjs", "@jsenv/plugin-explorer": "../plugin-explorer", "@jsenv/plugin-preact": "./", - "playwright": "1.61.1", + "playwright": "1.62.1", "preact": "11.0.0-beta.2", "react-redux": "9.3.0", "redux": "5.0.1" diff --git a/packages/related/plugin-preact/tests/jsx_import_meta_css/_jsx_import_meta_css_build.test.js/0_js_module/@jsenv/core/packages/related/plugin-preact/package.json b/packages/related/plugin-preact/tests/jsx_import_meta_css/_jsx_import_meta_css_build.test.js/0_js_module/@jsenv/core/packages/related/plugin-preact/package.json index 2b97233f57..0cf713e5ea 100644 --- a/packages/related/plugin-preact/tests/jsx_import_meta_css/_jsx_import_meta_css_build.test.js/0_js_module/@jsenv/core/packages/related/plugin-preact/package.json +++ b/packages/related/plugin-preact/tests/jsx_import_meta_css/_jsx_import_meta_css_build.test.js/0_js_module/@jsenv/core/packages/related/plugin-preact/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/plugin-preact", - "version": "1.7.41", + "version": "1.8.0", "type": "module", "repository": { "type": "git", @@ -21,10 +21,10 @@ "@jsenv/core": "*" }, "dependencies": { - "@babel/plugin-transform-react-jsx": "7.28.6", - "@babel/plugin-transform-react-jsx-development": "7.27.1", - "@babel/plugin-transform-react-jsx-source": "7.27.1", - "@jsenv/ast": "6.9.0", + "@babel/plugin-transform-react-jsx": "7.29.7", + "@babel/plugin-transform-react-jsx-development": "7.29.7", + "@babel/plugin-transform-react-jsx-source": "7.29.7", + "@jsenv/ast": "6.9.1", "@jsenv/sourcemap": "1.4.2", "@jsenv/url-meta": "8.5.7", "@prefresh/babel-plugin": "0.5.3", @@ -37,7 +37,7 @@ "@jsenv/plugin-commonjs": "../plugin-commonjs", "@jsenv/plugin-explorer": "../plugin-explorer", "@jsenv/plugin-preact": "./", - "playwright": "1.61.1", + "playwright": "1.62.1", "preact": "11.0.0-beta.2", "react-redux": "9.3.0", "redux": "5.0.1" diff --git a/packages/related/plugin-react/package.json b/packages/related/plugin-react/package.json index 03511e5f90..5864608ef1 100644 --- a/packages/related/plugin-react/package.json +++ b/packages/related/plugin-react/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/plugin-react", - "version": "1.7.46", + "version": "1.8.0", "type": "module", "repository": { "type": "git", @@ -21,11 +21,11 @@ "@jsenv/core": "*" }, "dependencies": { - "@babel/plugin-transform-react-jsx": "7.28.6", - "@babel/plugin-transform-react-jsx-development": "7.27.1", - "@babel/plugin-transform-react-jsx-source": "7.27.1", - "@jsenv/ast": "6.9.0", - "@jsenv/plugin-commonjs": "2.11.19", + "@babel/plugin-transform-react-jsx": "7.29.7", + "@babel/plugin-transform-react-jsx-development": "7.29.7", + "@babel/plugin-transform-react-jsx-source": "7.29.7", + "@jsenv/ast": "6.9.1", + "@jsenv/plugin-commonjs": "2.11.20", "@jsenv/sourcemap": "1.4.2", "@jsenv/url-meta": "8.5.7", "react-refresh": "0.18.0" @@ -36,7 +36,7 @@ "@jsenv/https-local": "../../tooling/https-local", "@jsenv/plugin-explorer": "../plugin-explorer", "@jsenv/plugin-react": "./", - "playwright": "1.61.1", + "playwright": "1.62.1", "react": "19.2.8", "react-dom": "19.2.8" }, diff --git a/packages/related/plugin-toolbar/dist/jsenv_plugin_toolbar_node_modules.js b/packages/related/plugin-toolbar/dist/jsenv_plugin_toolbar_node_modules.js index ac3f1e7e17..5161718888 100644 --- a/packages/related/plugin-toolbar/dist/jsenv_plugin_toolbar_node_modules.js +++ b/packages/related/plugin-toolbar/dist/jsenv_plugin_toolbar_node_modules.js @@ -542,6 +542,7 @@ function y({ data: t }) { const i = useSignal(t); + i.name = "ReactiveDom"; i.value = t; const [e, f] = q$1(() => { let t = this, @@ -633,12 +634,14 @@ g("__r", (t, i) => { }); n.c = t; return n; - }(() => { - var i; - if (h) null == (i = t.y) || i.call(t); - n.__$f |= 1; - n.setState({}); - }, "function" == typeof i.type ? i.type.displayName || i.type.name : ""); + }(function (t) { + return function () { + var i; + if (h) null == (i = this.y) || i.call(this); + t.__$f |= 1; + t.setState({}); + }; + }(n), "function" == typeof i.type ? i.type.displayName || i.type.name : ""); } b(t); } diff --git a/packages/related/plugin-toolbar/package.json b/packages/related/plugin-toolbar/package.json index 50354c375c..9f45b3dfdc 100644 --- a/packages/related/plugin-toolbar/package.json +++ b/packages/related/plugin-toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/plugin-toolbar", - "version": "1.5.34", + "version": "1.5.35", "type": "module", "repository": { "type": "git", @@ -28,15 +28,15 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@jsenv/ast": "6.9.0" + "@jsenv/ast": "6.9.1" }, "devDependencies": { "@jsenv/core": "../../", "@jsenv/filesystem": "../../tooling/filesystem", "@jsenv/plugin-toolbar": "./", "@jsenv/utils": "../../tooling/utils", - "@preact/signals": "2.9.4", - "playwright": "1.61.1" + "@preact/signals": "2.11.1", + "playwright": "1.62.1" }, "publishConfig": { "access": "public" diff --git a/packages/related/test/dist/jsenv_test_node_modules.js b/packages/related/test/dist/jsenv_test_node_modules.js index dc32eb2dbe..923681c6fd 100644 --- a/packages/related/test/dist/jsenv_test_node_modules.js +++ b/packages/related/test/dist/jsenv_test_node_modules.js @@ -223,8 +223,9 @@ function ansiRegex({onlyFirst = false} = {}) { // Valid string terminator sequences are BEL, ESC\, and 0x9c const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)'; - // OSC sequences only: ESC ] ... ST (non-greedy until the first ST) - const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`; + // OSC sequences only: ESC ] ... ST + // The payload stops at the first terminator character rather than scanning ahead for one, so an unterminated `ESC ]` cannot rescan the rest of the input. Terminals likewise abort a control string on an unexpected ESC. + const osc = `(?:\\u001B\\][^\\u0007\\u001B\\u009C]*${ST})`; // CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte const csi = '[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]'; diff --git a/packages/related/test/package.json b/packages/related/test/package.json index 23e4b74491..c568f49500 100644 --- a/packages/related/test/package.json +++ b/packages/related/test/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/test", - "version": "3.7.31", + "version": "3.7.32", "type": "module", "repository": { "type": "git", @@ -30,8 +30,8 @@ }, "dependencies": { "@c88/v8-coverage": "0.1.1", - "@jsenv/ast": "6.9.0", - "@jsenv/plugin-supervisor": "1.8.10", + "@jsenv/ast": "6.9.1", + "@jsenv/plugin-supervisor": "1.8.11", "@jsenv/sourcemap": "1.4.2", "he": "1.2.0", "istanbul-lib-coverage": "3.2.2", diff --git a/packages/tooling/assert/dist/browser/jsenv_assert_node_modules.js b/packages/tooling/assert/dist/browser/jsenv_assert_node_modules.js index 717551dcbe..8bbbbe2510 100644 --- a/packages/tooling/assert/dist/browser/jsenv_assert_node_modules.js +++ b/packages/tooling/assert/dist/browser/jsenv_assert_node_modules.js @@ -4,8 +4,9 @@ function ansiRegex({ // Valid string terminator sequences are BEL, ESC\, and 0x9c const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)'; - // OSC sequences only: ESC ] ... ST (non-greedy until the first ST) - const osc = "(?:\\u001B\\][\\s\\S]*?".concat(ST, ")"); + // OSC sequences only: ESC ] ... ST + // The payload stops at the first terminator character rather than scanning ahead for one, so an unterminated `ESC ]` cannot rescan the rest of the input. Terminals likewise abort a control string on an unexpected ESC. + const osc = "(?:\\u001B\\][^\\u0007\\u001B\\u009C]*".concat(ST, ")"); // CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte const csi = '[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]'; diff --git a/packages/tooling/assert/dist/node/jsenv_assert_node_modules.js b/packages/tooling/assert/dist/node/jsenv_assert_node_modules.js index a443162047..1c16d18d10 100644 --- a/packages/tooling/assert/dist/node/jsenv_assert_node_modules.js +++ b/packages/tooling/assert/dist/node/jsenv_assert_node_modules.js @@ -12614,8 +12614,9 @@ function ansiRegex({onlyFirst = false} = {}) { // Valid string terminator sequences are BEL, ESC\, and 0x9c const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)'; - // OSC sequences only: ESC ] ... ST (non-greedy until the first ST) - const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`; + // OSC sequences only: ESC ] ... ST + // The payload stops at the first terminator character rather than scanning ahead for one, so an unterminated `ESC ]` cannot rescan the rest of the input. Terminals likewise abort a control string on an unexpected ESC. + const osc = `(?:\\u001B\\][^\\u0007\\u001B\\u009C]*${ST})`; // CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte const csi = '[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]'; diff --git a/packages/tooling/eslint-config-relax/package.json b/packages/tooling/eslint-config-relax/package.json index fbb909aee8..aabe3adf40 100644 --- a/packages/tooling/eslint-config-relax/package.json +++ b/packages/tooling/eslint-config-relax/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/eslint-config-relax", - "version": "1.8.9", + "version": "1.9.0", "type": "module", "description": "A pleaseant ESLint configuration for any project", "repository": { @@ -27,7 +27,7 @@ ], "scripts": {}, "dependencies": { - "@babel/eslint-parser": "7.28.6", + "@babel/eslint-parser": "7.29.7", "@jsenv/eslint-import-resolver": "8.4.6", "@jsenv/eslint-plugin": "1.4.1", "@jsenv/urls": "2.9.10", @@ -35,8 +35,8 @@ "eslint-plugin-html": "8.1.4", "eslint-plugin-import-x": "4.17.1", "eslint-plugin-react": "7.37.5", - "eslint-plugin-regexp": "3.1.1", - "globals": "17.7.0" + "eslint-plugin-regexp": "3.2.0", + "globals": "17.11.0" }, "devDependencies": {}, "publishConfig": { diff --git a/packages/tooling/eslint-import-resolver/dist/jsenv_eslint_import_resolver.cjs b/packages/tooling/eslint-import-resolver/dist/jsenv_eslint_import_resolver.cjs index d2e5dc0109..ec1e1e74a3 100644 --- a/packages/tooling/eslint-import-resolver/dist/jsenv_eslint_import_resolver.cjs +++ b/packages/tooling/eslint-import-resolver/dist/jsenv_eslint_import_resolver.cjs @@ -262,8 +262,9 @@ function ansiRegex({onlyFirst = false} = {}) { // Valid string terminator sequences are BEL, ESC\, and 0x9c const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)'; - // OSC sequences only: ESC ] ... ST (non-greedy until the first ST) - const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`; + // OSC sequences only: ESC ] ... ST + // The payload stops at the first terminator character rather than scanning ahead for one, so an unterminated `ESC ]` cannot rescan the rest of the input. Terminals likewise abort a control string on an unexpected ESC. + const osc = `(?:\\u001B\\][^\\u0007\\u001B\\u009C]*${ST})`; // CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte const csi = '[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]'; diff --git a/packages/tooling/fetch/package.json b/packages/tooling/fetch/package.json index b24041cdc5..ddef9065aa 100644 --- a/packages/tooling/fetch/package.json +++ b/packages/tooling/fetch/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/fetch", - "version": "1.3.34", + "version": "1.3.35", "type": "module", "description": "Unified url fetcher (http, http, file, data)", "repository": { @@ -29,7 +29,7 @@ "/src/" ], "dependencies": { - "@jsenv/server": "17.6.0", + "@jsenv/server": "17.6.1", "@jsenv/urls": "2.9.10", "node-fetch": "3.3.2" }, diff --git a/packages/tooling/https-local/package.json b/packages/tooling/https-local/package.json index 3892f8d913..d3a16fbec6 100644 --- a/packages/tooling/https-local/package.json +++ b/packages/tooling/https-local/package.json @@ -50,7 +50,7 @@ "@jsenv/https-local": "./", "@jsenv/performance-impact": "../performance-impact", "@jsenv/test": "../../related/test", - "playwright": "1.61.1" + "playwright": "1.62.1" }, "publishConfig": { "access": "public" diff --git a/packages/tooling/lighthouse-impact/package.json b/packages/tooling/lighthouse-impact/package.json index a02aa79a8f..176df44a56 100644 --- a/packages/tooling/lighthouse-impact/package.json +++ b/packages/tooling/lighthouse-impact/package.json @@ -37,7 +37,7 @@ "@jsenv/assert": "../assert", "@jsenv/lighthouse-impact": "./", "@jsenv/server": "../../backend/server", - "playwright": "1.61.1" + "playwright": "1.62.1" }, "publishConfig": { "access": "public" diff --git a/packages/tooling/md-up/package.json b/packages/tooling/md-up/package.json index 2b886e95b2..3f33540901 100644 --- a/packages/tooling/md-up/package.json +++ b/packages/tooling/md-up/package.json @@ -19,7 +19,7 @@ "/src/" ], "dependencies": { - "@jsenv/ast": "6.9.0", + "@jsenv/ast": "6.9.1", "@jsenv/filesystem": "4.15.17", "@jsenv/urls": "2.9.10", "anchor-markdown-header": "0.8.4", diff --git a/packages/tooling/snapshot/package.json b/packages/tooling/snapshot/package.json index c16394163c..ab2a8d367b 100644 --- a/packages/tooling/snapshot/package.json +++ b/packages/tooling/snapshot/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/snapshot", - "version": "2.19.1", + "version": "2.19.2", "type": "module", "description": "Snapshot testing", "repository": { @@ -28,11 +28,11 @@ ], "dependencies": { "@jsenv/assert": "4.5.7", - "@jsenv/ast": "6.9.0", + "@jsenv/ast": "6.9.1", "@jsenv/exception": "1.2.2", "@jsenv/filesystem": "4.15.17", "@jsenv/humanize": "1.7.8", - "@jsenv/terminal-recorder": "1.5.54", + "@jsenv/terminal-recorder": "1.5.56", "@jsenv/url-meta": "8.5.7", "@jsenv/urls": "2.9.10", "@jsenv/utils": "2.3.1", diff --git a/packages/tooling/terminal-recorder/dist/jsenv_terminal_recorder_node_modules.js b/packages/tooling/terminal-recorder/dist/jsenv_terminal_recorder_node_modules.js index a356268959..557564158c 100644 --- a/packages/tooling/terminal-recorder/dist/jsenv_terminal_recorder_node_modules.js +++ b/packages/tooling/terminal-recorder/dist/jsenv_terminal_recorder_node_modules.js @@ -2,8 +2,9 @@ function ansiRegex({onlyFirst = false} = {}) { // Valid string terminator sequences are BEL, ESC\, and 0x9c const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)'; - // OSC sequences only: ESC ] ... ST (non-greedy until the first ST) - const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`; + // OSC sequences only: ESC ] ... ST + // The payload stops at the first terminator character rather than scanning ahead for one, so an unterminated `ESC ]` cannot rescan the rest of the input. Terminals likewise abort a control string on an unexpected ESC. + const osc = `(?:\\u001B\\][^\\u0007\\u001B\\u009C]*${ST})`; // CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte const csi = '[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]'; diff --git a/packages/tooling/terminal-recorder/package.json b/packages/tooling/terminal-recorder/package.json index b3893a0da1..4a4d0ad636 100644 --- a/packages/tooling/terminal-recorder/package.json +++ b/packages/tooling/terminal-recorder/package.json @@ -1,6 +1,6 @@ { "name": "@jsenv/terminal-recorder", - "version": "1.5.54", + "version": "1.5.56", "type": "module", "description": "Record terminal output as .svg, .gif, .webm, .mp4", "repository": { @@ -35,13 +35,13 @@ }, "dependencies": { "@jsenv/humanize": "1.7.8", - "@jsenv/server": "17.6.0", + "@jsenv/server": "17.6.1", "@jsenv/terminal-text-size": "0.0.4", - "@playwright/browser-chromium": "1.61.1", - "ansi-regex": "6.2.2", + "@playwright/browser-chromium": "1.62.1", + "ansi-regex": "6.3.0", "eastasianwidth": "0.3.0", "he": "1.2.0", - "playwright": "1.61.1", + "playwright": "1.62.1", "strip-ansi": "7.2.0", "webm-to-mp4": "1.0.0" }, diff --git a/src/plugins/autoreload/client/autoreload.js b/src/plugins/autoreload/client/autoreload.js index c7a058c70e..d549d9f0a7 100644 --- a/src/plugins/autoreload/client/autoreload.js +++ b/src/plugins/autoreload/client/autoreload.js @@ -12,6 +12,7 @@ import { export const initAutoreload = ({ mainFilePath }) => { let debug = false; + let fullReloadRequested = false; const reloader = { urlHotMetas, status: { @@ -64,6 +65,14 @@ export const initAutoreload = ({ mainFilePath }) => { }, currentExecution: null, reload: () => { + if (fullReloadRequested) { + // The document is already being replaced; anything we would do here + // dies with the current page. Calling window.location.reload() again + // would abort the navigation already in flight (net::ERR_ABORTED). + // This happens for a single file edit: the change is sent first, then + // the prune of the urls it no longer references arrives a few ms later. + return; + } const someEffectIsFullReload = reloader.changes.value.some( (reloadMessage) => { if (reloadMessage.type === "full") { @@ -90,6 +99,7 @@ export const initAutoreload = ({ mainFilePath }) => { }, ); if (someEffectIsFullReload) { + fullReloadRequested = true; dispatchBeforeFullReload(); reloadHtmlPage(); return; diff --git a/tests/build/new_url_directory/_new_url_directory.test.mjs/0_error/0_error.md b/tests/build/new_url_directory/_new_url_directory.test.mjs/0_error/0_error.md index 63db04854e..22e2fbf405 100644 --- a/tests/build/new_url_directory/_new_url_directory.test.mjs/0_error/0_error.md +++ b/tests/build/new_url_directory/_new_url_directory.test.mjs/0_error/0_error.md @@ -15,7 +15,7 @@ base/client/main.html:15:40 14 | \ No newline at end of file diff --git a/tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/.jsenv/chrome@149.0.0/main.html@L9C5-L13C14.js.map b/tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/.jsenv/chrome@151.0.0/main.html@L9C5-L13C14.js.map similarity index 100% rename from tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/.jsenv/chrome@149.0.0/main.html@L9C5-L13C14.js.map rename to tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/.jsenv/chrome@151.0.0/main.html@L9C5-L13C14.js.map diff --git a/tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/.jsenv/chrome@149.0.0/node_modules/foo/foo_index.js b/tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/.jsenv/chrome@151.0.0/node_modules/foo/foo_index.js similarity index 100% rename from tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/.jsenv/chrome@149.0.0/node_modules/foo/foo_index.js rename to tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/.jsenv/chrome@151.0.0/node_modules/foo/foo_index.js diff --git a/tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/0_chromium.md b/tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/0_chromium.md index 519499a362..8acc307141 100644 --- a/tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/0_chromium.md +++ b/tests/dev_and_build/sourcemap_js_inline/_sourcemap_js_inline_dev.test.mjs/0_chromium/0_chromium.md @@ -4,9 +4,9 @@ run({ browserLauncher: chromium }) ``` -# 1/2 write 3 files into "./.jsenv/chrome@149.0.0/" +# 1/2 write 3 files into "./.jsenv/chrome@151.0.0/" -see [./.jsenv/chrome@149.0.0/](./.jsenv/chrome@149.0.0/) +see [./.jsenv/chrome@151.0.0/](./.jsenv/chrome@151.0.0/) # 2/2 resolve diff --git a/tests/dev_and_build/spa/_spa_dev.test.mjs/0_chromium/.jsenv/chrome@149.0.0/index.html b/tests/dev_and_build/spa/_spa_dev.test.mjs/0_chromium/.jsenv/chrome@151.0.0/index.html similarity index 96% rename from tests/dev_and_build/spa/_spa_dev.test.mjs/0_chromium/.jsenv/chrome@149.0.0/index.html rename to tests/dev_and_build/spa/_spa_dev.test.mjs/0_chromium/.jsenv/chrome@151.0.0/index.html index 7cab2b263c..250ccaab46 100644 --- a/tests/dev_and_build/spa/_spa_dev.test.mjs/0_chromium/.jsenv/chrome@149.0.0/index.html +++ b/tests/dev_and_build/spa/_spa_dev.test.mjs/0_chromium/.jsenv/chrome@151.0.0/index.html @@ -53,7 +53,11 @@ import { initDependencyStatus } from "/@fs@jsenv/core/src/plugins/dependency_status/client/dependency_status.js"; initDependencyStatus({ - "problems": [] + "problems": [], + "watchInfo": { + "packageJsonPath": "package.json", + "pollInterval": 500 + } });