// ==UserScript== // @name JHS-enhance // @namespace JHS-enhance // @version 3.3.16 // @author xiebro,fireinrain // @description Jav-鉴黄师 增强脚本。列表页:作品状态标签、一键筛选、新作品检测、演员黑名单过滤;详情页:磁力链接高亮、DMM 多画质预览视频、标题翻译、整合字幕搜索(迅雷+SubTitleCat)并支持 115 直传、多源预览图(javfree/projectjav/javstore)含来源切换;数据:115 网盘目录匹配与多目录选择、云盘备份/恢复、跨 Tab 同步;其他:自动翻页、分类折叠、Top250、以图识图、热门榜单、评论查看、相关清单。支持 JavDB / JavBus / JavSee / SeeJav / FC2 / JavTrailers // @license MIT // @icon https://www.google.com/s2/favicons?sz=64&domain=javdb.com // @include https://javdb*.com/* // @include https://*javbus*/* // @include https://*javsee*/* // @include https://*seejav*/* // @include https://javtrailers.com/* // @include https://subtitlecat.com/* // @include https://www.aliyundrive.com/* // @include https://www.alipan.com/* // @include https://115.com/* // @match https://javdb.com/* // @match https://www.javbus.com/* // @exclude https://*javbus*/forum/* // @exclude https://*javbus*/*actresses // @exclude https://*javsee*/forum/* // @exclude https://*javsee*/*actresses // @exclude https://*seejav*/forum/* // @exclude https://*seejav*/*actresses // @require https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js // @require https://cdn.jsdelivr.net/npm/layui-layer@1.0.9/dist/layer.min.js // @require https://cdn.jsdelivr.net/npm/blueimp-md5@2.19.0/js/md5.min.js // @require https://cdn.jsdelivr.net/npm/toastify-js@1.12.0/src/toastify.min.js // @require https://cdn.jsdelivr.net/npm/localforage@1.10.0/dist/localforage.min.js // @require https://cdn.jsdelivr.net/npm/viewerjs@1.11.1/dist/viewer.min.js // @require https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js // @require https://cdn.jsdelivr.net/npm/systemjs@6.15.1/dist/system.min.js // @require https://cdn.jsdelivr.net/npm/systemjs@6.15.1/dist/extras/named-register.min.js // @require data:application/javascript,%3B(typeof%20System!%3D'undefined')%26%26(System%3Dnew%20System.constructor())%3B // @connect xunlei.com // @connect geilijiasu.com // @connect aliyundrive.com // @connect aliyundrive.net // @connect ja.wikipedia.org // @connect beta.magnet.pics // @connect jdforrepam.com // @connect cc3001.dmm.co.jp // @connect cc3001.dmm.com // @connect www.dmm.co.jp // @connect www.dmm.com // @connect api.dmm.com // @connect special.dmm.co.jp // @connect adult.contents.fc2.com // @connect fc2ppvdb.com // @connect 123av.com // @connect u3c3.com // @connect u9a9.com // @connect btsow.lol // @connect sukebei.nyaa.si // @connect javstore.net // @connect javbest.net // @connect missav.live // @connect jable.tv // @connect www.av.gl // @connect jav.rs // @connect javtrailers.com // @connect javdb.com // @connect javbus.com // @connect supjav.com // @connect 115.com // @connect jianguoyun.com // @connect 127.0.0.1 // @connect * // @grant GM_addStyle // @grant GM_info // @grant GM_openInTab // @grant GM_xmlhttpRequest // @grant unsafeWindow // @grant window.close // @run-at document-idle // ==/UserScript== (a => { if (typeof GM_addStyle == "function") { GM_addStyle(a); return } const A = document.createElement("style"); A.textContent = a, document.head.append(A) })(" html #layuicss-skinlayercss{display:none;position:absolute;width:1989px}.layui-layer-shade,.layui-layer{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%}.layui-layer{-webkit-overflow-scrolling:touch}.layui-layer{top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px #0000004d}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px #0003}.layui-layer-load{background:url(data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs=) #eee center center no-repeat}.layui-layer-ico{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANIAAAA9CAYAAADYizcVAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAACAASURBVHic7Z15fFvVmfe/92qzJMtrvCQ2jp09TuwsQCAkISwJDVAoW0hombaQhgKlnU5pmc60A+3M0FK6dxhoGghLC0PCEjJAIW8gISQhwWSzHcfO4tix41heZNmSLFnS1T3vH7IUy1qdmPfTd8a/z8cfS/ee5/zuvbq/e855znOeKwkhBEkg3FYC1j0Eug6i2upQHU0ITycAkjEfOaMMOXcWmrz5aAoXIZkKk1WZEs563ezstVLV10WNy8bJfgedPg8A+XojU8wZVKbnsiAzj6VZhUwwmEaFV7Xb8NXV4D95DKWliYC1DbW3FwA5KwtNYRHakjJ0U6ajn1WJnJ07KrzCd5aAYyeqqwrVXYM6cBLhH7zOunzktCnIpkrk9AVoMpYi6SeMCq/D4aCpqYmWlhasVis2mw2n0wmAxWIhNzeXwsJCSkpKKCsrIyMjY1R41Z5ufLWH8R8/ir+5kUB7G2pvDwByVg6a8UXoSiejm1aOvmIucs64UeG1+vvY42rkQH8zRz1nOeXtplMJnm++1sIkwzjKjRO42FzKovTJFOoyk9YpJRKSam9AaXoLpfld1J6jKR2knFOOtvRGtGW3IGfPSPHUIlHXb2dTRxObO5updfakZFNhyeHW/FLuLChjljn7vHiV1tMM7N2Jt2oPyummlGy0E8swLFhE2sKlaC+aeF68qrsOxbaJQM9m1P7alGxkcwWanFvR5t6JbJp1XrwdHR3U1NRQV1dHe3t7Sjbjx49n1qxZVFZWUlBQcF68SksTA7u2M7D3Y5TmxpRstKWTSVt4JWlLrkFbUnZevA0DVt6yH+Sd3hqOes6mZFNunMAXsyq5JXs+M9LiNxBxhCTwNzyPv/4lVFstctZUNIWLkPPno8kuR7KUIBmyEV47wtlCwH4UtfMgAese1N4TAMi5FehmfhXdjHsAKaWDFsAfzzSwvq2eQw4bM8xZLM0pZEFGPhXp2ZQZLeRoDfQoXpo8TmpddqocnezssdLQH2wx5mXksrZoJvcXz0iRFRACz7Z38XzwV/xNJ9EWlaArr0A3ZQbaiWVo8guQ0zNQXQ4CnR0op5vwn2zAf7QWpa0FAF3ZFIzLbsC4/EaQUmUWKB1/xN+xHtV1CNk0AzljKZr0BcimCqS0MiRtDkLpQQw0obprCbiqUB07Ud0NweucPg9dwVq0Bfenfp2FoKqqik8//ZS2tjby8/MpKyujpKSEwsJCsrOzMZlMuN1u7HY7VquVlpYWmpqa6OwMtpBFRUVcdtllLFiwACnV8xUC93tv4dn6Nv7G42gvmoh+9jx002aiLZ2EpmACssWC6nQS6DiL0nwK//F6fEcOobSeDl7nydMwfuEmTNffkvJ1Fgie797DS92fUOM+w9S0AhalT2G+uZRZxvGU6HPJ1pqwK25afDbqPO0c7G9mj+skJwY6AKg0FfPVcVdwz7hFSDGuc5SQhKcTX+1/4K95GjlvLrpJt6It+xJS+kXJD9jTiXJyE/5Tm1G7DgdPvPJB9BXfRjLmJ7S1+jz88nQtv2mu4ZLMPFYVTGJlQRkT09KT8lp9Hv7SfpKNHafY39cFwPdKK/nBxAoK9caEtmqvnf7/3oT77TfQTZ5G2hVXYVh4JZq8xMcbsh34+EMGPvkIf+NxAEw33Y755juRsxK3isJvxX/2l/jbfoNsuQRt7iq0uSuRDMlbNeG3onT9BcW2EdW5HwBd0ffQTfgBki5xt9rpdLJz50527dpFcXExc+bMoaKiguzs5K240+nk0KFDVFdXc+bMGQCWLFnC0qVLsVgsCW1Vew/9b75C/+ZX0U2dSdqSa0hbfDWa/OTDANXeg2fHVgZ2bcd/oh4A862rMd/2ZeTsnIS2nX4Hf+j8kKc7djDXVMJt2fP5UvZcLtIntgvZburZz5v2gxx2Bx+YDxZczXfyryVfF9m91fzkJz/5SeiL8HTiO/AE/rpn0c28F8MlP0I78UYkffI+IoCkM6MpWICm6CpQ/ajdh1A7PgO/C03ePCSdOaad1efhscYDPNVSxwMXzeTxyZdwS/5EsrT6lHjTNTquyCpgeU4RPqGy39HN3t4OnAE/l2bmka7RxbRTe+24Nr2E570tmL5wE+l33YNhwRXI5tjHGXW+aUZ008vRV86HgIK/8Tj+4/WIAQ+6ydOR0mKLWPit+FofQ2l/Ct34B9Bf9DjanFuQtFmp8WrS0ViuQJu1HPChuvajOvciVCea9EuRNLEfPk6nk23btvHJJ5+wcOFCVqxYwaxZszAaEz9sQjAYDEycOJGpU6eiqipnzpyhpaUFr9dLcXExBoMhpp1q78H18nO433kD0w23YvnqWtIWXolsTv6QBJCMRvQzKzDMuxQUBf+JBvwNRxAeN7qpM5HiHH+n38HP29/j2a6PuTdvMT+e8EVuzKokU5Pa+Zo1Bhakl3FVxnT8IsAhdwuf9TfjVL3MN5Vg1pw73yFCEvgO/QLl+CvoL34Ew6WPIhnzUiKMOnFDFtqS5aDRoXYdINC5HySBtuhqhnc/BPCTU4d4oe04j06+mCemXkpBklYkHrJ1Bm4YV4JO1lDV18Xevk6EJHFdblF0YywErtdeYmDH/yF95d2kf3kNcub5ja1kczqGeQuQtFr8J4/hP34UEBgqL47R/RD4z/yEQNcL6C96FH3JE0i68xtrSNpsNNk3IMk61P4qVMdekASarOuIus5CsG3bNvbv38+yZctYsWIF6emp3cjDYTQamT59OhqNhtbWVlpaWhBCMHXq1OhunhC4XtmA54O/YrnrXixfvx85K3lrEAtyugXDJQuRtLqgg6LhCCCCApOG31eCJ6zv8YrtUx4Zfz2PFd1Eni5xqxkPWRoTyzPL0UlaDvSfZn9/E6okuDpjRribFxaSv+F5fJ/9DP3Fj6Cf8z1S7W/Hh4SmcCFIEDi7C7XjMyRTHppx8yJK/fFMAz8++RmPTr6YH5XNGQVWuDK7EIHE9p6z7O3toMBg4tKMSI+PZ9u7uP7rBdJX3o35ti+PYFwTj1hCP7MCEPiOHMZ/vB5NVg66ydMiiikdf8R3+sfoL3oUXdGPGJXrnHElIAj0bUd17kXWFyCnXxpRqqqqiq1bt7Js2TKuueaa1Mc18VglibKy4KC/sbGRlpYWMjIyKC4ujijnfu8tXH9ej+WuezGv+uroXOfZc5AE+GoO4G84gpydg27qzIhiz3fv4fGz7/LI+Ot5uPC6mOOaEdEicUX6ZASwy3mcz/qbyddlMM9UAgwKSbU34Nv/ONrSL2K49FGS/biuZ3PxHXwS38En0U6+HSkt/hNGU3g5wtON2n0I0W9FU3g5kjF4U9f12/lx435uzS/liamXXvAtNRRLsgvp9HvY7+jmrK+fxdmF5A+2dErrafpffYG0yxaR/uU1ET+u0taC0tqcUt8dVcV7sArZaAp3L/QzZiMcffgbj6PabehmzEbODHbZVHcdvtYfo829FX3JEyS6zsJ3FqVzA0rX86h9HyB87UhpU5Dk2N0nTcYSUDpRXfsR/rNoLIuRdMFxXkdHB1u3bmX27NmsWLEioYjcbjdbtmzh/fffp6GhAbPZTG5ufPd+aWkp/f39nDlzBofDQWlpabilU1qacP15PWkLr8Ty9fsvXERDoJ9VidrXi/9EA2qPLTgNMdijaBiw8vjZd7gxq5LHim66YBENxcL0SXQrLg65W7D6+7g8fTLjtOnIAErTWyBr0JXfy4U/IYdDQlf5EHLeXFRbbZBrEJs6mtBIMg8Wl48Ka31/L58OOhsk4AcTK7kkM49DDhubOs65swf27gRZxviFmyJ+XLXXjvvtN+h/4xUC7WeS8nmrD9D/2p/xbH+fQHfn4OlKmG5eiW7yNPxNJ4Ncg1Bsm0DSoC14kETXOdCzGc/hcnxN30Wx/gn/2T/gPbkGz+FyAn3b41hJ6Cb8ANlyCarrUJBrEDU1NUiSxMKFCxOKqKWlhccff5yOjg5mzJiBTqfjxRdf5NVXX41rI0kSV155JcXFxbS1tVFTUxPeN7BrO8gaTDfeGlNEgY52+t94hYGd2xCKP2q/6ujD/e5m3O9uRnU5hxNjvv3L6KbOxN94PMg1iLfsB5GRWTNuyaiKCIIt07cLrmWuqYQa9xnesh8EQBZuK0rzu+gm3YqcPTNJNecH2TIR3aRbAVCa30W4rZz1utnc2cyqgknMTj+/sclQ1Lrs3Fb9AbdWb2NfX/CmLjNaWFUwCYDNnc2c9bpR7Ta8VXtIu+IqtBeVRtThqz2E58P38NUewvHc0wQ6rXH5fEcO43pxHf7G4/Rv/i8Gtm8N79PkF5J2xVUAeKv2oNptwcnWns1oc1chm2bHrVftP8xAw+0IpS+4QdaDHHSWCO8ZBupXoHrqY9pKhjK0uauAoBiF7ywOh4O6ujrmzJmTdN5nw4YNXHXVVXzrW9/ihhtu4O677+ZHP/oRtbW11NfH5gTIyclhzpw5ANTV1eFwOFB7uhnY+3Fw3mfipBgnqjKwcxvu/36N/rc24a8/ElXEe2Af/Vs24v7v1/Dt3xu1X1MwnrQl1wAwsPdj1J5urP4+3umt4bbs+cw0jo+y2Ww/xEXVPyDn4HeYWP2PbO2ri9ivCJVvn36FcQf/npyD32H5sd/QrbgiykzU53Jb9nwA3umtwervQw5Y94DqR1v2pbgXajSgnXInctZU1J6jBKx72Nlrxa+qrCw4v8m1oTjstLGy5kMa+ntp97r59rFzF/3u8VOYYc6i1tnDzl4rvroaUAIYFl4ZfYxlUzBcthgAX/V+nM8+FVNMvrpqnM89hdLWgqTTY7zmevRzL4kok3bltWiLSlBON+GrqyHg2AnCjzZ3ZcJz8bX+M0EXjIy+9BeY5jdhmn8KXfEjgASqH3/rv8S11+bdjWyagdpfS8Cxk6amJgKBABUVFQl5jx8/TiAQYPny5RHbTSYTc+bMYdu2bQnt582bR35+Pu3t7TQ1NeGrPQwBhbTFV8c+z5oDuP7reQK2Lvwn6nG99CfU/nM3rNLajOvP6wmcPYPS1oLzz+tRWpuj6jFe/QW0F01EaW7EV3uYPa5G/CLAl7LnRnOKAOu6dtIf8ALgDHh4qvPDiDINA+28bNuHSnBW6EB/M+/1RYv8zpxLmJpWwFHPWfa4GpEDXQeDYT0pzBNdCCRjPprCRQAEug5S1dfF0pzClOaJEuGw08ZdtTs4Njgha9Ho+M5F52b6C/VGluYExztVfV34Tx5DV14Rc55IW1xCxpqHSBsUmfdQVVBMHedm/X1HDuN87j9RzrQg6Q2YbriF9LvXoJsW2ZrLWdnoyoM3r//kMVRXFXLG0sTzRKoX1fUZAJrsZegmPIKkn4CkL0Zf8gtkY/ChE3Dui1uFpCtEzlgarM5VRUtLC2VlZUnnibq7uzHHcfsXFxfjdrsT2lsslrDzoaWlBf/xo+hnz4s71gx0dyH8/iHfOxFOR/i72tdHoKvz3Hd7D6o9OspFzs5BPzvowPIfP8qB/mYWpU+JOU8kI6GXNBHb9JI24rt22H4AXYxt+boMFqVPAYJik1VbHXL+/JgnO9oI8ai2OmpcNhZkxJ70PODo5pi7L2l9Na4e7q77KBzVkK7R8Uz5Yv5u/JSIciGeGpcNpaUJ3ZT4oUtydg6Wex4g7fIlwKCYnn8Gpa0V39EanBueRmltRtLpMd14G+Y7/w5JH9sBEOJRWppQ3TVo0hckPB+h9CB83QBRghNKD0KxJ7QPIcSjumuwWq2UlJSkZHehCPFYrVb8zY1RD5eh0M+ei27S1PB3w+VL0OSd63pqJ5aStnDJuf0XX4Z2mAc0hBCPv7mRo56zzDeXxiynlWT+vmA5BYOxc0X6bB4u/EJEmelpBfx94bKweJZnzmJFZuwQrBDPUc9ZtKqjCU12edwTHk2EeFRHEyctDiqGjY0EsNNu5a7a7YzTG3ijchnTTLEng2tcPdx95CPqnMGby6LRsW7WEu4qiO6Ph3hO9jsIWNvQTkzcnZSzc7Hc8wBCCLyf7sZ7YB+qsw/hHQiKSKvFdMMtmG+/K66IgDBPwNqGOnAS2ZS4eyVpMzFMeRZEANk8J7xddVXhbbwX4Q+eqyZzWeLjH+RRB05is9koLBydIOJkCPHYbDYC7W1oS2OMjQahKZxA1g//De+hKiSTmbTLFoPm3JNftmSS8cDDGC6+HKGqpC1cEncCN8QTaG/jlDebWTHGRiFcmzGD96d/l5MDXcw0FjJBFzkJLiHx2ISbuSlzDv2qnwXmiRjk2BP6IZ5T3m60wtOJZPl/88QK8QhPJ50+D2XGyAkyu9/LQ8f2YPW6sXrdrKrdzqsV1zB9mJhqXD3cVbODo/3BGytDq+NP5VeyKs54K8TT6fOg9vaiyU8+ASrnjCPj3gdxpRnx7NyG/3hwsC2nWzCuuBnzzSuRDGkJ6wjxqL29CH8nUlqS8aBsQpu/JmKTv/23+JoehsE+u2ycgn7ikwmrCfEIfydOpzNpt+73v/89TqcTt9vNr3/966j9Ho8nvO+aa65h3rx5MWohzON0OlF7e9AUJI5O10woxjShOO5+OTsn6FlNghCP2ttDp+KkRJ84Gn+iPpeJScrMNycP1QrxdCpOtACS4cK9ZqlgOE+ONvJpbtJoebC4nEeOf0p/QOGww8aq2u28Mvtqys3BJ8dhp41VNds5Ptj1y9TqWT9rCSvz49+kw3nk9NSWAcg54zBcuhDP7u0QCATPwZxO2uVLkIzJl2wM55G0I5vRVz3H8DV9L/xdm/dl9GVPIWkT/17DeUymxMfa3t7OqlWrkh7Pzp07qa+vjyuk4Txykvi70cJwnmzt6CynSYahPLJkzEd4U+t7XyhCPJIxn3y9kR7FG7E/TdbwYPFMfjfjCkya4CCw2mHj7iM7OOVxUt/fy+raHWERZWh1rCtPLCIgzJOvNyJnZaG6HAnLh+Crq6Z/86thEQGoPTZcr/2ZgDV5GH6IR87KQtLlI5TUloSEILzN4c/agq9jmPpyUhEBYR5Jl4/FYknqKJAkiXnz5oX/AoEAW7Zs4eDBgxHbkwWmhngsFgtyVg6q05mw/GghxCNn5ZCvtWBXEp/vaCHEk6+1oJUzyhDOFqS00VmclgjCGYyglTPKmGLOoMnjZJwuunv0jQnTCAiVf2jYi0cNcMhh47aaD3ArCicGRWTR6Hhm5uK43bmhaPIEL/QUcwaawiICnR3IlsSBuL4jh3E+O+ji1hswLrsB1dnHwK7teD/dA34/lnu/haYgfn880BkMwdcUFiGnTUEMNCGlj2BxmiSHP2qyvpCgYCTEQHDyWU6bQm5uLna7Pa5HLhb27dtHTk4OJ06cSP1YAbs9+KDMzc1FM76IQMdZ5IzUAp4h2DVTXU5QFNDpkDMyk/5OAIGO4ENNM76ISYZxtPhs5GpTP9/zRYvPBsAkwzi0cu4sAvajyHmxm+tYSP+G7byIA/bg4kA5dxaV6bnUuuxcmhE7MPabRTOQkfjusb24AwrVjnOcGVodfyyP7ViIhVpX8AeuTM9FW1KGcropKgZuKHx11Tif/Q+UtlaQ5aBj4bYvIwY84PczsG8X3kOfIZ79DzK+8e24YgotDtSWlCGbfKju2qgYuIQQyrnPgdSf7qo7uDhQNlVSWFiI1WqNioFLhOuuu44NGzYwffr0lG0g6K2DoNNBhwel+VRUDFzUsTr7GNizE1/1AZRTJ1B7exBCIMky8ri84JKLhUvQz78MSRt70K80nwJAVzqZcuME6jzt4Ri4oWgYsPKh4yhr85ZGucHjoS/g4Xcd21honsJ1mZFOuTpPcFqk3DgBWZM3H7XzYEqVXihCPJq8+SzIzKPK0Zmw/Nqi6fx6+uVR29eNQERAmGdBZh66KdPxn2yIW9bfcATn+kERaTSYb7oD821fRjIaw65xw2WLQAh81QdwrP9DxDxTRF2DPLop05HTFxBwVaV8zACy+WLSyt8nbdZWNJmpt0ghHjl9ASUlJbS0tIyId9q0aVRWVnLbbbeNyC7EU1JSgm5aedhBEwuqy4l7yyZs3/0GjqeeZGDXhwRsXaDXB1sxnY6A9Syebe9i/9cfYv/JD/DVHopZV4hHN62ci82lHOxvjlnursZ1/MuZt7jz5B855E58TVwBL6/2fMaKY7/l99YP+H7rJmxKf0SZEM/F5lK0msJFKI2bEa7WlCdlXc+e6waaVn6KnDklQekghKuVgHUPck45msJFLNVksNHayOkBV8JJ2fuLZmD1evhp4wEA/lS+hNUjENHpARc7e6xUWHJYmlWIflYmA5/sJNDVGTUp66urxrHu98E4O40W8xdvw7zy7gjvnJwzjow1D+GUNQzs/RhfzUEcT/8a47UrSLvynFs60NWJ/2gt2oll6GdVIpmno9g2IrynU1q8B8ExjvA2IwJONJnLkxsAwnsa1bEzuBQ9Yyll+nSqq6ux2+1xvXdCiKh4OlmWee+99yK2Wa3W8KTrcNjtdpqamhg/fjxlZWXolaKgODqtUZOy/lMncP7p9/iODC7+nDkbw/zL0JdXIueOQzKkIbwDBLo68NUewrt3F77D+7E3HCH97rWYbznnGAl0WvEdOYS2dDL6irksStex2X6AVl9P1KTsV3Ov4FfWrXzsPMbyYye4KWsOV1umM8M4nkyNEa+qcNrXQ427lb/21VI/uBx9nNbCA/lXYdGcuw9afT3scZ2k3DiBRemT0UqmQuTc2ShNW9BVPJToN7ogKE1bUHtPoJ//AyRTIROAOZZcXuto4vsTE8+v/GTSPNJkDTk6A2uLRtbdeK2jiYb+Xh6dPD+YHMVgQls6Ce/ejzHdfEdEWeHpR+3uDM4T3bwS8x1fQdJFLy6Us3Ox3PstkKSgKDvOojoiHRjevR+jtLVgXnl3ODmKbJqDYnsN3YTvp3Tsgb4P8DbeD4Cky0Ob97WkNortNVR3A7qLHkXSTyBDH8yzUFtby5VXRodFAVx77bXYbMm765MnT+b666+Pua+2tpbOzk6WLVsWTo6iLZvCwO4dmG+7K6KscDrw1deiGV9E+sq/w3DZ4nCE/FBoiydimLcA0/Iv0v/GK7i3vYPa0x1RZmD3DpTW06TfdQ9yzjgKgdnGIrbYD/NQwTURZf+hcDmLLVN5sv19PnQcZYv9EFvsh9BIMmmyDkWoeNVz0RZZWhM3Z83lOwXLmGSIHNtusR/mxEAHj4xfQaEuM+j+1pbdgq/6d2js9Z9L4Kpqr8d/ajNybgXaslvC2+8sKOOJ5mqOuOxJA1d/WFo5Yt4jLjsbO04xLyOXO4c4JdIWLqX/rVdRWpsjAlf1FRdjWfttVFs3puu/FFNEIchZ2aR/ZQ1ybh6GOfPRzTgXiKq0NjPwyUfoyqaQtnBpeLs29078Z59AdR9JGLga5kibipRWAoF+ZFN07NhwqO4jKLaNyOnz0ObeGd5eWVnJRx99REdHR8zA1eHxdSNFR0cH1dXVFBUVUVl57ndKW3IN/a/9BeX0qYjAVf2sOeT89FfIWTloSycnrV8zvgjLfd/BuOz6iHqU06cY2LU9mCJgyTnR3JI9n99Zt1HvaY8KXL3UXMprU+5nj6uRrX1H2O6op83Xi08oaCWZLF0ms4wTuDZjJsszypmSFh19U+9p5037QSpNxdwyGLyKGISv/jkxsPv7QghVjC5UMbD7+8K5Pkf46p+L2vt0a714oH7358AqxAP1uwX/Z714urU+ar9769vCsf4PQqijzKyqwrH+D8J6x3Lh3vp21G6/9WnhPfWASPk6B9xCVfpSIRbeUw8I1x6E3/p01N59+/aJzZs3C3WUz1dVVbF582bxyCOPiH379kXt73/3TdH3n7/6XK5z33/+SrTfuEj0v/tm1O7nunaJh1s2CjXJdVaFKuxKv2ga6BZnvHYxoPqTln+4ZaPIPvBt8VzXrvD2sH9VN+Me0KUR6BjZgDgZfNW/xV+/AV3lg4MZhSJxf/EMjBode3o7RpX3Z03VPNNaz/dKK7m/ODq2zrj8RtDrB5eFjx76N/8X7q1vY7rp9iDHMGgL7gfZSMC5J7UKZSOSJvkEsr/tZ/jbn0FX9L3BjEKRWLBgAVqtdsSOh2TYsWMHe/fuZcmSJSxYEB1LaLr+FiSDAV9DdAT1haB/059x/3Uz5ltXBzMKDcM94xaRJuuociVOqyYhkaUxUWrIpUifhWFYEOtw/Ma6jQ1du3mw4GruGbfo3I4ItXl7hepzjeS5kFC73sO/Fq4XisXArn8Qqrsjbkm73yucim+UWIX491OHRfqHL4j7ju4S7V53/LIup1A98fePjFgVrjdeFh133yz61v1OBOw98Yv67UJVnKPDK1ThO/Pvov/TdDHQeJ9Qfe1xS7rdbuH1ekeHVVXFhx9+KH784x+LN954QzgcjrhlA6N9nV99UVhvXyb6/uNJEeixxS3aq7iFKzAwOrRCFb9q3yqKDn1ffPf0q6LDF9lLSJggkoAXNPGDMuNBtdfjP7oh3BKlko5rKLxqAIOcmp9/KI647Dx95mi4JUolHddQCL8fSRd7riIRlNZmPFvfDrdEqaTjioDqhThLyBOauY+gdDwdbolSSccVcdyKglab+AkcCx0dHezduzfcEqWSjmsozvs6nz4VXDE72BKlko5rKLxCSdrixEK9p53nuneFW6JY6bjiC0lV8DdsgIAv9bx2rlaUpi34T20GNTDiBJEQXKH49JkGfGog5bx2pwdcvNbRxMaOUwSEOvIEkQCBAO6tb4OipJzXLtDViXfvxwx88hGo6nkkiASEgr/jaVB9qee1855Gsb2GYtsIIjDiBJEAqqqyd+/e8KK/VPLa2e12amtrqa6uRggx8gSRELzO725GKP6U89oFOq0M7N4RXE6uBkacIBKC99Vz3bvwq4GU89q1+nrYYj/Mm/aDqKgjSxA5FKGUxartCJKxIKVMq8i6UUtZXO20UWgwppRpVSfLo5ayWGk+FVyYl0KmVbSaUUtZrLqrFPi7jAAADFNJREFUkXSFKWVaRdKNWsri9vZ20tPTU8q0qtFoRi1lsdJ0Ejk7N6VMq2i0o5ay+IinjXxtRkqZVnWS5kJSFkdiLIn+WBL9sST6iZGSkMYwhjEkxshHXmMYQ2zEHLAkGz+pqroUWAyUAIVAAOiSJKkJ2C1J0u5RPs7PhVsaa5DG8HkilpCEEHOFEA8At0qSlDAvthCiDdgMPCPL8gVP+n1e3GNCGsOoI14rJIQoEUI8IEnSD8+nXiHEYwRv6q7zsP1cuceENIZRR5xWaJkQ4jVJkiKiU7u6uqitraW3txeXy4UkSZjNZnJycqisrCQnJ2d4Pe3AalmWP071eOJxd/R5+bRFwTYgc6ZPBWDaOJk8k+DyEh2mNN3weuJyjwlpDKOO4UJSVfVrkiS9MHRbTU0NH3zwQfjFZfEwYcIErrvuOmbMiJxKEULcLMvy28mOJRb3p80DPLvfT1VLdJrkEEw6iWum6vnGJTrKxkUGL8fiHhPSGEYdQ4WkquoXJEl6P/Td6XTy8ssv09zcPKI6p06dyl133RWRYEUIsTRRyzScu6df4dFtHnadii+g4dBp4OuXGLn/cgNazbml/8O5x4Q0hlFHSEiqqpYBtZIkmQHa2tp48cUXcThSSz4zHLm5uaxZsybc3RNCnJYkaY4kSVHZRAe5qyVJsgA0dfv4zn97aOkNDC+aEhaV6vj1F00Y9ZqY3HJC6zGM4cLwQEhEDofjgkQEwcSTzz33XDhbkSRJE4EHEnBbAGz9Ab61xX3eIgLY0+znh+95CKgiJveYkMbwuUAIcbEkST8Iff/LX/5yQSIKwWazsWnTpqGbfq6qakSug+HcP3zPTdugM+FC8FGjj2erBmJyX/CE7B0b2/5ZkliSvOQ5CMGuN1YX/wxg2pRL/lkgjcheQuw6duKzn4W/D+mT37HxTOwMGTHw+qriqNRJ06ZekrL9seOfpZ566X8ZBl3NABw+fDjmOqgJEyZw9mz8/IB6vR6TyUTvYHhWCA0NDRw/fpxp08KZoB4AHo7Fve24L6ZTYXqehmNd8Vsok04iM02i3RkpwOeqBrh1toH89HAb9ADwsLayYrEEPFJTu/sXcWsFKisW/yPwZE3t7ohBlSSxUpZ4WBWklKNLlshV4ddAUAgSK0E8rKoiNXtZymWo/TBIEsnXZCeEdIH2YwCQJCmce/nDDz+M2j937lxWr15NY2MjL7zwAn5/5M1eWFjImjVrMJlM/PSnP8Xn80Xs37ZtW1hIg1xhIQ3lfmqPJ4r7hhl6fn5DOlUtfh56y4VXifQTTB2n4Y+3W8hMk7nyaTtu/7n9XkWwbp+Hf1lmjuDWAo8Aj1dWLJZranf/PNZFqaxY/E/Avw1+jRKcKvx1r68qi1rieturZ8fJcqA2sqwyFyL98xqNqDtx4kCUfVlZ5TitVl87rOxcNTDabxUcw2hCVdVwEojm5ma6uqLnMBsbG3E4HEyePJmvf/3rEWLKz89n7dq1mM1mTpw4ESUigNbWVqxWayhxf6aqqgtkWa4ayl3drtBsj251DrUpdPerLCjR8dQt6RFimpSrYf3KDLKNEntP+yNEFML7x3w8cpUJg1Y6x01QGP8C/KyyYvFPhxsNbvvZYJmErdZwvLl6Qvfrd140XlY0V0tC6peEFJ0qJgGammq6T5zYP14I+WoJqV8iub0QzE30l8w+oKpzE/2N5Pj/F2Nx6EO8bK1Op5N169ZFiEmn05Gfn883v/lNzGYzp06d4qWXXopLMqzuxcP+s+90bDd3u1Nl7evOCDEZtBKTcjVsuDMoov1nFP5+iyumvcsrONoRIdDF2pra3QA/r6xYnAY8WlmxmJra3Y9BWESPAv8ar7VKhttfPTtTyIF3VJVvSDKvMEJv+6RJl86UUd8RSN8A8Uqy8q+vKq5OtH/lpsTvhm1sPJDQftrUEWRK/d+LcM60WK1RCDabjXXr1vHNb36TyZMns2bNGvLy8sIiev7556O6fEMxrO7pw/7TaIs/BjplC7D2dSfr77CwoETHutstlOZowiJ68E1nVJdvKBq6FOYVhV0M08Ofamp3P1ZZsRiCYgqllAyJ6LG4NSbAqk2nywME3lFV1ryx+qIdd2xqHZH99EmXlKuyeEdI0poTJz7bMXXqJTHLpeJgiOVYCCEVB8PxE/vHHAspQpKk8II0lyv2Uz0Em83G+vXrWbt2LaWlpQApiShG3YXDuW39iT11Q8UUEkUqIgKwuyP2F0Z47QbF1M+5Ltw/1tTuTvwynjhY+XrLrIAqvS0GRQQgVLE41dXB06dfPEsN8DaDIgLQKPJiNNFPmTEHw//fkCQpwvMqy6MzKxM4j1gDzXkOvy/4iAf0hiWv31ka5SgQqrx0qIgA3lhd0hhVDu2ShoZoR4OqykuHigigoakqyn4Mf3sQQoTfYJ2enjjnRl5eHmvXrsVisdDc3IzT6aS0tJQ1a9agS5IgZVjd1uHc48yJb+/SbA3r77AwziyHHRDzirSsu90SciTERbYpYr81okUaMib6x8FNv6isWGxO1LUzeL33r3ytrYghYw9VVatev7P46YRHMghJBO6fOuWSoshtVB0/8VlK9mP4m8Sx0Ie8vPhLfvLy8rjvvvuwWCzh7lxWVhb33XdfWEwbNmyI6bWLUfexYf+ZlBM/E1Vptobn7gyKKNSdK8qUw928dbdbePBNZ0yvXch+KHdYssMcC08Odun+leCYKcqbF4Ik8RWEqEewD8E+wCNL0nVxzyCqAvEVCaleIO8TyPskJI+QR2A/hr9FhFeWTp06NWaBkGCGisjv99PV1cWf/vSncMv0ta99LW5Xb1jdu4f9Z+HE2C3aeIscJSKvIsJjplDL9PsvpSPHaJgMWon5RRFt0G65smJxaJ4oyrEw+Dkkpn8adEZEQaBseW1V8cbXVhVvVFV1Z8xCCSBr1S0nT1ZtPHmyaqOKGLH9GP62IEnS9tDnkpKSqDVFAEVFRVgsFhobG6McCyExORwOSktLY+beKyoqGvqS6T5ZlquGc8+ZoKUoM1qEMwu0jDPLVLX4oxwLITF1ulTmFelIi9HFu2GGfmjXr0+W5SotwW7cvwH/HMvFPeiAGBgsozLCuaS/NaQyl5QIY3NJySFJUkBV1b2SJC2UZZlly5YNj4+jrq6O3/zmN3HXI3V1dfHLX/4So9EYs2s3NPG/EOKFmNwSPLTIxD/9NdJzuP2kj1tf7ONUHPf4KVuAL27oIzNNiura6TRw74Jzr3cJcWuBJwEShQjV1O7+eWXFYjVUdjgCssHwdxuaDAADGlnHYIRsaNtQuGSDQaMOd0tqDUVFiw0AsvDoxKAHJ7QtEn5DMEfF+SHZPFMyJJtnGkMYDwOfQDAcaM+ePbS1tUUUSLaoz+/3x3SBT5o0afhCv2fica+Yruflg1qOWJWIAvFEFIJXEXS6osdHX5mXRklWxPjoGQDtYOxc0lYmgdDOatXAzgHzYPOrCoTE6wADZm3Uq/G0agAkaWt4g5DOBpTATpMxeGIimIzmdQCTcSDKPqAAEluHbx/D3xZkWd47tFVavXo1zzzzTNIXQydDRkYGq1evDn8XQjwuy/KxoWUiuCX47c3p3PWyg+4k80rJsKBEx0OLIhYWhrn/RyzsW7mpbU6yMolaosmTL05qH6sl+p9w7T4PDFnYNweokiRJD8GFfS+88EI4+eRIkZWVxb333kt+fjCdtBDCBsyRZblteNlB7k8lSTIAHO1Q+PZbrvMW07wiLb+92UK2UYrJ/T9CSGP428Kwpea3S5L0euh7b28vL730UsLlE7FQWlrKV77ylYhk/UKIFbIsx+2dDOfu7lf5/jsuDrUp8Uxi4o5KAz+82oxuSI9uOPeYkMYw6kgl+clnn33Gjh076OnpSVhXXl4ey5cvj3gTIIAQYpUsy5vimCXk3n7Sx/pPBzjakVhQV03W8+AVRqbnRc5HxeIeE9IYRh2x0nGpqnoDsEWSIt+r0tbWxtGjR3E6nXR2diLLMuPGjSMzM5NZs2YNdXEDIITolSRppSRJH6R6PPG4z/YpfHI6QH3nOUEZdRKzCzRcVqIl2xQloLjcY0Iaw6gjXoJIVVXLCeZSOK+3fgshnpAk6RlJkkb82sHPm3tMSGMYdaSQ73shwSXad0iSlOxNcH1CiNcHb+IDF3psnxf3mJDGMOoYyYvHBle0Lia4DKJckqSAEOIY0AbslmV5e8IKLgCjyf1/AfzC3EiJBPQWAAAAAElFTkSuQmCC) no-repeat}.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a,.layui-layer-btn a{display:inline-block;*display: inline;*zoom: 1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position: absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layui-layer{border-radius:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}to{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}to{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}to{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}to{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translate(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translate(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translate(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translate(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translate(-100%) rotate(-120deg)}to{opacity:1;-webkit-transform:translateX(0px) rotate(0deg);transform:translate(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translate(-100%) rotate(-120deg)}to{opacity:1;-webkit-transform:translateX(0px) rotate(0deg);-ms-transform:translateX(0px) rotate(0deg);transform:translate(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.layer-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,to{-webkit-transform:translateX(0);transform:translate(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translate(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translate(10px)}}@keyframes shake{0%,to{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translate(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translate(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translate(10px)}}.layer-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}to{opacity:1}}@-webkit-keyframes bounceOut{to{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{to{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background-color:#f8f8f8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right: 0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2e2d3c;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2d93ca}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:0 -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right: -18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 10px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:0 6px;padding:0 15px;border:1px #dedede solid;background-color:#f1f1f1;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#4898d5;background-color:#2e8ded;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:#0009;color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-page .layui-layer-btn,.layui-layer-iframe .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:none}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:none;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(data:image/gif;base64,R0lGODlhPAAYALMPAPPJp/HYxfSwkf50Df5TD/+HAPe5bvqHR/twOviZavyrMu/m3f9EAP9mAP+ZAO7u7iH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6QUNDRERBRjMxRTIwNjgxMTg4QzZERjVGQ0I0MDI1NDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTlDMEI5MzU5RTY2MTFFMTkxRDY5NkVCMDBGOTBFRUYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTlDMEI5MzQ5RTY2MTFFMTkxRDY5NkVCMDBGOTBFRUYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpENzU2N0YwMTMyMjA2ODExODhDNkRGNUZDQjQwMjU0OSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQ0NEREFGMzFFMjA2ODExODhDNkRGNUZDQjQwMjU0OSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAkFAA8ALAAAAAA8ABgAAATy8MlJq7046827/2AWCEdpCkGodkHSvHCcLGttLUOsw0NqW4EDYog4+CyInbJxwAQMiqjCcMQkGNgsNmFxLZXcisFBLpMNGYF2zRBQAt9vFWCuOwCXAJt9FMSXbhILdnY0FQd7a00SB39Kiw9jhGZoFQSJWgQTjksTCpNmChaYa5ucOxOgdaOkWaanMamqZRaXrZoSObAvA56zDqKHrQyQjbtME5KqlW/DRwbHDcwBv1UTV5hhEnDHVcqEzF2J2te75BLflBsCCFoIgRU4pwOGFQCfZQp4PxUBSX8IrPELscDLkhkDB7bQxQthwocQI0rMEAEAIfkECQUADwAsAAAAADwAGAAABOjwyUmrvTjrzbv/ILcAiWCUQKh+y9G88DsIa40JcQ4ji30JCIZQeAhcAgOdMvEYKZ5PQA8UHFoZtEpSuVQ4vmCHwuhJXK+EqcTA5YbfYrUmcD4jKNt2rAB/GzoHdWdTAXo6fXByGASCV1k4hjADiG8pG41XTA9skS98lGB/l5hDmgmdnqChHIykWBKQnZOqDpYaVaRkAKgvtA6KFwCudxO8DZ+UCh64grYPp7yqwBgLzFYEWRNIvAlecGMhAq1DCGQVu7JNBn7THQDvAObBeVwJ7T4gLW2i+P0PAQkOCBQoxZ/BgwgTYogAACH5BAkFAA8ALAAAAAA8ABgAAATV8MlJq7046827/2AojmMgHE2aIslCckAcb8Kg3umQvFhCMEAgYXcR4HCFpEJhCPAkiKAUiLAEbMdGwcHtcg28w3R8qKCyA6/aASABxvC2JJBNrdcuURQ+rUqMWVt3XmAhC3xweWdHg2oKIm+IU3KLOI1qkJKTEpU3l14iAZpSTg+dKp9cjyKjQRMGdYKXhSFio2VzdQ2ppSEBP5IEvQ9YR7J3tCICmgIVALrHhE+/cMIXCboDxwpy0z5BQ8POxUcD3U88CwnkKQni6E8yMXnw9fb3+BURACH5BAkFAA8ALAAAAAA8ABgAAATe8MlJq7046827/2AojqQUCEeaJkC5CUgcJ0G2JE2u60jrVgICY0gcIhaXQ25QaDp1vt8jUKwaLbhGwcHtcgeNQe23EFqrAkogt/W6cwlp4mwlICW4gXvvyI1LZnRFaRJgfHtghCRUglUIJjmHbgUNBy4AjY4SAGGSXpSWJZiZRI8PnHqeXKAujKQMpmsNqquVP4GZioa0iT8Hrwx/Sqmefj8BuHRxE5xanmChPwKZRxVKznxgDX8/AAh00RQL18S1YYpSDzffpVEXAjvxB9zpiwnaOQfo9fz9/v8ApUQAACH5BAkFAA8ALAAAAAA8ABgAAATO8MlJq7046827/2AojmS1AIkhJEBQasFBMDRDHC62JE3v97iXJVEr1hKThUHBdBQGv99gIZwIjFiG4AFweL/eQtSHqD4Cs2yR0AW7xeOGoXpQYwvuPHQ8FabtNA15g3ENWyUBgEUDg3mFBy8AijV4jWB7UZAlkpMMlZZemD+aJImdjKBej0KdDIKpDoWHJXWdn42iPzmIrQagcGOkL0SKSG15wFF9ZgJ/WLMBvpeFCLtmMVhBOjxxB1RmJgDiAN8bAQkH6Qcs4O3u7/Dx4BEAIfkECQUADwAsAAAAADwAGAAABMrwyUmrvTjrzbv/YCiOV5AgKCosZIsFCCPPc5KZQ6M3QxK4lgWBRuMVFEgDS5LYOXc24CRGlBUc2CwW8BA8vw2BtFuVDbRoByAHdg6WrmG1kU63v9EWoMy41rVsdzoDQAllBH9oBYJOPy2GVYiJWYuMOlyPh5OUlpcuApqbDpWWjiQBfH6TgYxSVER0m50NeS2gZap1BrOmLpBVuVpcTYy1QLdzuQamAqxuYmMTMGUJcBYmeL3REgAJ3gkC2hgLAOUA1tvp6uvs7R8RACH5BAkFAA8ALAAAAAA8ABgAAATs8MlJq7046827/2AojpmQICgqBGSLCQgjz3OyZEFy7LvgXoDZoEAsDBoyxGWRaDifzgHgNwkQGA2HdqstyBKWA3TsZFEPDAJ37RjIzBIBmTy4uQKyAnuNPEwWR3NjPi4CWHtrXgQTcoJjAz8xA4hrbxJijmNwIzF6lFtXUw+YmU+iJJ2foAyipKUNp5wMk6oOV2aupZsihlmqioyvUT94DJ6USEoSAcINYJGHlF6sFE2lA7sjxQ3HXNN+FAu5ZIRUAlcEtF1IDAh2FQaC2FQUATE0NOVACYENAyv0LMCgQeBAtoAIEypcyLDhhAgAIfkECQUADwAsAAAAADwAGAAABODwyUmrvTjrzbv/YCiOWiAgTJoiSUC+V0KodEokZWIIugtPAYNCUSgMZjWV4BI4NJ7QxsH3Mjiu2OsgqaJKFoOoeLB4AbJoR4PLQFQQ4rh7tEin2QyqIM5fiqx2WVtcfg9hfGMjCoFZBWxzC4h8Xh6MaI8SAJJxACKWWZgPmptRnSGLn45ccwGkUWUhgJaDSTgSh6QDIwGfDkhJeq4Ntn+zbAQUYLmwI7Joa2ymQLhxA5QiAKhXBdBcBxcLcHEHzD+Zv0kEhRcACQfvCdLmFAEytdfzZgD7AOX5/wADCrwQAQAh+QQJBQAPACwAAAAAPAAYAAAE6fDJSau9OOvNu/8gFiQkKSxh+gkE474uEqj0tSBw/gq1twCKYBCAOuiOhJllkRg0no0BDxRQOK5YhyJx7BIsAKj4ifgsrNlsodtNUBbOsdjgMaTvA7buOznI5UoaC3eELXowSgF/cm4bAIR3DYcwUwKLYwMcdpBZkpMujQaXY5qcnZ+gEgmjYhyPpleen42WrFEcg7AOqC4AEoq2Bx1onGu8FAi2vriwvAzCE2Gj0B1VhApGqEkVAnFyByhmm1gGKDifUxULfmN0PerZbATLGD8A9+8ZLF0y+f4PAhAIFJgg0L+DCBMqrBABACH5BAkFAA8ALAAAAAA8ABgAAATz8MlJq7046827/2AWCEhpCkGodkHCvHCcpGtdBUSswwRt3waFUGHwVRC7JAOBaR2eh9nG4KhaqwaLS5lMaBvgMNiLAVzPDgAlwOUaBeJ4Q3BZoNGLiaCtpEsWcnJGElR3V1kSSHw7TBIJgXFkFAqGVwoTi0oTA5BiAxaVZ5iZOxOdcaChVqOkMaanYRaUqpcSOa0vBJuwDZ8VhaGID4q4jQ+PsJITAaoOPlu4kgG8g4TBa7gvRsiQyr+GwhMHuAcWBpDhFwCzVQpqFgu3iwR5FgAHYgfvP2vEXAj7+K1YMI7LgXoCf7SQx4CAlIQQI0qcmCECACH5BAkFAA8ALAAAAAA8ABgAAATe8MlJq7046827/2AXACQZnuCSEEzrEkmAzpjAunhLCDRmKI5g0LC4BHJIl+wRSByeh1hKSBUWKYtbMkdQNb7gb+IDrFIVlcR2iwi7GzwOwGwGTI7r5Pu91NDpEwJ5SAR7bmMaC39mVweDOYZuAxsBi1VLCI84kW6UllSYmi6cYZ6fQaGiDKRfkxunQRNqqoWkiBoGpwaBqi2sfRmKn1cPWb21hreUZWYKwA+zogkGhrsguVVEF5maCBIAB2EHdicLAecBxBYL3HkI6j0zgmvK8T1NWgwwz/b9/v8AMUQAACH5BAkFAA8ALAAAAAA8ABgAAATY8MlJq7046827/+AXACQQhCi4CATjvoSwpFww2nMWtG/vEieaZQFwGI8ORbCy4Pl6hMZhmlimFMiswypBPKGNsDicoBm02pxE8IWN3w0BaoHWGijOLxzO5RTrWRMBbT97b2UgWIBIQQmEDIZvAyGKi0aNj5FvlJZHmISaYyFnnVtrmaENkyCknRMAj1GhiB90nXcTeU+pfRytgGoPjoSyhrQqlWgATLo+xW+4NH9ZCsGCzT5vB8tCEkQG4Aa9EwtehAjW3TQLCdgJ6erdAgn0CXLx+Pn6+xwRACH5BAkFAA8ALAAAAAA8ABgAAATM8MlJq7046827/2AojuSzCAeiqsJSaoChzIoBaALB7DyfvJaAwkEsEhWBS6LHIDQaTt0PKFkMjViFiwLoOZ/gJ0FAfVyx2Ypu9w2HB9QAeu5ISgQ9t75BfhnoaAYTCDxte08HQIBzE2sMh25wL4toE3mQYYqURpY8mJmTm0WdO59gQH+ighKEbKaJLwCiDjd3l5h9L2eAChQBjoZ7di9ylMMSS4WHBFNUVnRaSkx6DM1lDwC7CgBbFwCOTAy51yQLCeDM3eTr7O3u7yMRACH5BAkFAA8ALAAAAAA8ABgAAATO8MlJq7046827/2AojiQVCEmaCEGpLYbizI5iLFlwMHzPHy1XJSCjGRVBU6JBYDZ8PEJSuCgaj7jJssHtMqGIrKt6LdeyAq/660sIHwaz2fBYrNdQxnRkldMUD1t3XQRQAi4BfmYBA4NqYIiKZQGOjz4IkZJGlJVdkCULmkYLjZ1Pl0KiNIGmhT6HLnGidJyVeXsioaJZgneuPZhviZJicIO/UcVjfVgWAAeWl7hjskY3GQsIeTxhbxcLAeEByhjPCOcIBwDe7O3u7/DxEhEAIfkECQUADwAsAAAAADwAGAAABMHwyUmrvTjrzbv/YCiOWGACgbcALEpygeHMtGNoAsLsPCG8GEBt6FAAEofkIRE48J68A7ASIBILjay2QYB6f9PHQmElDrbarveZmsrKRLR2/USE4dazXE1ntElVeENYcg19O2CAgoOFhocJQIGLM4RyhwyQL5KTlWiXiSSTNHpofHR/JG+TjZcEYQuinWmfYQ9CgrJZpmtStQ+bRAYAB1sHAAKHoLUxVjcZOV4+vhYxBtYACx0CCdwJytPg4eLj5B8RACH5BAkFAA8ALAAAAAA8ABgAAATI8MlJq7046827/2AojtgCGKgBLGSbLYojz7LClgnC7AyS3C7KgkacJQKWAIHHZBCQQQmsSG0khMsmkwB0GajUQkMg1WmbiO5oCKYOGsjE+Xz1tt3WR3a+DX7vRGIDAXxnUCQxgDRiDQCFWgAuiYoyjI6PTJEtk5SChJg8hyMAlDNvV3uPBEFspQ1wD3KgdZKlYnULqXNcUa2ADQYUn3xPURIBnFQHmkJmWmnGE1NUNi+yTD/RFQEGCt4KBqIvAOQr2ufo6errIhEAIfkECQUADwAsAAAAADwAGAAABLTwyUmrvTjrzbv/YCiO2QIYSqoYxkK+GODMdG1owoHsyCHAF0NtSLtZAgiGcqlEBIATGXEKqCwIzCyD4IIqplNFJanNIqBSMLEqAZTL7NdXTRRLDm/tAUgHT7B5TAR8fUQTgVqEhTWHiEyKizN/jkqDMHORdg9kjmcwaYtxApQMPzALkTNdEpx5nkBCi0YTAYgEcbCFsxQLrUwIq1APAXS4Fjm/AsHCDwsoQy3M0tPU1dbXGBEAIfkECQUADwAsAAAAADwAGAAABNbwyUmrvTjrzbv/4BWMZGiCC+CsrKMYyyljQWuvSqAFQA/os0kgcSjejoqYRYBgOJ8EQDDRqDYKR6Ql8ew+EzKBtTHIZqUTgHfNQKPGDXNWQWmyuwgTdSzPAgN3a0AfA2NYfTcGEgKBXgIhcIeILYoPXI1fkIaTNpWXmAxgIIVWZZwsaIygDI8ge1Wmpw5/qwyDHgFwsisUBKAEJ69XspUSapitJgabk0kVB40HQQAHsJy3ab5eUUEWCwpyzhkCCeUJbt0VCwZZMOnvxgbyBujw9vf4+RcRACH5BAUFAA8ALAAAAAA8ABgAAATi8MlJq7046827/2AWjGRogovirKyjAMspY0Zrr0owe0Fy/IeE7gG4GRWxS4LAaDIIiaQn0ahaq0KjVmEJMJ1gwpAjuJob2jSgggC7GYjO4nwdpLcUwfst4FDpVQV3WlJfe04EHAOAVYNaaw8Bh29jGIyNjjcGEgCTbpAZl2iZNptEnmCgGIuMpKUSkqhNlUqXgq4roIaTiRsBtrgrUgmyCR1/dHa4XBMLu28EUhsGgAYqrrReh2IgAAdXB2sLrqYWS24H0juR13flFwsA8jDr8DVq9fkB9ywKBur5AgocSPBCBAA7) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs=) no-repeat}.layui-layer-loading .layui-layer-loading2,.layui-layer-ico16{width:32px;height:32px;background:url(data:image/gif;base64,R0lGODlhIAAgALMAAP///7Ozs/v7+9bW1uHh4fLy8rq6uoGBgTQ0NAEBARsbG8TExJeXl/39/VRUVAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBQAAACwAAAAAIAAgAAAE5xDISSlLrOrNp0pKNRCdFhxVolJLEJQUoSgOpSYT4RowNSsvyW1icA16k8MMMRkCBjskBTFDAZyuAEkqCfxIQ2hgQRFvAQEEIjNxVDW6XNE4YagRjuBCwe60smQUDnd4Rz1ZAQZnFAGDd0hihh12CEE9kjAEVlycXIg7BAsMB6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YEvpJivxNaGmLHT0VnOgGYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ/V/nmOM82XiHQjYKhKP1oZmADdEAAAh+QQFBQAAACwAAAAAGAAXAAAEchDISasKNeuJFKoHs4mUYlJIkmjIV54Soypsa0wmLSnqoTEtBw52mG0AjhYpBxioEqRNy8V0qFzNw+GGwlJki4lBqx1IBgjMkRIghwjrzcDti2/Gh7D9qN774wQGAYOEfwCChIV/gYmDho+QkZKTR3p7EQAh+QQFBQAAACwBAAAAHQAOAAAEchDISWdANesNHHJZwE2DUSEo5SjKKB2HOKGYFLD1CB/DnEoIlkti2PlyuKGEATMBaAACSyGbEDYD4zN1YIEmh0SCQQgYehNmTNNaKsQJXmBuuEYPi9ECAU/UFnNzeUp9VBQEBoFOLmFxWHNoQw6RWEocEQAh+QQFBQAAACwHAAAAGQARAAAEaRDICdZZNOvNDsvfBhBDdpwZgohBgE3nQaki0AYEjEqOGmqDlkEnAzBUjhrA0CoBYhLVSkm4SaAAWkahCFAWTU0A4RxzFWJnzXFWJJWb9pTihRu5dvghl+/7NQmBggo/fYKHCX8AiAmEEQAh+QQFBQAAACwOAAAAEgAYAAAEZXCwAaq9ODAMDOUAI17McYDhWA3mCYpb1RooXBktmsbt944BU6zCQCBQiwPB4jAihiCK86irTB20qvWp7Xq/FYV4TNWNz4oqWoEIgL0HX/eQSLi69boCikTkE2VVDAp5d1p0CW4RACH5BAUFAAAALA4AAAASAB4AAASAkBgCqr3YBIMXvkEIMsxXhcFFpiZqBaTXisBClibgAnd+ijYGq2I4HAamwXBgNHJ8BEbzgPNNjz7LwpnFDLvgLGJMdnw/5DRCrHaE3xbKm6FQwOt1xDnpwCvcJgcJMgEIeCYOCQlrF4YmBIoJVV2CCXZvCooHbwGRcAiKcmFUJhEAIfkEBQUAAAAsDwABABEAHwAABHsQyAkGoRivELInnOFlBjeM1BCiFBdcbMUtKQdTN0CUJru5NJQrYMh5VIFTTKJcOj2HqJQRhEqvqGuU+uw6AwgEwxkOO55lxIihoDjKY8pBoThPxmpAYi+hKzoeewkTdHkZghMIdCOIhIuHfBMOjxiNLR4KCW1ODAlxSxEAIfkEBQUAAAAsCAAOABgAEgAABGwQyEkrCDgbYvvMoOF5ILaNaIoGKroch9hacD3MFMHUBzMHiBtgwJMBFolDB4GoGGBCACKRcAAUWAmzOWJQExysQsJgWj0KqvKalTiYPhp1LBFTtp10Is6mT5gdVFx1bRN8FTsVCAqDOB9+KhEAIfkEBQUAAAAsAgASAB0ADgAABHgQyEmrBePS4bQdQZBdR5IcHmWEgUFQgWKaKbWwwSIhc4LonsXhBSCsQoOSScGQDJiWwOHQnAxWBIYJNXEoFCiEWDI9jCzESey7GwMM5doEwW4jJoypQQ743u1WcTV0CgFzbhJ5XClfHYd/EwZnHoYVDgiOfHKQNREAIfkEBQUAAAAsAAAPABkAEQAABGeQqUQruDjrW3vaYCZ5X2ie6EkcKaooTAsi7ytnTq046BBsNcTvItz4AotMwKZBIC6H6CVAJaCcT0CUBTgaTg5nTCu9GKiDEMPJg5YBBOpwlnVzLwtqyKnZagZWahoMB2M3GgsHSRsRACH5BAUFAAAALAEACAARABgAAARcMKR0gL34npkUyyCAcAmyhBijkGi2UW02VHFt33iu7yiDIDaD4/erEYGDlu/nuBAOJ9Dvc2EcDgFAYIuaXS3bbOh6MIC5IAP5Eh5fk2exC4tpgwZyiyFgvhEMBBEAIfkEBQUAAAAsAAACAA4AHQAABHMQyAnYoViSlFDGXBJ808Ep5KRwV8qEg+pRCOeoioKMwJK0Ekcu54h9AoghKgXIMZgAApQZcCCu2Ax2O6NUud2pmJcyHA4L0uDM/ljYDCnGfGakJQE5YH0wUBYBAUYfBIFkHwaBgxkDgX5lgXpHAXcpBIsRADs=) no-repeat}.layui-layer-tips{background:none;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px #0003;background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow: hidden}.layui-layer-tips i.layui-layer-TipsT,.layui-layer-tips i.layui-layer-TipsB{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsR,.layui-layer-tips i.layui-layer-TipsL{top:1px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476a7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#bbb5b5;border:none}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#c9c5c5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92b8b1}.layui-layer-iconext{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAABkCAYAAAAv8xodAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNS8xMS8xNNdPc0oAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAWb0lEQVR4nO2de2wU172Av5l92t4Hti/YQHmVmFcSE7+WR0h0uRRVVC2pKldpCFJQEJAiFwg4diikgLEghEhpVLVQNblBSFEUVYoi+hc0FQoE87B52JQAiQnBJDY2wfix633O7P1jdwbbxe9d7yx3PgnJc87iM2M+js+cx+8nhMNhdHQeFcRE34COTizRhdZ5pNCF1nmk0IXWeaQwPqxQEIRYtpEKOABL9NoLtAKhWDbSDRF4EsgAbEBHtL3LAIN9CV6+fHlSvS0fOXLkof9oj8pzDJZ499A2YCxgWbJkiSUnJ8cApADZgCEO7ZmAImAyYFuxYsV4Iv+ZpgIFcWhPR2PEU2gbkAlQU1Mz67PPPvvvq1ev/s/f/va3LCIyp8e4PROQD4wrLi7Oam9vP/Dhhx/+9f79+3/Kz8+3AxPi0KaOxoiX0KrM169ff6KgoGAKgMFgEF944YWZ0c9Y+vrLw8BEpAceV1xcnHX48OE3HQ7HRIAxY8ZM2bdv39Lo5zJi2KaOBomH0D1knjFjxsTulX6/Pxj9Uo5Re4rMY1esWDH+8OHDb6akpGR2/4Db7fZEv4zXuF1HI8Ra6H5lDgQC0ttvv/1l9NIfg/Z6yPzee+/t6S1zY2PjpdWrV/8retkagzZ1NEwshR5Q5j179pzfu3dvOxBg5HINKHNTU1Ody+WqbG1tDQENQOcI29TROA+dthsGGYAd+pd5165d94nIfHeE7Q1K5qKioorvv/8+ANwGakfYZlwIh8MEg0F1OtFoNGIwxGMCKL4Eg0G6urqQJAlRFElNTcVsNo/6fcSih84A7BaLRRiCzCMZy5qAQoYm86URtBc3FJnNZjMzZswgKyuLQCCAJEmJvrUhEQwGcbvdZGdn88ILL5CTk0NnZ2dCnmOkPbQqc11d3eMPk3n37t01lZWVbcRW5v966aWXJh44cKAyWWWWZZlgMIjD4aCoqIj09HRkWeb48eO0tbUlTS+tyDxz5kz2799Peno6LS0trFq1Cr/fT2pq6qjez0iE1mUeJorM6enpFBUVkZaWRigUIhgMEgwGB/4GGiEQCOB2u5kzZw779u0jPT0yze/xeJAkKdYrzoNiuELrMg8TSZIIBoNkZWVRWFiI2WwmHA7j8/k4d+4cnZ2dCRl7DhWlZ37iiSd46623cDgcANy5c4c33ngDj8eD0+kc9fsajtC6zMMkGAwiSRKTJk0iLy9PHVZ4PB7Onj1LR0cHZrMZUdT2njGlZ87Ly2P//v1YrVYAmpqaKC0tpaGhAbvdnpBh01CFHm2ZzURmMx4ZmadNm0Zubi6iKCIIAm1tbZw5cwav14vJZNK8zH6/H7fbTWFhIXv27Okh85YtW7h9+zY2mw2TyZSQ+xuK0KrMly9ffiInJ2dC98pEyNzY2HjJ5XJVJoPMoVCI2bNnM2vWLCCyo/Hu3bucPXuWUCiE2WxOyJhzKCgyL1y4kMrKSiyWyO6Fb7/9li1bttDS0pJQmWHwQidU5oMHD+6xWq09NhYlg8zKtJwsy+Tm5vLYY48RDocRRZGmpiaqq6uRZRmTyaR5mX0+Hx6Ph2eeeYbdu3er0jY0NFBaWqoJmWFwQg8kc2j37t3ndZl70l3mvLw8pk6dSjgcxmAw8O2333LhwgVEUUyKF0C/36/KXFFRoUpbX19PaWkp9+7dw263YzTGap1u+Ax0B7rMwyAcDhMIBDAajbhcLsaPH48sy4iiyNdff01tbS1GozHhvdlgUHrmpUuXsmPHDvU3ybVr1ygtLaW9vV0zMkP/QusyDwNljtlsNlNUVMS4ceNUma9evcrVq1eTSma3282yZcvYvn27KvP169cpKyujo6NDUzJD30Kn8UDmJ3NycsZ3r4yDzABzeURkTklJweVykZGRQTgcRhAEamtr+eabbzCZTJoSoC+UnvnnP/85W7duVWWuq6ujtLQUn8+XsKm5/ujrJ2sDuHDhwpxRktkJZC9evDg92WV2OBy4XC5sNhuyLCPLMpcuXaKhoQGj0ZgUMnu9XtxuN7/61a947bXX1PLa2lrKy8s1KzP0LbR18uTJ4uzZs3vMM0uSJMdBZohuOy0rK1vUW+bm5uZ/a11mgFAohM1mY8GCBaSmpiJJEj6fj9raWpqbmzGZTJoUoDfBYBCPx/MfMl+8eJGtW7fS1dWlWZmhn912zc3N4WCvjQUGg0H85S9/mR29NEf/xAIvQGNj43/skU5PT//xypUrp0YvHUQO2WoOWZZxOp04HA4kScJgMNDS0sKdO3cQRVGzAvQmGAwyZswY1q5dq5bduHGD9evX4/F4cDgcmn6WvoTu8vv94YMHD16WJKnHUamCgoIpNTU1s6KXY4mEKRgprQAlJSWn6+vrP+9eYTabUysqKirKy8tnEBmaFKFBqUVR5M6dO9y+fRuj0YgkSUyePJk5c+aoG4+SAYPBgMfj4cqVK2rZhAkT+NnPfoYsywQCgQTe3cD0JXQrENq4ceMPf/7zny+OgtR+4JbX65Vzc3PfSUapTSYTsixz7tw5Ghoa1F5s5syZ5Ofnq2NsraM8x86dO6mpqQEgJSWF7du3s3z5ctxuN16vN8F32Td9CS0BzYyu1HUksdSCIKiLJDU1Ndy4cQODwUA4HGbatGkUFhYC9DidokVEUcThcODz+Xj99deprq4GIs9XXl5OcXExbrebrq6uBN/pw+lvJ0wIXeoho2wwunTpEteuXUMURWRZZtKkSbhcLkRR1LzUBoMBu91OIBDg97//PefOnVPrNm/ezPPPP4/H49Gk1ANt7dKlHgbKXPOVK1e4fPkygiAgyzLZ2dksXLgQq9WaFFI7HA78fj9btmzhxIkTat2mTZt48cUXNSn1YPYq6lIPA5PJhMlk4uuvv+bChQuqvJmZmcyfP5/U1FQCgQCyHKvwJLFHGX4IgsCOHTt6SF1SUsKqVavweDx4PJ5+vsvoMtjNt7rUw0BZ4r516xbV1dWEQiHC4TBOp5OFCxfidDrVDUxaRZFalmXeeOMNjh8/rtatW7eOl19+ma6uLs1IPZTd5LrUw8BgMGAymWhsbOTs2bP4/ZH4OmlpaSxcuJCxY8eqm/+1iiiK2O12ALZv386xY8fUujVr1rBu3To8Hg9utztRt6gy1OMRutTDwGAwYDabaWlpoaqqSh13WiwWXC4X2dnZSSO10Whk9+7dHD16VK1btWoVJSUlmuiph3PeR5d6GCh7n9vb2zl16hSdnZ0IgoDRaKSoqIgpU6aoJ1u0ijL8EEWRHTt2cOTIEbVu5cqVbNiwIeE99XAPsOlSDwNRFDGZTHg8Hqqqqrh37x6CIGAwGMjPz2f69OlIkqRpqQVBwG63Y7Va2bt3L59++qlat2LFCkpLS+nq6kqY1CM5kalLPQyUntrn81FVVUVzc7N6MDY3N1cNOKNllOGHxWJh3759fPLJJ2pdcXExr776Kj6fLyHL5CM9YqxLPQwEQVCXmM+cOUNDQwMQ2bap5d65O0pPrUj90UcfqXW/+MUvyMjISMizxOLMvC71MFCkFgSB6upqTp06xcmTJ+no6EiKPdPwQOqUlBTeffddDh48yHfffcfHH39MW1tbQk7lxCoIxHCkHmkE/6FKnTvC9uKCsgBz7949fD5fUgSa6Y4gCDgcDiwWC4cOHWLdunUcOHAAs9mc1ELDIKU+fvz4tOilPQZtDkrq4uLiLGAcGk1JocxVJ0Ogmb5wOp2MGzcOg8FAdnY2NpstIfcR65/egFIXFRVNjn4ZqxwrA0q9fv36Z6KXmhRaJ3bEozvoV+qOjg5l5j2Wm4P7lbq+vv776JeJX8rSiSvxevtQpM7auHHjD62trTWrV6+e5vP5gqWlpV9HPxOLHCvdqQPwer1TcnNz3zly5MjN3Nxc16VLl86sXbv2dLQ9PcfKI47wsC2MMQxLZSTyAtj77KEXaIlVI73IBaY8pPwk0KZnktU2I80kG+/5oRDQRORwq5VIKjcf8f3VX0fkdHg2kTFza/Qe2uLYpo5GiHcPrTm0vKleZ+T010ObiaQStkavfUR6uViPfbujJJ13EOnFlaTzo74W/Kj8qn5UnmOw9DXLYQbGA9a8vDxTXl6eiYjY2TwQPNYUEEky74gmnbcRSUJfRCSKv47OgPQl9BiAo0ePTjl//vzi8+fPL/7nP/+pLIhkEXup04EJ+fn59vv37//pww8//Gt7e/uBbgsi+ehS6wyCvoROcTqdwpIlS2YIUX7yk5/M+Pzzz6dH62MtdQbAvn37lo4ZM2YKgMPhmHj48OE3dal1hkKfCys+nw9Jknpsl3r22Wcfi5PUIeiRZB6AlJSUzF5SF6BLrdMP/YYC++CDD/4d7jUt8Oyzzz526tSpx6KXsZK6FWD16tX/amxs7BGMUZE6Oq4eiy61Tj/0FwpMeuWVV+6+//77tb2lXrhw4fQYS90JNLS2toZcLldlU1NTXffKlJSUzPfee2+PLrXOQAwUCkxas2ZN8/vvv1/be5NRHKSuBW5///33gaKioopklFqSJPx+P36/n0AgkLRz3oFAgLa2NlpbW2lvb0+KmHwK/W1OCtJN6g8++KBuEFKPdBP9JZJUakmSCAQCZGVlkZOTg8lk0nx0pIchSRJut5ucnBx+85vfkJWVhdvtThqpB9ptFwTuMHipxzH6UheiAaklSSIzM5Onn36auXPnsmjRIjU+nNbPCHbH7/fjdDrZu3cvJSUlHDhwgJycnKSRerChwAaUuqqqKid6OWpSv/TSSxOB/0IjUgeDQfx+P6FQCKfTyfz580lPT9d8dKTuCIKAJElq7JDMzEz279/PrFmz6OzsTNr40L0ZUOoFCxb8eLSlPnDgQKVWpDYajbjdbk6fPo3f7yccDpOamsqCBQsYN24cgUBA04FkFMxmM263mz/84Q+0tEQ2RGZmZvL222/z+OOPa76nHmooMF3qPlBibrS2tlJVVaVGEDKbzcybN48f/ehHBINBTcsAD0Lp3rx5k82bN9Pc3AxEjli9++67PPXUU5ruqYcTCiyhUj9snlprUnd0dPDFF1/Q0dGhBpIpKChg6tSpSJKkealNJhM2m41bt26xefNm7ty5A4DVauXNN98kPz8ft9utxunTEsMNBZYwqV0uV+UgpU5IzmFFap/PxxdffMEPP/yAIAgIgsBTTz3FzJkzkyLniiJ1Q0MDJSUl3Lp1CwCbzcZbb73FvHnz8Hg8mpN6JKHAtC51AQmSWom5EQwGOX36tJoJC2DOnDk8+eSThEIhzc9VK1I3Nzfz2muvcfv2bQA1DNjTTz+N2+3G5/Ml+E4fMNJQYANKfe7cuVGV+uDBg3u0IrXZbEaWZU6fPq0mEgqHw+Tk5JCXl6cmEtK61Ha7naamJjZu3Mg333wDRN4NKioqWLRokaZ66liEAutX6qKiolGV2mq1pmtFaniQc6Wmpob6+no1PcW0adOYN28eoihqvqc2Go3Y7Xbu3r3Lpk2buH79OhAJB7x3716WLFmimZ46VqHAdKn7Qcki2z2RUDgcZuLEicybNw+z2az5BRhF6ra2NsrKyvjqq6+AyDvDrl27+OlPf4rH40m41LEMBTZUqUcaaGaoUs8dYXsjQomMdPXqVerq6hAEgXA4TFZWFvPnz1cTCSWD1Pfv3+d3v/sdly9fBiLDq+3bt7Ns2bKESx3rUGADSt0tFJgjBm32kPq777670L1SkXrx4sXpRI6POWPQ5rBRktffuHGDCxcuIEkSsiyTkZHBokWLcDgcmpdamaf2er2UlZVRVxdZGhBFkW3btiU8OWc8QoH1kLr3B+bNm6fEzIhVKDBV6vnz5+9pbm7+d/dKq9WaXlZWtih6mRmjNoeNkkiooaGB6upqvF4vsixjt9uZP38+NptN8yF1Fak9Hg/l5eXU1tYCkZ66rKyM5557Do/Hk5CpyXiEAnMABovFIixZsiS7d6XX61Veh2O1DpwSbZOVK1dOTU9P/3HvDzQ2NioRkzSR09dgMCCKIk1NTbS0tGAwGJAkCafTidPp1HQPraDkMXS73fz2t7/lxo0bQETqdevWqRm+RptYC50B2C0Wi3D16tXcadOm9RA6EAgEKysrlazosRhopRA5Fe4sLy+fUVFRUWE2m3vEnq6vr/+8pKTkdPRSE6HAlFwqc+bMYfLkyUiShNFo5Pbt2zQ1NSVNBFLlRXbZsmVMmDBBLf/yyy/p6upS852PJrGMnDSgzK+//nrNO++800Ektsf9EbY3KJlzc3Pf8Xq9MnCL+MYUGRTKGDk/P5+pU6ciyzIGg4GGhgY1QWci4ioPFa/Xi8fj4bnnnqOsrEwNTlRTU8POnTuRZTkhzxEroYcq890Rtjccmf9jPD+ahMNhdWxcWFjIpEmTVJlv3LhBXV2dmn9F6yjp237961+zefNmtby6upqtW7cSCATUbFmjTSyEzgRsQ5R5JOPnpJQ5GAxiMBgoLCxk/PjxyLKMKIpcu3aNK1euqC+LWkeR+fnnn2fTpk1q+blz59i2bRuBQAC73Z6Q4QaMXOhMwOZ0OoWLFy/qMj8ERWar1UpRUREZGRnIsowgCFy+fJmvvvoq6WR+8cUXKSkpUctPnDjBtm3b1PQUiXwHGInQuswDoOzVSEtLw+VyMWbMGMLhMOFwmIsXL3Lr1i1MJlNSJAnyeDx0dXWxatUq1q1bp5afOHGCHTt2aEJmGL7QuswDoMjsdDpxuVykpaWp4+jz58/T2NioLolrHUXml19+mTVr1qjlx48fZ+fOnQCakBmGJ7Qu8wAo2WDHjh1LYWEhFktkDcnv91NTU0NLS0vSyOx2u+nq6uKVV15h1apVavmxY8fYtWuXuhyuBZlh6ELrMg+AciJl/PjxFBQUqGPjrq4uzp49S1tbW9KkblN65pKSElauXKmWHz16lMrKSs3JDEMTWpd5AEKhEKFQiClTpjB37lxEUUQQBDo6Ojhz5gwejydpZFZ65g0bNrBixQq1/MiRI+zZsweLxYLD4dBccPzBCq3LPAChUAhJkpg+fTq5uQ9yfN67d0/ds5EseQgVmUtLSykuLlbLP/30U/bt24fVasVms2lOZhic0LrMg0CWZdLT01WZlb0a1dXVSJKE2WzWpAC9CQQC+P1+Xn311R4yf/LJJ+zfvx+LxYLdbtfsswwktC7zEAiFQni9XlJSUmhoaOD8+fM9cnonA6FQiIyMDJYvX66WffTRR/zxj38kJSVF0zJD/0LrMg8Bo9FIZ2cnJ0+eJC0tTd1FlwwLJt0xmUy0tbXx8ccfs3TpUv7xj39w6NChpJAZ+hY6jdGVGSL5BZNSZugZvsDn8yXNtFxvlJM1f/nLX/j73/9Oa2ur+gKYDPQltA2gqqpqzijJnAGMKy4uzkpGmRVEUUyKl76BsNlsavL5sWPHJvhuhkZfP32r0+kUZs+ePbF7YZxkhmiOlfXr1z+TrDLraIO+hA62t7eHOzo6upSCOMoM0cyy3ZLME73WZdYZEn0J7QcoLy+/ePPmzeabN2/e2bBhw9k4yQyRkyT+tWvXnj527Nj/trS0fPnZZ58d0mXWGSp9pUYWiIQa6J1mwgf8QGxlVhgDPPOQ8pjKrCev1zbxSl4fJpKOwsGD09l+oGMkjQ1AG3CSSAZbJen8HUZ+VEvn/xEP7aF1dJKV5J9j0tHphi60ziOFLrTOI4UutM4jhS60ziPF/wHgvbfirVNMHAAAAABJRU5ErkJggg==) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px #0000001a inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px #0006}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display: inline;*zoom: 1;vertical-align:top}.layui-layer-imguide,.layui-layer-imgbar{display:none}.layui-layer-imgprev,.layui-layer-imgnext{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:none;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px}.layui-layer-imgnext{right:10px;background-position:-5px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:#000c;background-color:#000 \\ ;filter:Alpha(opacity=80);color:#fff;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;font-size:0}.layui-layer-imgtit *{display:inline-block;*display: inline;*zoom: 1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} "); System.addImportMap({ imports: { "jquery": "user:jquery", "localforage": "user:localforage", "toastify-js": "user:toastify-js", "blueimp-md5": "user:blueimp-md5", "viewerjs": "user:viewerjs", "qrcodejs": "user:qrcodejs" } }); System.set("user:jquery", (() => { const _ = jQuery; ('default' in _) || (_.default = _); return _ })()); System.set("user:localforage", (() => { const _ = localforage; ('default' in _) || (_.default = _); return _ })()); System.set("user:toastify-js", (() => { const _ = Toastify; ('default' in _) || (_.default = _); return _ })()); System.set("user:blueimp-md5", (() => { const _ = md5; ('default' in _) || (_.default = _); return _ })()); System.set("user:viewerjs", (() => { const _ = Viewer; ('default' in _) || (_.default = _); return _ })()); System.set("user:qrcodejs", (() => { const _ = QRCode; ('default' in _) || (_.default = _); return _ })()); System.register("./__entry.js", ['jquery', 'localforage', 'toastify-js', 'blueimp-md5', 'viewerjs', 'qrcodejs'], (function (exports, module) { 'use strict'; var $$1, localforage$1, Toastify$1, md5$1, Viewer$1, QRCode$1; return { setters: [module => { $$1 = module.default; }, module => { localforage$1 = module.default; }, module => { Toastify$1 = module.default; }, module => { md5$1 = module.default; }, module => { Viewer$1 = module.default; }, module => { QRCode$1 = module.default; }], execute: (async function () { var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var _a; const scriptRel = /* @__PURE__ */ (function detectScriptRel() { const relList = typeof document !== "undefined" && document.createElement("link").relList; return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload"; })(); const assetsURL = function (dep) { return "/" + dep; }; const seen = {}; const __vitePreload = function preload(baseModule, deps, importerUrl) { let promise = Promise.resolve(); if (deps && deps.length > 0) { let allSettled2 = function (promises) { return Promise.all( promises.map( (p) => Promise.resolve(p).then( (value) => ({status: "fulfilled", value}), (reason) => ({status: "rejected", reason}) ) ) ); }; document.getElementsByTagName("link"); const cspNonceMeta = document.querySelector( "meta[property=csp-nonce]" ); const cspNonce = (cspNonceMeta == null ? void 0 : cspNonceMeta.nonce) || (cspNonceMeta == null ? void 0 : cspNonceMeta.getAttribute("nonce")); promise = allSettled2( deps.map((dep) => { dep = assetsURL(dep); if (dep in seen) return; seen[dep] = true; const isCss = dep.endsWith(".css"); const cssSelector = isCss ? '[rel="stylesheet"]' : ""; if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) { return; } const link2 = document.createElement("link"); link2.rel = isCss ? "stylesheet" : scriptRel; if (!isCss) { link2.as = "script"; } link2.crossOrigin = ""; link2.href = dep; if (cspNonce) { link2.setAttribute("nonce", cspNonce); } document.head.appendChild(link2); if (isCss) { return new Promise((res, rej) => { link2.addEventListener("load", res); link2.addEventListener( "error", () => rej(new Error(`Unable to preload CSS for ${dep}`)) ); }); } }) ); } function handlePreloadError(err) { const e = new Event("vite:preloadError", { cancelable: true }); e.payload = err; window.dispatchEvent(e); if (!e.defaultPrevented) { throw err; } } return promise.then((res) => { for (const item of res || []) { if (item.status !== "rejected") continue; handlePreloadError(item.reason); } return baseModule().catch(handlePreloadError); }); }; var __defProp2 = Object.defineProperty, __typeError = (msg) => { throw TypeError(msg); }, __publicField2 = (obj, key, value) => ((obj2, key2, value2) => key2 in obj2 ? __defProp2(obj2, key2, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj2[key2] = value2)(obj, "symbol" != typeof key ? key + "" : key, value), __privateMethod = (obj, member, method) => (((obj2, member2, msg) => { member2.has(obj2) || __typeError("Cannot " + msg); })(obj, member, "access private method"), method); const currentHref$1 = window.location.href, isJavDb$1 = currentHref$1.includes("javdb"), isJavBus$1 = currentHref$1.includes("javbus") || currentHref$1.includes("seejav") || currentHref$1.includes("bus") || currentHref$1.includes("javsee") || "javbus" === (() => { try { const _alt = $(".hidden-xs").attr("alt"); return _alt ? _alt.trim().toLowerCase() : void 0; } catch (_e) { return void 0; } })(), isSearchPage = currentHref$1.includes("/search?q") || currentHref$1.includes("/search/") || currentHref$1.includes("/users/"), Status_RUNNING = "RUNNING", Status_SUCCESS = "SUCCESS", Status_FAIL = "FAIL", Status_LOADING = "LOADING", Status_FILTER$1 = "filter", Status_FAVORITE$1 = "favorite", Status_HAS_DOWN$1 = "hasDown", Status_HAS_WATCH$1 = "hasWatch", NO$1 = "no", YES$1 = "yes", qualityOptions = [{ id: "video-mhb", quality: "dmb_w", text: "旧视频源-中画质宽版 (404p)", canSelect: false }, { id: "video-mhb", quality: "sm_s", text: "旧视频源-低画质 (240p)", canSelect: false }, { id: "video-mhb", quality: "dm_s", text: "旧视频源-中画质 (360p)", canSelect: false }, { id: "video-mhb", quality: "dmb_s", text: "旧视频源-中画质 (480p)", canSelect: false }, { id: "video-mhb", quality: "mhb_w", text: "旧视频源-高画质宽版 (404p)", canSelect: false }, { id: "video-mmb", quality: "mmb", text: "中画质 (432p)", canSelect: true }, { id: "video-dm", quality: "dm", text: "低画质 (1000kbps)", canSelect: true }, { id: "video-dmb", quality: "dmb", text: "中画质 (1500kbps)", canSelect: true }, { id: "video-mhb", quality: "mhb", text: "高画质 (576p)", canSelect: true }, { id: "video-hmb", quality: "hmb", text: "HD (720p)", canSelect: true }, { id: "video-hhb", quality: "hhb", text: "FullHD (1080p)", canSelect: true }, { id: "video-hhbs", quality: "hhbs", text: "FullHD (1080p60fps)", canSelect: true }, { id: "video-4k", quality: "4k", text: "4K (2160p)", canSelect: true }, { id: "video-4ks", quality: "4ks", text: "4K (2160p60fps)", canSelect: true }]; let detailPageCss$1 = ""; window.location.href.includes("hideNav=1") && (detailPageCss$1 = "\n .navbar-default {\n display: none !important;\n }\n body {\n padding-top:0px!important;\n }\n "); const javBusStyle = ` `; let detailPageCss = ""; window.location.href.includes("hideNav=1") && (detailPageCss = "\n .main-nav,#search-bar-container {\n display: none !important;\n }\n \n html {\n padding-top:0px!important;\n }\n "); const javdbStyle = ` `; const mainCss = ` `; const subtitleModalCss = ` `; function insertStyle(css) { if (css) if (css.includes("\n" ); insertStyle(mainCss); insertStyle(subtitleModalCss); const isEligibleDmmCoverCode = (value) => { const raw = String(value || "").trim().toUpperCase(); if (!/^[A-Z0-9]{2,10}-\d{1,6}$/.test(raw)) return false; const code = raw.replace(/\s+/g, "-").toUpperCase(); if (!/^[A-Z0-9]{2,10}-\d{1,6}$/.test(code) || /^FC2-/.test(code)) return false; if (/^\d{6}[-_]\d{2,3}$/.test(code)) return false; if (/^(DUGA|MYWIFE|HEYZO|PACO|10MU|1PONDO|CARIBBEAN|CARIB|TOKYO|GACHI|REAL|JUKU|AKA|NTR)-/.test(code)) return false; return true; }; window.$ = window.jQuery = $$1; if (typeof $$1.trim !== "function") { $$1.trim = function (text) { return text == null ? "" : (text + "").trim(); }; } if (typeof jQuery !== "undefined" && typeof layer === "undefined") { await __vitePreload(() => module.import('./layer-npqWxmx4-BZXOsKge.js').then((n) => n.l), void 0); } window.localforage = localforage$1; window.Toastify = Toastify$1; window.md5 = md5$1; window.Viewer = Viewer$1; window.QRCode = QRCode$1; window.currentHref = currentHref$1; window.isJavDb = isJavDb$1; window.isJavBus = isJavBus$1; window.isSearchPage = isSearchPage; window.Status_RUNNING = Status_RUNNING; window.Status_SUCCESS = Status_SUCCESS; window.Status_FAIL = Status_FAIL; window.Status_LOADING = Status_LOADING; window.Status_FILTER = Status_FILTER$1; window.Status_FAVORITE = Status_FAVORITE$1; window.Status_HAS_DOWN = Status_HAS_DOWN$1; window.Status_HAS_WATCH = Status_HAS_WATCH$1; window.NO = NO$1; window.YES = YES$1; const HAS_GM = typeof GM !== "undefined"; const NEW_GM = ((scope, GM2) => { if (typeof GM_info === "undefined" || GM_info.scriptHandler !== "Tampermonkey" || compareVersions(GM_info.version, "5.3.2") < 0) return; const GM_xmlhttpRequestOrig = GM_xmlhttpRequest; const GM_xmlHttpRequestOrig = GM2.xmlHttpRequest; function compareVersions(v1, v2) { const parts1 = v1.split(".").map(Number); const parts2 = v2.split(".").map(Number); const length = Math.max(parts1.length, parts2.length); for (let i = 0; i < length; i++) { const num1 = parts1[i] || 0; const num2 = parts2[i] || 0; if (num1 > num2) return 1; if (num1 < num2) return -1; } return 0; } function GM_xmlhttpRequestWrapper(odetails) { if (odetails.redirect !== void 0) { return GM_xmlhttpRequestOrig(odetails); } if (odetails.onprogress || odetails.fetch === false) { console.warn("Fetch mode does not support onprogress in the background."); } const { onload, onloadend, onerror, onabort, ontimeout, ...details } = odetails; const handleRedirects = (initialDetails) => { const request = GM_xmlhttpRequestOrig({ ...initialDetails, redirect: "manual", onload: function (response) { if (response.status >= 300 && response.status < 400) { const m = response.responseHeaders.match(/Location:\s*(\S+)/i); const redirectUrl = m && m[1]; if (redirectUrl) { const absoluteUrl = new URL(redirectUrl, initialDetails.url).href; handleRedirects({...initialDetails, url: absoluteUrl}); return; } } if (onload) onload.call(this, response); if (onloadend) onloadend.call(this, response); }, onerror: function (response) { if (onerror) onerror.call(this, response); if (onloadend) onloadend.call(this, response); }, onabort: function (response) { if (onabort) onabort.call(this, response); if (onloadend) onloadend.call(this, response); }, ontimeout: function (response) { if (ontimeout) ontimeout.call(this, response); if (onloadend) onloadend.call(this, response); } }); return request; }; return handleRedirects(details); } function GM_xmlHttpRequestWrapper(odetails) { let abort; const p = new Promise((resolve, reject) => { const {onload, ontimeout, onerror, ...send} = odetails; send.onerror = function (r) { if (onerror) { resolve(r); onerror.call(this, r); } else { reject(r); } }; send.ontimeout = function (r) { if (ontimeout) { resolve(r); ontimeout.call(this, r); } else { reject(r); } }; send.onload = function (r) { resolve(r); if (onload) onload.call(this, r); }; const a = GM_xmlhttpRequestWrapper(send).abort; if (abort === true) { a(); } else { abort = a; } }); p.abort = () => { if (typeof abort === "function") { abort(); } else { abort = true; } }; return p; } GM_xmlhttpRequest = GM_xmlhttpRequestWrapper; scope.GM_xmlhttpRequestOrig = GM_xmlhttpRequestOrig; const gopd = Object.getOwnPropertyDescriptor(GM2, "xmlHttpRequest"); if (gopd && gopd.configurable === false) { return { __proto__: GM2, xmlHttpRequest: GM_xmlHttpRequestWrapper, xmlHttpRequestOrig: GM_xmlHttpRequestOrig }; } else { GM2.xmlHttpRequest = GM_xmlHttpRequestWrapper; GM2.xmlHttpRequestOrig = GM_xmlHttpRequestOrig; } })(globalThis, HAS_GM ? GM : {}); if (HAS_GM && NEW_GM) GM = NEW_GM; var _StorageManager_instances, setItem_fn, saveFilterItem_fn; _StorageManager_instances = /* @__PURE__ */ new WeakSet(); setItem_fn = async function (key, data) { key === this.favorite_actresses_key && window.clean_cacheFavoriteActressList(); key === this.blacklist_car_list_key && window.clean_cacheBlacklistCarList(); key === this.setting_key && window.clean_cacheSettingObj(); key === this.car_list_key && window.clean_cacheCarList(); await this.forage.setItem(key, data); }; saveFilterItem_fn = async function (items, storageKey, itemName) { let itemList; if (Array.isArray(items)) itemList = [...items]; else { itemList = await this.forage.getItem(storageKey) || []; if (itemList.includes(items)) { const errorMsg = `${items} ${itemName}已存在`; show.error(errorMsg); throw new Error(errorMsg); } itemList.push(items); } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, storageKey, itemList); return itemList; }; let StorageManager = class _StorageManager { constructor() { obj = this, (member = _StorageManager_instances).has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var obj, member, value; __publicField2(this, "car_list_key", "car_list"); __publicField2(this, "filter_keyword_title_key", "filter_keyword_title"); __publicField2(this, "filter_keyword_review_key", "filter_keyword_review"); __publicField2(this, "setting_key", "setting"); __publicField2(this, "blacklist_key", "blacklist"); __publicField2(this, "blacklist_car_list_key", "blacklist_car_list"); __publicField2(this, "favorite_actresses_key", "favorite_actresses"); __publicField2(this, "highlighted_tags_key", "highlighted_tags"); __publicField2(this, "forage", localforage.createInstance({ driver: localforage.INDEXEDDB, name: "JAV-JHS", version: 1, storeName: "appData" })); __publicField2(this, "cacheCarList", null); __publicField2(this, "cacheBlacklistCarList", null); __publicField2(this, "cacheFavoriteActressList", null); __publicField2(this, "cacheSettingObj", null); if (_StorageManager.instance) throw new Error("StorageManager已被实例化过了!"); _StorageManager.instance = this; } async getCarList() { if (this.cacheCarList) return utils.copyObj(this.cacheCarList); this.cacheCarList = await this.forage.getItem(this.car_list_key) || []; return utils.copyObj(this.cacheCarList); } async getCar(carNum2) { return (await this.getCarList()).find(((item) => item.carNum === carNum2)); } _handleSingleCar(carParam, carList) { let {carNum: carNum2, url, names, actionType, publishTime, starId} = carParam; if (!carNum2) { show.error("番号为空!"); throw new Error("番号为空!"); } if (!url) { show.error("url为空!"); throw new Error("url为空!"); } url.includes("http") || (url = window.location.origin + url); names && (names = names.trim()); let carData = carList.find(((item) => item.carNum === carNum2)); if (carData) { names && (carData.names = names); url && (carData.url = url); publishTime && (carData.publishTime = publishTime); carData.updateDate = utils.getNowStr(); } else { let nowStr = utils.getNowStr(); carData = { carNum: carNum2, url, names, status: "", createDate: nowStr, updateDate: nowStr, publishTime }; starId && (carData.starId = starId); carList.push(carData); } switch (actionType) { case Status_FILTER: if (carData.status === Status_FILTER) { const msg2 = `${carNum2} 已在屏蔽列表中`; show.error(msg2); throw new Error(msg2); } carData.status = Status_FILTER; break; case Status_FAVORITE: if (carData.status === Status_FAVORITE) { const msg2 = `${carNum2} 已在收藏列表中`; show.error(msg2); throw new Error(msg2); } carData.status = Status_FAVORITE; break; case Status_HAS_DOWN: carData.status = Status_HAS_DOWN; break; case Status_HAS_WATCH: carData.status = Status_HAS_WATCH; break; default: const msg = "actionType错误, 请联系作者更正: " + actionType; show.error(msg); throw new Error(msg); } } async saveCar(carData) { const carList = await this.forage.getItem(this.car_list_key) || []; this._handleSingleCar(carData, carList); await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, carList); await this.removeNewVideoList([carData.carNum]); } async updateCarInfo(carParam) { let {carNum: carNum2, url, names, actionType, publishTime, remark} = carParam; if (!carNum2) { show.error("番号为空!"); throw new Error("番号为空!"); } if (!url) { show.error("url为空!"); throw new Error("url为空!"); } names && (names = names.trim()); const carList = await this.forage.getItem(this.car_list_key) || []; let carData = carList.find(((item) => item.carNum === carNum2)); if (!carData) { const msg = "数据不存在: " + carNum2; show.error(msg); throw new Error(msg); } carData.names = names; carData.url = url; carData.remark = remark; carData.updateDate = utils.getNowStr(); switch (actionType) { case Status_FILTER: carData.status = Status_FILTER; break; case Status_FAVORITE: carData.status = Status_FAVORITE; break; case Status_HAS_DOWN: carData.status = Status_HAS_DOWN; break; case Status_HAS_WATCH: carData.status = Status_HAS_WATCH; break; default: const msg = "actionType错误, 请联系作者更正: " + actionType; show.error(msg); throw new Error(msg); } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, carList); } async saveCarList(carRecords) { if (!carRecords || !Array.isArray(carRecords) || 0 === carRecords.length) { show.error("记录列表为空!"); throw new Error("记录列表为空!"); } const carList = await this.forage.getItem(this.car_list_key) || []; for (const record of carRecords) try { this._handleSingleCar(record, carList); } catch (error) { throw error; } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, carList); } async removeNewVideoList(carNumList) { const favoriteActressesList = await this.getFavoriteActressList(); let hasAnyListBeenModified = false; const updatedActressList = favoriteActressesList.map(((actress) => { if (!actress.newVideoList || !Array.isArray(actress.newVideoList)) return actress; const newFilteredVideoList = actress.newVideoList.filter(((carNumString) => { const isRemoved = carNumList.includes(carNumString); if (isRemoved) { clog.log("移除关联女优新作品", actress.name, carNumString); hasAnyListBeenModified = true; } return !isRemoved; })); return { ...actress, newVideoList: newFilteredVideoList }; })); hasAnyListBeenModified && await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, updatedActressList); } async removeCar(carNum2) { const carList = await this.getCarList(), initialLength = carList.length, updatedList = carList.filter(((car) => car.carNum !== carNum2)); if (updatedList.length === initialLength) { show.error(`${carNum2} 不存在`); return false; } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, updatedList); return true; } async batchRemoveCars(carNumList) { if (!carNumList || 0 === carNumList.length) throw new Error("未传入参数"); const carList = await this.getCarList(), initialLength = carList.length, carNumsSet = new Set(carNumList), updatedList = carList.filter(((car) => !carNumsSet.has(car.carNum))), removedCount = initialLength - updatedList.length; if (0 === removedCount) return removedCount; await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.car_list_key, updatedList); return removedCount; } async getBlacklist() { return await this.forage.getItem(this.blacklist_key) || []; } async addBlacklistItem(item) { let {starId, name: name2, allName, role, movieType, url} = item; if (!starId) throw new Error("缺失starId"); if (!name2) throw new Error("缺失name"); if (!role) throw new Error("缺失role"); const blacklist = await this.getBlacklist(), existData = blacklist.find(((item2) => item2.starId === starId)); if (existData) { existData.url = url; existData.role = role; existData.movieType = movieType; clog.log("更新黑名单演员信息", existData); } else { const info = { starId, name: name2, allName: allName || [name2], createTime: utils.getNowStr(), role, movieType, url }; blacklist.push(info); clog.log("增加黑名单演员信息", info); } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_key, blacklist); } async updateBlacklistItem(item) { if (!item || !item.starId) throw new Error("参数不全"); const filterActorActressInfoList = await this.getBlacklist(), waitUpdateObj = filterActorActressInfoList.find(((i) => i.starId === item.starId)); if (!waitUpdateObj) throw new Error(`未找到黑名单演员信息:${item.name} ${item.starId}`); item.checkTime && (waitUpdateObj.checkTime = item.checkTime); item.lastPublishTime && (waitUpdateObj.lastPublishTime = item.lastPublishTime); await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_key, filterActorActressInfoList); } async deleteBlacklistItem(starId) { const blacklist = await this.getBlacklist(), updatedList = blacklist.filter(((item) => item.starId !== starId)); blacklist.length !== updatedList.length && await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_key, updatedList); } async getBlacklistCarList() { if (this.cacheBlacklistCarList && this.cacheBlacklistCarList.length > 0) return utils.deepFreeze(this.cacheBlacklistCarList); this.cacheBlacklistCarList = await this.forage.getItem(this.blacklist_car_list_key) || []; setTimeout((() => { utils.deepFreeze(this.cacheBlacklistCarList); }), 0); return this.cacheBlacklistCarList; } async batchSaveBlacklistCarList(carDataList) { const carList = await this.getCarList(); let blacklistCarList = await this.getBlacklistCarList(), hasChanged = false, copyBlacklistCarList = null, carNumList = []; for (const carData of carDataList) { let existBlacklistCarData = blacklistCarList.find(((item) => item.carNum === carData.carNum)); if (existBlacklistCarData) { console.log("已在鉴定记录中", existBlacklistCarData); continue; } let existCarData = carList.find(((item) => item.carNum === carData.carNum)); if (existCarData) console.log("已在鉴定记录中", existCarData); else { copyBlacklistCarList || (copyBlacklistCarList = utils.copyObj(blacklistCarList)); this._handleSingleCar(carData, copyBlacklistCarList); clog.log(`屏蔽演员番号: ${carData.names} ${carData.carNum}`); hasChanged = true; carNumList.push(carData.carNum); } } if (hasChanged) { if (!copyBlacklistCarList) throw new Error("程序异常, 黑名单番号数据对象为空!"); await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_car_list_key, copyBlacklistCarList); await this.removeNewVideoList(carNumList); } } async removeBlacklistCarList(starId) { const filterActorActressCarList = await this.getBlacklistCarList(), newList = filterActorActressCarList.filter(((car) => car.starId !== starId)); newList.length !== filterActorActressCarList.length && await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_car_list_key, newList); } async batchRemoveBlacklistCars(carNumList) { if (!carNumList || 0 === carNumList.length) throw new Error("未传入参数"); const blacklistCarList = await this.getBlacklistCarList(), initialLength = blacklistCarList.length, carNumsSet = new Set(carNumList), updatedList = blacklistCarList.filter(((car) => !carNumsSet.has(car.carNum))), removedCount = initialLength - updatedList.length; if (0 === removedCount) return removedCount; await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.blacklist_car_list_key, updatedList); return removedCount; } async getFavoriteActressList() { if (this.cacheFavoriteActressList) return utils.copyObj(this.cacheFavoriteActressList); this.cacheFavoriteActressList = await this.forage.getItem(this.favorite_actresses_key) || []; return utils.copyObj(this.cacheFavoriteActressList); } async addFavoriteActressList(actressList) { const favoriteActressesInfoList = await this.getFavoriteActressList(); let resultCount = 0; for (const actress of actressList) { let {starId, name: name2, allName, avatar, lastCheckTime, lastPublishTime, actressType} = actress; if (!starId) throw new Error("缺失starId"); if (!name2) throw new Error("缺失name"); allName || (allName = [name2]); const uncensoredText = "(無碼)"; if (!actressType) { actressType = name2.includes(uncensoredText) || allName.some(((element) => element.includes(uncensoredText))) ? "uncensored" : "censored"; } name2 = name2.replace(uncensoredText, ""); allName = allName.map(((n) => n.replace(uncensoredText, ""))); let favoriteActresses = favoriteActressesInfoList.find(((item) => item.starId === starId)); if (favoriteActresses) { if ((!favoriteActresses.avatar || !favoriteActresses.avatar.includes("https")) && avatar) { clog.log(avatar); favoriteActresses.avatar = avatar; clog.log(`补全女优头像: ${name2}`); resultCount++; } if (!favoriteActresses.actressType && actressType) { favoriteActresses.actressType = actressType; clog.log(`补全女优类别: ${name2} ${actressType}`); resultCount++; } if (favoriteActresses.name.includes(uncensoredText)) { favoriteActresses.name = name2; favoriteActresses.allName = allName; clog.log(`更正女优名字: ${name2} ${allName}`); resultCount++; } continue; } const nowStr = utils.getNowStr(); favoriteActressesInfoList.push({ starId, name: name2, allName, avatar, lastCheckTime, lastPublishTime, createDate: nowStr, updateDate: nowStr, actressType }); clog.log(`同步JavDB已收藏的演员: ${name2}`); resultCount++; } resultCount > 0 ? await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, favoriteActressesInfoList) : clog.log("信息已记录, 无需要进行同步收藏的演员"); return resultCount; } async removeFavoriteActress(starId) { const favoriteActressesInfoList = await this.getFavoriteActressList(), initialLength = favoriteActressesInfoList.length, updatedList = favoriteActressesInfoList.filter(((car) => car.starId !== starId)); if (updatedList.length === initialLength) { clog.error(`移除演员失败, ${starId} 不存在`); return false; } await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, updatedList); return true; } async updateFavoriteActress(actress) { const favoriteActressesInfoList = await this.getFavoriteActressList(), { starId, name: name2, allName, avatar, lastCheckTime, newVideoList, lastPublishTime, actressType, remark } = actress; if (!starId) throw new Error("缺失starId"); let favoriteActresses = favoriteActressesInfoList.find(((item) => item.starId === starId)); if (!favoriteActresses) { clog.error("未找到演员信息", starId, name2); return false; } name2 && (favoriteActresses.name = name2); allName && (favoriteActresses.allName = allName); avatar && (favoriteActresses.avatar = avatar); null != actressType && (favoriteActresses.actressType = actressType); lastCheckTime && (favoriteActresses.lastCheckTime = lastCheckTime); newVideoList && (favoriteActresses.newVideoList = newVideoList); lastPublishTime && (favoriteActresses.lastPublishTime = lastPublishTime); remark && (favoriteActresses.remark = remark); favoriteActresses.updateDate = utils.getNowStr(); await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, favoriteActressesInfoList); } async getHighlightedTags() { return await this.forage.getItem(this.highlighted_tags_key) || []; } async setHighlightedTags(highlightedTags) { return await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.highlighted_tags_key, highlightedTags); } async saveTitleFilterKeyword(keywords) { await __privateMethod(this, _StorageManager_instances, saveFilterItem_fn).call(this, keywords, this.filter_keyword_title_key, "标题关键词"); if (Array.isArray(keywords)) return null; const favoriteActressesList = await this.getFavoriteActressList(); let hasAnyListBeenModified = false; const updatedActressList = favoriteActressesList.map(((actress) => { if (!actress.newVideoList || !Array.isArray(actress.newVideoList)) return actress; const newFilteredVideoList = actress.newVideoList.filter(((carNumString) => { const isRemoved = carNumString.startsWith(keywords); if (isRemoved) { clog.log("移除关联女优新作品", actress.name, carNumString); hasAnyListBeenModified = true; } return !isRemoved; })); return { ...actress, newVideoList: newFilteredVideoList }; })); hasAnyListBeenModified && await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.favorite_actresses_key, updatedActressList); } async getTitleFilterKeyword() { return await this.forage.getItem(this.filter_keyword_title_key) || []; } async getReviewFilterKeywordList() { return await this.forage.getItem(this.filter_keyword_review_key) || []; } async saveReviewFilterKeyword(keywords) { return __privateMethod(this, _StorageManager_instances, saveFilterItem_fn).call(this, keywords, this.filter_keyword_review_key, "评论关键词"); } async getSetting(attribute = null, defaultVal) { this.cacheSettingObj || (this.cacheSettingObj = await this.forage.getItem(this.setting_key) || {}); let settingObj = utils.copyObj(this.cacheSettingObj); if (null === attribute) return settingObj; const value = settingObj[attribute]; return value ? "true" === value || "false" === value ? "true" === value.toLowerCase() : "string" != typeof value || "" === value.trim() || isNaN(Number(value)) ? value : Number(value) : defaultVal; } async saveSetting(settingObj) { settingObj ? await __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, this.setting_key, settingObj) : show.error("设置对象为空"); } async saveSettingItem(key, value) { if (!key) { show.error("key 不能为空"); return; } let settingObj = await this.getSetting(); settingObj[key] = value; await this.saveSetting(settingObj); } async importData(jsonData) { await this.forage.clear(); const importPromises = []; for (const key in jsonData) { const value = jsonData[key], setPromise = __privateMethod(this, _StorageManager_instances, setItem_fn).call(this, key, value); importPromises.push(setPromise); } await Promise.all(importPromises); } async exportData() { const dataMap = {}; await this.forage.iterate(((value, key) => { dataMap[key] = value; })); if (0 === Object.keys(dataMap).length) throw new Error("没有可导出的数据"); return dataMap; } }; const apiUrl = "https://jdforrepam.com/api"; function buildSignature() { const curr = Math.floor(Date.now() / 1e3), stored_sign = localStorage.getItem("jhs_jdsignature"); if (stored_sign) { const parts = stored_sign.split("."); if (3 === parts.length) { if (curr - parseInt(parts[0]) <= 300) return stored_sign; } } const sign = `${curr}.lpw6vgqzsp.${md5(`${curr}71cf27bb3c0bcdf207b64abecddc970098c7421ee7203b9cdae54478478a199e7d5a6e1a57691123c1a931c057842fb73ba3b3c83bcd69c17ccf174081e3d8aa`)}`; localStorage.setItem("jhs_jdsignature", sign); return sign; } const javDbApi = { getReviews: async (movieId, pageNum = 1, pageSize = 20) => { let url = `${apiUrl}/v1/movies/${movieId}/reviews`, headers = { jdSignature: await buildSignature() }; return (await gmHttp.get(url, { page: pageNum, sort_by: "hotly", limit: pageSize }, headers)).data.reviews; }, searchMovie: async (keyword) => { let url = `${apiUrl}/v2/search`, headers = { "user-agent": "Dart/3.5 (dart:io)", "accept-language": "zh-TW", host: "jdforrepam.com", jdsignature: await buildSignature() }, params = { q: keyword, page: 1, type: "movie", limit: 1, movie_type: "all", from_recent: "false", movie_filter_by: "all", movie_sort_by: "relevance" }; return (await gmHttp.get(url, params, headers)).data.movies; }, getMovieDetail: async (movieId) => { let url = `${apiUrl}/v4/movies/${movieId}`, headers = { jdSignature: await buildSignature() }; const res = await gmHttp.get(url, null, headers); if (!res.data) { show.error("获取视频详情失败: " + res.message); throw new Error(res.message); } const movie = res.data.movie, preview_images = movie.preview_images, imgList = []; preview_images.forEach(((item) => { imgList.push(item.large_url.replace("https://tp.spfcas.com/rhe951l4q", "https://c0.jdbstatic.com")); })); return { movieId: movie.id, actors: movie.actors, duration: movie.duration, title: movie.origin_title, carNum: movie.number, score: movie.score, releaseDate: movie.release_date, watchedCount: movie.watched_count, imgList }; }, related: async (movieId, page = 1, limit = 20) => { let url = `${apiUrl}/v1/lists/related?movie_id=${movieId}&page=${page}&limit=${limit}`, headers = { jdSignature: await buildSignature() }; const res = await gmHttp.get(url, null, headers), dataList = []; res.data.lists.forEach(((item) => { dataList.push({ relatedId: item.id, name: item.name, movieCount: item.movies_count, collectionCount: item.collections_count, viewCount: item.views_count, createTime: utils.formatDate(item.created_at) }); })); return dataList; }, getMagnets: async (movieId) => { let url = `${apiUrl}/v1/movies/${movieId}/magnets`, headers = { jdSignature: await buildSignature() }; return (await gmHttp.get(url, null, headers)).data.magnets; }, playback: async (period = "daily", filter_by = "high_score") => { let url = `${apiUrl}/v1/rankings/playback?period=${period}&filter_by=${filter_by}`, headers = { jdSignature: await buildSignature() }; return (await gmHttp.get(url, null, headers)).data.movies; }, login: async (username, password) => { let url = `${apiUrl}/v1/sessions?username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}&device_uuid=04b9534d-5118-53de-9f87-2ddded77111e&device_name=iPhone&device_model=iPhone&platform=ios&system_version=17.4&app_version=official&app_version_number=1.9.29&app_channel=official`, headers = { "user-agent": "Dart/3.5 (dart:io)", "accept-language": "zh-TW", "content-type": "multipart/form-data; boundary=--dio-boundary-2210433284", jdsignature: await buildSignature() }; return await gmHttp.post(url, null, headers); }, top250: async (type = "all", type_value = "", page = 1, limit = 40) => { let url = `${apiUrl}/v1/movies/top?start_rank=1&type=${type}&type_value=${type_value}&ignore_watched=false&page=${page}&limit=${limit}`, headers = { "user-agent": "Dart/3.5 (dart:io)", "accept-language": "zh-TW", host: "jdforrepam.com", authorization: "Bearer " + localStorage.getItem("jhs_appAuthorization"), jdsignature: await buildSignature() }; return await gmHttp.get(url, null, headers); }, buildSignature, markDataListHtml: (movies) => { let moviesHtml = ""; movies.forEach(((movie) => { moviesHtml += `
${movie.number} ${movie.origin_title}
${movie.release_date}
${movie.has_cnsub ? '含中字磁鏈' : movie.magnets_count > 0 ? '含磁鏈' : '无磁鏈'} ${movie.new_magnets ? '今日新種' : ""}
`; })); return moviesHtml; } }; var _GM_openInTab = /* @__PURE__ */ (() => typeof GM_openInTab != "undefined" ? GM_openInTab : void 0)(); var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)(); class Utils { constructor() { __publicField2(this, "intervalContainer", {}); __publicField2(this, "mimeTypes", { txt: "text/plain", html: "text/html", css: "text/css", csv: "text/csv", json: "application/json", xml: "application/xml", jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp", svg: "image/svg+xml", pdf: "application/pdf", doc: "application/msword", docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", xls: "application/vnd.ms-excel", xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ppt: "application/vnd.ms-powerpoint", pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation", zip: "application/zip", rar: "application/x-rar-compressed", "7z": "application/x-7z-compressed", mp3: "audio/mpeg", wav: "audio/wav", mp4: "video/mp4", webm: "video/webm", ogg: "audio/ogg" }); __publicField2(this, "timers", /* @__PURE__ */ new Map()); __publicField2(this, "insertStyle", ((css) => { if (css) { -1 === css.indexOf(""); $("head").append(css); } })); __publicField2(this, "layerIndexStack", []); Utils.instance || (Utils.instance = this); return Utils.instance; } importResource(url) { let tag; if (url.indexOf("css") >= 0) { tag = document.createElement("link"); tag.setAttribute("rel", "stylesheet"); tag.href = url; } else { tag = document.createElement("script"); tag.setAttribute("type", "text/javascript"); tag.src = url; } document.documentElement.appendChild(tag); } openPage(url, title, shadeClose, event) { if (!url) throw new Error("未传入url"); shadeClose = shadeClose ?? true; if (event && (event.ctrlKey || event.metaKey)) { _GM_openInTab(url.includes("http") ? url : window.location.origin + url, { insert: 0 }); return; } let finalUrl = url; url.includes("/actors/") || url.includes("/star/") || (finalUrl = url.includes("?") ? `${url}&hideNav=1` : `${url}?hideNav=1`); layer.open({ type: 2, title, content: finalUrl, scrollbar: false, shadeClose, area: this.getResponsiveArea(["85%", "90%"]), isOutAnim: false, anim: -1, success: (layero, index) => { this.setupEscClose(index); } }); } _handleGlobalEscKey(e) { if ("Escape" !== e.key && 27 !== e.keyCode) return; if (0 === this.layerIndexStack.length) return; const topLayerIndex = this.layerIndexStack[this.layerIndexStack.length - 1], $layer = $(`#layui-layer${topLayerIndex}`); let viewerExists = false; if ($layer.find(".viewer-container").length > 0) viewerExists = true; else { const iframe = $layer.find(`#layui-layer-iframe${topLayerIndex}`)[0]; if (iframe && iframe.contentDocument) try { $(iframe.contentDocument).find(".viewer-container").length > 0 && (viewerExists = true); } catch (error) { clog.warn("无法检查跨域 iframe 内的 .viewer-container"); } } if (!viewerExists) { this.layerIndexStack.pop(); typeof layer !== "undefined" && layer.close(topLayerIndex); } } setupEscClose(layerIndex) { var _a2; if (!this._boundHandler) { this._boundHandler = this._handleGlobalEscKey.bind(this); $(document).off("keydown.globalLayerEsc"); $(document).on("keydown.globalLayerEsc", this._boundHandler); } -1 === this.layerIndexStack.indexOf(layerIndex) && this.layerIndexStack.push(layerIndex); const $iframe = $(`#layui-layer-iframe${layerIndex}`), eventNamespace = `keydown.layerEsc${layerIndex}`; try { const iframeDocument = null == (_a2 = $iframe[0]) ? void 0 : _a2.contentDocument; if (iframeDocument) { if ("yes" === $iframe.attr("data-esc-bound")) return; $(iframeDocument).off(eventNamespace); $(iframeDocument).on(eventNamespace, this._boundHandler); $iframe.attr("data-esc-bound", "yes"); } } catch (e) { clog.error("iframe监听失败 (跨域或未加载完毕):", e); } } closePage() { storageManager.getSetting("needClosePage", "yes").then(((needClosePage) => { if ("yes" !== needClosePage) return; parent.document.documentElement.style.overflow = "auto"; [".layui-layer-shade", ".layui-layer-move", ".layui-layer"].forEach((function (selector) { const elements = parent.document.querySelectorAll(selector); if (elements.length > 0) { const elementToRemove = elements.length > 1 ? elements[elements.length - 1] : elements[0]; elementToRemove.parentNode.removeChild(elementToRemove); } })); window.close(); })); } loopDetector(condition, after, detectInterval = 20, timeout = 1e4, runWhenTimeout = true) { const uuid = Math.random(), start = (/* @__PURE__ */ new Date()).getTime(), stopAndRun = (shouldRun) => { clearInterval(this.intervalContainer[uuid]); shouldRun && after && after(); delete this.intervalContainer[uuid]; }; this.intervalContainer[uuid] = setInterval((() => { const timeElapsed = (/* @__PURE__ */ new Date()).getTime() - start; condition() ? stopAndRun(true) : timeElapsed >= timeout && stopAndRun(runWhenTimeout); }), detectInterval); } rightClick(container, targetSelector, callback) { let containerElement; "string" == typeof container ? containerElement = document.querySelector(container) : container instanceof HTMLElement && (containerElement = container); if (!containerElement) { console.warn("rightClick(), 容器无效或未提供,将使用 document.body 进行全局委托。"); containerElement = document.body; } "string" == typeof targetSelector && "" !== targetSelector.trim() ? containerElement.addEventListener("contextmenu", ((event) => { const targetElement = event.target.closest(targetSelector); targetElement && callback(event, targetElement); })) : console.error("rightClick(), 必须提供有效的 targetSelector。"); } q(event, msg, fun, cancelFun) { let x, y; if (event) { x = event.clientX - 130; y = event.clientY - 120; } else { x = window.innerWidth / 2 - 120; y = window.innerHeight / 2 - 120; } let confirmIndex = layer.confirm(msg, { offset: [y, x], title: "提示", btn: ["确定", "取消"], shade: 0, zIndex: 999999991 }, (function () { fun && fun(); layer.close(confirmIndex); }), (function () { cancelFun && cancelFun(); })); } alert(event, msg, yesFun) { let offset; event && (offset = [event.clientX - 200, event.clientY - 120]); let confirmIndex = layer.alert(msg, { offset, shade: 0, zIndex: 999999991 }, (function () { yesFun && yesFun(); layer.close(confirmIndex); })); } getNowStr(dateSplitStr = "-", timeSplitStr = ":", dateString = null) { let now; now = dateString ? new Date(dateString) : /* @__PURE__ */ new Date(); const year = now.getFullYear(), month = String(now.getMonth() + 1).padStart(2, "0"), day = String(now.getDate()).padStart(2, "0"), hours = String(now.getHours()).padStart(2, "0"), minutes = String(now.getMinutes()).padStart(2, "0"), seconds = String(now.getSeconds()).padStart(2, "0"); return `${[year, month, day].join(dateSplitStr)} ${[hours, minutes, seconds].join(timeSplitStr)}`; } formatDate(date2, dateSplitStr = "-", timeSplitStr = ":") { let targetDate; if (date2 instanceof Date) targetDate = date2; else { if ("string" != typeof date2) throw new Error("Invalid date input: must be Date object or date string"); targetDate = new Date(date2); if (isNaN(targetDate.getTime())) throw new Error("Invalid date string"); } const year = targetDate.getFullYear(), month = String(targetDate.getMonth() + 1).padStart(2, "0"), day = String(targetDate.getDate()).padStart(2, "0"), hours = String(targetDate.getHours()).padStart(2, "0"), minutes = String(targetDate.getMinutes()).padStart(2, "0"), seconds = String(targetDate.getSeconds()).padStart(2, "0"); return `${[year, month, day].join(dateSplitStr)} ${[hours, minutes, seconds].join(timeSplitStr)}`; } getHourDifference(date1, date2) { const timestamp1 = date1.getTime(), timestamp2 = date2.getTime(), differenceInHours = Math.abs(timestamp2 - timestamp1) / 36e5; return Math.floor(differenceInHours); } download(data, fileName) { show.info("开始请求下载..."); const fileExtension = fileName.split(".").pop().toLowerCase(); let blob, mimeType = this.mimeTypes[fileExtension] || "application/octet-stream"; if (data instanceof Blob) blob = data; else if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) blob = new Blob([data], { type: mimeType }); else if ("string" == typeof data && data.startsWith("data:")) { const byteString = atob(data.split(",")[1]), arrayBuffer = new ArrayBuffer(byteString.length), uintArray = new Uint8Array(arrayBuffer); for (let i = 0; i < byteString.length; i++) uintArray[i] = byteString.charCodeAt(i); blob = new Blob([uintArray], { type: mimeType }); } else blob = new Blob([data], { type: mimeType }); const url = URL.createObjectURL(blob), a = document.createElement("a"); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); setTimeout((() => { document.body.removeChild(a); URL.revokeObjectURL(url); }), 100); } smoothScrollToTop(duration = 500) { return new Promise(((resolve) => { const start = performance.now(), startPosition = window.pageYOffset; window.requestAnimationFrame((function scrollStep(timestamp) { const elapsed = timestamp - start, progress2 = Math.min(elapsed / duration, 1), easeInOutCubic = progress2 < 0.5 ? 4 * progress2 * progress2 * progress2 : 1 - Math.pow(-2 * progress2 + 2, 3) / 2; window.scrollTo(0, startPosition * (1 - easeInOutCubic)); progress2 < 1 ? window.requestAnimationFrame(scrollStep) : resolve(); })); })); } simpleId() { return crypto.randomUUID().replace("-", ""); } isUrl(urlString) { try { new URL(urlString); return true; } catch (_) { return false; } } setHrefParam(key, val) { const newUrl = new URL(window.location.href); newUrl.searchParams.set(key, val); window.history.pushState({}, "", newUrl.toString()); } getUrlParam(url, key) { const searchString = url.split("?")[1]; if (!searchString) return null; const regex = new RegExp(`(?:^|&)${key}=([^&]*)`), match = searchString.match(regex); let value = ""; match && match[1] && (value = decodeURIComponent(match[1].replace(/\+/g, " "))); return value ? "true" === value || "false" === value ? "true" === value.toLowerCase() : "string" != typeof value || "" === value.trim() || isNaN(Number(value)) ? value : Number(value) : value; } reBuildSignature() { return javDbApi.buildSignature(); } getResponsiveArea(defaultArea) { const screenWidth = window.innerWidth; return screenWidth >= 1200 ? defaultArea || this.getDefaultArea() : screenWidth >= 768 ? ["70%", "90%"] : ["95%", "95%"]; } getDefaultArea() { return ["85%", "90%"]; } isMobile() { const userAgent = navigator.userAgent.toLowerCase(); return ["iphone", "ipod", "ipad", "android", "blackberry", "windows phone", "nokia", "webos", "opera mini", "mobile", "mobi", "tablet"].some(((keyword) => userAgent.includes(keyword))); } copyToClipboard(type, text) { navigator.clipboard.writeText(text).then((() => show.info(`${type}已复制到剪切板, ${text}`))).catch(((err) => console.error("复制失败: ", err))); } htmlTo$dom(html2) { const parser = new DOMParser(); return $(parser.parseFromString(html2, "text/html")); } addCookie(cookieStr, options = {}) { const {maxAge = 604800, path = "/", domain = "", secure = false, sameSite = "Lax"} = options; cookieStr.split(";").forEach(((cookie) => { const trimmed = cookie.trim(); if (trimmed) { const parts = trimmed.split("="); if (parts.length >= 2 && parts[0].trim()) { let cookieParts = [`${parts[0].trim()}=${parts.slice(1).join("=")}`]; maxAge > 0 && cookieParts.push(`max-age=${maxAge}`); cookieParts.push(`path=${path}`); domain && cookieParts.push(`domain=${domain}`); secure && cookieParts.push("Secure"); sameSite && cookieParts.push(`SameSite=${sameSite}`); console.log("document.cookie = '" + cookieParts.join("; ") + "'"); document.cookie = cookieParts.join("; "); } } })); } isHidden(el) { const element = el.jquery ? el[0] : el; return !element || (element.offsetWidth <= 0 && element.offsetHeight <= 0 || "none" === window.getComputedStyle(element).display); } time(label = "default", unit = "s", precision = 2) { if (this.timers.has(label)) { const timer = this.timers.get(label), elapsedTime = performance.now() - timer.startTime; let formattedTime, unitLabel; if ("s" === timer.unit) { formattedTime = (elapsedTime / 1e3).toFixed(timer.precision); unitLabel = "秒"; } else { formattedTime = elapsedTime.toFixed(timer.precision); unitLabel = "毫秒"; } this.timers.delete(label); return `${label}: ${formattedTime}${unitLabel}`; } this.timers.set(label, { startTime: performance.now(), unit, precision }); } sleep(ms = 1e3) { return new Promise(((resolve) => setTimeout(resolve, ms))); } genericSort(arr, sortRules, emptyLast = true) { if (!Array.isArray(arr) || 0 === arr.length) return []; if (!Array.isArray(sortRules) || 0 === sortRules.length) return [...arr]; const sortedArr = [...arr], safeDateConvert = (value) => { if (value instanceof Date) return value; if ("string" == typeof value) { const date2 = new Date(value); if (!isNaN(date2.getTime())) return date2; } return value; }; return sortedArr.sort(((a, b) => { for (const rule of sortRules) { const {key, order = "asc"} = rule; let valA = a, valB = b; if (null != key) if ("function" == typeof key) { valA = key(a); valB = key(b); } else { valA = a && "object" == typeof a ? a[key] : void 0; valB = b && "object" == typeof b ? b[key] : void 0; } const dateA = safeDateConvert(valA), dateB = safeDateConvert(valB); let comparison = 0; const aIsNull = null == valA, bIsNull = null == valB; if (aIsNull && bIsNull) return 0; if (aIsNull) return emptyLast ? 1 : -1; if (bIsNull) return emptyLast ? 1 : -1; comparison = dateA instanceof Date && dateB instanceof Date ? dateA.getTime() - dateB.getTime() : "number" == typeof valA && "number" == typeof valB ? valA - valB : "string" == typeof valA && "string" == typeof valB ? valA.localeCompare(valB) : String(valA).localeCompare(String(valB)); "desc" === order && (comparison *= -1); if (0 !== comparison) return comparison; } return 0; })); } async retry(fun, tryCount = 3) { let runCount = 0; for (; runCount < tryCount;) try { const result = await fun(); runCount > 0 && clog.debug(`[重试] 成功,共发起 ${runCount + 1} 次。`); return result; } catch (e) { let errorString = String(e); errorString.startsWith("Error: ") && (errorString = errorString.replace("Error: ", "")); if (errorString.includes("Just a moment") || errorString.includes("重定向") || errorString.toLowerCase().includes("404 page not found") || errorString.toLowerCase().includes("404 not found")) throw e; runCount++; if (runCount === tryCount) { clog.debug(`[重试] 达到最大重试次数 (${tryCount}),最终失败:`, e); throw e; } clog.debug(`[重试] 准备第 ${runCount + 1} 次重试, 错误信息: ${errorString}`); } } copyObj(data) { return JSON.parse(JSON.stringify(data)); } deepFreeze(obj) { if (null === obj || "object" != typeof obj || Object.isFrozen(obj)) return obj; const propNames = Object.getOwnPropertyNames(obj); for (const name2 of propNames) { const value = obj[name2]; value && "object" == typeof value && this.deepFreeze(value); } return Object.freeze(obj); } } class AsyncQueue { constructor() { this.queue = Promise.resolve(); } addTask(fun) { this.queue = this.queue.then((() => fun())).catch(((e) => { console.error("执行异步队列任务失败:", e); })); } async waitAllFinished() { return this.queue; } } class GmHttp { async get(url, params = {}, headers = {}, noRedirect) { return this.gmRequest("GET", url, null, params, headers, noRedirect); } post(url, data = {}, headers = {}) { headers = { "Content-Type": "application/json", ...headers }; let jsonData = JSON.stringify(data); return this.gmRequest("POST", url, jsonData, null, headers); } postForm(url, data = {}, headers = {}) { headers || (headers = {}); headers["Content-Type"] || (headers["Content-Type"] = "application/x-www-form-urlencoded"); let body = ""; data && Object.keys(data).length > 0 && (body = Object.entries(data).map((([key, value]) => `${key}=${value}`)).join("&")); return this.gmRequest("POST", url, body, null, headers); } postFileFormData(url, data = {}, headers = {}) { headers || (headers = {}); const boundary = `----WebKitFormBoundary${Math.random().toString(36).substring(2)}`; headers["Content-Type"] = `multipart/form-data; boundary=${boundary}`; let body = ""; data && Object.keys(data).length > 0 && (body = Object.entries(data).map((([key, value]) => `--${boundary}\r Content-Disposition: form-data; name="${key}"\r \r ${value}\r `)).join("")); body += `--${boundary}--`; return this.gmRequest("POST", url, body, null, headers); } async downloadFileInChunks(url, headers = {}) { const httpTimeout = await storageManager.getSetting("httpTimeout", 5e3), httpRetryCount = await storageManager.getSetting("httpRetryCount", 3); clog.log("正在获取文件大小..."); let fileSize, mimeType; try { const sizeResponse = await utils.retry((() => new Promise(((resolve, reject) => { _GM_xmlhttpRequest({ method: "GET", url, headers: { ...headers, Range: "bytes=0-0" }, timeout: httpTimeout, onload: resolve, onerror: (e) => reject(new Error("网络错误:无法获取文件大小")), ontimeout: () => reject(new Error("超时:获取文件大小")) }); }))), httpRetryCount); if (206 !== sizeResponse.status && 200 !== sizeResponse.status) throw new Error(`请求文件大小失败,状态码: ${sizeResponse.status}`); { const rangeHeader = sizeResponse.responseHeaders.match(/content-range:\s*bytes\s*\d+-\d+\/(\d+)/i), typeHeader = sizeResponse.responseHeaders.match(/content-type:\s*([^\s;]+)/i); if (rangeHeader && rangeHeader[1]) fileSize = parseInt(rangeHeader[1], 10); else { if (!sizeResponse.responseHeaders.match(/content-length:\s*(\d+)/i) || 200 !== sizeResponse.status) throw new Error("无法从响应头中获取文件总大小,服务器可能不支持 Range 请求。"); { const lengthHeader = sizeResponse.responseHeaders.match(/content-length:\s*(\d+)/i); fileSize = parseInt(lengthHeader[1], 10); clog.warn("服务器返回 200 状态码,可能不支持 Range 请求。将尝试完整下载。"); } } typeHeader && typeHeader[1] && (mimeType = typeHeader[1]); clog.log(`文件总大小:${(fileSize / 1024 / 1024).toFixed(2)} MB, MIME 类型: ${mimeType || "未知"}`); } } catch (e) { clog.error("获取文件大小失败:", e.message); throw e; } if (!fileSize || fileSize <= 0) throw new Error("获取到的文件大小无效或服务器拒绝提供大小信息。"); const numChunks = Math.ceil(fileSize / 1048576), chunkPromises = [], downloadedChunks = new Array(numChunks); clog.log(`文件将被分为 ${numChunks} 块进行下载 (每块约 ${1.toFixed(2)} MB)`); for (let i = 0; i < numChunks; i++) { const start = 1048576 * i, rangeHeader = `bytes=${start}-${Math.min(start + 1048576 - 1, fileSize - 1)}`, chunkPromise = await utils.retry((() => new Promise(((resolve, reject) => { const currentHeaders = { ...headers, Range: rangeHeader, Accept: "application/octet-stream" }; _GM_xmlhttpRequest({ method: "GET", url, headers: currentHeaders, timeout: httpTimeout, responseType: "arraybuffer", onload: (response) => { if (206 === response.status || 200 === response.status) if (response.response instanceof ArrayBuffer) { downloadedChunks[i] = response.response; clog.log(`成功下载第 ${i + 1}/${numChunks} 块 (${rangeHeader})`); resolve(); } else reject(new Error(`第 ${i + 1} 块响应不是 ArrayBuffer。`)); else reject(new Error(`第 ${i + 1} 块请求失败,状态码: ${response.status}`)); }, onerror: (error) => reject(new Error(`第 ${i + 1} 块网络错误: ${error.error}`)), ontimeout: () => reject(new Error(`第 ${i + 1} 块超时。`)) }); }))), httpRetryCount); chunkPromises.push(chunkPromise); } try { await Promise.all(chunkPromises); clog.log("所有分块下载完成,开始合并..."); } catch (e) { clog.error("分块下载过程中发生错误:", e.message); throw e; } const finalBlob = new Blob(downloadedChunks); finalBlob.size !== fileSize && clog.warn(`警告:合并后的 Blob 大小 (${finalBlob.size}) 与预期文件大小 (${fileSize}) 不匹配!`); return await finalBlob.text(); } async gmRequest(method, url, data = {}, params = {}, headers = {}, noRedirect = false) { if (params && Object.keys(params).length) { const queryString = new URLSearchParams(params).toString(); url += (url.includes("?") ? "&" : "?") + queryString; } const httpTimeout = await storageManager.getSetting("httpTimeout", 5e3), httpRetryCount = await storageManager.getSetting("httpRetryCount", 3); data || (data = void 0); return await utils.retry((() => new Promise(((resolve, reject) => { _GM_xmlhttpRequest({ method, url, headers, timeout: httpTimeout, data, onload: (response) => { try { noRedirect && response.finalUrl !== url && reject("请求被重定向了,URL是:" + response.finalUrl); if (response.status >= 200 && response.status < 300) if (response.responseText) try { resolve(JSON.parse(response.responseText)); } catch (e) { resolve(response.responseText); } else resolve(response.responseText || response); else { clog.error("请求失败,状态码:", response.status, url); if (response.responseText) try { const errorData = JSON.parse(response.responseText); reject(errorData); } catch { reject(new Error(response.responseText || `请求发生错误 ${response.status}`)); } else reject(new Error(`请求发生错误 ${response.status}`)); } } catch (e) { reject(e); } }, onerror: (error) => { clog.error("网络错误:", url); reject(new Error(error.error || "网络错误")); }, ontimeout: () => { reject(new Error("请求超时: " + url)); } }); }))), httpRetryCount); } } class PluginManager { constructor() { this.plugins = /* @__PURE__ */ new Map(); } register(pluginClass) { if ("function" != typeof pluginClass) throw new Error("插件必须是一个类"); const instance = new pluginClass(); instance.pluginManager = this; const lowerName = instance.getName(); if (this.plugins.has(lowerName)) throw new Error(`插件"${name}"已注册`); this.plugins.set(lowerName, instance); } getBean(name2) { return this.plugins.get(name2); } async processCss() { const failedCssLoads = (await Promise.allSettled(Array.from(this.plugins).map((async ([name2, instance]) => { try { if ("function" == typeof instance.initCss) { const css = await instance.initCss(); css && utils.insertStyle(css); return { name: name2, status: "fulfilled" }; } return { name: name2, status: "skipped" }; } catch (e) { console.error(`插件 ${name2} 加载 CSS 失败`, e); return { name: name2, status: "rejected", error: e }; } })))).filter(((r) => "rejected" === r.status)); failedCssLoads.length && console.error("以下插件的 CSS 加载失败:", failedCssLoads.map(((p) => p.value.name))); } async processPlugins() { const failedPlugins = (await Promise.allSettled(Array.from(this.plugins).map((async ([name2, instance]) => { try { if ("function" == typeof instance.handle) { await instance.handle(); return { name: name2, status: "fulfilled" }; } } catch (e) { clog.error(`插件 ${name2} 执行失败`, e); return { name: name2, status: "rejected", error: e }; } })))).filter(((r) => "rejected" === r.status)); failedPlugins.length && console.error("以下插件执行失败:", failedPlugins.map(((p) => p.value.name))); } } class BasePlugin { constructor() { __publicField2(this, "pluginManager", null); __publicField2(this, "settingSvg", ''); __publicField2(this, "editSvg", ''); __publicField2(this, "deleteSvg", ''); __publicField2(this, "checkSvg", ''); __publicField2(this, "actressSvg", ''); __publicField2(this, "newSvg", ''); __publicField2(this, "refreshSvg", ''); __publicField2(this, "blacklistSvg", ''); __publicField2(this, "removeSvg", ''); __publicField2(this, "copySvg", ''); __publicField2(this, "titleSvg", ''); __publicField2(this, "carNumSvg", ''); __publicField2(this, "downSvg", ''); __publicField2(this, "handleSvg", ''); __publicField2(this, "siteSvg", ''); __publicField2(this, "videoSvg", ''); __publicField2(this, "screenSvg", ''); __publicField2(this, "recoveryVideoSvg", ''); __publicField2(this, "markDeleteSvg", ''); } getName() { throw new Error(`${this.constructor.name} 未显示getName()`); } getBean(name2) { return this.pluginManager.getBean(name2); } async initCss() { return ""; } async handle() { } getPageInfo() { let carNum2, url, actress, actors, publishTime, currentHref2 = window.location.href; if (isJavDb) { carNum2 = $('a[title="複製番號"]').attr("data-clipboard-text"); url = currentHref2.split("?")[0].split("#")[0]; actress = $(".female").prev().map(((i, el) => $(el).text())).get().join(" "); actors = $(".male").prev().map(((i, el) => $(el).text())).get().join(" "); publishTime = $('strong:contains("日期:")').parent(".panel-block").find(".value").text().trim(); } if (isJavBus) { url = currentHref2.split("?")[0]; carNum2 = url.split("/").filter(Boolean).pop().replace(/_\d{4}-\d{2}-\d{2}$/, ""); actress = $('span[onmouseover*="star_"] a').map(((i, el) => $(el).text())).get().join(" "); actors = ""; publishTime = $('span.header:contains("發行日期:")').parent("p").text().trim().replace("發行日期:", "").trim(); } return { carNum: carNum2, url, actress, actors, publishTime }; } getActressId() { const match = currentHref.match(/\/actors\/([^/?]+)/); return match && match.length > 1 ? match[1] : null; } getActressPageInfo() { let currentHref2 = window.location.href; if (!currentHref2.includes("/actors/") && !currentHref2.includes("/star/")) throw new Error("接口调用错误, 非演员详情页"); let nameList = [], $actor = isJavDb ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10"); $actor.length && $actor.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); let $sectionMeta = $(".section-meta:not(:contains('影片'))"); $sectionMeta.length && $sectionMeta.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); let role = $(".section-meta:contains('男優')").length > 0 ? "actor" : "actress", movieType = "censored"; nameList.some(((n) => n.includes("無碼"))) && (movieType = "uncensored"); currentHref2.includes("uncensored") && (movieType = "uncensored"); let blacklistUrl = null, starId = null; const urlObj = new URL(currentHref2); if (isJavDb) { starId = urlObj.pathname.split("/").filter(((segment) => "" !== segment.trim())).pop(); const params = urlObj.searchParams; params.delete("sort_type"); params.delete("page"); blacklistUrl = urlObj.toString(); } else if (isJavBus) { const splitStr = "/star/", parts = currentHref2.split(splitStr); if (parts.length < 2) throw new Error("提取演员url失败"); const host = parts[0]; starId = parts[1].split("/")[0]; blacklistUrl = host + splitStr + starId; } return { starId, name: nameList[0], allName: nameList, role, movieType, blacklistUrl }; } getSelector(type) { const key = type || (isJavDb ? "javdb" : isJavBus ? "javbus" : null), selectors = { javdb: { boxSelector: ".movie-list", itemSelector: ".movie-list .item", coverImgSelector: ".cover img", requestDomItemSelector: ".movie-list .item", nextPageSelector: ".pagination-next" }, javbus: { boxSelector: ".masonry", itemSelector: ".masonry .item", coverImgSelector: ".masonry .movie-box .photo-frame img", requestDomItemSelector: "#waterfall .item", nextPageSelector: "#next" } }; if (!key || !selectors[key]) throw new Error("类型错误: 无法确定选择器类型 (JavDb 或 JavBus)"); return selectors[key]; } parseMovieId(href) { return href.split("/").pop().split(/[?#]/)[0]; } getBoxCarInfo($box2) { var _a2, _b, _c; const $aLink = $box2.find("a"), url = $aLink.attr("href"); let carNum2 = null, title = null, publishTime = null; const $videoTitleEle = $box2.find(".video-title"); if ($videoTitleEle.length > 0) { const $strongEle = $videoTitleEle.find("strong"); $strongEle.length > 0 && (carNum2 = $strongEle.text().trim()); title = null == (_a2 = $aLink.attr("title")) ? void 0 : _a2.trim(); if (!title) { const fullTitleText = $videoTitleEle.text().trim(); title = carNum2 && fullTitleText.includes(carNum2) ? fullTitleText.replace(carNum2, "").trim() : fullTitleText; } publishTime = $box2.find(".meta").text().trim(); } if (!carNum2) { const $imgEle = $box2.find("img"); $imgEle.length > 0 && (title = (null == (_b = $imgEle.attr("title")) ? void 0 : _b.trim()) || (null == (_c = $imgEle.attr("data-title")) ? void 0 : _c.trim())); const dateElements = $box2.find("date").map(((i, el) => $(el).text().trim())).get(), isDate = (s) => /^\d{4}-\d{1,2}-\d{1,2}$/.test(s); publishTime = dateElements.find(isDate) || null; carNum2 = dateElements.find(((i) => !isDate(i))) || null; } if (!carNum2) { return null; } return { carNum: carNum2, url: url || "", aHref: url || "", title: title || "", publishTime: publishTime || "" }; } getBoxCarInfoList($boxList = null) { $boxList || ($boxList = $(this.getSelector().itemSelector)); if (0 === $boxList.length) { clog.error("获取当前列表页所有item的番号信息失败!"); return []; } const results = []; $boxList.each(((index, element) => { const $box2 = $(element); try { const carInfo = this.getBoxCarInfo($box2); results.push(carInfo); } catch (error) { clog.error("[getBoxCarInfoList] 提取单个 boxCar 信息失败:", error.message, "元素索引:", index); } })); return results; } checkDuplicateCarNumbers(currentList, newList) { if (!isJavDb) return false; if (!currentList || 0 === currentList.length || !newList || 0 === newList.length) return false; const currentCarNums = new Set(currentList.map(((item) => item.carNum)).filter(((n) => n))); if (0 === currentCarNums.size) return false; let consecutiveDuplicates = 0; for (let i = 0; i < newList.length; i++) { const newCarNum = newList[i] ? newList[i].carNum : null; if (newCarNum && currentCarNums.has(newCarNum)) { consecutiveDuplicates++; if (consecutiveDuplicates >= 2) { clog.warn("警告: 检测到连续番号信息重复, 该类别可能已被限制页码。"); return true; } } else consecutiveDuplicates = 0; } return false; } } class CoreFeature { constructor(table) { this.table = table; } ////////////////////////////////////////// /////////////// DataLoad ///////////////// ////////////////////////////////////////// reloadData(data, silent, columnsChanged) { return this.table.dataLoader.load(data, void 0, void 0, void 0, silent, columnsChanged); } ////////////////////////////////////////// ///////////// Localization /////////////// ////////////////////////////////////////// langText() { return this.table.modules.localize.getText(...arguments); } langBind() { return this.table.modules.localize.bind(...arguments); } langLocale() { return this.table.modules.localize.getLocale(...arguments); } ////////////////////////////////////////// ////////// Inter Table Comms ///////////// ////////////////////////////////////////// commsConnections() { return this.table.modules.comms.getConnections(...arguments); } commsSend() { return this.table.modules.comms.send(...arguments); } ////////////////////////////////////////// //////////////// Layout ///////////////// ////////////////////////////////////////// /** @returns {("fitData" | "fitDataFill" | "fitDataTable" | "fitDataStretch" | "fitColumns")} */ layoutMode() { return this.table.modules.layout.getMode(); } layoutRefresh(force) { return this.table.modules.layout.layout(force); } ////////////////////////////////////////// /////////////// Event Bus //////////////// ////////////////////////////////////////// subscribe() { return this.table.eventBus.subscribe(...arguments); } unsubscribe() { return this.table.eventBus.unsubscribe(...arguments); } subscribed(key) { return this.table.eventBus.subscribed(key); } subscriptionChange() { return this.table.eventBus.subscriptionChange(...arguments); } dispatch() { return this.table.eventBus.dispatch(...arguments); } chain() { return this.table.eventBus.chain(...arguments); } confirm() { return this.table.eventBus.confirm(...arguments); } dispatchExternal() { return this.table.externalEvents.dispatch(...arguments); } subscribedExternal(key) { return this.table.externalEvents.subscribed(key); } subscriptionChangeExternal() { return this.table.externalEvents.subscriptionChange(...arguments); } ////////////////////////////////////////// //////////////// Options ///////////////// ////////////////////////////////////////// options(key) { return this.table.options[key]; } setOption(key, value) { if (typeof value !== "undefined") { this.table.options[key] = value; } return this.table.options[key]; } ////////////////////////////////////////// /////////// Deprecation Checks /////////// ////////////////////////////////////////// deprecationCheck(oldOption, newOption, convert) { return this.table.deprecationAdvisor.check(oldOption, newOption, convert); } deprecationCheckMsg(oldOption, msg) { return this.table.deprecationAdvisor.checkMsg(oldOption, msg); } deprecationMsg(msg) { return this.table.deprecationAdvisor.msg(msg); } ////////////////////////////////////////// //////////////// Modules ///////////////// ////////////////////////////////////////// module(key) { return this.table.module(key); } } class Helpers { static elVisible(el) { return !(el.offsetWidth <= 0 && el.offsetHeight <= 0); } static elOffset(el) { var box = el.getBoundingClientRect(); return { top: box.top + window.pageYOffset - document.documentElement.clientTop, left: box.left + window.pageXOffset - document.documentElement.clientLeft }; } static retrieveNestedData(separator, field, data) { var structure = separator ? field.split(separator) : [field], length = structure.length, output; for (let i = 0; i < length; i++) { data = data[structure[i]]; output = data; if (!data) { break; } } return output; } static deepClone(obj, clone, list2 = []) { var objectProto = {}.__proto__, arrayProto = [].__proto__; if (!clone) { clone = Object.assign(Array.isArray(obj) ? [] : {}, obj); } for (var i in obj) { let subject = obj[i], match, copy; if (subject != null && typeof subject === "object" && (subject.__proto__ === objectProto || subject.__proto__ === arrayProto)) { match = list2.findIndex((item) => { return item.subject === subject; }); if (match > -1) { clone[i] = list2[match].copy; } else { copy = Object.assign(Array.isArray(subject) ? [] : {}, subject); list2.unshift({subject, copy}); clone[i] = this.deepClone(subject, copy, list2); } } } return clone; } } let Popup$1 = class Popup extends CoreFeature { constructor(table, element, parent2) { super(table); this.element = element; this.container = this._lookupContainer(); this.parent = parent2; this.reversedX = false; this.childPopup = null; this.blurable = false; this.blurCallback = null; this.blurEventsBound = false; this.renderedCallback = null; this.visible = false; this.hideable = true; this.element.classList.add("tabulator-popup-container"); this.blurEvent = this.hide.bind(this, false); this.escEvent = this._escapeCheck.bind(this); this.destroyBinding = this.tableDestroyed.bind(this); this.destroyed = false; } tableDestroyed() { this.destroyed = true; this.hide(true); } _lookupContainer() { var container = this.table.options.popupContainer; if (typeof container === "string") { container = document.querySelector(container); if (!container) { console.warn("Menu Error - no container element found matching selector:", this.table.options.popupContainer, "(defaulting to document body)"); } } else if (container === true) { container = this.table.element; } if (container && !this._checkContainerIsParent(container)) { container = false; console.warn("Menu Error - container element does not contain this table:", this.table.options.popupContainer, "(defaulting to document body)"); } if (!container) { container = document.body; } return container; } _checkContainerIsParent(container, element = this.table.element) { if (container === element) { return true; } else { return element.parentNode ? this._checkContainerIsParent(container, element.parentNode) : false; } } renderCallback(callback) { this.renderedCallback = callback; } containerEventCoords(e) { var touch = !(e instanceof MouseEvent); var x = touch ? e.touches[0].pageX : e.pageX; var y = touch ? e.touches[0].pageY : e.pageY; if (this.container !== document.body) { let parentOffset = Helpers.elOffset(this.container); x -= parentOffset.left; y -= parentOffset.top; } return {x, y}; } elementPositionCoords(element, position = "right") { var offset = Helpers.elOffset(element), containerOffset, x, y; if (this.container !== document.body) { containerOffset = Helpers.elOffset(this.container); offset.left -= containerOffset.left; offset.top -= containerOffset.top; } switch (position) { case "right": x = offset.left + element.offsetWidth; y = offset.top - 1; break; case "bottom": x = offset.left; y = offset.top + element.offsetHeight; break; case "left": x = offset.left; y = offset.top - 1; break; case "top": x = offset.left; y = offset.top; break; case "center": x = offset.left + element.offsetWidth / 2; y = offset.top + element.offsetHeight / 2; break; } return {x, y, offset}; } show(origin, position) { var x, y, parentEl, parentOffset, coords; if (this.destroyed || this.table.destroyed) { return this; } if (origin instanceof HTMLElement) { parentEl = origin; coords = this.elementPositionCoords(origin, position); parentOffset = coords.offset; x = coords.x; y = coords.y; } else if (typeof origin === "number") { parentOffset = {top: 0, left: 0}; x = origin; y = position; } else { coords = this.containerEventCoords(origin); x = coords.x; y = coords.y; this.reversedX = false; } this.element.style.top = y + "px"; this.element.style.left = x + "px"; this.container.appendChild(this.element); if (typeof this.renderedCallback === "function") { this.renderedCallback(); } this._fitToScreen(x, y, parentEl, parentOffset, position); this.visible = true; this.subscribe("table-destroy", this.destroyBinding); this.element.addEventListener("mousedown", (e) => { e.stopPropagation(); }); return this; } _fitToScreen(x, y, parentEl, parentOffset, position) { var scrollTop = this.container === document.body ? document.documentElement.scrollTop : this.container.scrollTop; if (x + this.element.offsetWidth >= this.container.offsetWidth || this.reversedX) { this.element.style.left = ""; if (parentEl) { this.element.style.right = this.container.offsetWidth - parentOffset.left + "px"; } else { this.element.style.right = this.container.offsetWidth - x + "px"; } this.reversedX = true; } let offsetHeight = Math.max(this.container.offsetHeight, scrollTop ? this.container.scrollHeight : 0); if (y + this.element.offsetHeight > offsetHeight) { if (parentEl) { switch (position) { case "bottom": this.element.style.top = parseInt(this.element.style.top) - this.element.offsetHeight - parentEl.offsetHeight - 1 + "px"; break; default: this.element.style.top = parseInt(this.element.style.top) - this.element.offsetHeight + parentEl.offsetHeight + 1 + "px"; } } else { let menuHeight = this.element.offsetHeight; if (menuHeight > offsetHeight) { this.element.style.top = "0px"; this.element.style.height = offsetHeight + "px"; } else { let newTop = y - menuHeight; if (newTop < 0) { newTop = offsetHeight - menuHeight; } this.element.style.top = newTop + "px"; } } } } isVisible() { return this.visible; } hideOnBlur(callback) { this.blurable = true; if (this.visible) { setTimeout(() => { if (this.visible) { this.table.rowManager.element.addEventListener("scroll", this.blurEvent); this.subscribe("cell-editing", this.blurEvent); document.body.addEventListener("click", this.blurEvent); document.body.addEventListener("contextmenu", this.blurEvent); document.body.addEventListener("mousedown", this.blurEvent); window.addEventListener("resize", this.blurEvent); document.body.addEventListener("keydown", this.escEvent); this.blurEventsBound = true; } }, 100); this.blurCallback = callback; } return this; } /** @param {KeyboardEvent} e */ _escapeCheck(e) { if (e.key == 27) { this.hide(); } } blockHide() { this.hideable = false; } restoreHide() { this.hideable = true; } hide(silent = false) { if (this.visible && this.hideable) { if (this.blurable && this.blurEventsBound) { document.body.removeEventListener("keydown", this.escEvent); document.body.removeEventListener("click", this.blurEvent); document.body.removeEventListener("contextmenu", this.blurEvent); document.body.removeEventListener("mousedown", this.blurEvent); window.removeEventListener("resize", this.blurEvent); this.table.rowManager.element.removeEventListener("scroll", this.blurEvent); this.unsubscribe("cell-editing", this.blurEvent); this.blurEventsBound = false; } if (this.childPopup) { this.childPopup.hide(); } if (this.parent) { this.parent.childPopup = null; } if (this.element.parentNode) { this.element.parentNode.removeChild(this.element); } this.visible = false; if (this.blurCallback && !silent) { this.blurCallback(); } this.unsubscribe("table-destroy", this.destroyBinding); } return this; } child(element) { if (this.childPopup) { this.childPopup.hide(); } this.childPopup = new Popup(this.table, element, this); return this.childPopup; } }; class Module extends CoreFeature { constructor(table, name2) { super(table); this._handler = null; } initialize() { } /////////////////////////////////// ////// Options Registration /////// /////////////////////////////////// registerTableOption(key, value) { this.table.optionsList.register(key, value); } registerColumnOption(key, value) { this.table.columnManager.optionsList.register(key, value); } /////////////////////////////////// /// Public Function Registration /// /////////////////////////////////// registerTableFunction(name2, func) { if (typeof this.table[name2] === "undefined") { this.table[name2] = (...args) => { this.table.initGuard(name2); return func(...args); }; } else { console.warn("Unable to bind table function, name already in use", name2); } } registerComponentFunction(component, func, handler) { return this.table.componentFunctionBinder.bind(component, func, handler); } /////////////////////////////////// ////////// Data Pipeline ////////// /////////////////////////////////// registerDataHandler(handler, priority) { this.table.rowManager.registerDataPipelineHandler(handler, priority); this._handler = handler; } registerDisplayHandler(handler, priority) { this.table.rowManager.registerDisplayPipelineHandler(handler, priority); this._handler = handler; } displayRows(adjust) { var index = this.table.rowManager.displayRows.length - 1, lookupIndex; if (this._handler) { lookupIndex = this.table.rowManager.displayPipeline.findIndex((item) => { return item.handler === this._handler; }); if (lookupIndex > -1) { index = lookupIndex; } } if (adjust) { index = index + adjust; } if (this._handler) { if (index > -1) { return this.table.rowManager.getDisplayRows(index); } else { return this.activeRows(); } } } activeRows() { return this.table.rowManager.activeRows; } refreshData(renderInPosition, handler) { if (!handler) { handler = this._handler; } if (handler) { this.table.rowManager.refreshActiveData(handler, false, renderInPosition); } } /////////////////////////////////// //////// Footer Management //////// /////////////////////////////////// footerAppend(element) { return this.table.footerManager.append(element); } footerPrepend(element) { return this.table.footerManager.prepend(element); } footerRemove(element) { return this.table.footerManager.remove(element); } /////////////////////////////////// //////// Popups Management //////// /////////////////////////////////// popup(menuEl, menuContainer) { return new Popup$1(this.table, menuEl, menuContainer); } /////////////////////////////////// //////// Alert Management //////// /////////////////////////////////// alert(content, type) { return this.table.alertManager.alert(content, type); } clearAlert() { return this.table.alertManager.clear(); } } var defaultAccessors = { rownum: function (value, data, type, params, column, row) { return row.getPosition(); } }; const _Accessor = class _Accessor extends Module { constructor(table) { super(table); this.allowedTypes = ["", "data", "download", "clipboard", "print", "htmlOutput"]; this.registerColumnOption("accessor"); this.registerColumnOption("accessorParams"); this.registerColumnOption("accessorData"); this.registerColumnOption("accessorDataParams"); this.registerColumnOption("accessorDownload"); this.registerColumnOption("accessorDownloadParams"); this.registerColumnOption("accessorClipboard"); this.registerColumnOption("accessorClipboardParams"); this.registerColumnOption("accessorPrint"); this.registerColumnOption("accessorPrintParams"); this.registerColumnOption("accessorHtmlOutput"); this.registerColumnOption("accessorHtmlOutputParams"); } initialize() { this.subscribe("column-layout", this.initializeColumn.bind(this)); this.subscribe("row-data-retrieve", this.transformRow.bind(this)); } //initialize column accessor initializeColumn(column) { var match = false, config = {}; this.allowedTypes.forEach((type) => { var key = "accessor" + (type.charAt(0).toUpperCase() + type.slice(1)), accessor; if (column.definition[key]) { accessor = this.lookupAccessor(column.definition[key]); if (accessor) { match = true; config[key] = { accessor, params: column.definition[key + "Params"] || {} }; } } }); if (match) { column.modules.accessor = config; } } lookupAccessor(value) { var accessor = false; switch (typeof value) { case "string": if (_Accessor.accessors[value]) { accessor = _Accessor.accessors[value]; } else { console.warn("Accessor Error - No such accessor found, ignoring: ", value); } break; case "function": accessor = value; break; } return accessor; } //apply accessor to row transformRow(row, type) { var key = "accessor" + (type.charAt(0).toUpperCase() + type.slice(1)), rowComponent = row.getComponent(); var data = Helpers.deepClone(row.data || {}); this.table.columnManager.traverse(function (column) { var value, accessor, params, colComponent; if (column.modules.accessor) { accessor = column.modules.accessor[key] || column.modules.accessor.accessor || false; if (accessor) { value = column.getFieldValue(data); if (value != "undefined") { colComponent = column.getComponent(); params = typeof accessor.params === "function" ? accessor.params(value, data, type, colComponent, rowComponent) : accessor.params; column.setFieldValue(data, accessor.accessor(value, data, type, params, colComponent, rowComponent)); } } } }); return data; } }; __publicField(_Accessor, "moduleName", "accessor"); //load defaults __publicField(_Accessor, "accessors", defaultAccessors); let Accessor = _Accessor; var defaultConfig = { method: "GET" }; function generateParamsList$1(data, prefix) { var output = []; prefix = prefix || ""; if (Array.isArray(data)) { data.forEach((item, i) => { output = output.concat(generateParamsList$1(item, prefix ? prefix + "[" + i + "]" : i)); }); } else if (typeof data === "object") { for (var key in data) { output = output.concat(generateParamsList$1(data[key], prefix ? prefix + "[" + key + "]" : key)); } } else { output.push({key: prefix, value: data}); } return output; } function serializeParams(params) { var output = generateParamsList$1(params), encoded = []; output.forEach(function (item) { encoded.push(encodeURIComponent(item.key) + "=" + encodeURIComponent(item.value)); }); return encoded.join("&"); } function urlBuilder(url, config, params) { if (url) { if (params && Object.keys(params).length) { if (!config.method || config.method.toLowerCase() == "get") { config.method = "get"; url += (url.includes("?") ? "&" : "?") + serializeParams(params); } } } return url; } function defaultLoaderPromise(url, config, params) { var contentType; return new Promise((resolve, reject) => { url = this.urlGenerator.call(this.table, url, config, params); if (config.method.toUpperCase() != "GET") { contentType = typeof this.table.options.ajaxContentType === "object" ? this.table.options.ajaxContentType : this.contentTypeFormatters[this.table.options.ajaxContentType]; if (contentType) { for (var key in contentType.headers) { if (!config.headers) { config.headers = {}; } if (typeof config.headers[key] === "undefined") { config.headers[key] = contentType.headers[key]; } } config.body = contentType.body.call(this, url, config, params); } else { console.warn("Ajax Error - Invalid ajaxContentType value:", this.table.options.ajaxContentType); } } if (url) { if (typeof config.headers === "undefined") { config.headers = {}; } if (typeof config.headers.Accept === "undefined") { config.headers.Accept = "application/json"; } if (typeof config.headers["X-Requested-With"] === "undefined") { config.headers["X-Requested-With"] = "XMLHttpRequest"; } if (typeof config.mode === "undefined") { config.mode = "cors"; } if (config.mode == "cors") { if (typeof config.headers["Origin"] === "undefined") { config.headers["Origin"] = window.location.origin; } if (typeof config.credentials === "undefined") { config.credentials = "same-origin"; } } else { if (typeof config.credentials === "undefined") { config.credentials = "include"; } } fetch(url, config).then((response) => { if (response.ok) { response.json().then((data) => { resolve(data); }).catch((error) => { reject(error); console.warn("Ajax Load Error - Invalid JSON returned", error); }); } else { console.error("Ajax Load Error - Connection Error: " + response.status, response.statusText); reject(response); } }).catch((error) => { console.error("Ajax Load Error - Connection Error: ", error); reject(error); }); } else { console.warn("Ajax Load Error - No URL Set"); resolve([]); } }); } function generateParamsList(data, prefix) { var output = []; prefix = prefix || ""; if (Array.isArray(data)) { data.forEach((item, i) => { output = output.concat(generateParamsList(item, prefix ? prefix + "[" + i + "]" : i)); }); } else if (typeof data === "object") { for (var key in data) { output = output.concat(generateParamsList(data[key], prefix ? prefix + "[" + key + "]" : key)); } } else { output.push({key: prefix, value: data}); } return output; } var defaultContentTypeFormatters = { "json": { headers: { "Content-Type": "application/json" }, body: function (url, config, params) { return JSON.stringify(params); } }, "form": { headers: {}, body: function (url, config, params) { var output = generateParamsList(params), form = new FormData(); output.forEach(function (item) { form.append(item.key, item.value); }); return form; } } }; const _Ajax = class _Ajax extends Module { constructor(table) { super(table); this.config = {}; this.url = ""; this.urlGenerator = false; this.params = false; this.loaderPromise = false; this.registerTableOption("ajaxURL", false); this.registerTableOption("ajaxURLGenerator", false); this.registerTableOption("ajaxParams", {}); this.registerTableOption("ajaxConfig", "get"); this.registerTableOption("ajaxContentType", "form"); this.registerTableOption("ajaxRequestFunc", false); this.registerTableOption("ajaxRequesting", function () { }); this.registerTableOption("ajaxResponse", false); this.contentTypeFormatters = _Ajax.contentTypeFormatters; } //initialize setup options initialize() { this.loaderPromise = this.table.options.ajaxRequestFunc || _Ajax.defaultLoaderPromise; this.urlGenerator = this.table.options.ajaxURLGenerator || _Ajax.defaultURLGenerator; if (this.table.options.ajaxURL) { this.setUrl(this.table.options.ajaxURL); } this.setDefaultConfig(this.table.options.ajaxConfig); this.registerTableFunction("getAjaxUrl", this.getUrl.bind(this)); this.subscribe("data-loading", this.requestDataCheck.bind(this)); this.subscribe("data-params", this.requestParams.bind(this)); this.subscribe("data-load", this.requestData.bind(this)); } requestParams(data, config, silent, params) { var ajaxParams = this.table.options.ajaxParams; if (ajaxParams) { if (typeof ajaxParams === "function") { ajaxParams = ajaxParams.call(this.table); } params = Object.assign(Object.assign({}, ajaxParams), params); } return params; } requestDataCheck(data, params, config, silent) { return !!(!data && this.url || typeof data === "string"); } requestData(url, params, config, silent, previousData) { var ajaxConfig; if (!previousData && this.requestDataCheck(url)) { if (url) { this.setUrl(url); } ajaxConfig = this.generateConfig(config); return this.sendRequest(this.url, params, ajaxConfig); } else { return previousData; } } setDefaultConfig(config = {}) { this.config = Object.assign({}, _Ajax.defaultConfig); if (typeof config == "string") { this.config.method = config; } else { Object.assign(this.config, config); } } //load config object generateConfig(config = {}) { var ajaxConfig = Object.assign({}, this.config); if (typeof config == "string") { ajaxConfig.method = config; } else { Object.assign(ajaxConfig, config); } return ajaxConfig; } //set request url setUrl(url) { this.url = url; } //get request url getUrl() { return this.url; } //send ajax request sendRequest(url, params, config) { if (this.table.options.ajaxRequesting.call(this.table, url, params) !== false) { return this.loaderPromise(url, config, params).then((data) => { if (this.table.options.ajaxResponse) { data = this.table.options.ajaxResponse.call(this.table, url, params, data); } return data; }); } else { return Promise.reject(); } } }; __publicField(_Ajax, "moduleName", "ajax"); //load defaults __publicField(_Ajax, "defaultConfig", defaultConfig); __publicField(_Ajax, "defaultURLGenerator", urlBuilder); __publicField(_Ajax, "defaultLoaderPromise", defaultLoaderPromise); __publicField(_Ajax, "contentTypeFormatters", defaultContentTypeFormatters); let Ajax = _Ajax; var defaultPasteActions = { replace: function (data) { return this.table.setData(data); }, update: function (data) { return this.table.updateOrAddData(data); }, insert: function (data) { return this.table.addData(data); } }; var defaultPasteParsers = { table: function (clipboard) { var data = [], headerFindSuccess = true, columns = this.table.columnManager.columns, columnMap = [], rows2 = []; clipboard = clipboard.split("\n"); clipboard.forEach(function (row) { data.push(row.split(" ")); }); if (data.length && !(data.length === 1 && data[0].length < 2)) { data[0].forEach(function (value) { var column = columns.find(function (column2) { return value && column2.definition.title && value.trim() && column2.definition.title.trim() === value.trim(); }); if (column) { columnMap.push(column); } else { headerFindSuccess = false; } }); if (!headerFindSuccess) { headerFindSuccess = true; columnMap = []; data[0].forEach(function (value) { var column = columns.find(function (column2) { return value && column2.field && value.trim() && column2.field.trim() === value.trim(); }); if (column) { columnMap.push(column); } else { headerFindSuccess = false; } }); if (!headerFindSuccess) { columnMap = this.table.columnManager.columnsByIndex; } } if (headerFindSuccess) { data.shift(); } data.forEach(function (item) { var row = {}; item.forEach(function (value, i) { if (columnMap[i]) { row[columnMap[i].field] = value; } }); rows2.push(row); }); return rows2; } else { return false; } } }; var bindings$2 = { copyToClipboard: ["ctrl + 67", "meta + 67"] }; var actions$2 = { copyToClipboard: function (e) { if (!this.table.modules.edit.currentCell) { if (this.table.modExists("clipboard", true)) { this.table.modules.clipboard.copy(false, true); } } } }; var extensions$4 = { keybindings: { bindings: bindings$2, actions: actions$2 } }; const _Clipboard = class _Clipboard extends Module { constructor(table) { super(table); this.mode = true; this.pasteParser = function () { }; this.pasteAction = function () { }; this.customSelection = false; this.rowRange = false; this.blocked = true; this.registerTableOption("clipboard", false); this.registerTableOption("clipboardCopyStyled", true); this.registerTableOption("clipboardCopyConfig", false); this.registerTableOption("clipboardCopyFormatter", false); this.registerTableOption("clipboardCopyRowRange", "active"); this.registerTableOption("clipboardPasteParser", "table"); this.registerTableOption("clipboardPasteAction", "insert"); this.registerColumnOption("clipboard"); this.registerColumnOption("titleClipboard"); } initialize() { this.mode = this.table.options.clipboard; this.rowRange = this.table.options.clipboardCopyRowRange; if (this.mode === true || this.mode === "copy") { this.table.element.addEventListener("copy", (e) => { var plain, html2, list2; if (!this.blocked) { e.preventDefault(); if (this.customSelection) { plain = this.customSelection; if (this.table.options.clipboardCopyFormatter) { plain = this.table.options.clipboardCopyFormatter("plain", plain); } } else { list2 = this.table.modules.export.generateExportList(this.table.options.clipboardCopyConfig, this.table.options.clipboardCopyStyled, this.rowRange, "clipboard"); html2 = this.table.modules.export.generateHTMLTable(list2); plain = html2 ? this.generatePlainContent(list2) : ""; if (this.table.options.clipboardCopyFormatter) { plain = this.table.options.clipboardCopyFormatter("plain", plain); html2 = this.table.options.clipboardCopyFormatter("html", html2); } } if (window.clipboardData && window.clipboardData.setData) { window.clipboardData.setData("Text", plain); } else if (e.clipboardData && e.clipboardData.setData) { e.clipboardData.setData("text/plain", plain); if (html2) { e.clipboardData.setData("text/html", html2); } } else if (e.originalEvent && e.originalEvent.clipboardData.setData) { e.originalEvent.clipboardData.setData("text/plain", plain); if (html2) { e.originalEvent.clipboardData.setData("text/html", html2); } } this.dispatchExternal("clipboardCopied", plain, html2); this.reset(); } }); } if (this.mode === true || this.mode === "paste") { this.table.element.addEventListener("paste", (e) => { this.paste(e); }); } this.setPasteParser(this.table.options.clipboardPasteParser); this.setPasteAction(this.table.options.clipboardPasteAction); this.registerTableFunction("copyToClipboard", this.copy.bind(this)); } reset() { this.blocked = true; this.customSelection = false; } generatePlainContent(list2) { var output = []; list2.forEach((row) => { var rowData = []; row.columns.forEach((col) => { var value = ""; if (col) { if (row.type === "group") { col.value = col.component.getKey(); } if (col.value === null) { value = ""; } else { switch (typeof col.value) { case "object": value = JSON.stringify(col.value); break; case "undefined": value = ""; break; default: value = col.value; } } } rowData.push(value); }); output.push(rowData.join(" ")); }); return output.join("\n"); } copy(range2, internal) { var sel, textRange; this.blocked = false; this.customSelection = false; if (this.mode === true || this.mode === "copy") { this.rowRange = range2 || this.table.options.clipboardCopyRowRange; if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") { range2 = document.createRange(); range2.selectNodeContents(this.table.element); sel = window.getSelection(); if (sel.toString() && internal) { this.customSelection = sel.toString(); } sel.removeAllRanges(); sel.addRange(range2); } else if (typeof document.selection != "undefined" && typeof document.body.createTextRange != "undefined") { textRange = document.body.createTextRange(); textRange.moveToElementText(this.table.element); textRange.select(); } document.execCommand("copy"); if (sel) { sel.removeAllRanges(); } } } //PASTE EVENT HANDLING setPasteAction(action) { switch (typeof action) { case "string": this.pasteAction = _Clipboard.pasteActions[action]; if (!this.pasteAction) { console.warn("Clipboard Error - No such paste action found:", action); } break; case "function": this.pasteAction = action; break; } } setPasteParser(parser) { switch (typeof parser) { case "string": this.pasteParser = _Clipboard.pasteParsers[parser]; if (!this.pasteParser) { console.warn("Clipboard Error - No such paste parser found:", parser); } break; case "function": this.pasteParser = parser; break; } } paste(e) { var data, rowData, rows2; if (this.checkPasteOrigin(e)) { data = this.getPasteData(e); rowData = this.pasteParser.call(this, data); if (rowData) { e.preventDefault(); if (this.table.modExists("mutator")) { rowData = this.mutateData(rowData); } rows2 = this.pasteAction.call(this, rowData); this.dispatchExternal("clipboardPasted", data, rowData, rows2); } else { this.dispatchExternal("clipboardPasteError", data); } } } mutateData(data) { var output = []; if (Array.isArray(data)) { data.forEach((row) => { output.push(this.table.modules.mutator.transformRow(row, "clipboard")); }); } else { output = data; } return output; } checkPasteOrigin(e) { var valid = true; var blocked = this.confirm("clipboard-paste", [e]); if (blocked || !["DIV", "SPAN"].includes(e.target.tagName)) { valid = false; } return valid; } getPasteData(e) { var data; if (window.clipboardData && window.clipboardData.getData) { data = window.clipboardData.getData("Text"); } else if (e.clipboardData && e.clipboardData.getData) { data = e.clipboardData.getData("text/plain"); } else if (e.originalEvent && e.originalEvent.clipboardData.getData) { data = e.originalEvent.clipboardData.getData("text/plain"); } return data; } }; __publicField(_Clipboard, "moduleName", "clipboard"); __publicField(_Clipboard, "moduleExtensions", extensions$4); //load defaults __publicField(_Clipboard, "pasteActions", defaultPasteActions); __publicField(_Clipboard, "pasteParsers", defaultPasteParsers); let Clipboard = _Clipboard; class CalcComponent { constructor(row) { this._row = row; return new Proxy(this, { get: function (target, name2, receiver) { if (typeof target[name2] !== "undefined") { return target[name2]; } else { return target._row.table.componentFunctionBinder.handle("row", target._row, name2); } } }); } getData(transform) { return this._row.getData(transform); } getElement() { return this._row.getElement(); } getTable() { return this._row.table; } getCells() { var cells = []; this._row.getCells().forEach(function (cell) { cells.push(cell.getComponent()); }); return cells; } getCell(column) { var cell = this._row.getCell(column); return cell ? cell.getComponent() : false; } _getSelf() { return this._row; } } class CellComponent { constructor(cell) { this._cell = cell; return new Proxy(this, { get: function (target, name2, receiver) { if (typeof target[name2] !== "undefined") { return target[name2]; } else { return target._cell.table.componentFunctionBinder.handle("cell", target._cell, name2); } } }); } getValue() { return this._cell.getValue(); } getOldValue() { return this._cell.getOldValue(); } getInitialValue() { return this._cell.initialValue; } getElement() { return this._cell.getElement(); } getRow() { return this._cell.row.getComponent(); } getData(transform) { return this._cell.row.getData(transform); } getType() { return "cell"; } getField() { return this._cell.column.getField(); } getColumn() { return this._cell.column.getComponent(); } setValue(value, mutate) { if (typeof mutate == "undefined") { mutate = true; } this._cell.setValue(value, mutate); } restoreOldValue() { this._cell.setValueActual(this._cell.getOldValue()); } restoreInitialValue() { this._cell.setValueActual(this._cell.initialValue); } checkHeight() { this._cell.checkHeight(); } getTable() { return this._cell.table; } _getSelf() { return this._cell; } } class Cell extends CoreFeature { constructor(column, row) { super(column.table); this.table = column.table; this.column = column; this.row = row; this.element = null; this.value = null; this.initialValue; this.oldValue = null; this.modules = {}; this.height = null; this.width = null; this.minWidth = null; this.component = null; this.loaded = false; this.build(); } //////////////// Setup Functions ///////////////// //generate element build() { this.generateElement(); this.setWidth(); this._configureCell(); this.setValueActual(this.column.getFieldValue(this.row.data)); this.initialValue = this.value; } generateElement() { this.element = document.createElement("div"); this.element.className = "tabulator-cell"; this.element.setAttribute("role", "gridcell"); if (this.column.isRowHeader) { this.element.classList.add("tabulator-row-header"); } } _configureCell() { var element = this.element, field = this.column.getField(), vertAligns = { top: "flex-start", bottom: "flex-end", middle: "center" }, hozAligns = { left: "flex-start", right: "flex-end", center: "center" }; element.style.textAlign = this.column.hozAlign; if (this.column.vertAlign) { element.style.display = "inline-flex"; element.style.alignItems = vertAligns[this.column.vertAlign] || ""; if (this.column.hozAlign) { element.style.justifyContent = hozAligns[this.column.hozAlign] || ""; } } if (field) { element.setAttribute("tabulator-field", field); } if (this.column.definition.cssClass) { var classNames = this.column.definition.cssClass.split(" "); classNames.forEach((className) => { element.classList.add(className); }); } this.dispatch("cell-init", this); if (!this.column.visible) { this.hide(); } } //generate cell contents _generateContents() { var val; val = this.chain("cell-format", this, null, () => { return this.element.innerHTML = this.value; }); switch (typeof val) { case "object": if (val instanceof Node) { while (this.element.firstChild) this.element.removeChild(this.element.firstChild); this.element.appendChild(val); } else { this.element.innerHTML = ""; if (val != null) { console.warn("Format Error - Formatter has returned a type of object, the only valid formatter object return is an instance of Node, the formatter returned:", val); } } break; case "undefined": this.element.innerHTML = ""; break; default: this.element.innerHTML = val; } } cellRendered() { this.dispatch("cell-rendered", this); } //////////////////// Getters //////////////////// getElement(containerOnly) { if (!this.loaded) { this.loaded = true; if (!containerOnly) { this.layoutElement(); } } return this.element; } getValue() { return this.value; } getOldValue() { return this.oldValue; } //////////////////// Actions //////////////////// setValue(value, mutate, force) { var changed = this.setValueProcessData(value, mutate, force); if (changed) { this.dispatch("cell-value-updated", this); this.cellRendered(); if (this.column.definition.cellEdited) { this.column.definition.cellEdited.call(this.table, this.getComponent()); } this.dispatchExternal("cellEdited", this.getComponent()); if (this.subscribedExternal("dataChanged")) { this.dispatchExternal("dataChanged", this.table.rowManager.getData()); } } } setValueProcessData(value, mutate, force) { var changed = false; if (this.value !== value || force) { changed = true; if (mutate) { value = this.chain("cell-value-changing", [this, value], null, value); } } this.setValueActual(value); if (changed) { this.dispatch("cell-value-changed", this); } return changed; } setValueActual(value) { this.oldValue = this.value; this.value = value; this.dispatch("cell-value-save-before", this); this.column.setFieldValue(this.row.data, value); this.dispatch("cell-value-save-after", this); if (this.loaded) { this.layoutElement(); } } layoutElement() { this._generateContents(); this.dispatch("cell-layout", this); } setWidth() { this.width = this.column.width; this.element.style.width = this.column.widthStyled; } clearWidth() { this.width = ""; this.element.style.width = ""; } getWidth() { return this.width || this.element.offsetWidth; } setMinWidth() { this.minWidth = this.column.minWidth; this.element.style.minWidth = this.column.minWidthStyled; } setMaxWidth() { this.maxWidth = this.column.maxWidth; this.element.style.maxWidth = this.column.maxWidthStyled; } checkHeight() { this.row.reinitializeHeight(); } clearHeight() { this.element.style.height = ""; this.height = null; this.dispatch("cell-height", this, ""); } setHeight() { this.height = this.row.height; this.element.style.height = this.row.heightStyled; this.dispatch("cell-height", this, this.row.heightStyled); } getHeight() { return this.height || this.element.offsetHeight; } show() { this.element.style.display = this.column.vertAlign ? "inline-flex" : ""; } hide() { this.element.style.display = "none"; } delete() { this.dispatch("cell-delete", this); if (!this.table.rowManager.redrawBlock && this.element.parentNode) { this.element.parentNode.removeChild(this.element); } this.element = false; this.column.deleteCell(this); this.row.deleteCell(this); this.calcs = {}; } getIndex() { return this.row.getCellIndex(this); } //////////////// Object Generation ///////////////// getComponent() { if (!this.component) { this.component = new CellComponent(this); } return this.component; } } class ColumnComponent { constructor(column) { this._column = column; this.type = "ColumnComponent"; return new Proxy(this, { get: function (target, name2, receiver) { if (typeof target[name2] !== "undefined") { return target[name2]; } else { return target._column.table.componentFunctionBinder.handle("column", target._column, name2); } } }); } getElement() { return this._column.getElement(); } getDefinition() { return this._column.getDefinition(); } getField() { return this._column.getField(); } getTitleDownload() { return this._column.getTitleDownload(); } getCells() { var cells = []; this._column.cells.forEach(function (cell) { cells.push(cell.getComponent()); }); return cells; } isVisible() { return this._column.visible; } show() { if (this._column.isGroup) { this._column.columns.forEach(function (column) { column.show(); }); } else { this._column.show(); } } hide() { if (this._column.isGroup) { this._column.columns.forEach(function (column) { column.hide(); }); } else { this._column.hide(); } } toggle() { if (this._column.visible) { this.hide(); } else { this.show(); } } delete() { return this._column.delete(); } getSubColumns() { var output = []; if (this._column.columns.length) { this._column.columns.forEach(function (column) { output.push(column.getComponent()); }); } return output; } getParentColumn() { return this._column.getParentComponent(); } _getSelf() { return this._column; } scrollTo(position, ifVisible) { return this._column.table.columnManager.scrollToColumn(this._column, position, ifVisible); } getTable() { return this._column.table; } move(to, after) { var toColumn = this._column.table.columnManager.findColumn(to); if (toColumn) { this._column.table.columnManager.moveColumn(this._column, toColumn, after); } else { console.warn("Move Error - No matching column found:", toColumn); } } getNextColumn() { var nextCol = this._column.nextColumn(); return nextCol ? nextCol.getComponent() : false; } getPrevColumn() { var prevCol = this._column.prevColumn(); return prevCol ? prevCol.getComponent() : false; } updateDefinition(updates) { return this._column.updateDefinition(updates); } getWidth() { return this._column.getWidth(); } setWidth(width) { var result; if (width === true) { result = this._column.reinitializeWidth(true); } else { result = this._column.setWidth(width); } this._column.table.columnManager.rerenderColumns(true); return result; } } var defaultColumnOptions = { "title": void 0, "field": void 0, "columns": void 0, "visible": void 0, "hozAlign": void 0, "vertAlign": void 0, "width": void 0, "minWidth": 40, "maxWidth": void 0, "maxInitialWidth": void 0, "cssClass": void 0, "variableHeight": void 0, "headerVertical": void 0, "headerHozAlign": void 0, "headerWordWrap": false, "editableTitle": void 0 }; const _Column = class _Column extends CoreFeature { constructor(def, parent2, rowHeader) { super(parent2.table); this.definition = def; this.parent = parent2; this.type = "column"; this.columns = []; this.cells = []; this.isGroup = false; this.isRowHeader = rowHeader; this.element = this.createElement(); this.contentElement = false; this.titleHolderElement = false; this.titleElement = false; this.groupElement = this.createGroupElement(); this.hozAlign = ""; this.vertAlign = ""; this.field = ""; this.fieldStructure = ""; this.getFieldValue = ""; this.setFieldValue = ""; this.titleDownload = null; this.titleFormatterRendered = false; this.mapDefinitions(); this.setField(this.definition.field); this.modules = {}; this.width = null; this.widthStyled = ""; this.maxWidth = null; this.maxWidthStyled = ""; this.maxInitialWidth = null; this.minWidth = null; this.minWidthStyled = ""; this.widthFixed = false; this.visible = true; this.component = null; if (this.definition.columns) { this.isGroup = true; this.definition.columns.forEach((def2, i) => { var newCol = new _Column(def2, this); this.attachColumn(newCol); }); this.checkColumnVisibility(); } else { parent2.registerColumnField(this); } this._initialize(); } createElement() { var el = document.createElement("div"); el.classList.add("tabulator-col"); el.setAttribute("role", "columnheader"); el.setAttribute("aria-sort", "none"); if (this.isRowHeader) { el.classList.add("tabulator-row-header"); } switch (this.table.options.columnHeaderVertAlign) { case "middle": el.style.justifyContent = "center"; break; case "bottom": el.style.justifyContent = "flex-end"; break; } return el; } createGroupElement() { var el = document.createElement("div"); el.classList.add("tabulator-col-group-cols"); return el; } mapDefinitions() { var defaults = this.table.options.columnDefaults; if (defaults) { for (let key in defaults) { if (typeof this.definition[key] === "undefined") { this.definition[key] = defaults[key]; } } } this.definition = this.table.columnManager.optionsList.generate(_Column.defaultOptionList, this.definition); } checkDefinition() { Object.keys(this.definition).forEach((key) => { if (_Column.defaultOptionList.indexOf(key) === -1) { console.warn("Invalid column definition option in '" + (this.field || this.definition.title) + "' column:", key); } }); } setField(field) { this.field = field; this.fieldStructure = field ? this.table.options.nestedFieldSeparator ? field.split(this.table.options.nestedFieldSeparator) : [field] : []; this.getFieldValue = this.fieldStructure.length > 1 ? this._getNestedData : this._getFlatData; this.setFieldValue = this.fieldStructure.length > 1 ? this._setNestedData : this._setFlatData; } //register column position with column manager registerColumnPosition(column) { this.parent.registerColumnPosition(column); } //register column position with column manager registerColumnField(column) { this.parent.registerColumnField(column); } //trigger position registration reRegisterPosition() { if (this.isGroup) { this.columns.forEach(function (column) { column.reRegisterPosition(); }); } else { this.registerColumnPosition(this); } } //build header element _initialize() { var def = this.definition; while (this.element.firstChild) this.element.removeChild(this.element.firstChild); if (def.headerVertical) { this.element.classList.add("tabulator-col-vertical"); if (def.headerVertical === "flip") { this.element.classList.add("tabulator-col-vertical-flip"); } } this.contentElement = this._buildColumnHeaderContent(); this.element.appendChild(this.contentElement); if (this.isGroup) { this._buildGroupHeader(); } else { this._buildColumnHeader(); } this.dispatch("column-init", this); } //build header element for header _buildColumnHeader() { var def = this.definition; this.dispatch("column-layout", this); if (typeof def.visible != "undefined") { if (def.visible) { this.show(true); } else { this.hide(true); } } if (def.cssClass) { var classNames = def.cssClass.split(" "); classNames.forEach((className) => { this.element.classList.add(className); }); } if (def.field) { this.element.setAttribute("tabulator-field", def.field); } this.setMinWidth(parseInt(def.minWidth)); if (def.maxInitialWidth) { this.maxInitialWidth = parseInt(def.maxInitialWidth); } if (def.maxWidth) { this.setMaxWidth(parseInt(def.maxWidth)); } this.reinitializeWidth(); this.hozAlign = this.definition.hozAlign; this.vertAlign = this.definition.vertAlign; this.titleElement.style.textAlign = this.definition.headerHozAlign; } _buildColumnHeaderContent() { var contentElement = document.createElement("div"); contentElement.classList.add("tabulator-col-content"); this.titleHolderElement = document.createElement("div"); this.titleHolderElement.classList.add("tabulator-col-title-holder"); contentElement.appendChild(this.titleHolderElement); this.titleElement = this._buildColumnHeaderTitle(); this.titleHolderElement.appendChild(this.titleElement); return contentElement; } //build title element of column _buildColumnHeaderTitle() { var def = this.definition; var titleHolderElement = document.createElement("div"); titleHolderElement.classList.add("tabulator-col-title"); if (def.headerWordWrap) { titleHolderElement.classList.add("tabulator-col-title-wrap"); } if (def.editableTitle) { var titleElement = document.createElement("input"); titleElement.classList.add("tabulator-title-editor"); titleElement.addEventListener("click", (e) => { e.stopPropagation(); titleElement.focus(); }); titleElement.addEventListener("mousedown", (e) => { e.stopPropagation(); }); titleElement.addEventListener("change", () => { def.title = titleElement.value; this.dispatchExternal("columnTitleChanged", this.getComponent()); }); titleHolderElement.appendChild(titleElement); if (def.field) { this.langBind("columns|" + def.field, (text) => { titleElement.value = text || (def.title || " "); }); } else { titleElement.value = def.title || " "; } } else { if (def.field) { this.langBind("columns|" + def.field, (text) => { this._formatColumnHeaderTitle(titleHolderElement, text || (def.title || " ")); }); } else { this._formatColumnHeaderTitle(titleHolderElement, def.title || " "); } } return titleHolderElement; } _formatColumnHeaderTitle(el, title) { var contents = this.chain("column-format", [this, title, el], null, () => { return title; }); switch (typeof contents) { case "object": if (contents instanceof Node) { el.appendChild(contents); } else { el.innerHTML = ""; console.warn("Format Error - Title formatter has returned a type of object, the only valid formatter object return is an instance of Node, the formatter returned:", contents); } break; case "undefined": el.innerHTML = ""; break; default: el.innerHTML = contents; } } //build header element for column group _buildGroupHeader() { this.element.classList.add("tabulator-col-group"); this.element.setAttribute("role", "columngroup"); this.element.setAttribute("aria-title", this.definition.title); if (this.definition.cssClass) { var classNames = this.definition.cssClass.split(" "); classNames.forEach((className) => { this.element.classList.add(className); }); } this.titleElement.style.textAlign = this.definition.headerHozAlign; this.element.appendChild(this.groupElement); } //flat field lookup _getFlatData(data) { return data[this.field]; } //nested field lookup _getNestedData(data) { var dataObj = data, structure = this.fieldStructure, length = structure.length, output; for (let i = 0; i < length; i++) { dataObj = dataObj[structure[i]]; output = dataObj; if (!dataObj) { break; } } return output; } //flat field set _setFlatData(data, value) { if (this.field) { data[this.field] = value; } } //nested field set _setNestedData(data, value) { var dataObj = data, structure = this.fieldStructure, length = structure.length; for (let i = 0; i < length; i++) { if (i == length - 1) { dataObj[structure[i]] = value; } else { if (!dataObj[structure[i]]) { if (typeof value !== "undefined") { dataObj[structure[i]] = {}; } else { break; } } dataObj = dataObj[structure[i]]; } } } //attach column to this group attachColumn(column) { if (this.groupElement) { this.columns.push(column); this.groupElement.appendChild(column.getElement()); column.columnRendered(); } else { console.warn("Column Warning - Column being attached to another column instead of column group"); } } //vertically align header in column verticalAlign(alignment, height) { var parentHeight = this.parent.isGroup ? this.parent.getGroupElement().clientHeight : height || this.parent.getHeadersElement().clientHeight; this.element.style.height = parentHeight + "px"; this.dispatch("column-height", this, this.element.style.height); if (this.isGroup) { this.groupElement.style.minHeight = parentHeight - this.contentElement.offsetHeight + "px"; } this.columns.forEach(function (column) { column.verticalAlign(alignment); }); } //clear vertical alignment clearVerticalAlign() { this.element.style.paddingTop = ""; this.element.style.height = ""; this.element.style.minHeight = ""; this.groupElement.style.minHeight = ""; this.columns.forEach(function (column) { column.clearVerticalAlign(); }); this.dispatch("column-height", this, ""); } //// Retrieve Column Information //// //return column header element getElement() { return this.element; } //return column group element getGroupElement() { return this.groupElement; } //return field name getField() { return this.field; } getTitleDownload() { return this.titleDownload; } //return the first column in a group getFirstColumn() { if (!this.isGroup) { return this; } else { if (this.columns.length) { return this.columns[0].getFirstColumn(); } else { return false; } } } //return the last column in a group getLastColumn() { if (!this.isGroup) { return this; } else { if (this.columns.length) { return this.columns[this.columns.length - 1].getLastColumn(); } else { return false; } } } //return all columns in a group getColumns(traverse) { var columns = []; if (traverse) { this.columns.forEach((column) => { columns.push(column); columns = columns.concat(column.getColumns(true)); }); } else { columns = this.columns; } return columns; } //return all columns in a group getCells() { return this.cells; } //retrieve the top column in a group of columns getTopColumn() { if (this.parent.isGroup) { return this.parent.getTopColumn(); } else { return this; } } //return column definition object getDefinition(updateBranches) { var colDefs = []; if (this.isGroup && updateBranches) { this.columns.forEach(function (column) { colDefs.push(column.getDefinition(true)); }); this.definition.columns = colDefs; } return this.definition; } //////////////////// Actions //////////////////// checkColumnVisibility() { var visible = false; this.columns.forEach(function (column) { if (column.visible) { visible = true; } }); if (visible) { this.show(); this.dispatchExternal("columnVisibilityChanged", this.getComponent(), false); } else { this.hide(); } } //show column show(silent, responsiveToggle) { if (!this.visible) { this.visible = true; this.element.style.display = ""; if (this.parent.isGroup) { this.parent.checkColumnVisibility(); } this.cells.forEach(function (cell) { cell.show(); }); if (!this.isGroup && this.width === null) { this.reinitializeWidth(); } this.table.columnManager.verticalAlignHeaders(); this.dispatch("column-show", this, responsiveToggle); if (!silent) { this.dispatchExternal("columnVisibilityChanged", this.getComponent(), true); } if (this.parent.isGroup) { this.parent.matchChildWidths(); } if (!this.silent) { this.table.columnManager.rerenderColumns(); } } } //hide column hide(silent, responsiveToggle) { if (this.visible) { this.visible = false; this.element.style.display = "none"; this.table.columnManager.verticalAlignHeaders(); if (this.parent.isGroup) { this.parent.checkColumnVisibility(); } this.cells.forEach(function (cell) { cell.hide(); }); this.dispatch("column-hide", this, responsiveToggle); if (!silent) { this.dispatchExternal("columnVisibilityChanged", this.getComponent(), false); } if (this.parent.isGroup) { this.parent.matchChildWidths(); } if (!this.silent) { this.table.columnManager.rerenderColumns(); } } } matchChildWidths() { var childWidth = 0; if (this.contentElement && this.columns.length) { this.columns.forEach(function (column) { if (column.visible) { childWidth += column.getWidth(); } }); this.contentElement.style.maxWidth = childWidth - 1 + "px"; if (this.table.initialized) { this.element.style.width = childWidth + "px"; } if (this.parent.isGroup) { this.parent.matchChildWidths(); } } } removeChild(child) { var index = this.columns.indexOf(child); if (index > -1) { this.columns.splice(index, 1); } if (!this.columns.length) { this.delete(); } } setWidth(width) { this.widthFixed = true; this.setWidthActual(width); } setWidthActual(width) { if (isNaN(width)) { width = Math.floor(this.table.element.clientWidth / 100 * parseInt(width)); } width = Math.max(this.minWidth, width); if (this.maxWidth) { width = Math.min(this.maxWidth, width); } this.width = width; this.widthStyled = width ? width + "px" : ""; this.element.style.width = this.widthStyled; if (!this.isGroup) { this.cells.forEach(function (cell) { cell.setWidth(); }); } if (this.parent.isGroup) { this.parent.matchChildWidths(); } this.dispatch("column-width", this); if (this.subscribedExternal("columnWidth")) { this.dispatchExternal("columnWidth", this.getComponent()); } } checkCellHeights() { var rows2 = []; this.cells.forEach(function (cell) { if (cell.row.heightInitialized) { if (cell.row.getElement().offsetParent !== null) { rows2.push(cell.row); cell.row.clearCellHeight(); } else { cell.row.heightInitialized = false; } } }); rows2.forEach(function (row) { row.calcHeight(); }); rows2.forEach(function (row) { row.setCellHeight(); }); } getWidth() { var width = 0; if (this.isGroup) { this.columns.forEach(function (column) { if (column.visible) { width += column.getWidth(); } }); } else { width = this.width; } return width; } getLeftOffset() { var offset = this.element.offsetLeft; if (this.parent.isGroup) { offset += this.parent.getLeftOffset(); } return offset; } getHeight() { return Math.ceil(this.element.getBoundingClientRect().height); } setMinWidth(minWidth) { if (this.maxWidth && minWidth > this.maxWidth) { minWidth = this.maxWidth; console.warn("the minWidth (" + minWidth + "px) for column '" + this.field + "' cannot be bigger that its maxWidth (" + this.maxWidthStyled + ")"); } this.minWidth = minWidth; this.minWidthStyled = minWidth ? minWidth + "px" : ""; this.element.style.minWidth = this.minWidthStyled; this.cells.forEach(function (cell) { cell.setMinWidth(); }); } setMaxWidth(maxWidth) { if (this.minWidth && maxWidth < this.minWidth) { maxWidth = this.minWidth; console.warn("the maxWidth (" + maxWidth + "px) for column '" + this.field + "' cannot be smaller that its minWidth (" + this.minWidthStyled + ")"); } this.maxWidth = maxWidth; this.maxWidthStyled = maxWidth ? maxWidth + "px" : ""; this.element.style.maxWidth = this.maxWidthStyled; this.cells.forEach(function (cell) { cell.setMaxWidth(); }); } delete() { return new Promise((resolve, reject) => { if (this.isGroup) { this.columns.forEach(function (column) { column.delete(); }); } this.dispatch("column-delete", this); var cellCount = this.cells.length; for (let i = 0; i < cellCount; i++) { this.cells[0].delete(); } if (this.element.parentNode) { this.element.parentNode.removeChild(this.element); } this.element = false; this.contentElement = false; this.titleElement = false; this.groupElement = false; if (this.parent.isGroup) { this.parent.removeChild(this); } this.table.columnManager.deregisterColumn(this); this.table.columnManager.rerenderColumns(true); this.dispatch("column-deleted", this); resolve(); }); } columnRendered() { if (this.titleFormatterRendered) { this.titleFormatterRendered(); } this.dispatch("column-rendered", this); } //////////////// Cell Management ///////////////// //generate cell for this column generateCell(row) { var cell = new Cell(this, row); this.cells.push(cell); return cell; } nextColumn() { var index = this.table.columnManager.findColumnIndex(this); return index > -1 ? this._nextVisibleColumn(index + 1) : false; } _nextVisibleColumn(index) { var column = this.table.columnManager.getColumnByIndex(index); return !column || column.visible ? column : this._nextVisibleColumn(index + 1); } prevColumn() { var index = this.table.columnManager.findColumnIndex(this); return index > -1 ? this._prevVisibleColumn(index - 1) : false; } _prevVisibleColumn(index) { var column = this.table.columnManager.getColumnByIndex(index); return !column || column.visible ? column : this._prevVisibleColumn(index - 1); } reinitializeWidth(force) { this.widthFixed = false; if (typeof this.definition.width !== "undefined" && !force) { this.setWidth(this.definition.width); } this.dispatch("column-width-fit-before", this); this.fitToData(force); this.dispatch("column-width-fit-after", this); } //set column width to maximum cell width for non group columns fitToData(force) { if (this.isGroup) { return; } if (!this.widthFixed) { this.element.style.width = ""; this.cells.forEach((cell) => { cell.clearWidth(); }); } var maxWidth = this.element.offsetWidth; if (!this.width || !this.widthFixed) { this.cells.forEach((cell) => { var width = cell.getWidth(); if (width > maxWidth) { maxWidth = width; } }); if (maxWidth) { var setTo = maxWidth + 1; if (force) { this.setWidth(setTo); } else { if (this.maxInitialWidth && !force) { setTo = Math.min(setTo, this.maxInitialWidth); } this.setWidthActual(setTo); } } } } updateDefinition(updates) { var definition; if (!this.isGroup) { if (!this.parent.isGroup) { definition = Object.assign({}, this.getDefinition()); definition = Object.assign(definition, updates); return this.table.columnManager.addColumn(definition, false, this).then((column) => { if (definition.field == this.field) { this.field = false; } return this.delete().then(() => { return column.getComponent(); }); }); } else { console.error("Column Update Error - The updateDefinition function is only available on ungrouped columns"); return Promise.reject("Column Update Error - The updateDefinition function is only available on columns, not column groups"); } } else { console.error("Column Update Error - The updateDefinition function is only available on ungrouped columns"); return Promise.reject("Column Update Error - The updateDefinition function is only available on columns, not column groups"); } } deleteCell(cell) { var index = this.cells.indexOf(cell); if (index > -1) { this.cells.splice(index, 1); } } //////////////// Object Generation ///////////////// getComponent() { if (!this.component) { this.component = new ColumnComponent(this); } return this.component; } getPosition() { return this.table.columnManager.getVisibleColumnsByIndex().indexOf(this) + 1; } getParentComponent() { return this.parent instanceof _Column ? this.parent.getComponent() : false; } }; __publicField(_Column, "defaultOptionList", defaultColumnOptions); let Column = _Column; class RowComponent { constructor(row) { this._row = row; return new Proxy(this, { get: function (target, name2, receiver) { if (typeof target[name2] !== "undefined") { return target[name2]; } else { return target._row.table.componentFunctionBinder.handle("row", target._row, name2); } } }); } getData(transform) { return this._row.getData(transform); } getElement() { return this._row.getElement(); } getCells() { var cells = []; this._row.getCells().forEach(function (cell) { cells.push(cell.getComponent()); }); return cells; } getCell(column) { var cell = this._row.getCell(column); return cell ? cell.getComponent() : false; } getIndex() { return this._row.getData("data")[this._row.table.options.index]; } getPosition() { return this._row.getPosition(); } watchPosition(callback) { return this._row.watchPosition(callback); } delete() { return this._row.delete(); } scrollTo(position, ifVisible) { return this._row.table.rowManager.scrollToRow(this._row, position, ifVisible); } move(to, after) { this._row.moveToRow(to, after); } update(data) { return this._row.updateData(data); } normalizeHeight() { this._row.normalizeHeight(true); } _getSelf() { return this._row; } reformat() { return this._row.reinitialize(); } getTable() { return this._row.table; } getNextRow() { var row = this._row.nextRow(); return row ? row.getComponent() : row; } getPrevRow() { var row = this._row.prevRow(); return row ? row.getComponent() : row; } } class Row extends CoreFeature { constructor(data, parent2, type = "row") { super(parent2.table); this.parent = parent2; this.data = {}; this.type = type; this.element = false; this.modules = {}; this.cells = []; this.height = 0; this.heightStyled = ""; this.manualHeight = false; this.outerHeight = 0; this.initialized = false; this.heightInitialized = false; this.position = 0; this.positionWatchers = []; this.component = null; this.created = false; this.setData(data); } create() { if (!this.created) { this.created = true; this.generateElement(); } } createElement() { var el = document.createElement("div"); el.classList.add("tabulator-row"); el.setAttribute("role", "row"); this.element = el; } getElement() { this.create(); return this.element; } detachElement() { if (this.element && this.element.parentNode) { this.element.parentNode.removeChild(this.element); } } generateElement() { this.createElement(); this.dispatch("row-init", this); } generateCells() { this.cells = this.table.columnManager.generateCells(this); } //functions to setup on first render initialize(force, inFragment) { this.create(); if (!this.initialized || force) { this.deleteCells(); while (this.element.firstChild) this.element.removeChild(this.element.firstChild); this.dispatch("row-layout-before", this); this.generateCells(); this.initialized = true; this.table.columnManager.renderer.renderRowCells(this, inFragment); if (force) { this.normalizeHeight(); } this.dispatch("row-layout", this); if (this.table.options.rowFormatter) { this.table.options.rowFormatter(this.getComponent()); } this.dispatch("row-layout-after", this); } else { this.table.columnManager.renderer.rerenderRowCells(this, inFragment); } } rendered() { this.cells.forEach((cell) => { cell.cellRendered(); }); } reinitializeHeight() { this.heightInitialized = false; if (this.element && this.element.offsetParent !== null) { this.normalizeHeight(true); } } deinitialize() { this.initialized = false; } deinitializeHeight() { this.heightInitialized = false; } reinitialize(children) { this.initialized = false; this.heightInitialized = false; if (!this.manualHeight) { this.height = 0; this.heightStyled = ""; } if (this.element && this.element.offsetParent !== null) { this.initialize(true); } this.dispatch("row-relayout", this); } //get heights when doing bulk row style calcs in virtual DOM calcHeight(force) { var maxHeight = 0, minHeight = 0; if (this.table.options.rowHeight) { this.height = this.table.options.rowHeight; } else { minHeight = this.calcMinHeight(); maxHeight = this.calcMaxHeight(); if (force) { this.height = Math.max(maxHeight, minHeight); } else { this.height = this.manualHeight ? this.height : Math.max(maxHeight, minHeight); } } this.heightStyled = this.height ? this.height + "px" : ""; this.outerHeight = this.element.offsetHeight; } calcMinHeight() { return this.table.options.resizableRows ? this.element.clientHeight : 0; } calcMaxHeight() { var maxHeight = 0; this.cells.forEach(function (cell) { var height = cell.getHeight(); if (height > maxHeight) { maxHeight = height; } }); return maxHeight; } //set of cells setCellHeight() { this.cells.forEach(function (cell) { cell.setHeight(); }); this.heightInitialized = true; } clearCellHeight() { this.cells.forEach(function (cell) { cell.clearHeight(); }); } //normalize the height of elements in the row normalizeHeight(force) { if (force && !this.table.options.rowHeight) { this.clearCellHeight(); } this.calcHeight(force); this.setCellHeight(); } //set height of rows setHeight(height, force) { if (this.height != height || force) { this.manualHeight = true; this.height = height; this.heightStyled = height ? height + "px" : ""; this.setCellHeight(); this.outerHeight = this.element.offsetHeight; if (this.subscribedExternal("rowHeight")) { this.dispatchExternal("rowHeight", this.getComponent()); } } } //return rows outer height getHeight() { return this.outerHeight; } //return rows outer Width getWidth() { return this.element.offsetWidth; } //////////////// Cell Management ///////////////// deleteCell(cell) { var index = this.cells.indexOf(cell); if (index > -1) { this.cells.splice(index, 1); } } //////////////// Data Management ///////////////// setData(data) { this.data = this.chain("row-data-init-before", [this, data], void 0, data); this.dispatch("row-data-init-after", this); } //update the rows data updateData(updatedData) { var visible = this.element && Helpers.elVisible(this.element), tempData = {}, newRowData; return new Promise((resolve, reject) => { if (typeof updatedData === "string") { updatedData = JSON.parse(updatedData); } this.dispatch("row-data-save-before", this); if (this.subscribed("row-data-changing")) { tempData = Object.assign(tempData, this.data); tempData = Object.assign(tempData, updatedData); } newRowData = this.chain("row-data-changing", [this, tempData, updatedData], null, updatedData); const cellsToUpdate = []; for (let attrname in updatedData) { let columns = this.table.columnManager.getColumnsByFieldRoot(attrname); columns.forEach((column) => { let cell = this.getCell(column.getField()); if (cell) { let value = column.getFieldValue(newRowData); if (cell.getValue() !== value) { cellsToUpdate.push([cell, value]); } } }); } for (let attrname in newRowData) { this.data[attrname] = newRowData[attrname]; } this.dispatch("row-data-save-after", this); cellsToUpdate.forEach(([cell, value]) => { cell.setValueProcessData(value); if (visible) { cell.cellRendered(); } }); if (visible) { this.normalizeHeight(true); if (this.table.options.rowFormatter) { this.table.options.rowFormatter(this.getComponent()); } } else { this.initialized = false; this.height = 0; this.heightStyled = ""; } this.dispatch("row-data-changed", this, visible, updatedData); this.dispatchExternal("rowUpdated", this.getComponent()); if (this.subscribedExternal("dataChanged")) { this.dispatchExternal("dataChanged", this.table.rowManager.getData()); } resolve(); }); } getData(transform) { if (transform) { return this.chain("row-data-retrieve", [this, transform], null, this.data); } return this.data; } getCell(column) { var match = false; column = this.table.columnManager.findColumn(column); if (!this.initialized && this.cells.length === 0) { this.generateCells(); } match = this.cells.find(function (cell) { return cell.column === column; }); return match; } getCellIndex(findCell) { return this.cells.findIndex(function (cell) { return cell === findCell; }); } findCell(subject) { return this.cells.find((cell) => { return cell.element === subject; }); } getCells() { if (!this.initialized && this.cells.length === 0) { this.generateCells(); } return this.cells; } nextRow() { var row = this.table.rowManager.nextDisplayRow(this, true); return row || false; } prevRow() { var row = this.table.rowManager.prevDisplayRow(this, true); return row || false; } moveToRow(to, before) { var toRow = this.table.rowManager.findRow(to); if (toRow) { this.table.rowManager.moveRowActual(this, toRow, !before); this.table.rowManager.refreshActiveData("display", false, true); } else { console.warn("Move Error - No matching row found:", to); } } ///////////////////// Actions ///////////////////// delete() { this.dispatch("row-delete", this); this.deleteActual(); return Promise.resolve(); } deleteActual(blockRedraw) { this.detachModules(); this.table.rowManager.deleteRow(this, blockRedraw); this.deleteCells(); this.initialized = false; this.heightInitialized = false; this.element = false; this.dispatch("row-deleted", this); } detachModules() { this.dispatch("row-deleting", this); } deleteCells() { var cellCount = this.cells.length; for (let i = 0; i < cellCount; i++) { this.cells[0].delete(); } } wipe() { this.detachModules(); this.deleteCells(); if (this.element) { while (this.element.firstChild) this.element.removeChild(this.element.firstChild); if (this.element.parentNode) { this.element.parentNode.removeChild(this.element); } } this.element = false; this.modules = {}; } isDisplayed() { return this.table.rowManager.getDisplayRows().includes(this); } getPosition() { return this.isDisplayed() ? this.position : false; } setPosition(position) { if (position != this.position) { this.position = position; this.positionWatchers.forEach((callback) => { callback(this.position); }); } } watchPosition(callback) { this.positionWatchers.push(callback); callback(this.position); } getGroup() { return this.modules.group || false; } //////////////// Object Generation ///////////////// getComponent() { if (!this.component) { this.component = new RowComponent(this); } return this.component; } } var defaultCalculations = { "avg": function (values, data, calcParams) { var output = 0, precision = typeof calcParams.precision !== "undefined" ? calcParams.precision : 2; if (values.length) { output = values.reduce(function (sum, value) { return Number(sum) + Number(value); }); output = output / values.length; output = precision !== false ? output.toFixed(precision) : output; } return parseFloat(output).toString(); }, "max": function (values, data, calcParams) { var output = null, precision = typeof calcParams.precision !== "undefined" ? calcParams.precision : false; values.forEach(function (value) { value = Number(value); if (value > output || output === null) { output = value; } }); return output !== null ? precision !== false ? output.toFixed(precision) : output : ""; }, "min": function (values, data, calcParams) { var output = null, precision = typeof calcParams.precision !== "undefined" ? calcParams.precision : false; values.forEach(function (value) { value = Number(value); if (value < output || output === null) { output = value; } }); return output !== null ? precision !== false ? output.toFixed(precision) : output : ""; }, "sum": function (values, data, calcParams) { var output = 0, precision = typeof calcParams.precision !== "undefined" ? calcParams.precision : false; if (values.length) { values.forEach(function (value) { value = Number(value); output += !isNaN(value) ? Number(value) : 0; }); } return precision !== false ? output.toFixed(precision) : output; }, "concat": function (values, data, calcParams) { var output = 0; if (values.length) { output = values.reduce(function (sum, value) { return String(sum) + String(value); }); } return output; }, "count": function (values, data, calcParams) { var output = 0; if (values.length) { values.forEach(function (value) { if (value) { output++; } }); } return output; }, "unique": function (values, data, calcParams) { var unique = values.filter((value, index) => { return (values || value === 0) && values.indexOf(value) === index; }); return unique.length; } }; const _ColumnCalcs = class _ColumnCalcs extends Module { constructor(table) { super(table); this.topCalcs = []; this.botCalcs = []; this.genColumn = false; this.topElement = this.createElement(); this.botElement = this.createElement(); this.topRow = false; this.botRow = false; this.topInitialized = false; this.botInitialized = false; this.blocked = false; this.recalcAfterBlock = false; this.registerTableOption("columnCalcs", true); this.registerColumnOption("topCalc"); this.registerColumnOption("topCalcParams"); this.registerColumnOption("topCalcFormatter"); this.registerColumnOption("topCalcFormatterParams"); this.registerColumnOption("bottomCalc"); this.registerColumnOption("bottomCalcParams"); this.registerColumnOption("bottomCalcFormatter"); this.registerColumnOption("bottomCalcFormatterParams"); } createElement() { var el = document.createElement("div"); el.classList.add("tabulator-calcs-holder"); return el; } initialize() { this.genColumn = new Column({field: "value"}, this); this.subscribe("cell-value-changed", this.cellValueChanged.bind(this)); this.subscribe("column-init", this.initializeColumnCheck.bind(this)); this.subscribe("row-deleted", this.rowsUpdated.bind(this)); this.subscribe("scroll-horizontal", this.scrollHorizontal.bind(this)); this.subscribe("row-added", this.rowsUpdated.bind(this)); this.subscribe("column-moved", this.recalcActiveRows.bind(this)); this.subscribe("column-add", this.recalcActiveRows.bind(this)); this.subscribe("data-refreshed", this.recalcActiveRowsRefresh.bind(this)); this.subscribe("table-redraw", this.tableRedraw.bind(this)); this.subscribe("rows-visible", this.visibleRows.bind(this)); this.subscribe("scrollbar-vertical", this.adjustForScrollbar.bind(this)); this.subscribe("redraw-blocked", this.blockRedraw.bind(this)); this.subscribe("redraw-restored", this.restoreRedraw.bind(this)); this.subscribe("table-redrawing", this.resizeHolderWidth.bind(this)); this.subscribe("column-resized", this.resizeHolderWidth.bind(this)); this.subscribe("column-show", this.resizeHolderWidth.bind(this)); this.subscribe("column-hide", this.resizeHolderWidth.bind(this)); this.registerTableFunction("getCalcResults", this.getResults.bind(this)); this.registerTableFunction("recalc", this.userRecalc.bind(this)); this.resizeHolderWidth(); } resizeHolderWidth() { this.topElement.style.minWidth = this.table.columnManager.headersElement.offsetWidth + "px"; } tableRedraw(force) { this.recalc(this.table.rowManager.activeRows); if (force) { this.redraw(); } } blockRedraw() { this.blocked = true; this.recalcAfterBlock = false; } restoreRedraw() { this.blocked = false; if (this.recalcAfterBlock) { this.recalcAfterBlock = false; this.recalcActiveRowsRefresh(); } } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// userRecalc() { this.recalc(this.table.rowManager.activeRows); } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// blockCheck() { if (this.blocked) { this.recalcAfterBlock = true; } return this.blocked; } visibleRows(viewable, rows2) { if (this.topRow) { rows2.unshift(this.topRow); } if (this.botRow) { rows2.push(this.botRow); } return rows2; } rowsUpdated(row) { if (this.table.options.groupBy) { this.recalcRowGroup(row); } else { this.recalcActiveRows(); } } recalcActiveRowsRefresh() { if (this.table.options.groupBy && this.table.options.dataTreeStartExpanded && this.table.options.dataTree) { this.recalcAll(); } else { this.recalcActiveRows(); } } recalcActiveRows() { this.recalc(this.table.rowManager.activeRows); } cellValueChanged(cell) { if (cell.column.definition.topCalc || cell.column.definition.bottomCalc) { if (this.table.options.groupBy) { if (this.table.options.columnCalcs == "table" || this.table.options.columnCalcs == "both") { this.recalcActiveRows(); } if (this.table.options.columnCalcs != "table") { this.recalcRowGroup(cell.row); } } else { this.recalcActiveRows(); } } } initializeColumnCheck(column) { if (column.definition.topCalc || column.definition.bottomCalc) { this.initializeColumn(column); } } //initialize column calcs initializeColumn(column) { var def = column.definition; var config = { topCalcParams: def.topCalcParams || {}, botCalcParams: def.bottomCalcParams || {} }; if (def.topCalc) { switch (typeof def.topCalc) { case "string": if (_ColumnCalcs.calculations[def.topCalc]) { config.topCalc = _ColumnCalcs.calculations[def.topCalc]; } else { console.warn("Column Calc Error - No such calculation found, ignoring: ", def.topCalc); } break; case "function": config.topCalc = def.topCalc; break; } if (config.topCalc) { column.modules.columnCalcs = config; this.topCalcs.push(column); if (this.table.options.columnCalcs != "group") { this.initializeTopRow(); } } } if (def.bottomCalc) { switch (typeof def.bottomCalc) { case "string": if (_ColumnCalcs.calculations[def.bottomCalc]) { config.botCalc = _ColumnCalcs.calculations[def.bottomCalc]; } else { console.warn("Column Calc Error - No such calculation found, ignoring: ", def.bottomCalc); } break; case "function": config.botCalc = def.bottomCalc; break; } if (config.botCalc) { column.modules.columnCalcs = config; this.botCalcs.push(column); if (this.table.options.columnCalcs != "group") { this.initializeBottomRow(); } } } } //dummy functions to handle being mock column manager registerColumnField() { } removeCalcs() { var changed = false; if (this.topInitialized) { this.topInitialized = false; this.topElement.parentNode.removeChild(this.topElement); changed = true; } if (this.botInitialized) { this.botInitialized = false; this.footerRemove(this.botElement); changed = true; } if (changed) { this.table.rowManager.adjustTableSize(); } } reinitializeCalcs() { if (this.topCalcs.length) { this.initializeTopRow(); } if (this.botCalcs.length) { this.initializeBottomRow(); } } initializeTopRow() { var fragment = document.createDocumentFragment(); if (!this.topInitialized) { fragment.appendChild(document.createElement("br")); fragment.appendChild(this.topElement); this.table.columnManager.getContentsElement().insertBefore(fragment, this.table.columnManager.headersElement.nextSibling); this.topInitialized = true; } } initializeBottomRow() { if (!this.botInitialized) { this.footerPrepend(this.botElement); this.botInitialized = true; } } scrollHorizontal(left) { if (this.botInitialized && this.botRow) { this.botElement.scrollLeft = left; } } recalc(rows2) { var data, row; if (!this.blockCheck()) { if (this.topInitialized || this.botInitialized) { data = this.rowsToData(rows2); if (this.topInitialized) { if (this.topRow) { this.topRow.deleteCells(); } row = this.generateRow("top", data); this.topRow = row; while (this.topElement.firstChild) this.topElement.removeChild(this.topElement.firstChild); this.topElement.appendChild(row.getElement()); row.initialize(true); } if (this.botInitialized) { if (this.botRow) { this.botRow.deleteCells(); } row = this.generateRow("bottom", data); this.botRow = row; while (this.botElement.firstChild) this.botElement.removeChild(this.botElement.firstChild); this.botElement.appendChild(row.getElement()); row.initialize(true); } this.table.rowManager.adjustTableSize(); if (this.table.modExists("frozenColumns")) { this.table.modules.frozenColumns.layout(); } } } } recalcRowGroup(row) { this.recalcGroup(this.table.modules.groupRows.getRowGroup(row)); } recalcAll() { if (this.topCalcs.length || this.botCalcs.length) { if (this.table.options.columnCalcs !== "group") { this.recalcActiveRows(); } if (this.table.options.groupBy && this.table.options.columnCalcs !== "table") { var groups = this.table.modules.groupRows.getChildGroups(); groups.forEach((group) => { this.recalcGroup(group); }); } } } recalcGroup(group) { var data, rowData; if (!this.blockCheck()) { if (group) { if (group.calcs) { if (group.calcs.bottom) { data = this.rowsToData(group.rows); rowData = this.generateRowData("bottom", data); group.calcs.bottom.updateData(rowData); group.calcs.bottom.reinitialize(); } if (group.calcs.top) { data = this.rowsToData(group.rows); rowData = this.generateRowData("top", data); group.calcs.top.updateData(rowData); group.calcs.top.reinitialize(); } } } } } //generate top stats row generateTopRow(rows2) { return this.generateRow("top", this.rowsToData(rows2)); } //generate bottom stats row generateBottomRow(rows2) { return this.generateRow("bottom", this.rowsToData(rows2)); } rowsToData(rows2) { var data = [], hasDataTreeColumnCalcs = this.table.options.dataTree && this.table.options.dataTreeChildColumnCalcs, dataTree = this.table.modules.dataTree; rows2.forEach((row) => { var _a2; data.push(row.getData()); if (hasDataTreeColumnCalcs && ((_a2 = row.modules.dataTree) == null ? void 0 : _a2.open)) { this.rowsToData(dataTree.getFilteredTreeChildren(row)).forEach((dataRow) => { data.push(dataRow); }); } }); return data; } //generate stats row generateRow(pos, data) { var rowData = this.generateRowData(pos, data), row; if (this.table.modExists("mutator")) { this.table.modules.mutator.disable(); } row = new Row(rowData, this, "calc"); if (this.table.modExists("mutator")) { this.table.modules.mutator.enable(); } row.getElement().classList.add("tabulator-calcs", "tabulator-calcs-" + pos); row.component = false; row.getComponent = () => { if (!row.component) { row.component = new CalcComponent(row); } return row.component; }; row.generateCells = () => { var cells = []; this.table.columnManager.columnsByIndex.forEach((column) => { this.genColumn.setField(column.getField()); this.genColumn.hozAlign = column.hozAlign; if (column.definition[pos + "CalcFormatter"] && this.table.modExists("format")) { this.genColumn.modules.format = { formatter: this.table.modules.format.lookupFormatter(column.definition[pos + "CalcFormatter"]), params: column.definition[pos + "CalcFormatterParams"] || {} }; } else { this.genColumn.modules.format = { formatter: this.table.modules.format.lookupFormatter("plaintext"), params: {} }; } this.genColumn.definition.cssClass = column.definition.cssClass; var cell = new Cell(this.genColumn, row); cell.getElement(); cell.column = column; cell.setWidth(); column.cells.push(cell); cells.push(cell); if (!column.visible) { cell.hide(); } }); row.cells = cells; }; return row; } //generate stats row generateRowData(pos, data) { var rowData = {}, calcs = pos == "top" ? this.topCalcs : this.botCalcs, type = pos == "top" ? "topCalc" : "botCalc", params, paramKey; calcs.forEach(function (column) { var values = []; if (column.modules.columnCalcs && column.modules.columnCalcs[type]) { data.forEach(function (item) { values.push(column.getFieldValue(item)); }); paramKey = type + "Params"; params = typeof column.modules.columnCalcs[paramKey] === "function" ? column.modules.columnCalcs[paramKey](values, data) : column.modules.columnCalcs[paramKey]; column.setFieldValue(rowData, column.modules.columnCalcs[type](values, data, params)); } }); return rowData; } hasTopCalcs() { return !!this.topCalcs.length; } hasBottomCalcs() { return !!this.botCalcs.length; } //handle table redraw redraw() { if (this.topRow) { this.topRow.normalizeHeight(true); } if (this.botRow) { this.botRow.normalizeHeight(true); } } //return the calculated getResults() { var results = {}, groups; if (this.table.options.groupBy && this.table.modExists("groupRows")) { groups = this.table.modules.groupRows.getGroups(true); groups.forEach((group) => { results[group.getKey()] = this.getGroupResults(group); }); } else { results = { top: this.topRow ? this.topRow.getData() : {}, bottom: this.botRow ? this.botRow.getData() : {} }; } return results; } //get results from a group getGroupResults(group) { var groupObj = group._getSelf(), subGroups = group.getSubGroups(), subGroupResults = {}, results = {}; subGroups.forEach((subgroup) => { subGroupResults[subgroup.getKey()] = this.getGroupResults(subgroup); }); results = { top: groupObj.calcs.top ? groupObj.calcs.top.getData() : {}, bottom: groupObj.calcs.bottom ? groupObj.calcs.bottom.getData() : {}, groups: subGroupResults }; return results; } adjustForScrollbar(width) { if (this.botRow) { if (this.table.rtl) { this.botElement.style.paddingLeft = width + "px"; } else { this.botElement.style.paddingRight = width + "px"; } } } }; __publicField(_ColumnCalcs, "moduleName", "columnCalcs"); //load defaults __publicField(_ColumnCalcs, "calculations", defaultCalculations); let ColumnCalcs = _ColumnCalcs; class DataTree extends Module { constructor(table) { super(table); this.indent = 10; this.field = ""; this.collapseEl = null; this.expandEl = null; this.branchEl = null; this.elementField = false; this.startOpen = function () { }; this.registerTableOption("dataTree", false); this.registerTableOption("dataTreeFilter", true); this.registerTableOption("dataTreeSort", true); this.registerTableOption("dataTreeElementColumn", false); this.registerTableOption("dataTreeBranchElement", true); this.registerTableOption("dataTreeChildIndent", 9); this.registerTableOption("dataTreeChildField", "_children"); this.registerTableOption("dataTreeCollapseElement", false); this.registerTableOption("dataTreeExpandElement", false); this.registerTableOption("dataTreeStartExpanded", false); this.registerTableOption("dataTreeChildColumnCalcs", false); this.registerTableOption("dataTreeSelectPropagate", false); this.registerComponentFunction("row", "treeCollapse", this.collapseRow.bind(this)); this.registerComponentFunction("row", "treeExpand", this.expandRow.bind(this)); this.registerComponentFunction("row", "treeToggle", this.toggleRow.bind(this)); this.registerComponentFunction("row", "getTreeParent", this.getTreeParent.bind(this)); this.registerComponentFunction("row", "getTreeChildren", this.getRowChildren.bind(this)); this.registerComponentFunction("row", "addTreeChild", this.addTreeChildRow.bind(this)); this.registerComponentFunction("row", "isTreeExpanded", this.isRowExpanded.bind(this)); } initialize() { if (this.table.options.dataTree) { var dummyEl = null, options = this.table.options; this.field = options.dataTreeChildField; this.indent = options.dataTreeChildIndent; if (this.options("movableRows")) { console.warn("The movableRows option is not available with dataTree enabled, moving of child rows could result in unpredictable behavior"); } if (options.dataTreeBranchElement) { if (options.dataTreeBranchElement === true) { this.branchEl = document.createElement("div"); this.branchEl.classList.add("tabulator-data-tree-branch"); } else { if (typeof options.dataTreeBranchElement === "string") { dummyEl = document.createElement("div"); dummyEl.innerHTML = options.dataTreeBranchElement; this.branchEl = dummyEl.firstChild; } else { this.branchEl = options.dataTreeBranchElement; } } } else { this.branchEl = document.createElement("div"); this.branchEl.classList.add("tabulator-data-tree-branch-empty"); } if (options.dataTreeCollapseElement) { if (typeof options.dataTreeCollapseElement === "string") { dummyEl = document.createElement("div"); dummyEl.innerHTML = options.dataTreeCollapseElement; this.collapseEl = dummyEl.firstChild; } else { this.collapseEl = options.dataTreeCollapseElement; } } else { this.collapseEl = document.createElement("div"); this.collapseEl.classList.add("tabulator-data-tree-control"); this.collapseEl.tabIndex = 0; this.collapseEl.innerHTML = "
"; } if (options.dataTreeExpandElement) { if (typeof options.dataTreeExpandElement === "string") { dummyEl = document.createElement("div"); dummyEl.innerHTML = options.dataTreeExpandElement; this.expandEl = dummyEl.firstChild; } else { this.expandEl = options.dataTreeExpandElement; } } else { this.expandEl = document.createElement("div"); this.expandEl.classList.add("tabulator-data-tree-control"); this.expandEl.tabIndex = 0; this.expandEl.innerHTML = "
"; } switch (typeof options.dataTreeStartExpanded) { case "boolean": this.startOpen = function (row, index) { return options.dataTreeStartExpanded; }; break; case "function": this.startOpen = options.dataTreeStartExpanded; break; default: this.startOpen = function (row, index) { return options.dataTreeStartExpanded[index]; }; break; } this.subscribe("row-init", this.initializeRow.bind(this)); this.subscribe("row-layout-after", this.layoutRow.bind(this)); this.subscribe("row-deleting", this.rowDeleting.bind(this)); this.subscribe("row-deleted", this.rowDelete.bind(this), 0); this.subscribe("row-data-changed", this.rowDataChanged.bind(this), 10); this.subscribe("cell-value-updated", this.cellValueChanged.bind(this)); this.subscribe("edit-cancelled", this.cellValueChanged.bind(this)); this.subscribe("column-moving-rows", this.columnMoving.bind(this)); this.subscribe("table-built", this.initializeElementField.bind(this)); this.subscribe("table-redrawing", this.tableRedrawing.bind(this)); this.registerDisplayHandler(this.getRows.bind(this), 30); } } tableRedrawing(force) { var rows2; if (force) { rows2 = this.table.rowManager.getRows(); rows2.forEach((row) => { this.reinitializeRowChildren(row); }); } } initializeElementField() { var firstCol = this.table.columnManager.getFirstVisibleColumn(); this.elementField = this.table.options.dataTreeElementColumn || (firstCol ? firstCol.field : false); } getRowChildren(row) { return this.getTreeChildren(row, true); } columnMoving() { var rows2 = []; this.table.rowManager.rows.forEach((row) => { rows2 = rows2.concat(this.getTreeChildren(row, false, true)); }); return rows2; } rowDataChanged(row, visible, updatedData) { if (this.redrawNeeded(updatedData)) { this.initializeRow(row); if (visible) { this.layoutRow(row); this.refreshData(true); } } } cellValueChanged(cell) { var field = cell.column.getField(); if (field === this.elementField) { this.layoutRow(cell.row); } } initializeRow(row) { var childArray = row.getData()[this.field]; var isArray = Array.isArray(childArray); var children = isArray || !isArray && typeof childArray === "object" && childArray !== null; if (!children && row.modules.dataTree && row.modules.dataTree.branchEl && row.modules.dataTree.branchEl.parentNode) { row.modules.dataTree.branchEl.parentNode.removeChild(row.modules.dataTree.branchEl); } if (!children && row.modules.dataTree && row.modules.dataTree.controlEl && row.modules.dataTree.controlEl.parentNode) { row.modules.dataTree.controlEl.parentNode.removeChild(row.modules.dataTree.controlEl); } row.modules.dataTree = { index: row.modules.dataTree ? row.modules.dataTree.index : 0, open: children ? row.modules.dataTree ? row.modules.dataTree.open : this.startOpen(row.getComponent(), 0) : false, controlEl: row.modules.dataTree && children ? row.modules.dataTree.controlEl : false, branchEl: row.modules.dataTree && children ? row.modules.dataTree.branchEl : false, parent: row.modules.dataTree ? row.modules.dataTree.parent : false, children }; } reinitializeRowChildren(row) { var children = this.getTreeChildren(row, false, true); children.forEach(function (child) { child.reinitialize(true); }); } layoutRow(row) { var cell = this.elementField ? row.getCell(this.elementField) : row.getCells()[0], el = cell.getElement(), config = row.modules.dataTree; if (config.branchEl) { if (config.branchEl.parentNode) { config.branchEl.parentNode.removeChild(config.branchEl); } config.branchEl = false; } if (config.controlEl) { if (config.controlEl.parentNode) { config.controlEl.parentNode.removeChild(config.controlEl); } config.controlEl = false; } this.generateControlElement(row, el); row.getElement().classList.add("tabulator-tree-level-" + config.index); if (config.index) { if (this.branchEl) { config.branchEl = this.branchEl.cloneNode(true); el.insertBefore(config.branchEl, el.firstChild); if (this.table.rtl) { config.branchEl.style.marginRight = (config.branchEl.offsetWidth + config.branchEl.style.marginLeft) * (config.index - 1) + config.index * this.indent + "px"; } else { config.branchEl.style.marginLeft = (config.branchEl.offsetWidth + config.branchEl.style.marginRight) * (config.index - 1) + config.index * this.indent + "px"; } } else { if (this.table.rtl) { el.style.paddingRight = parseInt(window.getComputedStyle(el, null).getPropertyValue("padding-right")) + config.index * this.indent + "px"; } else { el.style.paddingLeft = parseInt(window.getComputedStyle(el, null).getPropertyValue("padding-left")) + config.index * this.indent + "px"; } } } } generateControlElement(row, el) { var config = row.modules.dataTree, oldControl = config.controlEl; el = el || row.getCells()[0].getElement(); if (config.children !== false) { if (config.open) { config.controlEl = this.collapseEl.cloneNode(true); config.controlEl.addEventListener("click", (e) => { e.stopPropagation(); this.collapseRow(row); }); } else { config.controlEl = this.expandEl.cloneNode(true); config.controlEl.addEventListener("click", (e) => { e.stopPropagation(); this.expandRow(row); }); } config.controlEl.addEventListener("mousedown", (e) => { e.stopPropagation(); }); if (oldControl && oldControl.parentNode === el) { oldControl.parentNode.replaceChild(config.controlEl, oldControl); } else { el.insertBefore(config.controlEl, el.firstChild); } } } getRows(rows2) { var output = []; rows2.forEach((row, i) => { var config, children; output.push(row); if (row instanceof Row) { row.create(); config = row.modules.dataTree; if (!config.index && config.children !== false) { children = this.getChildren(row, false, true); children.forEach((child) => { child.create(); output.push(child); }); } } }); return output; } getChildren(row, allChildren, sortOnly) { var config = row.modules.dataTree, children = [], output = []; if (config.children !== false && (config.open || allChildren)) { if (!Array.isArray(config.children)) { config.children = this.generateChildren(row); } if (this.table.modExists("filter") && this.table.options.dataTreeFilter) { children = this.table.modules.filter.filter(config.children); } else { children = config.children; } if (this.table.modExists("sort") && this.table.options.dataTreeSort) { this.table.modules.sort.sort(children, sortOnly); } children.forEach((child) => { output.push(child); var subChildren = this.getChildren(child, false, true); subChildren.forEach((sub) => { output.push(sub); }); }); } return output; } generateChildren(row) { var children = []; var childArray = row.getData()[this.field]; if (!Array.isArray(childArray)) { childArray = [childArray]; } childArray.forEach((childData) => { var childRow = new Row(childData || {}, this.table.rowManager); childRow.create(); childRow.modules.dataTree.index = row.modules.dataTree.index + 1; childRow.modules.dataTree.parent = row; if (childRow.modules.dataTree.children) { childRow.modules.dataTree.open = this.startOpen(childRow.getComponent(), childRow.modules.dataTree.index); } children.push(childRow); }); return children; } expandRow(row, silent) { var config = row.modules.dataTree; if (config.children !== false) { config.open = true; row.reinitialize(); this.refreshData(true); this.dispatchExternal("dataTreeRowExpanded", row.getComponent(), row.modules.dataTree.index); } } collapseRow(row) { var config = row.modules.dataTree; if (config.children !== false) { config.open = false; row.reinitialize(); this.refreshData(true); this.dispatchExternal("dataTreeRowCollapsed", row.getComponent(), row.modules.dataTree.index); } } toggleRow(row) { var config = row.modules.dataTree; if (config.children !== false) { if (config.open) { this.collapseRow(row); } else { this.expandRow(row); } } } isRowExpanded(row) { return row.modules.dataTree.open; } getTreeParent(row) { return row.modules.dataTree.parent ? row.modules.dataTree.parent.getComponent() : false; } getTreeParentRoot(row) { return row.modules.dataTree && row.modules.dataTree.parent ? this.getTreeParentRoot(row.modules.dataTree.parent) : row; } getFilteredTreeChildren(row) { var config = row.modules.dataTree, output = [], children; if (config.children) { if (!Array.isArray(config.children)) { config.children = this.generateChildren(row); } if (this.table.modExists("filter") && this.table.options.dataTreeFilter) { children = this.table.modules.filter.filter(config.children); } else { children = config.children; } children.forEach((childRow) => { if (childRow instanceof Row) { output.push(childRow); } }); } return output; } rowDeleting(row) { var config = row.modules.dataTree; if (config && config.children && Array.isArray(config.children)) { config.children.forEach((childRow) => { if (childRow instanceof Row) { childRow.wipe(); } }); } } rowDelete(row) { var parent2 = row.modules.dataTree.parent, childIndex; if (parent2) { childIndex = this.findChildIndex(row, parent2); if (childIndex !== false) { parent2.data[this.field].splice(childIndex, 1); } if (!parent2.data[this.field].length) { delete parent2.data[this.field]; } this.initializeRow(parent2); this.layoutRow(parent2); } this.refreshData(true); } addTreeChildRow(row, data, top, index) { var childIndex = false; if (typeof data === "string") { data = JSON.parse(data); } if (!Array.isArray(row.data[this.field])) { row.data[this.field] = []; row.modules.dataTree.open = this.startOpen(row.getComponent(), row.modules.dataTree.index); } if (typeof index !== "undefined") { childIndex = this.findChildIndex(index, row); if (childIndex !== false) { row.data[this.field].splice(top ? childIndex : childIndex + 1, 0, data); } } if (childIndex === false) { if (top) { row.data[this.field].unshift(data); } else { row.data[this.field].push(data); } } this.initializeRow(row); this.layoutRow(row); this.refreshData(true); } findChildIndex(subject, parent2) { var match = false; if (typeof subject == "object") { if (subject instanceof Row) { match = subject.data; } else if (subject instanceof RowComponent) { match = subject._getSelf().data; } else if (typeof HTMLElement !== "undefined" && subject instanceof HTMLElement) { if (parent2.modules.dataTree) { match = parent2.modules.dataTree.children.find((childRow) => { return childRow instanceof Row ? childRow.element === subject : false; }); if (match) { match = match.data; } } } else if (subject === null) { match = false; } } else if (typeof subject == "undefined") { match = false; } else { match = parent2.data[this.field].find((row) => { return row.data[this.table.options.index] == subject; }); } if (match) { if (Array.isArray(parent2.data[this.field])) { match = parent2.data[this.field].indexOf(match); } if (match == -1) { match = false; } } return match; } getTreeChildren(row, component, recurse) { var config = row.modules.dataTree, output = []; if (config && config.children) { if (!Array.isArray(config.children)) { config.children = this.generateChildren(row); } config.children.forEach((childRow) => { if (childRow instanceof Row) { output.push(component ? childRow.getComponent() : childRow); if (recurse) { this.getTreeChildren(childRow, component, recurse).forEach((child) => { output.push(child); }); } } }); } return output; } getChildField() { return this.field; } redrawNeeded(data) { return (this.field ? typeof data[this.field] !== "undefined" : false) || (this.elementField ? typeof data[this.elementField] !== "undefined" : false); } } __publicField(DataTree, "moduleName", "dataTree"); function csv$1(list2, options = {}, setFileContents) { var delimiter = options.delimiter ? options.delimiter : ",", fileContents = [], headers = []; list2.forEach((row) => { var item = []; switch (row.type) { case "group": console.warn("Download Warning - CSV downloader cannot process row groups"); break; case "calc": console.warn("Download Warning - CSV downloader cannot process column calculations"); break; case "header": row.columns.forEach((col, i) => { if (col && col.depth === 1) { headers[i] = typeof col.value == "undefined" || col.value === null ? "" : '"' + String(col.value).split('"').join('""') + '"'; } }); break; case "row": row.columns.forEach((col) => { if (col) { switch (typeof col.value) { case "object": col.value = col.value !== null ? JSON.stringify(col.value) : ""; break; case "undefined": col.value = ""; break; } item.push('"' + String(col.value).split('"').join('""') + '"'); } }); fileContents.push(item.join(delimiter)); break; } }); if (headers.length) { fileContents.unshift(headers.join(delimiter)); } fileContents = fileContents.join("\n"); if (options.bom) { fileContents = "\uFEFF" + fileContents; } setFileContents(fileContents, "text/csv"); } function json$2(list2, options, setFileContents) { var fileContents = []; list2.forEach((row) => { var item = {}; switch (row.type) { case "header": break; case "group": console.warn("Download Warning - JSON downloader cannot process row groups"); break; case "calc": console.warn("Download Warning - JSON downloader cannot process column calculations"); break; case "row": row.columns.forEach((col) => { if (col) { item[col.component.getTitleDownload() || col.component.getField()] = col.value; } }); fileContents.push(item); break; } }); fileContents = JSON.stringify(fileContents, null, " "); setFileContents(fileContents, "application/json"); } function pdf(list2, options = {}, setFileContents) { var header = [], body = [], autoTableParams = {}, rowGroupStyles = options.rowGroupStyles || { fontStyle: "bold", fontSize: 12, cellPadding: 6, fillColor: 220 }, rowCalcStyles = options.rowCalcStyles || { fontStyle: "bold", fontSize: 10, cellPadding: 4, fillColor: 232 }, jsPDFParams = options.jsPDF || {}, title = options.title ? options.title : "", jspdfLib, doc; if (!jsPDFParams.orientation) { jsPDFParams.orientation = options.orientation || "landscape"; } if (!jsPDFParams.unit) { jsPDFParams.unit = "pt"; } list2.forEach((row) => { switch (row.type) { case "header": header.push(parseRow(row)); break; case "group": body.push(parseRow(row, rowGroupStyles)); break; case "calc": body.push(parseRow(row, rowCalcStyles)); break; case "row": body.push(parseRow(row)); break; } }); function parseRow(row, styles) { var rowData = []; row.columns.forEach((col) => { var cell; if (col) { switch (typeof col.value) { case "object": col.value = col.value !== null ? JSON.stringify(col.value) : ""; break; case "undefined": col.value = ""; break; } cell = { content: col.value, colSpan: col.width, rowSpan: col.height }; if (styles) { cell.styles = styles; } rowData.push(cell); } }); return rowData; } jspdfLib = this.dependencyRegistry.lookup("jspdf", "jsPDF"); doc = new jspdfLib(jsPDFParams); if (options.autoTable) { if (typeof options.autoTable === "function") { autoTableParams = options.autoTable(doc) || {}; } else { autoTableParams = options.autoTable; } } if (title) { autoTableParams.didDrawPage = function (data) { doc.text(title, 40, 30); }; } autoTableParams.head = header; autoTableParams.body = body; doc.autoTable(autoTableParams); if (options.documentProcessing) { options.documentProcessing(doc); } setFileContents(doc.output("arraybuffer"), "application/pdf"); } function xlsx$1(list2, options, setFileContents) { var self = this, sheetName = options.sheetName || "Sheet1", XLSXLib = this.dependencyRegistry.lookup("XLSX"), workbook = XLSXLib.utils.book_new(), tableFeatures = new CoreFeature(this), compression = "compress" in options ? options.compress : true, writeOptions = options.writeOptions || {bookType: "xlsx", bookSST: true, compression}, output; writeOptions.type = "binary"; workbook.SheetNames = []; workbook.Sheets = {}; function generateSheet() { var rows2 = [], merges = [], worksheet = {}, range2 = { s: {c: 0, r: 0}, e: { c: list2[0] ? list2[0].columns.reduce((a, b) => a + (b && b.width ? b.width : 1), 0) : 0, r: list2.length } }; list2.forEach((row, i) => { var rowData = []; row.columns.forEach(function (col, j) { if (col) { rowData.push(!(col.value instanceof Date) && typeof col.value === "object" ? JSON.stringify(col.value) : col.value); if (col.width > 1 || col.height > -1) { if (col.height > 1 || col.width > 1) { merges.push({s: {r: i, c: j}, e: {r: i + col.height - 1, c: j + col.width - 1}}); } } } else { rowData.push(""); } }); rows2.push(rowData); }); XLSXLib.utils.sheet_add_aoa(worksheet, rows2); worksheet["!ref"] = XLSXLib.utils.encode_range(range2); if (merges.length) { worksheet["!merges"] = merges; } return worksheet; } if (options.sheetOnly) { setFileContents(generateSheet()); return; } if (options.sheets) { for (var sheet in options.sheets) { if (options.sheets[sheet] === true) { workbook.SheetNames.push(sheet); workbook.Sheets[sheet] = generateSheet(); } else { workbook.SheetNames.push(sheet); tableFeatures.commsSend(options.sheets[sheet], "download", "intercept", { type: "xlsx", options: {sheetOnly: true}, active: self.active, intercept: function (data) { workbook.Sheets[sheet] = data; } }); } } } else { workbook.SheetNames.push(sheetName); workbook.Sheets[sheetName] = generateSheet(); } if (options.documentProcessing) { workbook = options.documentProcessing(workbook); } function s2ab(s) { var buf = new ArrayBuffer(s.length); var view = new Uint8Array(buf); for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 255; return buf; } output = XLSXLib.write(workbook, writeOptions); setFileContents(s2ab(output), "application/octet-stream"); } function html$1(list2, options, setFileContents) { if (this.modExists("export", true)) { setFileContents(this.modules.export.generateHTMLTable(list2), "text/html"); } } function jsonLines(list2, options, setFileContents) { const fileContents = []; list2.forEach((row) => { const item = {}; switch (row.type) { case "header": break; case "group": console.warn("Download Warning - JSON downloader cannot process row groups"); break; case "calc": console.warn("Download Warning - JSON downloader cannot process column calculations"); break; case "row": row.columns.forEach((col) => { if (col) { item[col.component.getTitleDownload() || col.component.getField()] = col.value; } }); fileContents.push(JSON.stringify(item)); break; } }); setFileContents(fileContents.join("\n"), "application/x-ndjson"); } var defaultDownloaders = { csv: csv$1, json: json$2, jsonLines, pdf, xlsx: xlsx$1, html: html$1 }; const _Download = class _Download extends Module { constructor(table) { super(table); this.registerTableOption("downloadEncoder", function (data, mimeType) { return new Blob([data], {type: mimeType}); }); this.registerTableOption("downloadConfig", {}); this.registerTableOption("downloadRowRange", "active"); this.registerColumnOption("download"); this.registerColumnOption("titleDownload"); } initialize() { this.deprecatedOptionsCheck(); this.registerTableFunction("download", this.download.bind(this)); this.registerTableFunction("downloadToTab", this.downloadToTab.bind(this)); } deprecatedOptionsCheck() { } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// downloadToTab(type, filename, options, active) { this.download(type, filename, options, active, true); } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// //trigger file download download(type, filename, options, range2, interceptCallback) { var downloadFunc = false; function buildLink(data, mime) { if (interceptCallback) { if (interceptCallback === true) { this.triggerDownload(data, mime, type, filename, true); } else { interceptCallback(data); } } else { this.triggerDownload(data, mime, type, filename); } } if (typeof type == "function") { downloadFunc = type; } else { if (_Download.downloaders[type]) { downloadFunc = _Download.downloaders[type]; } else { console.warn("Download Error - No such download type found: ", type); } } if (downloadFunc) { var list2 = this.generateExportList(range2); downloadFunc.call(this.table, list2, options || {}, buildLink.bind(this)); } } generateExportList(range2) { var list2 = this.table.modules.export.generateExportList(this.table.options.downloadConfig, false, range2 || this.table.options.downloadRowRange, "download"); var groupHeader = this.table.options.groupHeaderDownload; if (groupHeader && !Array.isArray(groupHeader)) { groupHeader = [groupHeader]; } list2.forEach((row) => { var group; if (row.type === "group") { group = row.columns[0]; if (groupHeader && groupHeader[row.indent]) { group.value = groupHeader[row.indent](group.value, row.component._group.getRowCount(), row.component._group.getData(), row.component); } } }); return list2; } triggerDownload(data, mime, type, filename, newTab) { var element = document.createElement("a"), blob = this.table.options.downloadEncoder(data, mime); if (blob) { if (newTab) { window.open(window.URL.createObjectURL(blob)); } else { filename = filename || "Tabulator." + (typeof type === "function" ? "txt" : type); if (navigator.msSaveOrOpenBlob) { navigator.msSaveOrOpenBlob(blob, filename); } else { element.setAttribute("href", window.URL.createObjectURL(blob)); element.setAttribute("download", filename); element.style.display = "none"; document.body.appendChild(element); element.click(); document.body.removeChild(element); } } this.dispatchExternal("downloadComplete"); } } commsReceived(table, action, data) { switch (action) { case "intercept": this.download(data.type, "", data.options, data.active, data.intercept); break; } } }; __publicField(_Download, "moduleName", "download"); //load defaults __publicField(_Download, "downloaders", defaultDownloaders); let Download = _Download; function maskInput(el, options) { var mask = options.mask, maskLetter = typeof options.maskLetterChar !== "undefined" ? options.maskLetterChar : "A", maskNumber = typeof options.maskNumberChar !== "undefined" ? options.maskNumberChar : "9", maskWildcard = typeof options.maskWildcardChar !== "undefined" ? options.maskWildcardChar : "*"; function fillSymbols(index) { var symbol = mask[index]; if (typeof symbol !== "undefined" && symbol !== maskWildcard && symbol !== maskLetter && symbol !== maskNumber) { el.value = el.value + "" + symbol; fillSymbols(index + 1); } } el.addEventListener("keydown", (e) => { var index = el.value.length, char = e.key; if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) { if (index >= mask.length) { e.preventDefault(); e.stopPropagation(); return false; } else { switch (mask[index]) { case maskLetter: if (char.toUpperCase() == char.toLowerCase()) { e.preventDefault(); e.stopPropagation(); return false; } break; case maskNumber: if (isNaN(char)) { e.preventDefault(); e.stopPropagation(); return false; } break; case maskWildcard: break; default: if (char !== mask[index]) { e.preventDefault(); e.stopPropagation(); return false; } } } } return; }); el.addEventListener("keyup", (e) => { if (e.key.length === 1) { if (options.maskAutoFill) { fillSymbols(el.value.length); } } }); if (!el.placeholder) { el.placeholder = mask; } if (options.maskAutoFill) { fillSymbols(el.value.length); } } function input(cell, onRendered, success, cancel, editorParams) { var cellValue = cell.getValue(), input2 = document.createElement("input"); input2.setAttribute("type", editorParams.search ? "search" : "text"); input2.style.padding = "4px"; input2.style.width = "100%"; input2.style.boxSizing = "border-box"; if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); input2.setAttribute(key, input2.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { input2.setAttribute(key, editorParams.elementAttributes[key]); } } } input2.value = typeof cellValue !== "undefined" ? cellValue : ""; onRendered(function () { if (cell.getType() === "cell") { input2.focus({preventScroll: true}); input2.style.height = "100%"; if (editorParams.selectContents) { input2.select(); } } }); function onChange(e) { if ((cellValue === null || typeof cellValue === "undefined") && input2.value !== "" || input2.value !== cellValue) { if (success(input2.value)) { cellValue = input2.value; } } else { cancel(); } } input2.addEventListener("change", onChange); input2.addEventListener("blur", onChange); input2.addEventListener("keydown", function (e) { switch (e.key) { // case "Tab": case "Enter": onChange(); break; case "Escape": cancel(); break; case "End": case "Home": e.stopPropagation(); break; } }); if (editorParams.mask) { maskInput(input2, editorParams); } return input2; } function textarea$1(cell, onRendered, success, cancel, editorParams) { var cellValue = cell.getValue(), vertNav = editorParams.verticalNavigation || "hybrid", value = String(cellValue !== null && typeof cellValue !== "undefined" ? cellValue : ""), input2 = document.createElement("textarea"), scrollHeight = 0; input2.style.display = "block"; input2.style.padding = "2px"; input2.style.height = "100%"; input2.style.width = "100%"; input2.style.boxSizing = "border-box"; input2.style.whiteSpace = "pre-wrap"; input2.style.resize = "none"; if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); input2.setAttribute(key, input2.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { input2.setAttribute(key, editorParams.elementAttributes[key]); } } } input2.value = value; onRendered(function () { if (cell.getType() === "cell") { input2.focus({preventScroll: true}); input2.style.height = "100%"; input2.scrollHeight; input2.style.height = input2.scrollHeight + "px"; cell.getRow().normalizeHeight(); if (editorParams.selectContents) { input2.select(); } } }); function onChange(e) { if ((cellValue === null || typeof cellValue === "undefined") && input2.value !== "" || input2.value !== cellValue) { if (success(input2.value)) { cellValue = input2.value; } setTimeout(function () { cell.getRow().normalizeHeight(); }, 300); } else { cancel(); } } input2.addEventListener("change", onChange); input2.addEventListener("blur", onChange); input2.addEventListener("keyup", function () { input2.style.height = ""; var heightNow = input2.scrollHeight; input2.style.height = heightNow + "px"; if (heightNow != scrollHeight) { scrollHeight = heightNow; cell.getRow().normalizeHeight(); } }); input2.addEventListener("keydown", function (e) { switch (e.key) { case "Enter": if (e.shiftKey && editorParams.shiftEnterSubmit) { onChange(); } break; case "Escape": cancel(); break; case "ArrowUp": if (vertNav == "editor" || vertNav == "hybrid" && input2.selectionStart) { e.stopImmediatePropagation(); e.stopPropagation(); } break; case "ArrowDown": if (vertNav == "editor" || vertNav == "hybrid" && input2.selectionStart !== input2.value.length) { e.stopImmediatePropagation(); e.stopPropagation(); } break; case "End": case "Home": e.stopPropagation(); break; } }); if (editorParams.mask) { maskInput(input2, editorParams); } return input2; } function number$1(cell, onRendered, success, cancel, editorParams) { var cellValue = cell.getValue(), vertNav = editorParams.verticalNavigation || "editor", input2 = document.createElement("input"); input2.setAttribute("type", "number"); if (typeof editorParams.max != "undefined") { input2.setAttribute("max", editorParams.max); } if (typeof editorParams.min != "undefined") { input2.setAttribute("min", editorParams.min); } if (typeof editorParams.step != "undefined") { input2.setAttribute("step", editorParams.step); } input2.style.padding = "4px"; input2.style.width = "100%"; input2.style.boxSizing = "border-box"; if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); input2.setAttribute(key, input2.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { input2.setAttribute(key, editorParams.elementAttributes[key]); } } } input2.value = cellValue; var blurFunc = function (e) { onChange(); }; onRendered(function () { if (cell.getType() === "cell") { input2.removeEventListener("blur", blurFunc); input2.focus({preventScroll: true}); input2.style.height = "100%"; input2.addEventListener("blur", blurFunc); if (editorParams.selectContents) { input2.select(); } } }); function onChange() { var value = input2.value; if (!isNaN(value) && value !== "") { value = Number(value); } if (value !== cellValue) { if (success(value)) { cellValue = value; } } else { cancel(); } } input2.addEventListener("keydown", function (e) { switch (e.key) { case "Enter": onChange(); break; case "Escape": cancel(); break; case "ArrowUp": case "ArrowDown": if (vertNav == "editor") { e.stopImmediatePropagation(); e.stopPropagation(); } break; case "End": case "Home": e.stopPropagation(); break; } }); if (editorParams.mask) { maskInput(input2, editorParams); } return input2; } function range(cell, onRendered, success, cancel, editorParams) { var cellValue = cell.getValue(), input2 = document.createElement("input"); input2.setAttribute("type", "range"); if (typeof editorParams.max != "undefined") { input2.setAttribute("max", editorParams.max); } if (typeof editorParams.min != "undefined") { input2.setAttribute("min", editorParams.min); } if (typeof editorParams.step != "undefined") { input2.setAttribute("step", editorParams.step); } input2.style.padding = "4px"; input2.style.width = "100%"; input2.style.boxSizing = "border-box"; if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); input2.setAttribute(key, input2.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { input2.setAttribute(key, editorParams.elementAttributes[key]); } } } input2.value = cellValue; onRendered(function () { if (cell.getType() === "cell") { input2.focus({preventScroll: true}); input2.style.height = "100%"; } }); function onChange() { var value = input2.value; if (!isNaN(value) && value !== "") { value = Number(value); } if (value != cellValue) { if (success(value)) { cellValue = value; } } else { cancel(); } } input2.addEventListener("blur", function (e) { onChange(); }); input2.addEventListener("keydown", function (e) { switch (e.key) { case "Enter": onChange(); break; case "Escape": cancel(); break; } }); return input2; } function date$1(cell, onRendered, success, cancel, editorParams) { var inputFormat = editorParams.format, vertNav = editorParams.verticalNavigation || "editor", DT = inputFormat ? window.DateTime || luxon.DateTime : null; var cellValue = cell.getValue(), input2 = document.createElement("input"); function convertDate(value) { var newDatetime; if (DT.isDateTime(value)) { newDatetime = value; } else if (inputFormat === "x") { newDatetime = DT.fromMillis(value); } else if (inputFormat === "iso") { newDatetime = DT.fromISO(String(value)); } else { newDatetime = DT.fromFormat(String(value), inputFormat); } return newDatetime.toFormat("yyyy-MM-dd"); } input2.type = "date"; input2.style.padding = "4px"; input2.style.width = "100%"; input2.style.boxSizing = "border-box"; if (editorParams.max) { input2.setAttribute("max", inputFormat ? convertDate(editorParams.max) : editorParams.max); } if (editorParams.min) { input2.setAttribute("min", inputFormat ? convertDate(editorParams.min) : editorParams.min); } if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); input2.setAttribute(key, input2.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { input2.setAttribute(key, editorParams.elementAttributes[key]); } } } cellValue = typeof cellValue !== "undefined" ? cellValue : ""; if (inputFormat) { if (DT) { cellValue = convertDate(cellValue); } else { console.error("Editor Error - 'date' editor 'format' param is dependant on luxon.js"); } } input2.value = cellValue; onRendered(function () { if (cell.getType() === "cell") { input2.focus({preventScroll: true}); input2.style.height = "100%"; if (editorParams.selectContents) { input2.select(); } } }); function onChange() { var value = input2.value, luxDate; if ((cellValue === null || typeof cellValue === "undefined") && value !== "" || value !== cellValue) { if (value && inputFormat) { luxDate = DT.fromFormat(String(value), "yyyy-MM-dd"); switch (inputFormat) { case true: value = luxDate; break; case "x": value = luxDate.toMillis(); break; case "iso": value = luxDate.toISO(); break; default: value = luxDate.toFormat(inputFormat); } } if (success(value)) { cellValue = input2.value; } } else { cancel(); } } input2.addEventListener("blur", function (e) { if (e.relatedTarget || e.rangeParent || e.explicitOriginalTarget !== input2) { onChange(); } }); input2.addEventListener("keydown", function (e) { switch (e.key) { // case "Tab": case "Enter": onChange(); break; case "Escape": cancel(); break; case "End": case "Home": e.stopPropagation(); break; case "ArrowUp": case "ArrowDown": if (vertNav == "editor") { e.stopImmediatePropagation(); e.stopPropagation(); } break; } }); return input2; } function time$1(cell, onRendered, success, cancel, editorParams) { var inputFormat = editorParams.format, vertNav = editorParams.verticalNavigation || "editor", DT = inputFormat ? window.DateTime || luxon.DateTime : null, newDatetime; var cellValue = cell.getValue(), input2 = document.createElement("input"); input2.type = "time"; input2.style.padding = "4px"; input2.style.width = "100%"; input2.style.boxSizing = "border-box"; if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); input2.setAttribute(key, input2.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { input2.setAttribute(key, editorParams.elementAttributes[key]); } } } cellValue = typeof cellValue !== "undefined" ? cellValue : ""; if (inputFormat) { if (DT) { if (DT.isDateTime(cellValue)) { newDatetime = cellValue; } else if (inputFormat === "x") { newDatetime = DT.fromMillis(cellValue); } else if (inputFormat === "iso") { newDatetime = DT.fromISO(String(cellValue)); } else { newDatetime = DT.fromFormat(String(cellValue), inputFormat); } cellValue = newDatetime.toFormat("HH:mm"); } else { console.error("Editor Error - 'date' editor 'format' param is dependant on luxon.js"); } } input2.value = cellValue; onRendered(function () { if (cell.getType() == "cell") { input2.focus({preventScroll: true}); input2.style.height = "100%"; if (editorParams.selectContents) { input2.select(); } } }); function onChange() { var value = input2.value, luxTime; if ((cellValue === null || typeof cellValue === "undefined") && value !== "" || value !== cellValue) { if (value && inputFormat) { luxTime = DT.fromFormat(String(value), "hh:mm"); switch (inputFormat) { case true: value = luxTime; break; case "x": value = luxTime.toMillis(); break; case "iso": value = luxTime.toISO(); break; default: value = luxTime.toFormat(inputFormat); } } if (success(value)) { cellValue = input2.value; } } else { cancel(); } } input2.addEventListener("blur", function (e) { if (e.relatedTarget || e.rangeParent || e.explicitOriginalTarget !== input2) { onChange(); } }); input2.addEventListener("keydown", function (e) { switch (e.key) { // case "Tab": case "Enter": onChange(); break; case "Escape": cancel(); break; case "End": case "Home": e.stopPropagation(); break; case "ArrowUp": case "ArrowDown": if (vertNav == "editor") { e.stopImmediatePropagation(); e.stopPropagation(); } break; } }); return input2; } function datetime$2(cell, onRendered, success, cancel, editorParams) { var inputFormat = editorParams.format, vertNav = editorParams.verticalNavigation || "editor", DT = inputFormat ? this.table.dependencyRegistry.lookup(["luxon", "DateTime"], "DateTime") : null, newDatetime; var cellValue = cell.getValue(), input2 = document.createElement("input"); input2.type = "datetime-local"; input2.style.padding = "4px"; input2.style.width = "100%"; input2.style.boxSizing = "border-box"; if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); input2.setAttribute(key, input2.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { input2.setAttribute(key, editorParams.elementAttributes[key]); } } } cellValue = typeof cellValue !== "undefined" ? cellValue : ""; if (inputFormat) { if (DT) { if (DT.isDateTime(cellValue)) { newDatetime = cellValue; } else if (inputFormat === "x") { newDatetime = DT.fromMillis(cellValue); } else if (inputFormat === "iso") { newDatetime = DT.fromISO(String(cellValue)); } else { newDatetime = DT.fromFormat(String(cellValue), inputFormat); } cellValue = newDatetime.toFormat("yyyy-MM-dd") + "T" + newDatetime.toFormat("HH:mm"); } else { console.error("Editor Error - 'date' editor 'format' param is dependant on luxon.js"); } } input2.value = cellValue; onRendered(function () { if (cell.getType() === "cell") { input2.focus({preventScroll: true}); input2.style.height = "100%"; if (editorParams.selectContents) { input2.select(); } } }); function onChange() { var value = input2.value, luxDateTime; if ((cellValue === null || typeof cellValue === "undefined") && value !== "" || value !== cellValue) { if (value && inputFormat) { luxDateTime = DT.fromISO(String(value)); switch (inputFormat) { case true: value = luxDateTime; break; case "x": value = luxDateTime.toMillis(); break; case "iso": value = luxDateTime.toISO(); break; default: value = luxDateTime.toFormat(inputFormat); } } if (success(value)) { cellValue = input2.value; } } else { cancel(); } } input2.addEventListener("blur", function (e) { if (e.relatedTarget || e.rangeParent || e.explicitOriginalTarget !== input2) { onChange(); } }); input2.addEventListener("keydown", function (e) { switch (e.key) { // case "Tab": case "Enter": onChange(); break; case "Escape": cancel(); break; case "End": case "Home": e.stopPropagation(); break; case "ArrowUp": case "ArrowDown": if (vertNav == "editor") { e.stopImmediatePropagation(); e.stopPropagation(); } break; } }); return input2; } let Edit$1 = class Edit { constructor(editor, cell, onRendered, success, cancel, editorParams) { this.edit = editor; this.table = editor.table; this.cell = cell; this.params = this._initializeParams(editorParams); this.data = []; this.displayItems = []; this.currentItems = []; this.focusedItem = null; this.input = this._createInputElement(); this.listEl = this._createListElement(); this.initialValues = null; this.isFilter = cell.getType() === "header"; this.filterTimeout = null; this.filtered = false; this.typing = false; this.values = []; this.popup = null; this.listIteration = 0; this.lastAction = ""; this.filterTerm = ""; this.blurable = true; this.actions = { success, cancel }; this._deprecatedOptionsCheck(); this._initializeValue(); onRendered(this._onRendered.bind(this)); } _deprecatedOptionsCheck() { } _initializeValue() { var initialValue = this.cell.getValue(); if (typeof initialValue === "undefined" && typeof this.params.defaultValue !== "undefined") { initialValue = this.params.defaultValue; } this.initialValues = this.params.multiselect ? initialValue : [initialValue]; if (this.isFilter) { this.input.value = this.initialValues ? this.initialValues.join(",") : ""; this.headerFilterInitialListGen(); } } _onRendered() { var cellEl = this.cell.getElement(); function clickStop(e) { e.stopPropagation(); } if (!this.isFilter) { this.input.style.height = "100%"; this.input.focus({preventScroll: true}); } cellEl.addEventListener("click", clickStop); setTimeout(() => { cellEl.removeEventListener("click", clickStop); }, 1e3); this.input.addEventListener("mousedown", this._preventPopupBlur.bind(this)); } _createListElement() { var listEl = document.createElement("div"); listEl.classList.add("tabulator-edit-list"); listEl.addEventListener("mousedown", this._preventBlur.bind(this)); listEl.addEventListener("keydown", this._inputKeyDown.bind(this)); return listEl; } _setListWidth() { var element = this.isFilter ? this.input : this.cell.getElement(); this.listEl.style.minWidth = element.offsetWidth + "px"; if (this.params.maxWidth) { if (this.params.maxWidth === true) { this.listEl.style.maxWidth = element.offsetWidth + "px"; } else if (typeof this.params.maxWidth === "number") { this.listEl.style.maxWidth = this.params.maxWidth + "px"; } else { this.listEl.style.maxWidth = this.params.maxWidth; } } } _createInputElement() { var attribs = this.params.elementAttributes; var input2 = document.createElement("input"); input2.setAttribute("type", this.params.clearable ? "search" : "text"); input2.style.padding = "4px"; input2.style.width = "100%"; input2.style.boxSizing = "border-box"; if (!this.params.autocomplete) { input2.style.cursor = "default"; input2.style.caretColor = "transparent"; } if (attribs && typeof attribs == "object") { for (let key in attribs) { if (key.charAt(0) == "+") { key = key.slice(1); input2.setAttribute(key, input2.getAttribute(key) + attribs["+" + key]); } else { input2.setAttribute(key, attribs[key]); } } } if (this.params.mask) { maskInput(input2, this.params); } this._bindInputEvents(input2); return input2; } _initializeParams(params) { var valueKeys = ["values", "valuesURL", "valuesLookup"], valueCheck; params = Object.assign({}, params); params.verticalNavigation = params.verticalNavigation || "editor"; params.placeholderLoading = typeof params.placeholderLoading === "undefined" ? "Searching ..." : params.placeholderLoading; params.placeholderEmpty = typeof params.placeholderEmpty === "undefined" ? "No Results Found" : params.placeholderEmpty; params.filterDelay = typeof params.filterDelay === "undefined" ? 300 : params.filterDelay; params.emptyValue = Object.keys(params).includes("emptyValue") ? params.emptyValue : ""; valueCheck = Object.keys(params).filter((key) => valueKeys.includes(key)).length; if (!valueCheck) { console.warn("list editor config error - either the values, valuesURL, or valuesLookup option must be set"); } else if (valueCheck > 1) { console.warn("list editor config error - only one of the values, valuesURL, or valuesLookup options can be set on the same editor"); } if (params.autocomplete) { if (params.multiselect) { params.multiselect = false; console.warn("list editor config error - multiselect option is not available when autocomplete is enabled"); } } else { if (params.freetext) { params.freetext = false; console.warn("list editor config error - freetext option is only available when autocomplete is enabled"); } if (params.filterFunc) { params.filterFunc = false; console.warn("list editor config error - filterFunc option is only available when autocomplete is enabled"); } if (params.filterRemote) { params.filterRemote = false; console.warn("list editor config error - filterRemote option is only available when autocomplete is enabled"); } if (params.mask) { params.mask = false; console.warn("list editor config error - mask option is only available when autocomplete is enabled"); } if (params.allowEmpty) { params.allowEmpty = false; console.warn("list editor config error - allowEmpty option is only available when autocomplete is enabled"); } if (params.listOnEmpty) { params.listOnEmpty = false; console.warn("list editor config error - listOnEmpty option is only available when autocomplete is enabled"); } } if (params.filterRemote && !(typeof params.valuesLookup === "function" || params.valuesURL)) { params.filterRemote = false; console.warn("list editor config error - filterRemote option should only be used when values list is populated from a remote source"); } return params; } ////////////////////////////////////// ////////// Event Handling //////////// ////////////////////////////////////// _bindInputEvents(input2) { input2.addEventListener("focus", this._inputFocus.bind(this)); input2.addEventListener("click", this._inputClick.bind(this)); input2.addEventListener("blur", this._inputBlur.bind(this)); input2.addEventListener("keydown", this._inputKeyDown.bind(this)); input2.addEventListener("search", this._inputSearch.bind(this)); if (this.params.autocomplete) { input2.addEventListener("keyup", this._inputKeyUp.bind(this)); } } _inputFocus(e) { this.rebuildOptionsList(); } _filter() { if (this.params.filterRemote) { clearTimeout(this.filterTimeout); this.filterTimeout = setTimeout(() => { this.rebuildOptionsList(); }, this.params.filterDelay); } else { this._filterList(); } } _inputClick(e) { e.stopPropagation(); } _inputBlur(e) { if (this.blurable) { if (this.popup) { this.popup.hide(); } else { this._resolveValue(true); } } } _inputSearch() { this._clearChoices(); } _inputKeyDown(e) { switch (e.key) { case "ArrowUp": this._keyUp(e); break; case "ArrowDown": this._keyDown(e); break; case "ArrowLeft": case "ArrowRight": this._keySide(e); break; case "Enter": this._keyEnter(); break; case "Escape": this._keyEsc(); break; case "Home": case "End": this._keyHomeEnd(e); break; case "Tab": this._keyTab(e); break; default: this._keySelectLetter(e); } } _inputKeyUp(e) { switch (e.key) { case "ArrowUp": case "ArrowLeft": case "ArrowRight": case "ArrowDown": case "Enter": case "Escape": break; default: this._keyAutoCompLetter(e); } } _preventPopupBlur() { if (this.popup) { this.popup.blockHide(); } setTimeout(() => { if (this.popup) { this.popup.restoreHide(); } }, 10); } _preventBlur() { this.blurable = false; setTimeout(() => { this.blurable = true; }, 10); } ////////////////////////////////////// //////// Keyboard Navigation ///////// ////////////////////////////////////// _keyTab(e) { if (this.params.autocomplete && this.lastAction === "typing") { this._resolveValue(true); } else { if (this.focusedItem) { this._chooseItem(this.focusedItem, true); } } } _keyUp(e) { var index = this.displayItems.indexOf(this.focusedItem); if (this.params.verticalNavigation == "editor" || this.params.verticalNavigation == "hybrid" && index) { e.stopImmediatePropagation(); e.stopPropagation(); e.preventDefault(); if (index > 0) { this._focusItem(this.displayItems[index - 1]); } } } _keyDown(e) { var index = this.displayItems.indexOf(this.focusedItem); if (this.params.verticalNavigation == "editor" || this.params.verticalNavigation == "hybrid" && index < this.displayItems.length - 1) { e.stopImmediatePropagation(); e.stopPropagation(); e.preventDefault(); if (index < this.displayItems.length - 1) { if (index == -1) { this._focusItem(this.displayItems[0]); } else { this._focusItem(this.displayItems[index + 1]); } } } } _keySide(e) { if (!this.params.autocomplete) { e.stopImmediatePropagation(); e.stopPropagation(); e.preventDefault(); } } _keyEnter(e) { if (this.params.autocomplete && this.lastAction === "typing") { this._resolveValue(true); } else { if (this.focusedItem) { if (this.isFilter && !this.params.multiselect && this.focusedItem.selected) { this._resolveValue(); } else { this._chooseItem(this.focusedItem); } } } } _keyEsc(e) { this._cancel(); } _keyHomeEnd(e) { if (this.params.autocomplete) { e.stopImmediatePropagation(); } } _keySelectLetter(e) { if (!this.params.autocomplete) { e.preventDefault(); if (e.key.length === 1) { this._scrollToValue(e.key.toUpperCase().charCodeAt(0)); } } } _keyAutoCompLetter(e) { this._filter(); this.lastAction = "typing"; this.typing = true; } _scrollToValue(char) { clearTimeout(this.filterTimeout); var character = String.fromCharCode(char).toLowerCase(); this.filterTerm += character.toLowerCase(); var match = this.displayItems.find((item) => { return typeof item.label !== "undefined" && item.label.toLowerCase().startsWith(this.filterTerm); }); if (match) { this._focusItem(match); } this.filterTimeout = setTimeout(() => { this.filterTerm = ""; }, 800); } _focusItem(item) { this.lastAction = "focus"; if (this.focusedItem && this.focusedItem.element) { this.focusedItem.element.classList.remove("focused"); } this.focusedItem = item; if (item && item.element) { item.element.classList.add("focused"); item.element.scrollIntoView({behavior: "smooth", block: "nearest", inline: "start"}); } } ////////////////////////////////////// /////// Data List Generation ///////// ////////////////////////////////////// headerFilterInitialListGen() { this._generateOptions(true); } rebuildOptionsList() { this._generateOptions().then(this._sortOptions.bind(this)).then(this._buildList.bind(this)).then(this._showList.bind(this)).catch((e) => { if (!Number.isInteger(e)) { console.error("List generation error", e); } }); } _filterList() { this._buildList(this._filterOptions()); this._showList(); } _generateOptions(silent) { var values = []; var iteration = ++this.listIteration; this.filtered = false; if (this.params.values) { values = this.params.values; } else if (this.params.valuesURL) { values = this._ajaxRequest(this.params.valuesURL, this.input.value); } else { if (typeof this.params.valuesLookup === "function") { values = this.params.valuesLookup(this.cell, this.input.value); } else if (this.params.valuesLookup) { values = this._uniqueColumnValues(this.params.valuesLookupField); } } if (values instanceof Promise) { if (!silent) { this._addPlaceholder(this.params.placeholderLoading); } return values.then().then((responseValues) => { if (this.listIteration === iteration) { return this._parseList(responseValues); } else { return Promise.reject(iteration); } }); } else { return Promise.resolve(this._parseList(values)); } } _addPlaceholder(contents) { var placeholder = document.createElement("div"); if (typeof contents === "function") { contents = contents(this.cell.getComponent(), this.listEl); } if (contents) { this._clearList(); if (contents instanceof HTMLElement) { placeholder = contents; } else { placeholder.classList.add("tabulator-edit-list-placeholder"); placeholder.innerHTML = contents; } this.listEl.appendChild(placeholder); this._showList(); } } _ajaxRequest(url, term) { var params = this.params.filterRemote ? {term} : {}; url = urlBuilder(url, {}, params); return fetch(url).then((response) => { if (response.ok) { return response.json().catch((error) => { console.warn("List Ajax Load Error - Invalid JSON returned", error); return Promise.reject(error); }); } else { console.error("List Ajax Load Error - Connection Error: " + response.status, response.statusText); return Promise.reject(response); } }).catch((error) => { console.error("List Ajax Load Error - Connection Error: ", error); return Promise.reject(error); }); } _uniqueColumnValues(field) { var output = {}, data = this.table.getData(this.params.valuesLookup), column; if (field) { column = this.table.columnManager.getColumnByField(field); } else { column = this.cell.getColumn()._getSelf(); } if (column) { data.forEach((row) => { var val = column.getFieldValue(row); if (!this._emptyValueCheck(val)) { if (this.params.multiselect && Array.isArray(val)) { val.forEach((item) => { if (!this._emptyValueCheck(item)) { output[item] = true; } }); } else { output[val] = true; } } }); } else { console.warn("unable to find matching column to create select lookup list:", field); output = []; } return Object.keys(output); } _emptyValueCheck(value) { return value === null || typeof value === "undefined" || value === ""; } _parseList(inputValues) { var data = []; if (!Array.isArray(inputValues)) { inputValues = Object.entries(inputValues).map(([key, value]) => { return { label: value, value: key }; }); } inputValues.forEach((value) => { if (typeof value !== "object") { value = { label: value, value }; } this._parseListItem(value, data, 0); }); if (!this.currentItems.length && this.params.freetext) { this.input.value = this.initialValues; this.typing = true; this.lastAction = "typing"; } if (this.params.multiselect) { this.initialValues = null; } this.data = data; return data; } _parseListItem(option, data, level) { var item = {}; if (option.options) { item = this._parseListGroup(option, level + 1); } else { item = { label: option.label, value: option.value, itemParams: option.itemParams, elementAttributes: option.elementAttributes, element: false, selected: false, visible: true, level, original: option }; if (this.params.multiselect) { var existingIndex = this.currentItems.findIndex((existing) => existing.value === option.value); if (existingIndex > -1) { if (this.focusedItem === this.currentItems[existingIndex]) { this.focusedItem = item; } this.currentItems[existingIndex] = item; item.selected = true; } else if (this.initialValues && this.initialValues.indexOf(option.value) > -1) { this._chooseItem(item, true); } } else if (this.initialValues && this.initialValues.indexOf(option.value) > -1) { this._chooseItem(item, true); } } data.push(item); } _parseListGroup(option, level) { var item = { label: option.label, group: true, itemParams: option.itemParams, elementAttributes: option.elementAttributes, element: false, visible: true, level, options: [], original: option }; option.options.forEach((child) => { this._parseListItem(child, item.options, level); }); return item; } _sortOptions(options) { var sorter; if (this.params.sort) { sorter = typeof this.params.sort === "function" ? this.params.sort : this._defaultSortFunction.bind(this); this._sortGroup(sorter, options); } return options; } _sortGroup(sorter, options) { options.sort((a, b) => { return sorter(a.label, b.label, a.value, b.value, a.original, b.original); }); options.forEach((option) => { if (option.group) { this._sortGroup(sorter, option.options); } }); } _defaultSortFunction(as, bs) { var a, b, a1, b1, i = 0, L, rx = /(\d+)|(\D+)/g, rd = /\d/; var emptyAlign = 0; if (this.params.sort === "desc") { [as, bs] = [bs, as]; } if (!as && as !== 0) { emptyAlign = !bs && bs !== 0 ? 0 : -1; } else if (!bs && bs !== 0) { emptyAlign = 1; } else { if (isFinite(as) && isFinite(bs)) return as - bs; a = String(as).toLowerCase(); b = String(bs).toLowerCase(); if (a === b) return 0; if (!(rd.test(a) && rd.test(b))) return a > b ? 1 : -1; a = a.match(rx); b = b.match(rx); L = a.length > b.length ? b.length : a.length; while (i < L) { a1 = a[i]; b1 = b[i++]; if (a1 !== b1) { if (isFinite(a1) && isFinite(b1)) { if (a1.charAt(0) === "0") a1 = "." + a1; if (b1.charAt(0) === "0") b1 = "." + b1; return a1 - b1; } else return a1 > b1 ? 1 : -1; } } return a.length > b.length; } return emptyAlign; } _filterOptions() { var filterFunc = this.params.filterFunc || this._defaultFilterFunc, term = this.input.value; if (term) { this.filtered = true; this.data.forEach((item) => { this._filterItem(filterFunc, term, item); }); } else { this.filtered = false; } return this.data; } _filterItem(func, term, item) { var matches = false; if (!item.group) { item.visible = func(term, item.label, item.value, item.original); } else { item.options.forEach((option) => { if (this._filterItem(func, term, option)) { matches = true; } }); item.visible = matches; } return item.visible; } _defaultFilterFunc(term, label, value, item) { term = String(term).toLowerCase(); if (label !== null && typeof label !== "undefined") { if (String(label).toLowerCase().indexOf(term) > -1 || String(value).toLowerCase().indexOf(term) > -1) { return true; } } return false; } ////////////////////////////////////// /////////// Display List ///////////// ////////////////////////////////////// _clearList() { while (this.listEl.firstChild) this.listEl.removeChild(this.listEl.firstChild); this.displayItems = []; } _buildList(data) { this._clearList(); data.forEach((option) => { this._buildItem(option); }); if (!this.displayItems.length) { this._addPlaceholder(this.params.placeholderEmpty); } } _buildItem(item) { var el = item.element, contents; if (!this.filtered || item.visible) { if (!el) { el = document.createElement("div"); el.tabIndex = 0; contents = this.params.itemFormatter ? this.params.itemFormatter(item.label, item.value, item.original, el) : item.label; if (contents instanceof HTMLElement) { el.appendChild(contents); } else { el.innerHTML = contents; } if (item.group) { el.classList.add("tabulator-edit-list-group"); } else { el.classList.add("tabulator-edit-list-item"); } el.classList.add("tabulator-edit-list-group-level-" + item.level); if (item.elementAttributes && typeof item.elementAttributes == "object") { for (let key in item.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); el.setAttribute(key, this.input.getAttribute(key) + item.elementAttributes["+" + key]); } else { el.setAttribute(key, item.elementAttributes[key]); } } } if (item.group) { el.addEventListener("click", this._groupClick.bind(this, item)); } else { el.addEventListener("click", this._itemClick.bind(this, item)); } el.addEventListener("mousedown", this._preventBlur.bind(this)); item.element = el; } this._styleItem(item); this.listEl.appendChild(el); if (item.group) { item.options.forEach((option) => { this._buildItem(option); }); } else { this.displayItems.push(item); } } } _showList() { var startVis = this.popup && this.popup.isVisible(); if (this.input.parentNode) { if (this.params.autocomplete && this.input.value === "" && !this.params.listOnEmpty) { if (this.popup) { this.popup.hide(true); } return; } this._setListWidth(); if (!this.popup) { this.popup = this.edit.popup(this.listEl); } this.popup.show(this.cell.getElement(), "bottom"); if (!startVis) { setTimeout(() => { this.popup.hideOnBlur(this._resolveValue.bind(this, true)); }, 10); } } } _styleItem(item) { if (item && item.element) { if (item.selected) { item.element.classList.add("active"); } else { item.element.classList.remove("active"); } } } ////////////////////////////////////// ///////// User Interaction /////////// ////////////////////////////////////// _itemClick(item, e) { e.stopPropagation(); this._chooseItem(item); } _groupClick(item, e) { e.stopPropagation(); } ////////////////////////////////////// ////// Current Item Management /////// ////////////////////////////////////// _cancel() { this.popup.hide(true); this.actions.cancel(); } _clearChoices() { this.typing = true; this.currentItems.forEach((item) => { item.selected = false; this._styleItem(item); }); this.currentItems = []; this.focusedItem = null; } _chooseItem(item, silent) { var index; this.typing = false; if (this.params.multiselect) { index = this.currentItems.indexOf(item); if (index > -1) { this.currentItems.splice(index, 1); item.selected = false; } else { this.currentItems.push(item); item.selected = true; } this.input.value = this.currentItems.map((item2) => item2.label).join(","); this._styleItem(item); } else { if (this.isFilter && !silent && item.selected) { this._clearChoices(); this.input.value = ""; this._resolveValue(); return; } this.currentItems = [item]; item.selected = true; this.input.value = item.label; this._styleItem(item); if (!silent) { this._resolveValue(); } } this._focusItem(item); } _resolveValue(blur) { var output, initialValue; if (this.popup) { this.popup.hide(true); } if (this.params.multiselect) { output = this.currentItems.map((item) => item.value); } else { if (blur && this.params.autocomplete && this.typing) { if (this.params.freetext || this.params.allowEmpty && this.input.value === "") { output = this.input.value; } else { this.actions.cancel(); return; } } else { if (this.currentItems[0]) { output = this.currentItems[0].value; } else if (this.isFilter && this.focusedItem && this.focusedItem.selected) { output = this.focusedItem.value; } else { initialValue = Array.isArray(this.initialValues) ? this.initialValues[0] : this.initialValues; if (initialValue === null || typeof initialValue === "undefined" || initialValue === "") { output = initialValue; } else { output = this.params.emptyValue; } } } } if (output === "") { output = this.params.emptyValue; } this.actions.success(output); if (this.isFilter) { this.initialValues = output && !Array.isArray(output) ? [output] : output; this.currentItems = []; } } }; function list(cell, onRendered, success, cancel, editorParams) { var list2 = new Edit$1(this, cell, onRendered, success, cancel, editorParams); return list2.input; } function star$1(cell, onRendered, success, cancel, editorParams) { var self = this, element = cell.getElement(), value = cell.getValue(), maxStars = element.getElementsByTagName("svg").length || 5, size = element.getElementsByTagName("svg")[0] ? element.getElementsByTagName("svg")[0].getAttribute("width") : 14, stars = [], starsHolder = document.createElement("div"), star2 = document.createElementNS("http://www.w3.org/2000/svg", "svg"); function starChange(val) { stars.forEach(function (star3, i2) { if (i2 < val) { if (self.table.browser == "ie") { star3.setAttribute("class", "tabulator-star-active"); } else { star3.classList.replace("tabulator-star-inactive", "tabulator-star-active"); } star3.innerHTML = ''; } else { if (self.table.browser == "ie") { star3.setAttribute("class", "tabulator-star-inactive"); } else { star3.classList.replace("tabulator-star-active", "tabulator-star-inactive"); } star3.innerHTML = ''; } }); } function buildStar(i2) { var starHolder = document.createElement("span"); var nextStar = star2.cloneNode(true); stars.push(nextStar); starHolder.addEventListener("mouseenter", function (e) { e.stopPropagation(); e.stopImmediatePropagation(); starChange(i2); }); starHolder.addEventListener("mousemove", function (e) { e.stopPropagation(); e.stopImmediatePropagation(); }); starHolder.addEventListener("click", function (e) { e.stopPropagation(); e.stopImmediatePropagation(); success(i2); element.blur(); }); starHolder.appendChild(nextStar); starsHolder.appendChild(starHolder); } function changeValue(val) { value = val; starChange(val); } element.style.whiteSpace = "nowrap"; element.style.overflow = "hidden"; element.style.textOverflow = "ellipsis"; starsHolder.style.verticalAlign = "middle"; starsHolder.style.display = "inline-block"; starsHolder.style.padding = "4px"; star2.setAttribute("width", size); star2.setAttribute("height", size); star2.setAttribute("viewBox", "0 0 512 512"); star2.setAttribute("xml:space", "preserve"); star2.style.padding = "0 1px"; if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); starsHolder.setAttribute(key, starsHolder.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { starsHolder.setAttribute(key, editorParams.elementAttributes[key]); } } } for (var i = 1; i <= maxStars; i++) { buildStar(i); } value = Math.min(parseInt(value), maxStars); starChange(value); starsHolder.addEventListener("mousemove", function (e) { starChange(0); }); starsHolder.addEventListener("click", function (e) { success(0); }); element.addEventListener("blur", function (e) { cancel(); }); element.addEventListener("keydown", function (e) { switch (e.key) { case "ArrowRight": changeValue(value + 1); break; case "ArrowLeft": changeValue(value - 1); break; case "Enter": success(value); break; case "Escape": cancel(); break; } }); return starsHolder; } function progress$1(cell, onRendered, success, cancel, editorParams) { var element = cell.getElement(), max = typeof editorParams.max === "undefined" ? element.getElementsByTagName("div")[0] && element.getElementsByTagName("div")[0].getAttribute("max") || 100 : editorParams.max, min = typeof editorParams.min === "undefined" ? element.getElementsByTagName("div")[0] && element.getElementsByTagName("div")[0].getAttribute("min") || 0 : editorParams.min, percent = (max - min) / 100, value = cell.getValue() || 0, handle2 = document.createElement("div"), bar = document.createElement("div"), mouseDrag, mouseDragWidth; function updateValue() { var style = window.getComputedStyle(element, null); var calcVal = percent * Math.round(bar.offsetWidth / ((element.clientWidth - parseInt(style.getPropertyValue("padding-left")) - parseInt(style.getPropertyValue("padding-right"))) / 100)) + min; success(calcVal); element.setAttribute("aria-valuenow", calcVal); element.setAttribute("aria-label", value); } handle2.style.position = "absolute"; handle2.style.right = "0"; handle2.style.top = "0"; handle2.style.bottom = "0"; handle2.style.width = "5px"; handle2.classList.add("tabulator-progress-handle"); bar.style.display = "inline-block"; bar.style.position = "relative"; bar.style.height = "100%"; bar.style.backgroundColor = "#488CE9"; bar.style.maxWidth = "100%"; bar.style.minWidth = "0%"; if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); bar.setAttribute(key, bar.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { bar.setAttribute(key, editorParams.elementAttributes[key]); } } } element.style.padding = "4px 4px"; value = Math.min(parseFloat(value), max); value = Math.max(parseFloat(value), min); value = Math.round((value - min) / percent); bar.style.width = value + "%"; element.setAttribute("aria-valuemin", min); element.setAttribute("aria-valuemax", max); bar.appendChild(handle2); handle2.addEventListener("mousedown", function (e) { mouseDrag = e.screenX; mouseDragWidth = bar.offsetWidth; }); handle2.addEventListener("mouseover", function () { handle2.style.cursor = "ew-resize"; }); element.addEventListener("mousemove", function (e) { if (mouseDrag) { bar.style.width = mouseDragWidth + e.screenX - mouseDrag + "px"; } }); element.addEventListener("mouseup", function (e) { if (mouseDrag) { e.stopPropagation(); e.stopImmediatePropagation(); mouseDrag = false; mouseDragWidth = false; updateValue(); } }); element.addEventListener("keydown", function (e) { switch (e.key) { case "ArrowRight": e.preventDefault(); bar.style.width = bar.clientWidth + element.clientWidth / 100 + "px"; break; case "ArrowLeft": e.preventDefault(); bar.style.width = bar.clientWidth - element.clientWidth / 100 + "px"; break; case "Tab": case "Enter": updateValue(); break; case "Escape": cancel(); break; } }); element.addEventListener("blur", function () { cancel(); }); return bar; } function tickCross$1(cell, onRendered, success, cancel, editorParams) { var value = cell.getValue(), input2 = document.createElement("input"), tristate = editorParams.tristate, indetermValue = typeof editorParams.indeterminateValue === "undefined" ? null : editorParams.indeterminateValue, indetermState = false, trueValueSet = Object.keys(editorParams).includes("trueValue"), falseValueSet = Object.keys(editorParams).includes("falseValue"); input2.setAttribute("type", "checkbox"); input2.style.marginTop = "5px"; input2.style.boxSizing = "border-box"; if (editorParams.elementAttributes && typeof editorParams.elementAttributes == "object") { for (let key in editorParams.elementAttributes) { if (key.charAt(0) == "+") { key = key.slice(1); input2.setAttribute(key, input2.getAttribute(key) + editorParams.elementAttributes["+" + key]); } else { input2.setAttribute(key, editorParams.elementAttributes[key]); } } } input2.value = value; if (tristate && (typeof value === "undefined" || value === indetermValue || value === "")) { indetermState = true; input2.indeterminate = true; } if (this.table.browser != "firefox" && this.table.browser != "safari") { onRendered(function () { if (cell.getType() === "cell") { input2.focus({preventScroll: true}); } }); } input2.checked = trueValueSet ? value === editorParams.trueValue : value === true || value === "true" || value === "True" || value === 1; function setValue(blur) { var checkedValue = input2.checked; if (trueValueSet && checkedValue) { checkedValue = editorParams.trueValue; } else if (falseValueSet && !checkedValue) { checkedValue = editorParams.falseValue; } if (tristate) { if (!blur) { if (input2.checked && !indetermState) { input2.checked = false; input2.indeterminate = true; indetermState = true; return indetermValue; } else { indetermState = false; return checkedValue; } } else { if (indetermState) { return indetermValue; } else { return checkedValue; } } } else { return checkedValue; } } input2.addEventListener("change", function (e) { success(setValue()); }); input2.addEventListener("blur", function (e) { success(setValue(true)); }); input2.addEventListener("keydown", function (e) { if (e.key == "Enter") { success(setValue()); } if (e.key == "Escape") { cancel(); } }); return input2; } function adaptable$1(cell, onRendered, success, cancel, params) { var column = cell._getSelf().column, lookup2, editorFunc, editorParams; function defaultLookup(cell2) { var value = cell2.getValue(), editor = "input"; switch (typeof value) { case "number": editor = "number"; break; case "boolean": editor = "tickCross"; break; case "string": if (value.includes("\n")) { editor = "textarea"; } break; } return editor; } lookup2 = params.editorLookup ? params.editorLookup(cell) : defaultLookup(cell); if (params.paramsLookup) { editorParams = typeof params.paramsLookup === "function" ? params.paramsLookup(lookup2, cell) : params.paramsLookup[lookup2]; } editorFunc = this.table.modules.edit.lookupEditor(lookup2, column); return editorFunc.call(this, cell, onRendered, success, cancel, editorParams || {}); } var defaultEditors = { input, textarea: textarea$1, number: number$1, range, date: date$1, time: time$1, datetime: datetime$2, list, star: star$1, progress: progress$1, tickCross: tickCross$1, adaptable: adaptable$1 }; const _Edit = class _Edit extends Module { constructor(table) { super(table); this.currentCell = false; this.mouseClick = false; this.recursionBlock = false; this.invalidEdit = false; this.editedCells = []; this.convertEmptyValues = false; this.editors = _Edit.editors; this.registerTableOption("editTriggerEvent", "focus"); this.registerTableOption("editorEmptyValue"); this.registerTableOption("editorEmptyValueFunc", this.emptyValueCheck.bind(this)); this.registerColumnOption("editable"); this.registerColumnOption("editor"); this.registerColumnOption("editorParams"); this.registerColumnOption("editorEmptyValue"); this.registerColumnOption("editorEmptyValueFunc"); this.registerColumnOption("cellEditing"); this.registerColumnOption("cellEdited"); this.registerColumnOption("cellEditCancelled"); this.registerTableFunction("getEditedCells", this.getEditedCells.bind(this)); this.registerTableFunction("clearCellEdited", this.clearCellEdited.bind(this)); this.registerTableFunction("navigatePrev", this.navigatePrev.bind(this)); this.registerTableFunction("navigateNext", this.navigateNext.bind(this)); this.registerTableFunction("navigateLeft", this.navigateLeft.bind(this)); this.registerTableFunction("navigateRight", this.navigateRight.bind(this)); this.registerTableFunction("navigateUp", this.navigateUp.bind(this)); this.registerTableFunction("navigateDown", this.navigateDown.bind(this)); this.registerComponentFunction("cell", "isEdited", this.cellIsEdited.bind(this)); this.registerComponentFunction("cell", "clearEdited", this.clearEdited.bind(this)); this.registerComponentFunction("cell", "edit", this.editCell.bind(this)); this.registerComponentFunction("cell", "cancelEdit", this.cellCancelEdit.bind(this)); this.registerComponentFunction("cell", "navigatePrev", this.navigatePrev.bind(this)); this.registerComponentFunction("cell", "navigateNext", this.navigateNext.bind(this)); this.registerComponentFunction("cell", "navigateLeft", this.navigateLeft.bind(this)); this.registerComponentFunction("cell", "navigateRight", this.navigateRight.bind(this)); this.registerComponentFunction("cell", "navigateUp", this.navigateUp.bind(this)); this.registerComponentFunction("cell", "navigateDown", this.navigateDown.bind(this)); } initialize() { this.subscribe("cell-init", this.bindEditor.bind(this)); this.subscribe("cell-delete", this.clearEdited.bind(this)); this.subscribe("cell-value-changed", this.updateCellClass.bind(this)); this.subscribe("column-layout", this.initializeColumnCheck.bind(this)); this.subscribe("column-delete", this.columnDeleteCheck.bind(this)); this.subscribe("row-deleting", this.rowDeleteCheck.bind(this)); this.subscribe("row-layout", this.rowEditableCheck.bind(this)); this.subscribe("data-refreshing", this.cancelEdit.bind(this)); this.subscribe("clipboard-paste", this.pasteBlocker.bind(this)); if (!this.confirm("edit-nav-disabled")) { this.subscribe("keybinding-nav-prev", this.navigatePrev.bind(this, void 0)); this.subscribe("keybinding-nav-next", this.keybindingNavigateNext.bind(this)); this.subscribe("keybinding-nav-up", this.navigateUp.bind(this, void 0)); this.subscribe("keybinding-nav-down", this.navigateDown.bind(this, void 0)); } this.subscribe("edit-check-editing", this.checkEditing.bind(this)); this.subscribe("edit-cancel-cell", this.cancelEditEvent.bind(this)); if (Object.keys(this.table.options).includes("editorEmptyValue")) { this.convertEmptyValues = true; } } /////////////////////////////////// ///////// Paste Negation ////////// /////////////////////////////////// pasteBlocker(e) { if (this.currentCell) { return true; } } /////////////////////////////////// ////// Keybinding Functions /////// /////////////////////////////////// keybindingNavigateNext(e) { var cell = this.currentCell, newRow = this.options("tabEndNewRow"); if (cell) { if (!this.navigateNext(cell, e)) { if (newRow) { cell.getElement().firstChild.blur(); if (!this.invalidEdit) { if (newRow === true) { newRow = this.table.addRow({}); } else { if (typeof newRow == "function") { newRow = this.table.addRow(newRow(cell.row.getComponent())); } else { newRow = this.table.addRow(Object.assign({}, newRow)); } } newRow.then(() => { setTimeout(() => { cell.getComponent().navigateNext(); }); }); } } } } } /////////////////////////////////// ///////// Cell Functions ////////// /////////////////////////////////// cellIsEdited(cell) { return !!cell.modules.edit && cell.modules.edit.edited; } cellCancelEdit(cell) { if (cell === this.currentCell) { this.table.modules.edit.cancelEdit(); } else { console.warn("Cancel Editor Error - This cell is not currently being edited "); } } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// updateCellClass(cell) { if (this.allowEdit(cell)) { cell.getElement().classList.add("tabulator-editable"); } else { cell.getElement().classList.remove("tabulator-editable"); } } clearCellEdited(cells) { if (!cells) { cells = this.table.modules.edit.getEditedCells(); } if (!Array.isArray(cells)) { cells = [cells]; } cells.forEach((cell) => { this.table.modules.edit.clearEdited(cell._getSelf()); }); } navigatePrev(cell = this.currentCell, e) { var nextCell, prevRow; if (cell) { if (e) { e.preventDefault(); } nextCell = this.navigateLeft(); if (nextCell) { return true; } else { prevRow = this.table.rowManager.prevDisplayRow(cell.row, true); if (prevRow) { nextCell = this.findPrevEditableCell(prevRow, prevRow.cells.length); if (nextCell) { nextCell.getComponent().edit(); return true; } } } } return false; } navigateNext(cell = this.currentCell, e) { var nextCell, nextRow; if (cell) { if (e) { e.preventDefault(); } nextCell = this.navigateRight(); if (nextCell) { return true; } else { nextRow = this.table.rowManager.nextDisplayRow(cell.row, true); if (nextRow) { nextCell = this.findNextEditableCell(nextRow, -1); if (nextCell) { nextCell.getComponent().edit(); return true; } } } } return false; } navigateLeft(cell = this.currentCell, e) { var index, nextCell; if (cell) { if (e) { e.preventDefault(); } index = cell.getIndex(); nextCell = this.findPrevEditableCell(cell.row, index); if (nextCell) { nextCell.getComponent().edit(); return true; } } return false; } navigateRight(cell = this.currentCell, e) { var index, nextCell; if (cell) { if (e) { e.preventDefault(); } index = cell.getIndex(); nextCell = this.findNextEditableCell(cell.row, index); if (nextCell) { nextCell.getComponent().edit(); return true; } } return false; } navigateUp(cell = this.currentCell, e) { var index, nextRow; if (cell) { if (e) { e.preventDefault(); } index = cell.getIndex(); nextRow = this.table.rowManager.prevDisplayRow(cell.row, true); if (nextRow) { nextRow.cells[index].getComponent().edit(); return true; } } return false; } navigateDown(cell = this.currentCell, e) { var index, nextRow; if (cell) { if (e) { e.preventDefault(); } index = cell.getIndex(); nextRow = this.table.rowManager.nextDisplayRow(cell.row, true); if (nextRow) { nextRow.cells[index].getComponent().edit(); return true; } } return false; } findNextEditableCell(row, index) { var nextCell = false; if (index < row.cells.length - 1) { for (var i = index + 1; i < row.cells.length; i++) { let cell = row.cells[i]; if (cell.column.modules.edit && Helpers.elVisible(cell.getElement())) { let allowEdit = this.allowEdit(cell); if (allowEdit) { nextCell = cell; break; } } } } return nextCell; } findPrevEditableCell(row, index) { var prevCell = false; if (index > 0) { for (var i = index - 1; i >= 0; i--) { let cell = row.cells[i]; if (cell.column.modules.edit && Helpers.elVisible(cell.getElement())) { let allowEdit = this.allowEdit(cell); if (allowEdit) { prevCell = cell; break; } } } } return prevCell; } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// initializeColumnCheck(column) { if (typeof column.definition.editor !== "undefined") { this.initializeColumn(column); } } columnDeleteCheck(column) { if (this.currentCell && this.currentCell.column === column) { this.cancelEdit(); } } rowDeleteCheck(row) { if (this.currentCell && this.currentCell.row === row) { this.cancelEdit(); } } rowEditableCheck(row) { row.getCells().forEach((cell) => { if (cell.column.modules.edit && typeof cell.column.modules.edit.check === "function") { this.updateCellClass(cell); } }); } //initialize column editor initializeColumn(column) { var convertEmpty = Object.keys(column.definition).includes("editorEmptyValue"); var config = { editor: false, blocked: false, check: column.definition.editable, params: column.definition.editorParams || {}, convertEmptyValues: convertEmpty, editorEmptyValue: column.definition.editorEmptyValue, editorEmptyValueFunc: column.definition.editorEmptyValueFunc }; config.editor = this.lookupEditor(column.definition.editor, column); if (config.editor) { column.modules.edit = config; } } lookupEditor(editor, column) { var editorFunc; switch (typeof editor) { case "string": if (this.editors[editor]) { editorFunc = this.editors[editor]; } else { console.warn("Editor Error - No such editor found: ", editor); } break; case "function": editorFunc = editor; break; case "boolean": if (editor === true) { if (typeof column.definition.formatter !== "function") { if (this.editors[column.definition.formatter]) { editorFunc = this.editors[column.definition.formatter]; } else { editorFunc = this.editors["input"]; } } else { console.warn("Editor Error - Cannot auto lookup editor for a custom formatter: ", column.definition.formatter); } } break; } return editorFunc; } getCurrentCell() { return this.currentCell ? this.currentCell.getComponent() : false; } checkEditing() { return !!this.currentCell; } cancelEditEvent() { if (this.currentCell) { this.cancelEdit(); return true; } return false; } clearEditor(cancel) { var cell = this.currentCell, cellEl; this.invalidEdit = false; if (cell) { this.currentCell = false; cellEl = cell.getElement(); this.dispatch("edit-editor-clear", cell, cancel); cellEl.classList.remove("tabulator-editing"); while (cellEl.firstChild) cellEl.removeChild(cellEl.firstChild); cell.row.getElement().classList.remove("tabulator-editing"); cell.table.element.classList.remove("tabulator-editing"); } } cancelEdit() { if (this.currentCell) { var cell = this.currentCell; var component = this.currentCell.getComponent(); this.clearEditor(true); cell.setValueActual(cell.getValue()); cell.cellRendered(); if (cell.column.definition.editor == "textarea" || cell.column.definition.variableHeight) { cell.row.normalizeHeight(true); } if (cell.column.definition.cellEditCancelled) { cell.column.definition.cellEditCancelled.call(this.table, component); } this.dispatch("edit-cancelled", cell); this.dispatchExternal("cellEditCancelled", component); } } //return a formatted value for a cell bindEditor(cell) { if (cell.column.modules.edit) { var self = this, element = cell.getElement(true); this.updateCellClass(cell); element.setAttribute("tabindex", 0); element.addEventListener("mousedown", function (e) { if (e.button === 2) { e.preventDefault(); } else { self.mouseClick = true; } }); if (this.options("editTriggerEvent") === "dblclick") { element.addEventListener("dblclick", function (e) { if (!element.classList.contains("tabulator-editing")) { element.focus({preventScroll: true}); self.edit(cell, e, false); } }); } if (this.options("editTriggerEvent") === "focus" || this.options("editTriggerEvent") === "click") { element.addEventListener("click", function (e) { if (!element.classList.contains("tabulator-editing")) { element.focus({preventScroll: true}); self.edit(cell, e, false); } }); } if (this.options("editTriggerEvent") === "focus") { element.addEventListener("focus", function (e) { if (!self.recursionBlock) { self.edit(cell, e, false); } }); } } } focusCellNoEvent(cell, block) { this.recursionBlock = true; if (!(block && this.table.browser === "ie")) { cell.getElement().focus({preventScroll: true}); } this.recursionBlock = false; } editCell(cell, forceEdit) { this.focusCellNoEvent(cell); this.edit(cell, false, forceEdit); } focusScrollAdjust(cell) { if (this.table.rowManager.getRenderMode() == "virtual") { var topEdge = this.table.rowManager.element.scrollTop, bottomEdge = this.table.rowManager.element.clientHeight + this.table.rowManager.element.scrollTop, rowEl = cell.row.getElement(); if (rowEl.offsetTop < topEdge) { this.table.rowManager.element.scrollTop -= topEdge - rowEl.offsetTop; } else { if (rowEl.offsetTop + rowEl.offsetHeight > bottomEdge) { this.table.rowManager.element.scrollTop += rowEl.offsetTop + rowEl.offsetHeight - bottomEdge; } } var leftEdge = this.table.rowManager.element.scrollLeft, rightEdge = this.table.rowManager.element.clientWidth + this.table.rowManager.element.scrollLeft, cellEl = cell.getElement(); if (this.table.modExists("frozenColumns")) { leftEdge += parseInt(this.table.modules.frozenColumns.leftMargin || 0); rightEdge -= parseInt(this.table.modules.frozenColumns.rightMargin || 0); } if (this.table.options.renderHorizontal === "virtual") { leftEdge -= parseInt(this.table.columnManager.renderer.vDomPadLeft); rightEdge -= parseInt(this.table.columnManager.renderer.vDomPadLeft); } if (cellEl.offsetLeft < leftEdge) { this.table.rowManager.element.scrollLeft -= leftEdge - cellEl.offsetLeft; } else { if (cellEl.offsetLeft + cellEl.offsetWidth > rightEdge) { this.table.rowManager.element.scrollLeft += cellEl.offsetLeft + cellEl.offsetWidth - rightEdge; } } } } allowEdit(cell) { var check = cell.column.modules.edit ? true : false; if (cell.column.modules.edit) { switch (typeof cell.column.modules.edit.check) { case "function": if (cell.row.initialized) { check = cell.column.modules.edit.check(cell.getComponent()); } break; case "string": check = !!cell.row.data[cell.column.modules.edit.check]; break; case "boolean": check = cell.column.modules.edit.check; break; } } return check; } edit(cell, e, forceEdit) { var self = this, allowEdit = true, rendered = function () { }, element = cell.getElement(), editFinished = false, cellEditor, component, params; if (this.currentCell) { if (!this.invalidEdit && this.currentCell !== cell) { this.cancelEdit(); } return; } function success(value) { if (self.currentCell === cell && !editFinished) { var valid = self.chain("edit-success", [cell, value], true, true); if (valid === true || self.table.options.validationMode === "highlight") { editFinished = true; self.clearEditor(); if (!cell.modules.edit) { cell.modules.edit = {}; } cell.modules.edit.edited = true; if (self.editedCells.indexOf(cell) == -1) { self.editedCells.push(cell); } value = self.transformEmptyValues(value, cell); cell.setValue(value, true); return valid === true; } else { editFinished = true; self.invalidEdit = true; self.focusCellNoEvent(cell, true); rendered(); setTimeout(() => { editFinished = false; }, 10); return false; } } } function cancel() { if (self.currentCell === cell && !editFinished) { self.cancelEdit(); } } function onRendered(callback) { rendered = callback; } if (!cell.column.modules.edit.blocked) { allowEdit = this.allowEdit(cell); if (allowEdit || forceEdit) { if (e) { e.stopPropagation(); } self.cancelEdit(); self.currentCell = cell; this.focusScrollAdjust(cell); component = cell.getComponent(); if (this.mouseClick) { this.mouseClick = false; if (cell.column.definition.cellClick) { cell.column.definition.cellClick.call(this.table, e, component); } } if (cell.column.definition.cellEditing) { cell.column.definition.cellEditing.call(this.table, component); } this.dispatch("cell-editing", cell); this.dispatchExternal("cellEditing", component); params = typeof cell.column.modules.edit.params === "function" ? cell.column.modules.edit.params(component) : cell.column.modules.edit.params; cellEditor = cell.column.modules.edit.editor.call(self, component, onRendered, success, cancel, params); if (this.currentCell && cellEditor !== false) { if (cellEditor instanceof Node) { element.classList.add("tabulator-editing"); cell.row.getElement().classList.add("tabulator-editing"); cell.table.element.classList.add("tabulator-editing"); while (element.firstChild) element.removeChild(element.firstChild); element.appendChild(cellEditor); rendered(); var children = element.children; for (var i = 0; i < children.length; i++) { children[i].addEventListener("click", function (e2) { e2.stopPropagation(); }); } } else { console.warn("Edit Error - Editor should return an instance of Node, the editor returned:", cellEditor); this.blur(element); return false; } } else { this.blur(element); return false; } return true; } else { this.mouseClick = false; this.blur(element); return false; } } else { this.mouseClick = false; this.blur(element); return false; } } emptyValueCheck(value) { return value === "" || value === null || typeof value === "undefined"; } transformEmptyValues(value, cell) { var mod = cell.column.modules.edit, convert = mod.convertEmptyValues || this.convertEmptyValues, checkFunc; if (convert) { checkFunc = mod.editorEmptyValueFunc || this.options("editorEmptyValueFunc"); if (checkFunc && checkFunc(value)) { value = mod.convertEmptyValues ? mod.editorEmptyValue : this.options("editorEmptyValue"); } } return value; } blur(element) { if (!this.confirm("edit-blur", [element])) { element.blur(); } } getEditedCells() { var output = []; this.editedCells.forEach((cell) => { output.push(cell.getComponent()); }); return output; } clearEdited(cell) { var editIndex; if (cell.modules.edit && cell.modules.edit.edited) { cell.modules.edit.edited = false; this.dispatch("edit-edited-clear", cell); } editIndex = this.editedCells.indexOf(cell); if (editIndex > -1) { this.editedCells.splice(editIndex, 1); } } }; __publicField(_Edit, "moduleName", "edit"); //load defaults __publicField(_Edit, "editors", defaultEditors); let Edit2 = _Edit; class ExportRow { constructor(type, columns, component, indent) { this.type = type; this.columns = columns; this.component = component || false; this.indent = indent || 0; } } class ExportColumn { constructor(value, component, width, height, depth) { this.value = value; this.component = component || false; this.width = width; this.height = height; this.depth = depth; } } var columnLookups$1 = {}; var rowLookups$1 = { visible: function () { return this.rowManager.getVisibleRows(false, true); }, all: function () { return this.rowManager.rows; }, selected: function () { return this.modules.selectRow.selectedRows; }, active: function () { if (this.options.pagination) { return this.rowManager.getDisplayRows(this.rowManager.displayRows.length - 2); } else { return this.rowManager.getDisplayRows(); } } }; const _Export = class _Export extends Module { constructor(table) { super(table); this.config = {}; this.cloneTableStyle = true; this.colVisProp = ""; this.colVisPropAttach = ""; this.registerTableOption("htmlOutputConfig", false); this.registerColumnOption("htmlOutput"); this.registerColumnOption("titleHtmlOutput"); } initialize() { this.registerTableFunction("getHtml", this.getHtml.bind(this)); } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// generateExportList(config, style, range2, colVisProp) { var headers, body, columns, colLookup; this.cloneTableStyle = style; this.config = config || {}; this.colVisProp = colVisProp; this.colVisPropAttach = this.colVisProp.charAt(0).toUpperCase() + this.colVisProp.slice(1); colLookup = _Export.columnLookups[range2]; if (colLookup) { columns = colLookup.call(this.table); columns = columns.filter((col) => this.columnVisCheck(col)); } headers = this.config.columnHeaders !== false ? this.headersToExportRows(this.generateColumnGroupHeaders(columns)) : []; if (columns) { columns = columns.map((col) => col.getComponent()); } body = this.bodyToExportRows(this.rowLookup(range2), columns); return headers.concat(body); } generateTable(config, style, range2, colVisProp) { var list2 = this.generateExportList(config, style, range2, colVisProp); return this.generateTableElement(list2); } rowLookup(range2) { var rows2 = [], rowLookup; if (typeof range2 == "function") { range2.call(this.table).forEach((row) => { row = this.table.rowManager.findRow(row); if (row) { rows2.push(row); } }); } else { rowLookup = _Export.rowLookups[range2] || _Export.rowLookups["active"]; rows2 = rowLookup.call(this.table); } return Object.assign([], rows2); } generateColumnGroupHeaders(columns) { var output = []; if (!columns) { columns = this.config.columnGroups !== false ? this.table.columnManager.columns : this.table.columnManager.columnsByIndex; } columns.forEach((column) => { var colData = this.processColumnGroup(column); if (colData) { output.push(colData); } }); return output; } processColumnGroup(column) { var subGroups = column.columns, maxDepth = 0, title = column.definition["title" + this.colVisPropAttach] || column.definition.title; var groupData = { title, column, depth: 1 }; if (subGroups.length) { groupData.subGroups = []; groupData.width = 0; subGroups.forEach((subGroup) => { var subGroupData = this.processColumnGroup(subGroup); if (subGroupData) { groupData.width += subGroupData.width; groupData.subGroups.push(subGroupData); if (subGroupData.depth > maxDepth) { maxDepth = subGroupData.depth; } } }); groupData.depth += maxDepth; if (!groupData.width) { return false; } } else { if (this.columnVisCheck(column)) { groupData.width = 1; } else { return false; } } return groupData; } columnVisCheck(column) { var visProp = column.definition[this.colVisProp]; if (this.config.rowHeaders === false && column.isRowHeader) { return false; } if (typeof visProp === "function") { visProp = visProp.call(this.table, column.getComponent()); } if (visProp === false || visProp === true) { return visProp; } return column.visible && column.field; } headersToExportRows(columns) { var headers = [], headerDepth = 0, exportRows = []; function parseColumnGroup(column, level) { var depth = headerDepth - level; if (typeof headers[level] === "undefined") { headers[level] = []; } column.height = column.subGroups ? 1 : depth - column.depth + 1; headers[level].push(column); if (column.height > 1) { for (let i = 1; i < column.height; i++) { if (typeof headers[level + i] === "undefined") { headers[level + i] = []; } headers[level + i].push(false); } } if (column.width > 1) { for (let i = 1; i < column.width; i++) { headers[level].push(false); } } if (column.subGroups) { column.subGroups.forEach(function (subGroup) { parseColumnGroup(subGroup, level + 1); }); } } columns.forEach(function (column) { if (column.depth > headerDepth) { headerDepth = column.depth; } }); columns.forEach(function (column) { parseColumnGroup(column, 0); }); headers.forEach((header) => { var columns2 = []; header.forEach((col) => { if (col) { let title = typeof col.title === "undefined" ? "" : col.title; columns2.push(new ExportColumn(title, col.column.getComponent(), col.width, col.height, col.depth)); } else { columns2.push(null); } }); exportRows.push(new ExportRow("header", columns2)); }); return exportRows; } bodyToExportRows(rows2, columns = []) { var exportRows = []; if (columns.length === 0) { this.table.columnManager.columnsByIndex.forEach((column) => { if (this.columnVisCheck(column)) { columns.push(column.getComponent()); } }); } if (this.config.columnCalcs !== false && this.table.modExists("columnCalcs")) { if (this.table.modules.columnCalcs.topInitialized) { rows2.unshift(this.table.modules.columnCalcs.topRow); } if (this.table.modules.columnCalcs.botInitialized) { rows2.push(this.table.modules.columnCalcs.botRow); } } rows2 = rows2.filter((row) => { switch (row.type) { case "group": return this.config.rowGroups !== false; case "calc": return this.config.columnCalcs !== false; case "row": return !(this.table.options.dataTree && this.config.dataTree === false && row.modules.dataTree.parent); } return true; }); rows2.forEach((row, i) => { var rowData = row.getData(this.colVisProp); var exportCols = []; var indent = 0; switch (row.type) { case "group": indent = row.level; exportCols.push(new ExportColumn(row.key, row.getComponent(), columns.length, 1)); break; case "calc": case "row": columns.forEach((col) => { exportCols.push(new ExportColumn(col._column.getFieldValue(rowData), col, 1, 1)); }); if (this.table.options.dataTree && this.config.dataTree !== false) { indent = row.modules.dataTree.index; } break; } exportRows.push(new ExportRow(row.type, exportCols, row.getComponent(), indent)); }); return exportRows; } generateTableElement(list2) { var table = document.createElement("table"), headerEl = document.createElement("thead"), bodyEl = document.createElement("tbody"), styles = this.lookupTableStyles(), rowFormatter = this.table.options["rowFormatter" + this.colVisPropAttach], setup = {}; setup.rowFormatter = rowFormatter !== null ? rowFormatter : this.table.options.rowFormatter; if (this.table.options.dataTree && this.config.dataTree !== false && this.table.modExists("columnCalcs")) { setup.treeElementField = this.table.modules.dataTree.elementField; } setup.groupHeader = this.table.options["groupHeader" + this.colVisPropAttach]; if (setup.groupHeader && !Array.isArray(setup.groupHeader)) { setup.groupHeader = [setup.groupHeader]; } table.classList.add("tabulator-print-table"); this.mapElementStyles(this.table.columnManager.getHeadersElement(), headerEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]); if (list2.length > 1e3) { console.warn("It may take a long time to render an HTML table with more than 1000 rows"); } list2.forEach((row, i) => { let rowEl; switch (row.type) { case "header": headerEl.appendChild(this.generateHeaderElement(row, setup, styles)); break; case "group": bodyEl.appendChild(this.generateGroupElement(row, setup, styles)); break; case "calc": bodyEl.appendChild(this.generateCalcElement(row, setup, styles)); break; case "row": rowEl = this.generateRowElement(row, setup, styles); this.mapElementStyles(i % 2 && styles.evenRow ? styles.evenRow : styles.oddRow, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]); bodyEl.appendChild(rowEl); break; } }); if (headerEl.innerHTML) { table.appendChild(headerEl); } table.appendChild(bodyEl); this.mapElementStyles(this.table.element, table, ["border-top", "border-left", "border-right", "border-bottom"]); return table; } lookupTableStyles() { var styles = {}; if (this.cloneTableStyle && window.getComputedStyle) { styles.oddRow = this.table.element.querySelector(".tabulator-row-odd:not(.tabulator-group):not(.tabulator-calcs)"); styles.evenRow = this.table.element.querySelector(".tabulator-row-even:not(.tabulator-group):not(.tabulator-calcs)"); styles.calcRow = this.table.element.querySelector(".tabulator-row.tabulator-calcs"); styles.firstRow = this.table.element.querySelector(".tabulator-row:not(.tabulator-group):not(.tabulator-calcs)"); styles.firstGroup = this.table.element.getElementsByClassName("tabulator-group")[0]; if (styles.firstRow) { styles.styleCells = styles.firstRow.getElementsByClassName("tabulator-cell"); styles.styleRowHeader = styles.firstRow.getElementsByClassName("tabulator-row-header")[0]; styles.firstCell = styles.styleCells[0]; styles.lastCell = styles.styleCells[styles.styleCells.length - 1]; } } return styles; } generateHeaderElement(row, setup, styles) { var rowEl = document.createElement("tr"); row.columns.forEach((column) => { if (column) { var cellEl = document.createElement("th"); var classNames = column.component._column.definition.cssClass ? column.component._column.definition.cssClass.split(" ") : []; cellEl.colSpan = column.width; cellEl.rowSpan = column.height; cellEl.innerHTML = column.value; if (this.cloneTableStyle) { cellEl.style.boxSizing = "border-box"; } classNames.forEach(function (className) { cellEl.classList.add(className); }); this.mapElementStyles(column.component.getElement(), cellEl, ["text-align", "border-left", "border-right", "background-color", "color", "font-weight", "font-family", "font-size"]); this.mapElementStyles(column.component._column.contentElement, cellEl, ["padding-top", "padding-left", "padding-right", "padding-bottom"]); if (column.component._column.visible) { this.mapElementStyles(column.component.getElement(), cellEl, ["width"]); } else { if (column.component._column.definition.width) { cellEl.style.width = column.component._column.definition.width + "px"; } } if (column.component._column.parent && column.component._column.parent.isGroup) { this.mapElementStyles(column.component._column.parent.groupElement, cellEl, ["border-top"]); } else { this.mapElementStyles(column.component.getElement(), cellEl, ["border-top"]); } if (column.component._column.isGroup) { this.mapElementStyles(column.component.getElement(), cellEl, ["border-bottom"]); } else { this.mapElementStyles(this.table.columnManager.getElement(), cellEl, ["border-bottom"]); } rowEl.appendChild(cellEl); } }); return rowEl; } generateGroupElement(row, setup, styles) { var rowEl = document.createElement("tr"), cellEl = document.createElement("td"), group = row.columns[0]; rowEl.classList.add("tabulator-print-table-row"); if (setup.groupHeader && setup.groupHeader[row.indent]) { group.value = setup.groupHeader[row.indent](group.value, row.component._group.getRowCount(), row.component._group.getData(), row.component); } else { if (setup.groupHeader !== false) { group.value = row.component._group.generator(group.value, row.component._group.getRowCount(), row.component._group.getData(), row.component); } } cellEl.colSpan = group.width; cellEl.innerHTML = group.value; rowEl.classList.add("tabulator-print-table-group"); rowEl.classList.add("tabulator-group-level-" + row.indent); if (group.component.isVisible()) { rowEl.classList.add("tabulator-group-visible"); } this.mapElementStyles(styles.firstGroup, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]); this.mapElementStyles(styles.firstGroup, cellEl, ["padding-top", "padding-left", "padding-right", "padding-bottom"]); rowEl.appendChild(cellEl); return rowEl; } generateCalcElement(row, setup, styles) { var rowEl = this.generateRowElement(row, setup, styles); rowEl.classList.add("tabulator-print-table-calcs"); this.mapElementStyles(styles.calcRow, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]); return rowEl; } generateRowElement(row, setup, styles) { var rowEl = document.createElement("tr"); rowEl.classList.add("tabulator-print-table-row"); row.columns.forEach((col, i) => { if (col) { var cellEl = document.createElement("td"), column = col.component._column, table = this.table, index = table.columnManager.findColumnIndex(column), value = col.value, cellStyle, styleProps; var cellWrapper = { modules: {}, getValue: function () { return value; }, getField: function () { return column.definition.field; }, getElement: function () { return cellEl; }, getType: function () { return "cell"; }, getColumn: function () { return column.getComponent(); }, getData: function () { return row.component.getData(); }, getRow: function () { return row.component; }, getTable: function () { return table; }, getComponent: function () { return cellWrapper; }, column }; var classNames = column.definition.cssClass ? column.definition.cssClass.split(" ") : []; classNames.forEach(function (className) { cellEl.classList.add(className); }); if (this.table.modExists("format") && this.config.formatCells !== false) { value = this.table.modules.format.formatExportValue(cellWrapper, this.colVisProp); } else { switch (typeof value) { case "object": value = value !== null ? JSON.stringify(value) : ""; break; case "undefined": value = ""; break; } } if (value instanceof Node) { cellEl.appendChild(value); } else { cellEl.innerHTML = value; } styleProps = ["padding-top", "padding-left", "padding-right", "padding-bottom", "border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "text-align"]; if (column.isRowHeader) { cellStyle = styles.styleRowHeader; styleProps.push("background-color"); } else { cellStyle = styles.styleCells && styles.styleCells[index] ? styles.styleCells[index] : styles.firstCell; } if (cellStyle) { this.mapElementStyles(cellStyle, cellEl, styleProps); if (column.definition.align) { cellEl.style.textAlign = column.definition.align; } } if (this.table.options.dataTree && this.config.dataTree !== false) { if (setup.treeElementField && setup.treeElementField == column.field || !setup.treeElementField && i == 0) { if (row.component._row.modules.dataTree.controlEl) { cellEl.insertBefore(row.component._row.modules.dataTree.controlEl.cloneNode(true), cellEl.firstChild); } if (row.component._row.modules.dataTree.branchEl) { cellEl.insertBefore(row.component._row.modules.dataTree.branchEl.cloneNode(true), cellEl.firstChild); } } } rowEl.appendChild(cellEl); if (cellWrapper.modules.format && cellWrapper.modules.format.renderedCallback) { cellWrapper.modules.format.renderedCallback(); } } }); if (setup.rowFormatter && row.type === "row" && this.config.formatCells !== false) { let formatComponent = Object.assign(row.component); formatComponent.getElement = function () { return rowEl; }; setup.rowFormatter(row.component); } return rowEl; } generateHTMLTable(list2) { var holder = document.createElement("div"); holder.appendChild(this.generateTableElement(list2)); return holder.innerHTML; } getHtml(visible, style, config, colVisProp) { var list2 = this.generateExportList(config || this.table.options.htmlOutputConfig, style, visible, colVisProp || "htmlOutput"); return this.generateHTMLTable(list2); } mapElementStyles(from, to, props) { if (this.cloneTableStyle && from && to) { var lookup2 = { "background-color": "backgroundColor", "color": "fontColor", "width": "width", "font-weight": "fontWeight", "font-family": "fontFamily", "font-size": "fontSize", "text-align": "textAlign", "border-top": "borderTop", "border-left": "borderLeft", "border-right": "borderRight", "border-bottom": "borderBottom", "padding-top": "paddingTop", "padding-left": "paddingLeft", "padding-right": "paddingRight", "padding-bottom": "paddingBottom" }; if (window.getComputedStyle) { var fromStyle = window.getComputedStyle(from); props.forEach(function (prop) { if (!to.style[lookup2[prop]]) { to.style[lookup2[prop]] = fromStyle.getPropertyValue(prop); } }); } } } }; __publicField(_Export, "moduleName", "export"); __publicField(_Export, "columnLookups", columnLookups$1); __publicField(_Export, "rowLookups", rowLookups$1); let Export = _Export; var defaultFilters = { //equal to "=": function (filterVal, rowVal, rowData, filterParams) { return rowVal == filterVal ? true : false; }, //less than "<": function (filterVal, rowVal, rowData, filterParams) { return rowVal < filterVal ? true : false; }, //less than or equal to "<=": function (filterVal, rowVal, rowData, filterParams) { return rowVal <= filterVal ? true : false; }, //greater than ">": function (filterVal, rowVal, rowData, filterParams) { return rowVal > filterVal ? true : false; }, //greater than or equal to ">=": function (filterVal, rowVal, rowData, filterParams) { return rowVal >= filterVal ? true : false; }, //not equal to "!=": function (filterVal, rowVal, rowData, filterParams) { return rowVal != filterVal ? true : false; }, "regex": function (filterVal, rowVal, rowData, filterParams) { if (typeof filterVal == "string") { filterVal = new RegExp(filterVal); } return filterVal.test(rowVal); }, //contains the string "like": function (filterVal, rowVal, rowData, filterParams) { if (filterVal === null || typeof filterVal === "undefined") { return rowVal === filterVal ? true : false; } else { if (typeof rowVal !== "undefined" && rowVal !== null) { return String(rowVal).toLowerCase().indexOf(filterVal.toLowerCase()) > -1; } else { return false; } } }, //contains the keywords "keywords": function (filterVal, rowVal, rowData, filterParams) { var keywords = filterVal.toLowerCase().split(typeof filterParams.separator === "undefined" ? " " : filterParams.separator), value = String(rowVal === null || typeof rowVal === "undefined" ? "" : rowVal).toLowerCase(), matches = []; keywords.forEach((keyword) => { if (value.includes(keyword)) { matches.push(true); } }); return filterParams.matchAll ? matches.length === keywords.length : !!matches.length; }, //starts with the string "starts": function (filterVal, rowVal, rowData, filterParams) { if (filterVal === null || typeof filterVal === "undefined") { return rowVal === filterVal ? true : false; } else { if (typeof rowVal !== "undefined" && rowVal !== null) { return String(rowVal).toLowerCase().startsWith(filterVal.toLowerCase()); } else { return false; } } }, //ends with the string "ends": function (filterVal, rowVal, rowData, filterParams) { if (filterVal === null || typeof filterVal === "undefined") { return rowVal === filterVal ? true : false; } else { if (typeof rowVal !== "undefined" && rowVal !== null) { return String(rowVal).toLowerCase().endsWith(filterVal.toLowerCase()); } else { return false; } } }, //in array "in": function (filterVal, rowVal, rowData, filterParams) { if (Array.isArray(filterVal)) { return filterVal.length ? filterVal.indexOf(rowVal) > -1 : true; } else { console.warn("Filter Error - filter value is not an array:", filterVal); return false; } }, // Smart filter // Supports ., !, <, >, <=, >=, = and falls back to like filter "smart": function (filterVal, rowVal, rowData, filterParams) { const search = filterVal.trim(); if (search === ".") return !(rowVal === null || rowVal == ""); if (search === "!") return rowVal === null || rowVal == ""; if (search.indexOf("<=") === 0) return this["<="]( parseFloat(search.substring(2)), rowVal, rowData, filterParams ); if (search.indexOf(">=") === 0) return this[">="]( parseFloat(search.substring(2)), rowVal, rowData, filterParams ); if (search.indexOf("<") === 0) return this["<"]( parseFloat(search.substring(1)), rowVal, rowData, filterParams ); if (search.indexOf(">") === 0) return this[">"]( parseFloat(search.substring(1)), rowVal, rowData, filterParams ); if (search.indexOf("=") === 0) return this["="](search.substring(1).trim(), rowVal, rowData, filterParams); if (search.includes(" ")) { const terms = search.split(/\s+/).filter((term) => term.length > 0); if (terms.length > 1) { const fuzzySearch = terms.join(" AND "); return this["smarter"](fuzzySearch, rowVal, rowData, filterParams); } } return this["like"](search, rowVal, rowData, filterParams); }, // Smarter filter // Just like the smart filter but you can combine multiple filters (AND/OR) // Examples: // - "john AND smith" - both terms must match // - "john OR jane" - either term must match // - ">100 AND <500" - value must be between 100 and 500 // - "! OR foo" - either empty or foo "smarter": function (filterVal, rowVal, rowData, filterParams) { const search = filterVal.trim(); if (!search) return true; const parts = search.split(/\s+(AND|OR)\s+/i); if (parts.length === 1) { return this["smart"](search, rowVal, rowData, filterParams); } let result = null; let currentOperator = null; for (let i = 0; i < parts.length; i++) { const part = parts[i].trim(); if (part === "AND" || part === "OR") { currentOperator = part; continue; } const partResult = this["smart"](part, rowVal, rowData, filterParams); if (result === null) { result = partResult; } else if (currentOperator === "AND") { result = result && partResult; } else if (currentOperator === "OR") { result = result || partResult; } } return result !== null ? result : true; } }; const _Filter = class _Filter extends Module { constructor(table) { super(table); this.filterList = []; this.headerFilters = {}; this.headerFilterColumns = []; this.prevHeaderFilterChangeCheck = ""; this.prevHeaderFilterChangeCheck = "{}"; this.changed = false; this.tableInitialized = false; this.registerTableOption("filterMode", "local"); this.registerTableOption("initialFilter", false); this.registerTableOption("initialHeaderFilter", false); this.registerTableOption("headerFilterLiveFilterDelay", 300); this.registerTableOption("placeholderHeaderFilter", false); this.registerColumnOption("headerFilter"); this.registerColumnOption("headerFilterPlaceholder"); this.registerColumnOption("headerFilterParams"); this.registerColumnOption("headerFilterEmptyCheck"); this.registerColumnOption("headerFilterFunc"); this.registerColumnOption("headerFilterFuncParams"); this.registerColumnOption("headerFilterLiveFilter"); this.registerTableFunction("searchRows", this.searchRows.bind(this)); this.registerTableFunction("searchData", this.searchData.bind(this)); this.registerTableFunction("setFilter", this.userSetFilter.bind(this)); this.registerTableFunction("refreshFilter", this.userRefreshFilter.bind(this)); this.registerTableFunction("addFilter", this.userAddFilter.bind(this)); this.registerTableFunction("getFilters", this.getFilters.bind(this)); this.registerTableFunction("setHeaderFilterFocus", this.userSetHeaderFilterFocus.bind(this)); this.registerTableFunction("getHeaderFilterValue", this.userGetHeaderFilterValue.bind(this)); this.registerTableFunction("setHeaderFilterValue", this.userSetHeaderFilterValue.bind(this)); this.registerTableFunction("getHeaderFilters", this.getHeaderFilters.bind(this)); this.registerTableFunction("removeFilter", this.userRemoveFilter.bind(this)); this.registerTableFunction("clearFilter", this.userClearFilter.bind(this)); this.registerTableFunction("clearHeaderFilter", this.userClearHeaderFilter.bind(this)); this.registerComponentFunction("column", "headerFilterFocus", this.setHeaderFilterFocus.bind(this)); this.registerComponentFunction("column", "reloadHeaderFilter", this.reloadHeaderFilter.bind(this)); this.registerComponentFunction("column", "getHeaderFilterValue", this.getHeaderFilterValue.bind(this)); this.registerComponentFunction("column", "setHeaderFilterValue", this.setHeaderFilterValue.bind(this)); } initialize() { this.subscribe("column-init", this.initializeColumnHeaderFilter.bind(this)); this.subscribe("column-width-fit-before", this.hideHeaderFilterElements.bind(this)); this.subscribe("column-width-fit-after", this.showHeaderFilterElements.bind(this)); this.subscribe("table-built", this.tableBuilt.bind(this)); this.subscribe("placeholder", this.generatePlaceholder.bind(this)); if (this.table.options.filterMode === "remote") { this.subscribe("data-params", this.remoteFilterParams.bind(this)); } this.registerDataHandler(this.filter.bind(this), 10); } tableBuilt() { if (this.table.options.initialFilter) { this.setFilter(this.table.options.initialFilter); } if (this.table.options.initialHeaderFilter) { this.table.options.initialHeaderFilter.forEach((item) => { var column = this.table.columnManager.findColumn(item.field); if (column) { this.setHeaderFilterValue(column, item.value); } else { console.warn("Column Filter Error - No matching column found:", item.field); return false; } }); } this.tableInitialized = true; } remoteFilterParams(data, config, silent, params) { params.filter = this.getFilters(true, true); return params; } generatePlaceholder(text) { if (this.table.options.placeholderHeaderFilter && Object.keys(this.headerFilters).length) { return this.table.options.placeholderHeaderFilter; } } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// //set standard filters userSetFilter(field, type, value, params) { this.setFilter(field, type, value, params); this.refreshFilter(); } //set standard filters userRefreshFilter() { this.refreshFilter(); } //add filter to array userAddFilter(field, type, value, params) { this.addFilter(field, type, value, params); this.refreshFilter(); } userSetHeaderFilterFocus(field) { var column = this.table.columnManager.findColumn(field); if (column) { this.setHeaderFilterFocus(column); } else { console.warn("Column Filter Focus Error - No matching column found:", field); return false; } } userGetHeaderFilterValue(field) { var column = this.table.columnManager.findColumn(field); if (column) { return this.getHeaderFilterValue(column); } else { console.warn("Column Filter Error - No matching column found:", field); } } userSetHeaderFilterValue(field, value) { var column = this.table.columnManager.findColumn(field); if (column) { this.setHeaderFilterValue(column, value); } else { console.warn("Column Filter Error - No matching column found:", field); return false; } } //remove filter from array userRemoveFilter(field, type, value) { this.removeFilter(field, type, value); this.refreshFilter(); } //clear filters userClearFilter(all) { this.clearFilter(all); this.refreshFilter(); } //clear header filters userClearHeaderFilter() { this.clearHeaderFilter(); this.refreshFilter(); } //search for specific row components searchRows(field, type, value) { return this.search("rows", field, type, value); } //search for specific data searchData(field, type, value) { return this.search("data", field, type, value); } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// initializeColumnHeaderFilter(column) { var def = column.definition; if (def.headerFilter) { this.initializeColumn(column); } } //initialize column header filter initializeColumn(column, value) { var self = this, field = column.getField(); function success(value2) { var filterType = column.modules.filter.tagType == "input" && column.modules.filter.attrType == "text" || column.modules.filter.tagType == "textarea" ? "partial" : "match", type = "", filterChangeCheck = "", filterFunc; if (typeof column.modules.filter.prevSuccess === "undefined" || column.modules.filter.prevSuccess !== value2) { column.modules.filter.prevSuccess = value2; if (!column.modules.filter.emptyFunc(value2)) { column.modules.filter.value = value2; switch (typeof column.definition.headerFilterFunc) { case "string": if (_Filter.filters[column.definition.headerFilterFunc]) { type = column.definition.headerFilterFunc; filterFunc = function (data) { var params = column.definition.headerFilterFuncParams || {}; var fieldVal = column.getFieldValue(data); params = typeof params === "function" ? params(value2, fieldVal, data) : params; return _Filter.filters[column.definition.headerFilterFunc](value2, fieldVal, data, params); }; } else { console.warn("Header Filter Error - Matching filter function not found: ", column.definition.headerFilterFunc); } break; case "function": filterFunc = function (data) { var params = column.definition.headerFilterFuncParams || {}; var fieldVal = column.getFieldValue(data); params = typeof params === "function" ? params(value2, fieldVal, data) : params; return column.definition.headerFilterFunc(value2, fieldVal, data, params); }; type = filterFunc; break; } if (!filterFunc) { switch (filterType) { case "partial": filterFunc = function (data) { var colVal = column.getFieldValue(data); if (typeof colVal !== "undefined" && colVal !== null) { return String(colVal).toLowerCase().indexOf(String(value2).toLowerCase()) > -1; } else { return false; } }; type = "like"; break; default: filterFunc = function (data) { return column.getFieldValue(data) == value2; }; type = "="; } } self.headerFilters[field] = {value: value2, func: filterFunc, type}; } else { delete self.headerFilters[field]; } column.modules.filter.value = value2; filterChangeCheck = JSON.stringify(self.headerFilters); if (self.prevHeaderFilterChangeCheck !== filterChangeCheck) { self.prevHeaderFilterChangeCheck = filterChangeCheck; self.trackChanges(); self.refreshFilter(); } } return true; } column.modules.filter = { success, attrType: false, tagType: false, emptyFunc: false }; this.generateHeaderFilterElement(column); } generateHeaderFilterElement(column, initialValue, reinitialize) { var self = this, success = column.modules.filter.success, field = column.getField(), filterElement, editor, editorElement, cellWrapper, typingTimer, searchTrigger, params, onRenderedCallback; column.modules.filter.value = initialValue; function cancel() { } function onRendered(callback) { onRenderedCallback = callback; } if (column.modules.filter.headerElement && column.modules.filter.headerElement.parentNode) { column.contentElement.removeChild(column.modules.filter.headerElement.parentNode); } if (field) { column.modules.filter.emptyFunc = column.definition.headerFilterEmptyCheck || function (value) { return !value && value !== 0; }; filterElement = document.createElement("div"); filterElement.classList.add("tabulator-header-filter"); switch (typeof column.definition.headerFilter) { case "string": if (self.table.modules.edit.editors[column.definition.headerFilter]) { editor = self.table.modules.edit.editors[column.definition.headerFilter]; if ((column.definition.headerFilter === "tick" || column.definition.headerFilter === "tickCross") && !column.definition.headerFilterEmptyCheck) { column.modules.filter.emptyFunc = function (value) { return value !== true && value !== false; }; } } else { console.warn("Filter Error - Cannot build header filter, No such editor found: ", column.definition.editor); } break; case "function": editor = column.definition.headerFilter; break; case "boolean": if (column.modules.edit && column.modules.edit.editor) { editor = column.modules.edit.editor; } else { if (column.definition.formatter && self.table.modules.edit.editors[column.definition.formatter]) { editor = self.table.modules.edit.editors[column.definition.formatter]; if ((column.definition.formatter === "tick" || column.definition.formatter === "tickCross") && !column.definition.headerFilterEmptyCheck) { column.modules.filter.emptyFunc = function (value) { return value !== true && value !== false; }; } } else { editor = self.table.modules.edit.editors["input"]; } } break; } if (editor) { cellWrapper = { getValue: function () { return typeof initialValue !== "undefined" ? initialValue : ""; }, getField: function () { return column.definition.field; }, getElement: function () { return filterElement; }, getColumn: function () { return column.getComponent(); }, getTable: () => { return this.table; }, getType: () => { return "header"; }, getRow: function () { return { normalizeHeight: function () { } }; } }; params = column.definition.headerFilterParams || {}; params = typeof params === "function" ? params.call(self.table, cellWrapper) : params; editorElement = editor.call(this.table.modules.edit, cellWrapper, onRendered, success, cancel, params); if (!editorElement) { console.warn("Filter Error - Cannot add filter to " + field + " column, editor returned a value of false"); return; } if (!(editorElement instanceof Node)) { console.warn("Filter Error - Cannot add filter to " + field + " column, editor should return an instance of Node, the editor returned:", editorElement); return; } self.langBind("headerFilters|columns|" + column.definition.field, function (value) { editorElement.setAttribute("placeholder", typeof value !== "undefined" && value ? value : column.definition.headerFilterPlaceholder || self.langText("headerFilters|default")); }); editorElement.addEventListener("click", function (e) { e.stopPropagation(); editorElement.focus(); }); editorElement.addEventListener("focus", (e) => { var left = this.table.columnManager.contentsElement.scrollLeft; var headerPos = this.table.rowManager.element.scrollLeft; if (left !== headerPos) { this.table.rowManager.scrollHorizontal(left); this.table.columnManager.scrollHorizontal(left); } }); typingTimer = false; searchTrigger = function (e) { if (typingTimer) { clearTimeout(typingTimer); } typingTimer = setTimeout(function () { success(editorElement.value); }, self.table.options.headerFilterLiveFilterDelay); }; column.modules.filter.headerElement = editorElement; column.modules.filter.attrType = editorElement.hasAttribute("type") ? editorElement.getAttribute("type").toLowerCase() : ""; column.modules.filter.tagType = editorElement.tagName.toLowerCase(); if (column.definition.headerFilterLiveFilter !== false) { if (!(column.definition.headerFilter === "autocomplete" || column.definition.headerFilter === "tickCross" || (column.definition.editor === "autocomplete" || column.definition.editor === "tickCross") && column.definition.headerFilter === true)) { editorElement.addEventListener("keyup", searchTrigger); editorElement.addEventListener("search", searchTrigger); if (column.modules.filter.attrType == "number") { editorElement.addEventListener("change", function (e) { success(editorElement.value); }); } if (column.modules.filter.attrType == "text" && this.table.browser !== "ie") { editorElement.setAttribute("type", "search"); } } if (column.modules.filter.tagType == "input" || column.modules.filter.tagType == "select" || column.modules.filter.tagType == "textarea") { editorElement.addEventListener("mousedown", function (e) { e.stopPropagation(); }); } } filterElement.appendChild(editorElement); column.contentElement.appendChild(filterElement); if (!reinitialize) { self.headerFilterColumns.push(column); } if (onRenderedCallback) { onRenderedCallback(); } } } else { console.warn("Filter Error - Cannot add header filter, column has no field set:", column.definition.title); } } //hide all header filter elements (used to ensure correct column widths in "fitData" layout mode) hideHeaderFilterElements() { this.headerFilterColumns.forEach(function (column) { if (column.modules.filter && column.modules.filter.headerElement) { column.modules.filter.headerElement.style.display = "none"; } }); } //show all header filter elements (used to ensure correct column widths in "fitData" layout mode) showHeaderFilterElements() { this.headerFilterColumns.forEach(function (column) { if (column.modules.filter && column.modules.filter.headerElement) { column.modules.filter.headerElement.style.display = ""; } }); } //programmatically set focus of header filter setHeaderFilterFocus(column) { if (column.modules.filter && column.modules.filter.headerElement) { column.modules.filter.headerElement.focus(); } else { console.warn("Column Filter Focus Error - No header filter set on column:", column.getField()); } } //programmatically get value of header filter getHeaderFilterValue(column) { if (column.modules.filter && column.modules.filter.headerElement) { return column.modules.filter.value; } else { console.warn("Column Filter Error - No header filter set on column:", column.getField()); } } //programmatically set value of header filter setHeaderFilterValue(column, value) { if (column) { if (column.modules.filter && column.modules.filter.headerElement) { this.generateHeaderFilterElement(column, value, true); column.modules.filter.success(value); } else { console.warn("Column Filter Error - No header filter set on column:", column.getField()); } } } reloadHeaderFilter(column) { if (column) { if (column.modules.filter && column.modules.filter.headerElement) { this.generateHeaderFilterElement(column, column.modules.filter.value, true); } else { console.warn("Column Filter Error - No header filter set on column:", column.getField()); } } } refreshFilter() { if (this.tableInitialized) { if (this.table.options.filterMode === "remote") { this.reloadData(null, false, false); } else { this.refreshData(true); } } } //check if the filters has changed since last use trackChanges() { this.changed = true; this.dispatch("filter-changed"); } //check if the filters has changed since last use hasChanged() { var changed = this.changed; this.changed = false; return changed; } //set standard filters setFilter(field, type, value, params) { this.filterList = []; if (!Array.isArray(field)) { field = [{field, type, value, params}]; } this.addFilter(field); } //add filter to array addFilter(field, type, value, params) { var changed = false; if (!Array.isArray(field)) { field = [{field, type, value, params}]; } field.forEach((filter) => { filter = this.findFilter(filter); if (filter) { this.filterList.push(filter); changed = true; } }); if (changed) { this.trackChanges(); } } findFilter(filter) { var column; if (Array.isArray(filter)) { return this.findSubFilters(filter); } var filterFunc = false; if (typeof filter.field == "function") { filterFunc = function (data) { return filter.field(data, filter.type || {}); }; } else { if (_Filter.filters[filter.type]) { column = this.table.columnManager.getColumnByField(filter.field); if (column) { filterFunc = function (data) { return _Filter.filters[filter.type](filter.value, column.getFieldValue(data), data, filter.params || {}); }; } else { filterFunc = function (data) { return _Filter.filters[filter.type](filter.value, data[filter.field], data, filter.params || {}); }; } } else { console.warn("Filter Error - No such filter type found, ignoring: ", filter.type); } } filter.func = filterFunc; return filter.func ? filter : false; } findSubFilters(filters) { var output = []; filters.forEach((filter) => { filter = this.findFilter(filter); if (filter) { output.push(filter); } }); return output.length ? output : false; } //get all filters getFilters(all, ajax) { var output = []; if (all) { output = this.getHeaderFilters(); } if (ajax) { output.forEach(function (item) { if (typeof item.type == "function") { item.type = "function"; } }); } output = output.concat(this.filtersToArray(this.filterList, ajax)); return output; } //filter to Object filtersToArray(filterList, ajax) { var output = []; filterList.forEach((filter) => { var item; if (Array.isArray(filter)) { output.push(this.filtersToArray(filter, ajax)); } else { item = {field: filter.field, type: filter.type, value: filter.value}; if (ajax) { if (typeof item.type == "function") { item.type = "function"; } } output.push(item); } }); return output; } //get all filters getHeaderFilters() { var output = []; for (var key in this.headerFilters) { output.push({field: key, type: this.headerFilters[key].type, value: this.headerFilters[key].value}); } return output; } //remove filter from array removeFilter(field, type, value) { if (!Array.isArray(field)) { field = [{field, type, value}]; } field.forEach((filter) => { var index = -1; if (typeof filter.field == "object") { index = this.filterList.findIndex((element) => { return filter === element; }); } else { index = this.filterList.findIndex((element) => { return filter.field === element.field && filter.type === element.type && filter.value === element.value; }); } if (index > -1) { this.filterList.splice(index, 1); } else { console.warn("Filter Error - No matching filter type found, ignoring: ", filter.type); } }); this.trackChanges(); } //clear filters clearFilter(all) { this.filterList = []; if (all) { this.clearHeaderFilter(); } this.trackChanges(); } //clear header filters clearHeaderFilter() { this.headerFilters = {}; this.prevHeaderFilterChangeCheck = "{}"; this.headerFilterColumns.forEach((column) => { if (typeof column.modules.filter.value !== "undefined") { delete column.modules.filter.value; } column.modules.filter.prevSuccess = void 0; this.reloadHeaderFilter(column); }); this.trackChanges(); } //search data and return matching rows search(searchType, field, type, value) { var activeRows = [], filterList = []; if (!Array.isArray(field)) { field = [{field, type, value}]; } field.forEach((filter) => { filter = this.findFilter(filter); if (filter) { filterList.push(filter); } }); this.table.rowManager.rows.forEach((row) => { var match = true; filterList.forEach((filter) => { if (!this.filterRecurse(filter, row.getData())) { match = false; } }); if (match) { activeRows.push(searchType === "data" ? row.getData("data") : row.getComponent()); } }); return activeRows; } //filter row array filter(rowList, filters) { var activeRows = [], activeRowComponents = []; if (this.subscribedExternal("dataFiltering")) { this.dispatchExternal("dataFiltering", this.getFilters(true)); } if (this.table.options.filterMode !== "remote" && (this.filterList.length || Object.keys(this.headerFilters).length)) { rowList.forEach((row) => { if (this.filterRow(row)) { activeRows.push(row); } }); } else { activeRows = rowList.slice(0); } if (this.subscribedExternal("dataFiltered")) { activeRows.forEach((row) => { activeRowComponents.push(row.getComponent()); }); this.dispatchExternal("dataFiltered", this.getFilters(true), activeRowComponents); } return activeRows; } //filter individual row filterRow(row, filters) { var match = true, data = row.getData(); this.filterList.forEach((filter) => { if (!this.filterRecurse(filter, data)) { match = false; } }); for (var field in this.headerFilters) { if (!this.headerFilters[field].func(data)) { match = false; } } return match; } filterRecurse(filter, data) { var match = false; if (Array.isArray(filter)) { filter.forEach((subFilter) => { if (this.filterRecurse(subFilter, data)) { match = true; } }); } else { match = filter.func(data); } return match; } }; __publicField(_Filter, "moduleName", "filter"); //load defaults __publicField(_Filter, "filters", defaultFilters); let Filter = _Filter; function plaintext(cell, formatterParams, onRendered) { return this.emptyToSpace(this.sanitizeHTML(cell.getValue())); } function html(cell, formatterParams, onRendered) { return cell.getValue(); } function textarea(cell, formatterParams, onRendered) { cell.getElement().style.whiteSpace = "pre-wrap"; return this.emptyToSpace(this.sanitizeHTML(cell.getValue())); } function money(cell, formatterParams, onRendered) { var floatVal = parseFloat(cell.getValue()), sign = "", number2, integer, decimal, rgx, value; var decimalSym = formatterParams.decimal || "."; var thousandSym = formatterParams.thousand || ","; var negativeSign = formatterParams.negativeSign || "-"; var symbol = formatterParams.symbol || ""; var after = !!formatterParams.symbolAfter; var precision = typeof formatterParams.precision !== "undefined" ? formatterParams.precision : 2; if (Number.isNaN(floatVal)) { return this.emptyToSpace(this.sanitizeHTML(cell.getValue())); } if (floatVal < 0) { floatVal = Math.abs(floatVal); sign = negativeSign; } number2 = precision !== false ? floatVal.toFixed(precision) : floatVal; number2 = String(number2).split("."); integer = number2[0]; decimal = number2.length > 1 ? decimalSym + number2[1] : ""; if (formatterParams.thousand !== false) { rgx = /(\d+)(\d{3})/; while (rgx.test(integer)) { integer = integer.replace(rgx, "$1" + thousandSym + "$2"); } } value = integer + decimal; if (sign === true) { value = "(" + value + ")"; return after ? value + symbol : symbol + value; } else { return after ? sign + value + symbol : sign + symbol + value; } } function link(cell, formatterParams, onRendered) { var value = cell.getValue(), urlPrefix = formatterParams.urlPrefix || "", download = formatterParams.download, label = value, el = document.createElement("a"), data; function labelTraverse(path, data2) { var item = path.shift(), value2 = data2[item]; if (path.length && typeof value2 === "object") { return labelTraverse(path, value2); } return value2; } if (formatterParams.labelField) { data = cell.getData(); label = labelTraverse(formatterParams.labelField.split(this.table.options.nestedFieldSeparator), data); } if (formatterParams.label) { switch (typeof formatterParams.label) { case "string": label = formatterParams.label; break; case "function": label = formatterParams.label(cell); break; } } if (label) { if (formatterParams.urlField) { data = cell.getData(); value = Helpers.retrieveNestedData(this.table.options.nestedFieldSeparator, formatterParams.urlField, data); } if (formatterParams.url) { switch (typeof formatterParams.url) { case "string": value = formatterParams.url; break; case "function": value = formatterParams.url(cell); break; } } el.setAttribute("href", urlPrefix + value); if (formatterParams.target) { el.setAttribute("target", formatterParams.target); } if (formatterParams.download) { if (typeof download == "function") { download = download(cell); } else { download = download === true ? "" : download; } el.setAttribute("download", download); } el.innerHTML = this.emptyToSpace(this.sanitizeHTML(label)); return el; } else { return " "; } } function image(cell, formatterParams, onRendered) { var el = document.createElement("img"), src = cell.getValue(); if (formatterParams.urlPrefix) { src = formatterParams.urlPrefix + cell.getValue(); } if (formatterParams.urlSuffix) { src = src + formatterParams.urlSuffix; } el.setAttribute("src", src); switch (typeof formatterParams.height) { case "number": el.style.height = formatterParams.height + "px"; break; case "string": el.style.height = formatterParams.height; break; } switch (typeof formatterParams.width) { case "number": el.style.width = formatterParams.width + "px"; break; case "string": el.style.width = formatterParams.width; break; } el.addEventListener("load", function () { cell.getRow().normalizeHeight(); }); return el; } function tickCross(cell, formatterParams, onRendered) { var value = cell.getValue(), element = cell.getElement(), empty = formatterParams.allowEmpty, truthy = formatterParams.allowTruthy, trueValueSet = Object.keys(formatterParams).includes("trueValue"), tick = typeof formatterParams.tickElement !== "undefined" ? formatterParams.tickElement : '', cross = typeof formatterParams.crossElement !== "undefined" ? formatterParams.crossElement : ''; if (trueValueSet && value === formatterParams.trueValue || !trueValueSet && (truthy && value || (value === true || value === "true" || value === "True" || value === 1 || value === "1"))) { element.setAttribute("aria-checked", true); return tick || ""; } else { if (empty && (value === "null" || value === "" || value === null || typeof value === "undefined")) { element.setAttribute("aria-checked", "mixed"); return ""; } else { element.setAttribute("aria-checked", false); return cross || ""; } } } function datetime$1(cell, formatterParams, onRendered) { var DT = this.table.dependencyRegistry.lookup(["luxon", "DateTime"], "DateTime"); var inputFormat = formatterParams.inputFormat || "yyyy-MM-dd HH:mm:ss"; var outputFormat = formatterParams.outputFormat || "dd/MM/yyyy HH:mm:ss"; var invalid = typeof formatterParams.invalidPlaceholder !== "undefined" ? formatterParams.invalidPlaceholder : ""; var value = cell.getValue(); if (typeof DT != "undefined") { var newDatetime; if (DT.isDateTime(value)) { newDatetime = value; } else if (inputFormat === "x") { newDatetime = DT.fromMillis(value); } else if (inputFormat === "iso") { newDatetime = DT.fromISO(String(value)); } else { newDatetime = DT.fromFormat(String(value), inputFormat); } if (newDatetime.isValid) { if (formatterParams.timezone) { newDatetime = newDatetime.setZone(formatterParams.timezone); } return newDatetime.toFormat(outputFormat); } else { if (invalid === true || !value) { return value; } else if (typeof invalid === "function") { return invalid(value); } else { return invalid; } } } else { console.error("Format Error - 'datetime' formatter is dependant on luxon.js"); } } function datetimediff(cell, formatterParams, onRendered) { var DT = this.table.dependencyRegistry.lookup(["luxon", "DateTime"], "DateTime"); var inputFormat = formatterParams.inputFormat || "yyyy-MM-dd HH:mm:ss"; var invalid = typeof formatterParams.invalidPlaceholder !== "undefined" ? formatterParams.invalidPlaceholder : ""; var suffix = typeof formatterParams.suffix !== "undefined" ? formatterParams.suffix : false; var unit = typeof formatterParams.unit !== "undefined" ? formatterParams.unit : "days"; var humanize = typeof formatterParams.humanize !== "undefined" ? formatterParams.humanize : false; var date2 = typeof formatterParams.date !== "undefined" ? formatterParams.date : DT.now(); var value = cell.getValue(); if (typeof DT != "undefined") { var newDatetime; if (DT.isDateTime(value)) { newDatetime = value; } else if (inputFormat === "x") { newDatetime = DT.fromMillis(value); } else if (inputFormat === "iso") { newDatetime = DT.fromISO(String(value)); } else { newDatetime = DT.fromFormat(String(value), inputFormat); } if (newDatetime.isValid) { if (humanize) { return newDatetime.diff(date2, unit).toHuman() + (suffix ? " " + suffix : ""); } else { return parseInt(newDatetime.diff(date2, unit)[unit]) + (suffix ? " " + suffix : ""); } } else { if (invalid === true) { return value; } else if (typeof invalid === "function") { return invalid(value); } else { return invalid; } } } else { console.error("Format Error - 'datetimediff' formatter is dependant on luxon.js"); } } function lookup(cell, formatterParams, onRendered) { var value = cell.getValue(); if (typeof formatterParams[value] === "undefined") { console.warn("Missing display value for " + value); return value; } return formatterParams[value]; } function star(cell, formatterParams, onRendered) { var value = cell.getValue(), element = cell.getElement(), maxStars = formatterParams && formatterParams.stars ? formatterParams.stars : 5, stars = document.createElement("span"), star2 = document.createElementNS("http://www.w3.org/2000/svg", "svg"), starActive = '', starInactive = ''; stars.style.verticalAlign = "middle"; star2.setAttribute("width", "14"); star2.setAttribute("height", "14"); star2.setAttribute("viewBox", "0 0 512 512"); star2.setAttribute("xml:space", "preserve"); star2.style.padding = "0 1px"; value = value && !Number.isNaN(value) ? parseInt(value) : 0; value = Math.max(0, Math.min(value, maxStars)); for (var i = 1; i <= maxStars; i++) { var nextStar = star2.cloneNode(true); nextStar.innerHTML = i <= value ? starActive : starInactive; stars.appendChild(nextStar); } element.style.whiteSpace = "nowrap"; element.style.overflow = "hidden"; element.style.textOverflow = "ellipsis"; element.setAttribute("aria-label", value); return stars; } function traffic(cell, formatterParams, onRendered) { var value = this.sanitizeHTML(cell.getValue()) || 0, el = document.createElement("span"), max = formatterParams && formatterParams.max ? formatterParams.max : 100, min = formatterParams && formatterParams.min ? formatterParams.min : 0, colors = formatterParams && typeof formatterParams.color !== "undefined" ? formatterParams.color : ["red", "orange", "green"], color2 = "#666666", percent, percentValue; if (Number.isNaN(value) || typeof cell.getValue() === "undefined") { return; } el.classList.add("tabulator-traffic-light"); percentValue = parseFloat(value) <= max ? parseFloat(value) : max; percentValue = parseFloat(percentValue) >= min ? parseFloat(percentValue) : min; percent = (max - min) / 100; percentValue = Math.round((percentValue - min) / percent); switch (typeof colors) { case "string": color2 = colors; break; case "function": color2 = colors(value); break; case "object": if (Array.isArray(colors)) { var unit = 100 / colors.length; var index = Math.floor(percentValue / unit); index = Math.min(index, colors.length - 1); index = Math.max(index, 0); color2 = colors[index]; break; } } el.style.backgroundColor = color2; return el; } function progress(cell, formatterParams = {}, onRendered) { var value = this.sanitizeHTML(cell.getValue()) || 0, element = cell.getElement(), max = formatterParams.max ? formatterParams.max : 100, min = formatterParams.min ? formatterParams.min : 0, legendAlign = formatterParams.legendAlign ? formatterParams.legendAlign : "center", percent, percentValue, color2, legend, legendColor; percentValue = parseFloat(value) <= max ? parseFloat(value) : max; percentValue = parseFloat(percentValue) >= min ? parseFloat(percentValue) : min; percent = (max - min) / 100; percentValue = Math.round((percentValue - min) / percent); switch (typeof formatterParams.color) { case "string": color2 = formatterParams.color; break; case "function": color2 = formatterParams.color(value); break; case "object": if (Array.isArray(formatterParams.color)) { let unit = 100 / formatterParams.color.length; let index = Math.floor(percentValue / unit); index = Math.min(index, formatterParams.color.length - 1); index = Math.max(index, 0); color2 = formatterParams.color[index]; break; } default: color2 = "#2DC214"; } switch (typeof formatterParams.legend) { case "string": legend = formatterParams.legend; break; case "function": legend = formatterParams.legend(value); break; case "boolean": legend = value; break; default: legend = false; } switch (typeof formatterParams.legendColor) { case "string": legendColor = formatterParams.legendColor; break; case "function": legendColor = formatterParams.legendColor(value); break; case "object": if (Array.isArray(formatterParams.legendColor)) { let unit = 100 / formatterParams.legendColor.length; let index = Math.floor(percentValue / unit); index = Math.min(index, formatterParams.legendColor.length - 1); index = Math.max(index, 0); legendColor = formatterParams.legendColor[index]; } break; default: legendColor = "#000"; } element.style.minWidth = "30px"; element.style.position = "relative"; element.setAttribute("aria-label", percentValue); var barEl = document.createElement("div"); barEl.style.display = "inline-block"; barEl.style.width = percentValue + "%"; barEl.style.backgroundColor = color2; barEl.style.height = "100%"; barEl.setAttribute("data-max", max); barEl.setAttribute("data-min", min); var barContainer = document.createElement("div"); barContainer.style.position = "relative"; barContainer.style.width = "100%"; barContainer.style.height = "100%"; if (legend) { var legendEl = document.createElement("div"); legendEl.style.position = "absolute"; legendEl.style.top = 0; legendEl.style.left = 0; legendEl.style.textAlign = legendAlign; legendEl.style.width = "100%"; legendEl.style.color = legendColor; legendEl.innerHTML = legend; } onRendered(function () { if (!(cell instanceof CellComponent)) { var holderEl = document.createElement("div"); holderEl.style.position = "absolute"; holderEl.style.top = "4px"; holderEl.style.bottom = "4px"; holderEl.style.left = "4px"; holderEl.style.right = "4px"; element.appendChild(holderEl); element = holderEl; } element.appendChild(barContainer); barContainer.appendChild(barEl); if (legend) { barContainer.appendChild(legendEl); } }); return ""; } function color(cell, formatterParams, onRendered) { cell.getElement().style.backgroundColor = this.sanitizeHTML(cell.getValue()); return ""; } function buttonTick(cell, formatterParams, onRendered) { return ''; } function buttonCross(cell, formatterParams, onRendered) { return ''; } function toggle(cell, formatterParams, onRendered) { var value = cell.getValue(), size = formatterParams.size || 15, sizePx = size + "px", containEl, switchEl, onValue = formatterParams.hasOwnProperty("onValue") ? formatterParams.onValue : true, offValue = formatterParams.hasOwnProperty("offValue") ? formatterParams.offValue : false, state = formatterParams.onTruthy ? value : value === onValue; containEl = document.createElement("div"); containEl.classList.add("tabulator-toggle"); if (state) { containEl.classList.add("tabulator-toggle-on"); containEl.style.flexDirection = "row-reverse"; if (formatterParams.onColor) { containEl.style.background = formatterParams.onColor; } } else { if (formatterParams.offColor) { containEl.style.background = formatterParams.offColor; } } containEl.style.width = 2.5 * size + "px"; containEl.style.borderRadius = sizePx; if (formatterParams.clickable) { containEl.addEventListener("click", (e) => { cell.setValue(state ? offValue : onValue); }); } switchEl = document.createElement("div"); switchEl.classList.add("tabulator-toggle-switch"); switchEl.style.height = sizePx; switchEl.style.width = sizePx; switchEl.style.borderRadius = sizePx; containEl.appendChild(switchEl); return containEl; } function rownum(cell, formatterParams, onRendered) { var content = document.createElement("span"); var row = cell.getRow(); var table = cell.getTable(); row.watchPosition((position) => { if (formatterParams.relativeToPage) { position += table.modules.page.getPageSize() * (table.modules.page.getPage() - 1); } content.innerText = position; }); return content; } function handle(cell, formatterParams, onRendered) { cell.getElement().classList.add("tabulator-row-handle"); return "
"; } function adaptable(cell, params, onRendered) { var lookup2, formatterFunc, formatterParams; function defaultLookup(cell2) { var value = cell2.getValue(), formatter = "plaintext"; switch (typeof value) { case "boolean": formatter = "tickCross"; break; case "string": if (value.includes("\n")) { formatter = "textarea"; } break; } return formatter; } lookup2 = params.formatterLookup ? params.formatterLookup(cell) : defaultLookup(cell); if (params.paramsLookup) { formatterParams = typeof params.paramsLookup === "function" ? params.paramsLookup(lookup2, cell) : params.paramsLookup[lookup2]; } formatterFunc = this.table.modules.format.lookupFormatter(lookup2); return formatterFunc.call(this, cell, formatterParams || {}, onRendered); } function array$2(cell, formatterParams, onRendered) { var delimiter = formatterParams.delimiter || ",", value = cell.getValue(), table = this.table, valueMap; if (formatterParams.valueMap) { if (typeof formatterParams.valueMap === "string") { valueMap = function (value2) { return value2.map((item) => { return Helpers.retrieveNestedData(table.options.nestedFieldSeparator, formatterParams.valueMap, item); }); }; } else { valueMap = formatterParams.valueMap; } } if (Array.isArray(value)) { if (valueMap) { value = valueMap(value); } return value.join(delimiter); } else { return value; } } function json$1(cell, formatterParams, onRendered) { var indent = formatterParams.indent || " ", multiline = typeof formatterParams.multiline === "undefined" ? true : formatterParams.multiline, replacer = formatterParams.replacer || null, value = cell.getValue(); if (multiline) { cell.getElement().style.whiteSpace = "pre-wrap"; } return JSON.stringify(value, replacer, indent); } var defaultFormatters = { plaintext, html, textarea, money, link, image, tickCross, datetime: datetime$1, datetimediff, lookup, star, traffic, progress, color, buttonTick, buttonCross, toggle, rownum, handle, adaptable, array: array$2, json: json$1 }; const _Format = class _Format extends Module { constructor(table) { super(table); this.registerColumnOption("formatter"); this.registerColumnOption("formatterParams"); this.registerColumnOption("formatterPrint"); this.registerColumnOption("formatterPrintParams"); this.registerColumnOption("formatterClipboard"); this.registerColumnOption("formatterClipboardParams"); this.registerColumnOption("formatterHtmlOutput"); this.registerColumnOption("formatterHtmlOutputParams"); this.registerColumnOption("titleFormatter"); this.registerColumnOption("titleFormatterParams"); } initialize() { this.subscribe("cell-format", this.formatValue.bind(this)); this.subscribe("cell-rendered", this.cellRendered.bind(this)); this.subscribe("column-layout", this.initializeColumn.bind(this)); this.subscribe("column-format", this.formatHeader.bind(this)); } //initialize column formatter initializeColumn(column) { column.modules.format = this.lookupTypeFormatter(column, ""); if (typeof column.definition.formatterPrint !== "undefined") { column.modules.format.print = this.lookupTypeFormatter(column, "Print"); } if (typeof column.definition.formatterClipboard !== "undefined") { column.modules.format.clipboard = this.lookupTypeFormatter(column, "Clipboard"); } if (typeof column.definition.formatterHtmlOutput !== "undefined") { column.modules.format.htmlOutput = this.lookupTypeFormatter(column, "HtmlOutput"); } } lookupTypeFormatter(column, type) { var config = {params: column.definition["formatter" + type + "Params"] || {}}, formatter = column.definition["formatter" + type]; config.formatter = this.lookupFormatter(formatter); return config; } lookupFormatter(formatter) { var formatterFunc; switch (typeof formatter) { case "string": if (_Format.formatters[formatter]) { formatterFunc = _Format.formatters[formatter]; } else { console.warn("Formatter Error - No such formatter found: ", formatter); formatterFunc = _Format.formatters.plaintext; } break; case "function": formatterFunc = formatter; break; default: formatterFunc = _Format.formatters.plaintext; break; } return formatterFunc; } cellRendered(cell) { if (cell.modules.format && cell.modules.format.renderedCallback && !cell.modules.format.rendered) { cell.modules.format.renderedCallback(); cell.modules.format.rendered = true; } } //return a formatted value for a column header formatHeader(column, title, el) { var formatter, params, onRendered, mockCell; if (column.definition.titleFormatter) { formatter = this.lookupFormatter(column.definition.titleFormatter); onRendered = (callback) => { column.titleFormatterRendered = callback; }; mockCell = { getValue: function () { return title; }, getElement: function () { return el; }, getType: function () { return "header"; }, getColumn: function () { return column.getComponent(); }, getTable: () => { return this.table; } }; params = column.definition.titleFormatterParams || {}; params = typeof params === "function" ? params() : params; return formatter.call(this, mockCell, params, onRendered); } else { return title; } } //return a formatted value for a cell formatValue(cell) { var component = cell.getComponent(), params = typeof cell.column.modules.format.params === "function" ? cell.column.modules.format.params(component) : cell.column.modules.format.params; function onRendered(callback) { if (!cell.modules.format) { cell.modules.format = {}; } cell.modules.format.renderedCallback = callback; cell.modules.format.rendered = false; } return cell.column.modules.format.formatter.call(this, component, params, onRendered); } formatExportValue(cell, type) { var formatter = cell.column.modules.format[type], params; if (formatter) { let onRendered = function (callback) { if (!cell.modules.format) { cell.modules.format = {}; } cell.modules.format.renderedCallback = callback; cell.modules.format.rendered = false; }; params = typeof formatter.params === "function" ? formatter.params(cell.getComponent()) : formatter.params; return formatter.formatter.call(this, cell.getComponent(), params, onRendered); } else { return this.formatValue(cell); } } sanitizeHTML(value) { if (value) { var entityMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "/": "/", "`": "`", "=": "=" }; return String(value).replace(/[&<>"'`=/]/g, function (s) { return entityMap[s]; }); } else { return value; } } emptyToSpace(value) { return value === null || typeof value === "undefined" || value === "" ? " " : value; } }; __publicField(_Format, "moduleName", "format"); //load defaults __publicField(_Format, "formatters", defaultFormatters); let Format = _Format; class FrozenColumns extends Module { constructor(table) { super(table); this.leftColumns = []; this.rightColumns = []; this.initializationMode = "left"; this.active = false; this.blocked = true; this.registerColumnOption("frozen"); } //reset initial state reset() { this.initializationMode = "left"; this.leftColumns = []; this.rightColumns = []; this.active = false; } initialize() { this.subscribe("cell-layout", this.layoutCell.bind(this)); this.subscribe("column-init", this.initializeColumn.bind(this)); this.subscribe("column-width", this.layout.bind(this)); this.subscribe("row-layout-after", this.layoutRow.bind(this)); this.subscribe("table-layout", this.layout.bind(this)); this.subscribe("columns-loading", this.reset.bind(this)); this.subscribe("column-add", this.reinitializeColumns.bind(this)); this.subscribe("column-deleted", this.reinitializeColumns.bind(this)); this.subscribe("column-hide", this.reinitializeColumns.bind(this)); this.subscribe("column-show", this.reinitializeColumns.bind(this)); this.subscribe("columns-loaded", this.reinitializeColumns.bind(this)); this.subscribe("table-redraw", this.layout.bind(this)); this.subscribe("layout-refreshing", this.blockLayout.bind(this)); this.subscribe("layout-refreshed", this.unblockLayout.bind(this)); this.subscribe("scrollbar-vertical", this.adjustForScrollbar.bind(this)); } blockLayout() { this.blocked = true; } unblockLayout() { this.blocked = false; } layoutCell(cell) { this.layoutElement(cell.element, cell.column); } reinitializeColumns() { this.reset(); this.table.columnManager.columnsByIndex.forEach((column) => { this.initializeColumn(column); }); this.layout(); } //initialize specific column initializeColumn(column) { var config = {margin: 0, edge: false}; if (!column.isGroup) { if (this.frozenCheck(column)) { config.position = this.initializationMode; if (this.initializationMode == "left") { this.leftColumns.push(column); } else { this.rightColumns.unshift(column); } this.active = true; column.modules.frozen = config; } else { this.initializationMode = "right"; } } } frozenCheck(column) { if (column.parent.isGroup && column.definition.frozen) { console.warn("Frozen Column Error - Parent column group must be frozen, not individual columns or sub column groups"); } if (column.parent.isGroup) { return this.frozenCheck(column.parent); } else { return column.definition.frozen; } } //layout calculation rows layoutCalcRows() { if (this.table.modExists("columnCalcs")) { if (this.table.modules.columnCalcs.topInitialized && this.table.modules.columnCalcs.topRow) { this.layoutRow(this.table.modules.columnCalcs.topRow); } if (this.table.modules.columnCalcs.botInitialized && this.table.modules.columnCalcs.botRow) { this.layoutRow(this.table.modules.columnCalcs.botRow); } if (this.table.modExists("groupRows")) { this.layoutGroupCalcs(this.table.modules.groupRows.getGroups()); } } } layoutGroupCalcs(groups) { groups.forEach((group) => { if (group.calcs.top) { this.layoutRow(group.calcs.top); } if (group.calcs.bottom) { this.layoutRow(group.calcs.bottom); } if (group.groupList && group.groupList.length) { this.layoutGroupCalcs(group.groupList); } }); } //calculate column positions and layout headers layoutColumnPosition(allCells) { var leftParents = []; var leftMargin = 0; var rightMargin = 0; this.leftColumns.forEach((column, i) => { column.modules.frozen.marginValue = leftMargin; column.modules.frozen.margin = column.modules.frozen.marginValue + "px"; if (column.visible) { leftMargin += column.getWidth(); } if (i == this.leftColumns.length - 1) { column.modules.frozen.edge = true; } else { column.modules.frozen.edge = false; } if (column.parent.isGroup) { var parentEl = this.getColGroupParentElement(column); if (!leftParents.includes(parentEl)) { this.layoutElement(parentEl, column); leftParents.push(parentEl); } parentEl.classList.toggle("tabulator-frozen-left", column.modules.frozen.edge && column.modules.frozen.position === "left"); parentEl.classList.toggle("tabulator-frozen-right", column.modules.frozen.edge && column.modules.frozen.position === "right"); } else { this.layoutElement(column.getElement(), column); } if (allCells) { column.cells.forEach((cell) => { this.layoutElement(cell.getElement(true), column); }); } }); this.rightColumns.forEach((column, i) => { column.modules.frozen.marginValue = rightMargin; column.modules.frozen.margin = column.modules.frozen.marginValue + "px"; if (column.visible) { rightMargin += column.getWidth(); } if (i == this.rightColumns.length - 1) { column.modules.frozen.edge = true; } else { column.modules.frozen.edge = false; } if (column.parent.isGroup) { this.layoutElement(this.getColGroupParentElement(column), column); } else { this.layoutElement(column.getElement(), column); } if (allCells) { column.cells.forEach((cell) => { this.layoutElement(cell.getElement(true), column); }); } }); } getColGroupParentElement(column) { return column.parent.isGroup ? this.getColGroupParentElement(column.parent) : column.getElement(); } //layout columns appropriately layout() { if (this.active && !this.blocked) { this.layoutColumnPosition(); this.reinitializeRows(); this.layoutCalcRows(); } } reinitializeRows() { var visibleRows = this.table.rowManager.getVisibleRows(true); var otherRows = this.table.rowManager.getRows().filter((row) => !visibleRows.includes(row)); otherRows.forEach((row) => { row.deinitialize(); }); visibleRows.forEach((row) => { if (row.type === "row") { this.layoutRow(row); } }); } layoutRow(row) { if (this.table.options.layout === "fitDataFill" && this.rightColumns.length) { this.table.rowManager.getTableElement().style.minWidth = "calc(100% - " + this.rightMargin + ")"; } this.leftColumns.forEach((column) => { var cell = row.getCell(column); if (cell) { this.layoutElement(cell.getElement(true), column); } }); this.rightColumns.forEach((column) => { var cell = row.getCell(column); if (cell) { this.layoutElement(cell.getElement(true), column); } }); } layoutElement(element, column) { var position; if (column.modules.frozen && element) { element.style.position = "sticky"; if (this.table.rtl) { position = column.modules.frozen.position === "left" ? "right" : "left"; } else { position = column.modules.frozen.position; } element.style[position] = column.modules.frozen.margin; element.classList.add("tabulator-frozen"); element.classList.toggle("tabulator-frozen-left", column.modules.frozen.edge && column.modules.frozen.position === "left"); element.classList.toggle("tabulator-frozen-right", column.modules.frozen.edge && column.modules.frozen.position === "right"); } } adjustForScrollbar(width) { if (this.rightColumns.length) { this.table.columnManager.getContentsElement().style.width = "calc(100% - " + width + "px)"; } } getFrozenColumns() { return this.leftColumns.concat(this.rightColumns); } _calcSpace(columns, index) { var width = 0; for (let i = 0; i < index; i++) { if (columns[i].visible) { width += columns[i].getWidth(); } } return width; } } __publicField(FrozenColumns, "moduleName", "frozenColumns"); class FrozenRows extends Module { constructor(table) { super(table); this.topElement = document.createElement("div"); this.rows = []; this.registerComponentFunction("row", "freeze", this.freezeRow.bind(this)); this.registerComponentFunction("row", "unfreeze", this.unfreezeRow.bind(this)); this.registerComponentFunction("row", "isFrozen", this.isRowFrozen.bind(this)); this.registerTableOption("frozenRowsField", "id"); this.registerTableOption("frozenRows", false); } initialize() { var fragment = document.createDocumentFragment(); this.rows = []; this.topElement.classList.add("tabulator-frozen-rows-holder"); fragment.appendChild(this.topElement); this.table.columnManager.getContentsElement().insertBefore(fragment, this.table.columnManager.headersElement.nextSibling); this.subscribe("row-deleting", this.detachRow.bind(this)); this.subscribe("rows-visible", this.visibleRows.bind(this)); this.registerDisplayHandler(this.getRows.bind(this), 10); if (this.table.options.frozenRows) { this.subscribe("data-processed", this.initializeRows.bind(this)); this.subscribe("row-added", this.initializeRow.bind(this)); this.subscribe("table-redrawing", this.resizeHolderWidth.bind(this)); this.subscribe("column-resized", this.resizeHolderWidth.bind(this)); this.subscribe("column-show", this.resizeHolderWidth.bind(this)); this.subscribe("column-hide", this.resizeHolderWidth.bind(this)); } this.resizeHolderWidth(); } resizeHolderWidth() { this.topElement.style.minWidth = this.table.columnManager.headersElement.offsetWidth + "px"; } initializeRows() { this.table.rowManager.getRows().forEach((row) => { this.initializeRow(row); }); } initializeRow(row) { var frozenRows = this.table.options.frozenRows, rowType = typeof frozenRows; if (rowType === "number") { if (row.getPosition() && row.getPosition() + this.rows.length <= frozenRows) { this.freezeRow(row); } } else if (rowType === "function") { if (frozenRows.call(this.table, row.getComponent())) { this.freezeRow(row); } } else if (Array.isArray(frozenRows)) { if (frozenRows.includes(row.data[this.options("frozenRowsField")])) { this.freezeRow(row); } } } isRowFrozen(row) { var index = this.rows.indexOf(row); return index > -1; } isFrozen() { return !!this.rows.length; } visibleRows(viewable, rows2) { this.rows.forEach((row) => { rows2.push(row); }); return rows2; } //filter frozen rows out of display data getRows(rows2) { var output = rows2.slice(0); this.rows.forEach(function (row) { var index = output.indexOf(row); if (index > -1) { output.splice(index, 1); } }); return output; } freezeRow(row) { if (!row.modules.frozen) { row.modules.frozen = true; this.topElement.appendChild(row.getElement()); row.initialize(); row.normalizeHeight(); this.rows.push(row); this.refreshData(false, "display"); this.table.rowManager.adjustTableSize(); this.styleRows(); } else { console.warn("Freeze Error - Row is already frozen"); } } unfreezeRow(row) { if (row.modules.frozen) { row.modules.frozen = false; this.detachRow(row); this.table.rowManager.adjustTableSize(); this.refreshData(false, "display"); if (this.rows.length) { this.styleRows(); } } else { console.warn("Freeze Error - Row is already unfrozen"); } } detachRow(row) { var index = this.rows.indexOf(row); if (index > -1) { var rowEl = row.getElement(); if (rowEl.parentNode) { rowEl.parentNode.removeChild(rowEl); } this.rows.splice(index, 1); } } styleRows(row) { this.rows.forEach((row2, i) => { this.table.rowManager.styleRow(row2, i); }); } } __publicField(FrozenRows, "moduleName", "frozenRows"); class GroupComponent { constructor(group) { this._group = group; this.type = "GroupComponent"; return new Proxy(this, { get: function (target, name2, receiver) { if (typeof target[name2] !== "undefined") { return target[name2]; } else { return target._group.groupManager.table.componentFunctionBinder.handle("group", target._group, name2); } } }); } getKey() { return this._group.key; } getField() { return this._group.field; } getElement() { return this._group.element; } getRows() { return this._group.getRows(true); } getSubGroups() { return this._group.getSubGroups(true); } getParentGroup() { return this._group.parent ? this._group.parent.getComponent() : false; } isVisible() { return this._group.visible; } show() { this._group.show(); } hide() { this._group.hide(); } toggle() { this._group.toggleVisibility(); } scrollTo(position, ifVisible) { return this._group.groupManager.table.rowManager.scrollToRow(this._group, position, ifVisible); } _getSelf() { return this._group; } getTable() { return this._group.groupManager.table; } } class Group { constructor(groupManager, parent2, level, key, field, generator, oldGroup) { this.groupManager = groupManager; this.parent = parent2; this.key = key; this.level = level; this.field = field; this.hasSubGroups = level < groupManager.groupIDLookups.length - 1; this.addRow = this.hasSubGroups ? this._addRowToGroup : this._addRow; this.type = "group"; this.old = oldGroup; this.rows = []; this.groups = []; this.groupList = []; this.generator = generator; this.element = false; this.elementContents = false; this.height = 0; this.outerHeight = 0; this.initialized = false; this.calcs = {}; this.initialized = false; this.modules = {}; this.arrowElement = false; this.visible = oldGroup ? oldGroup.visible : typeof groupManager.startOpen[level] !== "undefined" ? groupManager.startOpen[level] : groupManager.startOpen[0]; this.component = null; this.createElements(); this.addBindings(); this.createValueGroups(); } wipe(elementsOnly) { if (!elementsOnly) { if (this.groupList.length) { this.groupList.forEach(function (group) { group.wipe(); }); } else { this.rows.forEach((row) => { if (row.modules) { delete row.modules.group; } }); } } this.element = false; this.arrowElement = false; this.elementContents = false; } createElements() { var arrow = document.createElement("div"); arrow.classList.add("tabulator-arrow"); this.element = document.createElement("div"); this.element.classList.add("tabulator-row"); this.element.classList.add("tabulator-group"); this.element.classList.add("tabulator-group-level-" + this.level); this.element.setAttribute("role", "rowgroup"); this.arrowElement = document.createElement("div"); this.arrowElement.classList.add("tabulator-group-toggle"); this.arrowElement.appendChild(arrow); if (this.groupManager.table.options.movableRows !== false && this.groupManager.table.modExists("moveRow")) { this.groupManager.table.modules.moveRow.initializeGroupHeader(this); } } createValueGroups() { var level = this.level + 1; if (this.groupManager.allowedValues && this.groupManager.allowedValues[level]) { this.groupManager.allowedValues[level].forEach((value) => { this._createGroup(value, level); }); } } addBindings() { var toggleElement; if (this.groupManager.table.options.groupToggleElement) { toggleElement = this.groupManager.table.options.groupToggleElement == "arrow" ? this.arrowElement : this.element; toggleElement.addEventListener("click", (e) => { if (this.groupManager.table.options.groupToggleElement === "arrow") { e.stopPropagation(); e.stopImmediatePropagation(); } setTimeout(() => { this.toggleVisibility(); }); }); } } _createGroup(groupID, level) { var groupKey = level + "_" + groupID; var group = new Group(this.groupManager, this, level, groupID, this.groupManager.groupIDLookups[level].field, this.groupManager.headerGenerator[level] || this.groupManager.headerGenerator[0], this.old ? this.old.groups[groupKey] : false); this.groups[groupKey] = group; this.groupList.push(group); } _addRowToGroup(row) { var level = this.level + 1; if (this.hasSubGroups) { var groupID = this.groupManager.groupIDLookups[level].func(row.getData()), groupKey = level + "_" + groupID; if (this.groupManager.allowedValues && this.groupManager.allowedValues[level]) { if (this.groups[groupKey]) { this.groups[groupKey].addRow(row); } } else { if (!this.groups[groupKey]) { this._createGroup(groupID, level); } this.groups[groupKey].addRow(row); } } } _addRow(row) { this.rows.push(row); row.modules.group = this; } insertRow(row, to, after) { var data = this.conformRowData({}); row.updateData(data); var toIndex = this.rows.indexOf(to); if (toIndex > -1) { if (after) { this.rows.splice(toIndex + 1, 0, row); } else { this.rows.splice(toIndex, 0, row); } } else { if (after) { this.rows.push(row); } else { this.rows.unshift(row); } } row.modules.group = this; if (this.groupManager.table.modExists("columnCalcs") && this.groupManager.table.options.columnCalcs != "table") { this.groupManager.table.modules.columnCalcs.recalcGroup(this); } this.groupManager.updateGroupRows(true); } scrollHeader(left) { if (this.arrowElement) { this.arrowElement.style.marginLeft = left; this.groupList.forEach(function (child) { child.scrollHeader(left); }); } } getRowIndex(row) { } //update row data to match grouping constraints conformRowData(data) { if (this.field) { data[this.field] = this.key; } else { console.warn("Data Conforming Error - Cannot conform row data to match new group as groupBy is a function"); } if (this.parent) { data = this.parent.conformRowData(data); } return data; } removeRow(row) { var index = this.rows.indexOf(row); var el = row.getElement(); if (index > -1) { this.rows.splice(index, 1); } if (!this.groupManager.table.options.groupValues && !this.rows.length) { if (this.parent) { this.parent.removeGroup(this); } else { this.groupManager.removeGroup(this); } this.groupManager.updateGroupRows(true); } else { if (el.parentNode) { el.parentNode.removeChild(el); } if (!this.groupManager.blockRedraw) { this.generateGroupHeaderContents(); if (this.groupManager.table.modExists("columnCalcs") && this.groupManager.table.options.columnCalcs != "table") { this.groupManager.table.modules.columnCalcs.recalcGroup(this); } } } } removeGroup(group) { var groupKey = group.level + "_" + group.key, index; if (this.groups[groupKey]) { delete this.groups[groupKey]; index = this.groupList.indexOf(group); if (index > -1) { this.groupList.splice(index, 1); } if (!this.groupList.length) { if (this.parent) { this.parent.removeGroup(this); } else { this.groupManager.removeGroup(this); } } } } getHeadersAndRows() { var output = []; output.push(this); this._visSet(); if (this.calcs.top) { this.calcs.top.detachElement(); this.calcs.top.deleteCells(); } if (this.calcs.bottom) { this.calcs.bottom.detachElement(); this.calcs.bottom.deleteCells(); } if (this.visible) { if (this.groupList.length) { this.groupList.forEach(function (group) { output = output.concat(group.getHeadersAndRows()); }); } else { if (this.groupManager.table.options.columnCalcs != "table" && this.groupManager.table.modExists("columnCalcs") && this.groupManager.table.modules.columnCalcs.hasTopCalcs()) { this.calcs.top = this.groupManager.table.modules.columnCalcs.generateTopRow(this.rows); output.push(this.calcs.top); } output = output.concat(this.rows); if (this.groupManager.table.options.columnCalcs != "table" && this.groupManager.table.modExists("columnCalcs") && this.groupManager.table.modules.columnCalcs.hasBottomCalcs()) { this.calcs.bottom = this.groupManager.table.modules.columnCalcs.generateBottomRow(this.rows); output.push(this.calcs.bottom); } } } else { if (!this.groupList.length && this.groupManager.table.options.columnCalcs != "table") { if (this.groupManager.table.modExists("columnCalcs")) { if (this.groupManager.table.modules.columnCalcs.hasTopCalcs()) { if (this.groupManager.table.options.groupClosedShowCalcs) { this.calcs.top = this.groupManager.table.modules.columnCalcs.generateTopRow(this.rows); output.push(this.calcs.top); } } if (this.groupManager.table.modules.columnCalcs.hasBottomCalcs()) { if (this.groupManager.table.options.groupClosedShowCalcs) { this.calcs.bottom = this.groupManager.table.modules.columnCalcs.generateBottomRow(this.rows); output.push(this.calcs.bottom); } } } } } return output; } getData(visible, transform) { var output = []; this._visSet(); if (!visible || visible && this.visible) { this.rows.forEach((row) => { output.push(row.getData(transform || "data")); }); } return output; } getRowCount() { var count = 0; if (this.groupList.length) { this.groupList.forEach((group) => { count += group.getRowCount(); }); } else { count = this.rows.length; } return count; } toggleVisibility() { if (this.visible) { this.hide(); } else { this.show(); } } hide() { this.visible = false; if (this.groupManager.table.rowManager.getRenderMode() == "basic" && !this.groupManager.table.options.pagination) { this.element.classList.remove("tabulator-group-visible"); if (this.groupList.length) { this.groupList.forEach((group) => { var rows2 = group.getHeadersAndRows(); rows2.forEach((row) => { row.detachElement(); }); }); } else { this.rows.forEach((row) => { var rowEl = row.getElement(); rowEl.parentNode.removeChild(rowEl); }); } this.groupManager.updateGroupRows(true); } else { this.groupManager.updateGroupRows(true); } this.groupManager.table.externalEvents.dispatch("groupVisibilityChanged", this.getComponent(), false); } show() { this.visible = true; if (this.groupManager.table.rowManager.getRenderMode() == "basic" && !this.groupManager.table.options.pagination) { this.element.classList.add("tabulator-group-visible"); var prev = this.generateElement(); if (this.groupList.length) { this.groupList.forEach((group) => { var rows2 = group.getHeadersAndRows(); rows2.forEach((row) => { var rowEl = row.getElement(); prev.parentNode.insertBefore(rowEl, prev.nextSibling); row.initialize(); prev = rowEl; }); }); } else { this.rows.forEach((row) => { var rowEl = row.getElement(); prev.parentNode.insertBefore(rowEl, prev.nextSibling); row.initialize(); prev = rowEl; }); } this.groupManager.updateGroupRows(true); } else { this.groupManager.updateGroupRows(true); } this.groupManager.table.externalEvents.dispatch("groupVisibilityChanged", this.getComponent(), true); } _visSet() { var data = []; if (typeof this.visible == "function") { this.rows.forEach(function (row) { data.push(row.getData()); }); this.visible = this.visible(this.key, this.getRowCount(), data, this.getComponent()); } } getRowGroup(row) { var match = false; if (this.groupList.length) { this.groupList.forEach(function (group) { var result = group.getRowGroup(row); if (result) { match = result; } }); } else { if (this.rows.find(function (item) { return item === row; })) { match = this; } } return match; } getSubGroups(component) { var output = []; this.groupList.forEach(function (child) { output.push(component ? child.getComponent() : child); }); return output; } getRows(component, includeChildren) { var output = []; if (includeChildren && this.groupList.length) { this.groupList.forEach((group) => { output = output.concat(group.getRows(component, includeChildren)); }); } else { this.rows.forEach(function (row) { output.push(component ? row.getComponent() : row); }); } return output; } generateGroupHeaderContents() { var data = []; var rows2 = this.getRows(false, true); rows2.forEach(function (row) { data.push(row.getData()); }); this.elementContents = this.generator(this.key, this.getRowCount(), data, this.getComponent()); while (this.element.firstChild) this.element.removeChild(this.element.firstChild); if (typeof this.elementContents === "string") { this.element.innerHTML = this.elementContents; } else { this.element.appendChild(this.elementContents); } this.element.insertBefore(this.arrowElement, this.element.firstChild); } getPath(path = []) { path.unshift(this.key); if (this.parent) { this.parent.getPath(path); } return path; } ////////////// Standard Row Functions ////////////// getElement() { return this.elementContents ? this.element : this.generateElement(); } generateElement() { this.addBindings = false; this._visSet(); if (this.visible) { this.element.classList.add("tabulator-group-visible"); } else { this.element.classList.remove("tabulator-group-visible"); } for (var i = 0; i < this.element.childNodes.length; ++i) { this.element.childNodes[i].parentNode.removeChild(this.element.childNodes[i]); } this.generateGroupHeaderContents(); return this.element; } detachElement() { if (this.element && this.element.parentNode) { this.element.parentNode.removeChild(this.element); } } //normalize the height of elements in the row normalizeHeight() { this.setHeight(this.element.clientHeight); } initialize(force) { if (!this.initialized || force) { this.normalizeHeight(); this.initialized = true; } } reinitialize() { this.initialized = false; this.height = 0; if (Helpers.elVisible(this.element)) { this.initialize(true); } } setHeight(height) { if (this.height != height) { this.height = height; this.outerHeight = this.element.offsetHeight; } } //return rows outer height getHeight() { return this.outerHeight; } getGroup() { return this; } reinitializeHeight() { } calcHeight() { this.outerHeight = this.element.offsetHeight; } setCellHeight() { } clearCellHeight() { } deinitializeHeight() { } rendered() { } //////////////// Object Generation ///////////////// getComponent() { if (!this.component) { this.component = new GroupComponent(this); } return this.component; } } class GroupRows extends Module { constructor(table) { super(table); this.groupIDLookups = false; this.startOpen = [function () { return false; }]; this.headerGenerator = [function () { return ""; }]; this.groupList = []; this.allowedValues = false; this.groups = {}; this.displayHandler = this.getRows.bind(this); this.blockRedraw = false; this.registerTableOption("groupBy", false); this.registerTableOption("groupStartOpen", true); this.registerTableOption("groupValues", false); this.registerTableOption("groupUpdateOnCellEdit", false); this.registerTableOption("groupHeader", false); this.registerTableOption("groupHeaderPrint", null); this.registerTableOption("groupHeaderClipboard", null); this.registerTableOption("groupHeaderHtmlOutput", null); this.registerTableOption("groupHeaderDownload", null); this.registerTableOption("groupToggleElement", "arrow"); this.registerTableOption("groupClosedShowCalcs", false); this.registerTableFunction("setGroupBy", this.setGroupBy.bind(this)); this.registerTableFunction("setGroupValues", this.setGroupValues.bind(this)); this.registerTableFunction("setGroupStartOpen", this.setGroupStartOpen.bind(this)); this.registerTableFunction("setGroupHeader", this.setGroupHeader.bind(this)); this.registerTableFunction("getGroups", this.userGetGroups.bind(this)); this.registerTableFunction("getGroupedData", this.userGetGroupedData.bind(this)); this.registerComponentFunction("row", "getGroup", this.rowGetGroup.bind(this)); } //initialize group configuration initialize() { this.subscribe("table-destroy", this._blockRedrawing.bind(this)); this.subscribe("rows-wipe", this._blockRedrawing.bind(this)); this.subscribe("rows-wiped", this._restore_redrawing.bind(this)); if (this.table.options.groupBy) { if (this.table.options.groupUpdateOnCellEdit) { this.subscribe("cell-value-updated", this.cellUpdated.bind(this)); this.subscribe("row-data-changed", this.reassignRowToGroup.bind(this), 0); } this.subscribe("table-built", this.configureGroupSetup.bind(this)); this.subscribe("row-deleting", this.rowDeleting.bind(this)); this.subscribe("row-deleted", this.rowsUpdated.bind(this)); this.subscribe("scroll-horizontal", this.scrollHeaders.bind(this)); this.subscribe("rows-wipe", this.wipe.bind(this)); this.subscribe("rows-added", this.rowsUpdated.bind(this)); this.subscribe("row-moving", this.rowMoving.bind(this)); this.subscribe("row-adding-index", this.rowAddingIndex.bind(this)); this.subscribe("rows-sample", this.rowSample.bind(this)); this.subscribe("render-virtual-fill", this.virtualRenderFill.bind(this)); this.subscribe("table-layout", this.virtualRenderFill.bind(this)); this.registerDisplayHandler(this.displayHandler, 20); this.initialized = true; } } _blockRedrawing() { this.blockRedraw = true; } _restore_redrawing() { this.blockRedraw = false; } configureGroupSetup() { if (this.table.options.groupBy) { var groupBy = this.table.options.groupBy, startOpen = this.table.options.groupStartOpen, groupHeader = this.table.options.groupHeader; this.allowedValues = this.table.options.groupValues; if (Array.isArray(groupBy) && Array.isArray(groupHeader) && groupBy.length > groupHeader.length) { console.warn("Error creating group headers, groupHeader array is shorter than groupBy array"); } this.headerGenerator = [function () { return ""; }]; this.startOpen = [function () { return false; }]; this.langBind("groups|item", (langValue, lang) => { this.headerGenerator[0] = (value, count, data) => { return (typeof value === "undefined" ? "" : value) + "(" + count + " " + (count === 1 ? langValue : lang.groups.items) + ")"; }; }); this.groupIDLookups = []; if (groupBy) { if (this.table.modExists("columnCalcs") && this.table.options.columnCalcs != "table" && this.table.options.columnCalcs != "both") { this.table.modules.columnCalcs.removeCalcs(); } } else { if (this.table.modExists("columnCalcs") && this.table.options.columnCalcs != "group") { var cols = this.table.columnManager.getRealColumns(); cols.forEach((col) => { if (col.definition.topCalc) { this.table.modules.columnCalcs.initializeTopRow(); } if (col.definition.bottomCalc) { this.table.modules.columnCalcs.initializeBottomRow(); } }); } } if (!Array.isArray(groupBy)) { groupBy = [groupBy]; } groupBy.forEach((group, i) => { var lookupFunc, column; if (typeof group == "function") { lookupFunc = group; } else { column = this.table.columnManager.getColumnByField(group); if (column) { lookupFunc = function (data) { return column.getFieldValue(data); }; } else { lookupFunc = function (data) { return data[group]; }; } } this.groupIDLookups.push({ field: typeof group === "function" ? false : group, func: lookupFunc, values: this.allowedValues ? this.allowedValues[i] : false }); }); if (startOpen) { if (!Array.isArray(startOpen)) { startOpen = [startOpen]; } startOpen.forEach((level) => { }); this.startOpen = startOpen; } if (groupHeader) { this.headerGenerator = Array.isArray(groupHeader) ? groupHeader : [groupHeader]; } } else { this.groupList = []; this.groups = {}; } } rowSample(rows2, prevValue) { if (this.table.options.groupBy) { var group = this.getGroups(false)[0]; prevValue.push(group.getRows(false)[0]); } return prevValue; } virtualRenderFill() { const layout = this.layoutMode(); if (layout === "fitDataFill" || layout === "fitDataStretch" || layout === "fitColumns") { this.table.rowManager.tableElement.style.minWidth = this.table.columnManager.getWidth() + "px"; } } rowAddingIndex(row, index, top) { if (this.table.options.groupBy) { this.assignRowToGroup(row); var groupRows = row.modules.group.rows; if (groupRows.length > 1) { if (!index || index && groupRows.indexOf(index) == -1) { if (top) { if (groupRows[0] !== row) { index = groupRows[0]; this.table.rowManager.moveRowInArray(row.modules.group.rows, row, index, !top); } } else { if (groupRows[groupRows.length - 1] !== row) { index = groupRows[groupRows.length - 1]; this.table.rowManager.moveRowInArray(row.modules.group.rows, row, index, !top); } } } else { this.table.rowManager.moveRowInArray(row.modules.group.rows, row, index, !top); } } return index; } } trackChanges() { this.dispatch("group-changed"); } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// setGroupBy(groups) { this.table.options.groupBy = groups; if (!this.initialized) { this.initialize(); } this.configureGroupSetup(); if (!groups && this.table.modExists("columnCalcs") && this.table.options.columnCalcs === true) { this.table.modules.columnCalcs.reinitializeCalcs(); } this.refreshData(); this.trackChanges(); } setGroupValues(groupValues) { this.table.options.groupValues = groupValues; this.configureGroupSetup(); this.refreshData(); this.trackChanges(); } setGroupStartOpen(values) { this.table.options.groupStartOpen = values; this.configureGroupSetup(); if (this.table.options.groupBy) { this.refreshData(); this.trackChanges(); } else { console.warn("Grouping Update - cant refresh view, no groups have been set"); } } setGroupHeader(values) { this.table.options.groupHeader = values; this.configureGroupSetup(); if (this.table.options.groupBy) { this.refreshData(); this.trackChanges(); } else { console.warn("Grouping Update - cant refresh view, no groups have been set"); } } userGetGroups(values) { return this.getGroups(true); } // get grouped table data in the same format as getData() userGetGroupedData() { return this.table.options.groupBy ? this.getGroupedData() : this.getData(); } /////////////////////////////////////// ///////// Component Functions ///////// /////////////////////////////////////// rowGetGroup(row) { return row.modules.group ? row.modules.group.getComponent() : false; } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// rowMoving(from, to, after) { if (this.table.options.groupBy) { if (!after && to instanceof Group) { to = this.table.rowManager.prevDisplayRow(from) || to; } var toGroup = to instanceof Group ? to : to.modules.group; var fromGroup = from instanceof Group ? from : from.modules.group; if (toGroup === fromGroup) { this.table.rowManager.moveRowInArray(toGroup.rows, from, to, after); } else { if (fromGroup) { fromGroup.removeRow(from); } toGroup.insertRow(from, to, after); } } } rowDeleting(row) { if (this.table.options.groupBy && row.modules.group) { row.modules.group.removeRow(row); } } rowsUpdated(row) { if (this.table.options.groupBy) { this.updateGroupRows(true); } } cellUpdated(cell) { if (this.table.options.groupBy) { this.reassignRowToGroup(cell.row); } } //return appropriate rows with group headers getRows(rows2) { if (this.table.options.groupBy && this.groupIDLookups.length) { this.dispatchExternal("dataGrouping"); this.generateGroups(rows2); if (this.subscribedExternal("dataGrouped")) { this.dispatchExternal("dataGrouped", this.getGroups(true)); } return this.updateGroupRows(); } else { return rows2.slice(0); } } getGroups(component) { var groupComponents = []; this.groupList.forEach(function (group) { groupComponents.push(component ? group.getComponent() : group); }); return groupComponents; } getChildGroups(group) { var groupComponents = []; if (!group) { group = this; } group.groupList.forEach((child) => { if (child.groupList.length) { groupComponents = groupComponents.concat(this.getChildGroups(child)); } else { groupComponents.push(child); } }); return groupComponents; } wipe() { if (this.table.options.groupBy) { this.groupList.forEach(function (group) { group.wipe(); }); this.groupList = []; this.groups = {}; } } pullGroupListData(groupList) { var groupListData = []; groupList.forEach((group) => { var groupHeader = {}; groupHeader.level = 0; groupHeader.rowCount = 0; groupHeader.headerContent = ""; var childData = []; if (group.hasSubGroups) { childData = this.pullGroupListData(group.groupList); groupHeader.level = group.level; groupHeader.rowCount = childData.length - group.groupList.length; groupHeader.headerContent = group.generator(group.key, groupHeader.rowCount, group.rows, group); groupListData.push(groupHeader); groupListData = groupListData.concat(childData); } else { groupHeader.level = group.level; groupHeader.headerContent = group.generator(group.key, group.rows.length, group.rows, group); groupHeader.rowCount = group.getRows().length; groupListData.push(groupHeader); group.getRows().forEach((row) => { groupListData.push(row.getData("data")); }); } }); return groupListData; } getGroupedData() { return this.pullGroupListData(this.groupList); } getRowGroup(row) { var match = false; if (this.options("dataTree")) { row = this.table.modules.dataTree.getTreeParentRoot(row); } this.groupList.forEach((group) => { var result = group.getRowGroup(row); if (result) { match = result; } }); return match; } countGroups() { return this.groupList.length; } generateGroups(rows2) { var oldGroups = this.groups; this.groups = {}; this.groupList = []; if (this.allowedValues && this.allowedValues[0]) { this.allowedValues[0].forEach((value) => { this.createGroup(value, 0, oldGroups); }); rows2.forEach((row) => { this.assignRowToExistingGroup(row, oldGroups); }); } else { rows2.forEach((row) => { this.assignRowToGroup(row, oldGroups); }); } Object.values(oldGroups).forEach((group) => { group.wipe(true); }); } createGroup(groupID, level, oldGroups) { var groupKey = level + "_" + groupID, group; oldGroups = oldGroups || []; group = new Group(this, false, level, groupID, this.groupIDLookups[0].field, this.headerGenerator[0], oldGroups[groupKey]); this.groups[groupKey] = group; this.groupList.push(group); } assignRowToExistingGroup(row, oldGroups) { var groupID = this.groupIDLookups[0].func(row.getData()), groupKey = "0_" + groupID; if (this.groups[groupKey]) { this.groups[groupKey].addRow(row); } } assignRowToGroup(row, oldGroups) { var groupID = this.groupIDLookups[0].func(row.getData()), newGroupNeeded = !this.groups["0_" + groupID]; if (newGroupNeeded) { this.createGroup(groupID, 0, oldGroups); } this.groups["0_" + groupID].addRow(row); return !newGroupNeeded; } reassignRowToGroup(row) { if (row.type === "row") { var oldRowGroup = row.modules.group, oldGroupPath = oldRowGroup.getPath(), newGroupPath = this.getExpectedPath(row), samePath; samePath = oldGroupPath.length == newGroupPath.length && oldGroupPath.every((element, index) => { return element === newGroupPath[index]; }); if (!samePath) { oldRowGroup.removeRow(row); this.assignRowToGroup(row, this.groups); this.refreshData(true); } } } getExpectedPath(row) { var groupPath = [], rowData = row.getData(); this.groupIDLookups.forEach((groupId) => { groupPath.push(groupId.func(rowData)); }); return groupPath; } updateGroupRows(force) { var output = []; if (!this.blockRedraw) { this.groupList.forEach((group) => { output = output.concat(group.getHeadersAndRows()); }); if (force) { this.refreshData(true); } } return output; } scrollHeaders(left) { if (this.table.options.groupBy) { if (this.table.options.renderHorizontal === "virtual") { left -= this.table.columnManager.renderer.vDomPadLeft; } left = left + "px"; this.groupList.forEach((group) => { group.scrollHeader(left); }); } } removeGroup(group) { var groupKey = group.level + "_" + group.key, index; if (this.groups[groupKey]) { delete this.groups[groupKey]; index = this.groupList.indexOf(group); if (index > -1) { this.groupList.splice(index, 1); } } } checkBasicModeGroupHeaderWidth() { var element = this.table.rowManager.tableElement, onlyGroupHeaders = true; this.table.rowManager.getDisplayRows().forEach((row, index) => { this.table.rowManager.styleRow(row, index); element.appendChild(row.getElement()); row.initialize(true); if (row.type !== "group") { onlyGroupHeaders = false; } }); if (onlyGroupHeaders) { element.style.minWidth = this.table.columnManager.getWidth() + "px"; } else { element.style.minWidth = ""; } } } __publicField(GroupRows, "moduleName", "groupRows"); var defaultUndoers = { cellEdit: function (action) { action.component.setValueProcessData(action.data.oldValue); action.component.cellRendered(); }, rowAdd: function (action) { action.component.deleteActual(); this.table.rowManager.checkPlaceholder(); }, rowDelete: function (action) { var newRow = this.table.rowManager.addRowActual(action.data.data, action.data.pos, action.data.index); if (this.table.options.groupBy && this.table.modExists("groupRows")) { this.table.modules.groupRows.updateGroupRows(true); } this._rebindRow(action.component, newRow); this.table.rowManager.checkPlaceholder(); }, rowMove: function (action) { var after = action.data.posFrom - action.data.posTo > 0; this.table.rowManager.moveRowActual(action.component, this.table.rowManager.getRowFromPosition(action.data.posFrom), after); this.table.rowManager.regenerateRowPositions(); this.table.rowManager.reRenderInPosition(); } }; var defaultRedoers = { cellEdit: function (action) { action.component.setValueProcessData(action.data.newValue); action.component.cellRendered(); }, rowAdd: function (action) { var newRow = this.table.rowManager.addRowActual(action.data.data, action.data.pos, action.data.index); if (this.table.options.groupBy && this.table.modExists("groupRows")) { this.table.modules.groupRows.updateGroupRows(true); } this._rebindRow(action.component, newRow); this.table.rowManager.checkPlaceholder(); }, rowDelete: function (action) { action.component.deleteActual(); this.table.rowManager.checkPlaceholder(); }, rowMove: function (action) { this.table.rowManager.moveRowActual(action.component, this.table.rowManager.getRowFromPosition(action.data.posTo), action.data.after); this.table.rowManager.regenerateRowPositions(); this.table.rowManager.reRenderInPosition(); } }; var bindings$1 = { undo: ["ctrl + 90", "meta + 90"], redo: ["ctrl + 89", "meta + 89"] }; var actions$1 = { undo: function (e) { var cell = false; if (this.table.options.history && this.table.modExists("history") && this.table.modExists("edit")) { cell = this.table.modules.edit.currentCell; if (!cell) { e.preventDefault(); this.table.modules.history.undo(); } } }, redo: function (e) { var cell = false; if (this.table.options.history && this.table.modExists("history") && this.table.modExists("edit")) { cell = this.table.modules.edit.currentCell; if (!cell) { e.preventDefault(); this.table.modules.history.redo(); } } } }; var extensions$3 = { keybindings: { bindings: bindings$1, actions: actions$1 } }; const _History = class _History extends Module { constructor(table) { super(table); this.history = []; this.index = -1; this.registerTableOption("history", false); } initialize() { if (this.table.options.history) { this.subscribe("cell-value-updated", this.cellUpdated.bind(this)); this.subscribe("cell-delete", this.clearComponentHistory.bind(this)); this.subscribe("row-delete", this.rowDeleted.bind(this)); this.subscribe("rows-wipe", this.clear.bind(this)); this.subscribe("row-added", this.rowAdded.bind(this)); this.subscribe("row-move", this.rowMoved.bind(this)); } this.registerTableFunction("undo", this.undo.bind(this)); this.registerTableFunction("redo", this.redo.bind(this)); this.registerTableFunction("getHistoryUndoSize", this.getHistoryUndoSize.bind(this)); this.registerTableFunction("getHistoryRedoSize", this.getHistoryRedoSize.bind(this)); this.registerTableFunction("clearHistory", this.clear.bind(this)); } rowMoved(from, to, after) { this.action("rowMove", from, {posFrom: from.getPosition(), posTo: to.getPosition(), to, after}); } rowAdded(row, data, pos, index) { this.action("rowAdd", row, {data, pos, index}); } rowDeleted(row) { var index, rows2; if (this.table.options.groupBy) { rows2 = row.getComponent().getGroup()._getSelf().rows; index = rows2.indexOf(row); if (index) { index = rows2[index - 1]; } } else { index = row.table.rowManager.getRowIndex(row); if (index) { index = row.table.rowManager.rows[index - 1]; } } this.action("rowDelete", row, {data: row.getData(), pos: !index, index}); } cellUpdated(cell) { this.action("cellEdit", cell, {oldValue: cell.oldValue, newValue: cell.value}); } clear() { this.history = []; this.index = -1; } action(type, component, data) { this.history = this.history.slice(0, this.index + 1); this.history.push({ type, component, data }); this.index++; } getHistoryUndoSize() { return this.index + 1; } getHistoryRedoSize() { return this.history.length - (this.index + 1); } clearComponentHistory(component) { var index = this.history.findIndex(function (item) { return item.component === component; }); if (index > -1) { this.history.splice(index, 1); if (index <= this.index) { this.index--; } this.clearComponentHistory(component); } } undo() { if (this.index > -1) { let action = this.history[this.index]; _History.undoers[action.type].call(this, action); this.index--; this.dispatchExternal("historyUndo", action.type, action.component.getComponent(), action.data); return true; } else { console.warn(this.options("history") ? "History Undo Error - No more history to undo" : "History module not enabled"); return false; } } redo() { if (this.history.length - 1 > this.index) { this.index++; let action = this.history[this.index]; _History.redoers[action.type].call(this, action); this.dispatchExternal("historyRedo", action.type, action.component.getComponent(), action.data); return true; } else { console.warn(this.options("history") ? "History Redo Error - No more history to redo" : "History module not enabled"); return false; } } //rebind rows to new element after deletion _rebindRow(oldRow, newRow) { this.history.forEach(function (action) { if (action.component instanceof Row) { if (action.component === oldRow) { action.component = newRow; } } else if (action.component instanceof Cell) { if (action.component.row === oldRow) { var field = action.component.column.getField(); if (field) { action.component = newRow.getCell(field); } } } }); } }; __publicField(_History, "moduleName", "history"); __publicField(_History, "moduleExtensions", extensions$3); //load defaults __publicField(_History, "undoers", defaultUndoers); __publicField(_History, "redoers", defaultRedoers); let History = _History; class HtmlTableImport extends Module { constructor(table) { super(table); this.fieldIndex = []; this.hasIndex = false; } initialize() { this.tableElementCheck(); } tableElementCheck() { if (this.table.originalElement && this.table.originalElement.tagName === "TABLE") { if (this.table.originalElement.childNodes.length) { this.parseTable(); } else { console.warn("Unable to parse data from empty table tag, Tabulator should be initialized on a div tag unless importing data from a table element."); } } } parseTable() { var element = this.table.originalElement, options = this.table.options, headers = element.getElementsByTagName("th"), rows2 = element.getElementsByTagName("tbody")[0], data = []; this.hasIndex = false; this.dispatchExternal("htmlImporting"); rows2 = rows2 ? rows2.getElementsByTagName("tr") : []; this._extractOptions(element, options); if (headers.length) { this._extractHeaders(headers, rows2); } else { this._generateBlankHeaders(headers, rows2); } for (var index = 0; index < rows2.length; index++) { var row = rows2[index], cells = row.getElementsByTagName("td"), item = {}; if (!this.hasIndex) { item[options.index] = index; } for (var i = 0; i < cells.length; i++) { var cell = cells[i]; if (typeof this.fieldIndex[i] !== "undefined") { item[this.fieldIndex[i]] = cell.innerHTML; } } data.push(item); } options.data = data; this.dispatchExternal("htmlImported"); } //extract tabulator attribute options _extractOptions(element, options, defaultOptions2) { var attributes = element.attributes; var optionsArr = defaultOptions2 ? Object.keys(defaultOptions2) : Object.keys(options); var optionsList = {}; optionsArr.forEach((item) => { optionsList[item.toLowerCase()] = item; }); for (var index in attributes) { var attrib = attributes[index]; var name2; if (attrib && typeof attrib == "object" && attrib.name && attrib.name.indexOf("tabulator-") === 0) { name2 = attrib.name.replace("tabulator-", ""); if (typeof optionsList[name2] !== "undefined") { options[optionsList[name2]] = this._attribValue(attrib.value); } } } } //get value of attribute _attribValue(value) { if (value === "true") { return true; } if (value === "false") { return false; } return value; } //find column if it has already been defined _findCol(title) { var match = this.table.options.columns.find((column) => { return column.title === title; }); return match || false; } //extract column from headers _extractHeaders(headers, rows2) { for (var index = 0; index < headers.length; index++) { var header = headers[index], exists2 = false, col = this._findCol(header.textContent), width; if (col) { exists2 = true; } else { col = {title: header.textContent.trim()}; } if (!col.field) { col.field = header.textContent.trim().toLowerCase().replaceAll(" ", "_"); } width = header.getAttribute("width"); if (width && !col.width) { col.width = width; } this._extractOptions(header, col, this.table.columnManager.optionsList.registeredDefaults); this.fieldIndex[index] = col.field; if (col.field == this.table.options.index) { this.hasIndex = true; } if (!exists2) { this.table.options.columns.push(col); } } } //generate blank headers _generateBlankHeaders(headers, rows2) { for (var index = 0; index < headers.length; index++) { var header = headers[index], col = {title: "", field: "col" + index}; this.fieldIndex[index] = col.field; var width = header.getAttribute("width"); if (width) { col.width = width; } this.table.options.columns.push(col); } } } __publicField(HtmlTableImport, "moduleName", "htmlTableImport"); function csv(input2) { var data = [], row = 0, col = 0, inQuote = false; for (let index = 0; index < input2.length; index++) { let char = input2[index], nextChar = input2[index + 1]; if (!data[row]) { data[row] = []; } if (!data[row][col]) { data[row][col] = ""; } if (char == '"' && inQuote && nextChar == '"') { data[row][col] += char; index++; continue; } if (char == '"') { inQuote = !inQuote; continue; } if (char == "," && !inQuote) { col++; continue; } if (char == "\r" && nextChar == "\n" && !inQuote) { col = 0; row++; index++; continue; } if ((char == "\r" || char == "\n") && !inQuote) { col = 0; row++; continue; } data[row][col] += char; } return data; } function json(input2) { try { return JSON.parse(input2); } catch (e) { console.warn("JSON Import Error - File contents is invalid JSON", e); return Promise.reject(); } } function array$1(input2) { return input2; } function xlsx(input2) { var XLSXLib = this.dependencyRegistry.lookup("XLSX"), workbook2 = XLSXLib.read(input2), sheet = workbook2.Sheets[workbook2.SheetNames[0]]; return XLSXLib.utils.sheet_to_json(sheet, {header: 1}); } var defaultImporters = { csv, json, array: array$1, xlsx }; const _Import = class _Import extends Module { constructor(table) { super(table); this.registerTableOption("importFormat"); this.registerTableOption("importReader", "text"); this.registerTableOption("importHeaderTransform"); this.registerTableOption("importValueTransform"); this.registerTableOption("importDataValidator"); this.registerTableOption("importFileValidator"); } initialize() { this.registerTableFunction("import", this.importFromFile.bind(this)); if (this.table.options.importFormat) { this.subscribe("data-loading", this.loadDataCheck.bind(this), 10); this.subscribe("data-load", this.loadData.bind(this), 10); } } loadDataCheck(data) { return this.table.options.importFormat && (typeof data === "string" || Array.isArray(data) && data.length && Array.isArray(data)); } loadData(data, params, config, silent, previousData) { return this.importData(this.lookupImporter(), data).then(this.structureData.bind(this)).catch((err) => { console.error("Import Error:", err || "Unable to import data"); return Promise.reject(err); }); } lookupImporter(importFormat) { var importer; if (!importFormat) { importFormat = this.table.options.importFormat; } if (typeof importFormat === "string") { importer = _Import.importers[importFormat]; } else { importer = importFormat; } if (!importer) { console.error("Import Error - Importer not found:", importFormat); } return importer; } importFromFile(importFormat, extension, importReader) { var importer = this.lookupImporter(importFormat); if (importer) { return this.pickFile(extension, importReader).then(this.importData.bind(this, importer)).then(this.structureData.bind(this)).then(this.mutateData.bind(this)).then(this.validateData.bind(this)).then(this.setData.bind(this)).catch((err) => { this.dispatch("import-error", err); this.dispatchExternal("importError", err); console.error("Import Error:", err || "Unable to import file"); this.table.dataLoader.alertError(); setTimeout(() => { this.table.dataLoader.clearAlert(); }, 3e3); return Promise.reject(err); }); } } pickFile(extensions2, importReader) { return new Promise((resolve, reject) => { var input2 = document.createElement("input"); input2.type = "file"; input2.accept = extensions2; input2.addEventListener("change", (e) => { var file = input2.files[0], reader = new FileReader(), valid = this.validateFile(file); if (valid === true) { this.dispatch("import-importing", input2.files); this.dispatchExternal("importImporting", input2.files); switch (importReader || this.table.options.importReader) { case "buffer": reader.readAsArrayBuffer(file); break; case "binary": reader.readAsBinaryString(file); break; case "url": reader.readAsDataURL(file); break; case "text": default: reader.readAsText(file); } reader.onload = (e2) => { resolve(reader.result); }; reader.onerror = (e2) => { console.warn("File Load Error - Unable to read file"); reject(e2); }; } else { reject(valid); } }); this.dispatch("import-choose"); this.dispatchExternal("importChoose"); input2.click(); }); } importData(importer, fileContents) { var data; this.table.dataLoader.alertLoader(); return new Promise((resolve, reject) => { setTimeout(() => { data = importer.call(this.table, fileContents); if (data instanceof Promise) { resolve(data); } else { data ? resolve(data) : reject(); } }, 10); }); } structureData(parsedData) { var data = []; if (Array.isArray(parsedData) && parsedData.length && Array.isArray(parsedData[0])) { if (this.table.options.autoColumns) { data = this.structureArrayToObject(parsedData); } else { data = this.structureArrayToColumns(parsedData); } return data; } else { return parsedData; } } mutateData(data) { var output = []; if (Array.isArray(data)) { data.forEach((row) => { output.push(this.table.modules.mutator.transformRow(row, "import")); }); } else { output = data; } return output; } transformHeader(headers) { var output = []; if (this.table.options.importHeaderTransform) { headers.forEach((item) => { output.push(this.table.options.importHeaderTransform.call(this.table, item, headers)); }); } else { return headers; } return output; } transformData(row) { var output = []; if (this.table.options.importValueTransform) { row.forEach((item) => { output.push(this.table.options.importValueTransform.call(this.table, item, row)); }); } else { return row; } return output; } structureArrayToObject(parsedData) { var columns = this.transformHeader(parsedData.shift()); var data = parsedData.map((values) => { var row = {}; values = this.transformData(values); columns.forEach((key, i) => { row[key] = values[i]; }); return row; }); return data; } structureArrayToColumns(parsedData) { var data = [], firstRow = this.transformHeader(parsedData[0]), columns = this.table.getColumns(); if (columns[0] && firstRow[0]) { if (columns[0].getDefinition().title === firstRow[0]) { parsedData.shift(); } } parsedData.forEach((rowData) => { var row = {}; rowData = this.transformData(rowData); rowData.forEach((value, index) => { var column = columns[index]; if (column) { row[column.getField()] = value; } }); data.push(row); }); return data; } validateFile(file) { if (this.table.options.importFileValidator) { return this.table.options.importFileValidator.call(this.table, file); } return true; } validateData(data) { var result; if (this.table.options.importDataValidator) { result = this.table.options.importDataValidator.call(this.table, data); if (result === true) { return data; } else { return Promise.reject(result); } } return data; } setData(data) { this.dispatch("import-imported", data); this.dispatchExternal("importImported", data); this.table.dataLoader.clearAlert(); return this.table.setData(data); } }; __publicField(_Import, "moduleName", "import"); //load defaults __publicField(_Import, "importers", defaultImporters); let Import = _Import; class Interaction extends Module { constructor(table) { super(table); this.eventMap = { //row events rowClick: "row-click", rowDblClick: "row-dblclick", rowContext: "row-contextmenu", rowMouseEnter: "row-mouseenter", rowMouseLeave: "row-mouseleave", rowMouseOver: "row-mouseover", rowMouseOut: "row-mouseout", rowMouseMove: "row-mousemove", rowMouseDown: "row-mousedown", rowMouseUp: "row-mouseup", rowTap: "row", rowDblTap: "row", rowTapHold: "row", //cell events cellClick: "cell-click", cellDblClick: "cell-dblclick", cellContext: "cell-contextmenu", cellMouseEnter: "cell-mouseenter", cellMouseLeave: "cell-mouseleave", cellMouseOver: "cell-mouseover", cellMouseOut: "cell-mouseout", cellMouseMove: "cell-mousemove", cellMouseDown: "cell-mousedown", cellMouseUp: "cell-mouseup", cellTap: "cell", cellDblTap: "cell", cellTapHold: "cell", //column header events headerClick: "column-click", headerDblClick: "column-dblclick", headerContext: "column-contextmenu", headerMouseEnter: "column-mouseenter", headerMouseLeave: "column-mouseleave", headerMouseOver: "column-mouseover", headerMouseOut: "column-mouseout", headerMouseMove: "column-mousemove", headerMouseDown: "column-mousedown", headerMouseUp: "column-mouseup", headerTap: "column", headerDblTap: "column", headerTapHold: "column", //group header groupClick: "group-click", groupDblClick: "group-dblclick", groupContext: "group-contextmenu", groupMouseEnter: "group-mouseenter", groupMouseLeave: "group-mouseleave", groupMouseOver: "group-mouseover", groupMouseOut: "group-mouseout", groupMouseMove: "group-mousemove", groupMouseDown: "group-mousedown", groupMouseUp: "group-mouseup", groupTap: "group", groupDblTap: "group", groupTapHold: "group" }; this.subscribers = {}; this.touchSubscribers = {}; this.columnSubscribers = {}; this.touchWatchers = { row: { tap: null, tapDbl: null, tapHold: null }, cell: { tap: null, tapDbl: null, tapHold: null }, column: { tap: null, tapDbl: null, tapHold: null }, group: { tap: null, tapDbl: null, tapHold: null } }; this.registerColumnOption("headerClick"); this.registerColumnOption("headerDblClick"); this.registerColumnOption("headerContext"); this.registerColumnOption("headerMouseEnter"); this.registerColumnOption("headerMouseLeave"); this.registerColumnOption("headerMouseOver"); this.registerColumnOption("headerMouseOut"); this.registerColumnOption("headerMouseMove"); this.registerColumnOption("headerMouseDown"); this.registerColumnOption("headerMouseUp"); this.registerColumnOption("headerTap"); this.registerColumnOption("headerDblTap"); this.registerColumnOption("headerTapHold"); this.registerColumnOption("cellClick"); this.registerColumnOption("cellDblClick"); this.registerColumnOption("cellContext"); this.registerColumnOption("cellMouseEnter"); this.registerColumnOption("cellMouseLeave"); this.registerColumnOption("cellMouseOver"); this.registerColumnOption("cellMouseOut"); this.registerColumnOption("cellMouseMove"); this.registerColumnOption("cellMouseDown"); this.registerColumnOption("cellMouseUp"); this.registerColumnOption("cellTap"); this.registerColumnOption("cellDblTap"); this.registerColumnOption("cellTapHold"); } initialize() { this.initializeExternalEvents(); this.subscribe("column-init", this.initializeColumn.bind(this)); this.subscribe("cell-dblclick", this.cellContentsSelectionFixer.bind(this)); this.subscribe("scroll-horizontal", this.clearTouchWatchers.bind(this)); this.subscribe("scroll-vertical", this.clearTouchWatchers.bind(this)); } clearTouchWatchers() { var types = Object.values(this.touchWatchers); types.forEach((type) => { clearTimeout(type.tapDbl); clearTimeout(type.tapHold); for (let key in type) { type[key] = null; } }); } cellContentsSelectionFixer(e, cell) { var range2; if (this.table.modExists("edit")) { if (this.table.modules.edit.currentCell === cell) { return; } } e.preventDefault(); try { if (document.selection) { range2 = document.body.createTextRange(); range2.moveToElementText(cell.getElement()); range2.select(); } else if (window.getSelection) { range2 = document.createRange(); range2.selectNode(cell.getElement()); window.getSelection().removeAllRanges(); window.getSelection().addRange(range2); } } catch (e2) { } } initializeExternalEvents() { for (let key in this.eventMap) { this.subscriptionChangeExternal(key, this.subscriptionChanged.bind(this, key)); } } subscriptionChanged(key, added) { if (added) { if (!this.subscribers[key]) { if (this.eventMap[key].includes("-")) { this.subscribers[key] = this.handle.bind(this, key); this.subscribe(this.eventMap[key], this.subscribers[key]); } else { this.subscribeTouchEvents(key); } } } else { if (this.eventMap[key].includes("-")) { if (this.subscribers[key] && !this.columnSubscribers[key] && !this.subscribedExternal(key)) { this.unsubscribe(this.eventMap[key], this.subscribers[key]); delete this.subscribers[key]; } } else { this.unsubscribeTouchEvents(key); } } } subscribeTouchEvents(key) { var type = this.eventMap[key]; if (!this.touchSubscribers[type + "-touchstart"]) { this.touchSubscribers[type + "-touchstart"] = this.handleTouch.bind(this, type, "start"); this.touchSubscribers[type + "-touchend"] = this.handleTouch.bind(this, type, "end"); this.subscribe(type + "-touchstart", this.touchSubscribers[type + "-touchstart"]); this.subscribe(type + "-touchend", this.touchSubscribers[type + "-touchend"]); } this.subscribers[key] = true; } unsubscribeTouchEvents(key) { var noTouch = true, type = this.eventMap[key]; if (this.subscribers[key] && !this.subscribedExternal(key)) { delete this.subscribers[key]; for (let i in this.eventMap) { if (this.eventMap[i] === type) { if (this.subscribers[i]) { noTouch = false; } } } if (noTouch) { this.unsubscribe(type + "-touchstart", this.touchSubscribers[type + "-touchstart"]); this.unsubscribe(type + "-touchend", this.touchSubscribers[type + "-touchend"]); delete this.touchSubscribers[type + "-touchstart"]; delete this.touchSubscribers[type + "-touchend"]; } } } initializeColumn(column) { var def = column.definition; for (let key in this.eventMap) { if (def[key]) { this.subscriptionChanged(key, true); if (!this.columnSubscribers[key]) { this.columnSubscribers[key] = []; } this.columnSubscribers[key].push(column); } } } handle(action, e, component) { this.dispatchEvent(action, e, component); } handleTouch(type, action, e, component) { var watchers = this.touchWatchers[type]; if (type === "column") { type = "header"; } switch (action) { case "start": watchers.tap = true; clearTimeout(watchers.tapHold); watchers.tapHold = setTimeout(() => { clearTimeout(watchers.tapHold); watchers.tapHold = null; watchers.tap = null; clearTimeout(watchers.tapDbl); watchers.tapDbl = null; this.dispatchEvent(type + "TapHold", e, component); }, 1e3); break; case "end": if (watchers.tap) { watchers.tap = null; this.dispatchEvent(type + "Tap", e, component); } if (watchers.tapDbl) { clearTimeout(watchers.tapDbl); watchers.tapDbl = null; this.dispatchEvent(type + "DblTap", e, component); } else { watchers.tapDbl = setTimeout(() => { clearTimeout(watchers.tapDbl); watchers.tapDbl = null; }, 300); } clearTimeout(watchers.tapHold); watchers.tapHold = null; break; } } dispatchEvent(action, e, component) { var componentObj = component.getComponent(), callback; if (this.columnSubscribers[action]) { if (component instanceof Cell) { callback = component.column.definition[action]; } else if (component instanceof Column) { callback = component.definition[action]; } if (callback) { callback(e, componentObj); } } this.dispatchExternal(action, e, componentObj); } } __publicField(Interaction, "moduleName", "interaction"); var defaultBindings = { navPrev: "shift + 9", navNext: 9, navUp: 38, navDown: 40, navLeft: 37, navRight: 39, scrollPageUp: 33, scrollPageDown: 34, scrollToStart: 36, scrollToEnd: 35 }; var defaultActions = { keyBlock: function (e) { e.stopPropagation(); e.preventDefault(); }, scrollPageUp: function (e) { var rowManager = this.table.rowManager, newPos = rowManager.scrollTop - rowManager.element.clientHeight; e.preventDefault(); if (rowManager.displayRowsCount) { if (newPos >= 0) { rowManager.element.scrollTop = newPos; } else { rowManager.scrollToRow(rowManager.getDisplayRows()[0]); } } this.table.element.focus(); }, scrollPageDown: function (e) { var rowManager = this.table.rowManager, newPos = rowManager.scrollTop + rowManager.element.clientHeight, scrollMax = rowManager.element.scrollHeight; e.preventDefault(); if (rowManager.displayRowsCount) { if (newPos <= scrollMax) { rowManager.element.scrollTop = newPos; } else { rowManager.scrollToRow(rowManager.getDisplayRows()[rowManager.displayRowsCount - 1]); } } this.table.element.focus(); }, scrollToStart: function (e) { var rowManager = this.table.rowManager; e.preventDefault(); if (rowManager.displayRowsCount) { rowManager.scrollToRow(rowManager.getDisplayRows()[0]); } this.table.element.focus(); }, scrollToEnd: function (e) { var rowManager = this.table.rowManager; e.preventDefault(); if (rowManager.displayRowsCount) { rowManager.scrollToRow(rowManager.getDisplayRows()[rowManager.displayRowsCount - 1]); } this.table.element.focus(); }, navPrev: function (e) { this.dispatch("keybinding-nav-prev", e); }, navNext: function (e) { this.dispatch("keybinding-nav-next", e); }, navLeft: function (e) { this.dispatch("keybinding-nav-left", e); }, navRight: function (e) { this.dispatch("keybinding-nav-right", e); }, navUp: function (e) { this.dispatch("keybinding-nav-up", e); }, navDown: function (e) { this.dispatch("keybinding-nav-down", e); } }; const _Keybindings = class _Keybindings extends Module { constructor(table) { super(table); this.watchKeys = null; this.pressedKeys = null; this.keyupBinding = false; this.keydownBinding = false; this.registerTableOption("keybindings", {}); this.registerTableOption("tabEndNewRow", false); } initialize() { var bindings2 = this.table.options.keybindings, mergedBindings = {}; this.watchKeys = {}; this.pressedKeys = []; if (bindings2 !== false) { Object.assign(mergedBindings, _Keybindings.bindings); Object.assign(mergedBindings, bindings2); this.mapBindings(mergedBindings); this.bindEvents(); } this.subscribe("table-destroy", this.clearBindings.bind(this)); } mapBindings(bindings2) { for (let key in bindings2) { if (_Keybindings.actions[key]) { if (bindings2[key]) { if (typeof bindings2[key] !== "object") { bindings2[key] = [bindings2[key]]; } bindings2[key].forEach((binding) => { var bindingList = Array.isArray(binding) ? binding : [binding]; bindingList.forEach((item) => { this.mapBinding(key, item); }); }); } } else { console.warn("Key Binding Error - no such action:", key); } } } getKeyCode(e) { if (e.key.length === 1) { return e.key.toUpperCase().charCodeAt(0); } var specialKeys = { "Enter": 13, "Escape": 27, "Tab": 9, "Backspace": 8, "Delete": 46, "ArrowUp": 38, "ArrowDown": 40, "ArrowLeft": 37, "ArrowRight": 39, "Home": 36, "End": 35, "PageUp": 33, "PageDown": 34, "Insert": 45 }; return specialKeys[e.key] || e.keyCode || 0; } mapBinding(action, symbolsList) { var binding = { action: _Keybindings.actions[action], keys: [], ctrl: false, shift: false, meta: false }; var symbols = symbolsList.toString().toLowerCase().split(" ").join("").split("+"); symbols.forEach((symbol) => { switch (symbol) { case "ctrl": binding.ctrl = true; break; case "shift": binding.shift = true; break; case "meta": binding.meta = true; break; default: symbol = isNaN(symbol) ? symbol.toUpperCase().charCodeAt(0) : parseInt(symbol); binding.keys.push(symbol); if (!this.watchKeys[symbol]) { this.watchKeys[symbol] = []; } this.watchKeys[symbol].push(binding); } }); } bindEvents() { var self = this; this.keyupBinding = function (e) { var code = self.getKeyCode(e); var bindings2 = self.watchKeys[code]; if (bindings2) { self.pressedKeys.push(code); bindings2.forEach(function (binding) { self.checkBinding(e, binding); }); } }; this.keydownBinding = function (e) { var code = self.getKeyCode(e); var bindings2 = self.watchKeys[code]; if (bindings2) { var index = self.pressedKeys.indexOf(code); if (index > -1) { self.pressedKeys.splice(index, 1); } } }; this.table.element.addEventListener("keydown", this.keyupBinding); this.table.element.addEventListener("keyup", this.keydownBinding); } clearBindings() { if (this.keyupBinding) { this.table.element.removeEventListener("keydown", this.keyupBinding); } if (this.keydownBinding) { this.table.element.removeEventListener("keyup", this.keydownBinding); } } checkBinding(e, binding) { var match = true; if (e.ctrlKey == binding.ctrl && e.shiftKey == binding.shift && e.metaKey == binding.meta) { binding.keys.forEach((key) => { var index = this.pressedKeys.indexOf(key); if (index == -1) { match = false; } }); if (match) { binding.action.call(this, e); } return true; } return false; } }; __publicField(_Keybindings, "moduleName", "keybindings"); //load defaults __publicField(_Keybindings, "bindings", defaultBindings); __publicField(_Keybindings, "actions", defaultActions); let Keybindings = _Keybindings; class Menu extends Module { constructor(table) { super(table); this.menuContainer = null; this.nestedMenuBlock = false; this.currentComponent = null; this.rootPopup = null; this.columnSubscribers = {}; this.registerTableOption("rowContextMenu", false); this.registerTableOption("rowClickMenu", false); this.registerTableOption("rowDblClickMenu", false); this.registerTableOption("groupContextMenu", false); this.registerTableOption("groupClickMenu", false); this.registerTableOption("groupDblClickMenu", false); this.registerColumnOption("headerContextMenu"); this.registerColumnOption("headerClickMenu"); this.registerColumnOption("headerDblClickMenu"); this.registerColumnOption("headerMenu"); this.registerColumnOption("headerMenuIcon"); this.registerColumnOption("contextMenu"); this.registerColumnOption("clickMenu"); this.registerColumnOption("dblClickMenu"); } initialize() { this.deprecatedOptionsCheck(); this.initializeRowWatchers(); this.initializeGroupWatchers(); this.subscribe("column-init", this.initializeColumn.bind(this)); } deprecatedOptionsCheck() { } initializeRowWatchers() { if (this.table.options.rowContextMenu) { this.subscribe("row-contextmenu", this.loadMenuEvent.bind(this, this.table.options.rowContextMenu)); this.table.on("rowTapHold", this.loadMenuEvent.bind(this, this.table.options.rowContextMenu)); } if (this.table.options.rowClickMenu) { this.subscribe("row-click", this.loadMenuEvent.bind(this, this.table.options.rowClickMenu)); } if (this.table.options.rowDblClickMenu) { this.subscribe("row-dblclick", this.loadMenuEvent.bind(this, this.table.options.rowDblClickMenu)); } } initializeGroupWatchers() { if (this.table.options.groupContextMenu) { this.subscribe("group-contextmenu", this.loadMenuEvent.bind(this, this.table.options.groupContextMenu)); this.table.on("groupTapHold", this.loadMenuEvent.bind(this, this.table.options.groupContextMenu)); } if (this.table.options.groupClickMenu) { this.subscribe("group-click", this.loadMenuEvent.bind(this, this.table.options.groupClickMenu)); } if (this.table.options.groupDblClickMenu) { this.subscribe("group-dblclick", this.loadMenuEvent.bind(this, this.table.options.groupDblClickMenu)); } } initializeColumn(column) { var def = column.definition; if (def.headerContextMenu && !this.columnSubscribers.headerContextMenu) { this.columnSubscribers.headerContextMenu = this.loadMenuTableColumnEvent.bind(this, "headerContextMenu"); this.subscribe("column-contextmenu", this.columnSubscribers.headerContextMenu); this.table.on("headerTapHold", this.loadMenuTableColumnEvent.bind(this, "headerContextMenu")); } if (def.headerClickMenu && !this.columnSubscribers.headerClickMenu) { this.columnSubscribers.headerClickMenu = this.loadMenuTableColumnEvent.bind(this, "headerClickMenu"); this.subscribe("column-click", this.columnSubscribers.headerClickMenu); } if (def.headerDblClickMenu && !this.columnSubscribers.headerDblClickMenu) { this.columnSubscribers.headerDblClickMenu = this.loadMenuTableColumnEvent.bind(this, "headerDblClickMenu"); this.subscribe("column-dblclick", this.columnSubscribers.headerDblClickMenu); } if (def.headerMenu) { this.initializeColumnHeaderMenu(column); } if (def.contextMenu && !this.columnSubscribers.contextMenu) { this.columnSubscribers.contextMenu = this.loadMenuTableCellEvent.bind(this, "contextMenu"); this.subscribe("cell-contextmenu", this.columnSubscribers.contextMenu); this.table.on("cellTapHold", this.loadMenuTableCellEvent.bind(this, "contextMenu")); } if (def.clickMenu && !this.columnSubscribers.clickMenu) { this.columnSubscribers.clickMenu = this.loadMenuTableCellEvent.bind(this, "clickMenu"); this.subscribe("cell-click", this.columnSubscribers.clickMenu); } if (def.dblClickMenu && !this.columnSubscribers.dblClickMenu) { this.columnSubscribers.dblClickMenu = this.loadMenuTableCellEvent.bind(this, "dblClickMenu"); this.subscribe("cell-dblclick", this.columnSubscribers.dblClickMenu); } } initializeColumnHeaderMenu(column) { var icon = column.definition.headerMenuIcon, headerMenuEl; headerMenuEl = document.createElement("span"); headerMenuEl.classList.add("tabulator-header-popup-button"); if (icon) { if (typeof icon === "function") { icon = icon(column.getComponent()); } if (icon instanceof HTMLElement) { headerMenuEl.appendChild(icon); } else { headerMenuEl.innerHTML = icon; } } else { headerMenuEl.innerHTML = "⋮"; } headerMenuEl.addEventListener("click", (e) => { e.stopPropagation(); e.preventDefault(); this.loadMenuEvent(column.definition.headerMenu, e, column); }); column.titleElement.insertBefore(headerMenuEl, column.titleElement.firstChild); } loadMenuTableCellEvent(option, e, cell) { if (cell._cell) { cell = cell._cell; } if (cell.column.definition[option]) { this.loadMenuEvent(cell.column.definition[option], e, cell); } } loadMenuTableColumnEvent(option, e, column) { if (column._column) { column = column._column; } if (column.definition[option]) { this.loadMenuEvent(column.definition[option], e, column); } } loadMenuEvent(menu, e, component) { if (component._group) { component = component._group; } else if (component._row) { component = component._row; } menu = typeof menu == "function" ? menu.call(this.table, e, component.getComponent()) : menu; this.loadMenu(e, component, menu); } loadMenu(e, component, menu, parentEl, parentPopup) { var touch = !(e instanceof MouseEvent), menuEl = document.createElement("div"), popup; menuEl.classList.add("tabulator-menu"); if (!touch) { e.preventDefault(); } if (!menu || !menu.length) { return; } if (!parentEl) { if (this.nestedMenuBlock) { if (this.rootPopup) { return; } } else { this.nestedMenuBlock = setTimeout(() => { this.nestedMenuBlock = false; }, 100); } if (this.rootPopup) { this.rootPopup.hide(); } this.rootPopup = popup = this.popup(menuEl); } else { popup = parentPopup.child(menuEl); } menu.forEach((item) => { var itemEl = document.createElement("div"), label = item.label, disabled = item.disabled; if (item.separator) { itemEl.classList.add("tabulator-menu-separator"); } else { itemEl.classList.add("tabulator-menu-item"); if (typeof label == "function") { label = label.call(this.table, component.getComponent()); } if (label instanceof Node) { itemEl.appendChild(label); } else { itemEl.innerHTML = label; } if (typeof disabled == "function") { disabled = disabled.call(this.table, component.getComponent()); } if (disabled) { itemEl.classList.add("tabulator-menu-item-disabled"); itemEl.addEventListener("click", (e2) => { e2.stopPropagation(); }); } else { if (item.menu && item.menu.length) { itemEl.addEventListener("click", (e2) => { e2.stopPropagation(); this.loadMenu(e2, component, item.menu, itemEl, popup); }); } else { if (item.action) { itemEl.addEventListener("click", (e2) => { item.action(e2, component.getComponent()); }); } } } if (item.menu && item.menu.length) { itemEl.classList.add("tabulator-menu-item-submenu"); } } menuEl.appendChild(itemEl); }); menuEl.addEventListener("click", (e2) => { if (this.rootPopup) { this.rootPopup.hide(); } }); popup.show(parentEl || e); if (popup === this.rootPopup) { this.rootPopup.hideOnBlur(() => { this.rootPopup = null; if (this.currentComponent) { this.dispatch("menu-closed", menu, popup); this.dispatchExternal("menuClosed", this.currentComponent.getComponent()); this.currentComponent = null; } }); this.currentComponent = component; this.dispatch("menu-opened", menu, popup); this.dispatchExternal("menuOpened", component.getComponent()); } } } __publicField(Menu, "moduleName", "menu"); class MoveColumns extends Module { constructor(table) { super(table); this.placeholderElement = this.createPlaceholderElement(); this.hoverElement = false; this.checkTimeout = false; this.checkPeriod = 250; this.moving = false; this.toCol = false; this.toColAfter = false; this.startX = 0; this.autoScrollMargin = 40; this.autoScrollStep = 5; this.autoScrollTimeout = false; this.touchMove = false; this.moveHover = this.moveHover.bind(this); this.endMove = this.endMove.bind(this); this.registerTableOption("movableColumns", false); } createPlaceholderElement() { var el = document.createElement("div"); el.classList.add("tabulator-col"); el.classList.add("tabulator-col-placeholder"); return el; } initialize() { if (this.table.options.movableColumns) { this.subscribe("column-init", this.initializeColumn.bind(this)); this.subscribe("alert-show", this.abortMove.bind(this)); } } abortMove() { clearTimeout(this.checkTimeout); } initializeColumn(column) { var self = this, config = {}, colEl; if (!column.modules.frozen && !column.isGroup && !column.isRowHeader) { colEl = column.getElement(); config.mousemove = (function (e) { if (column.parent === self.moving.parent) { if ((self.touchMove ? e.touches[0].pageX : e.pageX) - Helpers.elOffset(colEl).left + self.table.columnManager.contentsElement.scrollLeft > column.getWidth() / 2) { if (self.toCol !== column || !self.toColAfter) { colEl.parentNode.insertBefore(self.placeholderElement, colEl.nextSibling); self.moveColumn(column, true); } } else { if (self.toCol !== column || self.toColAfter) { colEl.parentNode.insertBefore(self.placeholderElement, colEl); self.moveColumn(column, false); } } } }).bind(self); colEl.addEventListener("mousedown", function (e) { self.touchMove = false; if (e.which === 1) { self.checkTimeout = setTimeout(function () { self.startMove(e, column); }, self.checkPeriod); } }); colEl.addEventListener("mouseup", function (e) { if (e.which === 1) { if (self.checkTimeout) { clearTimeout(self.checkTimeout); } } }); self.bindTouchEvents(column); } column.modules.moveColumn = config; } bindTouchEvents(column) { var colEl = column.getElement(), startXMove = false, nextCol, prevCol, nextColWidth, prevColWidth, nextColWidthLast, prevColWidthLast; colEl.addEventListener("touchstart", (e) => { this.checkTimeout = setTimeout(() => { this.touchMove = true; nextCol = column.nextColumn(); nextColWidth = nextCol ? nextCol.getWidth() / 2 : 0; prevCol = column.prevColumn(); prevColWidth = prevCol ? prevCol.getWidth() / 2 : 0; nextColWidthLast = 0; prevColWidthLast = 0; startXMove = false; this.startMove(e, column); }, this.checkPeriod); }, {passive: true}); colEl.addEventListener("touchmove", (e) => { var diff, moveToCol; if (this.moving) { this.moveHover(e); if (!startXMove) { startXMove = e.touches[0].pageX; } diff = e.touches[0].pageX - startXMove; if (diff > 0) { if (nextCol && diff - nextColWidthLast > nextColWidth) { moveToCol = nextCol; if (moveToCol !== column) { startXMove = e.touches[0].pageX; moveToCol.getElement().parentNode.insertBefore(this.placeholderElement, moveToCol.getElement().nextSibling); this.moveColumn(moveToCol, true); } } } else { if (prevCol && -diff - prevColWidthLast > prevColWidth) { moveToCol = prevCol; if (moveToCol !== column) { startXMove = e.touches[0].pageX; moveToCol.getElement().parentNode.insertBefore(this.placeholderElement, moveToCol.getElement()); this.moveColumn(moveToCol, false); } } } if (moveToCol) { nextCol = moveToCol.nextColumn(); nextColWidthLast = nextColWidth; nextColWidth = nextCol ? nextCol.getWidth() / 2 : 0; prevCol = moveToCol.prevColumn(); prevColWidthLast = prevColWidth; prevColWidth = prevCol ? prevCol.getWidth() / 2 : 0; } } }, {passive: true}); colEl.addEventListener("touchend", (e) => { if (this.checkTimeout) { clearTimeout(this.checkTimeout); } if (this.moving) { this.endMove(e); } }); } startMove(e, column) { var element = column.getElement(), headerElement = this.table.columnManager.getContentsElement(), headersElement = this.table.columnManager.getHeadersElement(); if (this.table.modules.selectRange && this.table.modules.selectRange.columnSelection) { if (this.table.modules.selectRange.mousedown && this.table.modules.selectRange.selecting === "column") { return; } } this.moving = column; this.startX = (this.touchMove ? e.touches[0].pageX : e.pageX) - Helpers.elOffset(element).left; this.table.element.classList.add("tabulator-block-select"); this.placeholderElement.style.width = column.getWidth() + "px"; this.placeholderElement.style.height = column.getHeight() + "px"; element.parentNode.insertBefore(this.placeholderElement, element); element.parentNode.removeChild(element); this.hoverElement = element.cloneNode(true); this.hoverElement.classList.add("tabulator-moving"); headerElement.appendChild(this.hoverElement); this.hoverElement.style.left = "0"; this.hoverElement.style.bottom = headerElement.clientHeight - headersElement.offsetHeight + "px"; if (!this.touchMove) { this._bindMouseMove(); document.body.addEventListener("mousemove", this.moveHover); document.body.addEventListener("mouseup", this.endMove); } this.moveHover(e); this.dispatch("column-moving", e, this.moving); } _bindMouseMove() { this.table.columnManager.columnsByIndex.forEach(function (column) { if (column.modules.moveColumn.mousemove) { column.getElement().addEventListener("mousemove", column.modules.moveColumn.mousemove); } }); } _unbindMouseMove() { this.table.columnManager.columnsByIndex.forEach(function (column) { if (column.modules.moveColumn.mousemove) { column.getElement().removeEventListener("mousemove", column.modules.moveColumn.mousemove); } }); } moveColumn(column, after) { var movingCells = this.moving.getCells(); this.toCol = column; this.toColAfter = after; if (after) { column.getCells().forEach(function (cell, i) { var cellEl = cell.getElement(true); if (cellEl.parentNode && movingCells[i]) { cellEl.parentNode.insertBefore(movingCells[i].getElement(), cellEl.nextSibling); } }); } else { column.getCells().forEach(function (cell, i) { var cellEl = cell.getElement(true); if (cellEl.parentNode && movingCells[i]) { cellEl.parentNode.insertBefore(movingCells[i].getElement(), cellEl); } }); } } endMove(e) { if (e.which === 1 || this.touchMove) { this._unbindMouseMove(); this.placeholderElement.parentNode.insertBefore(this.moving.getElement(), this.placeholderElement.nextSibling); this.placeholderElement.parentNode.removeChild(this.placeholderElement); this.hoverElement.parentNode.removeChild(this.hoverElement); this.table.element.classList.remove("tabulator-block-select"); if (this.toCol) { this.table.columnManager.moveColumnActual(this.moving, this.toCol, this.toColAfter); } this.moving = false; this.toCol = false; this.toColAfter = false; if (!this.touchMove) { document.body.removeEventListener("mousemove", this.moveHover); document.body.removeEventListener("mouseup", this.endMove); } } } moveHover(e) { var columnHolder = this.table.columnManager.getContentsElement(), scrollLeft = columnHolder.scrollLeft, xPos = (this.touchMove ? e.touches[0].pageX : e.pageX) - Helpers.elOffset(columnHolder).left + scrollLeft, scrollPos; this.hoverElement.style.left = xPos - this.startX + "px"; if (xPos - scrollLeft < this.autoScrollMargin) { if (!this.autoScrollTimeout) { this.autoScrollTimeout = setTimeout(() => { scrollPos = Math.max(0, scrollLeft - 5); this.table.rowManager.getElement().scrollLeft = scrollPos; this.autoScrollTimeout = false; }, 1); } } if (scrollLeft + columnHolder.clientWidth - xPos < this.autoScrollMargin) { if (!this.autoScrollTimeout) { this.autoScrollTimeout = setTimeout(() => { scrollPos = Math.min(columnHolder.clientWidth, scrollLeft + 5); this.table.rowManager.getElement().scrollLeft = scrollPos; this.autoScrollTimeout = false; }, 1); } } } } __publicField(MoveColumns, "moduleName", "moveColumn"); var defaultSenders = { delete: function (fromRow, toRow, toTable) { fromRow.delete(); } }; var defaultReceivers = { insert: function (fromRow, toRow, fromTable) { this.table.addRow(fromRow.getData(), void 0, toRow); return true; }, add: function (fromRow, toRow, fromTable) { this.table.addRow(fromRow.getData()); return true; }, update: function (fromRow, toRow, fromTable) { if (toRow) { toRow.update(fromRow.getData()); return true; } return false; }, replace: function (fromRow, toRow, fromTable) { if (toRow) { this.table.addRow(fromRow.getData(), void 0, toRow); toRow.delete(); return true; } return false; } }; const _MoveRows = class _MoveRows extends Module { constructor(table) { super(table); this.placeholderElement = this.createPlaceholderElement(); this.hoverElement = false; this.checkTimeout = false; this.checkPeriod = 150; this.moving = false; this.toRow = false; this.toRowAfter = false; this.hasHandle = false; this.startY = 0; this.startX = 0; this.moveHover = this.moveHover.bind(this); this.endMove = this.endMove.bind(this); this.tableRowDropEvent = false; this.touchMove = false; this.connection = false; this.connectionSelectorsTables = false; this.connectionSelectorsElements = false; this.connectionElements = []; this.connections = []; this.connectedTable = false; this.connectedRow = false; this.registerTableOption("movableRows", false); this.registerTableOption("movableRowsConnectedTables", false); this.registerTableOption("movableRowsConnectedElements", false); this.registerTableOption("movableRowsSender", false); this.registerTableOption("movableRowsReceiver", "insert"); this.registerColumnOption("rowHandle"); } createPlaceholderElement() { var el = document.createElement("div"); el.classList.add("tabulator-row"); el.classList.add("tabulator-row-placeholder"); return el; } initialize() { if (this.table.options.movableRows) { this.connectionSelectorsTables = this.table.options.movableRowsConnectedTables; this.connectionSelectorsElements = this.table.options.movableRowsConnectedElements; this.connection = this.connectionSelectorsTables || this.connectionSelectorsElements; this.subscribe("cell-init", this.initializeCell.bind(this)); this.subscribe("column-init", this.initializeColumn.bind(this)); this.subscribe("row-init", this.initializeRow.bind(this)); } } initializeGroupHeader(group) { var self = this, config = {}; config.mouseup = (function (e) { self.tableRowDrop(e, group); }).bind(self); config.mousemove = (function (e) { var rowEl; if (e.pageY - Helpers.elOffset(group.element).top + self.table.rowManager.element.scrollTop > group.getHeight() / 2) { if (self.toRow !== group || !self.toRowAfter) { rowEl = group.getElement(); rowEl.parentNode.insertBefore(self.placeholderElement, rowEl.nextSibling); self.moveRow(group, true); } } else { if (self.toRow !== group || self.toRowAfter) { rowEl = group.getElement(); if (rowEl.previousSibling) { rowEl.parentNode.insertBefore(self.placeholderElement, rowEl); self.moveRow(group, false); } } } }).bind(self); group.modules.moveRow = config; } initializeRow(row) { var self = this, config = {}, rowEl; config.mouseup = (function (e) { self.tableRowDrop(e, row); }).bind(self); config.mousemove = (function (e) { var rowEl2 = row.getElement(); if (e.pageY - Helpers.elOffset(rowEl2).top + self.table.rowManager.element.scrollTop > row.getHeight() / 2) { if (self.toRow !== row || !self.toRowAfter) { rowEl2.parentNode.insertBefore(self.placeholderElement, rowEl2.nextSibling); self.moveRow(row, true); } } else { if (self.toRow !== row || self.toRowAfter) { rowEl2.parentNode.insertBefore(self.placeholderElement, rowEl2); self.moveRow(row, false); } } }).bind(self); if (!this.hasHandle) { rowEl = row.getElement(); rowEl.addEventListener("mousedown", function (e) { if (e.which === 1) { self.checkTimeout = setTimeout(function () { self.startMove(e, row); }, self.checkPeriod); } }); rowEl.addEventListener("mouseup", function (e) { if (e.which === 1) { if (self.checkTimeout) { clearTimeout(self.checkTimeout); } } }); this.bindTouchEvents(row, row.getElement()); } row.modules.moveRow = config; } initializeColumn(column) { if (column.definition.rowHandle && this.table.options.movableRows !== false) { this.hasHandle = true; } } initializeCell(cell) { if (cell.column.definition.rowHandle && this.table.options.movableRows !== false) { var self = this, cellEl = cell.getElement(true); cellEl.addEventListener("mousedown", function (e) { if (e.which === 1) { self.checkTimeout = setTimeout(function () { self.startMove(e, cell.row); }, self.checkPeriod); } }); cellEl.addEventListener("mouseup", function (e) { if (e.which === 1) { if (self.checkTimeout) { clearTimeout(self.checkTimeout); } } }); this.bindTouchEvents(cell.row, cellEl); } } bindTouchEvents(row, element) { var startYMove = false, nextRow, prevRow, nextRowHeight, prevRowHeight, nextRowHeightLast, prevRowHeightLast; element.addEventListener("touchstart", (e) => { this.checkTimeout = setTimeout(() => { this.touchMove = true; nextRow = row.nextRow(); nextRowHeight = nextRow ? nextRow.getHeight() / 2 : 0; prevRow = row.prevRow(); prevRowHeight = prevRow ? prevRow.getHeight() / 2 : 0; nextRowHeightLast = 0; prevRowHeightLast = 0; startYMove = false; this.startMove(e, row); }, this.checkPeriod); }, {passive: true}); this.moving, this.toRow, this.toRowAfter; element.addEventListener("touchmove", (e) => { var diff, moveToRow; if (this.moving) { e.preventDefault(); this.moveHover(e); if (!startYMove) { startYMove = e.touches[0].pageY; } diff = e.touches[0].pageY - startYMove; if (diff > 0) { if (nextRow && diff - nextRowHeightLast > nextRowHeight) { moveToRow = nextRow; if (moveToRow !== row) { startYMove = e.touches[0].pageY; moveToRow.getElement().parentNode.insertBefore(this.placeholderElement, moveToRow.getElement().nextSibling); this.moveRow(moveToRow, true); } } } else { if (prevRow && -diff - prevRowHeightLast > prevRowHeight) { moveToRow = prevRow; if (moveToRow !== row) { startYMove = e.touches[0].pageY; moveToRow.getElement().parentNode.insertBefore(this.placeholderElement, moveToRow.getElement()); this.moveRow(moveToRow, false); } } } if (moveToRow) { nextRow = moveToRow.nextRow(); nextRowHeightLast = nextRowHeight; nextRowHeight = nextRow ? nextRow.getHeight() / 2 : 0; prevRow = moveToRow.prevRow(); prevRowHeightLast = prevRowHeight; prevRowHeight = prevRow ? prevRow.getHeight() / 2 : 0; } } }); element.addEventListener("touchend", (e) => { if (this.checkTimeout) { clearTimeout(this.checkTimeout); } if (this.moving) { this.endMove(e); this.touchMove = false; } }); } _bindMouseMove() { this.table.rowManager.getDisplayRows().forEach((row) => { if ((row.type === "row" || row.type === "group") && row.modules.moveRow && row.modules.moveRow.mousemove) { row.getElement().addEventListener("mousemove", row.modules.moveRow.mousemove); } }); } _unbindMouseMove() { this.table.rowManager.getDisplayRows().forEach((row) => { if ((row.type === "row" || row.type === "group") && row.modules.moveRow && row.modules.moveRow.mousemove) { row.getElement().removeEventListener("mousemove", row.modules.moveRow.mousemove); } }); } startMove(e, row) { var element = row.getElement(); this.setStartPosition(e, row); this.moving = row; this.table.element.classList.add("tabulator-block-select"); this.placeholderElement.style.width = row.getWidth() + "px"; this.placeholderElement.style.height = row.getHeight() + "px"; if (!this.connection) { element.parentNode.insertBefore(this.placeholderElement, element); element.parentNode.removeChild(element); } else { this.table.element.classList.add("tabulator-movingrow-sending"); this.connectToTables(row); } this.hoverElement = element.cloneNode(true); this.hoverElement.classList.add("tabulator-moving"); if (this.connection) { document.body.appendChild(this.hoverElement); this.hoverElement.style.left = "0"; this.hoverElement.style.top = "0"; this.hoverElement.style.width = this.table.element.clientWidth + "px"; this.hoverElement.style.whiteSpace = "nowrap"; this.hoverElement.style.overflow = "hidden"; this.hoverElement.style.pointerEvents = "none"; } else { this.table.rowManager.getTableElement().appendChild(this.hoverElement); this.hoverElement.style.left = "0"; this.hoverElement.style.top = "0"; this._bindMouseMove(); } document.body.addEventListener("mousemove", this.moveHover); document.body.addEventListener("mouseup", this.endMove); this.dispatchExternal("rowMoving", row.getComponent()); this.moveHover(e); } setStartPosition(e, row) { var pageX = this.touchMove ? e.touches[0].pageX : e.pageX, pageY = this.touchMove ? e.touches[0].pageY : e.pageY, element, position; element = row.getElement(); if (this.connection) { position = element.getBoundingClientRect(); this.startX = position.left - pageX + window.pageXOffset; this.startY = position.top - pageY + window.pageYOffset; } else { this.startY = pageY - element.getBoundingClientRect().top; } } endMove(e) { if (!e || e.which === 1 || this.touchMove) { this._unbindMouseMove(); if (!this.connection) { this.placeholderElement.parentNode.insertBefore(this.moving.getElement(), this.placeholderElement.nextSibling); this.placeholderElement.parentNode.removeChild(this.placeholderElement); } this.hoverElement.parentNode.removeChild(this.hoverElement); this.table.element.classList.remove("tabulator-block-select"); if (this.toRow) { this.table.rowManager.moveRow(this.moving, this.toRow, this.toRowAfter); } else { this.dispatchExternal("rowMoveCancelled", this.moving.getComponent()); } this.moving = false; this.toRow = false; this.toRowAfter = false; document.body.removeEventListener("mousemove", this.moveHover); document.body.removeEventListener("mouseup", this.endMove); if (this.connection) { this.table.element.classList.remove("tabulator-movingrow-sending"); this.disconnectFromTables(); } } } moveRow(row, after) { this.toRow = row; this.toRowAfter = after; } moveHover(e) { if (this.connection) { this.moveHoverConnections.call(this, e); } else { this.moveHoverTable.call(this, e); } } moveHoverTable(e) { var rowHolder = this.table.rowManager.getElement(), scrollTop = rowHolder.scrollTop, yPos = (this.touchMove ? e.touches[0].pageY : e.pageY) - rowHolder.getBoundingClientRect().top + scrollTop; this.hoverElement.style.top = Math.min(yPos - this.startY, this.table.rowManager.element.scrollHeight - this.hoverElement.offsetHeight) + "px"; } moveHoverConnections(e) { this.hoverElement.style.left = this.startX + (this.touchMove ? e.touches[0].pageX : e.pageX) + "px"; this.hoverElement.style.top = this.startY + (this.touchMove ? e.touches[0].pageY : e.pageY) + "px"; } elementRowDrop(e, element, row) { this.dispatchExternal("movableRowsElementDrop", e, element, row ? row.getComponent() : false); } //establish connection with other tables connectToTables(row) { var connectionTables; if (this.connectionSelectorsTables) { connectionTables = this.commsConnections(this.connectionSelectorsTables); this.dispatchExternal("movableRowsSendingStart", connectionTables); this.commsSend(this.connectionSelectorsTables, "moveRow", "connect", { row }); } if (this.connectionSelectorsElements) { this.connectionElements = []; if (!Array.isArray(this.connectionSelectorsElements)) { this.connectionSelectorsElements = [this.connectionSelectorsElements]; } this.connectionSelectorsElements.forEach((query) => { if (typeof query === "string") { this.connectionElements = this.connectionElements.concat(Array.prototype.slice.call(document.querySelectorAll(query))); } else { this.connectionElements.push(query); } }); this.connectionElements.forEach((element) => { var dropEvent = (e) => { this.elementRowDrop(e, element, this.moving); }; element.addEventListener("mouseup", dropEvent); element.tabulatorElementDropEvent = dropEvent; element.classList.add("tabulator-movingrow-receiving"); }); } } //disconnect from other tables disconnectFromTables() { var connectionTables; if (this.connectionSelectorsTables) { connectionTables = this.commsConnections(this.connectionSelectorsTables); this.dispatchExternal("movableRowsSendingStop", connectionTables); this.commsSend(this.connectionSelectorsTables, "moveRow", "disconnect"); } this.connectionElements.forEach((element) => { element.classList.remove("tabulator-movingrow-receiving"); element.removeEventListener("mouseup", element.tabulatorElementDropEvent); delete element.tabulatorElementDropEvent; }); } //accept incomming connection connect(table, row) { if (!this.connectedTable) { this.connectedTable = table; this.connectedRow = row; this.table.element.classList.add("tabulator-movingrow-receiving"); this.table.rowManager.getDisplayRows().forEach((row2) => { if (row2.type === "row" && row2.modules.moveRow && row2.modules.moveRow.mouseup) { row2.getElement().addEventListener("mouseup", row2.modules.moveRow.mouseup); } }); this.tableRowDropEvent = this.tableRowDrop.bind(this); this.table.element.addEventListener("mouseup", this.tableRowDropEvent); this.dispatchExternal("movableRowsReceivingStart", row, table); return true; } else { console.warn("Move Row Error - Table cannot accept connection, already connected to table:", this.connectedTable); return false; } } //close incoming connection disconnect(table) { if (table === this.connectedTable) { this.connectedTable = false; this.connectedRow = false; this.table.element.classList.remove("tabulator-movingrow-receiving"); this.table.rowManager.getDisplayRows().forEach((row) => { if (row.type === "row" && row.modules.moveRow && row.modules.moveRow.mouseup) { row.getElement().removeEventListener("mouseup", row.modules.moveRow.mouseup); } }); this.table.element.removeEventListener("mouseup", this.tableRowDropEvent); this.dispatchExternal("movableRowsReceivingStop", table); } else { console.warn("Move Row Error - trying to disconnect from non connected table"); } } dropComplete(table, row, success) { var sender = false; if (success) { switch (typeof this.table.options.movableRowsSender) { case "string": sender = _MoveRows.senders[this.table.options.movableRowsSender]; break; case "function": sender = this.table.options.movableRowsSender; break; } if (sender) { sender.call(this, this.moving ? this.moving.getComponent() : void 0, row ? row.getComponent() : void 0, table); } else { if (this.table.options.movableRowsSender) { console.warn("Mover Row Error - no matching sender found:", this.table.options.movableRowsSender); } } this.dispatchExternal("movableRowsSent", this.moving.getComponent(), row ? row.getComponent() : void 0, table); } else { this.dispatchExternal("movableRowsSentFailed", this.moving.getComponent(), row ? row.getComponent() : void 0, table); } this.endMove(); } tableRowDrop(e, row) { var receiver = false, success = false; e.stopImmediatePropagation(); switch (typeof this.table.options.movableRowsReceiver) { case "string": receiver = _MoveRows.receivers[this.table.options.movableRowsReceiver]; break; case "function": receiver = this.table.options.movableRowsReceiver; break; } if (receiver) { success = receiver.call(this, this.connectedRow.getComponent(), row ? row.getComponent() : void 0, this.connectedTable); } else { console.warn("Mover Row Error - no matching receiver found:", this.table.options.movableRowsReceiver); } if (success) { this.dispatchExternal("movableRowsReceived", this.connectedRow.getComponent(), row ? row.getComponent() : void 0, this.connectedTable); } else { this.dispatchExternal("movableRowsReceivedFailed", this.connectedRow.getComponent(), row ? row.getComponent() : void 0, this.connectedTable); } this.commsSend(this.connectedTable, "moveRow", "dropcomplete", { row, success }); } commsReceived(table, action, data) { switch (action) { case "connect": return this.connect(table, data.row); case "disconnect": return this.disconnect(table); case "dropcomplete": return this.dropComplete(table, data.row, data.success); } } }; __publicField(_MoveRows, "moduleName", "moveRow"); //load defaults __publicField(_MoveRows, "senders", defaultSenders); __publicField(_MoveRows, "receivers", defaultReceivers); let MoveRows = _MoveRows; var defaultMutators = {}; const _Mutator = class _Mutator extends Module { constructor(table) { super(table); this.allowedTypes = ["", "data", "edit", "clipboard", "import"]; this.enabled = true; this.registerColumnOption("mutator"); this.registerColumnOption("mutatorParams"); this.registerColumnOption("mutatorData"); this.registerColumnOption("mutatorDataParams"); this.registerColumnOption("mutatorEdit"); this.registerColumnOption("mutatorEditParams"); this.registerColumnOption("mutatorClipboard"); this.registerColumnOption("mutatorClipboardParams"); this.registerColumnOption("mutatorImport"); this.registerColumnOption("mutatorImportParams"); this.registerColumnOption("mutateLink"); } initialize() { this.subscribe("cell-value-changing", this.transformCell.bind(this)); this.subscribe("cell-value-changed", this.mutateLink.bind(this)); this.subscribe("column-layout", this.initializeColumn.bind(this)); this.subscribe("row-data-init-before", this.rowDataChanged.bind(this)); this.subscribe("row-data-changing", this.rowDataChanged.bind(this)); } rowDataChanged(row, tempData, updatedData) { return this.transformRow(tempData, "data", updatedData); } //initialize column mutator initializeColumn(column) { var match = false, config = {}; this.allowedTypes.forEach((type) => { var key = "mutator" + (type.charAt(0).toUpperCase() + type.slice(1)), mutator; if (column.definition[key]) { mutator = this.lookupMutator(column.definition[key]); if (mutator) { match = true; config[key] = { mutator, params: column.definition[key + "Params"] || {} }; } } }); if (match) { column.modules.mutate = config; } } lookupMutator(value) { var mutator = false; switch (typeof value) { case "string": if (_Mutator.mutators[value]) { mutator = _Mutator.mutators[value]; } else { console.warn("Mutator Error - No such mutator found, ignoring: ", value); } break; case "function": mutator = value; break; } return mutator; } //apply mutator to row transformRow(data, type, updatedData) { var key = "mutator" + (type.charAt(0).toUpperCase() + type.slice(1)), value; if (this.enabled) { this.table.columnManager.traverse((column) => { var mutator, params, component; if (column.modules.mutate) { mutator = column.modules.mutate[key] || column.modules.mutate.mutator || false; if (mutator) { value = column.getFieldValue(typeof updatedData !== "undefined" ? updatedData : data); if (type == "data" && !updatedData || typeof value !== "undefined") { component = column.getComponent(); params = typeof mutator.params === "function" ? mutator.params(value, data, type, component) : mutator.params; column.setFieldValue(data, mutator.mutator(value, data, type, params, component)); } } } }); } return data; } //apply mutator to new cell value transformCell(cell, value) { if (cell.column.modules.mutate) { var mutator = cell.column.modules.mutate.mutatorEdit || cell.column.modules.mutate.mutator || false, tempData = {}; if (mutator) { tempData = Object.assign(tempData, cell.row.getData()); cell.column.setFieldValue(tempData, value); return mutator.mutator(value, tempData, "edit", mutator.params, cell.getComponent()); } } return value; } mutateLink(cell) { var links = cell.column.definition.mutateLink; if (links) { if (!Array.isArray(links)) { links = [links]; } links.forEach((link2) => { var linkCell = cell.row.getCell(link2); if (linkCell) { linkCell.setValue(linkCell.getValue(), true, true); } }); } } enable() { this.enabled = true; } disable() { this.enabled = false; } }; __publicField(_Mutator, "moduleName", "mutator"); //load defaults __publicField(_Mutator, "mutators", defaultMutators); let Mutator = _Mutator; function rows(pageSize, currentRow, currentPage, totalRows, totalPages) { var el = document.createElement("span"), showingEl = document.createElement("span"), valueEl = document.createElement("span"), ofEl = document.createElement("span"), totalEl = document.createElement("span"), rowsEl = document.createElement("span"); this.table.modules.localize.langBind("pagination|counter|showing", (value) => { showingEl.innerHTML = value; }); this.table.modules.localize.langBind("pagination|counter|of", (value) => { ofEl.innerHTML = value; }); this.table.modules.localize.langBind("pagination|counter|rows", (value) => { rowsEl.innerHTML = value; }); if (totalRows) { valueEl.innerHTML = " " + currentRow + "-" + Math.min(currentRow + pageSize - 1, totalRows) + " "; totalEl.innerHTML = " " + totalRows + " "; el.appendChild(showingEl); el.appendChild(valueEl); el.appendChild(ofEl); el.appendChild(totalEl); el.appendChild(rowsEl); } else { valueEl.innerHTML = " 0 "; el.appendChild(showingEl); el.appendChild(valueEl); el.appendChild(rowsEl); } return el; } function pages(pageSize, currentRow, currentPage, totalRows, totalPages) { var el = document.createElement("span"), showingEl = document.createElement("span"), valueEl = document.createElement("span"), ofEl = document.createElement("span"), totalEl = document.createElement("span"), rowsEl = document.createElement("span"); this.table.modules.localize.langBind("pagination|counter|showing", (value) => { showingEl.innerHTML = value; }); valueEl.innerHTML = " " + currentPage + " "; this.table.modules.localize.langBind("pagination|counter|of", (value) => { ofEl.innerHTML = value; }); totalEl.innerHTML = " " + totalPages + " "; this.table.modules.localize.langBind("pagination|counter|pages", (value) => { rowsEl.innerHTML = value; }); el.appendChild(showingEl); el.appendChild(valueEl); el.appendChild(ofEl); el.appendChild(totalEl); el.appendChild(rowsEl); return el; } var defaultPageCounters = { rows, pages }; const _Page = class _Page extends Module { constructor(table) { super(table); this.mode = "local"; this.progressiveLoad = false; this.element = null; this.pageCounterElement = null; this.pageCounter = null; this.size = 0; this.page = 1; this.count = 5; this.max = 1; this.remoteRowCountEstimate = null; this.initialLoad = true; this.dataChanging = false; this.pageSizes = []; this.registerTableOption("pagination", false); this.registerTableOption("paginationMode", "local"); this.registerTableOption("paginationSize", false); this.registerTableOption("paginationInitialPage", 1); this.registerTableOption("paginationCounter", false); this.registerTableOption("paginationCounterElement", false); this.registerTableOption("paginationButtonCount", 5); this.registerTableOption("paginationSizeSelector", false); this.registerTableOption("paginationElement", false); this.registerTableOption("paginationAddRow", "page"); this.registerTableOption("paginationOutOfRange", false); this.registerTableOption("progressiveLoad", false); this.registerTableOption("progressiveLoadDelay", 0); this.registerTableOption("progressiveLoadScrollMargin", 0); this.registerTableFunction("setMaxPage", this.setMaxPage.bind(this)); this.registerTableFunction("setPage", this.setPage.bind(this)); this.registerTableFunction("setPageToRow", this.userSetPageToRow.bind(this)); this.registerTableFunction("setPageSize", this.userSetPageSize.bind(this)); this.registerTableFunction("getPageSize", this.getPageSize.bind(this)); this.registerTableFunction("previousPage", this.previousPage.bind(this)); this.registerTableFunction("nextPage", this.nextPage.bind(this)); this.registerTableFunction("getPage", this.getPage.bind(this)); this.registerTableFunction("getPageMax", this.getPageMax.bind(this)); this.registerComponentFunction("row", "pageTo", this.setPageToRow.bind(this)); } initialize() { if (this.table.options.pagination) { this.subscribe("row-deleted", this.rowsUpdated.bind(this)); this.subscribe("row-added", this.rowsUpdated.bind(this)); this.subscribe("data-processed", this.initialLoadComplete.bind(this)); this.subscribe("table-built", this.calculatePageSizes.bind(this)); this.subscribe("footer-redraw", this.footerRedraw.bind(this)); if (this.table.options.paginationAddRow == "page") { this.subscribe("row-adding-position", this.rowAddingPosition.bind(this)); } if (this.table.options.paginationMode === "remote") { this.subscribe("data-params", this.remotePageParams.bind(this)); this.subscribe("data-loaded", this._parseRemoteData.bind(this)); } if (this.table.options.progressiveLoad) { console.error("Progressive Load Error - Pagination and progressive load cannot be used at the same time"); } this.registerDisplayHandler(this.restOnRenderBefore.bind(this), 40); this.registerDisplayHandler(this.getRows.bind(this), 50); this.createElements(); this.initializePageCounter(); this.initializePaginator(); } else if (this.table.options.progressiveLoad) { this.subscribe("data-params", this.remotePageParams.bind(this)); this.subscribe("data-loaded", this._parseRemoteData.bind(this)); this.subscribe("table-built", this.calculatePageSizes.bind(this)); this.subscribe("data-processed", this.initialLoadComplete.bind(this)); this.initializeProgressive(this.table.options.progressiveLoad); if (this.table.options.progressiveLoad === "scroll") { this.subscribe("scroll-vertical", this.scrollVertical.bind(this)); } } } rowAddingPosition(row, top) { var rowManager = this.table.rowManager, displayRows = rowManager.getDisplayRows(), index; if (top) { if (displayRows.length) { index = displayRows[0]; } else { if (rowManager.activeRows.length) { index = rowManager.activeRows[rowManager.activeRows.length - 1]; top = false; } } } else { if (displayRows.length) { index = displayRows[displayRows.length - 1]; top = displayRows.length < this.size ? false : true; } } return {index, top}; } calculatePageSizes() { var testElRow, testElCell; if (this.table.options.paginationSize) { this.size = this.table.options.paginationSize; } else { testElRow = document.createElement("div"); testElRow.classList.add("tabulator-row"); testElRow.style.visibility = "hidden"; testElCell = document.createElement("div"); testElCell.classList.add("tabulator-cell"); testElCell.innerHTML = "Page Row Test"; testElRow.appendChild(testElCell); this.table.rowManager.getTableElement().appendChild(testElRow); this.size = Math.floor(this.table.rowManager.getElement().clientHeight / testElRow.offsetHeight); this.table.rowManager.getTableElement().removeChild(testElRow); } this.dispatchExternal("pageSizeChanged", this.size); this.generatePageSizeSelectList(); } initialLoadComplete() { this.initialLoad = false; } remotePageParams(data, config, silent, params) { if (!this.initialLoad) { if (this.progressiveLoad && !silent || !this.progressiveLoad && !this.dataChanging) { this.reset(true); } } params.page = this.page; if (this.size) { params.size = this.size; } return params; } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// userSetPageToRow(row) { if (this.table.options.pagination) { row = this.table.rowManager.findRow(row); if (row) { return this.setPageToRow(row); } } return Promise.reject(); } userSetPageSize(size) { if (this.table.options.pagination) { this.setPageSize(size); return this.setPage(1); } else { return false; } } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// scrollVertical(top, dir) { var element, diff, margin; if (!dir && !this.table.dataLoader.loading) { element = this.table.rowManager.getElement(); diff = element.scrollHeight - element.clientHeight - top; margin = this.table.options.progressiveLoadScrollMargin || element.clientHeight * 2; if (diff < margin) { this.nextPage().catch(() => { }); } } } restOnRenderBefore(rows2, renderInPosition) { if (!renderInPosition) { if (this.mode === "local") { this.reset(); } } return rows2; } rowsUpdated() { this.refreshData(true, "all"); } createElements() { var button; this.element = document.createElement("span"); this.element.classList.add("tabulator-paginator"); this.pagesElement = document.createElement("span"); this.pagesElement.classList.add("tabulator-pages"); button = document.createElement("button"); button.classList.add("tabulator-page"); button.setAttribute("type", "button"); button.setAttribute("role", "button"); button.setAttribute("aria-label", ""); button.setAttribute("title", ""); this.firstBut = button.cloneNode(true); this.firstBut.setAttribute("data-page", "first"); this.prevBut = button.cloneNode(true); this.prevBut.setAttribute("data-page", "prev"); this.nextBut = button.cloneNode(true); this.nextBut.setAttribute("data-page", "next"); this.lastBut = button.cloneNode(true); this.lastBut.setAttribute("data-page", "last"); if (this.table.options.paginationSizeSelector) { this.pageSizeSelect = document.createElement("select"); this.pageSizeSelect.classList.add("tabulator-page-size"); } } generatePageSizeSelectList() { var pageSizes = []; if (this.pageSizeSelect) { if (Array.isArray(this.table.options.paginationSizeSelector)) { pageSizes = this.table.options.paginationSizeSelector; this.pageSizes = pageSizes; if (this.pageSizes.indexOf(this.size) == -1) { pageSizes.unshift(this.size); } } else { if (this.pageSizes.indexOf(this.size) == -1) { pageSizes = []; for (let i = 1; i < 5; i++) { pageSizes.push(this.size * i); } this.pageSizes = pageSizes; } else { pageSizes = this.pageSizes; } } while (this.pageSizeSelect.firstChild) this.pageSizeSelect.removeChild(this.pageSizeSelect.firstChild); pageSizes.forEach((item) => { var itemEl = document.createElement("option"); itemEl.value = item; if (item === true) { this.langBind("pagination|all", function (value) { itemEl.innerHTML = value; }); } else { itemEl.innerHTML = item; } this.pageSizeSelect.appendChild(itemEl); }); this.pageSizeSelect.value = this.size; } } initializePageCounter() { var counter = this.table.options.paginationCounter, pageCounter = null; if (counter) { if (typeof counter === "function") { pageCounter = counter; } else { pageCounter = _Page.pageCounters[counter]; } if (pageCounter) { this.pageCounter = pageCounter; this.pageCounterElement = document.createElement("span"); this.pageCounterElement.classList.add("tabulator-page-counter"); } else { console.warn("Pagination Error - No such page counter found: ", counter); } } } //setup pagination initializePaginator(hidden) { var pageSelectLabel, paginationCounterHolder; if (!hidden) { this.langBind("pagination|first", (value) => { this.firstBut.innerHTML = value; }); this.langBind("pagination|first_title", (value) => { this.firstBut.setAttribute("aria-label", value); this.firstBut.setAttribute("title", value); }); this.langBind("pagination|prev", (value) => { this.prevBut.innerHTML = value; }); this.langBind("pagination|prev_title", (value) => { this.prevBut.setAttribute("aria-label", value); this.prevBut.setAttribute("title", value); }); this.langBind("pagination|next", (value) => { this.nextBut.innerHTML = value; }); this.langBind("pagination|next_title", (value) => { this.nextBut.setAttribute("aria-label", value); this.nextBut.setAttribute("title", value); }); this.langBind("pagination|last", (value) => { this.lastBut.innerHTML = value; }); this.langBind("pagination|last_title", (value) => { this.lastBut.setAttribute("aria-label", value); this.lastBut.setAttribute("title", value); }); this.firstBut.addEventListener("click", () => { this.setPage(1); }); this.prevBut.addEventListener("click", () => { this.previousPage(); }); this.nextBut.addEventListener("click", () => { this.nextPage(); }); this.lastBut.addEventListener("click", () => { this.setPage(this.max); }); if (this.table.options.paginationElement) { this.element = this.table.options.paginationElement; } if (this.pageSizeSelect) { pageSelectLabel = document.createElement("label"); this.langBind("pagination|page_size", (value) => { this.pageSizeSelect.setAttribute("aria-label", value); this.pageSizeSelect.setAttribute("title", value); pageSelectLabel.innerHTML = value; }); this.element.appendChild(pageSelectLabel); this.element.appendChild(this.pageSizeSelect); this.pageSizeSelect.addEventListener("change", (e) => { this.setPageSize(this.pageSizeSelect.value == "true" ? true : this.pageSizeSelect.value); this.setPage(1); }); } this.element.appendChild(this.firstBut); this.element.appendChild(this.prevBut); this.element.appendChild(this.pagesElement); this.element.appendChild(this.nextBut); this.element.appendChild(this.lastBut); if (!this.table.options.paginationElement) { if (this.table.options.paginationCounter) { if (this.table.options.paginationCounterElement) { if (this.table.options.paginationCounterElement instanceof HTMLElement) { this.table.options.paginationCounterElement.appendChild(this.pageCounterElement); } else if (typeof this.table.options.paginationCounterElement === "string") { paginationCounterHolder = document.querySelector(this.table.options.paginationCounterElement); if (paginationCounterHolder) { paginationCounterHolder.appendChild(this.pageCounterElement); } else { console.warn("Pagination Error - Unable to find element matching paginationCounterElement selector:", this.table.options.paginationCounterElement); } } } else { this.footerAppend(this.pageCounterElement); } } this.footerAppend(this.element); } this.page = this.table.options.paginationInitialPage; this.count = this.table.options.paginationButtonCount; } this.mode = this.table.options.paginationMode; } initializeProgressive(mode) { this.initializePaginator(true); this.mode = "progressive_" + mode; this.progressiveLoad = true; } trackChanges() { this.dispatch("page-changed"); } //calculate maximum page from number of rows setMaxRows(rowCount) { if (!rowCount) { this.max = 1; } else { this.max = this.size === true ? 1 : Math.ceil(rowCount / this.size); } if (this.page > this.max) { this.page = this.max; } } //reset to first page without triggering action reset(force) { if (!this.initialLoad) { if (this.mode == "local" || force) { this.page = 1; this.trackChanges(); } } } //set the maximum page setMaxPage(max) { max = parseInt(max); this.max = max || 1; if (this.page > this.max) { this.page = this.max; this.trigger(); } } //set current page number setPage(page) { switch (page) { case "first": return this.setPage(1); case "prev": return this.previousPage(); case "next": return this.nextPage(); case "last": return this.setPage(this.max); } page = parseInt(page); if (page > 0 && page <= this.max || this.mode !== "local") { this.page = page; this.trackChanges(); return this.trigger(); } else { console.warn("Pagination Error - Requested page is out of range of 1 - " + this.max + ":", page); return Promise.reject(); } } setPageToRow(row) { var rows2 = this.displayRows(-1); var index = rows2.indexOf(row); if (index > -1) { var page = this.size === true ? 1 : Math.ceil((index + 1) / this.size); return this.setPage(page); } else { console.warn("Pagination Error - Requested row is not visible"); return Promise.reject(); } } setPageSize(size) { if (size !== true) { size = parseInt(size); } if (size > 0) { this.size = size; this.dispatchExternal("pageSizeChanged", size); } if (this.pageSizeSelect) { this.generatePageSizeSelectList(); } this.trackChanges(); } _setPageCounter(totalRows, size, currentRow) { var content; if (this.pageCounter) { if (this.mode === "remote") { size = this.size; currentRow = (this.page - 1) * this.size + 1; totalRows = this.remoteRowCountEstimate; } content = this.pageCounter.call(this, size, currentRow, this.page, totalRows, this.max); switch (typeof content) { case "object": if (content instanceof Node) { while (this.pageCounterElement.firstChild) this.pageCounterElement.removeChild(this.pageCounterElement.firstChild); this.pageCounterElement.appendChild(content); } else { this.pageCounterElement.innerHTML = ""; if (content != null) { console.warn("Page Counter Error - Page Counter has returned a type of object, the only valid page counter object return is an instance of Node, the page counter returned:", content); } } break; case "undefined": this.pageCounterElement.innerHTML = ""; break; default: this.pageCounterElement.innerHTML = content; } } } //setup the pagination buttons _setPageButtons() { let leftSize = Math.floor((this.count - 1) / 2); let rightSize = Math.ceil((this.count - 1) / 2); let min = this.max - this.page + leftSize + 1 < this.count ? this.max - this.count + 1 : Math.max(this.page - leftSize, 1); let max = this.page <= rightSize ? Math.min(this.count, this.max) : Math.min(this.page + rightSize, this.max); while (this.pagesElement.firstChild) this.pagesElement.removeChild(this.pagesElement.firstChild); if (this.page == 1) { this.firstBut.disabled = true; this.prevBut.disabled = true; } else { this.firstBut.disabled = false; this.prevBut.disabled = false; } if (this.page == this.max) { this.lastBut.disabled = true; this.nextBut.disabled = true; } else { this.lastBut.disabled = false; this.nextBut.disabled = false; } for (let i = min; i <= max; i++) { if (i > 0 && i <= this.max) { this.pagesElement.appendChild(this._generatePageButton(i)); } } this.footerRedraw(); } _generatePageButton(page) { var button = document.createElement("button"); button.classList.add("tabulator-page"); if (page == this.page) { button.classList.add("active"); } button.setAttribute("type", "button"); button.setAttribute("role", "button"); this.langBind("pagination|page_title", (value) => { button.setAttribute("aria-label", value + " " + page); button.setAttribute("title", value + " " + page); }); button.setAttribute("data-page", page); button.textContent = page; button.addEventListener("click", (e) => { this.setPage(page); }); return button; } //previous page previousPage() { if (this.page > 1) { this.page--; this.trackChanges(); return this.trigger(); } else { console.warn("Pagination Error - Previous page would be less than page 1:", 0); return Promise.reject(); } } //next page nextPage() { if (this.page < this.max) { this.page++; this.trackChanges(); return this.trigger(); } else { if (!this.progressiveLoad) { console.warn("Pagination Error - Next page would be greater than maximum page of " + this.max + ":", this.max + 1); } return Promise.reject(); } } //return current page number getPage() { return this.page; } //return max page number getPageMax() { return this.max; } getPageSize(size) { return this.size; } getMode() { return this.mode; } //return appropriate rows for current page getRows(data) { var actualRowPageSize = 0, output, start, end, actualStartRow; var actualRows = data.filter((row) => { return row.type === "row"; }); if (this.mode == "local") { output = []; this.setMaxRows(data.length); if (this.size === true) { start = 0; end = data.length; } else { start = this.size * (this.page - 1); end = start + parseInt(this.size); } this._setPageButtons(); for (let i = start; i < end; i++) { let row = data[i]; if (row) { output.push(row); if (row.type === "row") { if (!actualStartRow) { actualStartRow = row; } actualRowPageSize++; } } } this._setPageCounter(actualRows.length, actualRowPageSize, actualStartRow ? actualRows.indexOf(actualStartRow) + 1 : 0); return output; } else { this._setPageButtons(); this._setPageCounter(actualRows.length); return data.slice(0); } } trigger() { var left; switch (this.mode) { case "local": left = this.table.rowManager.scrollLeft; this.refreshData(); this.table.rowManager.scrollHorizontal(left); this.dispatchExternal("pageLoaded", this.getPage()); return Promise.resolve(); case "remote": this.dataChanging = true; return this.reloadData(null).finally(() => { this.dataChanging = false; }); case "progressive_load": case "progressive_scroll": return this.reloadData(null, true); default: console.warn("Pagination Error - no such pagination mode:", this.mode); return Promise.reject(); } } _parseRemoteData(data) { var margin, paginationOutOfRange; if (typeof data.last_page === "undefined") { console.warn("Remote Pagination Error - Server response missing '" + (this.options("dataReceiveParams").last_page || "last_page") + "' property"); } if (data.data) { this.max = parseInt(data.last_page) || 1; this.remoteRowCountEstimate = typeof data.last_row !== "undefined" ? data.last_row : data.last_page * this.size - (this.page == data.last_page ? this.size - data.data.length : 0); if (this.progressiveLoad) { switch (this.mode) { case "progressive_load": if (this.page == 1) { this.table.rowManager.setData(data.data, false, this.page == 1); } else { this.table.rowManager.addRows(data.data); } if (this.page < this.max) { setTimeout(() => { this.nextPage(); }, this.table.options.progressiveLoadDelay); } break; case "progressive_scroll": data = this.page === 1 ? data.data : this.table.rowManager.getData().concat(data.data); this.table.rowManager.setData(data, this.page !== 1, this.page == 1); margin = this.table.options.progressiveLoadScrollMargin || this.table.rowManager.element.clientHeight * 2; if (this.table.rowManager.element.scrollHeight <= this.table.rowManager.element.clientHeight + margin) { if (this.page < this.max) { setTimeout(() => { this.nextPage(); }); } } break; } return false; } else { if (this.page > this.max) { console.warn("Remote Pagination Error - Server returned last page value lower than the current page"); paginationOutOfRange = this.options("paginationOutOfRange"); if (paginationOutOfRange) { return this.setPage(typeof paginationOutOfRange === "function" ? paginationOutOfRange.call(this, this.page, this.max) : paginationOutOfRange); } } this.dispatchExternal("pageLoaded", this.getPage()); } } else { console.warn("Remote Pagination Error - Server response missing '" + (this.options("dataReceiveParams").data || "data") + "' property"); } return data.data; } //handle the footer element being redrawn footerRedraw() { var footer = this.table.footerManager.containerElement; if (Math.ceil(footer.clientWidth) - footer.scrollWidth < 0) { this.pagesElement.style.display = "none"; } else { this.pagesElement.style.display = ""; if (Math.ceil(footer.clientWidth) - footer.scrollWidth < 0) { this.pagesElement.style.display = "none"; } } } }; __publicField(_Page, "moduleName", "page"); //load defaults __publicField(_Page, "pageCounters", defaultPageCounters); let Page = _Page; var defaultReaders = { local: function (id, type) { var data = localStorage.getItem(id + "-" + type); return data ? JSON.parse(data) : false; }, cookie: function (id, type) { var cookie = document.cookie, key = id + "-" + type, cookiePos = cookie.indexOf(key + "="), end, data; if (cookiePos > -1) { cookie = cookie.slice(cookiePos); end = cookie.indexOf(";"); if (end > -1) { cookie = cookie.slice(0, end); } data = cookie.replace(key + "=", ""); } return data ? JSON.parse(data) : false; } }; var defaultWriters = { local: function (id, type, data) { localStorage.setItem(id + "-" + type, JSON.stringify(data)); }, cookie: function (id, type, data) { var expireDate = /* @__PURE__ */ new Date(); expireDate.setDate(expireDate.getDate() + 1e4); document.cookie = id + "-" + type + "=" + JSON.stringify(data) + "; expires=" + expireDate.toUTCString(); } }; const _Persistence = class _Persistence extends Module { constructor(table) { super(table); this.mode = ""; this.id = ""; this.defWatcherBlock = false; this.config = {}; this.readFunc = false; this.writeFunc = false; this.registerTableOption("persistence", false); this.registerTableOption("persistenceID", ""); this.registerTableOption("persistenceMode", true); this.registerTableOption("persistenceReaderFunc", false); this.registerTableOption("persistenceWriterFunc", false); } // Test for whether localStorage is available for use. localStorageTest() { var testKey = "_tabulator_test"; try { window.localStorage.setItem(testKey, testKey); window.localStorage.removeItem(testKey); return true; } catch (e) { return false; } } //setup parameters initialize() { if (this.table.options.persistence) { var mode = this.table.options.persistenceMode, id = this.table.options.persistenceID, retrievedData; this.mode = mode !== true ? mode : this.localStorageTest() ? "local" : "cookie"; if (this.table.options.persistenceReaderFunc) { if (typeof this.table.options.persistenceReaderFunc === "function") { this.readFunc = this.table.options.persistenceReaderFunc; } else { if (_Persistence.readers[this.table.options.persistenceReaderFunc]) { this.readFunc = _Persistence.readers[this.table.options.persistenceReaderFunc]; } else { console.warn("Persistence Read Error - invalid reader set", this.table.options.persistenceReaderFunc); } } } else { if (_Persistence.readers[this.mode]) { this.readFunc = _Persistence.readers[this.mode]; } else { console.warn("Persistence Read Error - invalid reader set", this.mode); } } if (this.table.options.persistenceWriterFunc) { if (typeof this.table.options.persistenceWriterFunc === "function") { this.writeFunc = this.table.options.persistenceWriterFunc; } else { if (_Persistence.writers[this.table.options.persistenceWriterFunc]) { this.writeFunc = _Persistence.writers[this.table.options.persistenceWriterFunc]; } else { console.warn("Persistence Write Error - invalid reader set", this.table.options.persistenceWriterFunc); } } } else { if (_Persistence.writers[this.mode]) { this.writeFunc = _Persistence.writers[this.mode]; } else { console.warn("Persistence Write Error - invalid writer set", this.mode); } } this.id = "tabulator-" + (id || (this.table.element.getAttribute("id") || "")); this.config = { sort: this.table.options.persistence === true || this.table.options.persistence.sort, filter: this.table.options.persistence === true || this.table.options.persistence.filter, headerFilter: this.table.options.persistence === true || this.table.options.persistence.headerFilter, group: this.table.options.persistence === true || this.table.options.persistence.group, page: this.table.options.persistence === true || this.table.options.persistence.page, columns: this.table.options.persistence === true ? ["title", "width", "visible"] : this.table.options.persistence.columns }; if (this.config.page) { retrievedData = this.retrieveData("page"); if (retrievedData) { if (typeof retrievedData.paginationSize !== "undefined" && (this.config.page === true || this.config.page.size)) { this.table.options.paginationSize = retrievedData.paginationSize; } if (typeof retrievedData.paginationInitialPage !== "undefined" && (this.config.page === true || this.config.page.page)) { this.table.options.paginationInitialPage = retrievedData.paginationInitialPage; } } } if (this.config.group) { retrievedData = this.retrieveData("group"); if (retrievedData) { if (typeof retrievedData.groupBy !== "undefined" && (this.config.group === true || this.config.group.groupBy)) { this.table.options.groupBy = retrievedData.groupBy; } if (typeof retrievedData.groupStartOpen !== "undefined" && (this.config.group === true || this.config.group.groupStartOpen)) { this.table.options.groupStartOpen = retrievedData.groupStartOpen; } if (typeof retrievedData.groupHeader !== "undefined" && (this.config.group === true || this.config.group.groupHeader)) { this.table.options.groupHeader = retrievedData.groupHeader; } } } if (this.config.columns) { this.table.options.columns = this.load("columns", this.table.options.columns); this.subscribe("column-init", this.initializeColumn.bind(this)); this.subscribe("column-show", this.save.bind(this, "columns")); this.subscribe("column-hide", this.save.bind(this, "columns")); this.subscribe("column-moved", this.save.bind(this, "columns")); } this.subscribe("table-built", this.tableBuilt.bind(this), 0); this.subscribe("table-redraw", this.tableRedraw.bind(this)); this.subscribe("filter-changed", this.eventSave.bind(this, "filter")); this.subscribe("filter-changed", this.eventSave.bind(this, "headerFilter")); this.subscribe("sort-changed", this.eventSave.bind(this, "sort")); this.subscribe("group-changed", this.eventSave.bind(this, "group")); this.subscribe("page-changed", this.eventSave.bind(this, "page")); this.subscribe("column-resized", this.eventSave.bind(this, "columns")); this.subscribe("column-width", this.eventSave.bind(this, "columns")); this.subscribe("layout-refreshed", this.eventSave.bind(this, "columns")); } this.registerTableFunction("getColumnLayout", this.getColumnLayout.bind(this)); this.registerTableFunction("setColumnLayout", this.setColumnLayout.bind(this)); } eventSave(type) { if (this.config[type]) { this.save(type); } } tableBuilt() { var sorters, filters, headerFilters; if (this.config.sort) { sorters = this.load("sort"); if (!sorters === false) { this.table.options.initialSort = sorters; } } if (this.config.filter) { filters = this.load("filter"); if (!filters === false) { this.table.options.initialFilter = filters; } } if (this.config.headerFilter) { headerFilters = this.load("headerFilter"); if (!headerFilters === false) { this.table.options.initialHeaderFilter = headerFilters; } } } tableRedraw(force) { if (force && this.config.columns) { this.save("columns"); } } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// getColumnLayout() { return this.parseColumns(this.table.columnManager.getColumns()); } setColumnLayout(layout) { this.table.columnManager.setColumns(this.mergeDefinition(this.table.options.columns, layout, true)); return true; } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// initializeColumn(column) { var def, keys; if (this.config.columns) { this.defWatcherBlock = true; def = column.getDefinition(); keys = this.config.columns === true ? Object.keys(def) : this.config.columns; keys.forEach((key) => { var props = Object.getOwnPropertyDescriptor(def, key); var value = def[key]; if (props) { Object.defineProperty(def, key, { set: (newValue) => { value = newValue; if (!this.defWatcherBlock) { this.save("columns"); } if (props.set) { props.set(newValue); } }, get: () => { if (props.get) { props.get(); } return value; } }); } }); this.defWatcherBlock = false; } } //load saved definitions load(type, current) { var data = this.retrieveData(type); if (current) { data = data ? this.mergeDefinition(current, data) : current; } return data; } //retrieve data from memory retrieveData(type) { return this.readFunc ? this.readFunc(this.id, type) : false; } //merge old and new column definitions mergeDefinition(oldCols, newCols, mergeAllNew) { var output = []; newCols = newCols || []; newCols.forEach((column, to) => { var from = this._findColumn(oldCols, column), keys; if (from) { if (mergeAllNew) { keys = Object.keys(column); } else if (this.config.columns === true || this.config.columns == void 0) { keys = Object.keys(from); keys.push("width"); } else { keys = this.config.columns; } keys.forEach((key) => { if (key !== "columns" && typeof column[key] !== "undefined") { from[key] = column[key]; } }); if (from.columns) { from.columns = this.mergeDefinition(from.columns, column.columns); } output.push(from); } }); oldCols.forEach((column, i) => { var from = this._findColumn(newCols, column); if (!from) { if (output.length > i) { output.splice(i, 0, column); } else { output.push(column); } } }); return output; } //find matching columns _findColumn(columns, subject) { var type = subject.columns ? "group" : subject.field ? "field" : "object"; return columns.find(function (col) { switch (type) { case "group": return col.title === subject.title && col.columns.length === subject.columns.length; case "field": return col.field === subject.field; case "object": return col === subject; } }); } //save data save(type) { var data = {}; switch (type) { case "columns": data = this.parseColumns(this.table.columnManager.getColumns()); break; case "filter": data = this.table.modules.filter.getFilters(); break; case "headerFilter": data = this.table.modules.filter.getHeaderFilters(); break; case "sort": data = this.validateSorters(this.table.modules.sort.getSort()); break; case "group": data = this.getGroupConfig(); break; case "page": data = this.getPageConfig(); break; } if (this.writeFunc) { this.writeFunc(this.id, type, data); } } //ensure sorters contain no function data validateSorters(data) { data.forEach(function (item) { item.column = item.field; delete item.field; }); return data; } getGroupConfig() { var data = {}; if (this.config.group) { if (this.config.group === true || this.config.group.groupBy) { data.groupBy = this.table.options.groupBy; } if (this.config.group === true || this.config.group.groupStartOpen) { data.groupStartOpen = this.table.options.groupStartOpen; } if (this.config.group === true || this.config.group.groupHeader) { data.groupHeader = this.table.options.groupHeader; } } return data; } getPageConfig() { var data = {}; if (this.config.page) { if (this.config.page === true || this.config.page.size) { data.paginationSize = this.table.modules.page.getPageSize(); } if (this.config.page === true || this.config.page.page) { data.paginationInitialPage = this.table.modules.page.getPage(); } } return data; } //parse columns for data to store parseColumns(columns) { var definitions = [], excludedKeys = ["headerContextMenu", "headerMenu", "contextMenu", "clickMenu"]; columns.forEach((column) => { var defStore = {}, colDef = column.getDefinition(), keys; if (column.isGroup) { defStore.title = colDef.title; defStore.columns = this.parseColumns(column.getColumns()); } else { defStore.field = column.getField(); if (this.config.columns === true || this.config.columns == void 0) { keys = Object.keys(colDef); keys.push("width"); keys.push("visible"); } else { keys = this.config.columns; } keys.forEach((key) => { switch (key) { case "width": defStore.width = column.getWidth(); break; case "visible": defStore.visible = column.visible; break; default: if (typeof colDef[key] !== "function" && excludedKeys.indexOf(key) === -1) { defStore[key] = colDef[key]; } } }); } definitions.push(defStore); }); return definitions; } }; __publicField(_Persistence, "moduleName", "persistence"); __publicField(_Persistence, "moduleInitOrder", -10); //load defaults __publicField(_Persistence, "readers", defaultReaders); __publicField(_Persistence, "writers", defaultWriters); let Persistence = _Persistence; class Popup2 extends Module { constructor(table) { super(table); this.columnSubscribers = {}; this.registerTableOption("rowContextPopup", false); this.registerTableOption("rowClickPopup", false); this.registerTableOption("rowDblClickPopup", false); this.registerTableOption("groupContextPopup", false); this.registerTableOption("groupClickPopup", false); this.registerTableOption("groupDblClickPopup", false); this.registerColumnOption("headerContextPopup"); this.registerColumnOption("headerClickPopup"); this.registerColumnOption("headerDblClickPopup"); this.registerColumnOption("headerPopup"); this.registerColumnOption("headerPopupIcon"); this.registerColumnOption("contextPopup"); this.registerColumnOption("clickPopup"); this.registerColumnOption("dblClickPopup"); this.registerComponentFunction("cell", "popup", this._componentPopupCall.bind(this)); this.registerComponentFunction("column", "popup", this._componentPopupCall.bind(this)); this.registerComponentFunction("row", "popup", this._componentPopupCall.bind(this)); this.registerComponentFunction("group", "popup", this._componentPopupCall.bind(this)); } initialize() { this.initializeRowWatchers(); this.initializeGroupWatchers(); this.subscribe("column-init", this.initializeColumn.bind(this)); } _componentPopupCall(component, contents, position) { this.loadPopupEvent(contents, null, component, position); } initializeRowWatchers() { if (this.table.options.rowContextPopup) { this.subscribe("row-contextmenu", this.loadPopupEvent.bind(this, this.table.options.rowContextPopup)); this.table.on("rowTapHold", this.loadPopupEvent.bind(this, this.table.options.rowContextPopup)); } if (this.table.options.rowClickPopup) { this.subscribe("row-click", this.loadPopupEvent.bind(this, this.table.options.rowClickPopup)); } if (this.table.options.rowDblClickPopup) { this.subscribe("row-dblclick", this.loadPopupEvent.bind(this, this.table.options.rowDblClickPopup)); } } initializeGroupWatchers() { if (this.table.options.groupContextPopup) { this.subscribe("group-contextmenu", this.loadPopupEvent.bind(this, this.table.options.groupContextPopup)); this.table.on("groupTapHold", this.loadPopupEvent.bind(this, this.table.options.groupContextPopup)); } if (this.table.options.groupClickPopup) { this.subscribe("group-click", this.loadPopupEvent.bind(this, this.table.options.groupClickPopup)); } if (this.table.options.groupDblClickPopup) { this.subscribe("group-dblclick", this.loadPopupEvent.bind(this, this.table.options.groupDblClickPopup)); } } initializeColumn(column) { var def = column.definition; if (def.headerContextPopup && !this.columnSubscribers.headerContextPopup) { this.columnSubscribers.headerContextPopup = this.loadPopupTableColumnEvent.bind(this, "headerContextPopup"); this.subscribe("column-contextmenu", this.columnSubscribers.headerContextPopup); this.table.on("headerTapHold", this.loadPopupTableColumnEvent.bind(this, "headerContextPopup")); } if (def.headerClickPopup && !this.columnSubscribers.headerClickPopup) { this.columnSubscribers.headerClickPopup = this.loadPopupTableColumnEvent.bind(this, "headerClickPopup"); this.subscribe("column-click", this.columnSubscribers.headerClickPopup); } if (def.headerDblClickPopup && !this.columnSubscribers.headerDblClickPopup) { this.columnSubscribers.headerDblClickPopup = this.loadPopupTableColumnEvent.bind(this, "headerDblClickPopup"); this.subscribe("column-dblclick", this.columnSubscribers.headerDblClickPopup); } if (def.headerPopup) { this.initializeColumnHeaderPopup(column); } if (def.contextPopup && !this.columnSubscribers.contextPopup) { this.columnSubscribers.contextPopup = this.loadPopupTableCellEvent.bind(this, "contextPopup"); this.subscribe("cell-contextmenu", this.columnSubscribers.contextPopup); this.table.on("cellTapHold", this.loadPopupTableCellEvent.bind(this, "contextPopup")); } if (def.clickPopup && !this.columnSubscribers.clickPopup) { this.columnSubscribers.clickPopup = this.loadPopupTableCellEvent.bind(this, "clickPopup"); this.subscribe("cell-click", this.columnSubscribers.clickPopup); } if (def.dblClickPopup && !this.columnSubscribers.dblClickPopup) { this.columnSubscribers.dblClickPopup = this.loadPopupTableCellEvent.bind(this, "dblClickPopup"); this.subscribe("cell-click", this.columnSubscribers.dblClickPopup); } } initializeColumnHeaderPopup(column) { var icon = column.definition.headerPopupIcon, headerPopupEl; headerPopupEl = document.createElement("span"); headerPopupEl.classList.add("tabulator-header-popup-button"); if (icon) { if (typeof icon === "function") { icon = icon(column.getComponent()); } if (icon instanceof HTMLElement) { headerPopupEl.appendChild(icon); } else { headerPopupEl.innerHTML = icon; } } else { headerPopupEl.innerHTML = "⋮"; } headerPopupEl.addEventListener("click", (e) => { e.stopPropagation(); e.preventDefault(); this.loadPopupEvent(column.definition.headerPopup, e, column); }); column.titleElement.insertBefore(headerPopupEl, column.titleElement.firstChild); } loadPopupTableCellEvent(option, e, cell) { if (cell._cell) { cell = cell._cell; } if (cell.column.definition[option]) { this.loadPopupEvent(cell.column.definition[option], e, cell); } } loadPopupTableColumnEvent(option, e, column) { if (column._column) { column = column._column; } if (column.definition[option]) { this.loadPopupEvent(column.definition[option], e, column); } } loadPopupEvent(contents, e, component, position) { var renderedCallback; function onRendered(callback) { renderedCallback = callback; } if (component._group) { component = component._group; } else if (component._row) { component = component._row; } contents = typeof contents == "function" ? contents.call(this.table, e, component.getComponent(), onRendered) : contents; this.loadPopup(e, component, contents, renderedCallback, position); } loadPopup(e, component, contents, renderedCallback, position) { var touch = !(e instanceof MouseEvent), contentsEl, popup; if (contents instanceof HTMLElement) { contentsEl = contents; } else { contentsEl = document.createElement("div"); contentsEl.innerHTML = contents; } contentsEl.classList.add("tabulator-popup"); contentsEl.addEventListener("click", (e2) => { e2.stopPropagation(); }); if (!touch) { e.preventDefault(); } popup = this.popup(contentsEl); if (typeof renderedCallback === "function") { popup.renderCallback(renderedCallback); } if (e) { popup.show(e); } else { popup.show(component.getElement(), position || "center"); } popup.hideOnBlur(() => { this.dispatchExternal("popupClosed", component.getComponent()); }); this.dispatchExternal("popupOpened", component.getComponent()); } } __publicField(Popup2, "moduleName", "popup"); class Print extends Module { constructor(table) { super(table); this.element = false; this.manualBlock = false; this.beforeprintEventHandler = null; this.afterprintEventHandler = null; this.registerTableOption("printAsHtml", false); this.registerTableOption("printFormatter", false); this.registerTableOption("printHeader", false); this.registerTableOption("printFooter", false); this.registerTableOption("printStyled", true); this.registerTableOption("printRowRange", "visible"); this.registerTableOption("printConfig", {}); this.registerColumnOption("print"); this.registerColumnOption("titlePrint"); } initialize() { if (this.table.options.printAsHtml) { this.beforeprintEventHandler = this.replaceTable.bind(this); this.afterprintEventHandler = this.cleanup.bind(this); window.addEventListener("beforeprint", this.beforeprintEventHandler); window.addEventListener("afterprint", this.afterprintEventHandler); this.subscribe("table-destroy", this.destroy.bind(this)); } this.registerTableFunction("print", this.printFullscreen.bind(this)); } destroy() { if (this.table.options.printAsHtml) { window.removeEventListener("beforeprint", this.beforeprintEventHandler); window.removeEventListener("afterprint", this.afterprintEventHandler); } } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// replaceTable() { if (!this.manualBlock) { this.element = document.createElement("div"); this.element.classList.add("tabulator-print-table"); this.element.appendChild(this.table.modules.export.generateTable(this.table.options.printConfig, this.table.options.printStyled, this.table.options.printRowRange, "print")); this.table.element.style.display = "none"; this.table.element.parentNode.insertBefore(this.element, this.table.element); } } cleanup() { document.body.classList.remove("tabulator-print-fullscreen-hide"); if (this.element && this.element.parentNode) { this.element.parentNode.removeChild(this.element); this.table.element.style.display = ""; } } printFullscreen(visible, style, config) { var scrollX = window.scrollX, scrollY = window.scrollY, headerEl = document.createElement("div"), footerEl = document.createElement("div"), tableEl = this.table.modules.export.generateTable(typeof config != "undefined" ? config : this.table.options.printConfig, typeof style != "undefined" ? style : this.table.options.printStyled, visible || this.table.options.printRowRange, "print"), headerContent, footerContent; this.manualBlock = true; this.element = document.createElement("div"); this.element.classList.add("tabulator-print-fullscreen"); if (this.table.options.printHeader) { headerEl.classList.add("tabulator-print-header"); headerContent = typeof this.table.options.printHeader == "function" ? this.table.options.printHeader.call(this.table) : this.table.options.printHeader; if (typeof headerContent == "string") { headerEl.innerHTML = headerContent; } else { headerEl.appendChild(headerContent); } this.element.appendChild(headerEl); } this.element.appendChild(tableEl); if (this.table.options.printFooter) { footerEl.classList.add("tabulator-print-footer"); footerContent = typeof this.table.options.printFooter == "function" ? this.table.options.printFooter.call(this.table) : this.table.options.printFooter; if (typeof footerContent == "string") { footerEl.innerHTML = footerContent; } else { footerEl.appendChild(footerContent); } this.element.appendChild(footerEl); } document.body.classList.add("tabulator-print-fullscreen-hide"); document.body.appendChild(this.element); if (this.table.options.printFormatter) { this.table.options.printFormatter(this.element, tableEl); } window.print(); this.cleanup(); window.scrollTo(scrollX, scrollY); this.manualBlock = false; } } __publicField(Print, "moduleName", "print"); class ReactiveData extends Module { constructor(table) { super(table); this.data = false; this.blocked = false; this.origFuncs = {}; this.currentVersion = 0; this.registerTableOption("reactiveData", false); } initialize() { if (this.table.options.reactiveData) { this.subscribe("cell-value-save-before", this.block.bind(this, "cellsave")); this.subscribe("cell-value-save-after", this.unblock.bind(this, "cellsave")); this.subscribe("row-data-save-before", this.block.bind(this, "rowsave")); this.subscribe("row-data-save-after", this.unblock.bind(this, "rowsave")); this.subscribe("row-data-init-after", this.watchRow.bind(this)); this.subscribe("data-processing", this.watchData.bind(this)); this.subscribe("table-destroy", this.unwatchData.bind(this)); } } watchData(data) { var self = this, version; this.currentVersion++; version = this.currentVersion; this.unwatchData(); this.data = data; this.origFuncs.push = data.push; Object.defineProperty(this.data, "push", { enumerable: false, configurable: true, value: function () { var args = Array.from(arguments), result; if (!self.blocked && version === self.currentVersion) { self.block("data-push"); args.forEach((arg) => { self.table.rowManager.addRowActual(arg, false); }); result = Array.prototype.push.apply(data, arguments); self.unblock("data-push"); } return result; } }); this.origFuncs.unshift = data.unshift; Object.defineProperty(this.data, "unshift", { enumerable: false, configurable: true, value: function () { var args = Array.from(arguments), result; if (!self.blocked && version === self.currentVersion) { self.block("data-unshift"); args.forEach((arg) => { self.table.rowManager.addRowActual(arg, true); }); result = Array.prototype.unshift.apply(data, arguments); self.unblock("data-unshift"); } return result; } }); this.origFuncs.shift = data.shift; Object.defineProperty(this.data, "shift", { enumerable: false, configurable: true, value: function () { var row, result; if (!self.blocked && version === self.currentVersion) { self.block("data-shift"); if (self.data.length) { row = self.table.rowManager.getRowFromDataObject(self.data[0]); if (row) { row.deleteActual(); } } result = Array.prototype.shift.call(data); self.unblock("data-shift"); } return result; } }); this.origFuncs.pop = data.pop; Object.defineProperty(this.data, "pop", { enumerable: false, configurable: true, value: function () { var row, result; if (!self.blocked && version === self.currentVersion) { self.block("data-pop"); if (self.data.length) { row = self.table.rowManager.getRowFromDataObject(self.data[self.data.length - 1]); if (row) { row.deleteActual(); } } result = Array.prototype.pop.call(data); self.unblock("data-pop"); } return result; } }); this.origFuncs.splice = data.splice; Object.defineProperty(this.data, "splice", { enumerable: false, configurable: true, value: function () { var args = Array.from(arguments), start = args[0] < 0 ? data.length + args[0] : args[0], end = args[1], newRows = args[2] ? args.slice(2) : false, startRow, result; if (!self.blocked && version === self.currentVersion) { self.block("data-splice"); if (newRows) { startRow = data[start] ? self.table.rowManager.getRowFromDataObject(data[start]) : false; if (startRow) { newRows.forEach((rowData) => { self.table.rowManager.addRowActual(rowData, true, startRow, true); }); } else { newRows = newRows.slice().reverse(); newRows.forEach((rowData) => { self.table.rowManager.addRowActual(rowData, true, false, true); }); } } if (end !== 0) { var oldRows = data.slice(start, typeof args[1] === "undefined" ? args[1] : start + end); oldRows.forEach((rowData, i) => { var row = self.table.rowManager.getRowFromDataObject(rowData); if (row) { row.deleteActual(i !== oldRows.length - 1); } }); } if (newRows || end !== 0) { self.table.rowManager.reRenderInPosition(); } result = Array.prototype.splice.apply(data, arguments); self.unblock("data-splice"); } return result; } }); } unwatchData() { if (this.data !== false) { for (var key in this.origFuncs) { Object.defineProperty(this.data, key, { enumerable: true, configurable: true, writable: true, value: this.origFuncs[key] }); } } } watchRow(row) { var data = row.getData(); for (var key in data) { this.watchKey(row, data, key); } if (this.table.options.dataTree) { this.watchTreeChildren(row); } } watchTreeChildren(row) { var self = this, childField = row.getData()[this.table.options.dataTreeChildField]; if (childField) { Object.defineProperty(childField, "push", { enumerable: false, configurable: true, value: function () { var result; if (!self.blocked) { self.block("tree-push"); result = Array.prototype.push.apply(childField, arguments); self.rebuildTree(row); self.unblock("tree-push"); } return result; } }); Object.defineProperty(childField, "unshift", { enumerable: false, configurable: true, value: function () { var result; if (!self.blocked) { self.block("tree-unshift"); result = Array.prototype.unshift.apply(childField, arguments); self.rebuildTree(row); self.unblock("tree-unshift"); } return result; } }); Object.defineProperty(childField, "shift", { enumerable: false, configurable: true, value: function () { var result; if (!self.blocked) { self.block("tree-shift"); result = Array.prototype.shift.call(childField); self.rebuildTree(row); self.unblock("tree-shift"); } return result; } }); Object.defineProperty(childField, "pop", { enumerable: false, configurable: true, value: function () { var result; if (!self.blocked) { self.block("tree-pop"); result = Array.prototype.pop.call(childField); self.rebuildTree(row); self.unblock("tree-pop"); } return result; } }); Object.defineProperty(childField, "splice", { enumerable: false, configurable: true, value: function () { var result; if (!self.blocked) { self.block("tree-splice"); result = Array.prototype.splice.apply(childField, arguments); self.rebuildTree(row); self.unblock("tree-splice"); } return result; } }); } } rebuildTree(row) { this.table.modules.dataTree.initializeRow(row); this.table.modules.dataTree.layoutRow(row); this.table.rowManager.refreshActiveData("tree", false, true); } watchKey(row, data, key) { var self = this, props = Object.getOwnPropertyDescriptor(data, key), value = data[key], version = this.currentVersion; Object.defineProperty(data, key, { set: (newValue) => { value = newValue; if (!self.blocked && version === self.currentVersion) { self.block("key"); var update = {}; update[key] = newValue; row.updateData(update); self.unblock("key"); } if (props.set) { props.set(newValue); } }, get: () => { if (props.get) { props.get(); } return value; } }); } unwatchRow(row) { var data = row.getData(); for (var key in data) { Object.defineProperty(data, key, { value: data[key] }); } } block(key) { if (!this.blocked) { this.blocked = key; } } unblock(key) { if (this.blocked === key) { this.blocked = false; } } } __publicField(ReactiveData, "moduleName", "reactiveData"); class ResizeColumns extends Module { constructor(table) { super(table); this.startColumn = false; this.startX = false; this.startWidth = false; this.latestX = false; this.handle = null; this.initialNextColumn = null; this.nextColumn = null; this.initialized = false; this.registerColumnOption("resizable", true); this.registerTableOption("resizableColumnFit", false); this.registerTableOption("resizableColumnGuide", false); } initialize() { this.subscribe("column-rendered", this.layoutColumnHeader.bind(this)); } initializeEventWatchers() { if (!this.initialized) { this.subscribe("cell-rendered", this.layoutCellHandles.bind(this)); this.subscribe("cell-delete", this.deInitializeComponent.bind(this)); this.subscribe("cell-height", this.resizeHandle.bind(this)); this.subscribe("column-moved", this.columnLayoutUpdated.bind(this)); this.subscribe("column-hide", this.deInitializeColumn.bind(this)); this.subscribe("column-show", this.columnLayoutUpdated.bind(this)); this.subscribe("column-width", this.columnWidthUpdated.bind(this)); this.subscribe("column-delete", this.deInitializeComponent.bind(this)); this.subscribe("column-height", this.resizeHandle.bind(this)); this.initialized = true; } } layoutCellHandles(cell) { if (cell.row.type === "row") { this.deInitializeComponent(cell); this.initializeColumn("cell", cell, cell.column, cell.element); } } layoutColumnHeader(column) { if (column.definition.resizable) { this.initializeEventWatchers(); this.deInitializeComponent(column); this.initializeColumn("header", column, column, column.element); } } columnLayoutUpdated(column) { var prev = column.prevColumn(); this.reinitializeColumn(column); if (prev) { this.reinitializeColumn(prev); } } columnWidthUpdated(column) { if (column.modules.frozen) { if (this.table.modules.frozenColumns.leftColumns.includes(column)) { this.table.modules.frozenColumns.leftColumns.forEach((col) => { this.reinitializeColumn(col); }); } else if (this.table.modules.frozenColumns.rightColumns.includes(column)) { this.table.modules.frozenColumns.rightColumns.forEach((col) => { this.reinitializeColumn(col); }); } } } frozenColumnOffset(column) { var offset = false; if (column.modules.frozen) { offset = column.modules.frozen.marginValue; if (column.modules.frozen.position === "left") { offset += column.getWidth() - 3; } else { if (offset) { offset -= 3; } } } return offset !== false ? offset + "px" : false; } reinitializeColumn(column) { var frozenOffset = this.frozenColumnOffset(column); column.cells.forEach((cell) => { if (cell.modules.resize && cell.modules.resize.handleEl) { if (frozenOffset) { cell.modules.resize.handleEl.style[column.modules.frozen.position] = frozenOffset; cell.modules.resize.handleEl.style["z-index"] = 11; } cell.element.after(cell.modules.resize.handleEl); } }); if (column.modules.resize && column.modules.resize.handleEl) { if (frozenOffset) { column.modules.resize.handleEl.style[column.modules.frozen.position] = frozenOffset; } column.element.after(column.modules.resize.handleEl); } } initializeColumn(type, component, column, element) { var self = this, variableHeight = false, mode = column.definition.resizable, config = {}, nearestColumn = column.getLastColumn(); if (type === "header") { variableHeight = column.definition.formatter == "textarea" || column.definition.variableHeight; config = {variableHeight}; } if ((mode === true || mode == type) && this._checkResizability(nearestColumn)) { var handle2 = document.createElement("span"); handle2.className = "tabulator-col-resize-handle"; handle2.addEventListener("click", function (e) { e.stopPropagation(); }); var handleDown = function (e) { self.startColumn = column; self.initialNextColumn = self.nextColumn = nearestColumn.nextColumn(); self._mouseDown(e, nearestColumn, handle2); }; handle2.addEventListener("mousedown", handleDown); handle2.addEventListener("touchstart", handleDown, {passive: true}); handle2.addEventListener("dblclick", (e) => { var oldWidth = nearestColumn.getWidth(); e.stopPropagation(); nearestColumn.reinitializeWidth(true); if (oldWidth !== nearestColumn.getWidth()) { self.dispatch("column-resized", nearestColumn); self.dispatchExternal("columnResized", nearestColumn.getComponent()); } }); if (column.modules.frozen) { handle2.style.position = "sticky"; handle2.style[column.modules.frozen.position] = this.frozenColumnOffset(column); } config.handleEl = handle2; if (element.parentNode && column.visible) { element.after(handle2); } } component.modules.resize = config; } deInitializeColumn(column) { this.deInitializeComponent(column); column.cells.forEach((cell) => { this.deInitializeComponent(cell); }); } deInitializeComponent(component) { var handleEl; if (component.modules.resize) { handleEl = component.modules.resize.handleEl; if (handleEl && handleEl.parentElement) { handleEl.parentElement.removeChild(handleEl); } } } resizeHandle(component, height) { if (component.modules.resize && component.modules.resize.handleEl) { component.modules.resize.handleEl.style.height = height; } } getResizingClientX(e) { var _a2, _b; if (typeof e.clientX !== "undefined") return e.clientX; const touch = this.table.options.resizableColumnGuide ? (_a2 = e.changedTouches) == null ? void 0 : _a2[0] : (_b = e.touches) == null ? void 0 : _b[0]; return touch == null ? void 0 : touch.clientX; } resize(e, column) { var x = this.getResizingClientX(e); if (typeof x !== "number" || !isFinite(x)) { console.warn("ResizeColumns: could not resolve pointer X from event", e); return; } var startDiff = x - this.startX, moveDiff = x - this.latestX, blockedBefore, blockedAfter; this.latestX = x; if (this.table.rtl) { startDiff = -startDiff; moveDiff = -moveDiff; } blockedBefore = column.width == column.minWidth || column.width == column.maxWidth; column.setWidth(this.startWidth + startDiff); blockedAfter = column.width == column.minWidth || column.width == column.maxWidth; if (moveDiff < 0) { this.nextColumn = this.initialNextColumn; } if (this.table.options.resizableColumnFit && this.nextColumn && !(blockedBefore && blockedAfter)) { let colWidth = this.nextColumn.getWidth(); if (moveDiff > 0) { if (colWidth <= this.nextColumn.minWidth) { this.nextColumn = this.nextColumn.nextColumn(); } } if (this.nextColumn) { this.nextColumn.setWidth(this.nextColumn.getWidth() - moveDiff); } } this.table.columnManager.rerenderColumns(true); if (!this.table.browserSlow && column.modules.resize && column.modules.resize.variableHeight) { column.checkCellHeights(); } } calcGuidePosition(e, column, handle2) { var mouseX = typeof e.clientX === "undefined" ? e.touches[0].clientX : e.clientX, handleX = handle2.getBoundingClientRect().x - this.table.element.getBoundingClientRect().x, tableX = this.table.element.getBoundingClientRect().x, columnX = column.element.getBoundingClientRect().left - tableX, mouseDiff = mouseX - this.startX, pos = Math.max(handleX + mouseDiff, columnX + column.minWidth); if (column.maxWidth) { pos = Math.min(pos, columnX + column.maxWidth); } return pos; } _checkResizability(column) { return column.definition.resizable; } _mouseDown(e, column, handle2) { var self = this, guideEl; this.dispatchExternal("columnResizing", column.getComponent()); if (self.table.options.resizableColumnGuide) { guideEl = document.createElement("span"); guideEl.classList.add("tabulator-col-resize-guide"); self.table.element.appendChild(guideEl); setTimeout(() => { guideEl.style.left = self.calcGuidePosition(e, column, handle2) + "px"; }); } self.table.element.classList.add("tabulator-block-select"); function mouseMove(e2) { if (self.table.options.resizableColumnGuide) { guideEl.style.left = self.calcGuidePosition(e2, column, handle2) + "px"; } else { self.resize(e2, column); } } function mouseUp(e2) { if (self.table.options.resizableColumnGuide) { self.resize(e2, column); guideEl.remove(); } if (self.startColumn.modules.edit) { self.startColumn.modules.edit.blocked = false; } if (self.table.browserSlow && column.modules.resize && column.modules.resize.variableHeight) { column.checkCellHeights(); } document.body.removeEventListener("mouseup", mouseUp); document.body.removeEventListener("mousemove", mouseMove); handle2.removeEventListener("touchmove", mouseMove); handle2.removeEventListener("touchend", mouseUp); self.table.element.classList.remove("tabulator-block-select"); if (self.startWidth !== column.getWidth()) { self.table.columnManager.verticalAlignHeaders(); self.dispatch("column-resized", column); self.dispatchExternal("columnResized", column.getComponent()); } } e.stopPropagation(); if (self.startColumn.modules.edit) { self.startColumn.modules.edit.blocked = true; } self.startX = typeof e.clientX === "undefined" ? e.touches[0].clientX : e.clientX; self.latestX = self.startX; self.startWidth = column.getWidth(); document.body.addEventListener("mousemove", mouseMove); document.body.addEventListener("mouseup", mouseUp); handle2.addEventListener("touchmove", mouseMove, {passive: true}); handle2.addEventListener("touchend", mouseUp); } } __publicField(ResizeColumns, "moduleName", "resizeColumns"); class ResizeRows extends Module { constructor(table) { super(table); this.startColumn = false; this.startY = false; this.startHeight = false; this.handle = null; this.prevHandle = null; this.registerTableOption("resizableRows", false); this.registerTableOption("resizableRowGuide", false); } initialize() { if (this.table.options.resizableRows) { this.subscribe("row-layout-after", this.initializeRow.bind(this)); } } initializeRow(row) { var self = this, rowEl = row.getElement(); var handle2 = document.createElement("div"); handle2.className = "tabulator-row-resize-handle"; var prevHandle = document.createElement("div"); prevHandle.className = "tabulator-row-resize-handle prev"; handle2.addEventListener("click", function (e) { e.stopPropagation(); }); var handleDown = function (e) { self.startRow = row; self._mouseDown(e, row, handle2); }; handle2.addEventListener("mousedown", handleDown); handle2.addEventListener("touchstart", handleDown, {passive: true}); prevHandle.addEventListener("click", function (e) { e.stopPropagation(); }); var prevHandleDown = function (e) { var prevRow = self.table.rowManager.prevDisplayRow(row); if (prevRow) { self.startRow = prevRow; self._mouseDown(e, prevRow, prevHandle); } }; prevHandle.addEventListener("mousedown", prevHandleDown); prevHandle.addEventListener("touchstart", prevHandleDown, {passive: true}); rowEl.appendChild(handle2); rowEl.appendChild(prevHandle); } resize(e, row) { row.setHeight(this.startHeight + ((typeof e.screenY === "undefined" ? e.touches[0].screenY : e.screenY) - this.startY)); } calcGuidePosition(e, row, handle2) { var mouseY = typeof e.screenY === "undefined" ? e.touches[0].screenY : e.screenY, handleY = handle2.getBoundingClientRect().y - this.table.element.getBoundingClientRect().y, tableY = this.table.element.getBoundingClientRect().y, rowY = row.element.getBoundingClientRect().top - tableY, mouseDiff = mouseY - this.startY; return Math.max(handleY + mouseDiff, rowY); } _mouseDown(e, row, handle2) { var self = this, guideEl; self.dispatchExternal("rowResizing", row.getComponent()); if (self.table.options.resizableRowGuide) { guideEl = document.createElement("span"); guideEl.classList.add("tabulator-row-resize-guide"); self.table.element.appendChild(guideEl); setTimeout(() => { guideEl.style.top = self.calcGuidePosition(e, row, handle2) + "px"; }); } self.table.element.classList.add("tabulator-block-select"); function mouseMove(e2) { if (self.table.options.resizableRowGuide) { guideEl.style.top = self.calcGuidePosition(e2, row, handle2) + "px"; } else { self.resize(e2, row); } } function mouseUp(e2) { if (self.table.options.resizableRowGuide) { self.resize(e2, row); guideEl.remove(); } document.body.removeEventListener("mouseup", mouseMove); document.body.removeEventListener("mousemove", mouseMove); handle2.removeEventListener("touchmove", mouseMove); handle2.removeEventListener("touchend", mouseUp); self.table.element.classList.remove("tabulator-block-select"); self.dispatchExternal("rowResized", row.getComponent()); } e.stopPropagation(); self.startY = typeof e.screenY === "undefined" ? e.touches[0].screenY : e.screenY; self.startHeight = row.getHeight(); document.body.addEventListener("mousemove", mouseMove); document.body.addEventListener("mouseup", mouseUp); handle2.addEventListener("touchmove", mouseMove, {passive: true}); handle2.addEventListener("touchend", mouseUp); } } __publicField(ResizeRows, "moduleName", "resizeRows"); class ResizeTable extends Module { constructor(table) { super(table); this.binding = false; this.visibilityObserver = false; this.resizeObserver = false; this.containerObserver = false; this.tableHeight = 0; this.tableWidth = 0; this.containerHeight = 0; this.containerWidth = 0; this.autoResize = false; this.visible = false; this.initialized = false; this.initialRedraw = false; this.registerTableOption("autoResize", true); } initialize() { if (this.table.options.autoResize) { var table = this.table, tableStyle; this.tableHeight = table.element.clientHeight; this.tableWidth = table.element.clientWidth; if (table.element.parentNode) { this.containerHeight = table.element.parentNode.clientHeight; this.containerWidth = table.element.parentNode.clientWidth; } if (typeof IntersectionObserver !== "undefined" && typeof ResizeObserver !== "undefined" && table.rowManager.getRenderMode() === "virtual") { this.initializeVisibilityObserver(); this.autoResize = true; this.resizeObserver = new ResizeObserver((entry) => { if (!table.browserMobile || table.browserMobile && (!table.modules.edit || table.modules.edit && !table.modules.edit.currentCell)) { var nodeHeight = Math.floor(entry[0].contentRect.height); var nodeWidth = Math.floor(entry[0].contentRect.width); if (this.tableHeight != nodeHeight || this.tableWidth != nodeWidth) { this.tableHeight = nodeHeight; this.tableWidth = nodeWidth; if (table.element.parentNode) { this.containerHeight = table.element.parentNode.clientHeight; this.containerWidth = table.element.parentNode.clientWidth; } this.redrawTable(); } } }); this.resizeObserver.observe(table.element); tableStyle = window.getComputedStyle(table.element); if (this.table.element.parentNode && !this.table.rowManager.fixedHeight && (tableStyle.getPropertyValue("max-height") || tableStyle.getPropertyValue("min-height"))) { this.containerObserver = new ResizeObserver((entry) => { if (!table.browserMobile || table.browserMobile && (!table.modules.edit || table.modules.edit && !table.modules.edit.currentCell)) { var nodeHeight = Math.floor(entry[0].contentRect.height); var nodeWidth = Math.floor(entry[0].contentRect.width); if (this.containerHeight != nodeHeight || this.containerWidth != nodeWidth) { this.containerHeight = nodeHeight; this.containerWidth = nodeWidth; this.tableHeight = table.element.clientHeight; this.tableWidth = table.element.clientWidth; } this.redrawTable(); } }); this.containerObserver.observe(this.table.element.parentNode); } this.subscribe("table-resize", this.tableResized.bind(this)); } else { this.binding = function () { if (!table.browserMobile || table.browserMobile && (!table.modules.edit || table.modules.edit && !table.modules.edit.currentCell)) { table.columnManager.rerenderColumns(true); table.redraw(); } }; window.addEventListener("resize", this.binding); } this.subscribe("table-destroy", this.clearBindings.bind(this)); } } initializeVisibilityObserver() { this.visibilityObserver = new IntersectionObserver((entries) => { this.visible = entries[entries.length - 1].isIntersecting; if (!this.initialized) { this.initialized = true; this.initialRedraw = !this.visible; } else { if (this.visible) { this.redrawTable(this.initialRedraw); this.initialRedraw = false; } } }); this.visibilityObserver.observe(this.table.element); } redrawTable(force) { if (this.initialized && this.visible) { this.table.columnManager.rerenderColumns(true); this.table.redraw(force); } } tableResized() { this.table.rowManager.redraw(); } clearBindings() { if (this.binding) { window.removeEventListener("resize", this.binding); } if (this.resizeObserver) { this.resizeObserver.unobserve(this.table.element); } if (this.visibilityObserver) { this.visibilityObserver.unobserve(this.table.element); } if (this.containerObserver) { this.containerObserver.unobserve(this.table.element.parentNode); } } } __publicField(ResizeTable, "moduleName", "resizeTable"); function responsiveCollapse(cell, formatterParams, onRendered) { var el = document.createElement("div"), config = cell.getRow()._row.modules.responsiveLayout; el.classList.add("tabulator-responsive-collapse-toggle"); el.innerHTML = ` `; cell.getElement().classList.add("tabulator-row-handle"); function toggleList(isOpen) { var collapseEl = config.element; config.open = isOpen; if (collapseEl) { if (config.open) { el.classList.add("open"); collapseEl.style.display = ""; } else { el.classList.remove("open"); collapseEl.style.display = "none"; } } } el.addEventListener("click", function (e) { e.stopImmediatePropagation(); toggleList(!config.open); cell.getTable().rowManager.adjustTableSize(); }); toggleList(config.open); return el; } var extensions$2 = { format: { formatters: { responsiveCollapse } } }; class ResponsiveLayout extends Module { constructor(table) { super(table); this.columns = []; this.hiddenColumns = []; this.mode = ""; this.index = 0; this.collapseFormatter = []; this.collapseStartOpen = true; this.collapseHandleColumn = false; this.registerTableOption("responsiveLayout", false); this.registerTableOption("responsiveLayoutCollapseStartOpen", true); this.registerTableOption("responsiveLayoutCollapseUseFormatters", true); this.registerTableOption("responsiveLayoutCollapseFormatter", false); this.registerColumnOption("responsive"); } //generate responsive columns list initialize() { if (this.table.options.responsiveLayout) { this.subscribe("column-layout", this.initializeColumn.bind(this)); this.subscribe("column-show", this.updateColumnVisibility.bind(this)); this.subscribe("column-hide", this.updateColumnVisibility.bind(this)); this.subscribe("columns-loaded", this.initializeResponsivity.bind(this)); this.subscribe("column-moved", this.initializeResponsivity.bind(this)); this.subscribe("column-add", this.initializeResponsivity.bind(this)); this.subscribe("column-delete", this.initializeResponsivity.bind(this)); this.subscribe("table-redrawing", this.tableRedraw.bind(this)); if (this.table.options.responsiveLayout === "collapse") { this.subscribe("row-data-changed", this.generateCollapsedRowContent.bind(this)); this.subscribe("row-init", this.initializeRow.bind(this)); this.subscribe("row-layout", this.layoutRow.bind(this)); } } } tableRedraw(force) { if (["fitColumns", "fitDataStretch"].indexOf(this.layoutMode()) === -1) { if (!force) { this.update(); } } } initializeResponsivity() { var columns = []; this.mode = this.table.options.responsiveLayout; this.collapseFormatter = this.table.options.responsiveLayoutCollapseFormatter || this.formatCollapsedData; this.collapseStartOpen = this.table.options.responsiveLayoutCollapseStartOpen; this.hiddenColumns = []; if (this.collapseFormatter) { this.collapseFormatter = this.collapseFormatter.bind(this.table); } this.table.columnManager.columnsByIndex.forEach((column, i) => { if (column.modules.responsive) { if (column.modules.responsive.order && column.modules.responsive.visible) { column.modules.responsive.index = i; columns.push(column); if (!column.visible && this.mode === "collapse") { this.hiddenColumns.push(column); } } } }); columns = columns.reverse(); columns = columns.sort((a, b) => { var diff = b.modules.responsive.order - a.modules.responsive.order; return diff || b.modules.responsive.index - a.modules.responsive.index; }); this.columns = columns; if (this.mode === "collapse") { this.generateCollapsedContent(); } for (let col of this.table.columnManager.columnsByIndex) { if (col.definition.formatter == "responsiveCollapse") { this.collapseHandleColumn = col; break; } } if (this.collapseHandleColumn) { if (this.hiddenColumns.length) { this.collapseHandleColumn.show(); } else { this.collapseHandleColumn.hide(); } } } //define layout information initializeColumn(column) { var def = column.getDefinition(); column.modules.responsive = { order: typeof def.responsive === "undefined" ? 1 : def.responsive, visible: def.visible === false ? false : true }; } initializeRow(row) { var el; if (row.type !== "calc") { el = document.createElement("div"); el.classList.add("tabulator-responsive-collapse"); row.modules.responsiveLayout = { element: el, open: this.collapseStartOpen }; if (!this.collapseStartOpen) { el.style.display = "none"; } } } layoutRow(row) { var rowEl = row.getElement(); if (row.modules.responsiveLayout) { rowEl.appendChild(row.modules.responsiveLayout.element); this.generateCollapsedRowContent(row); } } //update column visibility updateColumnVisibility(column, responsiveToggle) { if (!responsiveToggle && column.modules.responsive) { column.modules.responsive.visible = column.visible; this.initializeResponsivity(); } } hideColumn(column) { var colCount = this.hiddenColumns.length; column.hide(false, true); if (this.mode === "collapse") { this.hiddenColumns.unshift(column); this.generateCollapsedContent(); if (this.collapseHandleColumn && !colCount) { this.collapseHandleColumn.show(); } } } showColumn(column) { var index; column.show(false, true); column.setWidth(column.getWidth()); if (this.mode === "collapse") { index = this.hiddenColumns.indexOf(column); if (index > -1) { this.hiddenColumns.splice(index, 1); } this.generateCollapsedContent(); if (this.collapseHandleColumn && !this.hiddenColumns.length) { this.collapseHandleColumn.hide(); } } } //redraw columns to fit space update() { var working = true; while (working) { let width = this.table.modules.layout.getMode() == "fitColumns" ? this.table.columnManager.getFlexBaseWidth() : this.table.columnManager.getWidth(); let diff = (this.table.options.headerVisible ? this.table.columnManager.element.clientWidth : this.table.element.clientWidth) - width; if (diff < 0) { let column = this.columns[this.index]; if (column) { this.hideColumn(column); this.index++; } else { working = false; } } else { let column = this.columns[this.index - 1]; if (column) { if (diff > 0) { if (diff >= column.getWidth()) { this.showColumn(column); this.index--; } else { working = false; } } else { working = false; } } else { working = false; } } if (!this.table.rowManager.activeRowsCount) { this.table.rowManager.renderEmptyScroll(); } } } generateCollapsedContent() { var rows2 = this.table.rowManager.getDisplayRows(); rows2.forEach((row) => { this.generateCollapsedRowContent(row); }); } generateCollapsedRowContent(row) { var el, contents; if (row.modules.responsiveLayout) { el = row.modules.responsiveLayout.element; while (el.firstChild) el.removeChild(el.firstChild); contents = this.collapseFormatter(this.generateCollapsedRowData(row)); if (contents) { el.appendChild(contents); } row.calcHeight(true); } } generateCollapsedRowData(row) { var data = row.getData(), output = [], mockCellComponent; this.hiddenColumns.forEach((column) => { var value = column.getFieldValue(data); if (column.definition.title && column.field) { if (column.modules.format && this.table.options.responsiveLayoutCollapseUseFormatters) { let onRendered = function (callback) { callback(); }; mockCellComponent = { value: false, data: {}, getValue: function () { return value; }, getData: function () { return data; }, getType: function () { return "cell"; }, getElement: function () { return document.createElement("div"); }, getRow: function () { return row.getComponent(); }, getColumn: function () { return column.getComponent(); }, getTable: () => { return this.table; } }; output.push({ field: column.field, title: column.definition.title, value: column.modules.format.formatter.call(this.table.modules.format, mockCellComponent, column.modules.format.params, onRendered) }); } else { output.push({ field: column.field, title: column.definition.title, value }); } } }); return output; } formatCollapsedData(data) { var list2 = document.createElement("table"); data.forEach((item) => { var row = document.createElement("tr"); var titleData = document.createElement("td"); var valueData = document.createElement("td"); var node_content; var titleHighlight = document.createElement("strong"); titleData.appendChild(titleHighlight); this.modules.localize.bind("columns|" + item.field, function (text) { titleHighlight.innerHTML = text || item.title; }); if (item.value instanceof Node) { node_content = document.createElement("div"); node_content.appendChild(item.value); valueData.appendChild(node_content); } else { valueData.innerHTML = item.value; } row.appendChild(titleData); row.appendChild(valueData); list2.appendChild(row); }); return Object.keys(data).length ? list2 : ""; } } __publicField(ResponsiveLayout, "moduleName", "responsiveLayout"); __publicField(ResponsiveLayout, "moduleExtensions", extensions$2); function rowSelection(cell, formatterParams, onRendered) { var checkbox = document.createElement("input"); var blocked = false; checkbox.type = "checkbox"; checkbox.setAttribute("aria-label", "Select Row"); if (this.table.modExists("selectRow", true)) { checkbox.addEventListener("click", (e) => { e.stopPropagation(); }); if (typeof cell.getRow == "function") { var row = cell.getRow(); if (row instanceof RowComponent) { checkbox.addEventListener("change", (e) => { if (this.table.options.selectableRowsRangeMode === "click") { if (!blocked) { row.toggleSelect(); } else { blocked = false; } } else { row.toggleSelect(); } }); if (this.table.options.selectableRowsRangeMode === "click") { checkbox.addEventListener("click", (e) => { blocked = true; this.table.modules.selectRow.handleComplexRowClick(row._row, e); }); } checkbox.checked = row.isSelected && row.isSelected(); this.table.modules.selectRow.registerRowSelectCheckbox(row, checkbox); } else { checkbox = ""; } } else { checkbox.addEventListener("change", (e) => { if (this.table.modules.selectRow.selectedRows.length) { this.table.deselectRow(); } else { this.table.selectRow(formatterParams.rowRange); } }); this.table.modules.selectRow.registerHeaderSelectCheckbox(checkbox); } } return checkbox; } var extensions$1 = { format: { formatters: { rowSelection } } }; class SelectRow extends Module { constructor(table) { super(table); this.selecting = false; this.lastClickedRow = false; this.selectPrev = []; this.selectedRows = []; this.headerCheckboxElement = null; this.registerTableOption("selectableRows", "highlight"); this.registerTableOption("selectableRowsRangeMode", "drag"); this.registerTableOption("selectableRowsRollingSelection", true); this.registerTableOption("selectableRowsPersistence", true); this.registerTableOption("selectableRowsCheck", function (data, row) { return true; }); this.registerTableFunction("selectRow", this.selectRows.bind(this)); this.registerTableFunction("deselectRow", this.deselectRows.bind(this)); this.registerTableFunction("toggleSelectRow", this.toggleRow.bind(this)); this.registerTableFunction("getSelectedRows", this.getSelectedRows.bind(this)); this.registerTableFunction("getSelectedData", this.getSelectedData.bind(this)); this.registerComponentFunction("row", "select", this.selectRows.bind(this)); this.registerComponentFunction("row", "deselect", this.deselectRows.bind(this)); this.registerComponentFunction("row", "toggleSelect", this.toggleRow.bind(this)); this.registerComponentFunction("row", "isSelected", this.isRowSelected.bind(this)); } initialize() { this.deprecatedOptionsCheck(); if (this.table.options.selectableRows === "highlight" && this.table.options.selectableRange) { this.table.options.selectableRows = false; } if (this.table.options.selectableRows !== false) { this.subscribe("row-init", this.initializeRow.bind(this)); this.subscribe("row-deleting", this.rowDeleted.bind(this)); this.subscribe("rows-wipe", this.clearSelectionData.bind(this)); this.subscribe("rows-retrieve", this.rowRetrieve.bind(this)); if (this.table.options.selectableRows && !this.table.options.selectableRowsPersistence) { this.subscribe("data-refreshing", this.deselectRows.bind(this)); } } } deprecatedOptionsCheck() { } rowRetrieve(type, prevValue) { return type === "selected" ? this.selectedRows : prevValue; } rowDeleted(row) { this._deselectRow(row, true); } clearSelectionData(silent) { var prevSelected = this.selectedRows.length; this.selecting = false; this.lastClickedRow = false; this.selectPrev = []; this.selectedRows = []; if (prevSelected && silent !== true) { this._rowSelectionChanged(); } } initializeRow(row) { var self = this, selectable = self.checkRowSelectability(row), element = row.getElement(); var endSelect = function () { setTimeout(function () { self.selecting = false; }, 50); document.body.removeEventListener("mouseup", endSelect); }; row.modules.select = {selected: false}; element.classList.toggle("tabulator-selectable", selectable); element.classList.toggle("tabulator-unselectable", !selectable); if (self.checkRowSelectability(row)) { if (self.table.options.selectableRows && self.table.options.selectableRows != "highlight") { if (self.table.options.selectableRowsRangeMode === "click") { element.addEventListener("click", this.handleComplexRowClick.bind(this, row)); } else { element.addEventListener("click", function (e) { if (!self.table.modExists("edit") || !self.table.modules.edit.getCurrentCell()) { self.table._clearSelection(); } if (!self.selecting) { self.toggleRow(row); } }); element.addEventListener("mousedown", function (e) { if (e.shiftKey) { self.table._clearSelection(); self.selecting = true; self.selectPrev = []; document.body.addEventListener("mouseup", endSelect); document.body.addEventListener("keyup", endSelect); self.toggleRow(row); return false; } }); element.addEventListener("mouseenter", function (e) { if (self.selecting) { self.table._clearSelection(); self.toggleRow(row); if (self.selectPrev[1] == row) { self.toggleRow(self.selectPrev[0]); } } }); element.addEventListener("mouseout", function (e) { if (self.selecting) { self.table._clearSelection(); self.selectPrev.unshift(row); } }); } } } } handleComplexRowClick(row, e) { if (e.shiftKey) { this.table._clearSelection(); this.lastClickedRow = this.lastClickedRow || row; var lastClickedRowIdx = this.table.rowManager.getDisplayRowIndex(this.lastClickedRow); var rowIdx = this.table.rowManager.getDisplayRowIndex(row); var fromRowIdx = lastClickedRowIdx <= rowIdx ? lastClickedRowIdx : rowIdx; var toRowIdx = lastClickedRowIdx >= rowIdx ? lastClickedRowIdx : rowIdx; var rows2 = this.table.rowManager.getDisplayRows().slice(0); var toggledRows = rows2.splice(fromRowIdx, toRowIdx - fromRowIdx + 1); if (e.ctrlKey || e.metaKey) { toggledRows.forEach((toggledRow) => { if (toggledRow !== this.lastClickedRow) { if (this.table.options.selectableRows !== true && !this.isRowSelected(row)) { if (this.selectedRows.length < this.table.options.selectableRows) { this.toggleRow(toggledRow); } } else { this.toggleRow(toggledRow); } } }); this.lastClickedRow = row; } else { this.deselectRows(void 0, true); if (this.table.options.selectableRows !== true) { if (toggledRows.length > this.table.options.selectableRows) { toggledRows = toggledRows.slice(0, this.table.options.selectableRows); } } this.selectRows(toggledRows); } this.table._clearSelection(); } else if (e.ctrlKey || e.metaKey) { this.toggleRow(row); this.lastClickedRow = row; } else { this.deselectRows(void 0, true); if (this.selectedRows.length === 1 && this.isRowSelected(row)) ; else { this.selectRows(row); } this.lastClickedRow = row; } } checkRowSelectability(row) { if (row && row.type === "row") { return this.table.options.selectableRowsCheck.call(this.table, row.getComponent()); } return false; } //toggle row selection toggleRow(row) { if (this.checkRowSelectability(row)) { if (row.modules.select && row.modules.select.selected) { this._deselectRow(row); } else { this._selectRow(row); } } } //select a number of rows selectRows(rows2) { var changes = [], rowMatch, change; switch (typeof rows2) { case "undefined": rowMatch = this.table.rowManager.rows; break; case "number": rowMatch = this.table.rowManager.findRow(rows2); break; case "string": rowMatch = this.table.rowManager.findRow(rows2); if (!rowMatch) { rowMatch = this.table.rowManager.getRows(rows2); } break; default: rowMatch = rows2; break; } if (Array.isArray(rowMatch)) { if (rowMatch.length) { rowMatch.forEach((row) => { change = this._selectRow(row, true, true); if (change) { changes.push(change); } }); this._rowSelectionChanged(false, changes); } } else { if (rowMatch) { this._selectRow(rowMatch, false, true); } } } //select an individual row _selectRow(rowInfo, silent, force) { if (!isNaN(this.table.options.selectableRows) && this.table.options.selectableRows !== true && !force) { if (this.selectedRows.length >= this.table.options.selectableRows) { if (this.table.options.selectableRowsRollingSelection) { this._deselectRow(this.selectedRows[0]); } else { return false; } } } var row = this.table.rowManager.findRow(rowInfo); if (row) { if (this.selectedRows.indexOf(row) == -1) { row.getElement().classList.add("tabulator-selected"); if (!row.modules.select) { row.modules.select = {}; } row.modules.select.selected = true; if (row.modules.select.checkboxEl) { row.modules.select.checkboxEl.checked = true; } this.selectedRows.push(row); if (this.table.options.dataTreeSelectPropagate) { this.childRowSelection(row, true); } this.dispatchExternal("rowSelected", row.getComponent()); this._rowSelectionChanged(silent, row); return row; } } else { if (!silent) { console.warn("Selection Error - No such row found, ignoring selection:" + rowInfo); } } } isRowSelected(row) { return this.selectedRows.indexOf(row) !== -1; } //deselect a number of rows deselectRows(rows2, silent) { var changes = [], rowMatch, change; switch (typeof rows2) { case "undefined": rowMatch = Object.assign([], this.selectedRows); break; case "number": rowMatch = this.table.rowManager.findRow(rows2); break; case "string": rowMatch = this.table.rowManager.findRow(rows2); if (!rowMatch) { rowMatch = this.table.rowManager.getRows(rows2); } break; default: rowMatch = rows2; break; } if (Array.isArray(rowMatch)) { if (rowMatch.length) { rowMatch.forEach((row) => { change = this._deselectRow(row, true, true); if (change) { changes.push(change); } }); this._rowSelectionChanged(silent, [], changes); } } else { if (rowMatch) { this._deselectRow(rowMatch, silent, true); } } } //deselect an individual row _deselectRow(rowInfo, silent) { var self = this, row = self.table.rowManager.findRow(rowInfo), index, element; if (row) { index = self.selectedRows.findIndex(function (selectedRow) { return selectedRow == row; }); if (index > -1) { element = row.getElement(); if (element) { element.classList.remove("tabulator-selected"); } if (!row.modules.select) { row.modules.select = {}; } row.modules.select.selected = false; if (row.modules.select.checkboxEl) { row.modules.select.checkboxEl.checked = false; } self.selectedRows.splice(index, 1); if (this.table.options.dataTreeSelectPropagate) { this.childRowSelection(row, false); } this.dispatchExternal("rowDeselected", row.getComponent()); self._rowSelectionChanged(silent, void 0, row); return row; } } else { if (!silent) { console.warn("Deselection Error - No such row found, ignoring selection:" + rowInfo); } } } getSelectedData() { var data = []; this.selectedRows.forEach(function (row) { data.push(row.getData()); }); return data; } getSelectedRows() { var rows2 = []; this.selectedRows.forEach(function (row) { rows2.push(row.getComponent()); }); return rows2; } _rowSelectionChanged(silent, selected = [], deselected = []) { if (this.headerCheckboxElement) { if (this.selectedRows.length === 0) { this.headerCheckboxElement.checked = false; this.headerCheckboxElement.indeterminate = false; } else if (this.table.rowManager.rows.length === this.selectedRows.length) { this.headerCheckboxElement.checked = true; this.headerCheckboxElement.indeterminate = false; } else { this.headerCheckboxElement.indeterminate = true; this.headerCheckboxElement.checked = false; } } if (!silent) { if (!Array.isArray(selected)) { selected = [selected]; } selected = selected.map((row) => row.getComponent()); if (!Array.isArray(deselected)) { deselected = [deselected]; } deselected = deselected.map((row) => row.getComponent()); this.dispatchExternal("rowSelectionChanged", this.getSelectedData(), this.getSelectedRows(), selected, deselected); } } registerRowSelectCheckbox(row, element) { if (!row._row.modules.select) { row._row.modules.select = {}; } row._row.modules.select.checkboxEl = element; } registerHeaderSelectCheckbox(element) { this.headerCheckboxElement = element; } childRowSelection(row, select) { var children = this.table.modules.dataTree.getChildren(row, true, true); if (select) { for (let child of children) { this._selectRow(child, true); } } else { for (let child of children) { this._deselectRow(child, true); } } } } __publicField(SelectRow, "moduleName", "selectRow"); __publicField(SelectRow, "moduleExtensions", extensions$1); class RangeComponent { constructor(range2) { this._range = range2; return new Proxy(this, { get: function (target, name2, receiver) { if (typeof target[name2] !== "undefined") { return target[name2]; } else { return target._range.table.componentFunctionBinder.handle("range", target._range, name2); } } }); } getElement() { return this._range.element; } getData() { return this._range.getData(); } getCells() { return this._range.getCells(true, true); } getStructuredCells() { return this._range.getStructuredCells(); } getRows() { return this._range.getRows().map((row) => row.getComponent()); } getColumns() { return this._range.getColumns().map((column) => column.getComponent()); } getBounds() { return this._range.getBounds(); } getTopEdge() { return this._range.top; } getBottomEdge() { return this._range.bottom; } getLeftEdge() { return this._range.left; } getRightEdge() { return this._range.right; } setBounds(start, end) { if (this._range.destroyedGuard("setBounds")) { this._range.setBounds(start ? start._cell : start, end ? end._cell : end); } } setStartBound(start) { if (this._range.destroyedGuard("setStartBound")) { this._range.setEndBound(start ? start._cell : start); this._range.rangeManager.layoutElement(); } } setEndBound(end) { if (this._range.destroyedGuard("setEndBound")) { this._range.setEndBound(end ? end._cell : end); this._range.rangeManager.layoutElement(); } } clearValues() { if (this._range.destroyedGuard("clearValues")) { this._range.clearValues(); } } remove() { if (this._range.destroyedGuard("remove")) { this._range.destroy(true); } } } class Range extends CoreFeature { constructor(table, rangeManager, start, end) { super(table); this.rangeManager = rangeManager; this.element = null; this.initialized = false; this.initializing = { start: false, end: false }; this.destroyed = false; this.top = 0; this.bottom = 0; this.left = 0; this.right = 0; this.table = table; this.start = {row: void 0, col: void 0}; this.end = {row: void 0, col: void 0}; if (this.rangeManager.rowHeader) { this.left = 1; this.right = 1; this.start.col = 1; this.end.col = 1; } this.initElement(); setTimeout(() => { this.initBounds(start, end); }); } initElement() { this.element = document.createElement("div"); this.element.classList.add("tabulator-range"); } initBounds(start, end) { this._updateMinMax(); if (start) { this.setBounds(start, end || start); } } /////////////////////////////////// /////// Boundary Setup /////// /////////////////////////////////// setStart(row, col) { if (this.start.row !== row || this.start.col !== col) { this.start.row = row; this.start.col = col; this.initializing.start = true; this._updateMinMax(); } } setEnd(row, col) { if (this.end.row !== row || this.end.col !== col) { this.end.row = row; this.end.col = col; this.initializing.end = true; this._updateMinMax(); } } setBounds(start, end, visibleRows) { if (start) { this.setStartBound(start); } this.setEndBound(end || start); this.rangeManager.layoutElement(visibleRows); } setStartBound(element) { var row, col; if (element.type === "column") { if (this.rangeManager.columnSelection) { this.setStart(0, element.getPosition() - 1); } } else { row = element.row.position - 1; col = element.column.getPosition() - 1; if (element.column === this.rangeManager.rowHeader) { this.setStart(row, 1); } else { this.setStart(row, col); } } } setEndBound(element) { var rowsCount = this._getTableRows().length, row, col, isRowHeader; if (element.type === "column") { if (this.rangeManager.columnSelection) { if (this.rangeManager.selecting === "column") { this.setEnd(rowsCount - 1, element.getPosition() - 1); } else if (this.rangeManager.selecting === "cell") { this.setEnd(0, element.getPosition() - 1); } } } else { row = element.row.position - 1; col = element.column.getPosition() - 1; isRowHeader = element.column === this.rangeManager.rowHeader; if (this.rangeManager.selecting === "row") { this.setEnd(row, this._getTableColumns().length - 1); } else if (this.rangeManager.selecting !== "row" && isRowHeader) { this.setEnd(row, 0); } else if (this.rangeManager.selecting === "column") { this.setEnd(rowsCount - 1, col); } else { this.setEnd(row, col); } } } _updateMinMax() { this.top = Math.min(this.start.row, this.end.row); this.bottom = Math.max(this.start.row, this.end.row); this.left = Math.min(this.start.col, this.end.col); this.right = Math.max(this.start.col, this.end.col); if (this.initialized) { this.dispatchExternal("rangeChanged", this.getComponent()); } else { if (this.initializing.start && this.initializing.end) { this.initialized = true; this.dispatchExternal("rangeAdded", this.getComponent()); } } } _getTableColumns() { return this.table.columnManager.getVisibleColumnsByIndex(); } _getTableRows() { return this.table.rowManager.getDisplayRows().filter((row) => row.type === "row"); } /////////////////////////////////// /////// Rendering /////// /////////////////////////////////// layout() { var _vDomTop = this.table.rowManager.renderer.vDomTop, _vDomBottom = this.table.rowManager.renderer.vDomBottom, _vDomLeft = this.table.columnManager.renderer.leftCol, _vDomRight = this.table.columnManager.renderer.rightCol, top, bottom, left, right, topLeftCell, bottomRightCell, topLeftCellEl, bottomRightCellEl, topLeftRowEl, bottomRightRowEl; if (this.table.options.renderHorizontal === "virtual" && this.rangeManager.rowHeader) { _vDomRight += 1; } if (_vDomTop == null) { _vDomTop = 0; } if (_vDomBottom == null) { _vDomBottom = Infinity; } if (_vDomLeft == null) { _vDomLeft = 0; } if (_vDomRight == null) { _vDomRight = Infinity; } if (this.overlaps(_vDomLeft, _vDomTop, _vDomRight, _vDomBottom)) { top = Math.max(this.top, _vDomTop); bottom = Math.min(this.bottom, _vDomBottom); left = Math.max(this.left, _vDomLeft); right = Math.min(this.right, _vDomRight); topLeftCell = this.rangeManager.getCell(top, left); bottomRightCell = this.rangeManager.getCell(bottom, right); topLeftCellEl = topLeftCell.getElement(); bottomRightCellEl = bottomRightCell.getElement(); topLeftRowEl = topLeftCell.row.getElement(); bottomRightRowEl = bottomRightCell.row.getElement(); this.element.classList.add("tabulator-range-active"); if (this.table.rtl) { this.element.style.right = topLeftRowEl.offsetWidth - topLeftCellEl.offsetLeft - topLeftCellEl.offsetWidth + "px"; this.element.style.width = topLeftCellEl.offsetLeft + topLeftCellEl.offsetWidth - bottomRightCellEl.offsetLeft + "px"; } else { this.element.style.left = topLeftRowEl.offsetLeft + topLeftCellEl.offsetLeft + "px"; this.element.style.width = bottomRightCellEl.offsetLeft + bottomRightCellEl.offsetWidth - topLeftCellEl.offsetLeft + "px"; } this.element.style.top = topLeftRowEl.offsetTop + "px"; this.element.style.height = bottomRightRowEl.offsetTop + bottomRightRowEl.offsetHeight - topLeftRowEl.offsetTop + "px"; } } atTopLeft(cell) { return cell.row.position - 1 === this.top && cell.column.getPosition() - 1 === this.left; } atBottomRight(cell) { return cell.row.position - 1 === this.bottom && cell.column.getPosition() - 1 === this.right; } occupies(cell) { return this.occupiesRow(cell.row) && this.occupiesColumn(cell.column); } occupiesRow(row) { return this.top <= row.position - 1 && row.position - 1 <= this.bottom; } occupiesColumn(col) { return this.left <= col.getPosition() - 1 && col.getPosition() - 1 <= this.right; } overlaps(left, top, right, bottom) { if (this.left > right || left > this.right || (this.top > bottom || top > this.bottom)) { return false; } return true; } getData() { var data = [], rows2 = this.getRows(), columns = this.getColumns(); rows2.forEach((row) => { var rowData = row.getData(), result = {}; columns.forEach((column) => { result[column.field] = rowData[column.field]; }); data.push(result); }); return data; } getCells(structured, component) { var cells = [], rows2 = this.getRows(), columns = this.getColumns(); if (structured) { cells = rows2.map((row) => { var arr = []; row.getCells().forEach((cell) => { if (columns.includes(cell.column)) { arr.push(component ? cell.getComponent() : cell); } }); return arr; }); } else { rows2.forEach((row) => { row.getCells().forEach((cell) => { if (columns.includes(cell.column)) { cells.push(component ? cell.getComponent() : cell); } }); }); } return cells; } getStructuredCells() { return this.getCells(true, true); } getRows() { return this._getTableRows().slice(this.top, this.bottom + 1); } getColumns() { return this._getTableColumns().slice(this.left, this.right + 1); } clearValues() { var cells = this.getCells(); var clearValue = this.table.options.selectableRangeClearCellsValue; this.table.blockRedraw(); cells.forEach((cell) => { cell.setValue(clearValue); }); this.table.restoreRedraw(); } getBounds(component) { var cells = this.getCells(false, component), output = { start: null, end: null }; if (cells.length) { output.start = cells[0]; output.end = cells[cells.length - 1]; } else { console.warn("No bounds defined on range"); } return output; } getComponent() { if (!this.component) { this.component = new RangeComponent(this); } return this.component; } destroy(notify) { this.destroyed = true; this.element.remove(); if (notify) { this.rangeManager.rangeRemoved(this); } if (this.initialized) { this.dispatchExternal("rangeRemoved", this.getComponent()); } } destroyedGuard(func) { if (this.destroyed) { console.warn("You cannot call the " + func + " function on a destroyed range"); } return !this.destroyed; } } var bindings = { rangeJumpUp: ["ctrl + 38", "meta + 38"], rangeJumpDown: ["ctrl + 40", "meta + 40"], rangeJumpLeft: ["ctrl + 37", "meta + 37"], rangeJumpRight: ["ctrl + 39", "meta + 39"], rangeExpandUp: "shift + 38", rangeExpandDown: "shift + 40", rangeExpandLeft: "shift + 37", rangeExpandRight: "shift + 39", rangeExpandJumpUp: ["ctrl + shift + 38", "meta + shift + 38"], rangeExpandJumpDown: ["ctrl + shift + 40", "meta + shift + 40"], rangeExpandJumpLeft: ["ctrl + shift + 37", "meta + shift + 37"], rangeExpandJumpRight: ["ctrl + shift + 39", "meta + shift + 39"] }; var actions = { rangeJumpLeft: function (e) { this.dispatch("keybinding-nav-range", e, "left", true, false); }, rangeJumpRight: function (e) { this.dispatch("keybinding-nav-range", e, "right", true, false); }, rangeJumpUp: function (e) { this.dispatch("keybinding-nav-range", e, "up", true, false); }, rangeJumpDown: function (e) { this.dispatch("keybinding-nav-range", e, "down", true, false); }, rangeExpandLeft: function (e) { this.dispatch("keybinding-nav-range", e, "left", false, true); }, rangeExpandRight: function (e) { this.dispatch("keybinding-nav-range", e, "right", false, true); }, rangeExpandUp: function (e) { this.dispatch("keybinding-nav-range", e, "up", false, true); }, rangeExpandDown: function (e) { this.dispatch("keybinding-nav-range", e, "down", false, true); }, rangeExpandJumpLeft: function (e) { this.dispatch("keybinding-nav-range", e, "left", true, true); }, rangeExpandJumpRight: function (e) { this.dispatch("keybinding-nav-range", e, "right", true, true); }, rangeExpandJumpUp: function (e) { this.dispatch("keybinding-nav-range", e, "up", true, true); }, rangeExpandJumpDown: function (e) { this.dispatch("keybinding-nav-range", e, "down", true, true); } }; var pasteActions = { range: function (data) { var rows2 = [], range2 = this.table.modules.selectRange.activeRange, singleCell = false, bounds, startCell, startRow, rowWidth, dataLength; dataLength = data.length; if (range2) { bounds = range2.getBounds(); startCell = bounds.start; if (bounds.start === bounds.end) { singleCell = true; } if (startCell) { rows2 = this.table.rowManager.activeRows.slice(); startRow = rows2.indexOf(startCell.row); if (singleCell) { rowWidth = data.length; } else { rowWidth = rows2.indexOf(bounds.end.row) - startRow + 1; } if (startRow > -1) { this.table.blockRedraw(); rows2 = rows2.slice(startRow, startRow + rowWidth); rows2.forEach((row, i) => { row.updateData(data[i % dataLength]); }); this.table.restoreRedraw(); } } } return rows2; } }; var pasteParsers = { range: function (clipboard) { var data = [], rows2 = [], range2 = this.table.modules.selectRange.activeRange, singleCell = false, bounds, startCell, colWidth, columnMap, startCol; if (range2) { bounds = range2.getBounds(); startCell = bounds.start; if (bounds.start === bounds.end) { singleCell = true; } if (startCell) { clipboard = clipboard.split("\n"); clipboard.forEach(function (row) { data.push(row.split(" ")); }); if (data.length) { columnMap = this.table.columnManager.getVisibleColumnsByIndex(); startCol = columnMap.indexOf(startCell.column); if (startCol > -1) { if (singleCell) { colWidth = data[0].length; } else { colWidth = columnMap.indexOf(bounds.end.column) - startCol + 1; } columnMap = columnMap.slice(startCol, startCol + colWidth); data.forEach((item) => { var row = {}; var itemLength = item.length; columnMap.forEach(function (col, i) { row[col.field] = item[i % itemLength]; }); rows2.push(row); }); return rows2; } } } } return false; } }; var columnLookups = { range: function () { var columns = this.modules.selectRange.selectedColumns(); if (this.columnManager.rowHeader) { columns.unshift(this.columnManager.rowHeader); } return columns; } }; var rowLookups = { range: function () { return this.modules.selectRange.selectedRows(); } }; var extensions = { keybindings: { bindings, actions }, clipboard: { pasteActions, pasteParsers }, export: { columnLookups, rowLookups } }; class SelectRange extends Module { constructor(table) { super(table); this.selecting = "cell"; this.mousedown = false; this.ranges = []; this.overlay = null; this.rowHeader = null; this.layoutChangeTimeout = null; this.columnSelection = false; this.rowSelection = false; this.maxRanges = 0; this.activeRange = false; this.blockKeydown = false; this.keyDownEvent = this._handleKeyDown.bind(this); this.mouseUpEvent = this._handleMouseUp.bind(this); this.registerTableOption("selectableRange", false); this.registerTableOption("selectableRangeColumns", false); this.registerTableOption("selectableRangeRows", false); this.registerTableOption("selectableRangeClearCells", false); this.registerTableOption("selectableRangeClearCellsValue", void 0); this.registerTableOption("selectableRangeAutoFocus", true); this.registerTableOption("selectableRangeInitializeDefault", true); this.registerTableOption("selectableRangeBlurEditOnNavigate", void 0); this.registerTableFunction("getRangesData", this.getRangesData.bind(this)); this.registerTableFunction("getRanges", this.getRanges.bind(this)); this.registerTableFunction("addRange", this.addRangeFromComponent.bind(this)); this.registerComponentFunction("cell", "getRanges", this.cellGetRanges.bind(this)); this.registerComponentFunction("row", "getRanges", this.rowGetRanges.bind(this)); this.registerComponentFunction("column", "getRanges", this.colGetRanges.bind(this)); } /////////////////////////////////// /////// Initialization /////// /////////////////////////////////// initialize() { if (this.options("selectableRange")) { if (!this.options("selectableRows")) { this.maxRanges = this.options("selectableRange"); this.initializeTable(); this.initializeWatchers(); } else { console.warn("SelectRange functionality cannot be used in conjunction with row selection"); } if (this.options("columns").findIndex((column) => column.frozen) > 0) { console.warn("Having frozen column in arbitrary position with selectRange option may result in unpredictable behavior."); } if (this.options("columns").filter((column) => column.frozen) > 1) { console.warn("Having multiple frozen columns with selectRange option may result in unpredictable behavior."); } } this.subscribe("edit-nav-disabled", () => { return true; }); } initializeTable() { this.overlay = document.createElement("div"); this.overlay.classList.add("tabulator-range-overlay"); this.rangeContainer = document.createElement("div"); this.rangeContainer.classList.add("tabulator-range-container"); this.activeRangeCellElement = document.createElement("div"); this.activeRangeCellElement.classList.add("tabulator-range-cell-active"); this.overlay.appendChild(this.rangeContainer); this.overlay.appendChild(this.activeRangeCellElement); this.table.rowManager.element.addEventListener("keydown", this.keyDownEvent); this.setDefaultRange(); this.table.rowManager.element.appendChild(this.overlay); this.table.columnManager.element.setAttribute("tabindex", 0); this.table.element.classList.add("tabulator-ranges"); } initializeWatchers() { this.columnSelection = this.options("selectableRangeColumns"); this.rowSelection = this.options("selectableRangeRows"); this.subscribe("column-init", this.initializeColumn.bind(this)); this.subscribe("column-mousedown", this.handleColumnMouseDown.bind(this)); this.subscribe("column-mousemove", this.handleColumnMouseMove.bind(this)); this.subscribe("column-resized", this.handleColumnResized.bind(this)); this.subscribe("column-moving", this.handleColumnMoving.bind(this)); this.subscribe("column-moved", this.handleColumnMoved.bind(this)); this.subscribe("column-width", this.layoutChange.bind(this)); this.subscribe("column-height", this.layoutChange.bind(this)); this.subscribe("column-resized", this.layoutChange.bind(this)); this.subscribe("columns-loaded", this.updateHeaderColumn.bind(this)); this.subscribe("cell-height", this.layoutChange.bind(this)); this.subscribe("cell-rendered", this.renderCell.bind(this)); this.subscribe("cell-mousedown", this.handleCellMouseDown.bind(this)); this.subscribe("cell-mousemove", this.handleCellMouseMove.bind(this)); this.subscribe("cell-click", this.handleCellClick.bind(this)); this.subscribe("cell-editing", this.handleEditingCell.bind(this)); this.subscribe("page-changed", this.redraw.bind(this)); this.subscribe("scroll-vertical", this.layoutChange.bind(this)); this.subscribe("scroll-horizontal", this.layoutChange.bind(this)); this.subscribe("data-destroy", this.tableDestroyed.bind(this)); this.subscribe("data-processed", this.setDefaultRange.bind(this)); this.subscribe("table-layout", this.layoutElement.bind(this)); this.subscribe("table-redraw", this.redraw.bind(this)); this.subscribe("table-destroy", this.tableDestroyed.bind(this)); this.subscribe("edit-editor-clear", this.finishEditingCell.bind(this)); this.subscribe("edit-blur", this.restoreFocus.bind(this)); this.subscribe("keybinding-nav-prev", this.keyNavigate.bind(this, "prev")); this.subscribe("keybinding-nav-next", this.keyNavigate.bind(this, "next")); this.subscribe("keybinding-nav-left", this.keyNavigate.bind(this, "left")); this.subscribe("keybinding-nav-right", this.keyNavigate.bind(this, "right")); this.subscribe("keybinding-nav-up", this.keyNavigate.bind(this, "up")); this.subscribe("keybinding-nav-down", this.keyNavigate.bind(this, "down")); this.subscribe("keybinding-nav-range", this.keyNavigateRange.bind(this)); } initializeColumn(column) { if (this.columnSelection && column.definition.headerSort && this.options("headerSortClickElement") !== "icon") { console.warn("Using column headerSort with selectableRangeColumns option may result in unpredictable behavior. Consider using headerSortClickElement: 'icon'."); } } updateHeaderColumn() { var frozenCols; if (this.rowSelection) { this.rowHeader = this.table.columnManager.getVisibleColumnsByIndex()[0]; if (this.rowHeader) { this.rowHeader.definition.cssClass = this.rowHeader.definition.cssClass + " tabulator-range-row-header"; if (this.rowHeader.definition.headerSort) { console.warn("Using column headerSort with selectableRangeRows option may result in unpredictable behavior"); } if (this.rowHeader.definition.editor) { console.warn("Using column editor with selectableRangeRows option may result in unpredictable behavior"); } } } if (this.table.modules.frozenColumns && this.table.modules.frozenColumns.active) { frozenCols = this.table.modules.frozenColumns.getFrozenColumns(); if (frozenCols.length > 1 || frozenCols.length === 1 && frozenCols[0] !== this.rowHeader) { console.warn("Using frozen columns that are not the range header in combination with the selectRange option may result in unpredictable behavior"); } } } /////////////////////////////////// /////// Table Functions /////// /////////////////////////////////// getRanges() { return this.ranges.map((range2) => range2.getComponent()); } getRangesData() { return this.ranges.map((range2) => range2.getData()); } addRangeFromComponent(start, end) { start = start ? start._cell : null; end = end ? end._cell : null; return this.addRange(start, end); } /////////////////////////////////// /////// Component Functions /////// /////////////////////////////////// cellGetRanges(cell) { var ranges = []; if (cell.column === this.rowHeader) { ranges = this.ranges.filter((range2) => range2.occupiesRow(cell.row)); } else { ranges = this.ranges.filter((range2) => range2.occupies(cell)); } return ranges.map((range2) => range2.getComponent()); } rowGetRanges(row) { var ranges = this.ranges.filter((range2) => range2.occupiesRow(row)); return ranges.map((range2) => range2.getComponent()); } colGetRanges(col) { var ranges = this.ranges.filter((range2) => range2.occupiesColumn(col)); return ranges.map((range2) => range2.getComponent()); } /////////////////////////////////// ////////// Event Handlers ///////// /////////////////////////////////// _handleMouseUp(e) { this.mousedown = false; document.removeEventListener("mouseup", this.mouseUpEvent); } _handleKeyDown(e) { if (!this.blockKeydown && (!this.table.modules.edit || this.table.modules.edit && !this.table.modules.edit.currentCell)) { if (e.key === "Enter") { if (this.table.modules.edit && this.table.modules.edit.currentCell) { return; } var activeCell = this.getActiveCell(); if (!activeCell) { return; } this.table.modules.edit.editCell(activeCell); e.preventDefault(); } if ((e.key === "Backspace" || e.key === "Delete") && this.options("selectableRangeClearCells")) { if (this.activeRange) { this.activeRange.clearValues(); } } } } initializeFocus(cell) { var range2; this.restoreFocus(); try { if (document.selection) { range2 = document.body.createTextRange(); range2.moveToElementText(cell.getElement()); range2.select(); } else if (window.getSelection) { range2 = document.createRange(); range2.selectNode(cell.getElement()); window.getSelection().removeAllRanges(); window.getSelection().addRange(range2); } } catch (e) { } } restoreFocus(element) { this.table.rowManager.element.focus(); return true; } /////////////////////////////////// ////// Column Functionality /////// /////////////////////////////////// handleColumnResized(column) { var selected; if (this.selecting !== "column" && this.selecting !== "all") { return; } selected = this.ranges.some((range2) => range2.occupiesColumn(column)); if (!selected) { return; } this.ranges.forEach((range2) => { var selectedColumns = range2.getColumns(true); selectedColumns.forEach((selectedColumn) => { if (selectedColumn !== column) { selectedColumn.setWidth(column.width); } }); }); } handleColumnMoving(_event, column) { this.resetRanges().setBounds(column); this.overlay.style.visibility = "hidden"; } handleColumnMoved(from, _to, _after) { this.activeRange.setBounds(from); this.layoutElement(); } handleColumnMouseDown(event, column) { if (event.button === 2 && (this.selecting === "column" || this.selecting === "all") && this.activeRange.occupiesColumn(column)) { return; } if (this.table.options.movableColumns && this.selecting === "column" && this.activeRange.occupiesColumn(column)) { return; } this.mousedown = true; document.addEventListener("mouseup", this.mouseUpEvent); this.newSelection(event, column); } handleColumnMouseMove(e, column) { if (column === this.rowHeader || !this.mousedown || this.selecting === "all") { return; } this.activeRange.setBounds(false, column, true); } /////////////////////////////////// //////// Cell Functionality /////// /////////////////////////////////// renderCell(cell) { var el = cell.getElement(), rangeIdx = this.ranges.findIndex((range2) => range2.occupies(cell)); el.classList.toggle("tabulator-range-selected", rangeIdx !== -1); el.classList.toggle("tabulator-range-only-cell-selected", this.ranges.length === 1 && this.ranges[0].atTopLeft(cell) && this.ranges[0].atBottomRight(cell)); el.dataset.range = rangeIdx; } handleCellMouseDown(event, cell) { if (event.button === 2 && (this.activeRange.occupies(cell) || (this.selecting === "row" || this.selecting === "all") && this.activeRange.occupiesRow(cell.row))) { return; } this.mousedown = true; document.addEventListener("mouseup", this.mouseUpEvent); this.newSelection(event, cell); } handleCellMouseMove(e, cell) { if (!this.mousedown || this.selecting === "all") { return; } this.activeRange.setBounds(false, cell, true); } handleCellClick(e, cell) { this.initializeFocus(cell); } handleEditingCell(cell) { if (this.activeRange) { this.activeRange.setBounds(cell); } } finishEditingCell() { this.blockKeydown = true; this.table.rowManager.element.focus(); setTimeout(() => { this.blockKeydown = false; }, 10); } /////////////////////////////////// /////// Navigation /////// /////////////////////////////////// keyNavigate(dir, e) { if (this.options("selectableRangeBlurEditOnNavigate")) { const isEditing = this.chain("edit-check-editing"); if (isEditing) { if (dir === "next" || dir === "prev") { this.dispatch("edit-cancel-cell"); } else { return false; } } } if (dir === "prev") { dir = "left"; } else if (dir === "next") { dir = "right"; } if (this.navigate(false, false, dir)) { e.preventDefault(); } } keyNavigateRange(e, dir, jump, expand) { if (this.navigate(jump, expand, dir)) { e.preventDefault(); } } navigate(jump, expand, dir) { var moved = false, range2, rangeEdge, prevRect, nextRow, nextCol, row, column, rowRect, rowManagerRect, columnRect, columnManagerRect; if (this.table.modules.edit && this.table.modules.edit.currentCell) { return false; } if (this.ranges.length > 1) { this.ranges = this.ranges.filter((range3) => { if (range3 === this.activeRange) { range3.setEnd(range3.start.row, range3.start.col); return true; } range3.destroy(); return false; }); } range2 = this.activeRange; prevRect = { top: range2.top, bottom: range2.bottom, left: range2.left, right: range2.right }; rangeEdge = expand ? range2.end : range2.start; nextRow = rangeEdge.row; nextCol = rangeEdge.col; if (jump) { switch (dir) { case "left": nextCol = this.findJumpCellLeft(range2.start.row, rangeEdge.col); break; case "right": nextCol = this.findJumpCellRight(range2.start.row, rangeEdge.col); break; case "up": nextRow = this.findJumpCellUp(rangeEdge.row, range2.start.col); break; case "down": nextRow = this.findJumpCellDown(rangeEdge.row, range2.start.col); break; } } else { if (expand) { if (this.selecting === "row" && (dir === "left" || dir === "right") || this.selecting === "column" && (dir === "up" || dir === "down")) { return; } } switch (dir) { case "left": nextCol = Math.max(nextCol - 1, 0); break; case "right": nextCol = Math.min(nextCol + 1, this.getTableColumns().length - 1); break; case "up": nextRow = Math.max(nextRow - 1, 0); break; case "down": nextRow = Math.min(nextRow + 1, this.getTableRows().length - 1); break; } } if (this.rowHeader && nextCol === 0) { nextCol = 1; } if (!expand) { range2.setStart(nextRow, nextCol); } range2.setEnd(nextRow, nextCol); if (!expand) { this.selecting = "cell"; } moved = prevRect.top !== range2.top || prevRect.bottom !== range2.bottom || prevRect.left !== range2.left || prevRect.right !== range2.right; if (moved) { row = this.getRowByRangePos(range2.end.row); column = this.getColumnByRangePos(range2.end.col); rowRect = row.getElement().getBoundingClientRect(); columnRect = column.getElement().getBoundingClientRect(); rowManagerRect = this.table.rowManager.getElement().getBoundingClientRect(); columnManagerRect = this.table.columnManager.getElement().getBoundingClientRect(); if (!(rowRect.top >= rowManagerRect.top && rowRect.bottom <= rowManagerRect.bottom)) { if (row.getElement().parentNode && column.getElement().parentNode) { this.autoScroll(range2, row.getElement(), column.getElement()); } else { row.getComponent().scrollTo(void 0, false); } } if (!(columnRect.left >= columnManagerRect.left + this.getRowHeaderWidth() && columnRect.right <= columnManagerRect.right)) { if (row.getElement().parentNode && column.getElement().parentNode) { this.autoScroll(range2, row.getElement(), column.getElement()); } else { column.getComponent().scrollTo(void 0, false); } } this.layoutElement(); } return true; } rangeRemoved(removed) { this.ranges = this.ranges.filter((range2) => range2 !== removed); if (this.activeRange === removed) { if (this.ranges.length) { this.activeRange = this.ranges[this.ranges.length - 1]; } else { this.addRange(); } } this.layoutElement(true); } findJumpRow(column, rows2, reverse, emptyStart, emptySide) { if (reverse) { rows2 = rows2.reverse(); } return this.findJumpItem(emptyStart, emptySide, rows2, function (row) { return row.getData()[column.getField()]; }); } findJumpCol(row, columns, reverse, emptyStart, emptySide) { if (reverse) { columns = columns.reverse(); } return this.findJumpItem(emptyStart, emptySide, columns, function (column) { return row.getData()[column.getField()]; }); } findJumpItem(emptyStart, emptySide, items, valueResolver) { var nextItem; for (let currentItem of items) { let currentValue = valueResolver(currentItem); if (emptyStart) { nextItem = currentItem; if (currentValue) { break; } } else { if (emptySide) { nextItem = currentItem; if (currentValue) { break; } } else { if (currentValue) { nextItem = currentItem; } else { break; } } } } return nextItem; } findJumpCellLeft(rowPos, colPos) { var row = this.getRowByRangePos(rowPos), columns = this.getTableColumns(), isStartingCellEmpty = this.isEmpty(row.getData()[columns[colPos].getField()]), isLeftOfStartingCellEmpty = columns[colPos - 1] ? this.isEmpty(row.getData()[columns[colPos - 1].getField()]) : false, targetCols = this.rowHeader ? columns.slice(1, colPos) : columns.slice(0, colPos), jumpCol = this.findJumpCol(row, targetCols, true, isStartingCellEmpty, isLeftOfStartingCellEmpty); if (jumpCol) { return jumpCol.getPosition() - 1; } return colPos; } findJumpCellRight(rowPos, colPos) { var row = this.getRowByRangePos(rowPos), columns = this.getTableColumns(), isStartingCellEmpty = this.isEmpty(row.getData()[columns[colPos].getField()]), isRightOfStartingCellEmpty = columns[colPos + 1] ? this.isEmpty(row.getData()[columns[colPos + 1].getField()]) : false, jumpCol = this.findJumpCol(row, columns.slice(colPos + 1, columns.length), false, isStartingCellEmpty, isRightOfStartingCellEmpty); if (jumpCol) { return jumpCol.getPosition() - 1; } return colPos; } findJumpCellUp(rowPos, colPos) { var column = this.getColumnByRangePos(colPos), rows2 = this.getTableRows(), isStartingCellEmpty = this.isEmpty(rows2[rowPos].getData()[column.getField()]), isTopOfStartingCellEmpty = rows2[rowPos - 1] ? this.isEmpty(rows2[rowPos - 1].getData()[column.getField()]) : false, jumpRow = this.findJumpRow(column, rows2.slice(0, rowPos), true, isStartingCellEmpty, isTopOfStartingCellEmpty); if (jumpRow) { return jumpRow.position - 1; } return rowPos; } findJumpCellDown(rowPos, colPos) { var column = this.getColumnByRangePos(colPos), rows2 = this.getTableRows(), isStartingCellEmpty = this.isEmpty(rows2[rowPos].getData()[column.getField()]), isBottomOfStartingCellEmpty = rows2[rowPos + 1] ? this.isEmpty(rows2[rowPos + 1].getData()[column.getField()]) : false, jumpRow = this.findJumpRow(column, rows2.slice(rowPos + 1, rows2.length), false, isStartingCellEmpty, isBottomOfStartingCellEmpty); if (jumpRow) { return jumpRow.position - 1; } return rowPos; } /////////////////////////////////// /////// Selection /////// /////////////////////////////////// newSelection(event, element) { var range2; if (element.type === "column") { if (!this.columnSelection) { return; } if (element === this.rowHeader) { range2 = this.resetRanges(); this.selecting = "all"; var topLeftCell, bottomRightCell = this.getCell(-1, -1); if (this.rowHeader) { topLeftCell = this.getCell(0, 1); } else { topLeftCell = this.getCell(0, 0); } range2.setBounds(topLeftCell, bottomRightCell); return; } else { this.selecting = "column"; } } else if (element.column === this.rowHeader) { this.selecting = "row"; } else { this.selecting = "cell"; } if (event.shiftKey) { this.activeRange.setBounds(false, element, true); } else if (event.ctrlKey) { this.addRange().setBounds(element, void 0, true); } else { this.resetRanges().setBounds(element, void 0, true); } } autoScroll(range2, row, column) { var tableHolder = this.table.rowManager.element, rect, view, withinHorizontalView, withinVerticalView; if (typeof row === "undefined") { row = this.getRowByRangePos(range2.end.row).getElement(); } if (typeof column === "undefined") { column = this.getColumnByRangePos(range2.end.col).getElement(); } rect = { left: column.offsetLeft, right: column.offsetLeft + column.offsetWidth, top: row.offsetTop, bottom: row.offsetTop + row.offsetHeight }; view = { left: tableHolder.scrollLeft + this.getRowHeaderWidth(), right: Math.ceil(tableHolder.scrollLeft + tableHolder.clientWidth), top: tableHolder.scrollTop, bottom: tableHolder.scrollTop + tableHolder.offsetHeight - this.table.rowManager.scrollbarWidth }; withinHorizontalView = view.left < rect.left && rect.left < view.right && view.left < rect.right && rect.right < view.right; withinVerticalView = view.top < rect.top && rect.top < view.bottom && view.top < rect.bottom && rect.bottom < view.bottom; if (!withinHorizontalView) { if (rect.left < view.left) { tableHolder.scrollLeft = rect.left - this.getRowHeaderWidth(); } else if (rect.right > view.right) { tableHolder.scrollLeft = Math.min(rect.right - tableHolder.clientWidth, rect.left - this.getRowHeaderWidth()); } } if (!withinVerticalView) { if (rect.top < view.top) { tableHolder.scrollTop = rect.top; } else if (rect.bottom > view.bottom) { tableHolder.scrollTop = rect.bottom - tableHolder.clientHeight; } } } /////////////////////////////////// /////// Layout /////// /////////////////////////////////// layoutChange() { this.overlay.style.visibility = "hidden"; clearTimeout(this.layoutChangeTimeout); this.layoutChangeTimeout = setTimeout(this.layoutRanges.bind(this), 200); } redraw(force) { if (force) { this.selecting = "cell"; this.setDefaultRange(); this.layoutElement(); } } layoutElement(visibleRows) { var rows2; if (visibleRows) { rows2 = this.table.rowManager.getVisibleRows(true); } else { rows2 = this.table.rowManager.getRows(); } rows2.forEach((row) => { if (row.type === "row") { this.layoutRow(row); row.cells.forEach((cell) => this.renderCell(cell)); } }); this.getTableColumns().forEach((column) => { this.layoutColumn(column); }); this.layoutRanges(); } layoutRow(row) { var el = row.getElement(), selected = false, occupied = this.ranges.some((range2) => range2.occupiesRow(row)); if (this.selecting === "row") { selected = occupied; } else if (this.selecting === "all") { selected = true; } el.classList.toggle("tabulator-range-selected", selected); el.classList.toggle("tabulator-range-highlight", occupied); } layoutColumn(column) { var el = column.getElement(), selected = false, occupied = this.ranges.some((range2) => range2.occupiesColumn(column)); if (this.selecting === "column") { selected = occupied; } else if (this.selecting === "all") { selected = true; } el.classList.toggle("tabulator-range-selected", selected); el.classList.toggle("tabulator-range-highlight", occupied); } layoutRanges() { var activeCell, activeCellEl, activeRowEl; if (!this.table.initialized) { return; } activeCell = this.getActiveCell(); if (!activeCell) { return; } activeCellEl = activeCell.getElement(); activeRowEl = activeCell.row.getElement(); if (this.table.rtl) { this.activeRangeCellElement.style.right = activeRowEl.offsetWidth - activeCellEl.offsetLeft - activeCellEl.offsetWidth + "px"; } else { this.activeRangeCellElement.style.left = activeRowEl.offsetLeft + activeCellEl.offsetLeft + "px"; } this.activeRangeCellElement.style.top = activeRowEl.offsetTop + "px"; this.activeRangeCellElement.style.width = activeCellEl.offsetWidth + "px"; this.activeRangeCellElement.style.height = activeRowEl.offsetHeight + "px"; this.ranges.forEach((range2) => range2.layout()); this.overlay.style.visibility = "visible"; } /////////////////////////////////// /////// Helper Functions /////// /////////////////////////////////// getCell(rowIdx, colIdx) { var row; if (colIdx < 0) { colIdx = this.getTableColumns().length + colIdx; if (colIdx < 0) { return null; } } if (rowIdx < 0) { rowIdx = this.getTableRows().length + rowIdx; } row = this.table.rowManager.getRowFromPosition(rowIdx + 1); return row ? row.getCells(false, true).filter((cell) => cell.column.visible)[colIdx] : null; } getActiveCell() { if (!this.activeRange) return; return this.getCell(this.activeRange.start.row, this.activeRange.start.col); } getRowByRangePos(pos) { return this.getTableRows()[pos]; } getColumnByRangePos(pos) { return this.getTableColumns()[pos]; } getTableRows() { return this.table.rowManager.getDisplayRows().filter((row) => row.type === "row"); } getTableColumns() { return this.table.columnManager.getVisibleColumnsByIndex(); } addRange(start, end) { var range2; if (this.maxRanges !== true && this.ranges.length >= this.maxRanges) { this.ranges.shift().destroy(); } range2 = new Range(this.table, this, start, end); this.activeRange = range2; this.ranges.push(range2); this.rangeContainer.appendChild(range2.element); return range2; } createDefaultRange() { var range2, cell, visibleCells; range2 = this.addRange(); if (this.table.rowManager.activeRows.length) { visibleCells = this.table.rowManager.activeRows[0].cells.filter((cell2) => cell2.column.visible); cell = visibleCells[this.rowHeader ? 1 : 0]; if (cell) { range2.setBounds(cell); if (this.options("selectableRangeAutoFocus")) { this.initializeFocus(cell); } } } return range2; } clearRanges() { this.ranges.forEach((range2) => range2.destroy()); this.ranges = []; } setDefaultRange() { this.clearRanges(); if (this.options("selectableRangeInitializeDefault")) { this.createDefaultRange(); } } resetRanges() { this.clearRanges(); return this.createDefaultRange(); } tableDestroyed() { document.removeEventListener("mouseup", this.mouseUpEvent); this.table.rowManager.element.removeEventListener("keydown", this.keyDownEvent); } selectedRows(component) { return component ? this.activeRange.getRows().map((row) => row.getComponent()) : this.activeRange.getRows(); } selectedColumns(component) { return component ? this.activeRange.getColumns().map((col) => col.getComponent()) : this.activeRange.getColumns(); } getRowHeaderWidth() { if (!this.rowHeader) { return 0; } return this.rowHeader.getElement().offsetWidth; } isEmpty(value) { return value === null || value === void 0 || value === ""; } } __publicField(SelectRange, "moduleName", "selectRange"); __publicField(SelectRange, "moduleInitOrder", 1); __publicField(SelectRange, "moduleExtensions", extensions); function number(a, b, aRow, bRow, column, dir, params) { var alignEmptyValues = params.alignEmptyValues; var decimal = params.decimalSeparator; var thousand = params.thousandSeparator; var emptyAlign = 0; a = String(a); b = String(b); if (thousand) { a = a.split(thousand).join(""); b = b.split(thousand).join(""); } if (decimal) { a = a.split(decimal).join("."); b = b.split(decimal).join("."); } a = parseFloat(a); b = parseFloat(b); if (isNaN(a)) { emptyAlign = isNaN(b) ? 0 : -1; } else if (isNaN(b)) { emptyAlign = 1; } else { return a - b; } if (alignEmptyValues === "top" && dir === "desc" || alignEmptyValues === "bottom" && dir === "asc") { emptyAlign *= -1; } return emptyAlign; } function string(a, b, aRow, bRow, column, dir, params) { var alignEmptyValues = params.alignEmptyValues; var emptyAlign = 0; var locale; if (!a) { emptyAlign = !b ? 0 : -1; } else if (!b) { emptyAlign = 1; } else { switch (typeof params.locale) { case "boolean": if (params.locale) { locale = this.langLocale(); } break; case "string": locale = params.locale; break; } return String(a).toLowerCase().localeCompare(String(b).toLowerCase(), locale); } if (alignEmptyValues === "top" && dir === "desc" || alignEmptyValues === "bottom" && dir === "asc") { emptyAlign *= -1; } return emptyAlign; } function datetime(a, b, aRow, bRow, column, dir, params) { var DT = this.table.dependencyRegistry.lookup(["luxon", "DateTime"], "DateTime"); var format = params.format || "dd/MM/yyyy HH:mm:ss", alignEmptyValues = params.alignEmptyValues, emptyAlign = 0; if (typeof DT != "undefined") { if (!DT.isDateTime(a)) { if (format === "iso") { a = DT.fromISO(String(a)); } else if (format === "x") { a = DT.fromMillis(a); } else { a = DT.fromFormat(String(a), format); } } if (!DT.isDateTime(b)) { if (format === "iso") { b = DT.fromISO(String(b)); } else if (format === "x") { b = DT.fromMillis(b); } else { b = DT.fromFormat(String(b), format); } } if (!a.isValid) { emptyAlign = !b.isValid ? 0 : -1; } else if (!b.isValid) { emptyAlign = 1; } else { return a - b; } if (alignEmptyValues === "top" && dir === "desc" || alignEmptyValues === "bottom" && dir === "asc") { emptyAlign *= -1; } return emptyAlign; } else { console.error("Sort Error - 'datetime' sorter is dependant on luxon.js"); } } function date(a, b, aRow, bRow, column, dir, params) { if (!params.format) { params.format = "dd/MM/yyyy"; } return datetime.call(this, a, b, aRow, bRow, column, dir, params); } function time(a, b, aRow, bRow, column, dir, params) { if (!params.format) { params.format = "HH:mm"; } return datetime.call(this, a, b, aRow, bRow, column, dir, params); } function boolean(a, b, aRow, bRow, column, dir, params) { var el1 = a === true || a === "true" || a === "True" || a === 1 ? 1 : 0; var el2 = b === true || b === "true" || b === "True" || b === 1 ? 1 : 0; return el1 - el2; } function array(a, b, aRow, bRow, column, dir, params) { var type = params.type || "length", alignEmptyValues = params.alignEmptyValues, emptyAlign = 0, table = this.table, valueMap; if (params.valueMap) { if (typeof params.valueMap === "string") { valueMap = function (value) { return value.map((item) => { return Helpers.retrieveNestedData(table.options.nestedFieldSeparator, params.valueMap, item); }); }; } else { valueMap = params.valueMap; } } function calc(value) { var result; if (valueMap) { value = valueMap(value); } switch (type) { case "length": result = value.length; break; case "sum": result = value.reduce(function (c, d) { return c + d; }); break; case "max": result = Math.max.apply(null, value); break; case "min": result = Math.min.apply(null, value); break; case "avg": result = value.reduce(function (c, d) { return c + d; }) / value.length; break; case "string": result = value.join(""); break; } return result; } if (!Array.isArray(a)) { emptyAlign = !Array.isArray(b) ? 0 : -1; } else if (!Array.isArray(b)) { emptyAlign = 1; } else { if (type === "string") { return String(calc(a)).toLowerCase().localeCompare(String(calc(b)).toLowerCase()); } else { return calc(b) - calc(a); } } if (alignEmptyValues === "top" && dir === "desc" || alignEmptyValues === "bottom" && dir === "asc") { emptyAlign *= -1; } return emptyAlign; } function exists(a, b, aRow, bRow, column, dir, params) { var el1 = typeof a == "undefined" ? 0 : 1; var el2 = typeof b == "undefined" ? 0 : 1; return el1 - el2; } function alphanum(as, bs, aRow, bRow, column, dir, params) { var a, b, a1, b1, i = 0, L, rx = /(\d+)|(\D+)/g, rd = /\d/; var alignEmptyValues = params.alignEmptyValues; var emptyAlign = 0; if (!as && as !== 0) { emptyAlign = !bs && bs !== 0 ? 0 : -1; } else if (!bs && bs !== 0) { emptyAlign = 1; } else { if (isFinite(as) && isFinite(bs)) return as - bs; a = String(as).toLowerCase(); b = String(bs).toLowerCase(); if (a === b) return 0; if (!(rd.test(a) && rd.test(b))) return a > b ? 1 : -1; a = a.match(rx); b = b.match(rx); L = a.length > b.length ? b.length : a.length; while (i < L) { a1 = a[i]; b1 = b[i++]; if (a1 !== b1) { if (isFinite(a1) && isFinite(b1)) { if (a1.charAt(0) === "0") a1 = "." + a1; if (b1.charAt(0) === "0") b1 = "." + b1; return a1 - b1; } else return a1 > b1 ? 1 : -1; } } return a.length > b.length; } if (alignEmptyValues === "top" && dir === "desc" || alignEmptyValues === "bottom" && dir === "asc") { emptyAlign *= -1; } return emptyAlign; } var defaultSorters = { number, string, date, time, datetime, boolean, array, exists, alphanum }; const _Sort = class _Sort extends Module { constructor(table) { super(table); this.sortList = []; this.changed = false; this.registerTableOption("sortMode", "local"); this.registerTableOption("initialSort", false); this.registerTableOption("columnHeaderSortMulti", true); this.registerTableOption("sortOrderReverse", false); this.registerTableOption("headerSortElement", "
"); this.registerTableOption("headerSortClickElement", "header"); this.registerColumnOption("sorter"); this.registerColumnOption("sorterParams"); this.registerColumnOption("headerSort", true); this.registerColumnOption("headerSortStartingDir"); this.registerColumnOption("headerSortTristate"); } initialize() { this.subscribe("column-layout", this.initializeColumn.bind(this)); this.subscribe("table-built", this.tableBuilt.bind(this)); this.registerDataHandler(this.sort.bind(this), 20); this.registerTableFunction("setSort", this.userSetSort.bind(this)); this.registerTableFunction("getSorters", this.getSort.bind(this)); this.registerTableFunction("clearSort", this.clearSort.bind(this)); if (this.table.options.sortMode === "remote") { this.subscribe("data-params", this.remoteSortParams.bind(this)); } } tableBuilt() { if (this.table.options.initialSort) { this.setSort(this.table.options.initialSort); } } remoteSortParams(data, config, silent, params) { var sorters = this.getSort(); sorters.forEach((item) => { delete item.column; }); params.sort = sorters; return params; } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// userSetSort(sortList, dir) { this.setSort(sortList, dir); this.refreshSort(); } clearSort() { this.clear(); this.refreshSort(); } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// //initialize column header for sorting initializeColumn(column) { var sorter = false, colEl, arrowEl; switch (typeof column.definition.sorter) { case "string": if (_Sort.sorters[column.definition.sorter]) { sorter = _Sort.sorters[column.definition.sorter]; } else { console.warn("Sort Error - No such sorter found: ", column.definition.sorter); } break; case "function": sorter = column.definition.sorter; break; } column.modules.sort = { sorter, dir: "none", params: column.definition.sorterParams || {}, startingDir: column.definition.headerSortStartingDir || "asc", tristate: column.definition.headerSortTristate }; if (column.definition.headerSort !== false) { colEl = column.getElement(); colEl.classList.add("tabulator-sortable"); arrowEl = document.createElement("div"); arrowEl.classList.add("tabulator-col-sorter"); switch (this.table.options.headerSortClickElement) { case "icon": arrowEl.classList.add("tabulator-col-sorter-element"); break; case "header": colEl.classList.add("tabulator-col-sorter-element"); break; default: colEl.classList.add("tabulator-col-sorter-element"); break; } switch (this.table.options.headerSortElement) { case "function": break; case "object": arrowEl.appendChild(this.table.options.headerSortElement); break; default: arrowEl.innerHTML = this.table.options.headerSortElement; } column.titleHolderElement.appendChild(arrowEl); column.modules.sort.element = arrowEl; this.setColumnHeaderSortIcon(column, "none"); if (this.table.options.headerSortClickElement === "icon") { arrowEl.addEventListener("mousedown", (e) => { e.stopPropagation(); }); } (this.table.options.headerSortClickElement === "icon" ? arrowEl : colEl).addEventListener("click", (e) => { var dir = "", sorters = [], match = false; if (column.modules.sort) { if (column.modules.sort.tristate) { if (column.modules.sort.dir == "none") { dir = column.modules.sort.startingDir; } else { if (column.modules.sort.dir == column.modules.sort.startingDir) { dir = column.modules.sort.dir == "asc" ? "desc" : "asc"; } else { dir = "none"; } } } else { switch (column.modules.sort.dir) { case "asc": dir = "desc"; break; case "desc": dir = "asc"; break; default: dir = column.modules.sort.startingDir; } } if (this.table.options.columnHeaderSortMulti && (e.shiftKey || e.ctrlKey)) { sorters = this.getSort(); match = sorters.findIndex((sorter2) => { return sorter2.field === column.getField(); }); if (match > -1) { sorters[match].dir = dir; match = sorters.splice(match, 1)[0]; if (dir != "none") { sorters.push(match); } } else { if (dir != "none") { sorters.push({column, dir}); } } this.setSort(sorters); } else { if (dir == "none") { this.clear(); } else { this.setSort(column, dir); } } this.refreshSort(); } }); } } refreshSort() { if (this.table.options.sortMode === "remote") { this.reloadData(null, false, false); } else { this.refreshData(true); } } //check if the sorters have changed since last use hasChanged() { var changed = this.changed; this.changed = false; return changed; } //return current sorters getSort() { var self = this, sorters = []; self.sortList.forEach(function (item) { if (item.column) { sorters.push({ column: item.column.getComponent(), field: item.column.getField(), dir: item.dir }); } }); return sorters; } //change sort list and trigger sort setSort(sortList, dir) { var self = this, newSortList = []; if (!Array.isArray(sortList)) { sortList = [{column: sortList, dir}]; } sortList.forEach(function (item) { var column; column = self.table.columnManager.findColumn(item.column); if (column) { item.column = column; newSortList.push(item); self.changed = true; } else { console.warn("Sort Warning - Sort field does not exist and is being ignored: ", item.column); } }); self.sortList = newSortList; this.dispatch("sort-changed"); } //clear sorters clear() { this.setSort([]); } //find appropriate sorter for column findSorter(column) { var row = this.table.rowManager.activeRows[0], sorter = "string", field, value; if (row) { row = row.getData(); field = column.getField(); if (field) { value = column.getFieldValue(row); switch (typeof value) { case "undefined": sorter = "string"; break; case "boolean": sorter = "boolean"; break; default: if (!isNaN(Number(value)) && value !== "") { sorter = "number"; } else { if (value.match(/((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+$/i)) { sorter = "alphanum"; } } break; } } } return _Sort.sorters[sorter]; } //work through sort list sorting data sort(data, sortOnly) { var self = this, sortList = this.table.options.sortOrderReverse ? self.sortList.slice().reverse() : self.sortList, sortListActual = [], rowComponents = []; if (this.subscribedExternal("dataSorting")) { this.dispatchExternal("dataSorting", self.getSort()); } if (!sortOnly) { self.clearColumnHeaders(); } if (this.table.options.sortMode !== "remote") { sortList.forEach(function (item, i) { var sortObj; if (item.column) { sortObj = item.column.modules.sort; if (sortObj) { if (!sortObj.sorter) { sortObj.sorter = self.findSorter(item.column); } item.params = typeof sortObj.params === "function" ? sortObj.params(item.column.getComponent(), item.dir) : sortObj.params; sortListActual.push(item); } if (!sortOnly) { self.setColumnHeader(item.column, item.dir); } } }); if (sortListActual.length) { self._sortItems(data, sortListActual); } } else if (!sortOnly) { sortList.forEach(function (item, i) { self.setColumnHeader(item.column, item.dir); }); } if (this.subscribedExternal("dataSorted")) { data.forEach((row) => { rowComponents.push(row.getComponent()); }); this.dispatchExternal("dataSorted", self.getSort(), rowComponents); } return data; } //clear sort arrows on columns clearColumnHeaders() { this.table.columnManager.getRealColumns().forEach((column) => { if (column.modules.sort) { column.modules.sort.dir = "none"; column.getElement().setAttribute("aria-sort", "none"); this.setColumnHeaderSortIcon(column, "none"); } }); } //set the column header sort direction setColumnHeader(column, dir) { column.modules.sort.dir = dir; column.getElement().setAttribute("aria-sort", dir === "asc" ? "ascending" : "descending"); this.setColumnHeaderSortIcon(column, dir); } setColumnHeaderSortIcon(column, dir) { var sortEl = column.modules.sort.element, arrowEl; if (column.definition.headerSort && typeof this.table.options.headerSortElement === "function") { while (sortEl.firstChild) sortEl.removeChild(sortEl.firstChild); arrowEl = this.table.options.headerSortElement.call(this.table, column.getComponent(), dir); if (typeof arrowEl === "object") { sortEl.appendChild(arrowEl); } else { sortEl.innerHTML = arrowEl; } } } //sort each item in sort list _sortItems(data, sortList) { var sorterCount = sortList.length - 1; data.sort((a, b) => { var result; for (var i = sorterCount; i >= 0; i--) { let sortItem = sortList[i]; result = this._sortRow(a, b, sortItem.column, sortItem.dir, sortItem.params); if (result !== 0) { break; } } return result; }); } //process individual rows for a sort function on active data _sortRow(a, b, column, dir, params) { var el1Comp, el2Comp; var el1 = dir == "asc" ? a : b; var el2 = dir == "asc" ? b : a; a = column.getFieldValue(el1.getData()); b = column.getFieldValue(el2.getData()); a = typeof a !== "undefined" ? a : ""; b = typeof b !== "undefined" ? b : ""; el1Comp = el1.getComponent(); el2Comp = el2.getComponent(); return column.modules.sort.sorter.call(this, a, b, el1Comp, el2Comp, column.getComponent(), dir, params); } }; __publicField(_Sort, "moduleName", "sort"); //load defaults __publicField(_Sort, "sorters", defaultSorters); let Sort = _Sort; class GridCalculator { constructor(columns, rows2) { this.columnCount = columns; this.rowCount = rows2; this.columnString = []; this.columns = []; this.rows = []; } genColumns(data) { var colCount = Math.max(this.columnCount, Math.max(...data.map((item) => item.length))); this.columnString = []; this.columns = []; for (let i = 1; i <= colCount; i++) { this.incrementChar(this.columnString.length - 1); this.columns.push(this.columnString.join("")); } return this.columns; } genRows(data) { var rowCount = Math.max(this.rowCount, data.length); this.rows = []; for (let i = 1; i <= rowCount; i++) { this.rows.push(i); } return this.rows; } incrementChar(i) { let char = this.columnString[i]; if (char) { if (char !== "Z") { this.columnString[i] = String.fromCharCode(this.columnString[i].charCodeAt(0) + 1); } else { this.columnString[i] = "A"; if (i) { this.incrementChar(i - 1); } else { this.columnString.push("A"); } } } else { this.columnString.push("A"); } } setRowCount(count) { this.rowCount = count; } setColumnCount(count) { this.columnCount = count; } } class SheetComponent { constructor(sheet) { this._sheet = sheet; return new Proxy(this, { get: function (target, name2, receiver) { if (typeof target[name2] !== "undefined") { return target[name2]; } else { return target._sheet.table.componentFunctionBinder.handle("sheet", target._sheet, name2); } } }); } getTitle() { return this._sheet.title; } getKey() { return this._sheet.key; } getDefinition() { return this._sheet.getDefinition(); } getData() { return this._sheet.getData(); } setData(data) { return this._sheet.setData(data); } clear() { return this._sheet.clear(); } remove() { return this._sheet.remove(); } active() { return this._sheet.active(); } setTitle(title) { return this._sheet.setTitle(title); } setRows(rows2) { return this._sheet.setRows(rows2); } setColumns(columns) { return this._sheet.setColumns(columns); } } class Sheet extends CoreFeature { constructor(spreadsheetManager, definition) { super(spreadsheetManager.table); this.spreadsheetManager = spreadsheetManager; this.definition = definition; this.title = this.definition.title || ""; this.key = this.definition.key || this.definition.title; this.rowCount = this.definition.rows; this.columnCount = this.definition.columns; this.data = this.definition.data || []; this.element = null; this.isActive = false; this.grid = new GridCalculator(this.columnCount, this.rowCount); this.defaultColumnDefinition = {width: 100, headerHozAlign: "center", headerSort: false}; this.columnDefinition = Object.assign(this.defaultColumnDefinition, this.options("spreadsheetColumnDefinition")); this.columnDefs = []; this.rowDefs = []; this.columnFields = []; this.columns = []; this.rows = []; this.scrollTop = null; this.scrollLeft = null; this.initialize(); this.dispatchExternal("sheetAdded", this.getComponent()); } /////////////////////////////////// ///////// Initialization ////////// /////////////////////////////////// initialize() { this.initializeElement(); this.initializeColumns(); this.initializeRows(); } reinitialize() { this.initializeColumns(); this.initializeRows(); } initializeElement() { this.element = document.createElement("div"); this.element.classList.add("tabulator-spreadsheet-tab"); this.element.innerText = this.title; this.element.addEventListener("click", () => { this.spreadsheetManager.loadSheet(this); }); } initializeColumns() { this.grid.setColumnCount(this.columnCount); this.columnFields = this.grid.genColumns(this.data); this.columnDefs = []; this.columnFields.forEach((ref) => { var def = Object.assign({}, this.columnDefinition); def.field = ref; def.title = ref; this.columnDefs.push(def); }); } initializeRows() { var refs; this.grid.setRowCount(this.rowCount); refs = this.grid.genRows(this.data); this.rowDefs = []; refs.forEach((ref, i) => { var def = {"_id": ref}; var data = this.data[i]; if (data) { data.forEach((val, j) => { var field = this.columnFields[j]; if (field) { def[field] = val; } }); } this.rowDefs.push(def); }); } unload() { this.isActive = false; this.scrollTop = this.table.rowManager.scrollTop; this.scrollLeft = this.table.rowManager.scrollLeft; this.data = this.getData(true); this.element.classList.remove("tabulator-spreadsheet-tab-active"); } load() { var wasInactive = !this.isActive; this.isActive = true; this.table.blockRedraw(); this.table.setData([]); this.table.setColumns(this.columnDefs); this.table.setData(this.rowDefs); this.table.restoreRedraw(); if (wasInactive && this.scrollTop !== null) { this.table.rowManager.element.scrollLeft = this.scrollLeft; this.table.rowManager.element.scrollTop = this.scrollTop; } this.element.classList.add("tabulator-spreadsheet-tab-active"); this.dispatchExternal("sheetLoaded", this.getComponent()); } /////////////////////////////////// //////// Helper Functions ///////// /////////////////////////////////// getComponent() { return new SheetComponent(this); } getDefinition() { return { title: this.title, key: this.key, rows: this.rowCount, columns: this.columnCount, data: this.getData() }; } getData(full) { var output = [], rowWidths, outputWidth, outputHeight; this.rowDefs.forEach((rowData) => { var row = []; this.columnFields.forEach((field) => { row.push(rowData[field]); }); output.push(row); }); if (!full && !this.options("spreadsheetOutputFull")) { rowWidths = output.map((row) => row.findLastIndex((val) => typeof val !== "undefined") + 1); outputWidth = Math.max(...rowWidths); outputHeight = rowWidths.findLastIndex((width) => width > 0) + 1; output = output.slice(0, outputHeight); output = output.map((row) => row.slice(0, outputWidth)); } return output; } setData(data) { this.data = data; this.reinitialize(); this.dispatchExternal("sheetUpdated", this.getComponent()); if (this.isActive) { this.load(); } } clear() { this.setData([]); } setTitle(title) { this.title = title; this.element.innerText = title; this.dispatchExternal("sheetUpdated", this.getComponent()); } setRows(rows2) { this.rowCount = rows2; this.initializeRows(); this.dispatchExternal("sheetUpdated", this.getComponent()); if (this.isActive) { this.load(); } } setColumns(columns) { this.columnCount = columns; this.reinitialize(); this.dispatchExternal("sheetUpdated", this.getComponent()); if (this.isActive) { this.load(); } } remove() { this.spreadsheetManager.removeSheet(this); } destroy() { if (this.element.parentNode) { this.element.parentNode.removeChild(this.element); } this.dispatchExternal("sheetRemoved", this.getComponent()); } active() { this.spreadsheetManager.loadSheet(this); } } class Spreadsheet extends Module { constructor(table) { super(table); this.sheets = []; this.element = null; this.registerTableOption("spreadsheet", false); this.registerTableOption("spreadsheetRows", 50); this.registerTableOption("spreadsheetColumns", 50); this.registerTableOption("spreadsheetColumnDefinition", {}); this.registerTableOption("spreadsheetOutputFull", false); this.registerTableOption("spreadsheetData", false); this.registerTableOption("spreadsheetSheets", false); this.registerTableOption("spreadsheetSheetTabs", false); this.registerTableOption("spreadsheetSheetTabsElement", false); this.registerTableFunction("setSheets", this.setSheets.bind(this)); this.registerTableFunction("addSheet", this.addSheet.bind(this)); this.registerTableFunction("getSheets", this.getSheets.bind(this)); this.registerTableFunction("getSheetDefinitions", this.getSheetDefinitions.bind(this)); this.registerTableFunction("setSheetData", this.setSheetData.bind(this)); this.registerTableFunction("getSheet", this.getSheet.bind(this)); this.registerTableFunction("getSheetData", this.getSheetData.bind(this)); this.registerTableFunction("clearSheet", this.clearSheet.bind(this)); this.registerTableFunction("removeSheet", this.removeSheetFunc.bind(this)); this.registerTableFunction("activeSheet", this.activeSheetFunc.bind(this)); } /////////////////////////////////// ////// Module Initialization ////// /////////////////////////////////// initialize() { if (this.options("spreadsheet")) { this.subscribe("table-initialized", this.tableInitialized.bind(this)); this.subscribe("data-loaded", this.loadRemoteData.bind(this)); this.table.options.index = "_id"; if (this.options("spreadsheetData") && this.options("spreadsheetSheets")) { console.warn("You cannot use spreadsheetData and spreadsheetSheets at the same time, ignoring spreadsheetData"); this.table.options.spreadsheetData = false; } this.compatibilityCheck(); if (this.options("spreadsheetSheetTabs")) { this.initializeTabset(); } } } compatibilityCheck() { if (this.options("data")) { console.warn("Do not use the data option when working with spreadsheets, use either spreadsheetData or spreadsheetSheets to pass data into the table"); } if (this.options("pagination")) { console.warn("The spreadsheet module is not compatible with the pagination module"); } if (this.options("groupBy")) { console.warn("The spreadsheet module is not compatible with the row grouping module"); } if (this.options("responsiveCollapse")) { console.warn("The spreadsheet module is not compatible with the responsive collapse module"); } } initializeTabset() { this.element = document.createElement("div"); this.element.classList.add("tabulator-spreadsheet-tabs"); var altContainer = this.options("spreadsheetSheetTabsElement"); if (altContainer && !(altContainer instanceof HTMLElement)) { altContainer = document.querySelector(altContainer); if (!altContainer) { console.warn("Unable to find element matching spreadsheetSheetTabsElement selector:", this.options("spreadsheetSheetTabsElement")); } } if (altContainer) { altContainer.appendChild(this.element); } else { this.footerAppend(this.element); } } tableInitialized() { if (this.sheets.length) { this.loadSheet(this.sheets[0]); } else { if (this.options("spreadsheetSheets")) { this.loadSheets(this.options("spreadsheetSheets")); } else if (this.options("spreadsheetData")) { this.loadData(this.options("spreadsheetData")); } } } /////////////////////////////////// /////////// Ajax Parsing ////////// /////////////////////////////////// loadRemoteData(data, data1, data2) { console.log("data", data, data1, data2); if (Array.isArray(data)) { this.table.dataLoader.clearAlert(); this.dispatchExternal("dataLoaded", data); if (!data.length || Array.isArray(data[0])) { this.loadData(data); } else { this.loadSheets(data); } } else { console.error("Spreadsheet Loading Error - Unable to process remote data due to invalid data type \nExpecting: array \nReceived: ", typeof data, "\nData: ", data); } return false; } /////////////////////////////////// ///////// Sheet Management //////// /////////////////////////////////// loadData(data) { var def = { data }; this.loadSheet(this.newSheet(def)); } destroySheets() { this.sheets.forEach((sheet) => { sheet.destroy(); }); this.sheets = []; this.activeSheet = null; } loadSheets(sheets) { if (!Array.isArray(sheets)) { sheets = []; } this.destroySheets(); sheets.forEach((def) => { this.newSheet(def); }); this.loadSheet(this.sheets[0]); } loadSheet(sheet) { if (this.activeSheet !== sheet) { if (this.activeSheet) { this.activeSheet.unload(); } this.activeSheet = sheet; sheet.load(); } } newSheet(definition = {}) { var sheet; if (!definition.rows) { definition.rows = this.options("spreadsheetRows"); } if (!definition.columns) { definition.columns = this.options("spreadsheetColumns"); } sheet = new Sheet(this, definition); this.sheets.push(sheet); if (this.element) { this.element.appendChild(sheet.element); } return sheet; } removeSheet(sheet) { var index = this.sheets.indexOf(sheet), prevSheet; if (this.sheets.length > 1) { if (index > -1) { this.sheets.splice(index, 1); sheet.destroy(); if (this.activeSheet === sheet) { prevSheet = this.sheets[index - 1] || this.sheets[0]; if (prevSheet) { this.loadSheet(prevSheet); } else { this.activeSheet = null; } } } } else { console.warn("Unable to remove sheet, at least one sheet must be active"); } } lookupSheet(key) { if (!key) { return this.activeSheet; } else if (key instanceof Sheet) { return key; } else if (key instanceof SheetComponent) { return key._sheet; } else { return this.sheets.find((sheet) => sheet.key === key) || false; } } /////////////////////////////////// //////// Public Functions ///////// /////////////////////////////////// setSheets(sheets) { this.loadSheets(sheets); return this.getSheets(); } addSheet(sheet) { return this.newSheet(sheet).getComponent(); } getSheetDefinitions() { return this.sheets.map((sheet) => sheet.getDefinition()); } getSheets() { return this.sheets.map((sheet) => sheet.getComponent()); } getSheet(key) { var sheet = this.lookupSheet(key); return sheet ? sheet.getComponent() : false; } setSheetData(key, data) { if (key && !data) { data = key; key = false; } var sheet = this.lookupSheet(key); return sheet ? sheet.setData(data) : false; } getSheetData(key) { var sheet = this.lookupSheet(key); return sheet ? sheet.getData() : false; } clearSheet(key) { var sheet = this.lookupSheet(key); return sheet ? sheet.clear() : false; } removeSheetFunc(key) { var sheet = this.lookupSheet(key); if (sheet) { this.removeSheet(sheet); } } activeSheetFunc(key) { var sheet = this.lookupSheet(key); return sheet ? this.loadSheet(sheet) : false; } } __publicField(Spreadsheet, "moduleName", "spreadsheet"); class Tooltip extends Module { constructor(table) { super(table); this.tooltipSubscriber = null, this.headerSubscriber = null, this.timeout = null; this.popupInstance = null; this.registerTableOption("tooltipDelay", 300); this.registerColumnOption("tooltip"); this.registerColumnOption("headerTooltip"); } initialize() { this.deprecatedOptionsCheck(); this.subscribe("column-init", this.initializeColumn.bind(this)); } deprecatedOptionsCheck() { } initializeColumn(column) { if (column.definition.headerTooltip && !this.headerSubscriber) { this.headerSubscriber = true; this.subscribe("column-mousemove", this.mousemoveCheck.bind(this, "headerTooltip")); this.subscribe("column-mouseout", this.mouseoutCheck.bind(this, "headerTooltip")); } if (column.definition.tooltip && !this.tooltipSubscriber) { this.tooltipSubscriber = true; this.subscribe("cell-mousemove", this.mousemoveCheck.bind(this, "tooltip")); this.subscribe("cell-mouseout", this.mouseoutCheck.bind(this, "tooltip")); } } mousemoveCheck(action, e, component) { var tooltip = action === "tooltip" ? component.column.definition.tooltip : component.definition.headerTooltip; if (tooltip) { this.clearPopup(); this.timeout = setTimeout(this.loadTooltip.bind(this, e, component, tooltip), this.table.options.tooltipDelay); } } mouseoutCheck(action, e, component) { if (!this.popupInstance) { this.clearPopup(); } } clearPopup(action, e, component) { clearTimeout(this.timeout); this.timeout = null; if (this.popupInstance) { this.popupInstance.hide(); } } loadTooltip(e, component, tooltip) { var contentsEl, renderedCallback, coords; function onRendered(callback) { renderedCallback = callback; } if (typeof tooltip === "function") { tooltip = tooltip(e, component.getComponent(), onRendered); } if (tooltip instanceof HTMLElement) { contentsEl = tooltip; } else { contentsEl = document.createElement("div"); if (tooltip === true) { if (component instanceof Cell) { tooltip = component.value; } else { if (component.definition.field) { this.langBind("columns|" + component.definition.field, (value) => { contentsEl.innerHTML = tooltip = value || component.definition.title; }); } else { tooltip = component.definition.title; } } } contentsEl.innerHTML = tooltip; } if (tooltip || tooltip === 0 || tooltip === false) { contentsEl.classList.add("tabulator-tooltip"); contentsEl.addEventListener("mousemove", (e2) => e2.preventDefault()); this.popupInstance = this.popup(contentsEl); if (typeof renderedCallback === "function") { this.popupInstance.renderCallback(renderedCallback); } coords = this.popupInstance.containerEventCoords(e); this.popupInstance.show(coords.x + 15, coords.y + 15).hideOnBlur(() => { this.dispatchExternal("TooltipClosed", component.getComponent()); this.popupInstance = null; }); this.dispatchExternal("TooltipOpened", component.getComponent()); } } } __publicField(Tooltip, "moduleName", "tooltip"); var defaultValidators = { //is integer integer: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } value = Number(value); return !isNaN(value) && isFinite(value) && Math.floor(value) === value; }, //is float float: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } value = Number(value); return !isNaN(value) && isFinite(value) && value % 1 !== 0; }, //must be a number numeric: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } return !isNaN(value); }, //must be a string string: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } return isNaN(value); }, //must be alphanumeric alphanumeric: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } var reg = new RegExp(/^[a-z0-9]+$/i); return reg.test(value); }, //maximum value max: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } return parseFloat(value) <= parameters; }, //minimum value min: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } return parseFloat(value) >= parameters; }, //starts with value starts: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } return String(value).toLowerCase().startsWith(String(parameters).toLowerCase()); }, //ends with value ends: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } return String(value).toLowerCase().endsWith(String(parameters).toLowerCase()); }, //minimum string length minLength: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } return String(value).length >= parameters; }, //maximum string length maxLength: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } return String(value).length <= parameters; }, //in provided value list in: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } if (typeof parameters == "string") { parameters = parameters.split("|"); } return parameters.indexOf(value) > -1; }, //must match provided regex regex: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } var reg = new RegExp(parameters); return reg.test(value); }, //value must be unique in this column unique: function (cell, value, parameters) { if (value === "" || value === null || typeof value === "undefined") { return true; } var unique = true; var cellData = cell.getData(); var column = cell.getColumn()._getSelf(); this.table.rowManager.rows.forEach(function (row) { var data = row.getData(); if (data !== cellData) { if (value == column.getFieldValue(data)) { unique = false; } } }); return unique; }, //must have a value required: function (cell, value, parameters) { return value !== "" && value !== null && typeof value !== "undefined"; } }; const _Validate = class _Validate extends Module { constructor(table) { super(table); this.invalidCells = []; this.registerTableOption("validationMode", "blocking"); this.registerColumnOption("validator"); this.registerTableFunction("getInvalidCells", this.getInvalidCells.bind(this)); this.registerTableFunction("clearCellValidation", this.userClearCellValidation.bind(this)); this.registerTableFunction("validate", this.userValidate.bind(this)); this.registerComponentFunction("cell", "isValid", this.cellIsValid.bind(this)); this.registerComponentFunction("cell", "clearValidation", this.clearValidation.bind(this)); this.registerComponentFunction("cell", "validate", this.cellValidate.bind(this)); this.registerComponentFunction("column", "validate", this.columnValidate.bind(this)); this.registerComponentFunction("row", "validate", this.rowValidate.bind(this)); } initialize() { this.subscribe("cell-delete", this.clearValidation.bind(this)); this.subscribe("column-layout", this.initializeColumnCheck.bind(this)); this.subscribe("edit-success", this.editValidate.bind(this)); this.subscribe("edit-editor-clear", this.editorClear.bind(this)); this.subscribe("edit-edited-clear", this.editedClear.bind(this)); } /////////////////////////////////// ///////// Event Handling ////////// /////////////////////////////////// editValidate(cell, value, previousValue) { var valid = this.table.options.validationMode !== "manual" ? this.validate(cell.column.modules.validate, cell, value) : true; if (valid !== true) { setTimeout(() => { cell.getElement().classList.add("tabulator-validation-fail"); this.dispatchExternal("validationFailed", cell.getComponent(), value, valid); }); } return valid; } editorClear(cell, cancelled) { if (cancelled) { if (cell.column.modules.validate) { this.cellValidate(cell); } } cell.getElement().classList.remove("tabulator-validation-fail"); } editedClear(cell) { if (cell.modules.validate) { cell.modules.validate.invalid = false; } } /////////////////////////////////// ////////// Cell Functions ///////// /////////////////////////////////// cellIsValid(cell) { return cell.modules.validate ? cell.modules.validate.invalid || true : true; } cellValidate(cell) { return this.validate(cell.column.modules.validate, cell, cell.getValue()); } /////////////////////////////////// ///////// Column Functions //////// /////////////////////////////////// columnValidate(column) { var invalid = []; column.cells.forEach((cell) => { if (this.cellValidate(cell) !== true) { invalid.push(cell.getComponent()); } }); return invalid.length ? invalid : true; } /////////////////////////////////// ////////// Row Functions ////////// /////////////////////////////////// rowValidate(row) { var invalid = []; row.cells.forEach((cell) => { if (this.cellValidate(cell) !== true) { invalid.push(cell.getComponent()); } }); return invalid.length ? invalid : true; } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// userClearCellValidation(cells) { if (!cells) { cells = this.getInvalidCells(); } if (!Array.isArray(cells)) { cells = [cells]; } cells.forEach((cell) => { this.clearValidation(cell._getSelf()); }); } userValidate(cells) { var output = []; this.table.rowManager.rows.forEach((row) => { row = row.getComponent(); var valid = row.validate(); if (valid !== true) { output = output.concat(valid); } }); return output.length ? output : true; } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// initializeColumnCheck(column) { if (typeof column.definition.validator !== "undefined") { this.initializeColumn(column); } } //validate initializeColumn(column) { var self = this, config = [], validator; if (column.definition.validator) { if (Array.isArray(column.definition.validator)) { column.definition.validator.forEach((item) => { validator = self._extractValidator(item); if (validator) { config.push(validator); } }); } else { validator = this._extractValidator(column.definition.validator); if (validator) { config.push(validator); } } column.modules.validate = config.length ? config : false; } } _extractValidator(value) { var type, params, pos; switch (typeof value) { case "string": pos = value.indexOf(":"); if (pos > -1) { type = value.substring(0, pos); params = value.substring(pos + 1); } else { type = value; } return this._buildValidator(type, params); case "function": return this._buildValidator(value); case "object": return this._buildValidator(value.type, value.parameters); } } _buildValidator(type, params) { var func = typeof type == "function" ? type : _Validate.validators[type]; if (!func) { console.warn("Validator Setup Error - No matching validator found:", type); return false; } else { return { type: typeof type == "function" ? "function" : type, func, params }; } } validate(validators, cell, value) { var self = this, failedValidators = [], invalidIndex = this.invalidCells.indexOf(cell); if (validators) { validators.forEach((item) => { if (!item.func.call(self, cell.getComponent(), value, item.params)) { failedValidators.push({ type: item.type, parameters: item.params }); } }); } if (!cell.modules.validate) { cell.modules.validate = {}; } if (!failedValidators.length) { cell.modules.validate.invalid = false; cell.getElement().classList.remove("tabulator-validation-fail"); if (invalidIndex > -1) { this.invalidCells.splice(invalidIndex, 1); } } else { cell.modules.validate.invalid = failedValidators; if (this.table.options.validationMode !== "manual") { cell.getElement().classList.add("tabulator-validation-fail"); } if (invalidIndex == -1) { this.invalidCells.push(cell); } } return failedValidators.length ? failedValidators : true; } getInvalidCells() { var output = []; this.invalidCells.forEach((cell) => { output.push(cell.getComponent()); }); return output; } clearValidation(cell) { var invalidIndex; if (cell.modules.validate && cell.modules.validate.invalid) { cell.getElement().classList.remove("tabulator-validation-fail"); cell.modules.validate.invalid = false; invalidIndex = this.invalidCells.indexOf(cell); if (invalidIndex > -1) { this.invalidCells.splice(invalidIndex, 1); } } } }; __publicField(_Validate, "moduleName", "validate"); //load defaults __publicField(_Validate, "validators", defaultValidators); let Validate = _Validate; var allModules = /* @__PURE__ */ Object.freeze({ __proto__: null, AccessorModule: Accessor, AjaxModule: Ajax, ClipboardModule: Clipboard, ColumnCalcsModule: ColumnCalcs, DataTreeModule: DataTree, DownloadModule: Download, EditModule: Edit2, ExportModule: Export, FilterModule: Filter, FormatModule: Format, FrozenColumnsModule: FrozenColumns, FrozenRowsModule: FrozenRows, GroupRowsModule: GroupRows, HistoryModule: History, HtmlTableImportModule: HtmlTableImport, ImportModule: Import, InteractionModule: Interaction, KeybindingsModule: Keybindings, MenuModule: Menu, MoveColumnsModule: MoveColumns, MoveRowsModule: MoveRows, MutatorModule: Mutator, PageModule: Page, PersistenceModule: Persistence, PopupModule: Popup2, PrintModule: Print, ReactiveDataModule: ReactiveData, ResizeColumnsModule: ResizeColumns, ResizeRowsModule: ResizeRows, ResizeTableModule: ResizeTable, ResponsiveLayoutModule: ResponsiveLayout, SelectRangeModule: SelectRange, SelectRowModule: SelectRow, SortModule: Sort, SpreadsheetModule: Spreadsheet, TooltipModule: Tooltip, ValidateModule: Validate }); var defaultOptions = { debugEventsExternal: false, //flag to console log events debugEventsInternal: false, //flag to console log events debugInvalidOptions: true, //allow toggling of invalid option warnings debugInvalidComponentFuncs: true, //allow toggling of invalid component warnings debugInitialization: true, //allow toggling of pre initialization function call warnings debugDeprecation: true, //allow toggling of deprecation warnings height: false, //height of tabulator minHeight: false, //minimum height of tabulator maxHeight: false, //maximum height of tabulator columnHeaderVertAlign: "top", //vertical alignment of column headers popupContainer: false, columns: [], //store for colum header info columnDefaults: {}, //store column default props rowHeader: false, data: false, //default starting data autoColumns: false, //build columns from data row structure autoColumnsDefinitions: false, nestedFieldSeparator: ".", //separator for nested data footerElement: false, //hold footer element index: "id", //filed for row index textDirection: "auto", addRowPos: "bottom", //position to insert blank rows, top|bottom headerVisible: true, //hide header renderVertical: "virtual", renderHorizontal: "basic", renderVerticalBuffer: 0, // set virtual DOM buffer size scrollToRowPosition: "top", scrollToRowIfVisible: true, scrollToColumnPosition: "left", scrollToColumnIfVisible: true, rowFormatter: false, rowFormatterPrint: null, rowFormatterClipboard: null, rowFormatterHtmlOutput: null, rowHeight: null, placeholder: false, dataLoader: true, dataLoaderLoading: false, dataLoaderError: false, dataLoaderErrorTimeout: 3e3, dataSendParams: {}, dataReceiveParams: {}, dependencies: {} }; class OptionsList { constructor(table, msgType, defaults = {}) { this.table = table; this.msgType = msgType; this.registeredDefaults = Object.assign({}, defaults); } register(option, value) { this.registeredDefaults[option] = value; } generate(defaultOptions2, userOptions = {}) { var output = Object.assign({}, this.registeredDefaults), warn = this.table.options.debugInvalidOptions || userOptions.debugInvalidOptions === true; Object.assign(output, defaultOptions2); for (let key in userOptions) { if (!output.hasOwnProperty(key)) { if (warn) { console.warn("Invalid " + this.msgType + " option:", key); } output[key] = userOptions.key; } } for (let key in output) { if (key in userOptions) { output[key] = userOptions[key]; } else { if (Array.isArray(output[key])) { output[key] = Object.assign([], output[key]); } else if (typeof output[key] === "object" && output[key] !== null) { output[key] = Object.assign({}, output[key]); } else if (typeof output[key] === "undefined") { delete output[key]; } } } return output; } } class Renderer extends CoreFeature { constructor(table) { super(table); this.elementVertical = table.rowManager.element; this.elementHorizontal = table.columnManager.element; this.tableElement = table.rowManager.tableElement; this.verticalFillMode = "fit"; } /////////////////////////////////// /////// Internal Bindings ///////// /////////////////////////////////// initialize() { } clearRows() { } clearColumns() { } reinitializeColumnWidths(columns) { } renderRows() { } renderColumns() { } rerenderRows(callback) { if (callback) { callback(); } } rerenderColumns(update, blockRedraw) { } renderRowCells(row) { } rerenderRowCells(row, force) { } scrollColumns(left, dir) { } scrollRows(top, dir) { } resize() { } scrollToRow(row) { } scrollToRowNearestTop(row) { } visibleRows(includingBuffer) { return []; } /////////////////////////////////// //////// Helper Functions ///////// /////////////////////////////////// rows() { return this.table.rowManager.getDisplayRows(); } styleRow(row, index) { var rowEl = row.getElement(); if (index % 2) { rowEl.classList.add("tabulator-row-even"); rowEl.classList.remove("tabulator-row-odd"); } else { rowEl.classList.add("tabulator-row-odd"); rowEl.classList.remove("tabulator-row-even"); } } /////////////////////////////////// /////// External Triggers ///////// /////// (DO NOT OVERRIDE) ///////// /////////////////////////////////// clear() { this.clearRows(); this.clearColumns(); } render() { this.renderRows(); this.renderColumns(); } rerender(callback) { this.rerenderRows(); this.rerenderColumns(); } scrollToRowPosition(row, position, ifVisible) { var rowIndex = this.rows().indexOf(row), rowEl = row.getElement(), offset = 0; return new Promise((resolve, reject) => { if (rowIndex > -1) { if (typeof ifVisible === "undefined") { ifVisible = this.table.options.scrollToRowIfVisible; } if (!ifVisible) { if (Helpers.elVisible(rowEl)) { offset = Helpers.elOffset(rowEl).top - Helpers.elOffset(this.elementVertical).top; if (offset > 0 && offset < this.elementVertical.clientHeight - rowEl.offsetHeight) { resolve(); return false; } } } if (typeof position === "undefined") { position = this.table.options.scrollToRowPosition; } if (position === "nearest") { position = this.scrollToRowNearestTop(row) ? "top" : "bottom"; } this.scrollToRow(row); switch (position) { case "middle": case "center": if (this.elementVertical.scrollHeight - this.elementVertical.scrollTop == this.elementVertical.clientHeight) { this.elementVertical.scrollTop = this.elementVertical.scrollTop + (rowEl.offsetTop - this.elementVertical.scrollTop) - (this.elementVertical.scrollHeight - rowEl.offsetTop) / 2; } else { this.elementVertical.scrollTop = this.elementVertical.scrollTop - this.elementVertical.clientHeight / 2; } break; case "bottom": if (this.elementVertical.scrollHeight - this.elementVertical.scrollTop == this.elementVertical.clientHeight) { this.elementVertical.scrollTop = this.elementVertical.scrollTop - (this.elementVertical.scrollHeight - rowEl.offsetTop) + rowEl.offsetHeight; } else { this.elementVertical.scrollTop = this.elementVertical.scrollTop - this.elementVertical.clientHeight + rowEl.offsetHeight; } break; case "top": this.elementVertical.scrollTop = rowEl.offsetTop; break; } resolve(); } else { console.warn("Scroll Error - Row not visible"); reject("Scroll Error - Row not visible"); } }); } } class BasicHorizontal extends Renderer { constructor(table) { super(table); } renderRowCells(row, inFragment) { const rowFrag = document.createDocumentFragment(); row.cells.forEach((cell) => { rowFrag.appendChild(cell.getElement()); }); row.element.appendChild(rowFrag); if (!inFragment) { row.cells.forEach((cell) => { cell.cellRendered(); }); } } reinitializeColumnWidths(columns) { columns.forEach(function (column) { column.reinitializeWidth(); }); } } class VirtualDomHorizontal extends Renderer { constructor(table) { super(table); this.leftCol = 0; this.rightCol = 0; this.scrollLeft = 0; this.vDomScrollPosLeft = 0; this.vDomScrollPosRight = 0; this.vDomPadLeft = 0; this.vDomPadRight = 0; this.fitDataColAvg = 0; this.windowBuffer = 200; this.visibleRows = null; this.initialized = false; this.isFitData = false; this.columns = []; } initialize() { this.compatibilityCheck(); this.layoutCheck(); this.vertScrollListen(); } compatibilityCheck() { if (this.options("layout") == "fitDataTable") { console.warn("Horizontal Virtual DOM is not compatible with fitDataTable layout mode"); } if (this.options("responsiveLayout")) { console.warn("Horizontal Virtual DOM is not compatible with responsive columns"); } if (this.options("rtl")) { console.warn("Horizontal Virtual DOM is not currently compatible with RTL text direction"); } } layoutCheck() { this.isFitData = this.options("layout").startsWith("fitData"); } vertScrollListen() { this.subscribe("scroll-vertical", this.clearVisRowCache.bind(this)); this.subscribe("data-refreshed", this.clearVisRowCache.bind(this)); } clearVisRowCache() { this.visibleRows = null; } ////////////////////////////////////// ///////// Public Functions /////////// ////////////////////////////////////// renderColumns(row, force) { this.dataChange(); } scrollColumns(left, dir) { if (this.scrollLeft != left) { this.scrollLeft = left; this.scroll(left - (this.vDomScrollPosLeft + this.windowBuffer)); } } calcWindowBuffer() { var buffer = this.elementVertical.clientWidth; this.table.columnManager.columnsByIndex.forEach((column) => { if (column.visible) { var width = column.getWidth(); if (width > buffer) { buffer = width; } } }); this.windowBuffer = buffer * 2; } rerenderColumns(update, blockRedraw) { var old = { cols: this.columns, leftCol: this.leftCol, rightCol: this.rightCol }, colPos = 0; if (update && !this.initialized) { return; } this.clear(); this.calcWindowBuffer(); this.scrollLeft = this.elementVertical.scrollLeft; this.vDomScrollPosLeft = this.scrollLeft - this.windowBuffer; this.vDomScrollPosRight = this.scrollLeft + this.elementVertical.clientWidth + this.windowBuffer; this.table.columnManager.columnsByIndex.forEach((column) => { var config = {}, width; if (column.visible) { if (!column.modules.frozen) { width = column.getWidth(); config.leftPos = colPos; config.rightPos = colPos + width; config.width = width; if (this.isFitData) { config.fitDataCheck = column.modules.vdomHoz ? column.modules.vdomHoz.fitDataCheck : true; } if (colPos + width > this.vDomScrollPosLeft && colPos < this.vDomScrollPosRight) { if (this.leftCol == -1) { this.leftCol = this.columns.length; this.vDomPadLeft = colPos; } this.rightCol = this.columns.length; } else { if (this.leftCol !== -1) { this.vDomPadRight += width; } } this.columns.push(column); column.modules.vdomHoz = config; colPos += width; } } }); this.tableElement.style.paddingLeft = this.vDomPadLeft + "px"; this.tableElement.style.paddingRight = this.vDomPadRight + "px"; this.initialized = true; if (!blockRedraw) { if (!update || this.reinitChanged(old)) { this.reinitializeRows(); } } this.elementVertical.scrollLeft = this.scrollLeft; } renderRowCells(row) { if (this.initialized) { this.initializeRow(row); } else { const rowFrag = document.createDocumentFragment(); row.cells.forEach((cell) => { rowFrag.appendChild(cell.getElement()); }); row.element.appendChild(rowFrag); row.cells.forEach((cell) => { cell.cellRendered(); }); } } rerenderRowCells(row, force) { this.reinitializeRow(row, force); } reinitializeColumnWidths(columns) { for (let i = this.leftCol; i <= this.rightCol; i++) { let col = this.columns[i]; if (col) { col.reinitializeWidth(); } } } ////////////////////////////////////// //////// Internal Rendering ////////// ////////////////////////////////////// deinitialize() { this.initialized = false; } clear() { this.columns = []; this.leftCol = -1; this.rightCol = 0; this.vDomScrollPosLeft = 0; this.vDomScrollPosRight = 0; this.vDomPadLeft = 0; this.vDomPadRight = 0; } dataChange() { var change = false, row, rowEl; if (this.isFitData) { this.table.columnManager.columnsByIndex.forEach((column) => { if (!column.definition.width && column.visible) { change = true; } }); if (change && this.table.rowManager.getDisplayRows().length) { this.vDomScrollPosRight = this.scrollLeft + this.elementVertical.clientWidth + this.windowBuffer; row = this.chain("rows-sample", [1], [], () => { return this.table.rowManager.getDisplayRows(); })[0]; if (row) { rowEl = row.getElement(); row.generateCells(); this.tableElement.appendChild(rowEl); for (let colEnd = 0; colEnd < row.cells.length; colEnd++) { let cell = row.cells[colEnd]; rowEl.appendChild(cell.getElement()); cell.column.reinitializeWidth(); } rowEl.parentNode.removeChild(rowEl); this.rerenderColumns(false, true); } } } else { if (this.options("layout") === "fitColumns") { this.layoutRefresh(); this.rerenderColumns(false, true); } } } reinitChanged(old) { var match = true; if (old.cols.length !== this.columns.length || old.leftCol !== this.leftCol || old.rightCol !== this.rightCol) { return true; } old.cols.forEach((col, i) => { if (col !== this.columns[i]) { match = false; } }); return !match; } reinitializeRows() { var visibleRows = this.getVisibleRows(), otherRows = this.table.rowManager.getRows().filter((row) => !visibleRows.includes(row)); visibleRows.forEach((row) => { this.reinitializeRow(row, true); }); otherRows.forEach((row) => { row.deinitialize(); }); } getVisibleRows() { if (!this.visibleRows) { this.visibleRows = this.table.rowManager.getVisibleRows(); } return this.visibleRows; } scroll(diff) { this.vDomScrollPosLeft += diff; this.vDomScrollPosRight += diff; if (Math.abs(diff) > this.windowBuffer / 2) { this.rerenderColumns(); } else { if (diff > 0) { this.addColRight(); this.removeColLeft(); } else { this.addColLeft(); this.removeColRight(); } } } colPositionAdjust(start, end, diff) { for (let i = start; i < end; i++) { let column = this.columns[i]; column.modules.vdomHoz.leftPos += diff; column.modules.vdomHoz.rightPos += diff; } } addColRight() { var changes = false, working = true; while (working) { let column = this.columns[this.rightCol + 1]; if (column) { if (column.modules.vdomHoz.leftPos <= this.vDomScrollPosRight) { changes = true; this.getVisibleRows().forEach((row) => { if (row.type !== "group") { var cell = row.getCell(column); row.getElement().insertBefore(cell.getElement(), row.getCell(this.columns[this.rightCol]).getElement().nextSibling); cell.cellRendered(); } }); this.fitDataColActualWidthCheck(column); this.rightCol++; this.getVisibleRows().forEach((row) => { if (row.type !== "group") { row.modules.vdomHoz.rightCol = this.rightCol; } }); if (this.rightCol >= this.columns.length - 1) { this.vDomPadRight = 0; } else { this.vDomPadRight -= column.getWidth(); } } else { working = false; } } else { working = false; } } if (changes) { this.tableElement.style.paddingRight = this.vDomPadRight + "px"; } } addColLeft() { var changes = false, working = true; while (working) { let column = this.columns[this.leftCol - 1]; if (column) { if (column.modules.vdomHoz.rightPos >= this.vDomScrollPosLeft) { changes = true; this.getVisibleRows().forEach((row) => { if (row.type !== "group") { var cell = row.getCell(column); row.getElement().insertBefore(cell.getElement(), row.getCell(this.columns[this.leftCol]).getElement()); cell.cellRendered(); } }); this.leftCol--; this.getVisibleRows().forEach((row) => { if (row.type !== "group") { row.modules.vdomHoz.leftCol = this.leftCol; } }); if (this.leftCol <= 0) { this.vDomPadLeft = 0; } else { this.vDomPadLeft -= column.getWidth(); } let diff = this.fitDataColActualWidthCheck(column); if (diff) { this.scrollLeft = this.elementVertical.scrollLeft = this.elementVertical.scrollLeft + diff; this.vDomPadRight -= diff; } } else { working = false; } } else { working = false; } } if (changes) { this.tableElement.style.paddingLeft = this.vDomPadLeft + "px"; } } removeColRight() { var changes = false, working = true; while (working) { let column = this.columns[this.rightCol]; if (column) { if (column.modules.vdomHoz.leftPos > this.vDomScrollPosRight) { changes = true; this.getVisibleRows().forEach((row) => { if (row.type !== "group") { var cell = row.getCell(column); try { row.getElement().removeChild(cell.getElement()); } catch (ex) { console.warn("Could not removeColRight", ex.message); } } }); this.vDomPadRight += column.getWidth(); this.rightCol--; this.getVisibleRows().forEach((row) => { if (row.type !== "group") { row.modules.vdomHoz.rightCol = this.rightCol; } }); } else { working = false; } } else { working = false; } } if (changes) { this.tableElement.style.paddingRight = this.vDomPadRight + "px"; } } removeColLeft() { var changes = false, working = true; while (working) { let column = this.columns[this.leftCol]; if (column) { if (column.modules.vdomHoz.rightPos < this.vDomScrollPosLeft) { changes = true; this.getVisibleRows().forEach((row) => { if (row.type !== "group") { var cell = row.getCell(column); try { row.getElement().removeChild(cell.getElement()); } catch (ex) { console.warn("Could not removeColLeft", ex.message); } } }); this.vDomPadLeft += column.getWidth(); this.leftCol++; this.getVisibleRows().forEach((row) => { if (row.type !== "group") { row.modules.vdomHoz.leftCol = this.leftCol; } }); } else { working = false; } } else { working = false; } } if (changes) { this.tableElement.style.paddingLeft = this.vDomPadLeft + "px"; } } fitDataColActualWidthCheck(column) { var newWidth, widthDiff; if (column.modules.vdomHoz.fitDataCheck) { column.reinitializeWidth(); newWidth = column.getWidth(); widthDiff = newWidth - column.modules.vdomHoz.width; if (widthDiff) { column.modules.vdomHoz.rightPos += widthDiff; column.modules.vdomHoz.width = newWidth; this.colPositionAdjust(this.columns.indexOf(column) + 1, this.columns.length, widthDiff); } column.modules.vdomHoz.fitDataCheck = false; } return widthDiff; } initializeRow(row) { if (row.type !== "group") { row.modules.vdomHoz = { leftCol: this.leftCol, rightCol: this.rightCol }; if (this.table.modules.frozenColumns) { this.table.modules.frozenColumns.leftColumns.forEach((column) => { this.appendCell(row, column); }); } for (let i = this.leftCol; i <= this.rightCol; i++) { this.appendCell(row, this.columns[i]); } if (this.table.modules.frozenColumns) { this.table.modules.frozenColumns.rightColumns.forEach((column) => { this.appendCell(row, column); }); } } } appendCell(row, column) { if (column && column.visible) { let cell = row.getCell(column); row.getElement().appendChild(cell.getElement()); cell.cellRendered(); } } reinitializeRow(row, force) { if (row.type !== "group") { if (force || !row.modules.vdomHoz || row.modules.vdomHoz.leftCol !== this.leftCol || row.modules.vdomHoz.rightCol !== this.rightCol) { var rowEl = row.getElement(); while (rowEl.firstChild) rowEl.removeChild(rowEl.firstChild); this.initializeRow(row); } } } } class ColumnManager extends CoreFeature { constructor(table) { super(table); this.blockHozScrollEvent = false; this.headersElement = null; this.contentsElement = null; this.rowHeader = null; this.element = null; this.columns = []; this.columnsByIndex = []; this.columnsByField = {}; this.scrollLeft = 0; this.optionsList = new OptionsList(this.table, "column definition", defaultColumnOptions); this.redrawBlock = false; this.redrawBlockUpdate = null; this.renderer = null; } ////////////// Setup Functions ///////////////// initialize() { this.initializeRenderer(); this.headersElement = this.createHeadersElement(); this.contentsElement = this.createHeaderContentsElement(); this.element = this.createHeaderElement(); this.contentsElement.insertBefore(this.headersElement, this.contentsElement.firstChild); this.element.insertBefore(this.contentsElement, this.element.firstChild); this.initializeScrollWheelWatcher(); this.subscribe("scroll-horizontal", this.scrollHorizontal.bind(this)); this.subscribe("scrollbar-vertical", this.padVerticalScrollbar.bind(this)); } padVerticalScrollbar(width) { if (this.table.rtl) { this.headersElement.style.marginLeft = width + "px"; } else { this.headersElement.style.marginRight = width + "px"; } } initializeRenderer() { var renderClass; var renderers = { "virtual": VirtualDomHorizontal, "basic": BasicHorizontal }; if (typeof this.table.options.renderHorizontal === "string") { renderClass = renderers[this.table.options.renderHorizontal]; } else { renderClass = this.table.options.renderHorizontal; } if (renderClass) { this.renderer = new renderClass(this.table, this.element, this.tableElement); this.renderer.initialize(); } else { console.error("Unable to find matching renderer:", this.table.options.renderHorizontal); } } createHeadersElement() { var el = document.createElement("div"); el.classList.add("tabulator-headers"); el.setAttribute("role", "row"); return el; } createHeaderContentsElement() { var el = document.createElement("div"); el.classList.add("tabulator-header-contents"); return el; } createHeaderElement() { var el = document.createElement("div"); el.classList.add("tabulator-header"); el.setAttribute("role", "rowgroup"); if (!this.table.options.headerVisible) { el.classList.add("tabulator-header-hidden"); } return el; } //return containing element getElement() { return this.element; } //return containing contents element getContentsElement() { return this.contentsElement; } //return header containing element getHeadersElement() { return this.headersElement; } //scroll horizontally to match table body scrollHorizontal(left) { this.contentsElement.scrollLeft = left; this.scrollLeft = left; this.renderer.scrollColumns(left); } initializeScrollWheelWatcher() { this.contentsElement.addEventListener("wheel", (e) => { var left; if (e.deltaX) { left = this.contentsElement.scrollLeft + e.deltaX; this.table.rowManager.scrollHorizontal(left); this.table.columnManager.scrollHorizontal(left); } }); } ///////////// Column Setup Functions ///////////// generateColumnsFromRowData(data) { var cols = [], collProgress = {}, rowSample = this.table.options.autoColumns === "full" ? data : [data[0]], definitions = this.table.options.autoColumnsDefinitions; if (data && data.length) { rowSample.forEach((row) => { Object.keys(row).forEach((key, index) => { let value = row[key], col; if (!collProgress[key]) { col = { field: key, title: key, sorter: this.calculateSorterFromValue(value) }; cols.splice(index, 0, col); collProgress[key] = typeof value === "undefined" ? col : true; } else if (collProgress[key] !== true) { if (typeof value !== "undefined") { collProgress[key].sorter = this.calculateSorterFromValue(value); collProgress[key] = true; } } }); }); if (definitions) { switch (typeof definitions) { case "function": this.table.options.columns = definitions.call(this.table, cols); break; case "object": if (Array.isArray(definitions)) { cols.forEach((col) => { var match = definitions.find((def) => { return def.field === col.field; }); if (match) { Object.assign(col, match); } }); } else { cols.forEach((col) => { if (definitions[col.field]) { Object.assign(col, definitions[col.field]); } }); } this.table.options.columns = cols; break; } } else { this.table.options.columns = cols; } this.setColumns(this.table.options.columns); } } calculateSorterFromValue(value) { var sorter; switch (typeof value) { case "undefined": sorter = "string"; break; case "boolean": sorter = "boolean"; break; case "number": sorter = "number"; break; case "object": if (Array.isArray(value)) { sorter = "array"; } else { sorter = "string"; } break; default: if (!isNaN(Number(value)) && value !== "") { sorter = "number"; } else { if (value.match(/((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+$/i)) { sorter = "alphanum"; } else { sorter = "string"; } } break; } return sorter; } setColumns(cols, row) { while (this.headersElement.firstChild) this.headersElement.removeChild(this.headersElement.firstChild); this.columns = []; this.columnsByIndex = []; this.columnsByField = {}; this.dispatch("columns-loading"); this.dispatchExternal("columnsLoading"); if (this.table.options.rowHeader) { this.rowHeader = new Column(this.table.options.rowHeader === true ? {} : this.table.options.rowHeader, this, true); this.columns.push(this.rowHeader); this.headersElement.appendChild(this.rowHeader.getElement()); this.rowHeader.columnRendered(); } cols.forEach((def, i) => { this._addColumn(def); }); this._reIndexColumns(); this.dispatch("columns-loaded"); if (this.subscribedExternal("columnsLoaded")) { this.dispatchExternal("columnsLoaded", this.getComponents()); } this.rerenderColumns(false, true); this.redraw(true); } _addColumn(definition, before, nextToColumn) { var column = new Column(definition, this), colEl = column.getElement(), index = nextToColumn ? this.findColumnIndex(nextToColumn) : nextToColumn; if (before && this.rowHeader && (!nextToColumn || nextToColumn === this.rowHeader)) { before = false; nextToColumn = this.rowHeader; index = 0; } if (nextToColumn && index > -1) { var topColumn = nextToColumn.getTopColumn(); var parentIndex = this.columns.indexOf(topColumn); var nextEl = topColumn.getElement(); if (before) { this.columns.splice(parentIndex, 0, column); nextEl.parentNode.insertBefore(colEl, nextEl); } else { this.columns.splice(parentIndex + 1, 0, column); nextEl.parentNode.insertBefore(colEl, nextEl.nextSibling); } } else { if (before) { this.columns.unshift(column); this.headersElement.insertBefore(column.getElement(), this.headersElement.firstChild); } else { this.columns.push(column); this.headersElement.appendChild(column.getElement()); } } column.columnRendered(); return column; } registerColumnField(col) { if (col.definition.field) { this.columnsByField[col.definition.field] = col; } } registerColumnPosition(col) { this.columnsByIndex.push(col); } _reIndexColumns() { this.columnsByIndex = []; this.columns.forEach(function (column) { column.reRegisterPosition(); }); } //ensure column headers take up the correct amount of space in column groups verticalAlignHeaders() { var minHeight = 0; if (!this.redrawBlock) { this.headersElement.style.height = ""; this.columns.forEach((column) => { column.clearVerticalAlign(); }); this.columns.forEach((column) => { var height = column.getHeight(); if (height > minHeight) { minHeight = height; } }); this.headersElement.style.height = minHeight + "px"; this.columns.forEach((column) => { column.verticalAlign(this.table.options.columnHeaderVertAlign, minHeight); }); this.table.rowManager.adjustTableSize(); } } //////////////// Column Details ///////////////// findColumn(subject) { var columns; if (typeof subject == "object") { if (subject instanceof Column) { return subject; } else if (subject instanceof ColumnComponent) { return subject._getSelf() || false; } else if (typeof HTMLElement !== "undefined" && subject instanceof HTMLElement) { columns = []; this.columns.forEach((column) => { columns.push(column); columns = columns.concat(column.getColumns(true)); }); let match = columns.find((column) => { return column.element === subject; }); return match || false; } } else { return this.columnsByField[subject] || false; } return false; } getColumnByField(field) { return this.columnsByField[field]; } getColumnsByFieldRoot(root) { var matches = []; Object.keys(this.columnsByField).forEach((field) => { var fieldRoot = this.table.options.nestedFieldSeparator ? field.split(this.table.options.nestedFieldSeparator)[0] : field; if (fieldRoot === root) { matches.push(this.columnsByField[field]); } }); return matches; } getColumnByIndex(index) { return this.columnsByIndex[index]; } getFirstVisibleColumn() { var index = this.columnsByIndex.findIndex((col) => { return col.visible; }); return index > -1 ? this.columnsByIndex[index] : false; } getVisibleColumnsByIndex() { return this.columnsByIndex.filter((col) => col.visible); } getColumns() { return this.columns; } findColumnIndex(column) { return this.columnsByIndex.findIndex((col) => { return column === col; }); } //return all columns that are not groups getRealColumns() { return this.columnsByIndex; } //traverse across columns and call action traverse(callback) { this.columnsByIndex.forEach((column, i) => { callback(column, i); }); } //get definitions of actual columns getDefinitions(active) { var output = []; this.columnsByIndex.forEach((column) => { if (!active || active && column.visible) { output.push(column.getDefinition()); } }); return output; } //get full nested definition tree getDefinitionTree() { var output = []; this.columns.forEach((column) => { output.push(column.getDefinition(true)); }); return output; } getComponents(structured) { var output = [], columns = structured ? this.columns : this.columnsByIndex; columns.forEach((column) => { output.push(column.getComponent()); }); return output; } getWidth() { var width = 0; this.columnsByIndex.forEach((column) => { if (column.visible) { width += column.getWidth(); } }); return width; } moveColumn(from, to, after) { to.element.parentNode.insertBefore(from.element, to.element); if (after) { to.element.parentNode.insertBefore(to.element, from.element); } this.moveColumnActual(from, to, after); this.verticalAlignHeaders(); this.table.rowManager.reinitialize(); } moveColumnActual(from, to, after) { if (from.parent.isGroup) { this._moveColumnInArray(from.parent.columns, from, to, after); } else { this._moveColumnInArray(this.columns, from, to, after); } this._moveColumnInArray(this.columnsByIndex, from, to, after, true); this.rerenderColumns(true); this.dispatch("column-moved", from, to, after); if (this.subscribedExternal("columnMoved")) { this.dispatchExternal("columnMoved", from.getComponent(), this.table.columnManager.getComponents()); } } _moveColumnInArray(columns, from, to, after, updateRows) { var fromIndex = columns.indexOf(from), toIndex, rows2 = []; if (fromIndex > -1) { columns.splice(fromIndex, 1); toIndex = columns.indexOf(to); if (toIndex > -1) { if (after) { toIndex = toIndex + 1; } } else { toIndex = fromIndex; } columns.splice(toIndex, 0, from); if (updateRows) { rows2 = this.chain("column-moving-rows", [from, to, after], null, []) || []; rows2 = rows2.concat(this.table.rowManager.rows); rows2.forEach(function (row) { if (row.cells.length) { var cell = row.cells.splice(fromIndex, 1)[0]; row.cells.splice(toIndex, 0, cell); } }); } } } scrollToColumn(column, position, ifVisible) { var left = 0, offset = column.getLeftOffset(), adjust = 0, colEl = column.getElement(); return new Promise((resolve, reject) => { if (typeof position === "undefined") { position = this.table.options.scrollToColumnPosition; } if (typeof ifVisible === "undefined") { ifVisible = this.table.options.scrollToColumnIfVisible; } if (column.visible) { switch (position) { case "middle": case "center": adjust = -this.element.clientWidth / 2; break; case "right": adjust = colEl.clientWidth - this.headersElement.clientWidth; break; } if (!ifVisible) { if (offset > 0 && offset + colEl.offsetWidth < this.element.clientWidth) { return false; } } left = offset + adjust; left = Math.max(Math.min(left, this.table.rowManager.element.scrollWidth - this.table.rowManager.element.clientWidth), 0); this.table.rowManager.scrollHorizontal(left); this.scrollHorizontal(left); resolve(); } else { console.warn("Scroll Error - Column not visible"); reject("Scroll Error - Column not visible"); } }); } //////////////// Cell Management ///////////////// generateCells(row) { var cells = []; this.columnsByIndex.forEach((column) => { cells.push(column.generateCell(row)); }); return cells; } //////////////// Column Management ///////////////// getFlexBaseWidth() { var totalWidth = this.table.element.clientWidth, fixedWidth = 0; if (this.table.rowManager.element.scrollHeight > this.table.rowManager.element.clientHeight) { totalWidth -= this.table.rowManager.element.offsetWidth - this.table.rowManager.element.clientWidth; } this.columnsByIndex.forEach(function (column) { var width, minWidth, colWidth; if (column.visible) { width = column.definition.width || 0; minWidth = parseInt(column.minWidth); if (typeof width == "string") { if (width.indexOf("%") > -1) { colWidth = totalWidth / 100 * parseInt(width); } else { colWidth = parseInt(width); } } else { colWidth = width; } fixedWidth += colWidth > minWidth ? colWidth : minWidth; } }); return fixedWidth; } addColumn(definition, before, nextToColumn) { return new Promise((resolve, reject) => { var column = this._addColumn(definition, before, nextToColumn); this._reIndexColumns(); this.dispatch("column-add", definition, before, nextToColumn); if (this.layoutMode() != "fitColumns") { column.reinitializeWidth(); } this.redraw(true); this.table.rowManager.reinitialize(); this.rerenderColumns(); resolve(column); }); } //remove column from system deregisterColumn(column) { var field = column.getField(), index; if (field) { delete this.columnsByField[field]; } index = this.columnsByIndex.indexOf(column); if (index > -1) { this.columnsByIndex.splice(index, 1); } index = this.columns.indexOf(column); if (index > -1) { this.columns.splice(index, 1); } this.verticalAlignHeaders(); this.redraw(); } rerenderColumns(update, silent) { if (!this.redrawBlock) { this.renderer.rerenderColumns(update, silent); } else { if (update === false || update === true && this.redrawBlockUpdate === null) { this.redrawBlockUpdate = update; } } } blockRedraw() { this.redrawBlock = true; this.redrawBlockUpdate = null; } restoreRedraw() { this.redrawBlock = false; this.verticalAlignHeaders(); this.renderer.rerenderColumns(this.redrawBlockUpdate); } //redraw columns redraw(force) { if (Helpers.elVisible(this.element)) { this.verticalAlignHeaders(); } if (force) { this.table.rowManager.resetScroll(); this.table.rowManager.reinitialize(); } if (!this.confirm("table-redrawing", force)) { this.layoutRefresh(force); } this.dispatch("table-redraw", force); this.table.footerManager.redraw(); } } class BasicVertical extends Renderer { constructor(table) { super(table); this.verticalFillMode = "fill"; this.scrollTop = 0; this.scrollLeft = 0; this.scrollTop = 0; this.scrollLeft = 0; } clearRows() { var element = this.tableElement; while (element.firstChild) element.removeChild(element.firstChild); element.scrollTop = 0; element.scrollLeft = 0; element.style.minWidth = ""; element.style.minHeight = ""; element.style.display = ""; element.style.visibility = ""; } renderRows() { var element = this.tableElement, onlyGroupHeaders = true, tableFrag = document.createDocumentFragment(), rows2 = this.rows(); rows2.forEach((row, index) => { this.styleRow(row, index); row.initialize(false, true); if (row.type !== "group") { onlyGroupHeaders = false; } tableFrag.appendChild(row.getElement()); }); element.appendChild(tableFrag); rows2.forEach((row) => { row.rendered(); if (!row.heightInitialized) { row.calcHeight(true); } }); rows2.forEach((row) => { if (!row.heightInitialized) { row.setCellHeight(); } }); if (onlyGroupHeaders) { element.style.minWidth = this.table.columnManager.getWidth() + "px"; } else { element.style.minWidth = ""; } } rerenderRows(callback) { this.clearRows(); if (callback) { callback(); } this.renderRows(); if (!this.rows().length) { this.table.rowManager.tableEmpty(); } } scrollToRowNearestTop(row) { var rowTop = Helpers.elOffset(row.getElement()).top; return !(Math.abs(this.elementVertical.scrollTop - rowTop) > Math.abs(this.elementVertical.scrollTop + this.elementVertical.clientHeight - rowTop)); } scrollToRow(row) { var rowEl = row.getElement(); this.elementVertical.scrollTop = Helpers.elOffset(rowEl).top - Helpers.elOffset(this.elementVertical).top + this.elementVertical.scrollTop; } visibleRows(includingBuffer) { return this.rows(); } } class VirtualDomVertical extends Renderer { constructor(table) { super(table); this.verticalFillMode = "fill"; this.scrollTop = 0; this.scrollLeft = 0; this.vDomRowHeight = 20; this.vDomTop = 0; this.vDomBottom = 0; this.vDomScrollPosTop = 0; this.vDomScrollPosBottom = 0; this.vDomTopPad = 0; this.vDomBottomPad = 0; this.vDomMaxRenderChain = 90; this.vDomWindowBuffer = 0; this.vDomWindowMinTotalRows = 20; this.vDomWindowMinMarginRows = 5; this.vDomTopNewRows = []; this.vDomBottomNewRows = []; } ////////////////////////////////////// ///////// Public Functions /////////// ////////////////////////////////////// clearRows() { var element = this.tableElement; while (element.firstChild) element.removeChild(element.firstChild); element.style.paddingTop = ""; element.style.paddingBottom = ""; element.style.minHeight = ""; element.style.display = ""; element.style.visibility = ""; this.elementVertical.scrollTop = 0; this.elementVertical.scrollLeft = 0; this.scrollTop = 0; this.scrollLeft = 0; this.vDomTop = 0; this.vDomBottom = 0; this.vDomTopPad = 0; this.vDomBottomPad = 0; this.vDomScrollPosTop = 0; this.vDomScrollPosBottom = 0; } renderRows() { this._virtualRenderFill(); } rerenderRows(callback) { var scrollTop = this.elementVertical.scrollTop; var topRow = false; var topOffset = false; var left = this.table.rowManager.scrollLeft; var rows2 = this.rows(); for (var i = this.vDomTop; i <= this.vDomBottom; i++) { if (rows2[i]) { var diff = scrollTop - rows2[i].getElement().offsetTop; if (topOffset === false || Math.abs(diff) < topOffset) { topOffset = diff; topRow = i; } else { break; } } } rows2.forEach((row) => { row.deinitializeHeight(); }); if (callback) { callback(); } if (this.rows().length) { this._virtualRenderFill(topRow === false ? this.rows.length - 1 : topRow, true, topOffset || 0); } else { this.clear(); this.table.rowManager.tableEmpty(); } this.scrollColumns(left); } scrollColumns(left) { this.table.rowManager.scrollHorizontal(left); } scrollRows(top, dir) { var topDiff = top - this.vDomScrollPosTop; var bottomDiff = top - this.vDomScrollPosBottom; var margin = this.vDomWindowBuffer * 2; var rows2 = this.rows(); this.scrollTop = top; if (-topDiff > margin || bottomDiff > margin) { var left = this.table.rowManager.scrollLeft; this._virtualRenderFill(Math.floor(this.elementVertical.scrollTop / this.elementVertical.scrollHeight * rows2.length)); this.scrollColumns(left); } else { if (dir) { if (topDiff < 0) { this._addTopRow(rows2, -topDiff); } if (bottomDiff < 0) { if (this.vDomScrollHeight - this.scrollTop > this.vDomWindowBuffer) { this._removeBottomRow(rows2, -bottomDiff); } else { this.vDomScrollPosBottom = this.scrollTop; } } } else { if (bottomDiff >= 0) { this._addBottomRow(rows2, bottomDiff); } if (topDiff >= 0) { if (this.scrollTop > this.vDomWindowBuffer) { this._removeTopRow(rows2, topDiff); } else { this.vDomScrollPosTop = this.scrollTop; } } } } } resize() { this.vDomWindowBuffer = this.table.options.renderVerticalBuffer || this.elementVertical.clientHeight; } scrollToRowNearestTop(row) { var rowIndex = this.rows().indexOf(row); return !(Math.abs(this.vDomTop - rowIndex) > Math.abs(this.vDomBottom - rowIndex)); } scrollToRow(row) { var index = this.rows().indexOf(row); if (index > -1) { this._virtualRenderFill(index, true); } } visibleRows(includingBuffer) { var topEdge = this.elementVertical.scrollTop, bottomEdge = this.elementVertical.clientHeight + topEdge, topFound = false, topRow = 0, bottomRow = 0, rows2 = this.rows(); if (includingBuffer) { topRow = this.vDomTop; bottomRow = this.vDomBottom; } else { for (var i = this.vDomTop; i <= this.vDomBottom; i++) { if (rows2[i]) { if (!topFound) { if (topEdge - rows2[i].getElement().offsetTop >= 0) { topRow = i; } else { topFound = true; if (bottomEdge - rows2[i].getElement().offsetTop >= 0) { bottomRow = i; } else { break; } } } else { if (bottomEdge - rows2[i].getElement().offsetTop >= 0) { bottomRow = i; } else { break; } } } } } return rows2.slice(topRow, bottomRow + 1); } ////////////////////////////////////// //////// Internal Rendering ////////// ////////////////////////////////////// //full virtual render _virtualRenderFill(position, forceMove, offset) { var element = this.tableElement, holder = this.elementVertical, topPad = 0, rowsHeight = 0, rowHeight = 0, heightOccupied = 0, topPadHeight = 0, i = 0, rows2 = this.rows(), rowsCount = rows2.length, index = 0, row, rowFragment, renderedRows = [], totalRowsRendered = 0, rowsToRender = 0, fixedHeight = this.table.rowManager.fixedHeight, containerHeight = this.elementVertical.clientHeight, avgRowHeight = this.table.options.rowHeight, resized = true; position = position || 0; offset = offset || 0; if (!position) { this.clear(); } else { while (element.firstChild) element.removeChild(element.firstChild); heightOccupied = (rowsCount - position + 1) * this.vDomRowHeight; if (heightOccupied < containerHeight) { position -= Math.ceil((containerHeight - heightOccupied) / this.vDomRowHeight); if (position < 0) { position = 0; } } topPad = Math.min(Math.max(Math.floor(this.vDomWindowBuffer / this.vDomRowHeight), this.vDomWindowMinMarginRows), position); position -= topPad; } if (rowsCount && Helpers.elVisible(this.elementVertical)) { this.vDomTop = position; this.vDomBottom = position - 1; if (fixedHeight || this.table.options.maxHeight) { if (avgRowHeight) { rowsToRender = containerHeight / avgRowHeight + this.vDomWindowBuffer / avgRowHeight; } rowsToRender = Math.max(this.vDomWindowMinTotalRows, Math.ceil(rowsToRender)); } else { rowsToRender = rowsCount; } while ((rowsToRender == rowsCount || rowsHeight <= containerHeight + this.vDomWindowBuffer || totalRowsRendered < this.vDomWindowMinTotalRows) && this.vDomBottom < rowsCount - 1) { renderedRows = []; rowFragment = document.createDocumentFragment(); i = 0; while (i < rowsToRender && this.vDomBottom < rowsCount - 1) { index = this.vDomBottom + 1, row = rows2[index]; this.styleRow(row, index); row.initialize(false, true); if (!row.heightInitialized && !this.table.options.rowHeight) { row.clearCellHeight(); } rowFragment.appendChild(row.getElement()); renderedRows.push(row); this.vDomBottom++; i++; } if (!renderedRows.length) { break; } element.appendChild(rowFragment); renderedRows.forEach((row2) => { row2.rendered(); }); const rowsNeedingHeightInit = []; renderedRows.forEach((row2) => { if (!row2.heightInitialized || !row2.getHeight()) { row2.calcHeight(true); rowsNeedingHeightInit.push(row2); } }); rowsNeedingHeightInit.forEach((row2) => { row2.setCellHeight(); }); renderedRows.forEach((row2) => { rowHeight = row2.getHeight() || this.vDomRowHeight; if (totalRowsRendered < topPad) { topPadHeight += rowHeight; } else { rowsHeight += rowHeight; } if (rowHeight > this.vDomWindowBuffer) { this.vDomWindowBuffer = rowHeight * 2; } totalRowsRendered++; }); resized = this.table.rowManager.adjustTableSize(); containerHeight = this.elementVertical.clientHeight; if (resized && (fixedHeight || this.table.options.maxHeight)) { avgRowHeight = rowsHeight / totalRowsRendered; rowsToRender = Math.max(this.vDomWindowMinTotalRows, Math.ceil(containerHeight / avgRowHeight + this.vDomWindowBuffer / avgRowHeight)); } } if (!position) { this.vDomTopPad = 0; this.vDomRowHeight = Math.floor((rowsHeight + topPadHeight) / totalRowsRendered); this.vDomBottomPad = this.vDomRowHeight * (rowsCount - this.vDomBottom - 1); this.vDomScrollHeight = topPadHeight + rowsHeight + this.vDomBottomPad - containerHeight; } else { this.vDomTopPad = !forceMove ? this.scrollTop - topPadHeight : this.vDomRowHeight * this.vDomTop + offset; this.vDomBottomPad = this.vDomBottom == rowsCount - 1 ? 0 : Math.max(this.vDomScrollHeight - this.vDomTopPad - rowsHeight - topPadHeight, 0); } element.style.paddingTop = this.vDomTopPad + "px"; element.style.paddingBottom = this.vDomBottomPad + "px"; if (forceMove) { this.scrollTop = this.vDomTopPad + topPadHeight + offset - (this.elementVertical.scrollWidth > this.elementVertical.clientWidth ? this.elementVertical.offsetHeight - containerHeight : 0); } this.scrollTop = Math.min(this.scrollTop, this.elementVertical.scrollHeight - containerHeight); if (this.elementVertical.scrollWidth > this.elementVertical.clientWidth && forceMove) { this.scrollTop += this.elementVertical.offsetHeight - containerHeight; } this.vDomScrollPosTop = this.scrollTop; this.vDomScrollPosBottom = this.scrollTop; holder.scrollTop = this.scrollTop; this.dispatch("render-virtual-fill"); } } _addTopRow(rows2, fillableSpace) { var table = this.tableElement, addedRows = [], paddingAdjust = 0, index = this.vDomTop - 1, i = 0, working = true; while (working) { if (this.vDomTop) { let row = rows2[index], rowHeight, initialized; if (row && i < this.vDomMaxRenderChain) { rowHeight = row.getHeight() || this.vDomRowHeight; initialized = row.initialized; if (fillableSpace >= rowHeight) { this.styleRow(row, index); table.insertBefore(row.getElement(), table.firstChild); if (!row.initialized || !row.heightInitialized) { addedRows.push(row); } row.initialize(); if (!initialized) { rowHeight = row.getElement().offsetHeight; if (rowHeight > this.vDomWindowBuffer) { this.vDomWindowBuffer = rowHeight * 2; } } fillableSpace -= rowHeight; paddingAdjust += rowHeight; this.vDomTop--; index--; i++; } else { working = false; } } else { working = false; } } else { working = false; } } for (let row of addedRows) { row.clearCellHeight(); } this._quickNormalizeRowHeight(addedRows); if (paddingAdjust) { this.vDomTopPad -= paddingAdjust; if (this.vDomTopPad < 0) { this.vDomTopPad = index * this.vDomRowHeight; } if (index < 1) { this.vDomTopPad = 0; } table.style.paddingTop = this.vDomTopPad + "px"; this.vDomScrollPosTop -= paddingAdjust; } } _removeTopRow(rows2, fillableSpace) { var removableRows = [], paddingAdjust = 0, i = 0, working = true; while (working) { let row = rows2[this.vDomTop], rowHeight; if (row && i < this.vDomMaxRenderChain) { rowHeight = row.getHeight() || this.vDomRowHeight; if (fillableSpace >= rowHeight) { this.vDomTop++; fillableSpace -= rowHeight; paddingAdjust += rowHeight; removableRows.push(row); i++; } else { working = false; } } else { working = false; } } for (let row of removableRows) { let rowEl = row.getElement(); if (rowEl.parentNode) { rowEl.parentNode.removeChild(rowEl); } } if (paddingAdjust) { this.vDomTopPad += paddingAdjust; this.tableElement.style.paddingTop = this.vDomTopPad + "px"; this.vDomScrollPosTop += this.vDomTop ? paddingAdjust : paddingAdjust + this.vDomWindowBuffer; } } _addBottomRow(rows2, fillableSpace) { var table = this.tableElement, addedRows = [], paddingAdjust = 0, index = this.vDomBottom + 1, i = 0, working = true; while (working) { let row = rows2[index], rowHeight, initialized; if (row && i < this.vDomMaxRenderChain) { rowHeight = row.getHeight() || this.vDomRowHeight; initialized = row.initialized; if (fillableSpace >= rowHeight) { this.styleRow(row, index); table.appendChild(row.getElement()); if (!row.initialized || !row.heightInitialized) { addedRows.push(row); } row.initialize(); if (!initialized) { rowHeight = row.getElement().offsetHeight; if (rowHeight > this.vDomWindowBuffer) { this.vDomWindowBuffer = rowHeight * 2; } } fillableSpace -= rowHeight; paddingAdjust += rowHeight; this.vDomBottom++; index++; i++; } else { working = false; } } else { working = false; } } for (let row of addedRows) { row.clearCellHeight(); } this._quickNormalizeRowHeight(addedRows); if (paddingAdjust) { this.vDomBottomPad -= paddingAdjust; if (this.vDomBottomPad < 0 || index == rows2.length - 1) { this.vDomBottomPad = 0; } table.style.paddingBottom = this.vDomBottomPad + "px"; this.vDomScrollPosBottom += paddingAdjust; } } _removeBottomRow(rows2, fillableSpace) { var removableRows = [], paddingAdjust = 0, i = 0, working = true; while (working) { let row = rows2[this.vDomBottom], rowHeight; if (row && i < this.vDomMaxRenderChain) { rowHeight = row.getHeight() || this.vDomRowHeight; if (fillableSpace >= rowHeight) { this.vDomBottom--; fillableSpace -= rowHeight; paddingAdjust += rowHeight; removableRows.push(row); i++; } else { working = false; } } else { working = false; } } for (let row of removableRows) { let rowEl = row.getElement(); if (rowEl.parentNode) { rowEl.parentNode.removeChild(rowEl); } } if (paddingAdjust) { this.vDomBottomPad += paddingAdjust; if (this.vDomBottomPad < 0) { this.vDomBottomPad = 0; } this.tableElement.style.paddingBottom = this.vDomBottomPad + "px"; this.vDomScrollPosBottom -= paddingAdjust; } } _quickNormalizeRowHeight(rows2) { for (let row of rows2) { row.calcHeight(); } for (let row of rows2) { row.setCellHeight(); } } } class RowManager extends CoreFeature { constructor(table) { super(table); this.element = this.createHolderElement(); this.tableElement = this.createTableElement(); this.heightFixer = this.createTableElement(); this.placeholder = null; this.placeholderContents = null; this.firstRender = false; this.renderMode = "virtual"; this.fixedHeight = false; this.rows = []; this.activeRowsPipeline = []; this.activeRows = []; this.activeRowsCount = 0; this.displayRows = []; this.displayRowsCount = 0; this.scrollTop = 0; this.scrollLeft = 0; this.redrawBlock = false; this.redrawBlockRestoreConfig = false; this.redrawBlockRenderInPosition = false; this.dataPipeline = []; this.displayPipeline = []; this.scrollbarWidth = 0; this.renderer = null; } //////////////// Setup Functions ///////////////// createHolderElement() { var el = document.createElement("div"); el.classList.add("tabulator-tableholder"); el.setAttribute("tabindex", 0); return el; } createTableElement() { var el = document.createElement("div"); el.classList.add("tabulator-table"); el.setAttribute("role", "rowgroup"); el.setAttribute("id", "tabulator-table-body"); return el; } initializePlaceholder() { var placeholder = this.table.options.placeholder; if (typeof placeholder === "function") { placeholder = placeholder.call(this.table); } placeholder = this.chain("placeholder", [placeholder], placeholder, placeholder) || placeholder; if (placeholder) { let el = document.createElement("div"); el.classList.add("tabulator-placeholder"); if (typeof placeholder == "string") { let contents = document.createElement("div"); contents.classList.add("tabulator-placeholder-contents"); contents.innerHTML = placeholder; el.appendChild(contents); this.placeholderContents = contents; } else if (typeof HTMLElement !== "undefined" && placeholder instanceof HTMLElement) { el.appendChild(placeholder); this.placeholderContents = placeholder; } else { console.warn("Invalid placeholder provided, must be string or HTML Element", placeholder); this.el = null; } this.placeholder = el; } } //return containing element getElement() { return this.element; } //return table element getTableElement() { return this.tableElement; } initialize() { this.initializePlaceholder(); this.initializeRenderer(); this.element.appendChild(this.tableElement); this.firstRender = true; this.element.addEventListener("scroll", () => { var left = this.element.scrollLeft, leftDir = this.scrollLeft > left, top = this.element.scrollTop, topDir = this.scrollTop > top; if (this.scrollLeft != left) { this.scrollLeft = left; this.dispatch("scroll-horizontal", left, leftDir); this.dispatchExternal("scrollHorizontal", left, leftDir); this._positionPlaceholder(); } if (this.scrollTop != top) { this.scrollTop = top; this.renderer.scrollRows(top, topDir); this.dispatch("scroll-vertical", top, topDir); this.dispatchExternal("scrollVertical", top, topDir); } }); } ////////////////// Row Manipulation ////////////////// findRow(subject) { if (typeof subject == "object") { if (subject instanceof Row) { return subject; } else if (subject instanceof RowComponent) { return subject._getSelf() || false; } else if (typeof HTMLElement !== "undefined" && subject instanceof HTMLElement) { let match = this.rows.find((row) => { return row.getElement() === subject; }); return match || false; } else if (subject === null) { return false; } } else if (typeof subject == "undefined") { return false; } else { let match = this.rows.find((row) => { return row.data[this.table.options.index] == subject; }); return match || false; } return false; } getRowFromDataObject(data) { var match = this.rows.find((row) => { return row.data === data; }); return match || false; } getRowFromPosition(position) { return this.getDisplayRows().find((row) => { return row.type === "row" && row.getPosition() === position && row.isDisplayed(); }); } scrollToRow(row, position, ifVisible) { return this.renderer.scrollToRowPosition(row, position, ifVisible); } ////////////////// Data Handling ////////////////// setData(data, renderInPosition, columnsChanged) { return new Promise((resolve, reject) => { if (renderInPosition && this.getDisplayRows().length) { if (this.table.options.pagination) { this._setDataActual(data, true); } else { this.reRenderInPosition(() => { this._setDataActual(data); }); } } else { if (this.table.options.autoColumns && columnsChanged && this.table.initialized) { this.table.columnManager.generateColumnsFromRowData(data); } this.resetScroll(); this._setDataActual(data); } resolve(); }); } _setDataActual(data, renderInPosition) { this.dispatchExternal("dataProcessing", data); this._wipeElements(); if (Array.isArray(data)) { this.dispatch("data-processing", data); data.forEach((def, i) => { if (def && typeof def === "object") { var row = new Row(def, this); this.rows.push(row); } else { console.warn("Data Loading Warning - Invalid row data detected and ignored, expecting object but received:", def); } }); this.refreshActiveData(false, false, renderInPosition); this.dispatch("data-processed", data); this.dispatchExternal("dataProcessed", data); } else { console.error("Data Loading Error - Unable to process data due to invalid data type \nExpecting: array \nReceived: ", typeof data, "\nData: ", data); } } _wipeElements() { this.dispatch("rows-wipe"); this.destroy(); this.adjustTableSize(); this.dispatch("rows-wiped"); } destroy() { this.rows.forEach((row) => { row.wipe(); }); this.rows = []; this.activeRows = []; this.activeRowsPipeline = []; this.activeRowsCount = 0; this.displayRows = []; this.displayRowsCount = 0; } deleteRow(row, blockRedraw) { var allIndex = this.rows.indexOf(row), activeIndex = this.activeRows.indexOf(row); if (activeIndex > -1) { this.activeRows.splice(activeIndex, 1); } if (allIndex > -1) { this.rows.splice(allIndex, 1); } this.setActiveRows(this.activeRows); this.displayRowIterator((rows2) => { var displayIndex = rows2.indexOf(row); if (displayIndex > -1) { rows2.splice(displayIndex, 1); } }); if (!blockRedraw) { this.reRenderInPosition(); } this.regenerateRowPositions(); this.dispatchExternal("rowDeleted", row.getComponent()); if (!this.displayRowsCount) { this.tableEmpty(); } if (this.subscribedExternal("dataChanged")) { this.dispatchExternal("dataChanged", this.getData()); } } addRow(data, pos, index, blockRedraw) { var row = this.addRowActual(data, pos, index, blockRedraw); return row; } //add multiple rows addRows(data, pos, index, refreshDisplayOnly) { var rows2 = []; return new Promise((resolve, reject) => { pos = this.findAddRowPos(pos); if (!Array.isArray(data)) { data = [data]; } if (typeof index == "undefined" && pos || typeof index !== "undefined" && !pos) { data.reverse(); } data.forEach((item, i) => { var row = this.addRow(item, pos, index, true); rows2.push(row); this.dispatch("row-added", row, item, pos, index); }); this.refreshActiveData(refreshDisplayOnly ? "displayPipeline" : false, false, true); this.regenerateRowPositions(); if (this.displayRowsCount) { this._clearPlaceholder(); } resolve(rows2); }); } findAddRowPos(pos) { if (typeof pos === "undefined") { pos = this.table.options.addRowPos; } if (pos === "pos") { pos = true; } if (pos === "bottom") { pos = false; } return pos; } addRowActual(data, pos, index, blockRedraw) { var row = data instanceof Row ? data : new Row(data || {}, this), top = this.findAddRowPos(pos), allIndex = -1, activeIndex, chainResult; if (!index) { chainResult = this.chain("row-adding-position", [row, top], null, {index, top}); index = chainResult.index; top = chainResult.top; } if (typeof index !== "undefined") { index = this.findRow(index); } index = this.chain("row-adding-index", [row, index, top], null, index); if (index) { allIndex = this.rows.indexOf(index); } if (index && allIndex > -1) { activeIndex = this.activeRows.indexOf(index); this.displayRowIterator(function (rows2) { var displayIndex = rows2.indexOf(index); if (displayIndex > -1) { rows2.splice(top ? displayIndex : displayIndex + 1, 0, row); } }); if (activeIndex > -1) { this.activeRows.splice(top ? activeIndex : activeIndex + 1, 0, row); } this.rows.splice(top ? allIndex : allIndex + 1, 0, row); } else { if (top) { this.displayRowIterator(function (rows2) { rows2.unshift(row); }); this.activeRows.unshift(row); this.rows.unshift(row); } else { this.displayRowIterator(function (rows2) { rows2.push(row); }); this.activeRows.push(row); this.rows.push(row); } } this.setActiveRows(this.activeRows); this.dispatchExternal("rowAdded", row.getComponent()); if (this.subscribedExternal("dataChanged")) { this.dispatchExternal("dataChanged", this.table.rowManager.getData()); } if (!blockRedraw) { this.reRenderInPosition(); } return row; } moveRow(from, to, after) { this.dispatch("row-move", from, to, after); this.moveRowActual(from, to, after); this.regenerateRowPositions(); this.dispatch("row-moved", from, to, after); this.dispatchExternal("rowMoved", from.getComponent()); } moveRowActual(from, to, after) { this.moveRowInArray(this.rows, from, to, after); this.moveRowInArray(this.activeRows, from, to, after); this.displayRowIterator((rows2) => { this.moveRowInArray(rows2, from, to, after); }); this.dispatch("row-moving", from, to, after); } moveRowInArray(rows2, from, to, after) { var fromIndex, toIndex, start, end; if (from !== to) { fromIndex = rows2.indexOf(from); if (fromIndex > -1) { rows2.splice(fromIndex, 1); toIndex = rows2.indexOf(to); if (toIndex > -1) { if (after) { rows2.splice(toIndex + 1, 0, from); } else { rows2.splice(toIndex, 0, from); } } else { rows2.splice(fromIndex, 0, from); } } if (rows2 === this.getDisplayRows()) { start = fromIndex < toIndex ? fromIndex : toIndex; end = toIndex > fromIndex ? toIndex : fromIndex + 1; for (let i = start; i <= end; i++) { if (rows2[i]) { this.styleRow(rows2[i], i); } } } } } clearData() { this.setData([]); } getRowIndex(row) { return this.findRowIndex(row, this.rows); } getDisplayRowIndex(row) { var index = this.getDisplayRows().indexOf(row); return index > -1 ? index : false; } nextDisplayRow(row, rowOnly) { var index = this.getDisplayRowIndex(row), nextRow = false; if (index !== false && index < this.displayRowsCount - 1) { nextRow = this.getDisplayRows()[index + 1]; } if (nextRow && (!(nextRow instanceof Row) || nextRow.type != "row")) { return this.nextDisplayRow(nextRow, rowOnly); } return nextRow; } prevDisplayRow(row, rowOnly) { var index = this.getDisplayRowIndex(row), prevRow = false; if (index) { prevRow = this.getDisplayRows()[index - 1]; } if (rowOnly && prevRow && (!(prevRow instanceof Row) || prevRow.type != "row")) { return this.prevDisplayRow(prevRow, rowOnly); } return prevRow; } findRowIndex(row, list2) { var rowIndex; row = this.findRow(row); if (row) { rowIndex = list2.indexOf(row); if (rowIndex > -1) { return rowIndex; } } return false; } getData(active, transform) { var output = [], rows2 = this.getRows(active); rows2.forEach(function (row) { if (row.type == "row") { output.push(row.getData(transform || "data")); } }); return output; } getComponents(active) { var output = [], rows2 = this.getRows(active); rows2.forEach(function (row) { output.push(row.getComponent()); }); return output; } getDataCount(active) { var rows2 = this.getRows(active); return rows2.length; } scrollHorizontal(left) { this.scrollLeft = left; this.element.scrollLeft = left; this.dispatch("scroll-horizontal", left); } registerDataPipelineHandler(handler, priority) { if (typeof priority !== "undefined") { this.dataPipeline.push({handler, priority}); this.dataPipeline.sort((a, b) => { return a.priority - b.priority; }); } else { console.error("Data pipeline handlers must have a priority in order to be registered"); } } registerDisplayPipelineHandler(handler, priority) { if (typeof priority !== "undefined") { this.displayPipeline.push({handler, priority}); this.displayPipeline.sort((a, b) => { return a.priority - b.priority; }); } else { console.error("Display pipeline handlers must have a priority in order to be registered"); } } //set active data set refreshActiveData(handler, skipStage, renderInPosition) { var table = this.table, stage = "", index = 0, cascadeOrder = ["all", "dataPipeline", "display", "displayPipeline", "end"]; if (!this.table.destroyed) { if (typeof handler === "function") { index = this.dataPipeline.findIndex((item) => { return item.handler === handler; }); if (index > -1) { stage = "dataPipeline"; if (skipStage) { if (index == this.dataPipeline.length - 1) { stage = "display"; } else { index++; } } } else { index = this.displayPipeline.findIndex((item) => { return item.handler === handler; }); if (index > -1) { stage = "displayPipeline"; if (skipStage) { if (index == this.displayPipeline.length - 1) { stage = "end"; } else { index++; } } } else { console.error("Unable to refresh data, invalid handler provided", handler); return; } } } else { stage = handler || "all"; index = 0; } if (this.redrawBlock) { if (!this.redrawBlockRestoreConfig || this.redrawBlockRestoreConfig && (this.redrawBlockRestoreConfig.stage === stage && index < this.redrawBlockRestoreConfig.index || cascadeOrder.indexOf(stage) < cascadeOrder.indexOf(this.redrawBlockRestoreConfig.stage))) { this.redrawBlockRestoreConfig = { handler, skipStage, renderInPosition, stage, index }; } return; } else { if (Helpers.elVisible(this.element)) { if (renderInPosition) { this.reRenderInPosition(this.refreshPipelines.bind(this, handler, stage, index, renderInPosition)); } else { this.refreshPipelines(handler, stage, index, renderInPosition); if (!handler) { this.table.columnManager.renderer.renderColumns(); } this.renderTable(); if (table.options.layoutColumnsOnNewData) { this.table.columnManager.redraw(true); } } } else { this.refreshPipelines(handler, stage, index, renderInPosition); } this.dispatch("data-refreshed"); } } } refreshPipelines(handler, stage, index, renderInPosition) { this.dispatch("data-refreshing"); if (!handler || !this.activeRowsPipeline[0]) { this.activeRowsPipeline[0] = this.rows.slice(0); } switch (stage) { case "all": //handle case where all data needs refreshing case "dataPipeline": for (let i = index; i < this.dataPipeline.length; i++) { let result = this.dataPipeline[i].handler(this.activeRowsPipeline[i].slice(0)); this.activeRowsPipeline[i + 1] = result || this.activeRowsPipeline[i].slice(0); } this.setActiveRows(this.activeRowsPipeline[this.dataPipeline.length]); case "display": index = 0; this.resetDisplayRows(); case "displayPipeline": for (let i = index; i < this.displayPipeline.length; i++) { let result = this.displayPipeline[i].handler((i ? this.getDisplayRows(i - 1) : this.activeRows).slice(0), renderInPosition); this.setDisplayRows(result || this.getDisplayRows(i - 1).slice(0), i); } case "end": this.regenerateRowPositions(); } if (this.getDisplayRows().length) { this._clearPlaceholder(); } } //regenerate row positions regenerateRowPositions() { var rows2 = this.getDisplayRows(); var index = 1; rows2.forEach((row) => { if (row.type === "row") { row.setPosition(index); index++; } }); } setActiveRows(activeRows) { this.activeRows = this.activeRows = Object.assign([], activeRows); this.activeRowsCount = this.activeRows.length; } //reset display rows array resetDisplayRows() { this.displayRows = []; this.displayRows.push(this.activeRows.slice(0)); this.displayRowsCount = this.displayRows[0].length; } //set display row pipeline data setDisplayRows(displayRows, index) { this.displayRows[index] = displayRows; if (index == this.displayRows.length - 1) { this.displayRowsCount = this.displayRows[this.displayRows.length - 1].length; } } getDisplayRows(index) { if (typeof index == "undefined") { return this.displayRows.length ? this.displayRows[this.displayRows.length - 1] : []; } else { return this.displayRows[index] || []; } } getVisibleRows(chain, viewable) { var rows2 = Object.assign([], this.renderer.visibleRows(!viewable)); if (chain) { rows2 = this.chain("rows-visible", [viewable], rows2, rows2); } return rows2; } //repeat action across display rows displayRowIterator(callback) { this.activeRowsPipeline.forEach(callback); this.displayRows.forEach(callback); this.displayRowsCount = this.displayRows[this.displayRows.length - 1].length; } //return only actual rows (not group headers etc) getRows(type) { var rows2 = []; switch (type) { case "active": rows2 = this.activeRows; break; case "display": rows2 = this.table.rowManager.getDisplayRows(); break; case "visible": rows2 = this.getVisibleRows(false, true); break; default: rows2 = this.chain("rows-retrieve", type, null, this.rows) || this.rows; } return rows2; } ///////////////// Table Rendering ///////////////// //trigger rerender of table in current position reRenderInPosition(callback) { if (this.redrawBlock) { if (callback) { callback(); } else { this.redrawBlockRenderInPosition = true; } } else { this.dispatchExternal("renderStarted"); this.renderer.rerenderRows(callback); if (!this.fixedHeight) { this.adjustTableSize(); } this.scrollBarCheck(); this.dispatchExternal("renderComplete"); } } scrollBarCheck() { var scrollbarWidth = 0; if (this.element.scrollHeight > this.element.clientHeight) { scrollbarWidth = this.element.offsetWidth - this.element.clientWidth; } if (scrollbarWidth !== this.scrollbarWidth) { this.scrollbarWidth = scrollbarWidth; this.dispatch("scrollbar-vertical", scrollbarWidth); } } initializeRenderer() { var renderClass; var renderers = { "virtual": VirtualDomVertical, "basic": BasicVertical }; if (typeof this.table.options.renderVertical === "string") { renderClass = renderers[this.table.options.renderVertical]; } else { renderClass = this.table.options.renderVertical; } if (renderClass) { this.renderMode = this.table.options.renderVertical; this.renderer = new renderClass(this.table, this.element, this.tableElement); this.renderer.initialize(); if ((this.table.element.clientHeight || this.table.options.height) && !(this.table.options.minHeight && this.table.options.maxHeight)) { this.fixedHeight = true; } else { this.fixedHeight = false; } } else { console.error("Unable to find matching renderer:", this.table.options.renderVertical); } } getRenderMode() { return this.renderMode; } renderTable() { this.dispatchExternal("renderStarted"); this.element.scrollTop = 0; this._clearTable(); if (this.displayRowsCount) { this.renderer.renderRows(); if (this.firstRender) { this.firstRender = false; if (!this.fixedHeight) { this.adjustTableSize(); } this.layoutRefresh(true); } } else { this.renderEmptyScroll(); } if (!this.fixedHeight) { this.adjustTableSize(); } this.dispatch("table-layout"); if (!this.displayRowsCount) { this._showPlaceholder(); } this.scrollBarCheck(); this.dispatchExternal("renderComplete"); } //show scrollbars on empty table div renderEmptyScroll() { if (this.placeholder) { this.tableElement.style.display = "none"; } else { this.tableElement.style.minWidth = this.table.columnManager.getWidth() + "px"; } } _clearTable() { this._clearPlaceholder(); this.scrollTop = 0; this.scrollLeft = 0; this.renderer.clearRows(); } tableEmpty() { this.renderEmptyScroll(); this._showPlaceholder(); } checkPlaceholder() { if (this.displayRowsCount) { this._clearPlaceholder(); } else { this.tableEmpty(); } } _showPlaceholder() { if (this.placeholder) { if (this.placeholder && this.placeholder.parentNode) { this.placeholder.parentNode.removeChild(this.placeholder); } this.initializePlaceholder(); this.placeholder.setAttribute("tabulator-render-mode", this.renderMode); this.getElement().appendChild(this.placeholder); this._positionPlaceholder(); this.adjustTableSize(); } } _clearPlaceholder() { if (this.placeholder && this.placeholder.parentNode) { this.placeholder.parentNode.removeChild(this.placeholder); } this.tableElement.style.minWidth = ""; this.tableElement.style.display = ""; } _positionPlaceholder() { if (this.placeholder && this.placeholder.parentNode) { this.placeholder.style.width = this.table.columnManager.getWidth() + "px"; this.placeholderContents.style.width = this.table.rowManager.element.clientWidth + "px"; this.placeholderContents.style.marginLeft = this.scrollLeft + "px"; } } styleRow(row, index) { var rowEl = row.getElement(); if (index % 2) { rowEl.classList.add("tabulator-row-even"); rowEl.classList.remove("tabulator-row-odd"); } else { rowEl.classList.add("tabulator-row-odd"); rowEl.classList.remove("tabulator-row-even"); } } //normalize height of active rows normalizeHeight(force) { this.activeRows.forEach(function (row) { row.normalizeHeight(force); }); } //adjust the height of the table holder to fit in the Tabulator element adjustTableSize() { let initialHeight = this.element.clientHeight, minHeight; let resized = false; if (this.renderer.verticalFillMode === "fill") { let otherHeight = Math.floor(this.table.columnManager.getElement().getBoundingClientRect().height + (this.table.footerManager && this.table.footerManager.active && !this.table.footerManager.external ? this.table.footerManager.getElement().getBoundingClientRect().height : 0)); if (this.fixedHeight) { minHeight = isNaN(this.table.options.minHeight) ? this.table.options.minHeight : this.table.options.minHeight + "px"; const height = "calc(100% - " + otherHeight + "px)"; this.element.style.minHeight = minHeight || "calc(100% - " + otherHeight + "px)"; this.element.style.height = height; this.element.style.maxHeight = height; } else { this.element.style.height = ""; this.element.style.height = this.table.element.clientHeight - otherHeight + "px"; this.element.scrollTop = this.scrollTop; } this.renderer.resize(); if (!this.fixedHeight && initialHeight != this.element.clientHeight) { resized = true; if (!this.redrawing) { this.redrawing = true; if (this.subscribed("table-resize")) { this.dispatch("table-resize"); } else { this.redraw(); } this.redrawing = false; } } this.scrollBarCheck(); } this._positionPlaceholder(); return resized; } //reinitialize all rows reinitialize() { this.rows.forEach(function (row) { row.reinitialize(true); }); } //prevent table from being redrawn blockRedraw() { this.redrawBlock = true; this.redrawBlockRestoreConfig = false; } //restore table redrawing restoreRedraw() { this.redrawBlock = false; if (this.redrawBlockRestoreConfig) { this.refreshActiveData(this.redrawBlockRestoreConfig.handler, this.redrawBlockRestoreConfig.skipStage, this.redrawBlockRestoreConfig.renderInPosition); this.redrawBlockRestoreConfig = false; } else { if (this.redrawBlockRenderInPosition) { this.reRenderInPosition(); } } this.redrawBlockRenderInPosition = false; } //redraw table redraw(force) { this.adjustTableSize(); this.table.tableWidth = this.table.element.clientWidth; if (!force) { this.reRenderInPosition(); this.scrollHorizontal(this.scrollLeft); } else { this.renderTable(); } } resetScroll() { this.element.scrollLeft = 0; this.element.scrollTop = 0; if (this.table.browser === "ie") { var event = document.createEvent("Event"); event.initEvent("scroll", false, true); this.element.dispatchEvent(event); } else { this.element.dispatchEvent(new Event("scroll")); } } } class FooterManager extends CoreFeature { constructor(table) { super(table); this.active = false; this.element = this.createElement(); this.containerElement = this.createContainerElement(); this.external = false; } initialize() { this.initializeElement(); } createElement() { var el = document.createElement("div"); el.classList.add("tabulator-footer"); return el; } createContainerElement() { var el = document.createElement("div"); el.classList.add("tabulator-footer-contents"); this.element.appendChild(el); return el; } initializeElement() { if (this.table.options.footerElement) { switch (typeof this.table.options.footerElement) { case "string": if (this.table.options.footerElement[0] === "<") { this.containerElement.innerHTML = this.table.options.footerElement; } else { this.external = true; this.containerElement = document.querySelector(this.table.options.footerElement); } break; default: this.element = this.table.options.footerElement; break; } } } getElement() { return this.element; } append(element) { this.activate(); this.containerElement.appendChild(element); this.table.rowManager.adjustTableSize(); } prepend(element) { this.activate(); this.element.insertBefore(element, this.element.firstChild); this.table.rowManager.adjustTableSize(); } remove(element) { element.parentNode.removeChild(element); this.deactivate(); } deactivate(force) { if (!this.element.firstChild || force) { if (!this.external) { this.element.parentNode.removeChild(this.element); } this.active = false; } } activate() { if (!this.active) { this.active = true; if (!this.external) { this.table.element.appendChild(this.getElement()); this.table.element.style.display = ""; } } } redraw() { this.dispatch("footer-redraw"); } } class InteractionManager extends CoreFeature { constructor(table) { super(table); this.el = null; this.abortClasses = ["tabulator-headers", "tabulator-table"]; this.previousTargets = {}; this.listeners = [ "click", "dblclick", "contextmenu", "mouseenter", "mouseleave", "mouseover", "mouseout", "mousemove", "mouseup", "mousedown", "touchstart", "touchend" ]; this.componentMap = { "tabulator-cell": "cell", "tabulator-row": "row", "tabulator-group": "group", "tabulator-col": "column" }; this.pseudoTrackers = { "row": { subscriber: null, target: null }, "cell": { subscriber: null, target: null }, "group": { subscriber: null, target: null }, "column": { subscriber: null, target: null } }; this.pseudoTracking = false; } initialize() { this.el = this.table.element; this.buildListenerMap(); this.bindSubscriptionWatchers(); } buildListenerMap() { var listenerMap = {}; this.listeners.forEach((listener) => { listenerMap[listener] = { handler: null, components: [] }; }); this.listeners = listenerMap; } bindPseudoEvents() { Object.keys(this.pseudoTrackers).forEach((key) => { this.pseudoTrackers[key].subscriber = this.pseudoMouseEnter.bind(this, key); this.subscribe(key + "-mouseover", this.pseudoTrackers[key].subscriber); }); this.pseudoTracking = true; } pseudoMouseEnter(key, e, target) { if (this.pseudoTrackers[key].target !== target) { if (this.pseudoTrackers[key].target) { this.dispatch(key + "-mouseleave", e, this.pseudoTrackers[key].target); } this.pseudoMouseLeave(key, e); this.pseudoTrackers[key].target = target; this.dispatch(key + "-mouseenter", e, target); } } pseudoMouseLeave(key, e) { var leaveList = Object.keys(this.pseudoTrackers), linkedKeys = { "row": ["cell"], "cell": ["row"] }; leaveList = leaveList.filter((item) => { var links = linkedKeys[key]; return item !== key && (!links || links && !links.includes(item)); }); leaveList.forEach((key2) => { var target = this.pseudoTrackers[key2].target; if (this.pseudoTrackers[key2].target) { this.dispatch(key2 + "-mouseleave", e, target); this.pseudoTrackers[key2].target = null; } }); } bindSubscriptionWatchers() { var listeners = Object.keys(this.listeners), components = Object.values(this.componentMap); for (let comp of components) { for (let listener of listeners) { let key = comp + "-" + listener; this.subscriptionChange(key, this.subscriptionChanged.bind(this, comp, listener)); } } this.subscribe("table-destroy", this.clearWatchers.bind(this)); } subscriptionChanged(component, key, added) { var listener = this.listeners[key].components, index = listener.indexOf(component), changed = false; if (added) { if (index === -1) { listener.push(component); changed = true; } } else { if (!this.subscribed(component + "-" + key)) { if (index > -1) { listener.splice(index, 1); changed = true; } } } if ((key === "mouseenter" || key === "mouseleave") && !this.pseudoTracking) { this.bindPseudoEvents(); } if (changed) { this.updateEventListeners(); } } updateEventListeners() { for (let key in this.listeners) { let listener = this.listeners[key]; if (listener.components.length) { if (!listener.handler) { listener.handler = this.track.bind(this, key); this.el.addEventListener(key, listener.handler); } } else { if (listener.handler) { this.el.removeEventListener(key, listener.handler); listener.handler = null; } } } } track(type, e) { var path = e.composedPath && e.composedPath() || e.path; var targets = this.findTargets(path); targets = this.bindComponents(type, targets); this.triggerEvents(type, e, targets); if (this.pseudoTracking && (type == "mouseover" || type == "mouseleave") && !Object.keys(targets).length) { this.pseudoMouseLeave("none", e); } } findTargets(path) { var targets = {}; let componentMap = Object.keys(this.componentMap); for (let el of path) { let classList = el.classList ? [...el.classList] : []; let abort = classList.filter((item) => { return this.abortClasses.includes(item); }); if (abort.length) { break; } let elTargets = classList.filter((item) => { return componentMap.includes(item); }); for (let target of elTargets) { if (!targets[this.componentMap[target]]) { targets[this.componentMap[target]] = el; } } } if (targets.group && targets.group === targets.row) { delete targets.row; } return targets; } bindComponents(type, targets) { var keys = Object.keys(targets).reverse(), listener = this.listeners[type], matches = {}, output = {}, targetMatches = {}; for (let key of keys) { let component, target = targets[key], previousTarget = this.previousTargets[key]; if (previousTarget && previousTarget.target === target) { component = previousTarget.component; } else { switch (key) { case "row": case "group": if (listener.components.includes("row") || listener.components.includes("cell") || listener.components.includes("group")) { let rows2 = this.table.rowManager.getVisibleRows(true); component = rows2.find((row) => { return row.getElement() === target; }); if (targets["row"] && targets["row"].parentNode && targets["row"].parentNode.closest(".tabulator-row")) { targets[key] = false; } } break; case "column": if (listener.components.includes("column")) { component = this.table.columnManager.findColumn(target); } break; case "cell": if (listener.components.includes("cell")) { if (matches["row"] instanceof Row) { component = matches["row"].findCell(target); } else { if (targets["row"]) { console.warn("Event Target Lookup Error - The row this cell is attached to cannot be found, has the table been reinitialized without being destroyed first?"); } } } break; } } if (component) { matches[key] = component; targetMatches[key] = { target, component }; } } this.previousTargets = targetMatches; Object.keys(targets).forEach((key) => { let value = matches[key]; output[key] = value; }); return output; } triggerEvents(type, e, targets) { var listener = this.listeners[type]; for (let key in targets) { if (targets[key] && listener.components.includes(key)) { this.dispatch(key + "-" + type, e, targets[key]); } } } clearWatchers() { for (let key in this.listeners) { let listener = this.listeners[key]; if (listener.handler) { this.el.removeEventListener(key, listener.handler); listener.handler = null; } } } } class ComponentFunctionBinder { constructor(table) { this.table = table; this.bindings = {}; } bind(type, funcName, handler) { if (!this.bindings[type]) { this.bindings[type] = {}; } if (this.bindings[type][funcName]) { console.warn("Unable to bind component handler, a matching function name is already bound", type, funcName, handler); } else { this.bindings[type][funcName] = handler; } } handle(type, component, name2) { if (this.bindings[type] && this.bindings[type][name2] && typeof this.bindings[type][name2].bind === "function") { return this.bindings[type][name2].bind(null, component); } else { if (name2 !== "then" && typeof name2 === "string" && !name2.startsWith("_")) { if (this.table.options.debugInvalidComponentFuncs) { console.error("The " + type + " component does not have a " + name2 + " function, have you checked that you have the correct Tabulator module installed?"); } } } } } class DataLoader extends CoreFeature { constructor(table) { super(table); this.requestOrder = 0; this.loading = false; } initialize() { } load(data, params, config, replace, silent, columnsChanged) { var requestNo = ++this.requestOrder; if (this.table.destroyed) { return Promise.resolve(); } this.dispatchExternal("dataLoading", data); if (data && (data.indexOf("{") == 0 || data.indexOf("[") == 0)) { data = JSON.parse(data); } if (this.confirm("data-loading", [data, params, config, silent])) { this.loading = true; if (!silent) { this.alertLoader(); } params = this.chain("data-params", [data, config, silent], params || {}, params || {}); params = this.mapParams(params, this.table.options.dataSendParams); var result = this.chain("data-load", [data, params, config, silent], false, Promise.resolve([])); return result.then((response) => { if (!this.table.destroyed) { if (!Array.isArray(response) && typeof response == "object") { response = this.mapParams(response, this.objectInvert(this.table.options.dataReceiveParams)); } var rowData = this.chain("data-loaded", [response], null, response); if (requestNo == this.requestOrder) { this.clearAlert(); if (rowData !== false) { this.dispatchExternal("dataLoaded", rowData); this.table.rowManager.setData(rowData, replace, typeof columnsChanged === "undefined" ? !replace : columnsChanged); } } else { console.warn("Data Load Response Blocked - An active data load request was blocked by an attempt to change table data while the request was being made"); } } else { console.warn("Data Load Response Blocked - Table has been destroyed"); } }).catch((error) => { console.error("Data Load Error: ", error); this.dispatchExternal("dataLoadError", error); if (!silent) { this.alertError(); } setTimeout(() => { this.clearAlert(); }, this.table.options.dataLoaderErrorTimeout); }).finally(() => { this.loading = false; }); } else { this.dispatchExternal("dataLoaded", data); if (!data) { data = []; } this.table.rowManager.setData(data, replace, typeof columnsChanged === "undefined" ? !replace : columnsChanged); return Promise.resolve(); } } mapParams(params, map) { var output = {}; for (let key in params) { output[map.hasOwnProperty(key) ? map[key] : key] = params[key]; } return output; } objectInvert(obj) { var output = {}; for (let key in obj) { output[obj[key]] = key; } return output; } blockActiveLoad() { this.requestOrder++; } alertLoader() { var shouldLoad = typeof this.table.options.dataLoader === "function" ? this.table.options.dataLoader() : this.table.options.dataLoader; if (shouldLoad) { this.table.alertManager.alert(this.table.options.dataLoaderLoading || this.langText("data|loading")); } } alertError() { this.table.alertManager.alert(this.table.options.dataLoaderError || this.langText("data|error"), "error"); } clearAlert() { this.table.alertManager.clear(); } } class ExternalEventBus { constructor(table, optionsList, debug) { this.table = table; this.events = {}; this.optionsList = optionsList || {}; this.subscriptionNotifiers = {}; this.dispatch = debug ? this._debugDispatch.bind(this) : this._dispatch.bind(this); this.debug = debug; } subscriptionChange(key, callback) { if (!this.subscriptionNotifiers[key]) { this.subscriptionNotifiers[key] = []; } this.subscriptionNotifiers[key].push(callback); if (this.subscribed(key)) { this._notifySubscriptionChange(key, true); } } subscribe(key, callback) { if (!this.events[key]) { this.events[key] = []; } this.events[key].push(callback); this._notifySubscriptionChange(key, true); } unsubscribe(key, callback) { var index; if (this.events[key]) { if (callback) { index = this.events[key].findIndex((item) => { return item === callback; }); if (index > -1) { this.events[key].splice(index, 1); } else { console.warn("Cannot remove event, no matching event found:", key, callback); return; } } else { delete this.events[key]; } } else { console.warn("Cannot remove event, no events set on:", key); return; } this._notifySubscriptionChange(key, false); } subscribed(key) { return this.events[key] && this.events[key].length; } _notifySubscriptionChange(key, subscribed) { var notifiers = this.subscriptionNotifiers[key]; if (notifiers) { notifiers.forEach((callback) => { callback(subscribed); }); } } _dispatch() { var args = Array.from(arguments), key = args.shift(), result; if (this.events[key]) { this.events[key].forEach((callback, i) => { let callResult = callback.apply(this.table, args); if (!i) { result = callResult; } }); } return result; } _debugDispatch() { var args = Array.from(arguments), key = args[0]; args[0] = "ExternalEvent:" + args[0]; if (this.debug === true || this.debug.includes(key)) { console.log(...args); } return this._dispatch(...arguments); } } class InternalEventBus { constructor(debug) { this.events = {}; this.subscriptionNotifiers = {}; this.dispatch = debug ? this._debugDispatch.bind(this) : this._dispatch.bind(this); this.chain = debug ? this._debugChain.bind(this) : this._chain.bind(this); this.confirm = debug ? this._debugConfirm.bind(this) : this._confirm.bind(this); this.debug = debug; } subscriptionChange(key, callback) { if (!this.subscriptionNotifiers[key]) { this.subscriptionNotifiers[key] = []; } this.subscriptionNotifiers[key].push(callback); if (this.subscribed(key)) { this._notifySubscriptionChange(key, true); } } subscribe(key, callback, priority = 1e4) { if (!this.events[key]) { this.events[key] = []; } this.events[key].push({callback, priority}); this.events[key].sort((a, b) => { return a.priority - b.priority; }); this._notifySubscriptionChange(key, true); } unsubscribe(key, callback) { var index; if (this.events[key]) { if (callback) { index = this.events[key].findIndex((item) => { return item.callback === callback; }); if (index > -1) { this.events[key].splice(index, 1); } else { console.warn("Cannot remove event, no matching event found:", key, callback); return; } } } else { console.warn("Cannot remove event, no events set on:", key); return; } this._notifySubscriptionChange(key, false); } subscribed(key) { return this.events[key] && this.events[key].length; } _chain(key, args, initialValue, fallback) { var value = initialValue; if (!Array.isArray(args)) { args = [args]; } if (this.subscribed(key)) { this.events[key].forEach((subscriber, i) => { value = subscriber.callback.apply(this, args.concat([value])); }); return value; } else { return typeof fallback === "function" ? fallback() : fallback; } } _confirm(key, args) { var confirmed = false; if (!Array.isArray(args)) { args = [args]; } if (this.subscribed(key)) { this.events[key].forEach((subscriber, i) => { if (subscriber.callback.apply(this, args)) { confirmed = true; } }); } return confirmed; } _notifySubscriptionChange(key, subscribed) { var notifiers = this.subscriptionNotifiers[key]; if (notifiers) { notifiers.forEach((callback) => { callback(subscribed); }); } } _dispatch() { var args = Array.from(arguments), key = args.shift(); if (this.events[key]) { this.events[key].forEach((subscriber) => { subscriber.callback.apply(this, args); }); } } _debugDispatch() { var args = Array.from(arguments), key = args[0]; args[0] = "InternalEvent:" + key; if (this.debug === true || this.debug.includes(key)) { console.log(...args); } return this._dispatch(...arguments); } _debugChain() { var args = Array.from(arguments), key = args[0]; args[0] = "InternalEvent:" + key; if (this.debug === true || this.debug.includes(key)) { console.log(...args); } return this._chain(...arguments); } _debugConfirm() { var args = Array.from(arguments), key = args[0]; args[0] = "InternalEvent:" + key; if (this.debug === true || this.debug.includes(key)) { console.log(...args); } return this._confirm(...arguments); } } class DeprecationAdvisor extends CoreFeature { constructor(table) { super(table); } _warnUser() { if (this.options("debugDeprecation")) { console.warn(...arguments); } } check(oldOption, newOption, convert) { var msg = ""; if (typeof this.options(oldOption) !== "undefined") { msg = "Deprecated Setup Option - Use of the %c" + oldOption + "%c option is now deprecated"; if (newOption) { msg = msg + ", Please use the %c" + newOption + "%c option instead"; this._warnUser(msg, "font-weight: bold;", "font-weight: normal;", "font-weight: bold;", "font-weight: normal;"); if (convert) { this.table.options[newOption] = this.table.options[oldOption]; } } else { this._warnUser(msg, "font-weight: bold;", "font-weight: normal;"); } return false; } else { return true; } } checkMsg(oldOption, msg) { if (typeof this.options(oldOption) !== "undefined") { this._warnUser("%cDeprecated Setup Option - Use of the %c" + oldOption + " %c option is now deprecated, " + msg, "font-weight: normal;", "font-weight: bold;", "font-weight: normal;"); return false; } else { return true; } } msg(msg) { this._warnUser(msg); } } class DependencyRegistry extends CoreFeature { constructor(table) { super(table); this.deps = {}; this.props = {}; } initialize() { this.deps = Object.assign({}, this.options("dependencies")); } lookup(key, prop, silent) { if (Array.isArray(key)) { for (const item of key) { var match = this.lookup(item, prop, true); if (match) { break; } } if (match) { return match; } else { this.error(key); } } else { if (prop) { return this.lookupProp(key, prop, silent); } else { return this.lookupKey(key, silent); } } } lookupProp(key, prop, silent) { var dependency; if (this.props[key] && this.props[key][prop]) { return this.props[key][prop]; } else { dependency = this.lookupKey(key, silent); if (dependency) { if (!this.props[key]) { this.props[key] = {}; } this.props[key][prop] = dependency[prop] || dependency; return this.props[key][prop]; } } } lookupKey(key, silent) { var dependency; if (this.deps[key]) { dependency = this.deps[key]; } else if (window[key]) { this.deps[key] = window[key]; dependency = this.deps[key]; } else { if (!silent) { this.error(key); } } return dependency; } error(key) { console.error("Unable to find dependency", key, "Please check documentation and ensure you have imported the required library into your project"); } } function fitData(columns, forced) { if (forced) { this.table.columnManager.renderer.reinitializeColumnWidths(columns); } if (this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)) { this.table.modules.responsiveLayout.update(); } } function fitDataGeneral(columns, forced) { columns.forEach(function (column) { column.reinitializeWidth(); }); if (this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)) { this.table.modules.responsiveLayout.update(); } } function fitDataStretch(columns, forced) { var colsWidth = 0, tableWidth = this.table.rowManager.element.clientWidth, gap = 0, lastCol = false; columns.forEach((column, i) => { if (!column.widthFixed) { column.reinitializeWidth(); } if (this.table.options.responsiveLayout ? column.modules.responsive.visible : column.visible) { lastCol = column; } if (column.visible) { colsWidth += column.getWidth(); } }); if (lastCol) { gap = tableWidth - colsWidth + lastCol.getWidth(); if (this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)) { lastCol.setWidth(0); this.table.modules.responsiveLayout.update(); } if (gap > 0) { lastCol.setWidth(gap); } else { lastCol.reinitializeWidth(); } } else { if (this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)) { this.table.modules.responsiveLayout.update(); } } } function fitColumns(columns, forced) { var totalWidth = this.table.rowManager.element.getBoundingClientRect().width; var fixedWidth = 0; var flexWidth = 0; var flexGrowUnits = 0; var flexColWidth = 0; var flexColumns = []; var fixedShrinkColumns = []; var flexShrinkUnits = 0; var overflowWidth = 0; var gapFill = 0; function calcWidth(width) { var colWidth; if (typeof width == "string") { if (width.indexOf("%") > -1) { colWidth = totalWidth / 100 * parseInt(width); } else { colWidth = parseInt(width); } } else { colWidth = width; } return colWidth; } function scaleColumns(columns2, freeSpace, colWidth, shrinkCols) { var oversizeCols = [], oversizeSpace = 0, remainingSpace = 0, nextColWidth = 0, remainingFlexGrowUnits = flexGrowUnits, gap = 0, changeUnits = 0, undersizeCols = []; function calcGrow(col) { return colWidth * (col.column.definition.widthGrow || 1); } function calcShrink(col) { return calcWidth(col.width) - colWidth * (col.column.definition.widthShrink || 0); } columns2.forEach(function (col, i) { var width = shrinkCols ? calcShrink(col) : calcGrow(col); if (col.column.minWidth >= width) { oversizeCols.push(col); } else { if (col.column.maxWidth && col.column.maxWidth < width) { col.width = col.column.maxWidth; freeSpace -= col.column.maxWidth; remainingFlexGrowUnits -= shrinkCols ? col.column.definition.widthShrink || 1 : col.column.definition.widthGrow || 1; if (remainingFlexGrowUnits) { colWidth = Math.floor(freeSpace / remainingFlexGrowUnits); } } else { undersizeCols.push(col); changeUnits += shrinkCols ? col.column.definition.widthShrink || 1 : col.column.definition.widthGrow || 1; } } }); if (oversizeCols.length) { oversizeCols.forEach(function (col) { oversizeSpace += shrinkCols ? col.width - col.column.minWidth : col.column.minWidth; col.width = col.column.minWidth; }); remainingSpace = freeSpace - oversizeSpace; nextColWidth = changeUnits ? Math.floor(remainingSpace / changeUnits) : remainingSpace; gap = scaleColumns(undersizeCols, remainingSpace, nextColWidth, shrinkCols); } else { gap = changeUnits ? freeSpace - Math.floor(freeSpace / changeUnits) * changeUnits : freeSpace; undersizeCols.forEach(function (column) { column.width = shrinkCols ? calcShrink(column) : calcGrow(column); }); } return gap; } if (this.table.options.responsiveLayout && this.table.modExists("responsiveLayout", true)) { this.table.modules.responsiveLayout.update(); } if (this.table.rowManager.element.scrollHeight > this.table.rowManager.element.clientHeight) { totalWidth -= this.table.rowManager.element.offsetWidth - this.table.rowManager.element.clientWidth; } columns.forEach(function (column) { var width, minWidth, colWidth; if (column.visible) { width = column.definition.width; minWidth = parseInt(column.minWidth); if (width) { colWidth = calcWidth(width); fixedWidth += colWidth > minWidth ? colWidth : minWidth; if (column.definition.widthShrink) { fixedShrinkColumns.push({ column, width: colWidth > minWidth ? colWidth : minWidth }); flexShrinkUnits += column.definition.widthShrink; } } else { flexColumns.push({ column, width: 0 }); flexGrowUnits += column.definition.widthGrow || 1; } } }); flexWidth = totalWidth - fixedWidth; flexColWidth = Math.floor(flexWidth / flexGrowUnits); gapFill = scaleColumns(flexColumns, flexWidth, flexColWidth, false); if (flexColumns.length && gapFill > 0) { flexColumns[flexColumns.length - 1].width += gapFill; } flexColumns.forEach(function (col) { flexWidth -= col.width; }); overflowWidth = Math.abs(gapFill) + flexWidth; if (overflowWidth > 0 && flexShrinkUnits) { gapFill = scaleColumns(fixedShrinkColumns, overflowWidth, Math.floor(overflowWidth / flexShrinkUnits), true); } if (gapFill && fixedShrinkColumns.length) { fixedShrinkColumns[fixedShrinkColumns.length - 1].width -= gapFill; } flexColumns.forEach(function (col) { col.column.setWidth(col.width); }); fixedShrinkColumns.forEach(function (col) { col.column.setWidth(col.width); }); } var defaultModes = { fitData, fitDataFill: fitDataGeneral, fitDataTable: fitDataGeneral, fitDataStretch, fitColumns }; const _Layout = class _Layout extends Module { constructor(table) { super(table, "layout"); this.mode = null; this.registerTableOption("layout", "fitData"); this.registerTableOption("layoutColumnsOnNewData", false); this.registerColumnOption("widthGrow"); this.registerColumnOption("widthShrink"); } //initialize layout system initialize() { var layout = this.table.options.layout; if (_Layout.modes[layout]) { this.mode = layout; } else { console.warn("Layout Error - invalid mode set, defaulting to 'fitData' : " + layout); this.mode = "fitData"; } this.table.element.setAttribute("tabulator-layout", this.mode); this.subscribe("column-init", this.initializeColumn.bind(this)); } initializeColumn(column) { if (column.definition.widthGrow) { column.definition.widthGrow = Number(column.definition.widthGrow); } if (column.definition.widthShrink) { column.definition.widthShrink = Number(column.definition.widthShrink); } } getMode() { return this.mode; } //trigger table layout layout(dataChanged) { var variableHeight = this.table.columnManager.columnsByIndex.find((column) => column.definition.variableHeight || column.definition.formatter === "textarea"); this.dispatch("layout-refreshing"); _Layout.modes[this.mode].call(this, this.table.columnManager.columnsByIndex, dataChanged); if (variableHeight) { this.table.rowManager.normalizeHeight(true); } this.dispatch("layout-refreshed"); } }; __publicField(_Layout, "moduleName", "layout"); //load defaults __publicField(_Layout, "modes", defaultModes); let Layout = _Layout; var defaultLangs = { "default": { //hold default locale text "groups": { "item": "item", "items": "items" }, "columns": {}, "data": { "loading": "Loading", "error": "Error" }, "pagination": { "page_size": "Page Size", "page_title": "Show Page", "first": "First", "first_title": "First Page", "last": "Last", "last_title": "Last Page", "prev": "Prev", "prev_title": "Prev Page", "next": "Next", "next_title": "Next Page", "all": "All", "counter": { "showing": "Showing", "of": "of", "rows": "rows", "pages": "pages" } }, "headerFilters": { "default": "filter column...", "columns": {} } } }; const _Localize = class _Localize extends Module { constructor(table) { super(table); this.locale = "default"; this.lang = false; this.bindings = {}; this.langList = {}; this.registerTableOption("locale", false); this.registerTableOption("langs", {}); } initialize() { this.langList = Helpers.deepClone(_Localize.langs); if (this.table.options.columnDefaults.headerFilterPlaceholder !== false) { this.setHeaderFilterPlaceholder(this.table.options.columnDefaults.headerFilterPlaceholder); } for (let locale in this.table.options.langs) { this.installLang(locale, this.table.options.langs[locale]); } this.setLocale(this.table.options.locale); this.registerTableFunction("setLocale", this.setLocale.bind(this)); this.registerTableFunction("getLocale", this.getLocale.bind(this)); this.registerTableFunction("getLang", this.getLang.bind(this)); } //set header placeholder setHeaderFilterPlaceholder(placeholder) { this.langList.default.headerFilters.default = placeholder; } //setup a lang description object installLang(locale, lang) { if (this.langList[locale]) { this._setLangProp(this.langList[locale], lang); } else { this.langList[locale] = lang; } } _setLangProp(lang, values) { for (let key in values) { if (lang[key] && typeof lang[key] == "object") { this._setLangProp(lang[key], values[key]); } else { lang[key] = values[key]; } } } //set current locale setLocale(desiredLocale) { desiredLocale = desiredLocale || "default"; function traverseLang(trans, path) { for (var prop in trans) { if (typeof trans[prop] == "object") { if (!path[prop]) { path[prop] = {}; } traverseLang(trans[prop], path[prop]); } else { path[prop] = trans[prop]; } } } if (desiredLocale === true && navigator.language) { desiredLocale = navigator.language.toLowerCase(); } if (desiredLocale) { if (!this.langList[desiredLocale]) { let prefix = desiredLocale.split("-")[0]; if (this.langList[prefix]) { console.warn("Localization Error - Exact matching locale not found, using closest match: ", desiredLocale, prefix); desiredLocale = prefix; } else { console.warn("Localization Error - Matching locale not found, using default: ", desiredLocale); desiredLocale = "default"; } } } this.locale = desiredLocale; this.lang = Helpers.deepClone(this.langList.default || {}); if (desiredLocale != "default") { traverseLang(this.langList[desiredLocale], this.lang); } this.dispatchExternal("localized", this.locale, this.lang); this._executeBindings(); } //get current locale getLocale(locale) { return this.locale; } //get lang object for given local or current if none provided getLang(locale) { return locale ? this.langList[locale] : this.lang; } //get text for current locale getText(path, value) { var fillPath = value ? path + "|" + value : path, pathArray = fillPath.split("|"), text = this._getLangElement(pathArray, this.locale); return text || ""; } //traverse langs object and find localized copy _getLangElement(path, locale) { var root = this.lang; path.forEach(function (level) { var rootPath; if (root) { rootPath = root[level]; if (typeof rootPath != "undefined") { root = rootPath; } else { root = false; } } }); return root; } //set update binding bind(path, callback) { if (!this.bindings[path]) { this.bindings[path] = []; } this.bindings[path].push(callback); callback(this.getText(path), this.lang); } //iterate through bindings and trigger updates _executeBindings() { for (let path in this.bindings) { this.bindings[path].forEach((binding) => { binding(this.getText(path), this.lang); }); } } }; __publicField(_Localize, "moduleName", "localize"); //load defaults __publicField(_Localize, "langs", defaultLangs); let Localize = _Localize; class Comms extends Module { constructor(table) { super(table); } initialize() { this.registerTableFunction("tableComms", this.receive.bind(this)); } getConnections(selectors) { var connections = [], connection; connection = this.table.constructor.registry.lookupTable(selectors); connection.forEach((con) => { if (this.table !== con) { connections.push(con); } }); return connections; } send(selectors, module, action, data) { var connections = this.getConnections(selectors); connections.forEach((connection) => { connection.tableComms(this.table.element, module, action, data); }); if (!connections.length && selectors) { console.warn("Table Connection Error - No tables matching selector found", selectors); } } receive(table, module, action, data) { if (this.table.modExists(module)) { return this.table.modules[module].commsReceived(table, action, data); } else { console.warn("Inter-table Comms Error - no such module:", module); } } } __publicField(Comms, "moduleName", "comms"); var coreModules = /* @__PURE__ */ Object.freeze({ __proto__: null, CommsModule: Comms, LayoutModule: Layout, LocalizeModule: Localize }); const _TableRegistry = class _TableRegistry { static findTable(query) { var results = _TableRegistry.registry.lookupTable(query, true); return Array.isArray(results) && !results.length ? false : results; } }; __publicField(_TableRegistry, "registry", { tables: [], register(table) { _TableRegistry.registry.tables.push(table); }, deregister(table) { var index = _TableRegistry.registry.tables.indexOf(table); if (index > -1) { _TableRegistry.registry.tables.splice(index, 1); } }, lookupTable(query, silent) { var results = [], matches, match; if (typeof query === "string") { matches = document.querySelectorAll(query); if (matches.length) { for (var i = 0; i < matches.length; i++) { match = _TableRegistry.registry.matchElement(matches[i]); if (match) { results.push(match); } } } } else if (typeof HTMLElement !== "undefined" && query instanceof HTMLElement || query instanceof _TableRegistry) { match = _TableRegistry.registry.matchElement(query); if (match) { results.push(match); } } else if (Array.isArray(query)) { query.forEach(function (item) { results = results.concat(_TableRegistry.registry.lookupTable(item)); }); } else { if (!silent) { console.warn("Table Connection Error - Invalid Selector", query); } } return results; }, matchElement(element) { return _TableRegistry.registry.tables.find(function (table) { return element instanceof _TableRegistry ? table === element : table.element === element; }); } }); let TableRegistry = _TableRegistry; const _ModuleBinder = class _ModuleBinder extends TableRegistry { constructor() { super(); } static initializeModuleBinder(defaultModules) { if (!_ModuleBinder.modulesRegistered) { _ModuleBinder.modulesRegistered = true; _ModuleBinder._registerModules(coreModules, true); if (defaultModules) { _ModuleBinder._registerModules(defaultModules); } } } static _extendModule(name2, property, values) { if (_ModuleBinder.moduleBindings[name2]) { var source = _ModuleBinder.moduleBindings[name2][property]; if (source) { if (typeof values == "object") { for (let key in values) { source[key] = values[key]; } } else { console.warn("Module Error - Invalid value type, it must be an object"); } } else { console.warn("Module Error - property does not exist:", property); } } else { console.warn("Module Error - module does not exist:", name2); } } static _registerModules(modules, core) { var mods = Object.values(modules); if (core) { mods.forEach((mod) => { mod.prototype.moduleCore = true; }); } _ModuleBinder._registerModule(mods); } static _registerModule(modules) { if (!Array.isArray(modules)) { modules = [modules]; } modules.forEach((mod) => { _ModuleBinder._registerModuleBinding(mod); _ModuleBinder._registerModuleExtensions(mod); }); } static _registerModuleBinding(mod) { if (mod.moduleName) { _ModuleBinder.moduleBindings[mod.moduleName] = mod; } else { console.error("Unable to bind module, no moduleName defined", mod.moduleName); } } static _registerModuleExtensions(mod) { var extensions2 = mod.moduleExtensions; if (mod.moduleExtensions) { for (let modKey in extensions2) { let ext = extensions2[modKey]; if (_ModuleBinder.moduleBindings[modKey]) { for (let propKey in ext) { _ModuleBinder._extendModule(modKey, propKey, ext[propKey]); } } else { if (!_ModuleBinder.moduleExtensions[modKey]) { _ModuleBinder.moduleExtensions[modKey] = {}; } for (let propKey in ext) { if (!_ModuleBinder.moduleExtensions[modKey][propKey]) { _ModuleBinder.moduleExtensions[modKey][propKey] = {}; } Object.assign(_ModuleBinder.moduleExtensions[modKey][propKey], ext[propKey]); } } } } _ModuleBinder._extendModuleFromQueue(mod); } static _extendModuleFromQueue(mod) { var extensions2 = _ModuleBinder.moduleExtensions[mod.moduleName]; if (extensions2) { for (let propKey in extensions2) { _ModuleBinder._extendModule(mod.moduleName, propKey, extensions2[propKey]); } } } //ensure that module are bound to instantiated function _bindModules() { var orderedStartMods = [], orderedEndMods = [], unOrderedMods = []; this.modules = {}; for (var name2 in _ModuleBinder.moduleBindings) { let mod = _ModuleBinder.moduleBindings[name2]; let module = new mod(this); this.modules[name2] = module; if (mod.prototype.moduleCore) { this.modulesCore.push(module); } else { if (mod.moduleInitOrder) { if (mod.moduleInitOrder < 0) { orderedStartMods.push(module); } else { orderedEndMods.push(module); } } else { unOrderedMods.push(module); } } } orderedStartMods.sort((a, b) => a.moduleInitOrder > b.moduleInitOrder ? 1 : -1); orderedEndMods.sort((a, b) => a.moduleInitOrder > b.moduleInitOrder ? 1 : -1); this.modulesRegular = orderedStartMods.concat(unOrderedMods.concat(orderedEndMods)); } }; __publicField(_ModuleBinder, "moduleBindings", {}); __publicField(_ModuleBinder, "moduleExtensions", {}); __publicField(_ModuleBinder, "modulesRegistered", false); __publicField(_ModuleBinder, "defaultModules", false); let ModuleBinder = _ModuleBinder; class Alert extends CoreFeature { constructor(table) { super(table); this.element = this._createAlertElement(); this.msgElement = this._createMsgElement(); this.type = null; this.element.appendChild(this.msgElement); } _createAlertElement() { var el = document.createElement("div"); el.classList.add("tabulator-alert"); return el; } _createMsgElement() { var el = document.createElement("div"); el.classList.add("tabulator-alert-msg"); el.setAttribute("role", "alert"); return el; } _typeClass() { return "tabulator-alert-state-" + this.type; } alert(content, type = "msg") { if (content) { this.clear(); this.dispatch("alert-show", type); this.type = type; while (this.msgElement.firstChild) this.msgElement.removeChild(this.msgElement.firstChild); this.msgElement.classList.add(this._typeClass()); if (typeof content === "function") { content = content(); } if (content instanceof HTMLElement) { this.msgElement.appendChild(content); } else { this.msgElement.innerHTML = content; } this.table.element.appendChild(this.element); } } clear() { this.dispatch("alert-hide", this.type); if (this.element.parentNode) { this.element.parentNode.removeChild(this.element); } this.msgElement.classList.remove(this._typeClass()); } } let Tabulator$1 = (_a = class extends ModuleBinder { static extendModule() { _a.initializeModuleBinder(); _a._extendModule(...arguments); } static registerModule() { _a.initializeModuleBinder(); _a._registerModule(...arguments); } constructor(element, options, modules) { super(); _a.initializeModuleBinder(modules); this.options = {}; this.columnManager = null; this.rowManager = null; this.footerManager = null; this.alertManager = null; this.vdomHoz = null; this.externalEvents = null; this.eventBus = null; this.interactionMonitor = false; this.browser = ""; this.browserSlow = false; this.browserMobile = false; this.rtl = false; this.originalElement = null; this.componentFunctionBinder = new ComponentFunctionBinder(this); this.dataLoader = false; this.modules = {}; this.modulesCore = []; this.modulesRegular = []; this.deprecationAdvisor = new DeprecationAdvisor(this); this.optionsList = new OptionsList(this, "table constructor"); this.dependencyRegistry = new DependencyRegistry(this); this.initialized = false; this.destroyed = false; if (this.initializeElement(element)) { this.initializeCoreSystems(options); setTimeout(() => { this._create(); }); } this.constructor.registry.register(this); } initializeElement(element) { if (typeof HTMLElement !== "undefined" && element instanceof HTMLElement) { this.element = element; return true; } else if (typeof element === "string") { this.element = document.querySelector(element); if (this.element) { return true; } else { console.error("Tabulator Creation Error - no element found matching selector: ", element); return false; } } else { console.error("Tabulator Creation Error - Invalid element provided:", element); return false; } } initializeCoreSystems(options) { this.columnManager = new ColumnManager(this); this.rowManager = new RowManager(this); this.footerManager = new FooterManager(this); this.dataLoader = new DataLoader(this); this.alertManager = new Alert(this); this._bindModules(); this.options = this.optionsList.generate(_a.defaultOptions, options); this._clearObjectPointers(); this._mapDeprecatedFunctionality(); this.externalEvents = new ExternalEventBus(this, this.options, this.options.debugEventsExternal); this.eventBus = new InternalEventBus(this.options.debugEventsInternal); this.interactionMonitor = new InteractionManager(this); this.dataLoader.initialize(); this.footerManager.initialize(); this.dependencyRegistry.initialize(); } //convert deprecated functionality to new functions _mapDeprecatedFunctionality() { } _clearSelection() { this.element.classList.add("tabulator-block-select"); if (window.getSelection) { if (window.getSelection().empty) { window.getSelection().empty(); } else if (window.getSelection().removeAllRanges) { window.getSelection().removeAllRanges(); } } else if (document.selection) { document.selection.empty(); } this.element.classList.remove("tabulator-block-select"); } //create table _create() { this.externalEvents.dispatch("tableBuilding"); this.eventBus.dispatch("table-building"); this._rtlCheck(); this._buildElement(); this._initializeTable(); this.initialized = true; this._loadInitialData().finally(() => { this.eventBus.dispatch("table-initialized"); this.externalEvents.dispatch("tableBuilt"); }); } _rtlCheck() { var style = window.getComputedStyle(this.element); switch (this.options.textDirection) { case "auto": if (style.direction !== "rtl") { break; } case "rtl": this.element.classList.add("tabulator-rtl"); this.rtl = true; break; case "ltr": this.element.classList.add("tabulator-ltr"); default: this.rtl = false; } } //clear pointers to objects in default config object _clearObjectPointers() { this.options.columns = this.options.columns.slice(0); if (Array.isArray(this.options.data) && !this.options.reactiveData) { this.options.data = this.options.data.slice(0); } } //build tabulator element _buildElement() { var element = this.element, options = this.options, newElement; if (element.tagName === "TABLE") { this.originalElement = this.element; newElement = document.createElement("div"); var attributes = element.attributes; for (var i in attributes) { if (typeof attributes[i] == "object") { newElement.setAttribute(attributes[i].name, attributes[i].value); } } element.parentNode.replaceChild(newElement, element); this.element = element = newElement; } element.classList.add("tabulator"); element.setAttribute("role", "grid"); element.setAttribute("aria-owns", "tabulator-table-body"); while (element.firstChild) element.removeChild(element.firstChild); if (options.height) { options.height = isNaN(options.height) ? options.height : options.height + "px"; element.style.height = options.height; } if (options.minHeight !== false) { options.minHeight = isNaN(options.minHeight) ? options.minHeight : options.minHeight + "px"; element.style.minHeight = options.minHeight; } if (options.maxHeight !== false) { options.maxHeight = isNaN(options.maxHeight) ? options.maxHeight : options.maxHeight + "px"; element.style.maxHeight = options.maxHeight; } } //initialize core systems and modules _initializeTable() { var element = this.element, options = this.options; this.interactionMonitor.initialize(); this.columnManager.initialize(); this.rowManager.initialize(); this._detectBrowser(); this.modulesCore.forEach((mod) => { mod.initialize(); }); element.appendChild(this.columnManager.getElement()); element.appendChild(this.rowManager.getElement()); if (options.footerElement) { this.footerManager.activate(); } if (options.autoColumns && options.data) { this.columnManager.generateColumnsFromRowData(this.options.data); } this.modulesRegular.forEach((mod) => { mod.initialize(); }); this.columnManager.setColumns(options.columns); this.eventBus.dispatch("table-built"); } _loadInitialData() { return this.dataLoader.load(this.options.data).finally(() => { this.columnManager.verticalAlignHeaders(); }); } //deconstructor destroy() { var element = this.element; this.destroyed = true; this.constructor.registry.deregister(this); this.eventBus.dispatch("table-destroy"); this.rowManager.destroy(); while (element.firstChild) element.removeChild(element.firstChild); element.classList.remove("tabulator"); element.removeAttribute("tabulator-layout"); this.externalEvents.dispatch("tableDestroyed"); } _detectBrowser() { var ua = navigator.userAgent || navigator.vendor || window.opera; if (ua.indexOf("Trident") > -1) { this.browser = "ie"; this.browserSlow = true; } else if (ua.indexOf("Edge") > -1) { this.browser = "edge"; this.browserSlow = true; } else if (ua.indexOf("Firefox") > -1) { this.browser = "firefox"; this.browserSlow = false; } else if (ua.indexOf("Mac OS") > -1) { this.browser = "safari"; this.browserSlow = false; } else { this.browser = "other"; this.browserSlow = false; } this.browserMobile = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(ua) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(ua.slice(0, 4)); } initGuard(func, msg) { var stack, line; if (this.options.debugInitialization && !this.initialized) { if (!func) { stack = new Error().stack.split("\n"); line = stack[0] == "Error" ? stack[2] : stack[1]; if (line[0] == " ") { func = line.trim().split(" ")[1].split(".")[1]; } else { func = line.trim().split("@")[0]; } } console.warn("Table Not Initialized - Calling the " + func + " function before the table is initialized may result in inconsistent behavior, Please wait for the `tableBuilt` event before calling this function." + (msg ? " " + msg : "")); } return this.initialized; } ////////////////// Data Handling ////////////////// //block table redrawing blockRedraw() { this.initGuard(); this.eventBus.dispatch("redraw-blocking"); this.rowManager.blockRedraw(); this.columnManager.blockRedraw(); this.eventBus.dispatch("redraw-blocked"); } //restore table redrawing restoreRedraw() { this.initGuard(); this.eventBus.dispatch("redraw-restoring"); this.rowManager.restoreRedraw(); this.columnManager.restoreRedraw(); this.eventBus.dispatch("redraw-restored"); } //load data setData(data, params, config) { this.initGuard(false, "To set initial data please use the 'data' property in the table constructor."); return this.dataLoader.load(data, params, config, false); } //clear data clearData() { this.initGuard(); this.dataLoader.blockActiveLoad(); this.rowManager.clearData(); } //get table data array getData(active) { return this.rowManager.getData(active); } //get table data array count getDataCount(active) { return this.rowManager.getDataCount(active); } //replace data, keeping table in position with same sort replaceData(data, params, config) { this.initGuard(); return this.dataLoader.load(data, params, config, true, true); } //update table data updateData(data) { var responses = 0; this.initGuard(); return new Promise((resolve, reject) => { this.dataLoader.blockActiveLoad(); if (typeof data === "string") { data = JSON.parse(data); } if (data && data.length > 0) { data.forEach((item) => { var row = this.rowManager.findRow(item[this.options.index]); if (row) { responses++; row.updateData(item).then(() => { responses--; if (!responses) { resolve(); } }).catch((e) => { reject("Update Error - Unable to update row", item, e); }); } else { reject("Update Error - Unable to find row", item); } }); } else { console.warn("Update Error - No data provided"); reject("Update Error - No data provided"); } }); } addData(data, pos, index) { this.initGuard(); return new Promise((resolve, reject) => { this.dataLoader.blockActiveLoad(); if (typeof data === "string") { data = JSON.parse(data); } if (data) { this.rowManager.addRows(data, pos, index).then((rows2) => { var output = []; rows2.forEach(function (row) { output.push(row.getComponent()); }); resolve(output); }); } else { console.warn("Update Error - No data provided"); reject("Update Error - No data provided"); } }); } //update table data updateOrAddData(data) { var rows2 = [], responses = 0; this.initGuard(); return new Promise((resolve, reject) => { this.dataLoader.blockActiveLoad(); if (typeof data === "string") { data = JSON.parse(data); } if (data && data.length > 0) { data.forEach((item) => { var row = this.rowManager.findRow(item[this.options.index]); responses++; if (row) { row.updateData(item).then(() => { responses--; rows2.push(row.getComponent()); if (!responses) { resolve(rows2); } }); } else { this.rowManager.addRows(item).then((newRows) => { responses--; rows2.push(newRows[0].getComponent()); if (!responses) { resolve(rows2); } }); } }); } else { console.warn("Update Error - No data provided"); reject("Update Error - No data provided"); } }); } //get row object getRow(index) { var row = this.rowManager.findRow(index); if (row) { return row.getComponent(); } else { console.warn("Find Error - No matching row found:", index); return false; } } //get row object getRowFromPosition(position) { var row = this.rowManager.getRowFromPosition(position); if (row) { return row.getComponent(); } else { console.warn("Find Error - No matching row found:", position); return false; } } //delete row from table deleteRow(index) { var foundRows = []; this.initGuard(); if (!Array.isArray(index)) { index = [index]; } for (let item of index) { let row = this.rowManager.findRow(item, true); if (row) { foundRows.push(row); } else { console.error("Delete Error - No matching row found:", item); return Promise.reject("Delete Error - No matching row found"); } } foundRows.sort((a, b) => { return this.rowManager.rows.indexOf(a) > this.rowManager.rows.indexOf(b) ? 1 : -1; }); foundRows.forEach((row) => { row.delete(); }); this.rowManager.reRenderInPosition(); return Promise.resolve(); } //add row to table addRow(data, pos, index) { this.initGuard(); if (typeof data === "string") { data = JSON.parse(data); } return this.rowManager.addRows(data, pos, index, true).then((rows2) => { return rows2[0].getComponent(); }); } //update a row if it exists otherwise create it updateOrAddRow(index, data) { var row = this.rowManager.findRow(index); this.initGuard(); if (typeof data === "string") { data = JSON.parse(data); } if (row) { return row.updateData(data).then(() => { return row.getComponent(); }); } else { return this.rowManager.addRows(data).then((rows2) => { return rows2[0].getComponent(); }); } } //update row data updateRow(index, data) { var row = this.rowManager.findRow(index); this.initGuard(); if (typeof data === "string") { data = JSON.parse(data); } if (row) { return row.updateData(data).then(() => { return Promise.resolve(row.getComponent()); }); } else { console.warn("Update Error - No matching row found:", index); return Promise.reject("Update Error - No matching row found"); } } //scroll to row in DOM scrollToRow(index, position, ifVisible) { var row = this.rowManager.findRow(index); if (row) { return this.rowManager.scrollToRow(row, position, ifVisible); } else { console.warn("Scroll Error - No matching row found:", index); return Promise.reject("Scroll Error - No matching row found"); } } moveRow(from, to, after) { var fromRow = this.rowManager.findRow(from); this.initGuard(); if (fromRow) { fromRow.moveToRow(to, after); } else { console.warn("Move Error - No matching row found:", from); } } getRows(active) { return this.rowManager.getComponents(active); } //get position of row in table getRowPosition(index) { var row = this.rowManager.findRow(index); if (row) { return row.getPosition(); } else { console.warn("Position Error - No matching row found:", index); return false; } } /////////////// Column Functions /////////////// setColumns(definition) { this.initGuard(false, "To set initial columns please use the 'columns' property in the table constructor"); this.columnManager.setColumns(definition); } getColumns(structured) { return this.columnManager.getComponents(structured); } getColumn(field) { var column = this.columnManager.findColumn(field); if (column) { return column.getComponent(); } else { console.warn("Find Error - No matching column found:", field); return false; } } getColumnDefinitions() { return this.columnManager.getDefinitionTree(); } showColumn(field) { var column = this.columnManager.findColumn(field); this.initGuard(); if (column) { column.show(); } else { console.warn("Column Show Error - No matching column found:", field); return false; } } hideColumn(field) { var column = this.columnManager.findColumn(field); this.initGuard(); if (column) { column.hide(); } else { console.warn("Column Hide Error - No matching column found:", field); return false; } } toggleColumn(field) { var column = this.columnManager.findColumn(field); this.initGuard(); if (column) { if (column.visible) { column.hide(); } else { column.show(); } } else { console.warn("Column Visibility Toggle Error - No matching column found:", field); return false; } } addColumn(definition, before, field) { var column = this.columnManager.findColumn(field); this.initGuard(); return this.columnManager.addColumn(definition, before, column).then((column2) => { return column2.getComponent(); }); } deleteColumn(field) { var column = this.columnManager.findColumn(field); this.initGuard(); if (column) { return column.delete(); } else { console.warn("Column Delete Error - No matching column found:", field); return Promise.reject(); } } updateColumnDefinition(field, definition) { var column = this.columnManager.findColumn(field); this.initGuard(); if (column) { return column.updateDefinition(definition); } else { console.warn("Column Update Error - No matching column found:", field); return Promise.reject(); } } moveColumn(from, to, after) { var fromColumn = this.columnManager.findColumn(from), toColumn = this.columnManager.findColumn(to); this.initGuard(); if (fromColumn) { if (toColumn) { this.columnManager.moveColumn(fromColumn, toColumn, after); } else { console.warn("Move Error - No matching column found:", toColumn); } } else { console.warn("Move Error - No matching column found:", from); } } //scroll to column in DOM scrollToColumn(field, position, ifVisible) { return new Promise((resolve, reject) => { var column = this.columnManager.findColumn(field); if (column) { return this.columnManager.scrollToColumn(column, position, ifVisible); } else { console.warn("Scroll Error - No matching column found:", field); return Promise.reject("Scroll Error - No matching column found"); } }); } //////////// General Public Functions //////////// //redraw list without updating data redraw(force) { this.initGuard(); this.columnManager.redraw(force); this.rowManager.redraw(force); } setHeight(height) { this.options.height = isNaN(height) ? height : height + "px"; this.element.style.height = this.options.height; this.rowManager.initializeRenderer(); this.rowManager.redraw(true); } setMaxHeight(maxHeight) { this.options.maxHeight = isNaN(maxHeight) ? maxHeight : maxHeight + "px"; this.element.style.maxHeight = this.options.maxHeight; this.rowManager.initializeRenderer(); this.rowManager.redraw(true); } setMinHeight(minHeight) { this.options.minHeight = isNaN(minHeight) ? minHeight : minHeight + "px"; this.element.style.minHeight = this.options.minHeight; this.rowManager.initializeRenderer(); this.rowManager.redraw(true); } //////////////////// Event Bus /////////////////// on(key, callback) { this.externalEvents.subscribe(key, callback); } off(key, callback) { this.externalEvents.unsubscribe(key, callback); } dispatchEvent() { var args = Array.from(arguments); args.shift(); this.externalEvents.dispatch(...arguments); } //////////////////// Alerts /////////////////// alert(contents, type) { this.initGuard(); this.alertManager.alert(contents, type); } clearAlert() { this.initGuard(); this.alertManager.clear(); } ////////////// Extension Management ////////////// modExists(plugin, required) { if (this.modules[plugin]) { return true; } else { if (required) { console.error("Tabulator Module Not Installed: " + plugin); } return false; } } module(key) { var mod = this.modules[key]; if (!mod) { console.error("Tabulator module not installed: " + key); } return mod; } }, //default setup options __publicField(_a, "defaultOptions", defaultOptions), _a); class TabulatorFull extends Tabulator$1 { static extendModule() { Tabulator$1.initializeModuleBinder(allModules); Tabulator$1._extendModule(...arguments); } static registerModule() { Tabulator$1.initializeModuleBinder(allModules); Tabulator$1._registerModule(...arguments); } constructor(element, options, modules) { super(element, options, allModules); } } const DMM_GRAPHQL_URL = "https://api.video.dmm.co.jp/graphql"; const dmmGraphQLKeyword = (code) => { const match = String(code || "").trim().toUpperCase().match(/^([A-Z0-9]{2,10})-(\d{1,6})$/); if (!match) return ""; return `${match[1].toLowerCase()}${match[2].padStart(5, "0")}`; }; const dmmGraphQLRequest = (query) => { return new Promise((resolve, reject) => { _GM_xmlhttpRequest({ method: "POST", url: DMM_GRAPHQL_URL, data: JSON.stringify({query}), timeout: 12e3, headers: { Accept: "application/json", "Content-Type": "application/json", Referer: "https://video.dmm.co.jp/", "Fanza-Device": "BROWSER", "Cache-Control": "no-cache" }, onload: (response) => { if (response.status < 200 || response.status >= 400) { reject(new Error(`DMM GraphQL HTTP ${response.status || 0}`)); return; } try { const json2 = JSON.parse(response.responseText || "{}"); if (Array.isArray(json2.errors) && json2.errors.length) { reject(new Error(json2.errors[0].message || "DMM GraphQL error")); return; } resolve(json2.data || {}); } catch (e) { reject(new Error(`DMM GraphQL JSON parse error: ${e.message}`)); } }, onerror: () => reject(new Error("DMM GraphQL network error")), ontimeout: () => reject(new Error("DMM GraphQL timeout")) }); }); }; const searchDmmContentIdsByGraphQL = async (code) => { const keyword = dmmGraphQLKeyword(code); if (!keyword) return []; const carNumNoHyphen = String(code || "").replace(/-/g, "").toLowerCase(); const results = []; const searchKeywords = [.../* @__PURE__ */ new Set([keyword, `${keyword}#`])]; for (const searchKeyword of searchKeywords) { const query = `{ legacySearchPPV(limit: 10, offset: 0, sort: SALES_RANK_SCORE, floor: AV, queryWord: ${JSON.stringify(searchKeyword)}) { result { contents { contentId serviceCode floorCode } } } }`; const data = await dmmGraphQLRequest(query); const contents = Array.isArray(data && data.legacySearchPPV && data.legacySearchPPV.result && data.legacySearchPPV.result.contents) ? data.legacySearchPPV.result.contents : []; for (const item of contents) { const contentId = String(item.contentId || ""); if (contentId.replace(/[^a-z0-9]/gi, "").toLowerCase().includes(carNumNoHyphen)) { results.push({ serviceCode: item.serviceCode || "digital", floorCode: item.floorCode || "AV", contentId }); } } if (results.length > 0) break; } return results; }; const getDmmGraphQLVideoUrls = async (code) => { const contentItems = await searchDmmContentIdsByGraphQL(code); if (!contentItems.length) return null; const results = await Promise.allSettled( contentItems.map((item) => extractTrailerLinks(item)) ); for (const result of results) { if (result.status === "fulfilled" && result.value) { return result.value; } } return null; }; const extractTrailerLinks = (item) => { const {contentId, serviceCode, floorCode} = item; return new Promise((resolve, reject) => { const trailerPageUrl = `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${contentId}/mtype=AhRVShI_/service=${serviceCode}/floor=${floorCode}/mode=/`; _GM_xmlhttpRequest({ method: "GET", url: trailerPageUrl, timeout: 15e3, headers: { "accept-language": "ja-JP,ja;q=0.9", Cookie: "age_check_done=1" }, onload: (response) => { const htmlContent = response.responseText || ""; if (htmlContent.includes("このサービスはお住まいの地域からは")) { reject(new Error("节点不可用,请将DMM域名分流到日本ip")); return; } const match = htmlContent.match(/const\s+args\s+=\s+(.*);/); if (!match) { reject(new Error("未在脚本中找到 const args = ... 变量")); return; } let bitrates; try { bitrates = JSON.parse(match[1]).bitrates; } catch (e) { reject(new Error(`解析播放器脚本 JSON 失败: ${e.message}`)); return; } if (!Array.isArray(bitrates)) { reject(new Error("解析画质链接失败: bitrates 字段不是一个数组或不存在")); return; } const qualityKeys = [ "4ks", "4k", "hhbs", "hhb", "hmb", "mhb", "mmb", "dmb_w", "dmb_s", "dm_s", "sm_s", "mhb_w" ]; const qualityNameRegex = new RegExp(`(${qualityKeys.join("|")})\\.mp4$`); const finalQualityMap = {}; for (const bitrate of bitrates) { const url = bitrate && bitrate.src; if (!url || typeof url !== "string" || !url.endsWith(".mp4")) continue; const qualityMatch = url.match(qualityNameRegex); let qualityKey = ""; if (qualityMatch && qualityMatch[1]) qualityKey = qualityMatch[1]; if (qualityKey && !finalQualityMap[qualityKey]) { finalQualityMap[qualityKey] = url; } } if (Object.keys(finalQualityMap).length === 0) { reject(new Error("未找到匹配要求的预览画质视频")); return; } resolve(finalQualityMap); }, onerror: () => reject(new Error("DMM 播放页请求网络错误")), ontimeout: () => reject(new Error("DMM 播放页请求超时")) }); }); }; const selectDefaultQuality = (dmmVideoQualityList, intendedDefault) => { if (!dmmVideoQualityList || 0 === dmmVideoQualityList.length) return null; const availableSet = new Set(dmmVideoQualityList); if (availableSet.has(intendedDefault)) return intendedDefault; const priorityOrder = qualityOptions.map(((option) => option.quality)).reverse(); for (const quality of priorityOrder) if (availableSet.has(quality)) return quality; return dmmVideoQualityList[0]; }, CACHE_KEY = "jhs_dmm_video"; class DmmVideoFetcher { constructor(carNum2, showErrorMessages = true) { this.carNum = carNum2; this.showErrorMessages = showErrorMessages; } _checkCache() { const cachedData = localStorage.getItem(CACHE_KEY) ? JSON.parse(localStorage.getItem(CACHE_KEY)) : {}; if (cachedData[this.carNum]) { clog.debug("缓存中存在预览视频信息", cachedData[this.carNum]); return cachedData[this.carNum]; } return null; } _updateCache(videoMap) { const cachedData = localStorage.getItem(CACHE_KEY) ? JSON.parse(localStorage.getItem(CACHE_KEY)) : {}; cachedData[this.carNum] = videoMap; clog.debug("成功解析出预览视频并已缓存:", videoMap); localStorage.setItem(CACHE_KEY, JSON.stringify(cachedData)); } async _searchContentIds() { const carNum2 = this.carNum, carNumNoHyphen = carNum2.replace(/-/g, ""), keywordAttempts = [{ keyword: carNum2.replace("-", "00"), name: "00-替换关键词" }, { keyword: carNum2, name: "原始番号关键词" }, { keyword: carNumNoHyphen, name: "无连字符关键词" }], carNumLower = carNum2.toLowerCase(); for (const attempt of keywordAttempts) { const {keyword, name: name2} = attempt, currentTempCarNumLower = keyword.toLowerCase(), apiUrl2 = `https://api.dmm.com/affiliate/v3/ItemList?${new URLSearchParams({ api_id: "UrwskPfkqQ0DuVry2gYL", affiliate_id: "10278-996", output: "json", site: "FANZA", sort: "match", keyword }).toString()}`; let response; try { response = await gmHttp.get(apiUrl2); } catch (e) { clog.error(`API 请求失败,跳过 ${name2}:`, e); continue; } if (!response || !response.result || !response.result.result_count) { clog.debug(`使用 ${name2} (${keyword}) 进行 API 搜索 返回无结果,尝试下一个关键词。`); continue; } const newItems = []; for (const item of response.result.items) { if (newItems.length >= 2) break; const contentId = item.content_id || "", makerProduct = item.maker_product || ""; if (contentId.includes(currentTempCarNumLower.replace("-", "")) || carNumLower === makerProduct.toLowerCase() || contentId.includes(carNumNoHyphen.toLowerCase())) { newItems.push({ serviceCode: item.service_code, floorCode: item.floor_code, contentId, pageUrl: item.URL }); clog.debug(`[${name2}] cid|makerProduct 匹配成功:`, contentId, makerProduct); } } if (newItems.length > 0) { clog.debug(`--- 成功通过 ${name2} 找到 Content IDs ---`); const $btn2 = $("#fanzaBtn"); let url = `https://www.dmm.co.jp/search/=/searchstr=${keyword}`, type = "single"; if (newItems.length > 1) { $btn2.attr("href", url); $btn2.append('多结果'); $btn2.css("backgroundColor", "#7bc73b"); type = "multiple"; } else { url = newItems[0].pageUrl; $btn2.attr("href", url); $btn2.css("backgroundColor", "#7bc73b"); } const dmmCacheKey = "jhs_other_site_dmm", dmmCacheData = localStorage.getItem(dmmCacheKey) ? JSON.parse(localStorage.getItem(dmmCacheKey)) : {}; dmmCacheData[this.carNum] = { type, url }; localStorage.setItem(dmmCacheKey, JSON.stringify(dmmCacheData)); return newItems; } clog.debug(`[${name2}] API 返回结果数 ${response.result.result_count},但无精确匹配的 Content ID。`); } clog.warn("所有关键词尝试均未找到匹配的Content ID, 解析Dmm视频失败"); const $btn = $("#fanzaBtn"); $btn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`); $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", "#de3333"); return null; } async _extractTrailerLinks({contentId, serviceCode, floorCode}) { const trailerPageUrl = `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${contentId}/mtype=AhRVShI_/service=${serviceCode}/floor=${floorCode}/mode=/`, htmlContent = await gmHttp.get(trailerPageUrl, null, { "accept-language": "ja-JP,ja;q=0.9", Cookie: "age_check_done=1" }); if ("string" != typeof htmlContent) { clog.error(htmlContent); throw new Error("解析播放页内容失败, 非文本内容"); } if (htmlContent.includes("このサービスはお住まいの地域からは")) throw new Error("节点不可用,请将DMM域名分流到日本ip"); const match = htmlContent.match(/const\s+args\s+=\s+(.*);/); if (!match) throw new Error("未在脚本中找到 const args = ... 变量"); let bitrates; try { ({bitrates} = JSON.parse(match[1])); } catch (e) { throw new Error(`解析播放器脚本 JSON 失败: ${e.message}`); } const finalQualityMap = {}, qualityKeys = qualityOptions.map(((o) => o.quality)).join("|"), qualityNameRegex = new RegExp(`(${qualityKeys})\\.mp4$`); if (!Array.isArray(bitrates)) { clog.error("解析画质链接失败: bitrates 字段不是一个数组或不存在"); throw new Error("解析画质链接失败: bitrates 字段不是一个数组或不存在"); } clog.debug("原始数据返回:", bitrates); for (const item of bitrates) { const url = null == item ? void 0 : item.src; if (!url || "string" != typeof url || !url.endsWith(".mp4")) continue; const qualityMatch = url.match(qualityNameRegex); let qualityKey = ""; qualityMatch && qualityMatch[1] && (qualityKey = qualityMatch[1]); qualityKey && !finalQualityMap[qualityKey] && (finalQualityMap[qualityKey] = url); } if (0 === Object.keys(finalQualityMap).length) throw new Error("未找到匹配要求的预览画质视频"); return finalQualityMap; } async _searchViaGraphQL() { clog.debug("尝试通过 GraphQL API 搜索视频..."); const videoMap = await getDmmGraphQLVideoUrls(this.carNum); if (videoMap && Object.keys(videoMap).length > 0) { this._updateCache(videoMap); clog.debug("GraphQL 成功解析出预览视频:", videoMap); const dmmCacheKey = "jhs_other_site_dmm"; const dmmCacheData = localStorage.getItem(dmmCacheKey) ? JSON.parse(localStorage.getItem(dmmCacheKey)) : {}; dmmCacheData[this.carNum] = { type: "graphql", url: `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}` }; localStorage.setItem(dmmCacheKey, JSON.stringify(dmmCacheData)); return videoMap; } return null; } async fetchVideo() { const cachedResult = this._checkCache(); if (cachedResult) return cachedResult; let contentItems; try { const testCarNum = this.carNum.toLowerCase(); if (testCarNum.startsWith("heyzo") || /^(n\d+|\d+(-\d+)*)$/.test(testCarNum) || /^n\d+$/.test(testCarNum)) throw new Error("无码番号类型, 取消dmm解析"); if (this.carNum.includes("VR-")) throw new Error("VR类型, 取消dmm解析"); contentItems = await this._searchContentIds(); } catch (e) { clog.error("DMM API 搜索失败:", e); } if (!contentItems || 0 === contentItems.length) { clog.warn("Affiliate API 未找到视频,尝试 GraphQL API..."); const graphQLResult = await this._searchViaGraphQL(); if (graphQLResult) return graphQLResult; const $btn = $("#fanzaBtn"); $btn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`); $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", "#de3333"); return null; } try { const finalVideoMap = await Promise.any(contentItems.map(((item) => this._extractTrailerLinks(item)))); this._updateCache(finalVideoMap); return finalVideoMap; } catch (error) { const errors = error.errors || [error]; clog.error(`Affiliate 解析失败: ${errors[0] && errors[0].message || errors[0]}`, errors); clog.warn("尝试 GraphQL API 作为 fallback..."); const graphQLResult = await this._searchViaGraphQL(); if (graphQLResult) return graphQLResult; if (errors.some(((err) => err.message.includes("节点不可用")))) this.showErrorMessages && show.error("节点不可用,请将DMM域名分流到日本ip"); else { const displayError = errors[0].message || errors[0]; this.showErrorMessages && show.error(`解析失败: ${displayError}`); } const $btn = $("#fanzaBtn"); $btn.attr("href", `https://www.dmm.co.jp/search/=/searchstr=${this.carNum}`); $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", "#de3333"); return null; } } } const getDmmVideo = async (carNum2, showErrorMessages = true) => new DmmVideoFetcher(carNum2, showErrorMessages).fetchVideo(); class AutoPagePlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "preloadDistance", 500); __publicField2(this, "currentPage", this.getInitialPageNumber()); __publicField2(this, "pageItems", []); } getName() { return "AutoPagePlugin"; } async initCss() { return "\n \n "; } async handle() { this.waterfall().then(); } getInitialPageNumber() { if (isJavBus$1) { const match = currentHref$1.match(/\/(page|star\/[^/]+)\/(\d+)/); return match ? parseInt(match[2], 10) : 1; } if (isJavDb$1) { const match = currentHref$1.match(/[?&]page=(\d+)/); return match ? parseInt(match[1], 10) : 1; } return 1; } async waterfall() { if (await this.shouldDisablePaging()) return; const selector = this.getSelector(); this.container = document.querySelector(selector.boxSelector); if (!this.container) { console.error("没有找到容器节点,停止瀑布流!"); return; } this.loader = document.createElement("div"); this.loader.className = "jhs-scroll"; this.container.parentNode.insertBefore(this.loader, this.container.nextSibling); this.pageItems.push({ page: this.currentPage, top: 0, url: window.location.href }); this.loader.addEventListener("click", (() => { this.loader.classList.contains("waterfall-error") && this.loadNextPage().then(); })); window.addEventListener("scroll", (() => { this.checkLoad(); this.checkScrollPosition(); })); const nextLink = document.querySelector(selector.nextPageSelector); this.nextUrl = null == nextLink ? void 0 : nextLink.href; this.hasMore = !!this.nextUrl; this.hasMore || this.setState("waterfall-no-more", "已经到底了"); } async loadNextPage() { var _a2; if (await storageManager.getSetting("autoPage", YES$1) === NO$1) { this.setState("waterfall-loading", ""); return; } if (this.isLoading || !this.nextUrl) return; this.isLoading = true; this.setState("waterfall-loading", "加载中..."); const selector = this.getSelector(); try { const pageNum = utils.getUrlParam(this.nextUrl, "page"); let maxPage = 60; currentHref$1.includes("c11") && (maxPage = 30); if (isJavDb$1 && pageNum > maxPage || currentHref$1.includes("month")) { const beyond60Plugin = this.getBean("Beyond60Plugin"); if (beyond60Plugin) { const {html: html22, nextUrl, hasMore} = await beyond60Plugin.handleBeyond60(this.nextUrl); if (html22) { const pageTop2 = this.container.scrollHeight; this.pageItems.push({ page: this.currentPage + 1, top: pageTop2, url: this.nextUrl }); $(".movie-list").append(html22); } this.hasMore = hasMore; this.nextUrl = nextUrl; const $ul = beyond60Plugin.createPagination(pageNum, hasMore); $(".pagination").html($ul); this.setState("waterfall-loading", ""); this.hasMore || this.setState("waterfall-no-more", "已经到底了"); return; } } const html2 = await gmHttp.get(this.nextUrl); clog.log("请求下一页内容:", this.nextUrl); const $dom = utils.htmlTo$dom(html2); isJavBus$1 && $dom.find(".avatar-box").length > 0 && $dom.find(".avatar-box").parent().remove(); let itemList = $dom.find(this.getSelector().requestDomItemSelector); const currentBoxCarInfoList = this.getBoxCarInfoList(), nextPageBoxCarInfoList = this.getBoxCarInfoList(itemList); if (this.checkDuplicateCarNumbers(currentBoxCarInfoList, nextPageBoxCarInfoList)) { this.nextUrl = null; this.hasMore = false; this.setState("waterfall-error", "翻页内容出现重复数据, 可能首页已更新了新视频 或 页码受JavDB限制, 停止瀑布流"); return; } const pageTop = this.container.scrollHeight; this.pageItems.push({ page: this.currentPage + 1, top: pageTop, url: this.nextUrl }); const listPagePlugin = this.getBean("ListPagePlugin"); let coverImgNodeList = $dom.find(this.getSelector().coverImgSelector); listPagePlugin.replaceHdImg(coverImgNodeList); $(this.getSelector().boxSelector).append(itemList); this.nextUrl = null == (_a2 = $dom.find(selector.nextPageSelector)) ? void 0 : _a2.attr("href"); this.hasMore = !!this.nextUrl; let pagination = $dom.find(".pagination"); $(".pagination").replaceWith(pagination); this.setState("waterfall-loading", ""); this.hasMore || this.setState("waterfall-no-more", "已经到底了"); } catch (e) { clog.error("加载失败:", e); this.setState("waterfall-error", "加载失败,点击重试"); } finally { setTimeout((() => { this.isLoading = false; }), 600); } } checkScrollPosition() { const scrollPosition = window.scrollY; for (let i = this.pageItems.length - 1; i >= 0; i--) { const page = this.pageItems[i]; if (scrollPosition >= page.top) { if (this.currentPage !== page.page) { this.currentPage = page.page; this.updatePageUrl(page.url); } break; } } } checkLoad() { if (!this.loader || this.isLoading || !this.hasMore) return; if (window.scrollY < 100) return; const triggerDistance = 200; this.loader.getBoundingClientRect().top < window.innerHeight + triggerDistance && this.loadNextPage().then(); } async shouldDisablePaging() { if (!window.isListPage) return true; await storageManager.getSetting("autoPage", YES$1); return ["search?q", "/rankings/playback", "/rankings/top", "/want_watch_videos", "/watched_videos", "/advanced_search?type=100"].some(((path) => currentHref$1.includes(path))); } updatePageUrl_old(href) { window.history.pushState({}, "", href); if (isJavBus$1) { const match = href.match(/\/(page|star\/.*?)\/(\d+)/), pageNumber = match ? parseInt(match[2], 10) : null; document.title = document.title.replace(/第\d+頁/, "第" + pageNumber + "頁"); } } updatePageUrl(url) { window.history.replaceState({}, "", url); isJavBus$1 && (document.title = document.title.replace(/第\d+頁/, `第${this.currentPage}頁`)); } setState(state, text) { this.loader.className = `jhs-scroll ${state}`; this.loader.textContent = text; } } class AliyunApi { constructor(refresh_token) { this.baseApiUrl = "https://api.aliyundrive.com"; this.refresh_token = refresh_token; this.authorization = null; this.default_drive_id = null; this.backupFolderId = null; } async getDefaultDriveId() { if (this.default_drive_id) return this.default_drive_id; this.userInfo = await this.getUserInfo(); this.default_drive_id = this.userInfo.default_drive_id; return this.default_drive_id; } async getHeaders() { if (this.authorization) return { authorization: this.authorization }; this.authorization = await this.getAuthorization(); return { authorization: this.authorization }; } async getAuthorization() { let url = this.baseApiUrl + "/v2/account/token", data = { refresh_token: this.refresh_token, grant_type: "refresh_token" }; try { return "Bearer " + (await gmHttp.post(url, data)).access_token; } catch (e) { throw e.message.includes("is not valid") ? new Error("refresh_token无效, 请重新填写并保存") : e; } } async getUserInfo() { const headers = await this.getHeaders(); let url = this.baseApiUrl + "/v2/user/get"; return await gmHttp.post(url, {}, headers); } async deleteFile(file_id, drive_id = null) { if (!file_id) throw new Error("未传入file_id"); drive_id || (drive_id = await this.getDefaultDriveId()); let data = { file_id, drive_id }, url = this.baseApiUrl + "/v2/recyclebin/trash"; const headers = await this.getHeaders(); await gmHttp.post(url, data, headers); return {}; } async createFolder(name2, drive_id = null, parent_folder_id = "root") { drive_id || (drive_id = await this.getDefaultDriveId()); let url = this.baseApiUrl + "/adrive/v2/file/createWithFolders", data = { name: name2, type: "folder", parent_file_id: parent_folder_id, check_name_mode: "auto_rename", content_hash_name: "sha1", drive_id }; const headers = await this.getHeaders(); return await gmHttp.post(url, data, headers); } async getFileList(parent_folder_id = "root", drive_id = null) { drive_id || (drive_id = await this.getDefaultDriveId()); let url = this.baseApiUrl + "/adrive/v3/file/list"; const data = { drive_id, parent_file_id: parent_folder_id, limit: 200, all: false, url_expire_sec: 14400, image_thumbnail_process: "image/resize,w_256/format,avif", image_url_process: "image/resize,w_1920/format,avif", video_thumbnail_process: "video/snapshot,t_120000,f_jpg,m_lfit,w_256,ar_auto,m_fast", fields: "*", order_by: "updated_at", order_direction: "DESC" }, headers = await this.getHeaders(); return (await gmHttp.post(url, data, headers)).items; } async uploadFile(folder_id, fileName, uploadContent, drive_id = null) { show.info("请求存储空间中..."); let createFileUrl = this.baseApiUrl + "/adrive/v2/file/createWithFolders"; drive_id || (drive_id = await this.getDefaultDriveId()); let data = { drive_id, part_info_list: [{ part_number: 1 }], parent_file_id: folder_id, name: fileName, type: "file", check_name_mode: "auto_rename" }; const headers = await this.getHeaders(), createFileResult = await gmHttp.post(createFileUrl, data, headers), upload_id = createFileResult.upload_id, upload_file_id = createFileResult.file_id, upload_url = createFileResult.part_info_list[0].upload_url; show.info("开始上传文件..."); await this._doUpload(upload_url, uploadContent); await gmHttp.post("https://api.aliyundrive.com/v2/file/complete", data = { drive_id, file_id: upload_file_id, upload_id }, headers); } _doUpload(upload_url, uploadContent) { return new Promise(((resolve, reject) => { $.ajax({ type: "PUT", url: upload_url, data: uploadContent, contentType: " ", processData: false, success: (res, status, xhr) => { 200 === xhr.status ? resolve({}) : reject(xhr); }, error: (xhr) => { clog.error("上传失败", xhr.responseText); reject(xhr); } }); })); } async getDownloadUrl(file_id, drive_id = null) { drive_id || (drive_id = await this.getDefaultDriveId()); let url = this.baseApiUrl + "/v2/file/get_download_url"; const headers = await this.getHeaders(); let data = { file_id, drive_id }; return (await gmHttp.post(url, data, headers)).url; } async _createBackupFolder(folderName) { const fileList = await this.getFileList(); let folderObj = null; for (let i = 0; i < fileList.length; i++) { let file = fileList[i]; if (file.name === folderName) { folderObj = file; break; } } if (!folderObj) { show.info("不存在备份目录, 进行创建"); folderObj = await this.createFolder(folderName); } this.backupFolderId = folderObj.file_id; } async backup(folderName, fileName, uploadContent) { if (this.backupFolderId) await this.uploadFile(this.backupFolderId, fileName, uploadContent); else { await this._createBackupFolder(folderName); await this.uploadFile(this.backupFolderId, fileName, uploadContent); } } async getBackupList(folderName) { let dataList; if (this.backupFolderId) dataList = await this.getFileList(this.backupFolderId); else { await this._createBackupFolder(folderName); dataList = await this.getFileList(this.backupFolderId); } const fileList = []; dataList.forEach(((data) => { fileList.push({ name: data.name, fileId: data.file_id, createTime: data.created_at, size: data.size }); })); return fileList; } } class WebDavApi { constructor(davUrl, username, password) { this.davUrl = davUrl.endsWith("/") ? davUrl : davUrl + "/"; this.username = username; this.password = password; this.folderName = null; } _getAuthHeaders() { return { Authorization: `Basic ${btoa(`${this.username}:${this.password}`)}`, Depth: "1" }; } _sendRequest(method, path, headers = {}, data) { return new Promise(((resolve, reject) => { const url = this.davUrl + path, allHeaders = { ...this._getAuthHeaders(), ...headers }; _GM_xmlhttpRequest({ method, url, headers: allHeaders, data, onload: (response) => { if (response.status >= 200 && response.status < 300) resolve(response); else { console.error(response); reject(new Error(`请求失败 ${response.status}: ${response.statusText}`)); } }, onerror: (response) => { console.error("请求WebDav发生错误:", response); reject(new Error("请求WebDav失败, 请检查服务是否启动, 凭证是否正确")); } }); })); } async backup(folderName, fileName, uploadContent) { await this._sendRequest("MKCOL", folderName); const path = folderName + "/" + fileName; await this._sendRequest("PUT", path, { "Content-Type": "text/plain" }, uploadContent); } async getFileList(folderName) { var _a2, _b, _c; const xmlResponse = (await this._sendRequest("PROPFIND", folderName, { "Content-Type": "application/xml" }, '\n \n \n \n \n \n \n \n \n \n ')).responseText, items = new DOMParser().parseFromString(xmlResponse, "text/xml").getElementsByTagNameNS("DAV:", "response"), fileList = []; for (let i = 0; i < items.length; i++) { if (0 === i) continue; let item = items[i]; console.log(item); const name2 = item.getElementsByTagNameNS("DAV:", "displayname")[0].textContent, size = (null == (_a2 = item.getElementsByTagNameNS("DAV:", "getcontentlength")[0]) ? void 0 : _a2.textContent) || "0", createTime = (null == (_b = item.getElementsByTagNameNS("DAV:", "creationdate")[0]) ? void 0 : _b.textContent) || (null == (_c = item.getElementsByTagNameNS("DAV:", "getlastmodified")[0]) ? void 0 : _c.textContent) || ""; "0" !== size && fileList.push({ fileId: name2, name: name2, size: Number(size), createTime }); } fileList.reverse(); return fileList; } async deleteFile(fileId) { let path = this.folderName + "/" + encodeURI(fileId); await this._sendRequest("DELETE", path, { "Cache-Control": "no-cache" }); } async getBackupList(folderName) { this.folderName = folderName; await this._sendRequest("MKCOL", folderName); return this.getFileList(folderName); } async getFileContent(filePath) { let path = this.folderName + "/" + filePath; return (await this._sendRequest("GET", path, { Accept: "application/octet-stream" })).responseText; } } const SALT = "x7k9p3"; function simpleEncrypt(str) { return (SALT + str + SALT).split("").map(((char) => { const code = char.codePointAt(0); return String.fromCodePoint(code + 5); })).join(""); } class SettingPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "folderName", "JHS-数据备份"); __publicField2(this, "cacheItems", [{ key: "jhs_dmm_video", text: "🎥 预览视频缓存", title: "预览视频缓存" }, { key: "jhs_other_site", text: "🌍 第三方站点缓存", title: "第三方站点资源检测结果, 如missav,123Av等" }, { key: "jhs_screenShot", text: "🖼️ 缩略图缓存", title: "缩略图缓存" }, { key: "jhs_translate", text: "🆎 标题翻译", title: "标题翻译" }, { key: "jhs_actress_info", text: "👩 演员信息", title: "演员的年龄三围等数据信息" }, { key: "jhs_score_info", text: "⭐ Top250|热播 评分数据", title: "Top250及热播的评分数据" }]); } getName() { return "SettingPlugin"; } async initCss() { const settingObj = await storageManager.getSetting(); let containerWidth = (null == settingObj ? void 0 : settingObj.containerWidth) ?? "100", containerColumns = utils.isMobile() && window.innerWidth < 1e3 ? 1 : (null == settingObj ? void 0 : settingObj.containerColumns) ?? 5; this.applyImageMode().then(); let containerWidthCss = ` section .container{ max-width: 1000px !important; min-width: ${containerWidth}%; } .movie-list, .movie-list.v{ grid-template-columns: repeat(${containerColumns}, minmax(0, 1fr)); } `; isJavBus$1 && (containerWidthCss = ` .container-fluid .row{ max-width: 1000px !important; min-width: ${containerWidth}%; margin: auto auto; } .container { max-width: 1000px !important; min-width: 80%; margin: auto auto; } .masonry { grid-template-columns: repeat(${containerColumns}, minmax(0, 1fr)); } `); return ` `; } async handle() { await storageManager.getSetting("enableClog", YES$1) === YES$1 && clog.show(); if (isJavDb$1) { let handleResize2 = function () { if ($(".navbar-search").is(":hidden")) { $(".mini-setting-box").hide(); $(".setting-box").show(); } else { $(".mini-setting-box").show(); $(".setting-box").hide(); } }; $("#navbar-menu-user .navbar-end").prepend(''); utils.loopDetector((() => $("#miniHistoryBtn").length > 0), (() => { $(".miniHistoryBtnBox").before('\n \n '); handleResize2(); })); $(window).resize(handleResize2); } if (isJavBus$1) { utils.loopDetector((() => $("#waitCheckBtn").length), (() => { $("#waitCheckBtn").parent().append('\n
\n
\n \n 设置\n \n
\n
\n
\n '); }), 1, 1e4, false); window.isDetailPage && $("h3").before('\n
\n
\n
\n \n 设置\n \n
\n
\n
\n
\n '); } $(".main-nav, .container-fluid").on("click", "#setting-btn, #mini-setting-btn", (() => { clog.lowZIndex(); this.openSettingDialog(); })); $(".main-nav, .container-fluid").on("mouseenter", ".setting-box", (async () => { $(".simple-setting").html(await this.simpleSetting()).show(); this.initSimpleSettingForm().then(); clog.lowZIndex(); })).on("mouseleave", ".setting-box", (() => { $(".simple-setting").html("").hide(); })); $(".main-nav, .container-fluid").on("mouseenter", ".mini-setting-box", (async () => { $(".mini-simple-setting").html(await this.simpleSetting()).show(); this.initSimpleSettingForm().then(); clog.lowZIndex(); })).on("mouseleave", ".mini-setting-box", (() => { $(".mini-simple-setting").html("").hide(); })); } async openSettingDialog(defaultActivePanel = "backup-panel", fun) { const buttonsHTML = this.cacheItems.map(((item) => `
${item.text}
`)).join(""); let videoQualityHtml = ""; qualityOptions.forEach(((option) => { option.canSelect && (videoQualityHtml += ``); })); const coverButtonPlugin = this.getBean("CoverButtonPlugin"); let settingHtml = `
💾 数据备份
⚙️ 基础配置
🚫 屏蔽配置
📋 定时任务
📂 离线配置
🌐 外部网站
🗑️ 标记删除
⌨️ 快捷键配置
🧹 清理缓存
💵 打赏作者
阿里云盘备份
refresh_token:

WebDav备份
服务地址:
用户名:
密码:
已鉴定标签展示位置:
已鉴定内容处理方式:
鉴定补录演员信息 ❓

列表页功能按钮
按钮-打开待鉴定:
按钮-打开已收藏:
打开待鉴定|已收藏 窗口数:
随机打开已收藏:

封面快捷按钮
${coverButtonPlugin.screenSvg}长缩略图:
${coverButtonPlugin.videoSvg}预览视频:
${coverButtonPlugin.handleSvg}鉴定按钮:
${coverButtonPlugin.siteSvg}第三方跳转:
${coverButtonPlugin.copySvg}复制按钮:

预览视频默认画质:
评论区条数:
高亮已收藏演员 ❓
分类标签|高亮演员-边框样式:

请求超时时间(毫秒):
请求失败重试次数:

启用控制台日志:
日志最大行数:
请求并发数量:
请求间隔时间(毫秒):

自动检测屏蔽黑名单演员
任务开关: ❓
任务间隔时间:
检测规则:
自动同步已收藏的演员
任务开关: ❓
任务间隔时间:
自动检测已收藏演员的最新作品
任务开关: ❓
任务间隔时间:
检测规则:
115离线文件夹命名
启用自定义格式:
命名格式:
可用变量(\${} 形式):
code - 完整番号(如 ABC-123)
title - 作品标题
maker - 片商
actor - 演员(多演员只取第一个)
date - 当前日期(YYYY-MM-DD)
例: \${actor}/\${code} = 演员目录下的番号子目录
支持 / 分隔的多级目录,离线文件下载到最末级子目录
域名 - MissAv:
域名 - Jable:
域名 - JavTrailer:
域名 - 123Av:
域名 - JavDb:
域名 - JavBus:
域名 - SupJav:
标记删除列表
加载中...

⚠ 作品删除目前暂无法删除115下载记录,如果需要重新下载,请去115云下载记录里删除或清空记录

快捷键修改后, 刷新页面生效

🚫 屏蔽:
⭐ 收藏:
📥️ 已下载:
🔍 已观看:
❓ 对视频列表页启用快捷键:
⏩ 快进:
▲ 折叠:
💻 控制台:
显示已鉴定内容
屏蔽单番号:
屏蔽演员:
屏蔽关键词:
收藏:
已下载:
已观看:
显示所有:
启用划词屏蔽 ❓

评论区屏蔽词:

视频标题屏蔽词:

以下操作, 不会对核心数据造成影响


${buttonsHTML}

如果JHS-enhance给您带来了便捷和价值,请考虑给予一点支持,您的鼓励是我持续创作的最大动力!感谢您的慷慨支持!

SOL二维码

SOL

点击地址可复制

`; layer.open({ type: 1, title: "设置", content: settingHtml, area: utils.getResponsiveArea(["55%", "90%"]), scrollbar: false, success: (layero, index) => { $(layero).find(".layui-layer-content").css("position", "relative"); this.loadForm(); this.bindClick(); utils.setupEscClose(index); fun && fun(); }, end: () => { this.getBean("CoverButtonPlugin").enableSvgBtn(); } }); } async simpleSetting() { let settingObj = await storageManager.getSetting(); const showFilterItemHotKey = settingObj.showFilterItemHotKey, showFilterActorItemHotKey = settingObj.showFilterActorItemHotKey, showFilterKeywordItemHotKey = settingObj.showFilterKeywordItemHotKey, showFavoriteItemHotKey = settingObj.showFavoriteItemHotKey, showHasDownItemHotKey = settingObj.showHasDownItemHotKey, showHasWatchItemHotKey = settingObj.showHasWatchItemHotKey, showAllItemHotKey = settingObj.showAllItemHotKey; return `
显示已鉴定内容:
屏蔽单番号${showFilterItemHotKey ? `(${showFilterItemHotKey})` : ""}:
屏蔽演员${showFilterActorItemHotKey ? `(${showFilterActorItemHotKey})` : ""}:
屏蔽关键词${showFilterKeywordItemHotKey ? `(${showFilterKeywordItemHotKey})` : ""}:
收藏${showFavoriteItemHotKey ? `(${showFavoriteItemHotKey})` : ""}:
已下载${showHasDownItemHotKey ? `(${showHasDownItemHotKey})` : ""}:
已观看${showHasWatchItemHotKey ? `(${showHasWatchItemHotKey})` : ""}:
❓ 显示所有${showAllItemHotKey ? `(${showAllItemHotKey})` : ""}:

❓ 弹窗方式打开页面:
鉴定后立即关闭页面:

❓ 瀑布流模式:
启用标题翻译:
启用悬浮大图:
❓ 不规则重命名:(刷新详情页生效,有延迟)
启用115视频匹配:

${isJavDb$1 ? '\n
\n \n ❓ 加载女优信息:\n \n
\n \n
\n
' : ""}
❓ 加载第三方视频资源:
❓ 加载长缩略图:
❓ 更高画质预览视频:

❓ 竖图模式:
页面列数:
页面宽度:
`; } async loadForm() { let settingObj = await storageManager.getSetting(); $("#videoQuality").val(settingObj.videoQuality); $("#reviewCount").val(settingObj.reviewCount || 20); $("#tagPosition").val(settingObj.tagPosition || "rightTop"); $("#movieShowType").val(settingObj.movieShowType || "hide"); $("#waitCheckCount").val(settingObj.waitCheckCount || 5); $("#showWaitCheckBtn").prop("checked", !settingObj.showWaitCheckBtn || settingObj.showWaitCheckBtn === YES$1); $("#showWaitDownBtn").prop("checked", !settingObj.showWaitDownBtn || settingObj.showWaitDownBtn === YES$1); $("#randomOpenWaitDown").prop("checked", !!settingObj.randomOpenWaitDown && settingObj.randomOpenWaitDown === YES$1); $("#checkConcurrencyCount").val(settingObj.checkConcurrencyCount || 2); $("#checkRequestSleep").val(settingObj.checkRequestSleep || 100); $("#enableCheckBlacklist").val(settingObj.enableCheckBlacklist || YES$1); $("#checkBlacklist_intervalTime").val(settingObj.checkBlacklist_intervalTime || 12); $("#checkBlacklist_ruleTime").val(settingObj.checkBlacklist_ruleTime || 8760); $("#enableCheckFavoriteActress").val(settingObj.enableCheckFavoriteActress || YES$1); $("#checkFavoriteActress_IntervalTime").val(settingObj.checkFavoriteActress_IntervalTime || 24); $("#enableCheckNewVideo").val(settingObj.enableCheckNewVideo || YES$1); $("#checkNewVideo_intervalTime").val(settingObj.checkNewVideo_intervalTime || 12); $("#checkNewVideo_ruleTime").val(settingObj.checkNewVideo_ruleTime || 8760); const highlightedTagNumber = settingObj.highlightedTagNumber || 1, highlightedTagColor = settingObj.highlightedTagColor || "#ce2222"; $("#highlightedTagNumber").val(settingObj.highlightedTagNumber || 1); $("#highlightedTagColor").val(settingObj.highlightedTagColor || "#ce2222"); $("#highlightedTagLabel").css("border", `${highlightedTagNumber}px solid ${highlightedTagColor}`); $("#enableClog").val(settingObj.enableClog || YES$1); $("#clogMsgCount").val(settingObj.clogMsgCount || 2e3); $("#refresh_token").val(settingObj.refresh_token || ""); $("#httpTimeout").val(settingObj.httpTimeout || 5e3); $("#httpRetryCount").val(settingObj.httpRetryCount || 3); $("#webDavUrl").val(settingObj.webDavUrl || ""); $("#webDavUsername").val(settingObj.webDavUsername || ""); $("#webDavPassword").val(settingObj.webDavPassword || ""); $("#enableTitleSelectFilter").prop("checked", !settingObj.enableTitleSelectFilter || settingObj.enableTitleSelectFilter === YES$1); $("#enableFavoriteActresses").prop("checked", !settingObj.enableFavoriteActresses || settingObj.enableFavoriteActresses === YES$1); $("#enableSaveActressCarInfo").prop("checked", !!settingObj.enableSaveActressCarInfo && settingObj.enableSaveActressCarInfo === YES$1); $("#enableScreenSvg").prop("checked", !settingObj.enableScreenSvg || settingObj.enableScreenSvg === YES$1); $("#enableVideoSvg").prop("checked", !settingObj.enableVideoSvg || settingObj.enableVideoSvg === YES$1); $("#enableHandleSvg").prop("checked", !settingObj.enableHandleSvg || settingObj.enableHandleSvg === YES$1); $("#enableSiteSvg").prop("checked", !settingObj.enableSiteSvg || settingObj.enableSiteSvg === YES$1); $("#enableCopySvg").prop("checked", !settingObj.enableCopySvg || settingObj.enableCopySvg === YES$1); const otherSitePlugin = this.getBean("OtherSitePlugin"), missAvUrl = await otherSitePlugin.getMissAvUrl(), jableUrl = await otherSitePlugin.getjableUrl(), javTrailersUrl = await otherSitePlugin.getJavTrailersUrl(), av123Url = await otherSitePlugin.getAv123Url(), javDbUrl = await otherSitePlugin.getJavDbUrl(), javBusUrl = await otherSitePlugin.getJavBusUrl(), supJavUrl = await otherSitePlugin.getSupJavUrl(); $("#missAvUrl").val(missAvUrl); $("#jableUrl").val(jableUrl); $("#javTrailersUrl").val(javTrailersUrl); $("#av123Url").val(av123Url); $("#javDbUrl").val(javDbUrl); $("#javBusUrl").val(javBusUrl); $("#supJavUrl").val(supJavUrl); let reviewKeywordList = await storageManager.getReviewFilterKeywordList(), filterKeywordList = await storageManager.getTitleFilterKeyword(); reviewKeywordList && reviewKeywordList.forEach(((reviewKeyword) => { this.addLabelTag("#reviewKeywordContainer", reviewKeyword); })); filterKeywordList && filterKeywordList.forEach(((reviewKeyword) => { this.addLabelTag("#filterKeywordContainer", reviewKeyword); })); ["#reviewKeywordContainer", "#filterKeywordContainer"].forEach(((containerId) => { $(`${containerId} .add-tag-btn`).on("click", ((event) => this.addKeyword(event, containerId))); $(`${containerId} .keyword-input`).on("keypress", ((event) => { "Enter" === event.key && this.addKeyword(event, containerId); })); })); $("#hotkey-panel [id]").map(((i, el) => el.id)).get().forEach(((containerId) => { const $element = $(`#${containerId}`), defaultValue = void 0 !== settingObj[containerId] ? settingObj[containerId] : $element.attr("data-default-hotkey") || ""; $element.val(defaultValue).on("input", ((event) => { let value = $(event.target).val(); if (/[\u4e00-\u9fa5]/.test(value) || /^Shift[a-zA-Z0-9]+$/.test(value)) { $(event.target).val(""); show.error("非法输入:不能输入中文或输入法转换错误"); } })).on("keydown", ((event) => this.handleHotkeyInput(event, $element))); })); $("#enableImageHotKey").prop("checked", !!settingObj.enableImageHotKey && settingObj.enableImageHotKey === YES$1); $("#enable115OfflineFormat").prop("checked", !!settingObj.enable115OfflineFormat && settingObj.enable115OfflineFormat === YES$1); $("#offlineFolderFormat").val(settingObj.offlineFolderFormat || ""); } handleHotkeyInput(event, $input) { event.preventDefault(); const hotkey = this.parseHotkey(event); "" !== hotkey ? this.isDuplicateHotkey(hotkey, $input.attr("id")) ? show.error("该快捷键已被其他功能使用!") : $input.val(hotkey) : $input.val(""); } parseHotkey(event) { if ("Backspace" === event.key || "Process" === event.key) return ""; const keys = []; event.ctrlKey && keys.push("Ctrl"); event.shiftKey && keys.push("Shift"); event.altKey && keys.push("Alt"); event.metaKey && keys.push("Cmd"); const key = { " ": "Space", Control: "Ctrl", Meta: "Cmd", ArrowUp: "Up", ArrowDown: "Down", ArrowLeft: "Left", ArrowRight: "Right" }[event.key] || (event.key.length > 1 ? event.key.replace("Arrow", "") : event.key); ["Control", "Shift", "Alt", "Meta"].includes(event.key) || keys.push(key); return keys.length > 0 ? keys.join("+") : ""; } isDuplicateHotkey(hotkey, currentInputId) { let isDuplicate = false; $("#hotkey-panel [id]").each(((i, el) => { if (el.id !== currentInputId && hotkey && hotkey === $(el).val()) { isDuplicate = true; return false; } })); return isDuplicate; } async initSimpleSettingForm() { let settingObj = await storageManager.getSetting(); $("#containerColumns").val(settingObj.containerColumns || 5); $("#showContainerColumns").text(settingObj.containerColumns || 5); $("#containerWidth").val((settingObj.containerWidth || 100) - 70); $("#showContainerWidth").text((settingObj.containerWidth || 100) + "%"); $("#dialogOpenDetail").prop("checked", !settingObj.dialogOpenDetail || settingObj.dialogOpenDetail === YES$1); $("#needClosePage").prop("checked", !settingObj.needClosePage || settingObj.needClosePage === YES$1); $("#autoPage").prop("checked", !settingObj.autoPage || settingObj.autoPage === YES$1); $("#translateTitle").prop("checked", !settingObj.translateTitle || settingObj.translateTitle === YES$1); $("#enableLoadActressInfo").prop("checked", !settingObj.enableLoadActressInfo || settingObj.enableLoadActressInfo === YES$1); $("#enableLoadOtherSite").prop("checked", !settingObj.enableLoadOtherSite || settingObj.enableLoadOtherSite === YES$1); $("#containerColumns").on("input", (async (event) => { let columns = $("#containerColumns").val(); $("#showContainerColumns").text(columns); if (isJavDb$1) { document.querySelector(".movie-list").style.gridTemplateColumns = `repeat(${columns}, minmax(0, 1fr))`; } if (isJavBus$1) { document.querySelector(".masonry").style.gridTemplateColumns = `repeat(${columns}, minmax(0, 1fr))`; } await storageManager.saveSettingItem("containerColumns", columns); this.applyImageMode(); })); $("#containerWidth").on("input", (async (event) => { let containerWidth = parseInt($(event.target).val()); const value = containerWidth + 70 + "%"; $("#showContainerWidth").text(value); if (isJavDb$1) { document.querySelector("section .container").style.minWidth = value; } if (isJavBus$1) { document.querySelector(".container-fluid .row").style.minWidth = value; } storageManager.saveSettingItem("containerWidth", containerWidth + 70); })); $("#dialogOpenDetail").on("change", ((event) => { let dialogOpenDetail = $("#dialogOpenDetail").is(":checked") ? YES$1 : NO$1; storageManager.saveSettingItem("dialogOpenDetail", dialogOpenDetail); })); $("#showFilterItem").prop("checked", !!settingObj.showFilterItem && settingObj.showFilterItem === YES$1); $("#showFilterActorItem").prop("checked", !!settingObj.showFilterActorItem && settingObj.showFilterActorItem === YES$1); $("#showFilterKeywordItem").prop("checked", !!settingObj.showFilterKeywordItem && settingObj.showFilterKeywordItem === YES$1); $("#showFavoriteItem").prop("checked", !settingObj.showFavoriteItem || settingObj.showFavoriteItem === YES$1); $("#showHasDownItem").prop("checked", !settingObj.showHasDownItem || settingObj.showHasDownItem === YES$1); $("#showHasWatchItem").prop("checked", !settingObj.showHasWatchItem || settingObj.showHasWatchItem === YES$1); $("#showFilterItem").on("change", (async (event) => { let showFilterItem = $("#showFilterItem").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("showFilterItem", showFilterItem); window.refresh(); })); $("#showFilterActorItem").on("change", (async (event) => { let showFilterActorItem = $("#showFilterActorItem").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("showFilterActorItem", showFilterActorItem); window.refresh(); })); $("#showFilterKeywordItem").on("change", (async (event) => { let showFilterKeywordItem = $("#showFilterKeywordItem").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("showFilterKeywordItem", showFilterKeywordItem); window.refresh(); })); $("#showFavoriteItem").on("change", (async (event) => { let showFavoriteItem = $("#showFavoriteItem").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("showFavoriteItem", showFavoriteItem); window.refresh(); })); $("#showHasDownItem").on("change", (async (event) => { let showHasDownItem = $("#showHasDownItem").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("showHasDownItem", showHasDownItem); window.refresh(); })); $("#showHasWatchItem").on("change", (async (event) => { let showHasWatchItem = $("#showHasWatchItem").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("showHasWatchItem", showHasWatchItem); window.refresh(); })); const $otherCheckboxes = $("#showFilterItem, #showFilterActorItem, #showFilterKeywordItem, #showFavoriteItem, #showHasDownItem, #showHasWatchItem"), updateOtherCheckboxesState = () => { const isShowAllChecked = $("#showAllItem").is(":checked"); $otherCheckboxes.prop("disabled", isShowAllChecked); isShowAllChecked ? $otherCheckboxes.attr("data-tip", "请先关闭显示所有才可点击") : $otherCheckboxes.removeAttr("data-tip"); }; $("#showAllItem").prop("checked", !!settingObj.showAllItem && settingObj.showAllItem === YES$1); $("#showAllItem").on("change", (async (event) => { let showAllItem = $("#showAllItem").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("showAllItem", showAllItem); updateOtherCheckboxesState(); window.refresh(); })); updateOtherCheckboxesState(); $("#needClosePage").on("change", (async (event) => { await storageManager.saveSettingItem("needClosePage", $("#needClosePage").is(":checked") ? YES$1 : NO$1); window.refresh(); })); $("#autoPage").on("change", (async (event) => { const autoPage = $("#autoPage").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("autoPage", autoPage); autoPage === YES$1 ? ($("#sort-toggle-btn").hide(), $("#one-time-sort-btn").show()) : ($("#sort-toggle-btn").show(), $("#one-time-sort-btn").hide()); })); $("#translateTitle").on("change", (async (event) => { const translateTitle = $("#translateTitle").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("translateTitle", translateTitle); if (translateTitle === YES$1) { await this.getBean("ListPagePlugin").doFilter(); window.isDetailPage && await this.getBean("TranslatePlugin").translate(); } else { await this.getBean("ListPagePlugin").revertTranslation(); $(".translated-title").remove(); } })); $("#hoverBigImg").prop("checked", !!settingObj.hoverBigImg && settingObj.hoverBigImg === YES$1); $("#hoverBigImg").on("change", (async (event) => { const hoverBigImg = $("#hoverBigImg").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("hoverBigImg", hoverBigImg); hoverBigImg === YES$1 ? window.imageHoverPreviewObj = new ImageHoverPreview({ selector: this.getSelector().coverImgSelector }) : window.imageHoverPreviewObj && window.imageHoverPreviewObj.destroy(); })); $("#enableLoadActressInfo").on("change", (async (event) => { const enableLoadActressInfo = $("#enableLoadActressInfo").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("enableLoadActressInfo", enableLoadActressInfo); enableLoadActressInfo === YES$1 ? this.getBean("ActressInfoPlugin").loadActressInfo() : $(".actress-info").remove(); })); $("#enableLoadOtherSite").on("change", (async (event) => { const enableLoadOtherSite = $("#enableLoadOtherSite").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("enableLoadOtherSite", enableLoadOtherSite); enableLoadOtherSite === YES$1 ? this.getBean("OtherSitePlugin").loadOtherSite().then() : $("#otherSiteBox").remove(); })); $("#enableLoadScreenShot").prop("checked", !settingObj.enableLoadScreenShot || settingObj.enableLoadScreenShot === YES$1); $("#enableLoadScreenShot").on("change", (async (event) => { const enableLoadScreenShot = $("#enableLoadScreenShot").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("enableLoadScreenShot", enableLoadScreenShot); enableLoadScreenShot === YES$1 ? this.getBean("ScreenShotPlugin").loadScreenShot().then() : $(".screen-container").remove(); })); $("#enableLoadPreviewVideo").prop("checked", !settingObj.enableLoadPreviewVideo || settingObj.enableLoadPreviewVideo === YES$1); $("#enableLoadPreviewVideo").on("change", (async (event) => { const enableLoadPreviewVideo = $("#enableLoadPreviewVideo").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("enableLoadPreviewVideo", enableLoadPreviewVideo); })); $("#enable115AutoRename").prop("checked", !!settingObj.enable115AutoRename && settingObj.enable115AutoRename === YES$1); $("#autoRenameScope").val(settingObj.autoRenameScope || "vr_only"); $("#autoRenameScopeRow").toggle(!!settingObj.enable115AutoRename && settingObj.enable115AutoRename === YES$1); $("#enable115AutoRename").on("change", (async (event) => { const enable115AutoRename = $("#enable115AutoRename").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("enable115AutoRename", enable115AutoRename); $("#autoRenameScopeRow").toggle(enable115AutoRename === YES$1); })); $("#autoRenameScope").on("change", (async (event) => { await storageManager.saveSettingItem("autoRenameScope", $("#autoRenameScope").val()); })); $("#enable115Match").prop("checked", !!settingObj.enable115Match && settingObj.enable115Match === YES$1); $("#enable115Match").on("change", (async (event) => { const enable115Match = $("#enable115Match").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("enable115Match", enable115Match); let movieList = $(this.getSelector().itemSelector).toArray(); await this.getBean("WangPan115MatchPlugin").matchMovieList(movieList); })); $("#enableVerticalModel").prop("checked", !!settingObj.enableVerticalModel && settingObj.enableVerticalModel === YES$1); $("#enableVerticalModel").on("change", (async (event) => { const enableVerticalModel = $("#enableVerticalModel").is(":checked") ? YES$1 : NO$1; await storageManager.saveSettingItem("enableVerticalModel", enableVerticalModel); this.applyImageMode(); })); $("#moreBtn").on("click", (() => { $(".simple-setting").html("").hide(); this.openSettingDialog("base-panel"); })); $("#helpBtn").on("click", (() => { layer.open({ type: 1, title: "", shadeClose: true, scrollbar: false, content: `

使用说明

1. 无法查看预览视频,提示分流?

JavDB限制日本IP的访问,而预览视频来自DMM,需要日本IP才能访问。

这样会导致二者无法同时使用,需要对其一进行代理转发。

将 cc3001.dmm.co.jp 及 dmm.co 分流到日本ip。

Clash Verge分流规则设置 (如果你是别的代理软件,自行搜索如何分流)

2. 如何屏蔽某一系列的番号?

方法一:设置中-添加视频标题关键词,如: VENX-

方法二:进入详情页,选中标题文字,右键可加入

进入详情页,选中标题,进行右键
3. 屏蔽某演员,如何只屏蔽单体影片?

屏蔽演员前,先筛选分类,再点屏蔽

屏蔽演员前,先筛选分类,再点屏蔽
4. 如何多浏览器同时登录115网盘?

① 访问115登录页, 选择JHS-扫码面板, 并扫码登录

② 进入网盘后, 右下角悬浮按钮, 复制Cookie

③ 打开另一个浏览器(需装JHS脚本), 进入登录页面, 选择JHS-扫码面板, 输入Cookie并回车

`, area: utils.getResponsiveArea(["50%", "90%"]) }); })); } async applyImageMode() { $("#verticalImgStyle").remove(); if (await storageManager.getSetting("enableVerticalModel", NO$1) === YES$1) { let imgPosition = "100% 50% !important"; window.location.href.includes("/advanced_search?type=100") && (imgPosition = "50% 50% !important"); const verticalStyle = ` .cover { min-height: 350px !important; overflow: hidden !important; padding-top: 142% !important; } .cover img { object-fit: cover !important; object-position: ${imgPosition}; } /* bus的 */ .masonry .movie-box img { min-height: 500px !important; object-fit: cover !important; object-position: top right; } `; $("\n "; } async showNewVideoCount() { const totalNewVideoCount = (await storageManager.getFavoriteActressList()).reduce(((accumulator, actress) => { var _a2; return accumulator + ((null == (_a2 = actress.newVideoList) ? void 0 : _a2.length) ?? 0); }), 0); $("#newVideoCount").text(`${totalNewVideoCount}`); } async resetBtnTip() { const taskPlugin = this.getBean("TaskPlugin"), settingObj = await storageManager.getSetting(), lastCheckTimeStr = localStorage.getItem(taskPlugin.lastCheckFavoriteActressTimeKey) || "无", checkFavoriteActress_IntervalTime = settingObj.checkFavoriteActress_IntervalTime, lastCheckNewVideoTimeStr = localStorage.getItem(taskPlugin.lastCheckNewVideoTimeKey) || "无", checkNewVideo_intervalTime = settingObj.checkNewVideo_intervalTime; $("#checkFavoriteActress").attr("data-tip", `上次自动同步时间: ${lastCheckTimeStr}; 检测间隔时间: ${checkFavoriteActress_IntervalTime}小时`); $("#checkNewVideo").attr("data-tip", `上次检测时间: ${lastCheckNewVideoTimeStr}; 检测间隔时间: ${checkNewVideo_intervalTime}小时`); } async openDialog() { const taskPlugin = this.getBean("TaskPlugin"), settingObj = await storageManager.getSetting(), lastCheckTimeStr = localStorage.getItem(taskPlugin.lastCheckFavoriteActressTimeKey) || "无", checkFavoriteActress_IntervalTime = settingObj.checkFavoriteActress_IntervalTime, lastCheckNewVideoTimeStr = localStorage.getItem(taskPlugin.lastCheckNewVideoTimeKey) || "无", checkNewVideo_intervalTime = settingObj.checkNewVideo_intervalTime; let html2 = ` `; layer.open({ type: 1, title: '新作品检测 ❓', content: html2, scrollbar: false, area: utils.getResponsiveArea(["80%", "90%"]), anim: -1, success: async (layero, index) => { this.loadData(); this.bindClick(); utils.setupEscClose(index); } }); } bindClick() { const taskPlugin = this.getBean("TaskPlugin"); $("#reLoad").on("click", ((event) => { this.loadData(); $("#checkNewVideoMsg").text(""); })); $("#toSetting").on("click", ((event) => { this.getBean("SettingPlugin").openSettingDialog("task-panel", (() => { $("#setting-checkFavoriteActress").css({ border: "1px solid #f40" }); $("#setting-checkNewVideo").css({ border: "1px solid #f40" }); })); })); $("#checkFavoriteActress").on("click", ((event) => { utils.q({ clientX: event.clientX, clientY: event.clientY + 20 }, "是否手动同步演员?", (() => { navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { if (!lock) { show.error("当前有定时任务在后台执行中, 无法发起手动任务"); return; } if ($('a[href*="/users/profile"]').length > 0) { await taskPlugin.checkFavoriteActress(); this.loadData(); } else show.error("未登录JavDb, 同步失败"); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); })); $("#checkNewVideo").on("click", ((event) => { utils.q({ clientX: event.clientX, clientY: event.clientY + 20 }, "是否手动检测最新作品?", (() => { navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { lock ? await taskPlugin.checkNewVideo(true) : show.error("当前有定时任务在后台执行中, 无法发起手动任务"); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); })); $("#paramActressType").on("change", ((event) => { this.loadData(); })); } loadData() { this.currentPage = 1; this.renderActressCards().then(); } async renderActressCards() { const $actressCardContainer = $("#actress-card-container"); if (!$actressCardContainer.length) return; let favoriteActressesList = await storageManager.getFavoriteActressList(); const paramActressType = $("#paramActressType").val(); "all" !== paramActressType && (favoriteActressesList = favoriteActressesList.filter(((actress) => actress.actressType === paramActressType))); const fullActressList = utils.genericSort(favoriteActressesList, [{ key: (item) => { var _a2; return (null == (_a2 = item.newVideoList) ? void 0 : _a2.length) ?? 0; }, order: "desc" }, { key: "lastPublishTime", order: "desc" }]), totalCount = fullActressList.length, totalPages = Math.ceil(totalCount / this.pageSize), startIndex = (this.currentPage - 1) * this.pageSize, endIndex = startIndex + this.pageSize, pagedData = fullActressList.slice(startIndex, endIndex), javDbUrl = await this.getBean("OtherSitePlugin").getJavDbUrl(), taskPlugin = this.getBean("TaskPlugin"), checkNewVideo_ruleTime = await storageManager.getSetting("checkNewVideo_ruleTime") || 8760, cardsHtml = pagedData.map(((data) => { var _a2, _b, _c; const allNamesStr = Array.isArray(data.allName) ? data.allName.join(",") : ""; Array.isArray(data.newVideoList) && data.newVideoList.join(","); const detailLink = `${javDbUrl}/actors/${data.starId}?t=d`; let isUnCheck = false; data.lastPublishTime && (isUnCheck = !taskPlugin.isUnnecessaryCheck(data.lastPublishTime, checkNewVideo_ruleTime)); let actressTypeText = "未知", actressTypeBgc = "#9E9E9E"; if ("uncensored" === data.actressType) { actressTypeText = "无码"; actressTypeBgc = "#4CAF50"; } else if ("censored" === data.actressType) { actressTypeText = "有码"; actressTypeBgc = "#FF9800"; } let cardUnnecessaryBtnCss = ""; isUnCheck && (cardUnnecessaryBtnCss = "background: linear-gradient(145deg, #e0e0e0 0%, #cabdbd 100%);box-shadow: none"); return `
${allNamesStr}
${data.name}
${allNamesStr}
上次检测: ${data.lastCheckTime || ""}
最后发行作品: ${data.lastPublishTime || ""}
${isUnCheck ? "停更" + checkNewVideo_ruleTime / 24 / 365 + "年以上, 下轮任务不再进行检测" : ""}
${data.remark || ""}
${actressTypeText}
🔔 ${(null == (_c = data.newVideoList) ? void 0 : _c.length) || 0}
`; })).join(""); $actressCardContainer.html(cardsHtml); $(".btn-delete-actress").off("click").on("click", ((e) => { e.preventDefault(); const starId = $(e.currentTarget).attr("data-starId"), actress = fullActressList.find(((item) => item.starId === starId)); utils.q(e, `是否取消收藏 ${actress.name}?`, (async () => { let deleteActressUrl = `${await this.getBean("OtherSitePlugin").getJavDbUrl()}/actors/${starId}/uncollect`; const csrfToken = document.querySelector("meta[name=csrf-token]").content, res = await gmHttp.post(deleteActressUrl, null, { "x-csrf-token": csrfToken }); if (res.includes("removeClass")) { await storageManager.removeFavoriteActress(starId); this.loadData(); } else { show.error("移除失败"); clog.error("移除失败,返回值:", res); } })); })); $(".btn-edit-actress").off("click").on("click", ((e) => { e.preventDefault(); const starId = $(e.currentTarget).attr("data-starId"), actress = fullActressList.find(((item) => item.starId === starId)); actress ? this.editActress(actress) : show.error(`未找到 starId 为 ${starId} 的女优记录。`); })); $(".btn-check-actress").off("click").on("click", ((e) => { e.preventDefault(); navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { if (!lock) { show.error("当前有定时任务在后台执行中, 无法发起手动任务"); return; } const starId = $(e.currentTarget).attr("data-starId"), actress = fullActressList.find(((item) => item.starId === starId)); await taskPlugin.checkOneNewVideo(actress); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); this.renderPagination(totalCount, totalPages); show.ok("加载完成"); } async editActress(actress) { const initialName = actress.name, initialAvatar = actress.avatar, initialRemark = actress.remark || "", initialAllName = Array.isArray(actress.allName) ? actress.allName.join(",") : "", initialNewVideoList = Array.isArray(actress.newVideoList) ? actress.newVideoList.join(",") : "", starId = actress.starId, textareaStyle = "width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; min-height: 60px; overflow-y: hidden;", initActressType = actress.actressType || "", editFormHtml = `
Avatar Preview
`; layer.open({ type: 1, title: `编辑女优: ${initialName} (${starId})`, area: ["500px", "750px"], content: editFormHtml, btn: ["保存", "取消"], success: (layero, index) => { const autoResizeTextarea = ($textarea) => { $textarea.css("height", "auto"); $textarea.css("height", $textarea[0].scrollHeight + 15 + "px"); }; $("#edit-actress-avatar").on("input", (function () { const newUrl = $(this).val(); $("#edit-avatar-preview").attr("src", newUrl); })); const $allNameTextarea = $("#edit-actress-allname"); $allNameTextarea.on("input", (function () { autoResizeTextarea($(this)); })); autoResizeTextarea($allNameTextarea); const $videoListTextarea = $("#edit-actress-newvideolist"); $videoListTextarea.on("input", (function () { autoResizeTextarea($(this)); })); autoResizeTextarea($videoListTextarea); $("#search-avatar-btn").on("click", (async () => { await this.searchAvatar(); })); $("#select-cdn-btn").on("click", (async () => { await (async function () { const initialIndex = currentCdnIndex, radioOptions = CDN_SOURCES.map(((source, index2) => `
`)).join(""), cdnSelectHtml = `

请选择头像数据源 (当前: ${CDN_SOURCES[initialIndex].name}):

${radioOptions}

切换源会清除本地缓存的数据,并在下次搜索时重新加载。

`; layer.open({ type: 1, title: "选择 CDN 源", area: ["400px", "auto"], content: cdnSelectHtml, btn: ["确定", "取消"], success: (layero2, index2) => { utils.setupEscClose(index2); }, yes: async (index2) => { const newIndexStr = $('input[name="cdn-source"]:checked').val(), newIndex = parseInt(newIndexStr, 10); if (newIndex !== currentCdnIndex) { currentCdnIndex = newIndex; localStorage.setItem("jhs_img_cdn_index", newIndex.toString()); window.G_FRIENDS_JSON_URL = CDN_SOURCES[newIndex].json; window.CDN_BASE_URL = CDN_SOURCES[newIndex].base; window.G_FRIENDS_DATA_CACHE = null; window.G_FRIENDS_AVATAR_MAP = null; try { await dbHelper.set("filetree_data", null); } catch (e) { clog.error("清除 IndexedDB 缓存失败:", e); } show.ok(`CDN 源已切换为: ${CDN_SOURCES[newIndex].name}`); layer.close(index2); } else layer.close(index2); } }); })(); })); utils.setupEscClose(index); }, yes: async (index) => { const newAvatar = $("#edit-actress-avatar").val().trim(), newName = $("#edit-actress-name").val().trim(), newAllNameStr = $("#edit-actress-allname").val().trim(), newVideoListStr = $("#edit-actress-newvideolist").val().trim(), newRemark = $("#edit-remark").val().trim(), newActressType = $("#actressType").val(); if (!newName) { show.error("主名称不能为空"); return false; } const newAllName = newAllNameStr.split(/[\uff0c,]/).map(((n) => n.trim())).filter(((n) => n.length > 0)), newVideoList = newVideoListStr.split(/[\uff0c,]/).map(((n) => n.trim())).filter(((n) => n.length > 0)); actress.avatar = newAvatar; actress.name = newName; actress.allName = newAllName; actress.newVideoList = newVideoList; actress.actressType = newActressType; actress.remark = newRemark; if (await storageManager.updateFavoriteActress(actress)) show.error("修改失败"); else { show.ok(`女优 ${newName} 信息已更新`); await this.renderActressCards(); layer.close(index); } } }); } renderPagination(totalCount, totalPages) { const currentPage = this.currentPage; let paginationHtml = ""; const $actressPagination = $("#actress-pagination"); if (0 === totalPages) { paginationHtml = '共 0 条记录'; $actressPagination.html(paginationHtml); return; } currentPage > 1 && totalPages > 5 && (paginationHtml += ''); currentPage > 1 && (paginationHtml += ``); let startPage = Math.max(1, currentPage - Math.floor(2.5)), endPage = Math.min(totalPages, startPage + 5 - 1); endPage - startPage < 4 && (startPage = Math.max(1, endPage - 5 + 1)); for (let i = startPage; i <= endPage; i++) { paginationHtml += ``; } currentPage < totalPages && (paginationHtml += ``); currentPage < totalPages && totalPages > 5 && (paginationHtml += ``); paginationHtml += `共 ${totalCount} 条记录 (第 ${currentPage}/${totalPages} 页)`; $actressPagination.html(paginationHtml); $(".pagination-btn").off("click").on("click", ((e) => { if ($(e.currentTarget).is("[disabled]")) return; const newPage = parseInt($(e.currentTarget).data("page")); if (newPage >= 1 && newPage <= totalPages && newPage !== this.currentPage) { this.currentPage = newPage; this.renderActressCards(); } })); } async searchAvatar() { const $mainNameInput = $("#edit-actress-name"), $aliasInput = $("#edit-actress-allname"), currentName = $mainNameInput.val().trim(), searchNames = $aliasInput.val().trim().split(/[\uff0c,]/).map(((n) => n.trim())).filter(((n) => n.length > 0)); currentName && searchNames.unshift(currentName); if (0 === searchNames.length) { show.error("请先填写女优主名称或别名进行搜索。"); return; } const loadObj = loading("正在搜索头像..."); let imageLinks = []; try { imageLinks = await searchActorAvatars(searchNames); } catch (e) { show.error(`头像数据加载或搜索失败: ${e.message || e}`); return; } finally { loadObj.close(); } if (0 === imageLinks.length) { show.error(`未找到与 '${searchNames.join("、")}' 相关的头像。请检查名称。`); return; } const imageItems = imageLinks.map(((url, index) => `
...
`)).join(""), imageListHtml = `

点击图片即可选择(初始共 ${imageLinks.length} 张)

${imageItems}
`; let errorCount = 0; layer.open({ type: 1, title: `选择女优头像 (${imageLinks.length} 张)`, area: utils.getResponsiveArea(["900px", "85%"]), content: imageListHtml, btn: ["关闭"], success: (selectionLayero, selectionIndex) => { const $container = $(selectionLayero), $images = $container.find(".gfriends-selectable-img"), $prompt = $container.find("#gfriends-prompt"); $images.each((function () { const $img = $(this), wrapperId = $img.data("wrapper-id"), $wrapper = $container.find(`#${wrapperId}`), $sizeTag = $container.find(`.gfriends-size-tag[data-size-for="${wrapperId}"]`); $img.on("load", (function () { const width = this.naturalWidth, height = this.naturalHeight; $sizeTag.text(`${width} x ${height}`); })); $img.on("error", (function () { $wrapper.remove(); errorCount++; const validCount = imageLinks.length - errorCount; $prompt.text(`点击图片即可选择(已移除 ${errorCount} 张错误图片,剩余 ${validCount} 张)`); if (0 === validCount) { show.error("所有搜索到的头像链接均已失效,无法选择。"); layer.close(selectionIndex); } })); this.complete && (this.naturalWidth > 0 ? $img.trigger("load") : $img.trigger("error")); })); $images.on("click", (function () { const $clickedImg = $(this), selectedUrl = $clickedImg.data("url"); $("#edit-actress-avatar").val(selectedUrl); $("#edit-avatar-preview").attr("src", selectedUrl); $images.removeClass("is-selected"); $clickedImg.addClass("is-selected"); setTimeout((() => { layer.close(selectionIndex); }), 150); })); utils.setupEscClose(selectionIndex); } }); } } class HistoryPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "tableObj", null); } getName() { return "HistoryPlugin"; } async initCss() { return "\n \n "; } handle() { let fc2Url = "/advanced_search?type=3&score_min=0&d=1"; $('.navbar-item:contains("FC2")').attr("href", fc2Url); $('.tabs a:contains("FC2")').attr("href", fc2Url); if (currentHref$1.includes("advanced_search?type=3")) { $("h2.section-title").contents().first().replaceWith("Fc2PPV"); $(".section .container > .box").remove(); } if (currentHref$1.includes("collection_codes?movieId")) { $("section").html(""); const urlParams = new URLSearchParams(window.location.search); let movieId = urlParams.get("movieId"), carNum2 = urlParams.get("carNum"), url = urlParams.get("url"); movieId && carNum2 && url && this.openFc2Dialog(movieId, carNum2, url); } } openFc2Dialog(movieId, carNum2, href) { let tempCarNum = carNum2.replace("FC2-", ""); if (href.includes("123av")) { this.getBean("Fc2By123AvPlugin").open123AvFc2Dialog(carNum2, href); return; } layer.open({ type: 1, title: carNum2, content: '\n
\n \n \n
\n
加载中...
\n
\n \n
第三方资源:
\n \n \n
\n \n
\n
\n
\n \n \n \n
\n ', area: utils.getResponsiveArea(["70%", "90%"]), skin: "movie-detail-layer", scrollbar: false, success: (layero, index) => { this.loadData(movieId, carNum2); $("#favoriteBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-releaseDate").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_FAVORITE$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#filterBtn").on("click", ((event) => { utils.q(event, `是否屏蔽${carNum2}?`, (async () => { const actress = $("#data-actress").text(), publishTime = $("#data-releaseDate").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_FILTER$1, publishTime }); window.refresh(); layer.closeAll(); window.location.href.includes("collection_codes?movieId") && utils.closePage(); })); })); $("#hasDownBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-releaseDate").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_HAS_DOWN$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#hasWatchBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-releaseDate").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_HAS_WATCH$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#subtitleSearchBtn").on("click", (() => { window.JavPackSubtitle.openSearchModal({details: {code: carNum2}}); })); window.JavPackSubtitle.preload115Matches(carNum2); $("#magnetSearchBtn").on("click", (() => { let magnetHub = this.getBean("MagnetHubPlugin").createMagnetHub(carNum2); layer.open({ type: 1, title: "磁力搜索", content: '
', area: utils.getResponsiveArea(["60%", "80%"]), scrollbar: false, success: () => { $("#magnetHubBox").append(magnetHub); } }); })); this.getBean("OtherSitePlugin").loadOtherSite(tempCarNum, carNum2).then(); utils.setupEscClose(index); }, end() { window.location.href.includes("collection_codes?movieId") && utils.closePage(); } }); } loadData(movieId, carNum2) { let tempCarNum = carNum2.replace("FC2-", ""); this.handleMovieDetail(movieId); this.handleLongImg(tempCarNum); this.handleMagnets(movieId); this.getBean("ReviewPlugin").showReview(movieId, $("#reviews-content")).then(); this.getBean("RelatedPlugin").showRelated($("#related-content"), movieId).then(); } handleMovieDetail(movieId) { javDbApi.getMovieDetail(movieId).then(((res) => { const actors = res.actors || [], imgList = res.imgList || []; let actorsHtml = ""; if (actors.length > 0) { let actress = ""; for (let i = 0; i < actors.length; i++) { let actor = actors[i]; actorsHtml += `${actor.name}`; 0 === actor.gender && (actress += actor.name + " "); } $("#data-actress").text(actress); } else actorsHtml = '暂无演员信息'; let imagesHtml = ""; imagesHtml = Array.isArray(imgList) && imgList.length > 0 ? imgList.map(((img, index) => ` `)).join("") : '
暂无剧照
'; $(".movie-info-container").html(`

${res.title || "无标题"}

番号: ${res.carNum || "未知"} 年份: ${res.releaseDate || "未知"} 评分: ${res.score || "无"} 时长: ${res.duration + " m" || "无"}
主演: ${actorsHtml}
`); this.getBean("TranslatePlugin").translate(res.carNum, false).then(); })).catch(((err) => { console.error(err); $(".movie-info-container").html(`
加载失败: ${err.message}
`); })); } handleLongImg(carNum2) { utils.loopDetector((() => $(".movie-gallery .image-list").length > 0), (async () => { $(".movie-gallery .image-list").prepend('
正在加载缩略图
'); const screenShotPlugin = this.getBean("ScreenShotPlugin"), imgUrl = await screenShotPlugin.getScreenshot(carNum2); imgUrl && await screenShotPlugin.addImg("缩略图", imgUrl, carNum2); })); } handleMagnets(movieId) { javDbApi.getMagnets(movieId).then(((magnetList) => { let magnetsHtml = ""; if (magnetList.length > 0) for (let i = 0; i < magnetList.length; i++) { let magnet = magnetList[i], oddClass = ""; i % 2 == 0 && (oddClass = "odd"); magnetsHtml += ` `; } else magnetsHtml = '暂无磁力信息'; $("#magnets-content").html(magnetsHtml); $(".buttons button[data-clipboard-text*='magnet:']").each(((i, el) => { $(el).parent().append($("'); $tag.append(button); button.fadeIn(0); }), (function () { $(this).find(".highlight-btn").fadeOut(0, (function () { $(this).remove(); })); })); $(document).on("click", ".highlight-btn", (async function (e) { e.stopPropagation(); e.preventDefault(); const $tag = $(this).closest("a.tag"), clonedTag = $tag.clone(); clonedTag.find(".highlight-btn").remove(); const tagText = clonedTag.text().trim().replace(/\s*\(\d+\)$/, ""); let highlightedTags = await storageManager.getHighlightedTags(); if (highlightedTags.includes(tagText)) { highlightedTags = highlightedTags.filter(((item) => item !== tagText)); $tag.removeClass("highlighted"); } else { highlightedTags.push(tagText); $tag.addClass("highlighted"); } await storageManager.setHighlightedTags(highlightedTags); })); } async createFoldBtn() { const foldCategoryHotKey = await storageManager.getSetting("foldCategoryHotKey"); let $subTags = $("#tags"), checkTagStr = $("#tags dl div.tag.is-info").map((function () { return $(this).text().replaceAll("\n", "").replaceAll(" ", ""); })).get().join(" "); if (!checkTagStr) return; $(".tabs").append(`
已选分类: ${checkTagStr}
${foldCategoryHotKey ? ` (${foldCategoryHotKey})` : ""}
`); let $section = $("h2.section-title"); if ($section.length > 0) { $section.append('\n
\n \n \n \n \n
\n '); $subTags = $("section > div > div.box"); } if (!$subTags) return; let $foldCategoryBtn = $("#foldCategoryBtn"), isFolded = localStorage.getItem("jhs_foldCategory") === YES$1, [newText, newIcon] = isFolded ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"]; $foldCategoryBtn.find("span").text(newText).end().find("i").attr("class", newIcon); window.location.href.includes("noFold=1") || $subTags[isFolded ? "hide" : "show"](); $foldCategoryBtn.on("click", (async (event) => { event.preventDefault(); isFolded = !isFolded; localStorage.setItem("jhs_foldCategory", isFolded ? YES$1 : NO$1); const [newText2, newIcon2] = isFolded ? ["展开", "icon-angle-double-down"] : ["折叠", "icon-angle-double-up"]; $foldCategoryBtn.find("span").text(newText2).end().find("i").attr("class", newIcon2); $subTags[isFolded ? "hide" : "show"](); })); } } class ActressInfoPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "apiUrl", "https://ja.wikipedia.org/wiki/"); } getName() { return "ActressInfoPlugin"; } async handle() { "yes" === await storageManager.getSetting("enableLoadActressInfo", "yes") && this.loadActressInfo(); } loadActressInfo() { this.handleDetailPage().then(); this.handleStarPage().then(); } async initCss() { return "\n \n "; } async handleDetailPage() { if ($(".actress-info").length > 0) return; let nameList = $(".female").prev().map(((i, el) => $(el).text().trim())).get(); if (!nameList.length) return; const cacheKey = "jhs_actress_info", cacheData = localStorage.getItem(cacheKey) ? JSON.parse(localStorage.getItem(cacheKey)) : {}; let result = null, infoHtml = ""; for (let i = 0; i < nameList.length; i++) { let name2 = nameList[i]; result = cacheData[name2]; if (!result) try { result = await this.searchInfo(name2); result && (cacheData[name2] = result); } catch (e) { console.error("该名称查询失败,尝试其它名称"); } let contentHtml = ""; contentHtml = result ? ` ` : ` `; infoHtml += contentHtml; } $('strong:contains("演員")').parent().after(infoHtml); localStorage.setItem(cacheKey, JSON.stringify(cacheData)); } async handleStarPage() { if ($(".actress-info").length > 0) return; let nameList = [], $actor = $(".actor-section-name"); $actor.length && $actor.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); let $sectionMeta = $(".section-meta:not(:contains('影片'))"); $sectionMeta.length && $sectionMeta.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); if (!nameList.length) return; const cacheKey = "jhs_actress_info", cacheData = localStorage.getItem(cacheKey) ? JSON.parse(localStorage.getItem(cacheKey)) : {}; let result = null; for (let i = 0; i < nameList.length; i++) { let name2 = nameList[i]; result = cacheData[name2]; if (result) break; try { result = await this.searchInfo(name2); } catch (e) { console.error("该名称查询失败,尝试其它名称"); } if (result) break; } result && nameList.forEach(((name2) => { cacheData[name2] = result; })); let contentHtml = '
无此相关演员信息
'; result && (contentHtml = `
出生日期: ${result.birthday} 年龄: ${result.age} 身高: ${result.height}
体重: ${result.weight} 三围: ${result.threeSizeText} 罩杯: ${result.braSize}
`); $actor.parent().append(contentHtml); localStorage.setItem(cacheKey, JSON.stringify(cacheData)); } async searchInfo(name2) { "三上悠亞" === name2 && (name2 = "三上悠亜"); let url = this.apiUrl + name2; const html2 = await gmHttp.get(url), parser = new DOMParser(), $dom = $(parser.parseFromString(html2, "text/html")); let birthday = $dom.find('a[title="誕生日"]').parent().parent().find("td").text().trim(), age = $dom.find("th:contains('現年齢')").parent().find("td").text().trim() ? parseInt($dom.find("th:contains('現年齢')").parent().find("td").text().trim()) + "岁" : "", height = $dom.find('tr:has(a[title="身長"]) td').text().trim().split(" ")[0] + "cm", weight = $dom.find('tr:has(a[title="体重"]) td').text().trim().split("/")[1].trim(); "― kg" === weight && (weight = ""); return { birthday, age, height, weight, threeSizeText: $dom.find('a[title="スリーサイズ"]').closest("tr").find("td").text().replace("cm", "").trim(), braSize: $dom.find('th:contains("ブラサイズ")').next("td").contents().first().text().trim(), url }; } } class AliyunPanPlugin extends BasePlugin { getName() { return "AliyunPanPlugin"; } handle() { $("body").append('获取refresh_token'); $("#refresh-token-btn").on("click", ((event) => { let tokenStr = localStorage.getItem("token"); if (!tokenStr) { alert("请先登录!"); return; } let refresh_token = JSON.parse(tokenStr).refresh_token; navigator.clipboard.writeText(refresh_token).then((() => { alert("已复制到剪切板 如失败, 请手动复制: " + refresh_token); })).catch(((err) => { console.error("Failed to copy refresh token: ", err); })); })); } } class HitShowPlugin extends BasePlugin { constructor() { super(); __publicField2(this, "$contentBox", $(".section .container")); } getName() { return "HitShowPlugin"; } handle() { $('a[href*="rankings/playback"]').on("click", ((event) => { event.preventDefault(); event.stopPropagation(); window.location.href = "/rankings/playback"; })); this.handlePlayback().then(); } hookPage() { let $h2 = $("h2.section-title"); $h2.contents().first().replaceWith("热播"); $h2.css("marginBottom", "0"); $(".empty-message").remove(); $(".section .container .box").remove(); $("#sort-toggle-btn").remove(); this.$contentBox.append('
'); this.$contentBox.append('
'); } async handlePlayback() { if (!window.location.href.includes("rankings/playback")) return; let period = new URLSearchParams(window.location.search).get("period"); this.toolBar(period); this.hookPage(); let $movieBox = $(".movie-list"); $movieBox.html(""); let loadObj = loading(); try { const movies = await javDbApi.playback(period); let moviesHtml = javDbApi.markDataListHtml(movies); $movieBox.html(moviesHtml); this.pluginManager.getBean("ListPagePlugin").doFilter(); } catch (e) { clog.error("发生错误:", e); } finally { loadObj.close(); } } toolBar(period) { let conditionHtml = ` `; this.$contentBox.append(conditionHtml); } getStarRating(score) { let stars = ""; const fullStars = Math.floor(score); for (let i = 0; i < fullStars; i++) stars += ''; for (let i = 0; i < 5 - fullStars; i++) stars += ''; return stars; } loadScore(movies) { if (0 === movies.length) return; (async () => { for (const movie of movies) try { const movieId = movie.id; if (!$(`#score_${movieId}`).length) return; if ($(`#${movieId}`).is(":hidden")) continue; const cacheData = localStorage.getItem("jhs_score_info") ? JSON.parse(localStorage.getItem("jhs_score_info")) : {}, cached = cacheData[movieId]; if (cached) { this.appendScoreHtml(movieId, cached); continue; } for (; !document.hasFocus();) await new Promise(((r) => setTimeout(r, 500))); const res = await javDbApi.getMovieDetail(movieId); let score = res.score, watchedCount = res.watchedCount, html2 = ` ${this.getStarRating(score)}   ${score}分,由${watchedCount}人評價 `; this.appendScoreHtml(movieId, html2); cacheData[movieId] = html2; localStorage.setItem("jhs_score_info", JSON.stringify(cacheData)); await new Promise(((r) => setTimeout(r, 500))); } catch (err) { clog.error(`🚨 解析评分数据失败 | 编号: ${movie.number} `, `错误详情: ${err.message} `, err.stack ? `调用栈: ${err.stack}` : ""); } })(); } appendScoreHtml(movieId, scoreHtml) { let $scoreBox = $(`#score_${movieId}`); $scoreBox.length && "" === $scoreBox.html().trim() && $scoreBox.slideUp(0, (function () { $(this).html(scoreHtml).slideDown(500); })); } } class TOP250Plugin extends BasePlugin { constructor() { super(); __publicField2(this, "has_cnsub", ""); __publicField2(this, "$contentBox", $(".section .container")); __publicField2(this, "movies", []); } getName() { return "TOP250Plugin"; } handle() { $('.main-tabs ul li:contains("猜你喜歡")').html('Top250'); $('a[href*="rankings/top"]').on("click", ((event) => { event.preventDefault(); event.stopPropagation(); const $target = $(event.target), href = ($target.is("a") ? $target : $target.closest("a")).attr("href"); let queryString = href.includes("?") ? href.split("?")[1] : href; const urlParams = new URLSearchParams(queryString); this.checkLogin(event, urlParams); })); this.handleTop().then(); } hookPage() { $("h2.section-title").contents().first().replaceWith("Top250"); $(".empty-message").remove(); $(".section .container .box").remove(); $("#sort-toggle-btn").remove(); this.$contentBox.append('
'); this.$contentBox.append('
'); this.renderPagination(); } renderPagination() { const urlParams = new URLSearchParams(window.location.search); let currentPage = parseInt(urlParams.get("page")) || 1; this.$contentBox.append(((page) => { const isNextDisabled = page >= 5; let paginationListHTML = ""; for (let i = 1; i <= 5; i++) { paginationListHTML += `
  • ${i}
  • `; } return ` `; })(currentPage)); this.$contentBox.on("click", ".pagination-link, .pagination-previous, .pagination-next", ((event) => { event.preventDefault(); const newPage = parseInt($(event.currentTarget).data("page")); !isNaN(newPage) && newPage > 0 && ((newPage2) => { urlParams.set("page", newPage2); window.history.pushState({}, "", "?" + urlParams.toString()); window.location.reload(); })(newPage); })); } async handleTop() { if (!window.location.href.includes("/rankings/top")) return; const urlParams = new URLSearchParams(window.location.search); let t = urlParams.get("t") || "", type = "all", type_value = ""; if (/^y\d+$/.test(t)) { type = "year"; type_value = t.substring(1); } else if ("" !== t) { type = "video_type"; type_value = t; } this.has_cnsub = urlParams.get("has_cnsub") || ""; let page = urlParams.get("page") || 1; this.toolBar(type, type_value, page); this.hookPage(); let $movieBox = $(".movie-list"); $movieBox.html(""); let loadObj = loading(); try { const res = await javDbApi.top250(type, type_value, page, 50); let successFlag = res.success, message = res.message, action = res.action; if (1 === successFlag) { let dataList = res.data.movies; if (0 === dataList.length) { show.error("无数据"); loadObj.close(); return; } this.movies = dataList; const filter_movies = dataList.filter(((item) => "1" === this.has_cnsub ? item.has_cnsub : "0" !== this.has_cnsub || !item.has_cnsub)); let moviesHtml = javDbApi.markDataListHtml(filter_movies); $movieBox.html(moviesHtml); this.pluginManager.getBean("ListPagePlugin").doFilter(); } else { clog.error(res); $movieBox.html(`

    ${message}

    `); show.error(message); if ("JWTVerificationError" === action) { await localStorage.removeItem("jhs_appAuthorization"); await this.checkLogin(null, new URLSearchParams(window.location.search)); } } } catch (e) { clog.error("发生错误:", e); } finally { loadObj.close(); } } toolBar(type, type_value, currentPage) { "5" === currentPage.toString() && $(".pagination-next").remove(); $(".pagination-ellipsis").closest("li").remove(); $(".pagination-list li a").each((function () { parseInt($(this).text()) > 5 && $(this).closest("li").remove(); })); let yearHtml = ""; for (let year = (/* @__PURE__ */ new Date()).getFullYear(); year >= 2008; year--) yearHtml += ` ${year} `; let conditionHtml = ` `; this.$contentBox.append(conditionHtml); $("a[data-cnsub-value]").on("click", ((event) => { const cnsubValue = $(event.currentTarget).data("cnsub-value"); this.has_cnsub = cnsubValue.toString(); $("a[data-cnsub-value]").removeClass("is-info"); $(event.currentTarget).addClass("is-info"); $(".toolbar a.button").not("[data-cnsub-value]").each(((index, element) => { const $link = $(element), url = new URL($link.attr("href"), window.location.origin); url.searchParams.set("has_cnsub", cnsubValue); $link.attr("href", url.toString()); })); const filter_movies = this.movies.filter(((item) => "1" === this.has_cnsub ? item.has_cnsub : "0" !== this.has_cnsub || !item.has_cnsub)); let moviesHtml = javDbApi.markDataListHtml(filter_movies); $(".movie-list").html(moviesHtml); })); } async checkLogin(event, urlParams) { if (!localStorage.getItem("jhs_appAuthorization")) { show.error("该类别依赖移动端接口,请先完成登录"); this.openLoginDialog(); return; } let type = "all", type_value = "", t = urlParams.get("t") || ""; if (/^y\d+$/.test(t)) { type = "year"; type_value = t.substring(1); } else if ("" !== t) { type = "video_type"; type_value = t; } let tParam = "year" === type ? `y${type_value}` : "all" === type ? "" : type_value; let url = `/rankings/top${tParam ? `?t=${tParam}` : ""}`; event && (event.ctrlKey || event.metaKey) ? _GM_openInTab(window.location.origin + url, { insert: 0 }) : window.location.href = url; } openLoginDialog() { layer.open({ type: 1, title: "JavDB", closeBtn: 1, area: ["360px", "auto"], shadeClose: false, content: `
    `, success: (layero, index) => { $("#loginBtn").click((function () { const username = $("#username").val(), password = $("#password").val(); if (!username || !password) { show.error("请输入用户名和密码"); return; } let loadObj = loading(); javDbApi.login(username, password).then((async (res) => { let success = res.success; if (0 === success) show.error(res.message); else { if (1 !== success) { clog.error("登录失败", res); throw new Error(res.message); } { let token = res.data.token; await localStorage.setItem("jhs_appAuthorization", token); show.ok("登录成功"); layer.close(index); window.location.href = "/rankings/top"; } } })).catch(((err) => { clog.error("登录异常:", err); show.error(err.message); })).finally((() => { loadObj.close(); })); })); } }); } } class NavBarPlugin extends BasePlugin { getName() { return "NavBarPlugin"; } async initCss() { return "\n .highlight-red {\n /* 核心要求:高亮红色文本 */\n color: red !important; \n \n /* 建议:增加字体加粗,效果更明显 */\n font-weight: bold;\n \n /* 建议:增加背景色,效果更突出 */\n /* background-color: yellow; */ \n}\n "; } handle() { this.margeNav(); this.hookSearch(); this.hookOldSearch(); this.toggleOtherNavItem(); $(window).resize(this.toggleOtherNavItem); if (window.location.href.includes("/search")) { const urlParams = new URLSearchParams(window.location.search); let q = urlParams.get("q"), f = urlParams.get("f"); $("#search-keyword").val(q); f && $("#search-type").val(f); q && this.highlightKeyword(q); } } highlightKeyword(keyword) { const trimedKeyword = keyword.trim(); if (!trimedKeyword) return; const lowerCaseKeyword = trimedKeyword.toLowerCase(); $(".video-title strong, .actor-box strong").each((function () { const $strongElement = $(this); $strongElement.text().toLowerCase().includes(lowerCaseKeyword) && $strongElement.addClass("highlight-red"); })); } hookSearch() { $("#navbar-menu-hero").after('\n \n '); $("#search-keyword").on("paste", ((event) => { const items = event.originalEvent.clipboardData.items; for (let i = 0; i < items.length; i++) if (-1 !== items[i].type.indexOf("image")) { const blob = items[i].getAsFile(); $("#search-keyword").blur(); const imageRecognitionPlugin = this.getBean("ImageRecognitionPlugin"); imageRecognitionPlugin.open((() => { imageRecognitionPlugin.handleImageFile(blob); imageRecognitionPlugin.resetSearchUI(); })); return; } setTimeout((() => { $("#search-btn").click(); }), 0); })).on("keypress", ((event) => { "Enter" === event.key && setTimeout((() => { $("#search-btn").click(); }), 0); })); $("#search-btn").on("click", ((event) => { let keyword = $("#search-keyword").val(), searchCurrentType = $("#search-type option:selected").val(); "" !== keyword && (window.location.href.includes("/search") ? window.location.href = "/search?q=" + keyword + "&f=" + searchCurrentType : window.open("/search?q=" + keyword + "&f=" + searchCurrentType)); })); $("#search-img-btn").on("click", (() => { this.getBean("ImageRecognitionPlugin").open(); })); } hookOldSearch() { const searchImage = document.querySelector(".search-image"); if (!searchImage) return; const clonedImage = searchImage.cloneNode(true); searchImage.parentNode.replaceChild(clonedImage, searchImage); $("#button-search-image").attr("data-tooltip", "以图识图"); $(".search-image").on("click", ((event) => { this.getBean("ImageRecognitionPlugin").open(); })); } margeNav() { $('a[href*="/feedbacks/new"]').remove(); $('a[href*="theporndude.com"]').remove(); $('a.navbar-link[href="/makers"]').parent().after('\n \n '); } toggleOtherNavItem() { let $searchBox = $("#search-box"), $oldSearchBox = $("#search-bar-container"); if ($(window).width() < 1600 && $(window).width() > 1023) { $searchBox.hide(); $oldSearchBox.show(); } if ($(window).width() > 1600) { $searchBox.show(); $oldSearchBox.hide(); } } } class BusDetailPagePlugin extends BasePlugin { getName() { return "BusDetailPagePlugin"; } async initCss() { if (!window.isDetailPage) return ""; $("h4:contains('推薦')").hide(); } async handle() { if (window.location.href.includes("/star/")) { const $avatarBox = $(".avatar-box"); if ($avatarBox.length > 0) { let parent2 = $avatarBox.parent(); parent2.css("position", "initial"); parent2.insertBefore(parent2.parent()); } } $(".genre a").each((function () { const href2 = $(this).attr("href"); href2 && (href2.startsWith("http://") || href2.startsWith("https://") || href2.startsWith("/")) && $(this).attr("target", "_blank"); })); this.addCopyCarNumBtn(); } addCopyCarNumBtn() { let headerSpan = null; const headerSpans = document.querySelectorAll("span.header"); for (const span of headerSpans) if ("識別碼:" === span.textContent.trim()) { headerSpan = span; break; } if (headerSpan) { const targetSpan = headerSpan.nextElementSibling; if (targetSpan && "SPAN" === targetSpan.tagName) { const identifierText = targetSpan.textContent.trim(), copyButton = document.createElement("button"); copyButton.textContent = "复制"; copyButton.style.marginLeft = "10px"; copyButton.style.padding = "0 10px"; copyButton.style.cursor = "pointer"; copyButton.style.border = "1px solid #ccc"; copyButton.style.borderRadius = "5px"; copyButton.style.backgroundColor = "#f0f0f0"; copyButton.style.fontSize = "12px"; copyButton.addEventListener("click", (function (event) { event.preventDefault(); const copyAction = (text) => { this.textContent = "已复制"; setTimeout((() => { this.textContent = "复制"; }), 1500); }; navigator.clipboard && navigator.clipboard.writeText && navigator.clipboard.writeText(identifierText).then((() => copyAction())).catch(((err) => { console.error("无法通过标准API复制:", err); alert("复制失败,请手动复制: " + identifierText); })); })); targetSpan.parentNode.insertBefore(copyButton, targetSpan.nextSibling); } } } } class BusPreviewVideoPlugin extends BasePlugin { getName() { return "BusPreviewVideoPlugin"; } async initCss() { return "\n /* 弹窗/Modal 样式 */\n .bus-preview-modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.95); \n /* 关键修改:更新 z-index */\n z-index: 12345699; \n display: flex;\n justify-content: center;\n align-items: center;\n opacity: 0; \n visibility: hidden; \n transition: opacity 0.2s ease;\n }\n .bus-preview-modal.is-open {\n opacity: 1;\n visibility: visible;\n }\n /* 垂直排列视频和按钮,并居中 */\n .bus-preview-modal-content {\n position: relative;\n max-width: 95%; \n max-height: 95%;\n display: flex; \n flex-direction: column; \n align-items: center; \n gap: 15px; \n }\n \n /* 移除 .bus-preview-close-btn 的样式 */\n\n /* 视频播放器容器 */\n .video-player-wrapper {\n /* 关键修改:更新 width 和 max-height */\n width: 80vw; \n max-height: 85vh; \n aspect-ratio: 16 / 9; \n position: relative; \n background-color: black; \n max-width: 100%; \n }\n /* 视频元素 */\n .video-player-wrapper #preview-video {\n position: absolute; \n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: block;\n }\n\n /* 画质控制盒 (底部按钮) */\n .video-control-box {\n display: flex;\n flex-direction: row; \n justify-content: center; \n flex-wrap: wrap; \n gap: 10px;\n padding: 10px 0; \n }\n\n /* 按钮样式 (保留) */\n .video-control-btn {\n min-width:80px;\n padding: 6px 12px;\n background: rgba(255,255,255,0.2);\n color: white;\n border: 1px solid rgba(255,255,255,0.5);\n border-radius: 4px;\n cursor: pointer;\n text-align: center;\n font-size: 14px;\n transition: background-color 0.2s, border-color 0.2s;\n }\n .video-control-btn:hover {\n background: rgba(255,255,255,0.4);\n }\n .video-control-btn.active {\n background-color: #1890ff; \n color: white;\n font-weight: bold;\n border: 1px solid #096dd9;\n }\n "; } initModal() { if (0 === $("#bus-preview-modal").length) { $("body").append('\n
    \n
    \n
    \n
    \n '); const $modal = $("#bus-preview-modal"); $modal.on("click", ((e) => { "bus-preview-modal" === e.target.id && this.closeVideoModal(); })); $(document).on("keydown", ((e) => { "Escape" === e.key && $modal.hasClass("is-open") && this.closeVideoModal(); })); } } closeVideoModal() { const $previewVideo = $("#preview-video"); if ($previewVideo.length > 0) { const videoEl = $previewVideo[0]; videoEl.pause(); } $("#bus-preview-modal").removeClass("is-open"); } async handle() { if (!window.isDetailPage) return; this.initModal(); const firstImageSrc = $("#sample-waterfall .sample-box .photo-frame img:first").attr("src"), videoPreview = $(`
    ▶
    `); $("#sample-waterfall").prepend(videoPreview); "yes" === await storageManager.getSetting("enableLoadPreviewVideo", "yes") && getDmmVideo(this.getPageInfo().carNum, false).then(); let isHandlingVideo = false, $preview = $(".preview-video-container"); $preview.on("click", (async (event) => { event.preventDefault(); event.stopPropagation(); if (isHandlingVideo) show.info("正在加载中, 勿重复点击"); else { isHandlingVideo = true; try { await this.handleVideo(); } finally { isHandlingVideo = false; } } })); window.location.href.includes("autoPlay=1") && $preview.trigger("click"); } async handleVideo() { const $modal = $("#bus-preview-modal"), $modalContent = $modal.find(".bus-preview-modal-content"); let $previewVideo = $("#preview-video"); if ($previewVideo.length > 0) { $modal.addClass("is-open"); const videoEl = $previewVideo[0]; videoEl.play().catch(((e) => console.warn("尝试播放失败 (可能被浏览器阻止):", e))); return; } let carNum2 = this.getPageInfo().carNum; const dmmVideoMap = await getDmmVideo(carNum2); if (dmmVideoMap && 0 !== Object.keys(dmmVideoMap).length) { await this.createVideoPlayerAndControls(dmmVideoMap, $modalContent); $previewVideo = $("#preview-video"); if ($previewVideo.length > 0) { $modal.addClass("is-open"); $previewVideo[0].play().catch(((e) => console.warn("尝试播放失败 (可能被浏览器阻止):", e))); } else show.error("视频播放器创建失败。"); } else show.error("未找到可用的视频源。"); } async createVideoPlayerAndControls(dmmVideoMap, $container) { let defaultVideoQuality = await storageManager.getSetting("videoQuality"); defaultVideoQuality = selectDefaultQuality(Object.keys(dmmVideoMap), defaultVideoQuality); let defaultVideoUrl = dmmVideoMap[defaultVideoQuality]; $container.html(`
    `); const $videoEl = $("#preview-video"), $qualityControlsBox = $container.find(".video-control-box"); if (!$videoEl.length) return; const videoEl = $videoEl[0]; const jhs_videoMuted = localStorage.getItem("jhs_videoMuted"); videoEl.muted = !jhs_videoMuted || "yes" === jhs_videoMuted; videoEl.addEventListener("volumechange", (function () { localStorage.setItem("jhs_videoMuted", videoEl.muted ? "yes" : "no"); })); let buttonsHtml = ""; qualityOptions.forEach(((option) => { let dmmVideoUrl = dmmVideoMap[option.quality]; if (dmmVideoUrl) { const isActive = defaultVideoQuality === option.quality; buttonsHtml += ` `; } })); $qualityControlsBox.html(buttonsHtml); const $buttons = $qualityControlsBox.find(".video-control-btn"); $qualityControlsBox.off("click").on("click", ".video-control-btn", (async (e) => { try { const $button = $(e.currentTarget); if ($button.hasClass("active")) return; let videoSrc = $button.attr("data-video-src"); const currentTime = videoEl.currentTime; videoEl.src = videoSrc; videoEl.load(); if (currentTime > 0 && Number.isFinite(currentTime)) { videoEl.currentTime = currentTime; } await videoEl.play(); $buttons.removeClass("active"); $button.addClass("active"); } catch (error) { console.error("切换画质失败:", error); } })); } } const HLS_SCRIPT_URL = "https://cdn.jsdelivr.net/npm/hls.js@1.5.18/dist/hls.min.js"; let hlsLoadPromise = null; const getHlsClass = () => { const HlsClass = window.Hls || globalThis.Hls || (typeof Hls !== "undefined" ? Hls : null); return HlsClass && HlsClass.isSupported && HlsClass.isSupported() ? HlsClass : null; }; const binaryTextToArrayBuffer = (value) => { const text = String(value || ""); const bytes = new Uint8Array(text.length); for (let index = 0; index < text.length; index += 1) { bytes[index] = text.charCodeAt(index) & 255; } return bytes.buffer; }; const loadHlsLibrary = () => { const readyHls = getHlsClass(); if (readyHls) return Promise.resolve(readyHls); if (hlsLoadPromise) return hlsLoadPromise; const loadByGm = () => new Promise((resolve) => { _GM_xmlhttpRequest({ method: "GET", url: HLS_SCRIPT_URL, timeout: 15e3, onload: (r) => { if (r.status >= 200 && r.status < 300 && r.responseText) { try { Function(`${r.responseText} //# sourceURL=${HLS_SCRIPT_URL}`).call(globalThis); } catch (err) { clog.error("HLS: hls.js 执行失败", err); } } resolve(getHlsClass()); }, onerror: () => resolve(getHlsClass()), ontimeout: () => resolve(getHlsClass()) }); }); hlsLoadPromise = new Promise((resolve) => { const existing = document.querySelector('script[data-jhs-hls="1"]'); if (existing) { existing.addEventListener("load", () => resolve(getHlsClass()), {once: true}); existing.addEventListener("error", () => loadByGm().then(resolve), {once: true}); setTimeout(() => { if (!getHlsClass()) loadByGm().then(resolve); }, 4e3); return; } const hlsScript = document.createElement("script"); hlsScript.src = HLS_SCRIPT_URL; hlsScript.async = true; hlsScript.dataset.jhsHls = "1"; hlsScript.onload = () => resolve(getHlsClass()); hlsScript.onerror = () => loadByGm().then(resolve); document.head.appendChild(hlsScript); }).then((HlsClass) => { if (!HlsClass) hlsLoadPromise = null; return HlsClass; }); return hlsLoadPromise; }; const createHlsLoader = () => { class GMHlsLoader { constructor(config) { this.config = config; this.context = null; this.callbacks = null; this.loader = null; this.stats = this.createStats(); } createStats() { return { aborted: false, loaded: 0, retry: 0, total: 0, chunkCount: 0, bwEstimate: 0, trequest: 0, tfirst: 0, tload: 0, loading: {start: 0, first: 0, end: 0}, parsing: {start: 0, end: 0}, buffering: {start: 0, first: 0, end: 0} }; } destroy() { this.abort(); } abort() { if (this.stats) this.stats.aborted = true; if (this.loader) { try { this.loader.abort(); } catch (e) { } } this.loader = null; } load(context, config, callbacks) { this.context = context; this.callbacks = callbacks; const requestUrl = context.url; const wantsArrayBuffer = context.responseType === "arraybuffer" || /\.(?:ts|m4s|mp4|key)(?:[?#]|$)/i.test(requestUrl); const startedAt = performance.now(); const stats = this.stats = this.createStats(); stats.trequest = startedAt; stats.tfirst = startedAt; stats.tload = startedAt; stats.loading.start = startedAt; this.loader = _GM_xmlhttpRequest({ method: "GET", url: requestUrl, responseType: "text", overrideMimeType: wantsArrayBuffer ? "text/plain; charset=x-user-defined" : void 0, timeout: config && config.timeout ? config.timeout : 2e4, headers: { Accept: wantsArrayBuffer ? "*/*" : "application/vnd.apple.mpegurl, application/x-mpegURL, */*" }, onprogress: (event) => { stats.loaded = Number(event && event.loaded ? event.loaded : stats.loaded || 0); stats.total = Number(event && event.total ? event.total : stats.total || stats.loaded || 0); if (!stats.loading.first && stats.loaded > 0) { stats.loading.first = performance.now(); } }, onload: (r) => { const status = Number(r.status || 0); const response = { code: status, text: r.statusText || "", url: r.finalUrl || requestUrl }; stats.tfirst = stats.tfirst || performance.now(); stats.tload = performance.now(); stats.loading.first = stats.loading.first || stats.tload; stats.loading.end = stats.tload; if (status < 200 || status >= 300) { callbacks.onError && callbacks.onError(response, context, null, stats); return; } const responseText = r.responseText || r.response || ""; const data = wantsArrayBuffer ? binaryTextToArrayBuffer(responseText) : responseText; stats.loaded = data && data.byteLength ? data.byteLength : data && data.length ? data.length : stats.loaded || 0; stats.total = stats.total || stats.loaded; stats.bwEstimate = stats.loading.end > stats.loading.first ? Math.round(stats.total * 8e3 / (stats.loading.end - stats.loading.first)) : 0; callbacks.onSuccess && callbacks.onSuccess({data, url: response.url}, stats, context, response); }, onerror: () => { callbacks.onError && callbacks.onError({ code: 0, text: "network error", url: requestUrl }, context, null, stats); }, ontimeout: () => { stats.tload = performance.now(); stats.loading.end = stats.tload; callbacks.onTimeout && callbacks.onTimeout(stats, context, null); } }); } } return GMHlsLoader; }; const isM3U8Url = (url) => /\.m3u8(?:[?#].*)?$/i.test(String(url || "")); const attachHlsToVideo = (videoEl, src) => { return new Promise((resolve, reject) => { if (!videoEl || !src) { reject(new Error("video element or source missing")); return; } const HlsClass = getHlsClass(); if (!HlsClass) { videoEl.src = src; videoEl.load && videoEl.load(); resolve(false); return; } const hls = new HlsClass({ enableWorker: false, lowLatencyMode: true, loader: createHlsLoader(), autoStartLoad: true, startPosition: 0, capLevelToPlayerSize: true, testBandwidth: false, preferManagedMediaSource: false, maxBufferLength: 6, maxMaxBufferLength: 12, backBufferLength: 30, maxBufferHole: 0.5, nudgeOffset: 0.1, manifestLoadingMaxRetry: 2, levelLoadingMaxRetry: 2, fragLoadingMaxRetry: 2, manifestLoadingTimeOut: 12e3, levelLoadingTimeOut: 12e3, fragLoadingTimeOut: 12e3, abrEwmaFastLive: 3, abrEwmaSlowLive: 9 }); hls.on(HlsClass.Events.MANIFEST_PARSED, () => { hls.startLoad(0); videoEl.play().catch(() => { }); resolve(true); }); hls.on(HlsClass.Events.ERROR, (_, data) => { if (!data || !data.fatal) return; clog.error("HLS: 播放失败", data); if (data.type === HlsClass.ErrorTypes.MEDIA_ERROR) { if (videoEl.readyState >= 2) { try { hls.recoverMediaError && hls.recoverMediaError(); } catch (e) { } return; } } reject(new Error(data.details || "HLS fatal error")); }); hls.loadSource(src); hls.attachMedia(videoEl); videoEl._hls = hls; }); }; const destroyHls = (videoEl) => { if (videoEl && videoEl._hls) { try { videoEl._hls.destroy(); } catch (e) { } videoEl._hls = null; } }; const COVER_API_URL = [77, 81, 81, 85, 86, 31, 10, 10, 68, 85, 76, 8, 65, 72, 72, 87, 86, 70, 11, 20, 22, 20, 17, 22, 22, 11, 93, 92, 95, 10, 68, 85, 76, 10, 70, 74, 83, 64, 87].map((v) => String.fromCharCode(v ^ 37)).join(""); const TRAILER_API_URL = [77, 81, 81, 85, 86, 31, 10, 10, 68, 85, 76, 8, 65, 72, 72, 87, 86, 70, 11, 20, 22, 20, 17, 22, 22, 11, 93, 92, 95, 10, 68, 85, 76, 10, 81, 87, 68, 76, 73, 64, 87].map((v) => String.fromCharCode(v ^ 37)).join(""); const BASE_URL = [77, 81, 81, 85, 86, 31, 10, 10, 68, 85, 76, 8, 65, 72, 72, 87, 86, 70, 11, 20, 22, 20, 17, 22, 22, 11, 93, 92, 95].map((v) => String.fromCharCode(v ^ 37)).join(""); const COVER_API_TOKEN = [18, 28, 19, 19, 65, 65, 70, 28, 64, 29, 23, 21, 17, 29, 67, 70, 18, 64, 21, 22, 64, 16, 65, 29, 22, 19, 16, 28, 16, 17, 64, 29].map((v) => String.fromCharCode(v ^ 37)).join(""); const getCover = async (code) => { var _a2; const query = String(code || "").trim(); if (!query) return null; try { const r = await gmHttp.get(`${COVER_API_URL}/${encodeURIComponent(query)}`, null, { "Authorization": `Bearer ${COVER_API_TOKEN}` }, {timeout: 15e3}); if ((_a2 = r == null ? void 0 : r.cover) == null ? void 0 : _a2.large) { return r.cover.large; } return null; } catch (e) { clog.error("获取封面失败:", e.message); return null; } }; const getTrailer = async (code) => { const query = String(code || "").trim(); if (!query) return null; try { const r = await gmHttp.get(`${TRAILER_API_URL}/${encodeURIComponent(query)}`, null, { "Authorization": `Bearer ${COVER_API_TOKEN}` }, {timeout: 15e3}); if ((r == null ? void 0 : r.trailers) && Array.isArray(r.trailers) && r.trailers.length > 0) { const qualityMap = {}; r.trailers.forEach((item) => { if (item.proxy && item.quality) { qualityMap[item.quality] = `${BASE_URL}${item.proxy}`; } }); if (Object.keys(qualityMap).length > 0) { clog.log("预告片播放地址:", JSON.stringify(qualityMap)); return qualityMap; } } return null; } catch (e) { clog.error("获取预告片失败:", e.message); return null; } }; class JavopenPreviewVideoPlugin extends BasePlugin { getName() { return "JavopenPreviewVideoPlugin"; } _attachVideoSrc(videoEl, src) { if (!videoEl || !src) return; destroyHls(videoEl); if (isM3U8Url(src)) { const HlsClass = getHlsClass(); if (HlsClass) { attachHlsToVideo(videoEl, src).catch((err) => { clog.error("视频预览 HLS 加载失败:", err); }); } else { loadHlsLibrary().then((HlsClass2) => { if (videoEl && videoEl.isConnected && HlsClass2) { attachHlsToVideo(videoEl, src).catch((err) => { clog.error("视频预览 HLS 延迟加载失败:", err); }); } }); } } else { videoEl.src = src; videoEl.load && videoEl.load(); } } _selectDefaultQuality(qualityList, intendedDefault) { if (!qualityList || qualityList.length === 0) return null; const availableSet = new Set(qualityList); if (availableSet.has(intendedDefault)) return intendedDefault; const priorityOrder = qualityOptions.map((o) => o.quality).reverse(); for (const q of priorityOrder) if (availableSet.has(q)) return q; return qualityList[0]; } async initCss() { return ` .jhs-javopen-video-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.95); z-index: 12345700; display: flex; justify-content: center; align-items: center; opacity: 0; visibility: hidden; transition: opacity 0.2s; } .jhs-javopen-video-modal.is-open { opacity: 1; visibility: visible; } .jhs-javopen-video-modal-inner { display: flex; flex-direction: column; align-items: center; gap: 12px; max-width: 90vw; max-height: 90vh; } .jhs-javopen-video-wrapper { width: 80vw; max-height: 80vh; aspect-ratio: 16/9; background: #000; position: relative; } .jhs-javopen-video-wrapper video { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .jhs-javopen-quality-bar { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; } .jhs-javopen-quality-btn { min-width: 60px; padding: 5px 10px; font-size: 13px; background: rgba(255,255,255,0.2); color: #fff; border: 1px solid rgba(255,255,255,0.5); border-radius: 4px; cursor: pointer; transition: background 0.2s; } .jhs-javopen-quality-btn:hover { background: rgba(255,255,255,0.4); } .jhs-javopen-quality-btn.active { background: #1890ff; border-color: #096dd9; font-weight: bold; } .jhs-javopen-loading { color: #fff; font-size: 16px; text-align: center; } `; } async handle() { if (!window.isDetailPage) return; const carNum2 = this.getPageInfo().carNum; if (!carNum2) return; this._addJavopenButton(carNum2); } _addJavopenButton(carNum2) { let $target = null; if (isJavDb$1) { $target = $('a[title="複製番號"]'); } else if (isJavBus$1) { const headerSpans = document.querySelectorAll("span.header"); for (const span of headerSpans) { if (span.textContent.trim() === "識別碼:") { const nextSpan = span.nextElementSibling; if (nextSpan && nextSpan.tagName === "SPAN") { const copyBtn = nextSpan.nextElementSibling; if (copyBtn && copyBtn.tagName === "BUTTON" && copyBtn.textContent.trim() === "复制") { $target = $(copyBtn); } else { $target = $(nextSpan); } } break; } } } if (!$target || !$target.length) return; const $btn = $(`视频预览`); $btn.on("click", async (e) => { e.preventDefault(); e.stopPropagation(); $btn.css("pointer-events", "none").find("span").text("..."); try { await this._openJavopenPlayer(carNum2); } finally { $btn.css("pointer-events", "").find("span").text("视频预览"); } }); $target.after($btn); } async _openJavopenPlayer(carNum2) { if ($("#jhs-javopen-video-modal").length === 0) { $("body").append(`
    `); const $modal2 = $("#jhs-javopen-video-modal"); $modal2.on("click", (e) => { if (e.target.id === "jhs-javopen-video-modal") { $modal2.removeClass("is-open"); const videoEl2 = document.getElementById("jhs-javopen-video"); if (videoEl2) { destroyHls(videoEl2); videoEl2.pause(); } } }); $(document).on("keydown", (e) => { if (e.key === "Escape" && $modal2.hasClass("is-open")) { $modal2.removeClass("is-open"); const videoEl2 = document.getElementById("jhs-javopen-video"); if (videoEl2) { destroyHls(videoEl2); videoEl2.pause(); } } }); } const $modal = $("#jhs-javopen-video-modal"); const $wrapper = $modal.find(".jhs-javopen-video-wrapper"); const $qualityBar = $modal.find(".jhs-javopen-quality-bar"); $wrapper.html('
    视频预览加载中...
    '); $qualityBar.empty(); $modal.addClass("is-open"); let videoMap = null; try { videoMap = await getTrailer(carNum2); } catch (err) { $wrapper.html(`
    视频预览请求失败: ${err.message}
    `); return; } if (!videoMap || Object.keys(videoMap).length === 0) { $wrapper.html('
    未找到可用的视频源
    '); return; } const qualityList = Object.keys(videoMap); const defaultQuality = this._selectDefaultQuality(qualityList, "720p"); const defaultUrl = videoMap[defaultQuality]; $wrapper.html(``); const videoEl = document.getElementById("jhs-javopen-video"); if (!videoEl) return; if (isM3U8Url(defaultUrl)) { this._attachVideoSrc(videoEl, defaultUrl); } let buttonsHtml = ""; qualityOptions.forEach((option) => { const url = videoMap[option.quality]; if (url) { buttonsHtml += ``; } }); $qualityBar.html(buttonsHtml); $qualityBar.off("click").on("click", ".jhs-javopen-quality-btn", (e) => { const $btn = $(e.currentTarget); if ($btn.hasClass("active")) return; const src = $btn.attr("data-video-src"); const currentTime = videoEl.currentTime; destroyHls(videoEl); this._attachVideoSrc(videoEl, src); videoEl.load(); if (currentTime > 0 && Number.isFinite(currentTime)) { videoEl.currentTime = currentTime; } videoEl.play().catch(() => { }); $qualityBar.find(".jhs-javopen-quality-btn").removeClass("active"); $btn.addClass("active"); }); videoEl.play().catch((e) => console.warn("视频预览播放失败:", e)); } } class ImageRecognitionPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "siteList", [{ name: "Google旧版", url: "https://www.google.com/searchbyimage?image_url={占位符}&client=firefox-b-d", ico: "https://www.google.com/favicon.ico" }, { name: "Google", url: "https://lens.google.com/uploadbyurl?url={占位符}", ico: "https://www.google.com/favicon.ico" }, { name: "Yandex", url: "https://yandex.ru/images/search?rpt=imageview&url={占位符}", ico: "https://yandex.ru/favicon.ico" }]); __publicField2(this, "isUploading", false); } getName() { return "ImageRecognitionPlugin"; } async initCss() { return "\n \n "; } open(onOpenFun) { layer.open({ type: 1, title: "以图识图", content: '\n
    \n
    \n
    \n

    拖拽图片到此处 或 点击按钮选择图片

    \n

    也可以直接 Ctrl+V 粘贴图片或 图片URL

    \n
    \n \n \n
    \n \n \n \n \n \n
    \n ', area: utils.isMobile() ? utils.getResponsiveArea() : ["40%", "80%"], success: async (layero) => { this.initEventListeners(); onOpenFun && onOpenFun(); }, end: () => { $(document).off("paste.searchImg"); } }); } initEventListeners() { const $uploadArea = $("#upload-area"), $fileInput = $("#image-file"), $selectBtn = $("#select-image-btn"), $previewArea = $("#preview-area"), $previewImage = $("#preview-image"), $actionBtns = $("#action-btns"), $searchImage = $("#handle-btn"), $cancelBtn = $("#cancel-btn"), $urlInputContainer = $("#url-input-container"), $imageUrlInput = $("#image-url"), $searchResults = $("#search-results"), $siteBtnsContainer = $("#search-img-site-btns-container"); $uploadArea.on("dragover", ((e) => { e.preventDefault(); $uploadArea.addClass("highlight"); })).on("dragleave", (() => { $uploadArea.removeClass("highlight"); })).on("drop", ((e) => { e.preventDefault(); $uploadArea.removeClass("highlight"); if (e.originalEvent.dataTransfer.files && e.originalEvent.dataTransfer.files[0]) { this.handleImageFile(e.originalEvent.dataTransfer.files[0]); this.resetSearchUI(); } })); $selectBtn.on("click", (() => { $fileInput.trigger("click"); })); $fileInput.on("change", ((e) => { if (e.target.files && e.target.files[0]) { this.handleImageFile(e.target.files[0]); this.resetSearchUI(); } })); $(document).on("paste.searchImg", (async (e) => { const items = e.originalEvent.clipboardData.items; for (let i = 0; i < items.length; i++) if (-1 !== items[i].type.indexOf("image")) { const blob = items[i].getAsFile(); this.handleImageFile(blob); this.resetSearchUI(); return; } const text = e.originalEvent.clipboardData.getData("text"); if (text && utils.isUrl(text)) { $urlInputContainer.show(); $imageUrlInput.val(text); $previewImage.attr("src", text); $previewArea.show(); this.resetSearchUI(); } })); $searchImage.on("click", (async () => { const imageSrc = $previewImage.attr("src"); if (imageSrc) { if (!this.isUploading) { this.isUploading = true; try { const imgUrl = await this.searchByImage(imageSrc); $actionBtns.hide(); $searchResults.show(); $siteBtnsContainer.empty(); const key = "jhs_selectedSites", selectedSites = JSON.parse(localStorage.getItem(key) || "{}"); this.siteList.forEach(((site) => { const siteUrl = site.url.replace("{占位符}", encodeURIComponent(imgUrl)), isChecked = false !== selectedSites[site.name]; $siteBtnsContainer.append(` ${site.name} ${site.name} `); })); $siteBtnsContainer.on("change", ".site-checkbox", (function () { const siteName = $(this).data("site-name"); selectedSites[siteName] = $(this).is(":checked"); localStorage.setItem(key, JSON.stringify(selectedSites)); })); $siteBtnsContainer.show(); } finally { this.isUploading = false; } } } else show.info("请粘贴或上传图片"); })); $cancelBtn.on("click", (() => { $previewArea.hide(); $urlInputContainer.hide(); $fileInput.val(""); $imageUrlInput.val(""); })); $imageUrlInput.on("change", (() => { if (utils.isUrl($imageUrlInput.val())) { $previewImage.attr("src", $imageUrlInput.val()); $previewArea.show(); } })); $("#openAll").on("click", (() => { $(".search-img-site-btn").each((function () { $(this).find(".site-checkbox").is(":checked") && window.open($(this).attr("href")); })); })); } resetSearchUI() { $("#action-btns").show(); $("#search-results").hide(); $("#search-img-site-btns-container").hide().empty(); } handleImageFile(file) { const previewImage = document.getElementById("preview-image"), previewArea = document.getElementById("preview-area"), urlInputContainer = document.getElementById("url-input-container"); if (!file.type.match("image.*")) { show.info("请选择图片文件"); return; } const reader = new FileReader(); reader.onload = (e) => { previewImage.src = e.target.result; previewArea.style.display = "block"; urlInputContainer.style.display = "none"; $("#handle-btn")[0].click(); }; reader.readAsDataURL(file); } async searchByImage(imageSrc) { let loadObj = loading(); try { let imageUrl = imageSrc; if (imageSrc.startsWith("data:")) { show.info("开始上传图片..."); const imgurUrl = await (async function (base64Data) { var _a2; const matches = base64Data.match(/^data:(.+);base64,(.+)$/); if (!matches || matches.length < 3) throw new Error("无效的Base64图片数据"); const mimeType = matches[1], imageData = matches[2], byteCharacters = atob(imageData), byteNumbers = new Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) byteNumbers[i] = byteCharacters.charCodeAt(i); const byteArray = new Uint8Array(byteNumbers), blob = new Blob([byteArray], { type: mimeType }), formData = new FormData(); formData.append("image", blob); const idKey = "jhs_imgurClientId"; let clientId = localStorage.getItem(idKey); if (!clientId) { clientId = window.prompt("请输入您自己的Imgur Client-ID(可前往 https://api.imgur.com/oauth2/addclient 免费申请)用于图片上传搜索:"); if (!clientId) throw new Error("未提供Imgur Client-ID,无法上传图片"); localStorage.setItem(idKey, clientId); } const response = await fetch("https://api.imgur.com/3/image", { method: "POST", headers: { Authorization: `Client-ID ${clientId}` }, body: formData }), data = await response.json(); if (data.success && data.data && data.data.link) return data.data.link; throw new Error((null == (_a2 = data.data) ? void 0 : _a2.error) || "上传到Imgur失败"); })(imageSrc); if (!imgurUrl) { show.error("上传到失败"); return; } imageUrl = imgurUrl; } return imageUrl; } catch (error) { show.error(`搜索失败: ${error.message}`); console.error("搜索失败:", error); } finally { loadObj.close(); } } } class BusNavBarPlugin extends BasePlugin { getName() { return "BusNavBarPlugin"; } handle() { $("#navbar > div > div > span").append('\n \n '); $("#search-img-btn").on("click", (() => { this.getBean("ImageRecognitionPlugin").open(); })); } } class RelatedPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "floorIndex", 1); __publicField2(this, "isInit", false); } getName() { return "RelatedPlugin"; } async showRelated($eleBox, movieId) { const enableLoadRelated = await storageManager.getSetting("enableLoadRelated", NO$1), $magnets = $eleBox; if (movieId) { $magnets.append(`
    相关清单 ${enableLoadRelated === YES$1 ? "折叠" : "展开"} ${enableLoadRelated === YES$1 ? "▲" : "▼"}
    `); $("#relatedFold").on("click", ((event) => { event.preventDefault(); event.stopPropagation(); const $text = $("#relatedFold .toggle-text"), $icon = $("#relatedFold .toggle-icon"), isFolded = "展开" === $text.text(); $text.text(isFolded ? "折叠" : "展开"); $icon.text(isFolded ? "▲" : "▼"); if (isFolded) { $("#relatedContainer").show(); $("#relatedFooter").show(); if (!this.isInit) { this.fetchAndDisplayRelateds(movieId); this.isInit = true; } storageManager.saveSettingItem("enableLoadRelated", YES$1); } else { $("#relatedContainer").hide(); $("#relatedFooter").hide(); storageManager.saveSettingItem("enableLoadRelated", NO$1); } })); $magnets.append('
    '); $magnets.append('
    '); enableLoadRelated === YES$1 && await this.fetchAndDisplayRelateds(movieId); } else show.error("未传入movieId"); } async fetchAndDisplayRelateds(movieId) { const $relatedContainer = $("#relatedContainer"), $relatedFooter = $("#relatedFooter"); $relatedContainer.append('
    获取清单中...
    '); let dataList = null; try { dataList = await javDbApi.related(movieId, 1, 20); } catch (e) { console.error("获取清单失败:", e); } finally { $("#relatedLoading").remove(); } if (dataList) if (0 !== dataList.length) { this.displayRelateds(dataList, $relatedContainer); if (20 === dataList.length) { $relatedFooter.html('\n \n \n '); let currentPage = 1, $loadMoreRelateds = $("#loadMoreRelateds"); $loadMoreRelateds.on("click", (async () => { $loadMoreRelateds.text("加载中...").prop("disabled", true); currentPage++; let moreData; try { moreData = await javDbApi.related(movieId, currentPage, 20); } catch (e) { console.error("加载更多清单失败:", e); } finally { $loadMoreRelateds.text("加载失败, 请点击重试").prop("disabled", false); } if (moreData) { this.displayRelateds(moreData, $relatedContainer); if (moreData.length < 20) { $loadMoreRelateds.remove(); $("#relatedEnd").show(); } else $loadMoreRelateds.text("加载更多清单").prop("disabled", false); } })); } else $relatedFooter.html('
    已加载全部清单
    '); } else $relatedContainer.append('
    无清单
    '); else { $relatedContainer.append('\n
    \n 获取清单失败\n 重试\n
    \n '); $("#retryFetchRelateds").on("click", (async () => { $("#retryFetchRelateds").parent().remove(); await this.fetchAndDisplayRelateds(movieId); })); } } displayRelateds(dataList, $container) { dataList.length && dataList.forEach(((item) => { let commentHtml = `
    #${this.floorIndex++} 创建时间: ${item.createTime}

    ${item.name}

    视频个数: ${item.movieCount}

    收藏次数: ${item.collectionCount} 被查看次数: ${item.viewCount}

    `; $container.append(commentHtml); })); } } class WantAndWatchedVideosPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "type", null); } getName() { return "WantAndWatchedVideosPlugin"; } async handle() { if (window.location.href.includes("/want_watch_videos")) { $("h3").append('导入至 JHS'); $("#wantWatchBtn").on("click", ((event) => { this.type = Status_FAVORITE$1; this.importWantWatchVideos(event, "是否将 想看的影片 导入到 JHS-收藏?"); })); } if (window.location.href.includes("/watched_videos")) { $("h3").append('导入至 JHS'); $("#wantWatchBtn").on("click", ((event) => { this.type = Status_HAS_DOWN$1; this.importWantWatchVideos(event, "是否将 看过的影片 导入到 JHS-已下载?"); })); } } importWantWatchVideos(event, title) { utils.q(null, `${title}
    执行此功能前请记得备份数据`, (async () => { let loadObj = loading(); try { await this.parseMovieList(); } catch (e) { console.error(e); } finally { loadObj.close(); } })); } async parseMovieList($dom) { let movieList, nextPageLink; if ($dom) { movieList = $dom.find(this.getSelector().itemSelector); nextPageLink = $dom.find(".pagination-next").attr("href"); } else { movieList = $(this.getSelector().itemSelector); nextPageLink = $(".pagination-next").attr("href"); } for (const element of movieList) { const item = $(element), href = item.find("a").attr("href"), carNum2 = item.find(".video-title strong").text().trim(), publishTime = item.find(".meta").text().trim(); if (href && carNum2) try { if (await storageManager.getCar(carNum2)) { show.info(`${carNum2} 已存在, 跳过`); continue; } await storageManager.saveCar({ carNum: carNum2, url: href, names: null, actionType: this.type, publishTime }); } catch (error) { console.error(`保存失败 [${carNum2}]:`, error); } } if (nextPageLink) { show.info("发现下一页,正在解析:", nextPageLink); await new Promise(((resolve) => setTimeout(resolve, 1e3))); $.ajax({ url: nextPageLink, method: "GET", success: (html2) => { const parser = new DOMParser(), next$dom = $(parser.parseFromString(html2, "text/html")); this.parseMovieList(next$dom); }, error: function (err) { console.error(err); show.error("加载下一页失败:" + err.message); } }); } else { show.ok("导入结束!"); window.refresh(); } } } function showCopiedFeedback($btn) { const originalText = $btn.text(); $btn.addClass("copied").text("已复制"); setTimeout((() => { $btn.removeClass("copied").text(originalText); }), 2e3); } function fallbackCopy(text, $btn) { const textarea2 = document.createElement("textarea"); textarea2.value = text; textarea2.style.position = "fixed"; document.body.appendChild(textarea2); textarea2.select(); try { document.execCommand("copy"); showCopiedFeedback($btn); } catch (err) { console.error("复制失败:", err); alert("复制失败,请手动复制链接"); } document.body.removeChild(textarea2); } function parseSize(sizeStr) { if (!sizeStr || sizeStr === "未知") return 0; const match = String(sizeStr).replace(/,/g, "").match(/([\d.]+)\s*(TiB|GiB|MiB|KiB|TB|GB|MB|KB|B)?/i); if (!match) return 0; const num = parseFloat(match[1]); const unit = (match[2] || "B").toUpperCase(); const multipliers = { TIB: 1099511627776, TB: 1099511627776, GIB: 1073741824, GB: 1073741824, MIB: 1048576, MB: 1048576, KIB: 1024, KB: 1024, B: 1 }; return Number.isFinite(num) ? num * (multipliers[unit] || 1) : 0; } function parseDate(dateStr) { if (!dateStr || dateStr === "未知") return 0; const ts = Date.parse(dateStr.replace(/\//g, "-")); return isNaN(ts) ? 0 : ts; } function formatMagnetDate(dateStr) { if (!dateStr) return ""; const match = String(dateStr).match(/(\d{4})[-/](\d{1,2})[-/](\d{1,2})/); if (match) return `${match[1]}-${match[2].padStart(2, "0")}-${match[3].padStart(2, "0")}`; const ts = parseDate(dateStr); return ts ? new Date(ts).toISOString().slice(0, 10) : dateStr; } function classifyQuality(title) { const text = String(title || "").replace(/【[^】]*(?:APP|夸克|UC搜|(?:[a-z0-9-]+\.)+(?:com|net|org|cc|sbs|top))[^】]*】/gi, " "); const hasCJK = /[\u4e00-\u9fff]/.test(text); const hasJP = /[\u3040-\u309f\u30a0-\u30ff]/.test(text); const isChinese = /(?:[^A-Za-z]|^)FHDC(?:[^A-Za-z]|$)/i.test(text) || /[-_](?:UC|CH?)(?:[^A-Za-z]|$)/.test(text) || /(?:\u4e2d\u5b57|\u4e2d\u6587|\u5b57\u5e55|\u4e2d\u6587\u5b57\u5e55|\u7e41\u9ad4\u4e2d\u5b57|\u7e41\u4f53\u4e2d\u5b57|\u7e41\u9ad4\u4e2d\u6587|\u7e41\u4f53\u4e2d\u6587|\u7e41\u9ad4\u5b57\u5e55|\u7e41\u4f53\u5b57\u5e55|\u7e41\u4e2d|\u7e41\u5b57|\u81ea\u63d0|\u5f81\u7528|\u5fb5\u7528|\u6f22\u5316|\u6c49\u5316|\u5167\u5d4c|\u5185\u5d4c|\u5167\u5c01|\u5185\u5c01|\u96d9\u8a9e|\u53cc\u8bed)/.test(text) || hasCJK && !hasJP; const is4K = /(?:[^A-Za-z0-9]|^)(?:4K(?:UHD)?|2160P)(?:[^A-Za-z0-9]|$)/i.test(text); const isCracked = /(?:\u7834\u89e3|\u7834\u574f|\u7834\u58de|\u7834\u58ca|\u65e0\u7801|\u7121\u78bc)/.test(text) || /\b(?:uncensored|mosaic)\b/i.test(text); return {isChinese, is4K, isCracked}; } function gmFetch(url, opts = {}) { return new Promise(((resolve) => { _GM_xmlhttpRequest({ method: opts.method || "GET", url, headers: opts.headers || {}, data: opts.data, onload: (r) => { resolve({ loadstuts: r.status >= 200 && r.status < 300, status: r.status, responseText: r.responseText, finalUrl: r.finalUrl || url }); }, onerror: () => resolve({loadstuts: false, status: 0, responseText: "", finalUrl: url}) }); })); } function parseHTML(html2) { const parser = new DOMParser(); return parser.parseFromString(html2, "text/html"); } class MagnetHubPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "currentEngine", null); __publicField2(this, "searchEngines", [ { name: "Sukebei", id: "Sukebei", url: "https://sukebei.nyaa.si/?f=0&c=0_0&q={keyword}", targetPage: "https://sukebei.nyaa.si/?f=0&c=0_0&q={keyword}", parseHtml: this.parseSukebei }, { name: "U9A9", id: "u9a9", url: "https://u9a9.com/?type=2&search={keyword}", targetPage: "https://u9a9.com/?type=2&search={keyword}", parseHtml: this.parseU9A9 }, { name: "U3C3", id: "u3c3", url: "https://u3c3.com/?search={keyword}", targetPage: "https://u3c3.com/?search={keyword}", parseHtml: this.parseU3C3 }, { name: "BTSearch", id: "btsearch", url: "https://btsearch.love/api/search?keyword={keyword}&limit=10&offset=0&sort=size&sort_type=desc", targetPage: "https://btsearch.love/search?keyword={keyword}", parseJson: this.parseBTSearch }, { name: "SoKitty", id: "sokitty", url: "https://w1.sokitty.me/search?key={keyword}", targetPage: "https://w1.sokitty.me/search?key={keyword}", parseHtml: this.parseSokitty }, { name: "ØMagnet", id: "omag", url: "https://xn--mag-zna.net/search?q={keyword}", targetPage: "https://xn--mag-zna.net/search?q={keyword}", parseHtml: this.parseOmag } ]); } getName() { return "MagnetHubPlugin"; } async initCss() { return ` `; } createMagnetHub(keyword) { keyword = keyword.replace("FC2-", ""); const $container = $('
    '); const $tabs = $('
    '); const key = "jhs_magnetHub_selectedEngine"; const savedEngineId = localStorage.getItem(key); let defaultEngineIndex = 0; const $tabBox = $('
    '); this.searchEngines.forEach(((engine, index) => { const $tab = $(`
    ${engine.name}
    `); if (savedEngineId && engine.id === savedEngineId) { $tab.addClass("active"); this.currentEngine = engine; defaultEngineIndex = index; } else if (!savedEngineId && engine.id === "Sukebei") { $tab.addClass("active"); this.currentEngine = engine; } $tabBox.append($tab); })); const sortMode = localStorage.getItem("jhs_magnetSort") || "size"; const $sortSelect = $(``); $tabBox.append($sortSelect); $tabs.append($tabBox); $tabs.append(`原网页`); $container.append($tabs); const $resultsContainer = $('
    '); $container.append($resultsContainer); $container.on("click", ".magnet-tab", ((e) => { const engineId = $(e.target).data("engine"); this.currentEngine = this.searchEngines.find(((engine) => engine.id === engineId)); this.resolveUrl(this.currentEngine, keyword).then(((url) => { $("#targetBox").attr("href", url); })); localStorage.setItem(key, engineId); $container.find(".magnet-tab").removeClass("active"); $(e.target).addClass("active"); this.searchEngine($resultsContainer, $sortSelect, this.currentEngine, keyword); })); $sortSelect.on("change", () => { localStorage.setItem("jhs_magnetSort", $sortSelect.val()); RerenderSort($resultsContainer, $sortSelect.val()); }); $container.on("click", ".copy-btn", function () { const $btn = $(this), magnet = $btn.data("magnet"); const dnCode = $btn.data("dncode") || ""; const magWithDn = magnet + (dnCode ? `&dn=${encodeURIComponent(dnCode)}` : ""); navigator.clipboard ? navigator.clipboard.writeText(magWithDn).then((() => { showCopiedFeedback($btn); })).catch((() => { fallbackCopy(magWithDn, $btn); })) : fallbackCopy(magWithDn, $btn); }); $container.on("click", ".down-115", (async (event) => { event.stopPropagation(); const magnet = $(event.currentTarget).data("magnet"); let loadObj = loading(); try { await this.getBean("WangPan115TaskPlugin").handleAddTask(magnet); } catch (e) { show.error("发生错误:" + e); console.error(e); } finally { loadObj.close(); } })); $container.on("click", ".check-btn", (async (event) => { event.stopPropagation(); const magnet = $(event.currentTarget).data("magnet"); this.checkWhatslink(magnet); })); this.searchEngine($resultsContainer, $sortSelect, this.currentEngine || this.searchEngines[defaultEngineIndex], keyword); this.resolveUrl(this.currentEngine || this.searchEngines[defaultEngineIndex], keyword).then(((url) => { $("#targetBox").attr("href", url); })); return $container; } searchEngine($container, $sortSelect, engine, keyword) { this.currentEngine = engine; this.currentKeyword = keyword; $container.html(`
    正在从 ${engine.name} 搜索 "${keyword}"...
    `); const cacheKey = `${engine.name}_${keyword}`; sessionStorage.getItem(cacheKey); this.resolveUrl(engine, keyword).then(((url) => { const requestOpts = {method: "GET", url}; if (engine.parseHtml) { _GM_xmlhttpRequest(Object.assign(requestOpts, { method: "GET", url, onload: async (response) => { try { const results = await engine.parseHtml.call(this, response.responseText, keyword); results.length > 0 && sessionStorage.setItem(cacheKey, JSON.stringify(results)); $container.data("_magnetData", results); $sortSelect.prop("disabled", results.length === 0); this.displayResults($container, results, engine.name); } catch (e) { this.showError($container, `解析 ${engine.name} 结果失败: ${e.message}`); } }, onerror: (error) => { this.showError($container, `从 ${engine.name} 获取数据失败: ${error.statusText}`); } })); } if (engine.parseJson) { engine.parseJson.call(this, $container, $sortSelect, engine, keyword, cacheKey); } })); } showError($container, message) { $container.html(`
    ${message} 🔄 刷新
    `); const $select = $container.closest(".magnet-container").find(".magnet-sort-select"); $container.find("#retrySearch").on("click", ((e) => { e.preventDefault(); this.searchEngine($container, $select, this.currentEngine, this.currentKeyword); })); } displayResults($container, results, engineName) { $container.empty(); const sortMode = localStorage.getItem("jhs_magnetSort") || "size"; if (0 !== results.length) { results = this.sortMagnetData(results, sortMode); const $table = $(`
    名称 大小 日期 操作
    `); const $tbody = $table.find("tbody"); results.forEach(((result) => { const quality = classifyQuality(result.title); let badges = ""; if (quality.is4K) badges += '4K'; if (quality.isChinese) badges += '中字'; if (quality.isCracked) badges += '破解'; const displayDate = formatMagnetDate(result.date); const dnCode = extractCode(result.title); const $row = $(`
    ${badges} ${result.title}
    ${result.size || "未知"} ${displayDate || "未知"} `); $tbody.append($row); })); $container.append($table); } else { $container.append(`
    无搜索结果 🔄 刷新
    `); $("#retrySearch").on("click", ((e) => { e.preventDefault(); this.searchEngine($container, $container.closest(".magnet-container").find(".magnet-sort-select"), this.currentEngine, this.currentKeyword); })); } } sortMagnetData(data, mode) { return [...data].sort((a, b) => { const sizeDelta = parseSize(b.size) - parseSize(a.size); if (mode === "size") return sizeDelta; const aTime = parseDate(a.date); const bTime = parseDate(b.date); if (!aTime && !bTime) return sizeDelta; if (!aTime) return 1; if (!bTime) return -1; const timeDelta = mode === "oldest" ? aTime - bTime : bTime - aTime; return timeDelta || sizeDelta; }); } async resolveUrl(engine, keyword) { return engine.url.replace("{keyword}", encodeURIComponent(keyword)); } async checkWhatslink(magnet) { var _a2; (_a2 = document.querySelector(".whatslink-overlay")) == null ? void 0 : _a2.remove(); const overlay = document.createElement("div"); overlay.className = "whatslink-overlay"; overlay.innerHTML = ''; document.body.appendChild(overlay); try { const url = `https://whatslink.info/api/v1/link?url=${encodeURIComponent(magnet)}`; const r = await gmFetch(url, {timeout: 2e4}); if (!r.loadstuts) throw new Error("WhatsLink 请求失败"); const data = JSON.parse(r.responseText || "{}"); overlay.remove(); this.showWhatslinkModal(data, magnet); } catch (e) { overlay.remove(); this.showWhatslinkModal({ error: e.message || "查询失败", name: "查询失败", type: "-", file_type: "-", size: 0, count: "-", screenshots: [] }, magnet); } } showWhatslinkModal(payload, magnet) { var _a2; (_a2 = document.querySelector(".whatslink-overlay")) == null ? void 0 : _a2.remove(); const shots = Array.isArray(payload == null ? void 0 : payload.screenshots) ? payload.screenshots.map((item) => item == null ? void 0 : item.screenshot).filter(Boolean) : []; let index = 0; const resourceName = (payload == null ? void 0 : payload.name) || "未知资源"; const resourceCount = (payload == null ? void 0 : payload.count) ?? "-"; const overlay = document.createElement("div"); overlay.className = "whatslink-overlay"; const modal = document.createElement("section"); modal.className = `whatslink-modal${shots.length ? "" : " no-shots"}`; modal.innerHTML = ``; overlay.appendChild(modal); document.body.appendChild(overlay); modal.querySelector(".whatslink-gallery-name").textContent = resourceName; modal.querySelector(".whatslink-gallery-count").textContent = `${resourceCount} 个文件`; const visual = modal.querySelector(".whatslink-gallery-visual"); const hero = modal.querySelector(".whatslink-gallery-hero"); const thumbs = modal.querySelector(".whatslink-gallery-thumbs"); const currentIndex = modal.querySelector(".whatslink-gallery-index"); const closeButton = modal.querySelector(".whatslink-gallery-close"); const prevButton = modal.querySelector(".whatslink-gallery-prev"); const nextButton = modal.querySelector(".whatslink-gallery-next"); const sizeGallery = () => { if (!shots.length || !hero.naturalWidth || !hero.naturalHeight) return; const infoHeight = modal.querySelector(".whatslink-gallery-info").offsetHeight || 23; const thumbsHeight = thumbs.offsetHeight || 58; const maxHeight = Math.max(180, window.innerHeight - 36 - infoHeight - thumbsHeight - 6); const maxWidth = modal.clientWidth; const ratio = hero.naturalWidth / hero.naturalHeight; const height = Math.min(maxHeight, maxWidth / ratio); const width = height * ratio; visual.style.width = `${width}px`; visual.style.height = `${height}px`; }; const positionControls = () => { if (!shots.length || !hero.naturalWidth || !hero.naturalHeight) return; const width = visual.clientWidth; const height = visual.clientHeight; const scale = Math.min(width / hero.naturalWidth, height / hero.naturalHeight); const imageWidth = hero.naturalWidth * scale; const imageHeight = hero.naturalHeight * scale; const imageLeft = (width - imageWidth) / 2; const imageTop = (height - imageHeight) / 2; const closeSize = closeButton.offsetWidth || 32; const arrowSize = prevButton.offsetWidth || 34; const sideGap = 12; closeButton.style.left = `${imageLeft + imageWidth - closeSize - sideGap}px`; closeButton.style.right = "auto"; closeButton.style.top = `${imageTop + sideGap}px`; prevButton.style.left = `${imageLeft + sideGap}px`; prevButton.style.right = "auto"; prevButton.style.top = `${imageTop + (imageHeight - arrowSize) / 2}px`; nextButton.style.left = "auto"; nextButton.style.right = `${width - imageLeft - imageWidth + sideGap}px`; nextButton.style.top = `${imageTop + (imageHeight - arrowSize) / 2}px`; }; const render = () => { modal.classList.toggle("has-shots", Boolean(shots.length)); if (!shots.length) { currentIndex.textContent = "NO PREVIEW"; return; } hero.src = shots[index]; const frame = `${index + 1} / ${shots.length}`; currentIndex.textContent = frame; [...thumbs.children].forEach((btn, i) => btn.classList.toggle("active", i === index)); if (hero.complete) { sizeGallery(); positionControls(); } }; shots.forEach((url, i) => { const btn = document.createElement("button"); btn.type = "button"; btn.className = "whatslink-gallery-thumb"; btn.innerHTML = `截图${i + 1}`; btn.addEventListener("click", () => { index = i; render(); }); thumbs.appendChild(btn); }); prevButton.addEventListener("click", () => { if (!shots.length) return; index = (index + shots.length - 1) % shots.length; render(); }); nextButton.addEventListener("click", () => { if (!shots.length) return; index = (index + 1) % shots.length; render(); }); const onKeydown = (event) => { if (!document.body.contains(overlay)) return; if (event.key === "ArrowLeft" && shots.length) { index = (index + shots.length - 1) % shots.length; render(); } if (event.key === "ArrowRight" && shots.length) { index = (index + 1) % shots.length; render(); } if (event.key === "Escape") close(); }; const onResize = () => { sizeGallery(); positionControls(); }; const removeResize = () => window.removeEventListener("resize", onResize); const close = () => { overlay.remove(); document.removeEventListener("keydown", onKeydown); removeResize(); }; closeButton.addEventListener("click", close); overlay.addEventListener("click", (e) => { if (e.target === overlay) close(); }); hero.addEventListener("load", () => { sizeGallery(); positionControls(); }); window.addEventListener("resize", onResize); document.addEventListener("keydown", onKeydown); render(); } parseU9A9(html2, keyword) { const $dom = utils.htmlTo$dom(html2), results = []; const kwNorm = String(keyword).toUpperCase().replace(/[^A-Z0-9]/g, ""); $dom.find(".torrent-list tbody tr").each(((i, el) => { const $el = $(el); if ($el.text().includes("置顶")) return; const $titleA = $el.find("td:nth-child(2) a").first(); const title = $titleA.attr("title") || $titleA.text().trim(); if (!title) return; if (kwNorm) { const titleNorm = title.toUpperCase().replace(/[^A-Z0-9]/g, ""); if (!titleNorm.includes(kwNorm)) return; } const magnet = $el.find("td:nth-child(3) a[href^='magnet:']").attr("href"); const size = $el.find("td:nth-child(4)").text().trim(); const date2 = $el.find("td:nth-child(5)").text().trim(); magnet && results.push({title, magnet, size, date: date2}); })); return results; } async parseU3C3(_html, keyword) { var _a2; const base = "https://www.u3c3.com"; const referer = base + "/"; const home = await gmFetch(base + "/", { headers: {Referer: referer} }); if (!home.loadstuts) return []; const homeDoc = parseHTML(home.responseText); const searchScript = [...homeDoc.scripts].map((script) => script.textContent || "").find((text) => text.includes("function search21")) || ""; const token = ((_a2 = searchScript.match(/^\s*var\s+nmefafej\s*=\s*["']([^"']+)["'];?/m)) == null ? void 0 : _a2[1]) || ""; if (!token) return []; const searchUrl = `${base}/?search2=${encodeURIComponent(token)}&search=${encodeURIComponent(keyword)}`; const r = await gmFetch(searchUrl, { headers: {Referer: referer} }); if (!r.loadstuts) return []; const doc = parseHTML(r.responseText); const normalizedKeyword = String(keyword || "").toUpperCase().replace(/[-_\s]/g, ""); const data = [...doc.querySelectorAll("table.torrent-list tbody tr.default, table.torrent-list tbody tr.success")].map((row) => { var _a3, _b, _c, _d, _e, _f; const titleA = row.querySelector('td:nth-child(2) a[href*="/view?id="]'); const magnetA = row.querySelector('td:nth-child(3) a[href^="magnet:"]'); const title = ((_a3 = titleA == null ? void 0 : titleA.getAttribute("title")) == null ? void 0 : _a3.trim()) || ((_b = titleA == null ? void 0 : titleA.textContent) == null ? void 0 : _b.trim()) || ""; if (!title || !(magnetA == null ? void 0 : magnetA.href)) return null; if (normalizedKeyword && !title.toUpperCase().replace(/[-_\s]/g, "").includes(normalizedKeyword)) return null; const href = titleA.getAttribute("href") || ""; return { title, magnet: magnetA.href, size: ((_d = (_c = row.querySelector("td:nth-child(4)")) == null ? void 0 : _c.textContent) == null ? void 0 : _d.trim()) || "", date: ((_f = (_e = row.querySelector("td:nth-child(5)")) == null ? void 0 : _e.textContent) == null ? void 0 : _f.trim()) || "", src: href ? new URL(href, base).href : searchUrl }; }).filter(Boolean); return data; } parseSukebei(html2, keyword) { const $dom = utils.htmlTo$dom(html2), results = []; $dom.find("tr.default, tr.success").each(((i, el) => { const $el = $(el); const title = $el.find("td:nth-child(2) a").first().attr("title") || $el.find("td:nth-child(2) a").first().text().trim(); if (!title) return; const magnet = $el.find("td:nth-child(3) a:last-child").attr("href"); const size = $el.find("td:nth-child(4)").text().trim(); const date2 = $el.find("td:nth-child(5)").text().trim(); magnet && results.push({title, magnet, size, date: date2}); })); return results; } async parseOmag(html2, keyword) { const doc = parseHTML(html2); const base = "https://xn--mag-zna.net"; const entries = [...doc.querySelectorAll("tr")].map((row) => { const titleA = row.querySelector('td.result-title a[href^="/!"]'); if (!titleA) return null; const href = titleA.getAttribute("href") || ""; if (!href) return null; let src; try { src = new URL(href, base).href; } catch (_) { return null; } const title = titleA.textContent.trim(); const meta = [...row.querySelectorAll(".result-meta > div")].map((item) => item.textContent.trim()); return {title, size: meta[0] || "", date: meta[1] || "", src}; }).filter(Boolean); const searchUrl = `https://xn--mag-zna.net/search?q=${encodeURIComponent(keyword)}`; const results = []; for (const entry of entries) { try { const r = await gmFetch(entry.src, { headers: {Referer: searchUrl} }); if (!r.loadstuts) continue; const detailDoc = parseHTML(r.responseText); const magnetInput = detailDoc.querySelector("#input-magnet"); const magnetA = detailDoc.querySelector('a[href^="magnet:"]'); const maglink = ((magnetInput == null ? void 0 : magnetInput.getAttribute("value")) || (magnetA == null ? void 0 : magnetA.getAttribute("href")) || (magnetA == null ? void 0 : magnetA.href) || "").trim(); if (!maglink || !/^magnet:\?xt=urn:btih:/i.test(maglink)) continue; results.push({title: entry.title, magnet: maglink, size: entry.size, date: entry.date}); } catch (_) { } } return results; } parseBTSearch($container, $sortSelect, engine, keyword, cacheKey) { const _this = this; const apiUrl2 = engine.url.replace("{keyword}", encodeURIComponent(keyword)); const timestamp = Math.floor(Date.now() / 1e3).toString(); const nonce = randomString(8); const parts = [`timestamp=${timestamp}`, `nonce=${nonce}`, `keyword=${keyword}`, `limit=10`, `offset=0`, `sort=size`, `sort_type=desc`]; const signText = `${parts.sort().join("&")}&key=long2ice`; const sign = window.md5 ? window.md5(signText).toUpperCase() : ""; _GM_xmlhttpRequest({ method: "GET", url: apiUrl2, headers: { Accept: "application/json", Referer: engine.targetPage.replace("{keyword}", encodeURIComponent(keyword)), "x-timestamp": timestamp, "x-nonce": nonce, "x-sign": sign }, onload: (response) => { var _a2; try { const json2 = JSON.parse(response.responseText); const items = Array.isArray(json2 == null ? void 0 : json2.data) ? json2.data : Array.isArray((_a2 = json2 == null ? void 0 : json2.data) == null ? void 0 : _a2.data) ? json2.data.data : []; const results = items.map((item) => { const title = String((item == null ? void 0 : item.name) || "").replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim(); const hash = String((item == null ? void 0 : item.hash) || "").replace(/^magnet:\?xt=urn:btih:/i, "").replace(/[^a-z0-9]/gi, ""); if (!/^[a-f0-9]{32,40}$/i.test(hash)) return null; const size = formatBytes(item == null ? void 0 : item.size); const date2 = (item == null ? void 0 : item.created_at) || (item == null ? void 0 : item.created_time) || (item == null ? void 0 : item.create_time) || ""; return { title: title || `magnet:?xt=urn:btih:${hash}`, magnet: `magnet:?xt=urn:btih:${hash}`, size, date: date2 }; }).filter(Boolean); results.length > 0 && sessionStorage.setItem(cacheKey, JSON.stringify(results)); $container.data("_magnetData", results); $sortSelect.prop("disabled", results.length === 0); _this.displayResults($container, results, engine.name); } catch (e) { _this.showError($container, `解析 ${engine.name} 结果失败: ${e.message}`); } }, onerror: (error) => { _this.showError($container, `从 ${engine.name} 获取数据失败: ${error.statusText}`); } }); } parseSokitty(html2, keyword) { const doc = parseHTML(html2); const kwNorm = String(keyword).toUpperCase().replace(/[-_\s]/g, ""); const results = []; doc.querySelectorAll(".panel.search-panel").forEach((panel) => { var _a2, _b, _c, _d; const titleA = panel.querySelector("h3.panel-title > a.list-title"); if (!titleA) return; const href = titleA.getAttribute("href") || ""; if (!href.startsWith("/bt/")) return; const hash = href.replace("/bt/", ""); if (!hash) return; const title = titleA.textContent.trim(); if (!title.toUpperCase().replace(/[-_\s]/g, "").includes(kwNorm)) return; const maglink = `magnet:?xt=urn:btih:${hash}`; const infoItems = [...panel.querySelectorAll(".panel-footer .info-item")]; const size = ((_b = (_a2 = infoItems[0]) == null ? void 0 : _a2.textContent) == null ? void 0 : _b.trim()) || ""; const date2 = ((_d = (_c = infoItems[2]) == null ? void 0 : _c.textContent) == null ? void 0 : _d.trim()) || ""; results.push({title, magnet: maglink, size, date: date2}); }); return results; } } function formatBytes(bytes) { if (!bytes || bytes <= 0) return ""; const units = ["B", "KB", "MB", "GB", "TB"]; let i = 0; let size = bytes; while (size >= 1024 && i < units.length - 1) { size /= 1024; i++; } return size.toFixed(1) + " " + units[i]; } function extractCode(text) { if (!text) return null; const patterns = [ /FC2[-\s_]?(?:PPV)?[-\s_]?(\d{6,9})/i, /([A-Z]{2,15})-(\d{2,10})(?:-(\d+))?/i, /([A-Z]{2,15})-([A-Z]{0,2}\d{2,10})/i, /^[A-Z0-9]+[-_](\d{6}[-_]\d{2,3})/i, /(\d{6}[-_]\d{2,3})[-_][A-Z0-9]+$/i, new RegExp("(? { const sizeDelta = parseSize(b.size) - parseSize(a.size); if (sortMode === "size") return sizeDelta; const aTime = parseDate(a.date), bTime = parseDate(b.date); if (!aTime && !bTime) return sizeDelta; if (!aTime) return 1; if (!bTime) return -1; const timeDelta = sortMode === "oldest" ? aTime - bTime : bTime - aTime; return timeDelta || sizeDelta; }); $container.empty(); const $table = $(`
    名称 大小 日期 操作
    `); const $tbody = $table.find("tbody"); results.forEach(((result) => { const quality = classifyQuality(result.title); let badges = ""; if (quality.is4K) badges += '4K'; if (quality.isChinese) badges += '中字'; if (quality.isCracked) badges += '破解'; const displayDate = formatMagnetDate(result.date); const dnCode = extractCode(result.title); const $row = $(`
    ${badges} ${result.title}
    ${result.size || "未知"} ${displayDate || "未知"} `); $tbody.append($row); })); $container.append($table); } const Thumbnail = { sources: ["javfree", "projectjav", "javstore"], cacheKey(code) { return `thumb_cache_v3_${code}`; }, lookupCode(code) { const text = String(code || "").trim(); const fc2 = text.match(/^(?:FC2[-_\s]?(?:PPV[-_\s]?)?)?(\d{6,9})$/i); return fc2 ? fc2[1] : text; }, sourceOrder() { const savedOrder = ["javfree", "projectjav", "javstore"]; const seen2 = /* @__PURE__ */ new Set(); return [...savedOrder, ...this.sources].filter((src) => { if (seen2.has(src) || typeof this[src] !== "function") return false; seen2.add(src); return true; }); }, async fetchFromSource(source, code) { try { return await this[source](this.lookupCode(code)); } catch (e) { clog.debug(`Thumbnail[${source}] 异常:`, e.message); return null; } }, normalizeForCompare(text) { return String(text || "").toLowerCase().replace(/[^a-z0-9]/g, ""); }, isCodeMatched(text, code) { const normalizedText = this.normalizeForCompare(text); const normalizedCode = this.normalizeForCompare(code); return !!normalizedCode && normalizedText.includes(normalizedCode); }, isDetailMatched(doc, url, code) { var _a2, _b; const title = ((_a2 = doc == null ? void 0 : doc.querySelector("title")) == null ? void 0 : _a2.textContent) || ""; const headings = [...(doc == null ? void 0 : doc.querySelectorAll("h1,h2,h3,.entry-title,.movie-title,.post-title")) || []].map((el) => el.textContent || "").join(" "); const bodyText = (((_b = doc == null ? void 0 : doc.body) == null ? void 0 : _b.textContent) || "").slice(0, 5e3); return this.isCodeMatched([url, title, headings, bodyText].join(" "), code); }, normalizePreviewUrl(url, baseUrl = "") { if (!url) return ""; const absolute = /^https?:\/\//i.test(url) ? url : baseUrl ? new URL(url, baseUrl).href : url; return absolute.replace(/^http:/, "https:"); }, isJavfreePreviewImage(url, code) { const cleanUrl = String(url || "").split("?")[0]; const lookupCode = this.lookupCode(code); const isFc2Numeric = /^\d{6,9}$/.test(lookupCode); const fc2ShotPattern = isFc2Numeric ? new RegExp(`${lookupCode}_\\d+\\.(?:jpe?g|png|webp)$`, "i") : null; return this.isCodeMatched(cleanUrl, code) && (/-(?:1080p|demosaic)\.(?:jpe?g|png|webp)$/i.test(cleanUrl) || isFc2Numeric && fc2ShotPattern.test(cleanUrl)); }, selectJavfreePreviewUrl(doc, detailUrl, code) { const urls = [...doc.querySelectorAll("p > img[src]")].map((img) => this.normalizePreviewUrl(img.getAttribute("src") || img.src || "", detailUrl)).filter((url) => this.isJavfreePreviewImage(url, code)); return urls.find((url) => /-1080p\./i.test(url)) || urls.find((url) => /-demosaic\./i.test(url)) || urls.find((url) => /_1\.(?:jpe?g|png|webp)$/i.test(url)) || ""; }, async javfree(code) { var _a2; code = this.lookupCode(code); const cacheKey = this.cacheKey(code); const cached = sessionStorage.getItem(cacheKey); if (cached) return cached; try { const html2 = await gmHttp.get(`https://javfree.me/search/${code}`); const doc = new DOMParser().parseFromString(html2, "text/html"); const link2 = (_a2 = [...doc.querySelectorAll(".entry-title>a")].find((a) => this.isCodeMatched([a.href, a.textContent].join(" "), code))) == null ? void 0 : _a2.href; if (!link2) return null; const dHtml = await gmHttp.get(link2); const dDoc = new DOMParser().parseFromString(dHtml, "text/html"); if (!this.isDetailMatched(dDoc, link2, code)) return null; const url = this.selectJavfreePreviewUrl(dDoc, link2, code); if (url) { sessionStorage.setItem(cacheKey, url); return url; } return null; } catch { return null; } }, async javstore(code) { code = this.lookupCode(code); try { const normalizedCode = code.replace(/^fc2-?/i, "").replace(/-/g, "").toLowerCase(); const searchUrl = `https://javstore.net/search?q=${encodeURIComponent(code)}`; const searchHtml = await gmHttp.get(searchUrl); const searchDoc = new DOMParser().parseFromString(searchHtml, "text/html"); const candidateLinks = searchDoc.querySelectorAll('a[href*="/"]'); const detailUrls = []; for (const link2 of candidateLinks) { const href = link2.getAttribute("href"); if (!href) continue; if (href.startsWith("http") && !href.includes("javstore.net")) continue; const urlObj = new URL(href, searchUrl); if (!/javstore\.net$/i.test(urlObj.hostname)) continue; if (/^\/search(?:[/?#]|$)/i.test(urlObj.pathname)) continue; const fullUrl = urlObj.href; const pathLastPart = decodeURIComponent(urlObj.pathname.split("/").pop() || ""); const normalizedPath = pathLastPart.toLowerCase().replace(/-/g, ""); const looksLikeDetail = /\.html$/i.test(urlObj.pathname) || /^\/\d+[-/]/.test(urlObj.pathname); if (looksLikeDetail && normalizedPath.includes(normalizedCode) && !detailUrls.includes(fullUrl)) { detailUrls.push(fullUrl); } } if (detailUrls.length === 0) return null; for (const detailUrl of detailUrls) { const imgUrl = await this._extractImgFromDetail(detailUrl, code); if (imgUrl) return imgUrl; } return null; } catch (e) { clog.debug("javstore 获取失败", e); return null; } }, async _extractImgFromDetail(detailUrl, code) { try { const detailHtml = await gmHttp.get(detailUrl); const detailDoc = new DOMParser().parseFromString(detailHtml, "text/html"); if (!this.isDetailMatched(detailDoc, detailUrl, code)) return null; for (const link2 of detailDoc.querySelectorAll("a")) { if (link2.textContent.includes("CLICK HERE")) { const imgUrl = link2.href || link2.getAttribute("href") || ""; if (imgUrl) return this.normalizePreviewUrl(imgUrl, detailUrl); } } const img = detailDoc.querySelector('img[src*="_s.jpg"]'); if (img) { let src = img.getAttribute("src") || ""; if (!src.startsWith("http")) src = new URL(src, detailUrl).href; return this.normalizePreviewUrl(src.replace(/_s\.jpg$/, "_l.jpg"), detailUrl); } return null; } catch (e) { clog.debug("javstore: 详情页请求失败", detailUrl, e.message); return null; } }, async projectjav(code) { var _a2; code = this.lookupCode(code); try { const request = (url) => gmHttp.get(url, {}, { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8" }); const searchUrl = `https://projectjav.com/?searchTerm=${encodeURIComponent(code)}`; const searchHtml = await request(searchUrl); const searchDoc = new DOMParser().parseFromString(searchHtml, "text/html"); let detailUrl = ""; const allMovieLinks = [...searchDoc.querySelectorAll('a[href*="/movie/"]')]; const firstLink = ((_a2 = allMovieLinks[0]) == null ? void 0 : _a2.getAttribute("href")) || ""; if (firstLink) { detailUrl = firstLink.startsWith("http") ? firstLink : `https://projectjav.com${firstLink}`; } if (!detailUrl) return null; const detailHtml = await request(detailUrl); const detailDoc = new DOMParser().parseFromString(detailHtml, "text/html"); const screenshotLink = [...detailDoc.querySelectorAll('.col-md-12.thumbnail a[data-featherlight="image"], .thumbnail a[data-featherlight="image"]')].find((a) => { var _a3; return this.isCodeMatched([a.outerHTML, (_a3 = a.closest(".thumbnail")) == null ? void 0 : _a3.outerHTML, detailUrl].join(" "), code); }); if (screenshotLink) { const thumbImg = screenshotLink.querySelector("img"); const href = screenshotLink.getAttribute("href") || ""; if (href) return this.normalizePreviewUrl(href, detailUrl); if (thumbImg) { const src = (thumbImg.getAttribute("src") || "").replace(/\?.*$/, ""); if (src) return this.normalizePreviewUrl(src, detailUrl); } } return null; } catch (e) { clog.debug("[projectjav] 异常:", e.message); return null; } }, async get(code) { const cacheKey = this.cacheKey(code); const cached = sessionStorage.getItem(cacheKey); if (cached) return {url: cached, source: null}; let url = null, source = null; for (const src of this.sourceOrder()) { url = await this.fetchFromSource(src, code); if (url) { source = src; break; } } if (url) sessionStorage.setItem(cacheKey, url); return {url, source}; } }; let previewOverlay = null; function showPreviewOverlay$1(imgUrl, code, source = null) { if (previewOverlay) { previewOverlay.close(); } let currentBlobUrl = null; const originalHtmlOverflow = document.documentElement.style.overflow; const originalBodyOverflow = document.body.style.overflow; document.documentElement.style.overflow = "hidden"; document.body.style.overflow = "hidden"; const container = document.createElement("div"); container.className = "jhs-preview-overlay"; const img = document.createElement("img"); img.src = imgUrl; img.className = "jhs-preview-img"; img.onclick = (e) => { e.stopPropagation(); img.classList.toggle("zoomed"); }; img.onerror = () => { img.src = imgUrl; }; const toolbar = document.createElement("div"); toolbar.className = "jhs-preview-toolbar"; const createButton = (text, className, onClick) => { const btn = document.createElement("button"); btn.className = `jhs-preview-btn ${className}`; btn.textContent = text; btn.onclick = onClick; return btn; }; const setActiveSource = (activeSource) => { toolbar.querySelectorAll(".jhs-preview-btn").forEach((b) => b.classList.remove("active")); const activeBtn = toolbar.querySelector(`.jhs-preview-btn-${activeSource}`); if (activeBtn) activeBtn.classList.add("active"); }; const changeSource = async (src, srcName) => { const loadingOverlay = document.createElement("div"); loadingOverlay.className = "jhs-loading-overlay"; loadingOverlay.innerHTML = '
    正在加载预览图...
    '; document.body.appendChild(loadingOverlay); try { const newUrl = src === "javfree" ? await Thumbnail.javfree(code) : src === "projectjav" ? await Thumbnail.projectjav(code) : await Thumbnail.javstore(code); if (newUrl) { if (currentBlobUrl) { URL.revokeObjectURL(currentBlobUrl); currentBlobUrl = null; } img.src = newUrl; setActiveSource(src); } else { showToast(`${srcName} 未找到预览图`, "error"); } } catch (err) { showToast(`${srcName} 加载失败: ${err.message}`, "error"); } finally { loadingOverlay.remove(); } }; let toastTimer = null; const showToast = (msg, type = "info") => { const existing = container.querySelector(".jhs-preview-toast"); if (existing) existing.remove(); if (toastTimer) clearTimeout(toastTimer); const toast = document.createElement("div"); toast.className = `jhs-preview-toast jhs-preview-toast-${type}`; toast.textContent = msg; container.appendChild(toast); requestAnimationFrame(() => toast.classList.add("show")); toastTimer = setTimeout(() => { toast.classList.remove("show"); setTimeout(() => toast.remove(), 300); }, 3e3); }; const javfreeBtn = createButton("javfree", "jhs-preview-btn-javfree javfree", (e) => { e.stopPropagation(); changeSource("javfree", "javfree"); }); const projectjavBtn = createButton("projectjav", "jhs-preview-btn-projectjav projectjav", (e) => { e.stopPropagation(); changeSource("projectjav", "projectjav"); }); const javstoreBtn = createButton("javstore", "jhs-preview-btn-javstore javstore", (e) => { e.stopPropagation(); changeSource("javstore", "javstore"); }); const newWindowBtn = createButton("新窗口打开", "jhs-preview-btn-action action", (e) => { e.stopPropagation(); window.open(img.src); }); const downloadBtn = createButton("下载", "jhs-preview-btn-action action", (e) => { e.stopPropagation(); const a = document.createElement("a"); a.href = img.src; a.download = `${code}.jpg`; a.click(); }); toolbar.appendChild(javfreeBtn); toolbar.appendChild(projectjavBtn); toolbar.appendChild(javstoreBtn); toolbar.appendChild(newWindowBtn); toolbar.appendChild(downloadBtn); if (source === "javfree") setActiveSource("javfree"); else if (source === "projectjav") setActiveSource("projectjav"); else if (source === "javstore") setActiveSource("javstore"); else setActiveSource("javfree"); container.appendChild(img); const closeOverlay = () => { if (container.parentNode) { container.remove(); toolbar.remove(); document.documentElement.style.overflow = originalHtmlOverflow; document.body.style.overflow = originalBodyOverflow; if (currentBlobUrl) { URL.revokeObjectURL(currentBlobUrl); currentBlobUrl = null; } } }; container.onclick = closeOverlay; const escHandler = (e) => { if (e.key === "Escape") { closeOverlay(); document.removeEventListener("keydown", escHandler); } }; document.addEventListener("keydown", escHandler); document.body.appendChild(container); document.body.appendChild(toolbar); previewOverlay = {container, toolbar, close: closeOverlay}; } class ScreenShotPlugin extends BasePlugin { getName() { return "ScreenShotPlugin"; } async handle() { this.loadScreenShot().then(); } async loadScreenShot() { if (!window.isDetailPage) return; if ("yes" !== await storageManager.getSetting("enableLoadScreenShot", "yes")) return; let carNum2 = this.getPageInfo().carNum; isJavDb$1 && $(".preview-images .tile-item").first().before( '
    正在加载缩略图
    ' ); isJavBus$1 && $("#sample-waterfall .sample-box:first").after( '
    正在加载缩略图
    ' ); try { const result = await Thumbnail.get(carNum2); if (result.url) { this.addImg("缩略图", result.url, carNum2, result.source); clog.log("加载缩略图:", result.url, result.source); } else { this.showErrorFallback(carNum2, new Error("所有来源均未找到预览图")); } } catch (e) { this.showErrorFallback(carNum2, e); } } async getScreenshot(carNum2) { const result = await Thumbnail.get(carNum2); if (result.url) return result.url; throw new Error("未找到预览图"); } async getJavStoreScreenShot(carNum2) { return await Thumbnail.javstore(carNum2); } async getJavFreeScreenShot(carNum2) { return await Thumbnail.javfree(carNum2); } addImg(title, imgUrl, code, source) { if (!imgUrl) return; isJavDb$1 && $(".screen-container").html( `${title}` ); isJavBus$1 && $(".screen-container").html( `
    ${title}
    ` ); $(".screen-container").on("click", (event) => { event.stopPropagation(); event.preventDefault(); showPreviewOverlay$1(imgUrl, code || title, source); }); } showErrorFallback(carNum2, error) { var _a2, _b; console.error("获取缩略图失败:", ((_b = (_a2 = error == null ? void 0 : error.message) == null ? void 0 : _a2.substring) == null ? void 0 : _b.call(_a2, 0, 100)) || error); let differentCss = isJavBus$1 ? "margin-top:30px" : "margin-top:50px"; $(".screen-container").html( `
    获取缩略图失败

    点击重试 或 前往确认` ).off("click", ".retry-link").off("click", ".check-link").on("click", ".retry-link", async (e) => { e.stopPropagation(); e.preventDefault(); $(".screen-container").html( `
    正在重新加载...
    ` ); try { const result = await Thumbnail.get(carNum2); if (result.url) { this.addImg("缩略图", result.url, carNum2, result.source); } else { this.showErrorFallback(carNum2, new Error("所有来源均未找到预览图")); } } catch (err) { this.showErrorFallback(carNum2, err); } }).on("click", ".check-link", (e) => { e.stopPropagation(); e.preventDefault(); window.open(`https://javstore.net/search?q=${encodeURIComponent(carNum2)}`, "_blank"); }); } } window.showPreviewOverlay = showPreviewOverlay$1; const getDownPathList = async () => { const res = await gmHttp.get("https://webapi.115.com/offine/downpath"); return "object" == typeof res ? res.data : null; }; const searchFiles = async (search_value, offset = 0, limit = 30, params = {}) => { const queryParams = new URLSearchParams({search_value, offset, limit, ...params}); const url = `https://webapi.115.com/files/search?${queryParams}`; return await gmHttp.get(url); }; class WangPan115TaskPlugin extends BasePlugin { getName() { return "WangPan115TaskPlugin"; } async handle() { $(".buttons button[data-clipboard-text*='magnet:']").each(((i, el) => { const $copyBtn = $(el); const magnet = $copyBtn.attr("data-clipboard-text"); $copyBtn.css("margin-right", parseInt($copyBtn.css("margin-right")) - 4 + "px"); const $downloadBtn = $copyBtn.siblings("a.button, button").first(); if ($downloadBtn.length) { $downloadBtn.css("margin-right", parseInt($downloadBtn.css("margin-right")) + 3 + "px"); } $(" `; document.body.appendChild(panel); const header = document.getElementById("jhs-cookie-header"); document.getElementById("jhs-cookie-content"); const toggleIcon = document.getElementById("jhs-toggle-icon"), copyButton = document.getElementById("jhs-copy-btn"); header.addEventListener("click", (() => { const isExpanded = panel.classList.toggle("expanded"); toggleIcon.textContent = isExpanded ? "▲" : "▼"; })); copyButton.addEventListener("click", (async (e) => { e.stopPropagation(); try { await navigator.clipboard.writeText(cookieValue); this.showMessage("Cookie 已成功复制到剪贴板!"); } catch (err) { console.error("Failed to copy text using clipboard API: ", err); const tempTextArea = document.createElement("textarea"); tempTextArea.value = cookieValue; document.body.appendChild(tempTextArea); tempTextArea.select(); document.execCommand("copy"); document.body.removeChild(tempTextArea); this.showMessage("Cookie 已复制! (回退方案)"); } })); panel.classList.remove("expanded"); } } const _WangPan115MatchPlugin = class _WangPan115MatchPlugin2 extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "loginStatus", _WangPan115MatchPlugin2.LoginStatus.UNCHECKED); } getName() { return "WangPan115MatchPlugin"; } async initCss() { return "\n \n "; } async handle() { $(document).on("click", ".jhs-match-no-login-btn", (async (event) => { event.preventDefault(); event.stopPropagation(); await this.handleLoginRedirect(); })); $(document).on("click", ".jhs-match-btn", ((event) => { event.preventDefault(); event.stopImmediatePropagation(); this.showMatchDetail(event.currentTarget); })); $(document).on("click", ".jhs-match-error-btn", (async (event) => { event.preventDefault(); event.stopPropagation(); await this.retryMatch(event.currentTarget); })); $(document).on("click", ".jhs-detail-delete-btn", (async (event) => { event.preventDefault(); event.stopPropagation(); const $btn = $(event.currentTarget); const matchData = $btn.attr("data-match"); if (!matchData) return; const matchList = JSON.parse(matchData); const carNum2 = this.getPageInfo().carNum; this.showDeleteDialog(carNum2, matchList, (async (newMatchList) => { $("#115-match-table").remove(); await this.matchDetailPage(newMatchList); })); })); await this.matchDetailPage(); $(document).on("click", ".jhs-match-detail-error-btn", (async (event) => { event.preventDefault(); event.stopPropagation(); $(event.currentTarget).replaceWith(`匹配中...`); try { const carNum2 = this.getPageInfo().carNum, matchList = await this.searchFiles(carNum2); $(".jhs-115-match-detail").remove(); await this.matchDetailPage(matchList); } catch (error) { console.error(`重新匹配失败 [${carNum}]:`, error); this.showMatchError($box, carNum, error); } })); } async matchDetailPage(matchList) { if (!window.isDetailPage) return; if (await storageManager.getSetting("enable115Match", NO$1) === NO$1) return; const $detail = $('\n
    \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    115匹配
    名称大小时间播放
    \n
    \n '), $tbody = $detail.find("tbody"); try { const carNum2 = this.getPageInfo().carNum; if (!matchList) { const {results, usedPhase2} = await this._searchFilesWithPhase(carNum2); matchList = results; if (usedPhase2 && matchList.length > 0) { await this.autoRenameFiles(carNum2, matchList); } } await this.checkLoginStatus(); if (this.loginStatus === _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT) $tbody.append(` `); else if (matchList.length > 0) { const rowsHtml = matchList.map(((match) => ` ${match.name} ${this.formatSize(match.size)} ${match.createTime} 播放 `)).join(""); $tbody.append(rowsHtml); $detail.css("position", "relative"); $detail.append(`🗑️ 删除`); } else $tbody.append(` 未匹配 `); } catch (error) { $tbody.append(` 加载失败,请重试 `); console.error("加载文件列表时发生错误:", error); } if (isJavDb$1) if ($("#all-match-box").length) $("#all-match-box").append($detail); else { $("#tabs-container").before("
    "); $("#all-match-box").append($detail); } else $("#mag-submit-show").before($detail); } async matchMovieList(movieListElement) { if (await storageManager.getSetting("enable115Match", NO$1) !== NO$1) { await this.checkLoginStatus(); await this.processMovieElements(movieListElement); } else $(".video-title [class^='jhs-match-']").remove(); } showMatchDetail(buttonElement) { const $el = $(buttonElement), matchData = $el.attr("data-match"); $(".jhs-match-detail").remove(); const matches = this.parseMatchData(matchData); if (0 === matches.length) return; if (1 === matches.length) { const pickcode = matches[0].videoId; window.open(`https://115vod.com/?pickcode=${pickcode}&share_id=0`, "_blank"); return; } const $detail = this.createMatchDetailElement(matches); this.positionDetailElement($detail, $el); this.addOutsideClickHandler($detail); $detail.on("click", ((e) => { e.stopPropagation(); })); } parseMatchData(matchData) { try { return JSON.parse(matchData) || []; } catch (e) { console.error("解析匹配数据失败:", e); return []; } } createMatchDetailElement(matches) { const $detail = $(`
    ${matches.map(((match) => ` `)).join("")}
    名称 大小 时间 播放
    ${match.name} ${this.formatSize(match.size)} ${match.createTime} 播放
    `); $("body").append($detail); return $detail; } positionDetailElement($detail, $trigger) { const offset = $trigger.offset(); $detail.css({ top: offset.top - $detail.outerHeight() + 20, left: offset.left }); } addOutsideClickHandler($detail) { setTimeout((() => { $(document).on("click.jhs-match-detail", ((e) => { if (!$detail.is(e.target) && 0 === $detail.has(e.target).length) { $detail.remove(); $(document).off("click.jhs-match-detail"); } })); }), 100); } async retryMatch(buttonElement) { const $el = $(buttonElement), $box2 = $el.closest(".movie-box, .item"), carNum2 = $el.attr("data-keyword"); $el.replaceWith(`匹配中...`); try { const matchList = await this.searchFiles(carNum2); this.updateMatchStatus($box2, carNum2, matchList); } catch (error) { console.error(`重新匹配失败 [${carNum2}]:`, error); this.showMatchError($box2, carNum2, error); } } updateMatchStatus($box2, carNum2, matchList) { if (matchList.length > 0) { $box2.find(".jhs-match-btn").replaceWith(`匹配${matchList.length}个`); const $deleteBtn = $box2.find(".delete115Svg"); const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0 && matchList.some((m) => m.dirId != null)) { $deleteBtn.attr("data-match", JSON.stringify(matchList)); $deleteBtn.show(); } if ($markBtn.length > 0) { $markBtn.show(); this.updateMarkBtnStyle($markBtn, carNum2); } } else { $box2.find(".jhs-match-btn").replaceWith(`未匹配`); const $deleteBtn = $box2.find(".delete115Svg"); const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0) { $deleteBtn.removeAttr("data-match"); $deleteBtn.hide(); } if ($markBtn.length > 0) { $markBtn.hide(); } } } async updateMarkBtnStyle($markBtn, carNum2) { const markedList = await storageManager.forage.getItem("markedDeleteList") || []; const isMarked = markedList.some((item) => item.carNum === carNum2); isMarked ? $markBtn.addClass("marked") : $markBtn.removeClass("marked"); $markBtn.attr("title", isMarked ? "取消标记删除" : "标记删除"); } async batchDeleteMarked(selectedItems, onProgress) { let deletedCount = 0; let failedCount = 0; const randomDelay = () => new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 2e3)); for (let i = 0; i < selectedItems.length; i++) { const item = selectedItems[i]; try { if (i > 0) { await randomDelay(); } const matchList = await this.searchFiles(item.carNum); if (matchList.length === 0) { failedCount++; onProgress && onProgress(i + 1, selectedItems.length, item, "未匹配到115文件"); continue; } const {regex: safeRegex} = codeParse(item.carNum); const safeMatchList = matchList.filter((m) => safeRegex.test(m.name)); if (safeMatchList.length === 0) { failedCount++; onProgress && onProgress(i + 1, selectedItems.length, item, "无安全可删除目录"); continue; } let itemDeleted = 0; const groupedByDir = {}; for (const m of safeMatchList) { if (!groupedByDir[m.dirId]) groupedByDir[m.dirId] = []; groupedByDir[m.dirId].push(m); } const entries = Object.entries(groupedByDir); for (let g = 0; g < entries.length; g++) { if (g > 0) { await randomDelay(); } const [dirId, items] = entries[g]; if (items.length === 1) { const result = await this._deleteOne(dirId, false, i, selectedItems, item, onProgress); if (result) { itemDeleted++; deletedCount++; } } else { for (let f = 0; f < items.length; f++) { if (f > 0) await randomDelay(); const fileDeleted = await this._deleteOne(items[f].folderId, true, i, selectedItems, item, onProgress); if (fileDeleted) { itemDeleted++; deletedCount++; } } } } onProgress && onProgress(i + 1, selectedItems.length, item, itemDeleted > 0 ? `已删除${itemDeleted}个` : "删除失败"); } catch (error) { failedCount++; onProgress && onProgress(i + 1, selectedItems.length, item, error.message || "出错"); console.error(`批量删除出错 [${item.carNum}]:`, error); } } return {deletedCount, failedCount}; } isRateLimited(response) { if (!response) return false; if (response.state === false) { const msg = (response.error || response.msg || "").toLowerCase(); if (msg.includes("频繁") || msg.includes("操作太频繁") || msg.includes("稍后再试") || msg.includes("rate") || msg.includes("limit") || msg.includes("too many")) { return true; } } if (response.status === 403 || response.status === 429) { return true; } return false; } async _deleteOne(deleteId, isFile, batchIndex, selectedItems, item, onProgress) { let retryCount = 0; const maxRetries = 3; const label = isFile ? "文件" : "目录"; while (retryCount <= maxRetries) { try { const result = await gmHttp.postForm("https://webapi.115.com/rb/delete", { "fid[0]": deleteId, ignore_warn: "1" }); if (result && result.state) { return 1; } else { const errMsg = result && result.error || "未知错误"; if (retryCount < maxRetries && this.isRateLimited(result)) { retryCount++; const backoff = (retryCount + 1) * 2e3 + Math.random() * 1e3; onProgress && onProgress(batchIndex + 1, selectedItems.length, item, `风控触发,${backoff / 1e3}秒后重试(${retryCount}/${maxRetries})`); await new Promise((resolve) => setTimeout(resolve, backoff)); continue; } console.error(`删除${label}失败: ${deleteId}`, errMsg); return 0; } } catch (e) { if (retryCount < maxRetries && this.isRateLimited(e)) { retryCount++; const backoff = (retryCount + 1) * 2e3 + Math.random() * 1e3; onProgress && onProgress(batchIndex + 1, selectedItems.length, item, `风控触发,${backoff / 1e3}秒后重试(${retryCount}/${maxRetries})`); await new Promise((resolve) => setTimeout(resolve, backoff)); continue; } console.error(`删除${label}失败: ${deleteId}`, e); return 0; } } return 0; } showDeleteDialog(carNum2, matchList, afterDelete) { const {regex: safeRegex} = codeParse(carNum2); const safeMatchList = matchList.filter((m) => safeRegex.test(m.name)); const unsafeCount = matchList.length - safeMatchList.length; if (safeMatchList.length === 0) { show.error(`作品 "${carNum2}" 匹配到的 ${matchList.length} 个项目均不含番号,无法安全删除。请手动在115中删除。`); return; } const formatSize = (bytes) => { if (!bytes) return "-"; const units = ["B", "KB", "MB", "GB", "TB"]; let size = parseFloat(bytes), unit = 0; for (; size >= 1024 && unit < units.length - 1;) size /= 1024, unit++; return size.toFixed(2) + " " + units[unit]; }; let html2 = '
    '; html2 += `
    作品 ${carNum2} 匹配到 ${matchList.length} 个文件夹(可删除 ${safeMatchList.length} 个):
    `; if (unsafeCount > 0) { html2 += `
    ⚠ 已自动排除 ${unsafeCount} 个目录名不含番号的项目,防止误删
    `; } html2 += '
    '; for (let i = 0; i < safeMatchList.length; i++) { const m = safeMatchList[i]; html2 += `
    `; } html2 += "
    "; const layerIndex = layer.open({ type: 1, title: "删除115作品文件夹", content: html2, area: ["520px", "auto"], btn: ["确定删除", "取消"], shadeClose: false, yes: async (index) => { var _a2, _b; const $layer = $(`#layui-layer${index}`); const checked = $layer.find(".jhs-delete-folder-cb:checked"); if (0 === checked.length) { show.error("请至少选择一个文件夹"); return; } const selected = checked.map((function () { return parseInt($(this).data("index")); })).get(); layer.close(index); let loadObj = loading(); let deletedCount = 0; try { const selectedItems = selected.map((i) => safeMatchList[i]); const totalByDir = {}; for (const m of safeMatchList) { totalByDir[m.dirId] = (totalByDir[m.dirId] || 0) + 1; } const groupedByDir = {}; for (const item of selectedItems) { if (!groupedByDir[item.dirId]) groupedByDir[item.dirId] = []; groupedByDir[item.dirId].push(item); } for (const [dirId, items] of Object.entries(groupedByDir)) { if (items.length === totalByDir[dirId]) { const result = await gmHttp.postForm("https://webapi.115.com/rb/delete", { "fid[0]": dirId, ignore_warn: "1" }); result && result.state ? deletedCount++ : console.error(`删除目录失败: ${dirId}`, result); } else { for (const item of items) { const result = await gmHttp.postForm("https://webapi.115.com/rb/delete", { "fid[0]": item.folderId, ignore_warn: "1" }); result && result.state ? deletedCount++ : console.error(`删除文件失败: ${item.folderId}`, result); } } } loadObj.close(); show.ok(`作品 "${carNum2}" 已删除 ${deletedCount} 个文件夹`); const newMatchList = await this.searchFiles(carNum2); afterDelete && afterDelete(newMatchList); const remainingCids = new Set(newMatchList.map((m) => m.dirId)); JavPackSubtitle.cached115Matches = JavPackSubtitle.cached115Matches.filter((m) => remainingCids.has(m.cid)); JavPackSubtitle.current115Cids = JavPackSubtitle.current115Cids.filter((cid) => remainingCids.has(cid)); try { (_b = (_a2 = this.getBean("WangPan115TaskPlugin")).deleteOfflineTasksByKeyword) == null ? void 0 : _b.call(_a2, carNum2); } catch (e) { console.warn("删除离线任务暂不可用:", e.message); } } catch (error) { loadObj.close(); console.error("删除115文件失败:", error); show.error(`删除失败: ${error.message || "网络错误"}`); } } }); $(`#layui-layer${layerIndex}`).on("change", ".jhs-delete-select-all", (function () { const checked = $(this).prop("checked"); $(`#layui-layer${layerIndex} .jhs-delete-folder-cb`).prop("checked", checked); })); } async handleLoginRedirect() { window.open("https://115.com"); } async searchFiles(carNum2) { return (await this._searchFilesWithPhase(carNum2)).results; } async _searchFilesWithPhase(carNum2) { const {regex} = codeParse(carNum2); const searchKeyword = carNum2.toLowerCase().replace("fc2-", ""); const mapAndFilter = (rawData) => { return rawData.map(((data) => ({ folderId: data.fid, dirId: data.cid, videoId: data.pc, name: data.n, createTime: utils.formatDate(new Date(1e3 * data.te)), size: data.s, isVideo: true }))).filter(((x) => x.folderId && regex.test(x.name))); }; var _a2; const preciseData = (null == (_a2 = (await searchFiles(searchKeyword, 0, 30, {type: 4})).data) ? void 0 : _a2) || []; const preciseResults = mapAndFilter(preciseData); if (preciseResults.length > 0) return {results: preciseResults, usedPhase2: false}; const numberPart = extractNumberPart(carNum2); if (numberPart === searchKeyword) return {results: preciseResults, usedPhase2: false}; const fuzzyData = (null == (_a2 = (await searchFiles(numberPart, 0, 30, {type: 4})).data) ? void 0 : _a2) || []; return {results: mapAndFilter(fuzzyData), usedPhase2: true}; } buildRenameTarget(carNum2, originalName) { const {regex} = codeParse(carNum2); const match = regex.exec(originalName); if (!match) return null; const before = originalName.slice(0, match.index); const after = originalName.slice(match.index + match[0].length); return before + carNum2 + after; } async autoRenameFiles(carNum2, matchList) { try { const enableAutoRename = await storageManager.getSetting("enable115AutoRename", NO$1); if (enableAutoRename !== YES) return; const scope = await storageManager.getSetting("autoRenameScope", "vr_only"); if (scope === "vr_only" && !carNum2.toUpperCase().includes("VR")) return; const renameObj = {}; matchList.forEach((match) => { const newName = this.buildRenameTarget(carNum2, match.name); if (newName && newName !== match.name) { renameObj[match.folderId] = newName; } }); if (Object.keys(renameObj).length === 0) return; const formData = {}; Object.entries(renameObj).forEach(([fid, newName]) => { formData[`files_new_name[${fid}]`] = newName; }); const result = await gmHttp.postForm("https://webapi.115.com/files/batch_rename", formData); if (result && result.state) { Object.entries(renameObj).forEach(([fid, newName]) => { const match = matchList.find((m) => m.folderId === fid); console.log(`[115重命名] ${match.name} → ${newName}`); }); } else { console.warn("[115重命名] 重命名失败:", result && result.error || "未知错误"); } } catch (e) { console.warn("[115重命名] 重命名失败,跳过:", e.message); } } showMatchError($box2, carNum2, error) { $box2.find(".jhs-match-btn").replaceWith(`匹配失败`); show.error(`${carNum2} 匹配失败: ${error.message || "网络错误"}`); } async checkLoginStatus() { var _a2; if (this.loginStatus === _WangPan115MatchPlugin2.LoginStatus.UNCHECKED) try { const testResult = await searchFiles("test"); this.loginStatus = (null == (_a2 = testResult.error) ? void 0 : _a2.includes("登录")) ? _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT : _WangPan115MatchPlugin2.LoginStatus.LOGGED_IN; } catch { this.loginStatus = _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT; } } async processMovieElements(movieListElement) { const promises = Array.from(movieListElement).filter(((ele) => !utils.isHidden(ele))).filter(((ele) => !(isJavBus$1 && $(ele).find(".avatar-box").length > 0))).map(((ele) => this.processSingleMovieElement(ele))); await Promise.all(promises); } async processSingleMovieElement(element) { const $box2 = $(element), {carNum: carNum2} = this.getBoxCarInfo($box2); if (!($box2.find("[class^='jhs-match-']").length > 0)) if (this.loginStatus !== _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT) try { const matchList = await this.searchFiles(carNum2); this.addTag($box2, carNum2, matchList); } catch (error) { console.error(`搜索失败 [${carNum2}]:`, error); this.addTag($box2, carNum2, []); } else this.addTag($box2, carNum2, []); } addTag($box2, carNum2, matchList) { if (!($box2.find("[class^='jhs-match-']").length > 0)) if (this.loginStatus === _WangPan115MatchPlugin2.LoginStatus.LOGGED_OUT) $box2.find(".video-title").prepend(``); else if (matchList.length > 0) { const title = 1 === matchList.length ? "点击直接播放" : `点击查看${matchList.length}个匹配结果`; $box2.find(".video-title").prepend(`匹配${matchList.length}个`); const $deleteBtn = $box2.find(".delete115Svg"); const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0 && matchList.some((m) => m.dirId != null)) { $deleteBtn.attr("data-match", JSON.stringify(matchList)); $deleteBtn.show(); } if ($markBtn.length > 0) { $markBtn.show(); this.updateMarkBtnStyle($markBtn, carNum2); } } else $box2.find(".video-title").prepend(`未匹配`); } formatSize(bytes) { if (!bytes) return "-"; const units = ["B", "KB", "MB", "GB", "TB"]; let size = parseFloat(bytes), unit = 0; for (; size >= 1024 && unit < units.length - 1;) { size /= 1024; unit++; } return `${size.toFixed(2)} ${units[unit]}`; } }; __publicField2(_WangPan115MatchPlugin, "LoginStatus", { UNCHECKED: -1, LOGGED_OUT: 0, LOGGED_IN: 1 }); let WangPan115MatchPlugin = _WangPan115MatchPlugin; const extractNumberPart = (carNum2) => { let cleaned = carNum2.replace(/^fc2[-_]?(ppv[-_])?/i, ""); const match = cleaned.match(/(\d+)$/); return match ? match[1] : cleaned; }; const codeParse = (code) => { const codes = code.split(/-|_/); const sep = "\\s?(0|-|_){0,2}\\s?"; let pattern = codes.join(sep); if (/^fc2/i.test(code)) pattern = `${codes[0]}${sep}(ppv)?${sep}${codes.at(-1)}`; return { code, codes, prefix: codes[0], regex: new RegExp(`(? .highlighted { border: ${tagNumber}px solid ${tagColor} !important; } `; } async handle() { this.bindEvent(); await this.highlightActress(); this.replaceActressAvatar(); } async highlightActress() { if (!window.isDetailPage) return; if (await storageManager.getSetting("enableFavoriteActresses", YES$1) !== YES$1) return; const favoriteActressesInfoList = await storageManager.getFavoriteActressList(); if (!favoriteActressesInfoList || 0 === favoriteActressesInfoList.length) return; const favoriteStarIdSet = /* @__PURE__ */ new Set(); favoriteActressesInfoList.forEach(((actress) => { actress.starId && favoriteStarIdSet.add(String(actress.starId).trim()); })); 0 !== favoriteStarIdSet.size && $(".female").prev().each(((index, element) => { const $element = $(element), href = $element.attr("href"); let elementStarId = null; if (href) { const parts = (href.endsWith("/") ? href.slice(0, -1) : href).split("/"), lastPart = parts[parts.length - 1]; lastPart && (elementStarId = lastPart.trim()); } let isFavorite = false; elementStarId && (isFavorite = favoriteStarIdSet.has(elementStarId)); if (isFavorite) { $element.addClass("highlighted"); $element.attr("title", "高亮已收藏演员, 可在设置-基础配置中关闭"); } })); } async removeActorFromStorage(actorId) { await storageManager.removeFavoriteActress(actorId) && clog.log("移除演员成功"); } bindEvent() { const listIdRegex = /\/actors\/(\w+)\/(collect|uncollect)/; $(document).on("confirm:complete", 'a[href*="/actors/"][href*="/uncollect"]', (async (event) => { const [userConfirmed] = event.detail; if (!userConfirmed) return; const match = $(event.currentTarget).attr("href").match(listIdRegex), actorId = match ? match[1] : null; actorId && await this.removeActorFromStorage(actorId); })); $("#button-collect-actor").click((async (event) => { const match = $("#button-collect-actor").attr("href").match(listIdRegex), actorId = match ? match[1] : null; let nameList = [], $actor = $(".actor-section-name"); $actor.length && $actor.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); let $sectionMeta = $(".section-meta:not(:contains('影片'))"); $sectionMeta.length && $sectionMeta.text().trim().split(",").forEach(((name2) => { nameList.push(name2.trim()); })); if (!nameList) { clog.error("获取演员名称失败"); return; } const actorName = nameList[0]; if (!actorId) { clog.error("无法获取演员ID进行收藏操作。"); return; } const avatar = ($(".avatar").first().css("background-image") || "").replace(/^url\(["']?|["']?\)$/g, ""), actressInfo = { starId: actorId, name: actorName, allName: nameList, avatar }; 1 === await storageManager.addFavoriteActressList([actressInfo]) ? clog.log(`收藏演员成功: ${actorName} (ID: ${actorId})`) : clog.log(`收藏演员失败: ${actorName} (ID: ${actorId})`); })); $("#button-uncollect-actor").click((async (event) => { const match = $("#button-uncollect-actor").attr("href").match(listIdRegex), actorId = match ? match[1] : null; actorId ? await this.removeActorFromStorage(actorId) : clog.error("无法获取演员ID进行取消收藏操作。"); })); } async replaceActressAvatar() { const actressId = this.getActressId(); if (!actressId) return; const actress = (await storageManager.getFavoriteActressList()).find(((item) => item.starId === actressId)); if (actress && actress.avatar) { const newAvatarUrl = `url('${actress.avatar}')`; let $avatarElement = $(".avatar").first(); if (0 === $avatarElement.length) { const newAvatarHtml = '
    '; $(".section-columns").prepend(newAvatarHtml); $avatarElement = $(".avatar").first(); } if (0 === $avatarElement.length) return; if ($avatarElement.css("background-image").trim().toLowerCase() !== newAvatarUrl.trim().toLowerCase()) { $avatarElement.css("background-image", newAvatarUrl); $avatarElement.css("background-size", "cover"); $avatarElement.css("background-position", "top center"); $avatarElement.css("background-repeat", "no-repeat"); } } } } class BusImgPlugin extends BasePlugin { getName() { return "BusImgPlugin"; } handle() { } async getVisibleImageItems(itemSelector, imgSelector) { let itemList = []; const allItems = document.querySelectorAll(itemSelector); for (const item of allItems) { if (!utils.isHidden(item)) { const imgElement = item.querySelector(imgSelector); if (!(imgElement instanceof HTMLImageElement)) continue; imgElement.style.removeProperty("height"); let imageHeight = imgElement.offsetHeight; imageHeight > 0 && itemList.push({ element: item, imgElement, height: imageHeight }); } } return itemList; } async logImageHeightsByRow() { if (await storageManager.getSetting("enableVerticalModel", NO$1) === YES$1) return; const itemSelector = this.getSelector().itemSelector, containerColumns = await storageManager.getSetting("containerColumns", 5), itemList = await this.getVisibleImageItems(itemSelector, "img"); if (0 === itemList.length) return; const groupedItems = []; for (let i = 0; i < itemList.length; i++) { const rowIndex = Math.floor(i / containerColumns); groupedItems[rowIndex] || (groupedItems[rowIndex] = []); groupedItems[rowIndex].push(itemList[i]); } groupedItems.forEach(((row, rowIndex) => { const originalHeights = row.map(((item) => item.height)); if (originalHeights.length < 2) return; const minHeight = Math.min(...originalHeights), maxHeight = Math.max(...originalHeights); let targetHeight = 0; if (maxHeight - minHeight > 50) { targetHeight = minHeight; row.forEach(((item) => { if (item.height !== targetHeight) { const heightValue = `${targetHeight}px`; item.imgElement.style.setProperty("height", heightValue, "important"); } })); } })); } } class ListPageButtonPlugin extends BasePlugin { getName() { return "ListPageButtonPlugin"; } async handle() { if (!window.isListPage) return; await this.createMenuBtn(); this.bindEvent(); await storageManager.getSetting("autoPage") === YES$1 ? $("#sort-toggle-btn").hide() : this.sortItems().then(); this.initOneTimeSortBtn(); } async createMenuBtn() { const showWaitCheckBtn = await storageManager.getSetting("showWaitCheckBtn", YES$1), showWaitDownBtn = await storageManager.getSetting("showWaitDownBtn", YES$1); if (isJavDb$1) { const isStarPage = currentHref$1.includes("/actors/"); let $el = $(".main-tabs, .tabs"), addBlacklistBtnText = "加入黑名单", addBlacklistBtnColor = "#d22020", otherCss = "", blacklistItem = null; if (isStarPage) { $el = $(".toolbar, .section-addition").filter(":last"); const blacklist = await storageManager.getBlacklist(), actressPageInfo = this.getActressPageInfo(); if (blacklist.find(((item) => item.starId === actressPageInfo.starId))) { addBlacklistBtnText = "已加入黑名单"; addBlacklistBtnColor = "#885d5d"; } } else currentHref$1.includes("/tags") && utils.loopDetector((() => $("#jhs-check-tag").text().trim()), (async () => { const $addBlacklistBtn = $("#addBlacklistBtn"); $addBlacklistBtn.attr("data-tip", "将当前分类标签加入到黑名单, 后续有作品更新也会纳入屏蔽中"); const checkTag = $("#jhs-check-tag").text().trim(); if (!checkTag) return; const tagStarId = "no-" + checkTag, blacklist = await storageManager.getBlacklist(); blacklistItem = blacklist.find(((item) => item.starId === tagStarId)); if (blacklistItem) { $addBlacklistBtn.css("backgroundColor", "#885d5d"); $("#addBlacklistBtn span").text("已加入黑名单"); } })); const isFc2Page2 = currentHref$1.includes("advanced_search"); isFc2Page2 ? $el = $("h2.section-title") : otherCss = "flex-grow:1;"; const jhs_sortMethod = localStorage.getItem("jhs_sortMethod"), sortText = "当前排序方式: " + ("rateCount" === jhs_sortMethod ? "评价人数" : "date" === jhs_sortMethod ? "时间" : "默认"); $el.append(`
    打开待鉴定 打开已收藏 ${isStarPage ? ` ${addBlacklistBtnText} 一键屏蔽所有作品 ` : ""} ${currentHref$1.includes("/tags") ? ` ${addBlacklistBtnText} ` : ""}
    一次性排序: 默认 新作品检测 (0) 演员黑名单 ${isSearchPage || isFc2Page2 ? "" : ` ${sortText} `}
    `); } if (isJavBus$1) { const isStarPage = currentHref$1.includes("/star/"); let addBlacklistBtnText = "加入黑名单", addBlacklistBtnColor = "#d22020"; if (isStarPage) { const blacklist = await storageManager.getBlacklist(), actressPageInfo = this.getActressPageInfo(); if (blacklist.find(((item) => item.starId === actressPageInfo.starId))) { addBlacklistBtnText = "已加入黑名单"; addBlacklistBtnColor = "#885d5d"; } } $(".masonry").parent().prepend(`
    打开待鉴定 打开已收藏 ${isStarPage ? ` ${addBlacklistBtnText} 一键屏蔽所有作品 ` : '演员黑名单'}
    `); } const newVideoPlugin = window.pluginManager.getBean("NewVideoPlugin"); newVideoPlugin && newVideoPlugin.showNewVideoCount().then(); } bindEvent() { $("#waitCheckBtn").on("click", ((event) => { this.openWaitCheck(event).then(); })); $("#waitDownBtn").on("click", ((event) => { this.openFavorite(event).then(); })); $("#newVideoBtn").on("click", ((event) => { this.getBean("NewVideoPlugin").openDialog(); })); $("#blacklistBtn").on("click", ((event) => { this.getBean("BlacklistPlugin").openBlacklistDialog(); })); $("#sort-toggle-btn").on("click", ((event) => { const currentMethod = localStorage.getItem("jhs_sortMethod") || "default"; const sortCycle = ["default", "rateCount", "totalScore", "date"]; let nextIndex = sortCycle.indexOf(currentMethod) + 1; if (nextIndex >= sortCycle.length || nextIndex < 0) nextIndex = 0; const newMethod = sortCycle[nextIndex]; const methodText = { default: "默认", rateCount: "评价人数", totalScore: "总分", date: "时间" }[newMethod]; $(event.target).text(`当前排序方式: ${methodText}`); localStorage.setItem("jhs_sortMethod", newMethod); this.sortItems().then(); })); $("#one-time-sort-btn").on("click", ((event) => { const currentMethod = localStorage.getItem("jhs_oneTimeSortMethod") || "default"; const sortCycle = ["default", "rateCount", "totalScore", "date"]; let nextIndex = sortCycle.indexOf(currentMethod) + 1; if (nextIndex >= sortCycle.length || nextIndex < 0) nextIndex = 0; const newMethod = sortCycle[nextIndex]; const methodText = { default: "默认", rateCount: "评价人数", totalScore: "总分", date: "时间" }[newMethod]; $("#one-time-sort-btn").text(`一次性排序: ${methodText}`); localStorage.setItem("jhs_oneTimeSortMethod", newMethod); this.oneTimeSortItems().then(); })); const blacklistPlugin = this.getBean("BlacklistPlugin"); $("#addBlacklistBtn").on("click", (async (event) => { await blacklistPlugin.addBlacklist(event); })); $("#filterAllVideo").on("click", (async (event) => { let tempEvent = { clientX: event.clientX, clientY: event.clientY + 80 }, $actor = isJavDb$1 ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10"); if (0 === $actor.length) { show.error("获取演员名称失败"); return; } let actorName = $actor.text().trim().split(",")[0]; utils.q(tempEvent, "一键屏蔽已选分类的视频列表至鉴定记录中?", (async () => { this.loadObj = loading(); try { await blacklistPlugin.filterAllVideo(actorName); window.refresh(); } catch (e) { console.error(e); } finally { this.loadObj.close(); } })); })); } async sortItems() { if (currentHref$1.includes("handle") || currentHref$1.includes("advanced_search")) return; const autoPage = await storageManager.getSetting("autoPage"); if (isSearchPage || autoPage === YES$1) return; const method = localStorage.getItem("jhs_sortMethod"); if (!method) return; $(".movie-list .item").each((function (index) { $(this).attr("data-original-index") || $(this).attr("data-original-index", index); })); const $container = $(".movie-list"), $items = $(".item", $container); if ("default" === method) { $items.sort((function (a, b) { return $(a).data("original-index") - $(b).data("original-index"); })).appendTo($container); } else { const items = $items.get(); items.sort((function (a, b) { if ("rateCount" === method) { const getScore = (el) => { const match = $(el).find(".score .value").text().match(/由(\d+)人/); return match ? parseFloat(match[1]) : 0; }; return getScore(b) - getScore(a); } else if ("totalScore" === method) { const getTotalScore = (el) => { const text = $(el).find(".score .value").text(); const scoreMatch = text.match(/([\d.]+)分/); const countMatch = text.match(/由(\d+)人/); const score = scoreMatch ? parseFloat(scoreMatch[1]) : 0; const count = countMatch ? parseFloat(countMatch[1]) : 0; return score * count; }; return getTotalScore(b) - getTotalScore(a); } else { const getDate = (el) => { const dateStr = $(el).find(".meta").text().trim(); return new Date(dateStr); }; return getDate(b) - getDate(a); } })); $container.empty().append(items); } } async initOneTimeSortBtn() { const autoPage = await storageManager.getSetting("autoPage"); if (autoPage !== YES$1) { $("#one-time-sort-btn").hide(); return; } const oneTimeMethod = localStorage.getItem("jhs_oneTimeSortMethod") || "default"; const methodText = { default: "默认", rateCount: "评价人数", totalScore: "总分", date: "时间" }[oneTimeMethod]; $("#one-time-sort-btn").text(`一次性排序: ${methodText}`).show(); } async oneTimeSortItems() { if (currentHref$1.includes("handle") || currentHref$1.includes("advanced_search")) return; if (isSearchPage) return; const method = localStorage.getItem("jhs_oneTimeSortMethod"); if (!method) return; $(".movie-list .item").each((function (index) { $(this).attr("data-original-index") || $(this).attr("data-original-index", index); })); const $container = $(".movie-list"), $items = $(".item", $container); if ("default" === method) { $items.sort((function (a, b) { return $(a).data("original-index") - $(b).data("original-index"); })).appendTo($container); } else { const items = $items.get(); items.sort((function (a, b) { if ("rateCount" === method) { const getScore = (el) => { const match = $(el).find(".score .value").text().match(/由(\d+)人/); return match ? parseFloat(match[1]) : 0; }; return getScore(b) - getScore(a); } else if ("totalScore" === method) { const getTotalScore = (el) => { const text = $(el).find(".score .value").text(); const scoreMatch = text.match(/([\d.]+)分/); const countMatch = text.match(/由(\d+)人/); const score = scoreMatch ? parseFloat(scoreMatch[1]) : 0; const count = countMatch ? parseFloat(countMatch[1]) : 0; return score * count; }; return getTotalScore(b) - getTotalScore(a); } else { const getDate = (el) => { const dateStr = $(el).find(".meta").text().trim(); return new Date(dateStr); }; return getDate(b) - getDate(a); } })); $container.empty().append(items); } } async openWaitCheck(event) { const maxCount = await storageManager.getSetting("waitCheckCount", 5); let count = 0; $(`${this.getSelector().itemSelector}:visible`).each(((i, el) => { if (count >= maxCount) return false; const $box2 = $(el); if ($box2.find(".status-tag").length > 0) return; const {carNum: carNum2, aHref} = this.getBoxCarInfo($box2); if (carNum2.includes("FC2-")) { const movieId = this.parseMovieId(aHref); this.getBean("Fc2Plugin").openFc2Page(movieId, carNum2, aHref); } else { let url = aHref + (aHref.includes("?") ? "&autoPlay=1" : "?autoPlay=1"); window.open(url); } count++; })); 0 === count && show.info("没有需鉴定的视频"); } async openFavorite() { let favoriteList, openCount = await storageManager.getSetting("waitCheckCount", 5), randomOpenWaitDown = await storageManager.getSetting("randomOpenWaitDown", NO$1), dataList = await storageManager.getCarList(); favoriteList = randomOpenWaitDown === YES$1 ? dataList.filter(((item) => item.status === Status_FAVORITE$1)).sort((() => Math.random() - 0.5)) : dataList.filter(((item) => item.status === Status_FAVORITE$1)).sort(((a, b) => b.createDate - a.createDate)); for (let i = 0; i < openCount; i++) { if (i >= favoriteList.length) return; let data = favoriteList[i], carNum2 = data.carNum, url = data.url; if (carNum2.includes("FC2-")) { const movieId = this.parseMovieId(url); await this.getBean("Fc2Plugin").openFc2Page(movieId, carNum2, url); } else window.open(url); clog.debug("打开已收藏", carNum2, url); } } } const translateText = async (text, sourceLang = "ja", targetLang = "zh-CN") => { if (!text) throw new Error("翻译文本不能为空"); const url = "https://translate-pa.googleapis.com/v1/translate?" + new URLSearchParams({ "params.client": "gtx", dataTypes: "TRANSLATION", key: "AIzaSyDLEeFI5OtFBwYBIoK_jj5m32rZK5CkCXA", "query.sourceLanguage": sourceLang, "query.targetLanguage": targetLang, "query.text": text }), res = await fetch(url); if (!res.ok) throw new Error(`${res.status} ${res.statusText}`); return (await res.json()).translation; }, TAG_STYLES = { IS_FILTERED: { text: "🚫 已屏蔽", color: "#de3333", reasonType: "单番号屏蔽", isCounted: true, countKey: "currentPageFilterCount" }, IS_FAVORITE: { text: "⭐ 已收藏", color: "#25b1dc", reasonType: "", isCounted: true, countKey: "currentPageFavoriteCount" }, IS_HAS_DOWN: { text: "📥️ 已下载", color: "#7bc73b", reasonType: "", isCounted: true, countKey: "currentPageHasDownCount" }, IS_HAS_WATCH: { text: "🔍 已观看", color: "#d7a80c", reasonType: "", isCounted: true, countKey: "currentPageHasWatchCount" }, IS_KEYWORD_FILTER: { text: "❌ 关键词屏蔽", color: "#de3333", reasonType: "", isCounted: true, countKey: "currentPageKeywordFilterCount" }, IS_ACTOR_FILTER: { text: "♂️ 男演员屏蔽", color: "#b22222", reasonType: "", isCounted: true, countKey: "currentPageActorFilterCount" }, IS_ACTRESS_FILTER: { text: "♀️ 女演员屏蔽", color: "#cd5c5c", reasonType: "", isCounted: true, countKey: "currentPageActorFilterCount" }, IS_WAIT_CHECK: { text: "", color: "", reasonType: "", isCounted: true, countKey: "currentPageWaitCheckCount" } }; class TranslatePlugin extends BasePlugin { getName() { return "TranslatePlugin"; } async initCss() { return "\n \n "; } handle() { window.isDetailPage && this.translate(); } async translate(carNum2, showCarNum = true) { if (await storageManager.getSetting("translateTitle", YES$1) !== YES$1) return; isJavBus$1 && (showCarNum = false); let $titleElement = $(".origin-title"); $titleElement.length || ($titleElement = $(".current-title")); $titleElement.length || ($titleElement = $("h3")); if (!$titleElement.length) return; const originalText = $titleElement.text().trim(); if (!originalText) { show.error("获取标题失败, 无法进行翻译"); return; } $titleElement.after('
    翻译中...
    '); const $loadingElement = $titleElement.next(".translated-title"); carNum2 || (carNum2 = this.getPageInfo().carNum); const cache = localStorage.getItem("jhs_translate") ? JSON.parse(localStorage.getItem("jhs_translate")) : {}; cache[carNum2] ? $loadingElement.html(showCarNum ? carNum2 + "   " + cache[carNum2] : cache[carNum2]) : translateText(originalText, "ja", "zh-CN").then(((translatedText) => { $loadingElement.html(showCarNum ? carNum2 + "   " + translatedText : translatedText); })).catch(((error) => { console.error("翻译失败:", error); $loadingElement.replaceWith(`
    翻译失败: ${error.message}
    `); })); } } class OtherSitePlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "okBackgroundColor", "#7bc73b"); __publicField2(this, "errorBackgroundColor", "#de3333"); __publicField2(this, "warnBackgroundColor", "#d7a80c"); __publicField2(this, "domainErrorBackgroundColor", "#d7780c"); __publicField2(this, "asyncQueue", new AsyncQueue()); __publicField2(this, "siteConfigs", [ { id: "MissAvBtn", getBaseUrl: async () => await this.getMissAvUrl(), itemSelector: ".text-secondary", searchPath: (baseUrl, carNum2) => `${baseUrl}/search/${carNum2}`, getDetailPageHref: ($box2) => $box2.attr("href"), findCarNumOrTitle: ($box2) => $box2.text() }, { id: "123AvBtn", getBaseUrl: async () => await this.getAv123Url() + "/ja", itemSelector: ".box-item", searchPath: (baseUrl, carNum2) => `${baseUrl}/search?keyword=${carNum2}`, getDetailPageHref: ($box2) => $box2.find(".detail a").attr("href"), findCarNumOrTitle: ($box2) => $box2.find("img").attr("title") }, { id: "JableBtn", getBaseUrl: async () => await this.getjableUrl(), itemSelector: "#list_videos_videos_list_search_result .detail .title a", searchPath: (baseUrl, carNum2) => `${baseUrl}/search/${carNum2}/`, getDetailPageHref: ($box2) => $box2.attr("href"), findCarNumOrTitle: ($box2) => $box2.text() }, { id: "SupJavBtn", getBaseUrl: async () => await this.getSupJavUrl(), itemSelector: ".posts post", searchPath: (baseUrl, carNum2) => `${baseUrl}/?s=${carNum2}`, getDetailPageHref: ($box2, baseUrl, carNum2) => $box2.attr("href"), findCarNumOrTitle: ($box2) => $box2.attr("title") }, { id: "JavTrailersBtn", getBaseUrl: async () => await this.getJavTrailersUrl(), itemSelector: ".videos-list .video-link", searchPath: (baseUrl, carNum2) => `${baseUrl}/search/${carNum2}`, getDetailPageHref: ($box2) => $box2.attr("href"), findCarNumOrTitle: ($box2) => $box2.find("p.card-text").text() }, { id: "JavDbBtn", getBaseUrl: async () => await this.getJavDbUrl(), itemSelector: ".movie-list .item", searchPath: (baseUrl, carNum2) => `${baseUrl}/search?q=${carNum2}`, getDetailPageHref: ($box2) => $box2.find("a").attr("href"), findCarNumOrTitle: ($box2) => $box2.find(".video-title").text(), condition: (sourceCarNum) => isJavBus$1 }, { id: "JavBusBtn", getBaseUrl: async () => await this.getJavBusUrl(), itemSelector: ".container h3", searchPath: (baseUrl, carNum2) => `${baseUrl}/${carNum2}`, getDetailPageHref: ($box2, baseUrl, carNum2) => `${baseUrl}/${carNum2}`, findCarNumOrTitle: ($box2) => $box2.text(), condition: (sourceCarNum) => isJavDb$1 && sourceCarNum && !sourceCarNum.includes("FC2") } ]); __publicField2(this, "settingCache", null); __publicField2(this, "lastFetchTime", 0); __publicField2(this, "CACHE_DURATION", 1e4); } getName() { return "OtherSitePlugin"; } async initCss() { return "\n \n "; } async handle() { window.isDetailPage && this.loadOtherSite().then(); } async loadOtherSite(carNum2, sourceCarNum) { if ("yes" !== await storageManager.getSetting("enableLoadOtherSite", "yes")) return; carNum2 || (carNum2 = this.getPageInfo().carNum); const enabledSites = this.getEnabledSites(), html2 = `
    ${this.siteConfigs.map(((config) => { config.sourceCarNum = sourceCarNum; if (config.condition && false === config.condition(config.sourceCarNum)) return ""; return `${config.id.replace("Btn", "")}`; })).join("")} 跳转设置
    `; $(".movie-panel-info").append(html2); $(".container .info").append(html2); $("#javTrailersBtn").on("click", (async (event) => { event.preventDefault(); let settingObj = await storageManager.getSetting(); const filterHotKey = settingObj.filterHotKey, favoriteHotKey = settingObj.favoriteHotKey, speedVideoHotKey = settingObj.speedVideoHotKey; let href = $("#javTrailersBtn").attr("href"), url = href + `?handle=1&filterHotKey=${filterHotKey}&favoriteHotKey=${favoriteHotKey}&speedVideoHotKey=${speedVideoHotKey}`; event && (event.ctrlKey || event.metaKey) && (url = href); utils.openPage(url, carNum2, false, event); })); await Promise.all(this.siteConfigs.map((async (config) => { config.condition && false === config.condition(config.sourceCarNum) || await this.handleSite(carNum2, config); }))); this.renderSettingsArea(); this.setupEventListeners(); } async handleSite(carNum2, config) { const $btn = $(`#${config.id}`); try { if ($btn.attr("href")) return; if (utils.isHidden($btn)) return; const cacheKey = "jhs_other_site", cacheData = localStorage.getItem(cacheKey) ? JSON.parse(localStorage.getItem(cacheKey)) : {}, siteKey = carNum2 + "_" + config.id.replace("Btn", ""), cachedResult = cacheData[siteKey]; if (cachedResult) { if ("single" === cachedResult.type) { $btn.attr("href", cachedResult.url); $btn.css("backgroundColor", this.okBackgroundColor); } else if ("multiple" === cachedResult.type) { $btn.attr("href", cachedResult.url); $btn.append('多结果'); $btn.css("backgroundColor", this.okBackgroundColor); } return; } const baseUrl = await config.getBaseUrl(), searchUrl = config.searchPath(baseUrl, carNum2); $btn.attr("href", searchUrl); const html2 = await gmHttp.get(searchUrl, null, config.headers, true), $dom = utils.htmlTo$dom(html2), resultUrlList = []; $dom.find(config.itemSelector).each(((index, element) => { const $box2 = $(element); if (!config.findCarNumOrTitle($box2).toLowerCase().includes(carNum2.toLowerCase())) return; let href = config.getDetailPageHref($box2, baseUrl, carNum2); if (!href) throw new Error("解析href失败"); href.includes("http") || (href = baseUrl + (href.startsWith("/") ? href : "/" + href)); resultUrlList.push(href); })); let tagHtml = "", insertCacheData = null; if (1 === resultUrlList.length) { let resultUrl = resultUrlList[0]; $btn.attr("href", resultUrl); $btn.css("backgroundColor", this.okBackgroundColor); insertCacheData = { type: "single", url: resultUrl }; } else if (resultUrlList.length > 1) { $btn.attr("href", searchUrl); tagHtml += '多结果'; $btn.css("backgroundColor", this.okBackgroundColor); insertCacheData = { type: "multiple", url: searchUrl }; } else { $btn.attr("href", searchUrl); $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", this.errorBackgroundColor); } insertCacheData && this.asyncQueue.addTask((() => { const newCacheData = localStorage.getItem(cacheKey) ? JSON.parse(localStorage.getItem(cacheKey)) : {}; newCacheData[siteKey] = insertCacheData; localStorage.setItem(cacheKey, JSON.stringify(newCacheData)); })); tagHtml && $btn.append(tagHtml); } catch (e) { const errorString = String(e), siteName = config.id.replace("Btn", ""); if (errorString.includes("Just a moment")) { $btn.attr("title", "请求失败:Cloudflare 安全检查。"); $btn.css("backgroundColor", this.warnBackgroundColor); clog.warn(`检测第三方资源失败, ${siteName} 需Cloudflare安全检查`); } else if (errorString.includes("重定向")) { $btn.attr("title", "域名失效"); $btn.css("backgroundColor", this.domainErrorBackgroundColor); clog.warn(`检测第三方资源失败, ${siteName} 域名被重定向`); } else if (errorString.includes("404 Page Not Found")) { $btn.attr("title", "未查询到, 点击前往搜索页"); $btn.css("backgroundColor", this.errorBackgroundColor); } else { console.error(e); $btn.attr("title", "请求失败。"); $btn.css("backgroundColor", this.errorBackgroundColor); clog.warn(`检测第三方资源失败, ${siteName}`); } } } async getSettingCache() { const now = Date.now(); if (!this.settingCache || now - this.lastFetchTime > this.CACHE_DURATION) { this.settingCache = await storageManager.getSetting(); this.lastFetchTime = now; } return this.settingCache; } async getMissAvUrl() { return (await this.getSettingCache()).missAvUrl || "https://missav.live"; } async getjableUrl() { return (await this.getSettingCache()).jableUrl || "https://jable.tv"; } async getJavTrailersUrl() { return (await this.getSettingCache()).javTrailersUrl || "https://javtrailers.com"; } async getAv123Url() { return (await this.getSettingCache()).av123Url || "https://123av.com"; } async getJavDbUrl() { return (await this.getSettingCache()).javDbUrl || "https://javdb.com"; } async getJavBusUrl() { return (await this.getSettingCache()).javBusUrl || "https://www.javbus.com"; } async getSupJavUrl() { return (await this.getSettingCache()).supJavUrl || "https://supjav.com"; } getEnabledSites() { const enabledSites = localStorage.getItem("jhs_enabled_sites"); return enabledSites ? JSON.parse(enabledSites) : this.siteConfigs.map(((c) => c.id)); } saveEnabledSites(sites) { localStorage.setItem("jhs_enabled_sites", JSON.stringify(sites)); } renderSettingsArea() { const enabledSites = this.getEnabledSites(), checkboxesDiv = document.getElementById("siteCheckboxes"); checkboxesDiv && (checkboxesDiv.innerHTML = this.siteConfigs.map(((config) => { const isEnabled = enabledSites.includes(config.id); return `
    `; })).join("")); } setupEventListeners() { const settingsArea = document.getElementById("settingsArea"); document.addEventListener("click", ((event) => { if ("settingSiteBtn" === event.target.id || event.target.closest("#settingSiteBtn")) { const isHidden = "none" === settingsArea.style.display || "" === settingsArea.style.display; settingsArea.style.display = isHidden ? "block" : "none"; } })); settingsArea.addEventListener("change", ((event) => { if ("checkbox" === event.target.type) { const siteId = event.target.getAttribute("data-site-id"); if (event.target.checked) { $(`#${siteId}`).show(); const carNum2 = this.getPageInfo().carNum, config = this.siteConfigs.find(((item) => item.id === siteId)); this.handleSite(carNum2, config).then(); } else $(`#${siteId}`).hide(); const enabledSites = Array.from(settingsArea.querySelectorAll('input[type="checkbox"]:checked')).map(((checkbox) => checkbox.getAttribute("data-site-id"))); this.saveEnabledSites(enabledSites); } })); } } class ReviewPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "floorIndex", 1); __publicField2(this, "isInit", false); } getName() { return "ReviewPlugin"; } async handle() { $(document).on("click", ".down-115", (async (event) => { const magnet = $(event.currentTarget).data("magnet"); let loadObj = loading(); try { await this.getBean("WangPan115TaskPlugin").handleAddTask(magnet); } catch (e) { show.error("发生错误:" + e); console.error(e); } finally { loadObj.close(); } })); if (window.isDetailPage) { if (isJavDb$1) { const movieId = this.parseMovieId(window.location.href); await this.showReview(movieId); await this.getBean("RelatedPlugin").showRelated($("#magnets-content"), movieId); } if (isJavBus$1) { let carNum2 = this.getPageInfo().carNum; const movies = await javDbApi.searchMovie(carNum2); let movieId = null; for (let i = 0; i < movies.length; i++) { let item = movies[i]; if (item.number.toLowerCase() === carNum2.toLowerCase()) { movieId = item.id; break; } } if (!movieId) return; this.showReview(movieId, $("#sample-waterfall")).then(); } } } async showReview(movieId, $eleBox) { const enableLoadReview = await storageManager.getSetting("enableLoadReview", YES$1), $magnets = $eleBox || $("#magnets-content"); $magnets.append(`
    ❓ 评论区 ${enableLoadReview === YES$1 ? "折叠" : "展开"} ${enableLoadReview === YES$1 ? "▲" : "▼"}
    `); $("#reviewsFold").on("click", ((event) => { event.preventDefault(); event.stopPropagation(); const $text = $("#reviewsFold .toggle-text"), $icon = $("#reviewsFold .toggle-icon"), isFolded = "展开" === $text.text(); $text.text(isFolded ? "折叠" : "展开"); $icon.text(isFolded ? "▲" : "▼"); if (isFolded) { $("#reviewsContainer").show(); $("#reviewsFooter").show(); if (!this.isInit) { this.fetchAndDisplayReviews(movieId); this.isInit = true; } storageManager.saveSettingItem("enableLoadReview", YES$1); } else { $("#reviewsContainer").hide(); $("#reviewsFooter").hide(); storageManager.saveSettingItem("enableLoadReview", NO$1); } })); $magnets.append('
    '); $magnets.append('
    '); enableLoadReview === YES$1 && await this.fetchAndDisplayReviews(movieId); } async fetchAndDisplayReviews(movieId) { const $reviewsContainer = $("#reviewsContainer"), $reviewsFooter = $("#reviewsFooter"); $reviewsContainer.append('
    获取评论中...
    '); const reviewCount = await storageManager.getSetting("reviewCount", 20); let dataList = null; try { dataList = await javDbApi.getReviews(movieId, 1, reviewCount); } catch (e) { e.toString().includes("簽名已過期") && show.error("生成签名失败, 请检查系统时间及时区是否正确!"); clog.error("获取评论失败:", e); console.error("获取评论失败:", e); } finally { $("#reviewsLoading").remove(); } if (!dataList) { $reviewsContainer.append('\n
    \n 获取评论失败\n 重试\n
    \n '); $("#retryFetchReviews").on("click", (async () => { $("#retryFetchReviews").parent().remove(); await this.fetchAndDisplayReviews(movieId); })); return; } if (0 === dataList.length) { $reviewsContainer.append('
    无评论
    '); return; } const reviewKeywordList = await storageManager.getReviewFilterKeywordList(); this.displayReviews(dataList, $reviewsContainer, reviewKeywordList); if (dataList.length === reviewCount) { $reviewsFooter.html('\n \n \n '); let currentPage = 1, $loadMoreReviews = $("#loadMoreReviews"); $loadMoreReviews.on("click", (async () => { $loadMoreReviews.text("加载中...").prop("disabled", true); currentPage++; let moreData; try { moreData = await javDbApi.getReviews(movieId, currentPage, reviewCount); } catch (e) { console.error("加载更多评论失败:", e); } finally { $loadMoreReviews.text("加载失败, 请点击重试").prop("disabled", false); } if (moreData) { this.displayReviews(moreData, $reviewsContainer, reviewKeywordList); if (moreData.length < reviewCount) { $loadMoreReviews.remove(); $("#reviewsEnd").show(); } else $loadMoreReviews.text("加载更多评论").prop("disabled", false); } })); } else $reviewsFooter.html('
    已加载全部评论
    '); } displayReviews(dataList, $container, reviewKeywordList) { if (dataList.length) { dataList.forEach(((item) => { if (reviewKeywordList.some(((keyword) => item.content.includes(keyword)))) return; const starsHtml = Array(item.score).fill('').join(""), content = item.content.replace(/ed2k:\/\/\|file\|[^|]+\|\d+\|[a-fA-F0-9]{32}\|\/|magnet:\?[^\s"'<>`\u4e00-\u9fa5,。?!()【】]+|https?:\/\/[^\s"'<>`\u4e00-\u9fa5,。?!()【】]+/g, ((match) => match.startsWith("ed2k://") ? ` ${match} ` : match.startsWith("magnet:") ? ` ${match} ` : match.startsWith("http://") || match.startsWith("https://") ? ` ${match} ` : match)), commentHtml = `
    #${this.floorIndex++}楼 ${item.username}    ${starsHtml} ${utils.formatDate(item.created_at)}    点赞:${item.likes_count}

    ${content}

    `; $container.append(commentHtml); })); this.rightClickFilter(); } } async rightClickFilter() { await storageManager.getSetting("enableTitleSelectFilter", YES$1) === YES$1 && utils.rightClick(document.body, ".review-content", (async (event) => { const selectedText = window.getSelection().toString(); if (selectedText) { event.preventDefault(); await utils.q(event, `是否将 '${selectedText}' 加入评论区关键词?`, (async () => { await storageManager.saveReviewFilterKeyword(selectedText); show.ok("操作成功, 刷新页面后生效"); })); } })); } } class FilterTitleKeywordPlugin extends BasePlugin { getName() { return "FilterTitleKeywordPlugin"; } async handle() { if (!window.isDetailPage && !window.isFc2Page) return; if (await storageManager.getSetting("enableTitleSelectFilter", YES$1) !== YES$1) return; let targetSelector; isJavDb$1 ? targetSelector = ".title strong, .current-title" : isJavBus$1 && (targetSelector = "h3"); utils.rightClick(document.body, targetSelector, ((event) => { const selectedText = window.getSelection().toString(); if (selectedText) { event.preventDefault(); let tempEvent = { clientX: event.clientX, clientY: event.clientY + 80 }; utils.q(tempEvent, `是否屏蔽标题关键词 ${selectedText}?`, (async () => { await storageManager.saveTitleFilterKeyword(selectedText); window.refresh(); utils.closePage(); })); } })); } } class CoverButtonPlugin extends BasePlugin { getName() { return "CoverButtonPlugin"; } async initCss() { return ` `; } handle() { if (window.isListPage) { this.addSvgBtn(); this.bindClick().then(); } } async addSvgBtn() { $(this.getSelector().itemSelector).toArray().forEach(((ele) => { let $box2 = $(ele); if (!($box2.find(".tool-box").length > 0)) { isJavDb$1 && $box2.find(".tags").append(`
    ${this.screenSvg} ${this.videoSvg}
    ${this.copySvg}
    `); if (isJavBus$1) { if ($box2.find(".avatar-box").length > 0) return; $box2.find(".photo-info").append(`
    ${this.screenSvg} ${this.videoSvg}
    ${this.copySvg}
    `); } } })); this.enableSvgBtn(); } async enableSvgBtn() { const settingObj = await storageManager.getSetting(), { enableScreenSvg = YES$1, enableVideoSvg = YES$1, enableHandleSvg = YES$1, enableSiteSvg = YES$1, enableCopySvg = YES$1 } = settingObj; [{ selector: ".screenSvg", enabled: enableScreenSvg }, { selector: ".videoSvg", enabled: enableVideoSvg }, { selector: ".handleSvg", enabled: enableHandleSvg }, { selector: ".siteSvg", enabled: enableSiteSvg }, { selector: ".copySvg", enabled: enableCopySvg }].forEach((({selector, enabled}) => { $(selector).toggle(enabled === YES$1); })); } async bindClick() { this.getSelector(); const listPagePlugin = this.getBean("ListPagePlugin"); $(document).on("click", ".more-tools-container", ((event) => { event.preventDefault(); var $currentTools = $(event.target).closest(".more-tools-container").find(".more-tools"); $(".more-tools").not($currentTools).stop(true, true).removeClass("elastic-in").addClass("elastic-out").hide(); $currentTools.is(":visible") ? $currentTools.stop(true, true).removeClass("elastic-in").addClass("elastic-out").hide() : $currentTools.stop(true, true).removeClass("elastic-out").addClass("elastic-in").show(); })); $(document).on("click", (function (event) { $(event.target).closest(".more-tools-container").length || $(".more-tools").stop(true, true).removeClass("elastic-in").addClass("elastic-out").hide(); })); $(document).on("click", ".videoSvg", ((event) => { event.preventDefault(); $('.videoSvg[title!="播放视频"]').each(((index, element) => { const $otherSvgElement = $(element); let $otherBox = $otherSvgElement.closest(".item"), $otherImg = $otherBox.find("img"), {carNum: carNum2} = this.getBoxCarInfo($otherBox); this.showImg($otherSvgElement, $otherImg, carNum2); $otherSvgElement.html(this.videoSvg).attr("title", "播放视频"); })); const $currentBox = $(event.target).closest(".item"), $svgElement = $currentBox.find(".videoSvg"); if ("播放视频" === $svgElement.attr("title")) { $svgElement.html(this.recoveryVideoSvg).attr("title", "切回封面"); const {carNum: carNum2} = this.getBoxCarInfo($currentBox); let $img = $currentBox.find("img"); if (!$img.length) { show.error("没有找到图片"); return; } this.showVideo($svgElement, $img, carNum2).then(); } })); $(document).on("click", ".screenSvg", (async (event) => { event.preventDefault(); let loadObj = loading(); try { const $box2 = $(event.currentTarget).closest(".item"); let {carNum: carNum2} = this.getBoxCarInfo($box2); carNum2 = carNum2.replace("FC2-", ""); const imgUrl = await this.getBean("ScreenShotPlugin").getScreenshot(carNum2); loadObj.close(); showPreviewOverlay(imgUrl, carNum2); } catch (error) { console.error("图片预览出错:", error); show.error("图片预览出错:" + error); } finally { loadObj.close(); } })); $(document).on("click", ".filterBtn, .favoriteBtn, .hasDownBtn, .hasWatchBtn", ((event) => { event.preventDefault(); event.stopPropagation(); const $btn = $(event.target).closest(".menu-btn"), $box2 = $btn.closest(".item"), { carNum: carNum2, url, publishTime } = this.getBoxCarInfo($box2), handleAction = async (status) => { let actress = await listPagePlugin.parseActressName(url); await storageManager.saveCar({ carNum: carNum2, url, names: actress, actionType: status, publishTime }); window.refresh(); show.ok("操作成功"); }; $btn.hasClass("filterBtn") ? utils.q(event, `是否屏蔽${carNum2}?`, (() => handleAction(Status_FILTER$1))) : $btn.hasClass("favoriteBtn") ? handleAction(Status_FAVORITE$1).then() : $btn.hasClass("hasDownBtn") ? handleAction(Status_HAS_DOWN$1).then() : $btn.hasClass("hasWatchBtn") && handleAction(Status_HAS_WATCH$1).then(); $(".more-tools").stop(true, true).removeClass("elastic-in").addClass("elastic-out").hide(); })); const otherSitePlugin = this.getBean("OtherSitePlugin"), missAvUrl = await otherSitePlugin.getMissAvUrl(), jableUrl = await otherSitePlugin.getjableUrl(), av123Url = await otherSitePlugin.getAv123Url(); $(document).on("click", ".site-jable, .site-miss-av, .site-123-av", ((event) => { event.preventDefault(); event.stopPropagation(); const $currentTarget = $(event.currentTarget), $box2 = $currentTarget.closest(".item"), {carNum: carNum2} = this.getBoxCarInfo($box2); let url = null; $currentTarget.hasClass("site-jable") ? url = `${jableUrl}/search/${carNum2}/` : $currentTarget.hasClass("site-miss-av") ? url = `${missAvUrl}/search/${carNum2}` : $currentTarget.hasClass("site-123-av") && (url = `${av123Url}/ja/search?keyword=${carNum2}`); event && (event.ctrlKey || event.metaKey) ? _GM_openInTab(url, { insert: 0 }) : window.open(url); })); $(document).on("click", ".titleSvg, .carNumSvg, .downSvg", ((event) => { event.preventDefault(); event.stopPropagation(); const $box2 = $(event.currentTarget).closest(".item"), { carNum: carNum2, title } = this.getBoxCarInfo($box2), $img = $box2.find(isJavBus$1 ? ".photo-frame img" : ".cover img"); $(event.currentTarget).hasClass("titleSvg") ? utils.copyToClipboard("标题", title) : $(event.currentTarget).hasClass("carNumSvg") ? utils.copyToClipboard("番号", carNum2) : $(event.currentTarget).hasClass("downSvg") && fetch($img.attr("src")).then(((response) => response.blob())).then(((blob) => { utils.download(blob, carNum2 + " " + title + ".jpg"); })); })); $(document).on("click", ".markDeleteSvg", (async (event) => { event.preventDefault(); event.stopPropagation(); const $btn = $(event.currentTarget); const $box2 = $btn.closest(".item"); const {carNum: carNum2, title, publishTime, url} = this.getBoxCarInfo($box2); const score = $box2.find(".score").text().trim() || "无评分"; let markedList = await storageManager.forage.getItem("markedDeleteList") || []; const existingIndex = markedList.findIndex((item) => item.carNum === carNum2); if (existingIndex > -1) { markedList.splice(existingIndex, 1); $btn.removeClass("marked"); $btn.attr("title", "标记删除"); show.ok(`已取消标记: ${title}`); } else { markedList.push({ carNum: carNum2, title, publishTime, score, url, markedTime: (/* @__PURE__ */ new Date()).toISOString() }); $btn.addClass("marked"); $btn.attr("title", "取消标记删除"); show.ok(`已标记删除: ${title}`); } await storageManager.forage.setItem("markedDeleteList", markedList); })); $(document).on("click", ".delete115Svg", (async (event) => { event.preventDefault(); event.stopPropagation(); const $btn = $(event.currentTarget); const matchData = $btn.attr("data-match"); if (!matchData) { show.error("未找到115匹配数据"); return; } const matchList = JSON.parse(matchData); const $box2 = $btn.closest(".item"); const {carNum: carNum2} = this.getBoxCarInfo($box2); this.getBean("WangPan115MatchPlugin").showDeleteDialog(carNum2, matchList, (async (newMatchList) => { this.getBean("WangPan115MatchPlugin").updateMatchStatus($box2, carNum2, newMatchList); })); })); } showImg($svgElement, $img, carNum2) { $svgElement.html(this.videoSvg).attr("title", "播放视频"); let $video = $(`#${`${carNum2}_preview_video`}`); if ($video.length > 0) { $video[0].pause(); $video.parent().hide(); } $img.show(); $img.removeClass("loading"); $img.next(".loading-spinner").remove(); } async showVideo($svgElement, $img, carNum2) { const id = `${carNum2}_preview_video`; let $video = $(`#${id}`); if ($video.length > 0) { $video.parent().show(); $video[0].play(); $img.hide(); return; } $img.addClass("loading"); $img.after('
    '); const poster = $img.attr("src"), dmmVideoMap = await getDmmVideo(carNum2); if (!dmmVideoMap) { show.error("未解析到视频"); this.showImg($svgElement, $img, carNum2); return; } let defaultVideoQuality = await storageManager.getSetting("videoQuality"); defaultVideoQuality = selectDefaultQuality(Object.keys(dmmVideoMap), defaultVideoQuality); let videoUrl = dmmVideoMap[defaultVideoQuality], videoHtml = `
    `; isJavBus$1 && (videoHtml = `
    `); $img.parent().append(videoHtml); $img.hide(); $img.removeClass("loading"); $img.next(".loading-spinner").remove(); $video = $(`#${id}`); let videoElement = $video[0]; videoElement.load(); videoElement.muted = false; videoElement.play(); $video.trigger("focus"); } } class Fc2By123AvPlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "$contentBox", $(".section .container")); __publicField2(this, "urlParams", new URLSearchParams(window.location.search)); __publicField2(this, "sortVal", this.urlParams.get("sort") || "release_date"); __publicField2(this, "currentPage", this.urlParams.get("page") ? parseInt(this.urlParams.get("page")) : 1); __publicField2(this, "maxPage", null); __publicField2(this, "keyword", this.urlParams.get("keyword") || null); } getName() { return "Fc2By123AvPlugin"; } async getBaseUrl() { const otherSitePlugin = this.getBean("OtherSitePlugin"); return await otherSitePlugin.getAv123Url() + "/ja"; } handle() { $("#navbar-menu-hero > div > div:nth-child(1) > div > a:nth-child(4)").after('123Av-Fc2'); $('.tabs li:contains("FC2")').after('
  • 123Av-Fc2
  • '); if (currentHref$1.includes("/advanced_search?type=100")) { this.hookPage(); this.handleQuery().then(); } } hookPage() { let $h2 = $("h2.section-title"); $h2.contents().first().replaceWith("123Av"); $h2.css("marginBottom", "0"); $h2.append('\n
    \n \n 搜索\n 重置\n
    \n '); $("#search-123av-keyword").val(this.keyword); $("#search-123av-btn").on("click", (async () => { let keyword = $("#search-123av-keyword").val().trim(); if (keyword) { this.keyword = keyword; utils.setHrefParam("keyword", keyword); await this.handleQuery(); } })); $("#clear-123av-btn").on("click", (async () => { $("#search-123av-keyword").val(""); this.keyword = ""; utils.setHrefParam("keyword", ""); $(".page-box").show(); $(".tool-box").show(); await this.handleQuery(); })); $(".empty-message").remove(); $("#foldCategoryBtn").remove(); $(".section .container .box").remove(); $("#sort-toggle-btn").remove(); this.$contentBox.append('
    '); this.$contentBox.append('
    '); this.$contentBox.append('
    '); $(".tool-box").append('\n \n '); $(`#conditionBox a[data-sort="${this.sortVal}"]`).addClass("is-info"); utils.setHrefParam("sort", this.sortVal); utils.setHrefParam("page", this.currentPage); $("#conditionBox").on("click", "a.button", ((e) => { let $target = $(e.target); this.sortVal = $target.data("sort"); utils.setHrefParam("sort", this.sortVal); $target.siblings().removeClass("is-info"); $target.addClass("is-info"); this.handleQuery(); })); $(".page-box").append('\n \n '); $(document).on("click", ".pagination-link", ((e) => { e.preventDefault(); this.currentPage = parseInt($(e.target).data("page")); utils.setHrefParam("page", this.currentPage); this.renderPagination(); this.handleQuery(); })); $(".pagination-previous").on("click", ((e) => { e.preventDefault(); if (this.currentPage > 1) { this.currentPage--; utils.setHrefParam("page", this.currentPage); this.renderPagination(); this.handleQuery(); } })); $(".pagination-next").on("click", ((e) => { e.preventDefault(); if (this.currentPage < this.maxPage) { this.currentPage++; utils.setHrefParam("page", this.currentPage); this.renderPagination(); this.handleQuery(); } })); } renderPagination() { const $paginationList = $(".pagination-list"); $paginationList.empty(); let startPage = Math.max(1, this.currentPage - 2), endPage = Math.min(this.maxPage, this.currentPage + 2); this.currentPage <= 3 ? endPage = Math.min(6, this.maxPage) : this.currentPage >= this.maxPage - 2 && (startPage = Math.max(this.maxPage - 5, 1)); if (startPage > 1) { $paginationList.append('
  • 1
  • '); startPage > 2 && $paginationList.append('
  • …
  • '); } for (let i = startPage; i <= endPage; i++) { const activeClass = i === this.currentPage ? " is-current" : ""; $paginationList.append(`
  • ${i}
  • `); } if (endPage < this.maxPage) { endPage < this.maxPage - 1 && $paginationList.append('
  • …
  • '); $paginationList.append(`
  • ${this.maxPage}
  • `); } } async handleQuery() { let loadObj = loading(); try { let pagesToFetch = []; pagesToFetch = 1 === this.currentPage ? [1, 2] : [2 * this.currentPage - 1, 2 * this.currentPage]; if (this.keyword) { pagesToFetch = [1]; $(".page-box").hide(); $(".tool-box").hide(); } const baseUrl = await this.getBaseUrl(), fetchPromises = pagesToFetch.map(((page) => { let url = `${baseUrl}/tags/fc2?sort=${this.sortVal}&page=${page}`; this.keyword && (url = `${baseUrl}/search?keyword=${this.keyword}`); return gmHttp.get(url); })), htmlResults = await Promise.all(fetchPromises); let dataList = []; for (const html2 of htmlResults) { let $dom = $(html2); $dom.find(".box-item").each(((index, element) => { const $item = $(element), imgSrc = $item.find("img").attr("data-src"); let carNum2 = $item.find("img").attr("title"); const detailLink = $item.find(".detail a"), link2 = detailLink.attr("href"), href = baseUrl + (link2.startsWith("/") ? link2 : "/" + link2), title = detailLink.text().trim().replace(carNum2 + " - ", ""); carNum2 = carNum2.replace("FC2-PPV", "FC2"); dataList.push({ imgSrc, carNum: carNum2, href, title }); })); if (!this.maxPage) { let rawMaxPage, lastPageItem = $dom.find(".page-item:not(.disabled)").last(); if (lastPageItem.find("a.page-link").length) { let href = lastPageItem.find("a.page-link").attr("href"); rawMaxPage = parseInt(href.split("page=")[1]); } else rawMaxPage = parseInt(lastPageItem.find("span.page-link").text()); this.maxPage = Math.ceil(rawMaxPage / 2); this.renderPagination(); } } if (0 === dataList.length) { console.log(dataList); show.error("无结果"); let errorUrl = `${baseUrl}/dm4/tags/fc2?sort=${this.sortVal}`; this.keyword && (errorUrl = `${baseUrl}/search?keyword=${this.keyword}`); console.error("获取数据失败!", errorUrl); } let movieHtml = this.markDataListHtml(dataList); $(".movie-list").html(movieHtml); await utils.smoothScrollToTop(); } catch (e) { console.error(e); } finally { loadObj.close(); } } async open123AvFc2Dialog(carNum2, href) { let otherHtml = ""; await storageManager.getSetting("enableLoadOtherSite", YES$1) === YES$1 && (otherHtml = '
    第三方站点:
    '); let pageHtml = ` `; layer.open({ type: 1, title: carNum2, content: pageHtml, area: utils.getDefaultArea(), skin: "movie-detail-layer", scrollbar: false, success: (layero, index) => { utils.setupEscClose(index); this.loadData(carNum2, href); let keyword = carNum2.replace("FC2-", ""); $("#magnets-content").append(this.getBean("MagnetHubPlugin").createMagnetHub(keyword)); $("#favoriteBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-publishTime").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_FAVORITE$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#filterBtn").on("click", ((event) => { utils.q(event, `是否屏蔽${carNum2}?`, (async () => { const actress = $("#data-actress").text(), publishTime = $("#data-publishTime").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_FILTER$1, publishTime }); window.refresh(); layer.closeAll(); window.location.href.includes("collection_codes?movieId") && utils.closePage(); })); })); $("#hasDownBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-publishTime").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_HAS_DOWN$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#hasWatchBtn").on("click", (async (event) => { const actress = $("#data-actress").text(), publishTime = $("#data-publishTime").text(); await storageManager.saveCar({ carNum: carNum2, url: href, names: actress, actionType: Status_HAS_WATCH$1, publishTime }); window.refresh(); layer.closeAll(); })); $("#subtitleSearchBtn").on("click", (() => { window.JavPackSubtitle.openSearchModal({details: {code: carNum2}}); })); window.JavPackSubtitle.preload115Matches(carNum2); let tempCarNum = carNum2.replace("FC2-", ""); this.getBean("OtherSitePlugin").loadOtherSite(tempCarNum, carNum2).then(); } }); } async loadData(carNum2, href) { let loadObj = loading(); try { const {id, publishDate, title, moviePoster} = await this.get123AvVideoInfo(href); $(".movie-info-container").html(`

    ${title || "无标题"}

    番号: ${carNum2 || "未知"} 年份: ${publishDate || "未知"} 站点: fc2ppvdb fc2电子市场
    主演:
    販売者:
    `); this.getImgList(carNum2).then(); this.getActressInfo(carNum2).then(); this.getBean("TranslatePlugin").translate(carNum2, false).then(); } catch (e) { console.error(e); } finally { loadObj.close(); } } handleLongImg(carNum2) { utils.loopDetector((() => $(".movie-gallery .image-list").length > 0), (async () => { $(".movie-gallery .image-list").prepend('
    正在加载缩略图
    '); const imgUrl = await this.getBean("ScreenShotPlugin").getScreenshot(carNum2); if (imgUrl) { $(".screen-container").html(``); $(".screen-container").on("click", ((event) => { event.stopPropagation(); event.preventDefault(); showPreviewOverlay(imgUrl, carNum2); })); } })); } async get123AvVideoInfo(href) { const html2 = await gmHttp.get(href), match = html2.match(/v-scope="Movie\({id:\s*(\d+),/), id = match ? match[1] : null, $dom = utils.htmlTo$dom(html2); return { id, publishDate: $dom.find('span:contains("リリース日:")').next("span").text(), title: $dom.find("h1").text().trim(), moviePoster: $dom.find("#player").attr("data-poster") }; } async getActressInfo(fc2Num) { let url = `https://fc2ppvdb.com/articles/${fc2Num.replace("FC2-", "")}`; const html2 = await gmHttp.get(url), $dom = $(html2), actressNodeList = $dom.find("div").filter((function () { return 0 === $(this).text().trim().indexOf("女優:"); })); if (0 === actressNodeList.length || actressNodeList.length > 1) { show.error("解析女优信息失败"); return; } const $actress = $(actressNodeList[0]).find("a"); let actorsHtml = "主演: "; if ($actress.length > 0) { let actress = ""; $actress.each(((index, ele) => { let $actor = $(ele), name2 = $actor.text(), actressHref = $actor.attr("href"); actorsHtml += `${name2}`; actress += name2 + " "; })); $("#data-actress").text(actress); } else actorsHtml += "暂无演员信息"; $(".actor-list").html(actorsHtml); const sellerNodeList = $dom.find("div").filter((function () { return 0 === $(this).text().trim().indexOf("販売者:"); })); if (sellerNodeList.length > 0) { const $sellerA = $(sellerNodeList[0]).find("a"); if ($sellerA.length > 0) { const $seller = $($sellerA[0]); let name2 = $seller.text(), sellerHref = $seller.attr("href"); $(".movie-seller").html(`販売者: ${name2}`); } } } async getImgList(fc2Num) { let tempCarNum = fc2Num.replace("FC2-", ""), url = `https://adult.contents.fc2.com/article/${fc2Num.replace("FC2-", "")}/`; const html2 = await gmHttp.get(url, null, { referer: url }); let imgList = $(html2).find(".items_article_SampleImagesArea img").map((function () { return $(this).attr("src"); })).get(), imagesHtml = ""; Array.isArray(imgList) && imgList.length > 0 ? imagesHtml = imgList.map(((img, index) => ` `)).join("") : $(".movie-gallery").html("

    剧照: 暂无剧照

    "); $(".image-list").html(imagesHtml); this.handleLongImg(tempCarNum); } async getMovie(id, moviePoster) { let url = `${await this.getBaseUrl()}/ajax/v/${id}/videos`, loadObj = loading(); try { let movieList = (await gmHttp.get(url)).result.watch; if (movieList.length > 0) { movieList.forEach(((movieItem) => { movieItem.url = movieItem.url + "?poster=" + moviePoster; })); return movieList; } return null; } catch (e) { console.error(e); } finally { loadObj.close(); } } markDataListHtml(movies) { let moviesHtml = ""; movies.forEach(((movie) => { moviesHtml += ` `; })); return moviesHtml; } } class DetailPageButtonPlugin extends BasePlugin { getName() { return "DetailPageButtonPlugin"; } constructor() { super(); this.answerCount = 1; } async handle() { let settingObj = await storageManager.getSetting(); this.filterHotKey = settingObj.filterHotKey; this.favoriteHotKey = settingObj.favoriteHotKey; this.hasDownHotKey = settingObj.hasDownHotKey; this.hasWatchHotKey = settingObj.hasWatchHotKey; this.speedVideoHotKey = settingObj.speedVideoHotKey; this.bindHotkey().then(); this.hideVideoControls(); window.isDetailPage && this.createMenuBtn(); } async createMenuBtn() { const pageInfo = this.getPageInfo(), carNum2 = pageInfo.carNum, buttonsHtml = '\n
    \n \n \n \n
    \n '; isJavDb$1 && $(".tabs").after(buttonsHtml); isJavBus$1 && $("#mag-submit-show").before(buttonsHtml); $("#favoriteBtn").on("click", (() => this.favoriteOne())); $("#filterBtn").on("click", ((event) => this.filterOne(event))); $("#hasDownBtn").on("click", (async () => this.hasDownOne())); $("#hasWatchBtn").on("click", (async () => this.hasWatchOne())); $("#magnetSearchBtn").on("click", (() => { let magnetHub = this.getBean("MagnetHubPlugin").createMagnetHub(pageInfo.carNum); layer.open({ type: 1, title: "磁力搜索 " + pageInfo.carNum, content: '
    ', area: utils.getResponsiveArea(["60%", "80%"]), scrollbar: false, success: () => { $("#magnetHubBox").append(magnetHub); } }); })); const highlightMagnetPlugin = this.getBean("HighlightMagnetPlugin"), enableMagnetsFilter = await storageManager.getSetting("enableMagnetsFilter", NO$1); if (enableMagnetsFilter === YES$1) { const savedSettings = await storageManager.getSetting("magnetSortAndFilter", "{}"); let settings; try { settings = JSON.parse(savedSettings); } catch (e) { settings = {}; } const sortEnabled = settings.sortEnabled || {}; const defaultFullSortOrder = isJavBus$1 ? ["date", "size"] : ["date", "size", "files"]; const fullSortOrder = settings.sortOrder && settings.sortOrder.length === defaultFullSortOrder.length ? settings.sortOrder : defaultFullSortOrder; const effectiveSortOrder = fullSortOrder.filter((key) => sortEnabled[key]); const applySettings = { filterHD: settings.filterHD || false, filter4K: settings.filter4K || false, filterSubtitle: settings.filterSubtitle || false, filterUncensored: settings.filterUncensored || false, sortOrder: effectiveSortOrder }; const rowReadyCondition = isJavBus$1 ? (() => $("#magnet-table tr").length > 1) : (() => $("#magnets-content .item[data-rank]").length > 0); utils.loopDetector(rowReadyCondition, () => { highlightMagnetPlugin.applySortAndFilter(applySettings); }); $("#magnets-span").text("取消磁力过滤"); } else { $("#magnets-span").text("排序与过滤"); } $("#enable-magnets-filter").on("click", ((event) => { const $span = $("#magnets-span"); if ("取消磁力过滤" === $span.text()) { highlightMagnetPlugin.showAll(); $span.text("排序与过滤"); storageManager.saveSettingItem("enableMagnetsFilter", NO$1); } else { this.openMagnetSortFilterPanel(highlightMagnetPlugin); } })); $("#subtitleSearchBtn").on("click", (() => { window.JavPackSubtitle.openSearchModal({details: {code: carNum2}}); })); $("#downloadCoverBtn").on("click", (async () => { if (!isEligibleDmmCoverCode(carNum2)) { show.error("当前番号不支持下载高清封面"); return; } const $btn = $("#downloadCoverBtn"); $btn.addClass("is-loading").prop("disabled", true); try { const coverUrl = await getCover(carNum2); if (!coverUrl) { show.error("未找到高清封面"); return; } const fileName = `${carNum2}-cover.jpg`; _GM_xmlhttpRequest({ method: "GET", url: coverUrl, responseType: "blob", onload: (response) => { if (response.status === 200) { const blobUrl = URL.createObjectURL(response.response); const a = document.createElement("a"); a.href = blobUrl; a.download = fileName; document.body.appendChild(a); a.click(); setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(blobUrl); }, 100); show.ok("封面下载已开始"); } else { show.error(`封面下载失败: ${response.status}`); } }, onerror: () => show.error("封面下载网络错误"), ontimeout: () => show.error("封面下载超时") }); } catch (e) { show.error("获取封面信息失败"); } finally { $btn.removeClass("is-loading").prop("disabled", false); } })); window.JavPackSubtitle.preload115Matches(carNum2); this.showStatus(carNum2).then(); } async openMagnetSortFilterPanel(highlightMagnetPlugin) { const existingPanel = document.getElementById("magnet-sort-filter-panel"); if (existingPanel) { existingPanel.remove(); return; } const savedSettings = await storageManager.getSetting("magnetSortAndFilter", "{}"); let settings; try { settings = JSON.parse(savedSettings); } catch (e) { settings = {}; } const sortEnabled = settings.sortEnabled || (isJavBus$1 ? {date: false, size: false} : { date: false, size: false, files: false }); const defaultSortOrder = isJavBus$1 ? ["date", "size"] : ["date", "size", "files"]; const sortOrder = settings.sortOrder && settings.sortOrder.length === defaultSortOrder.length ? settings.sortOrder : defaultSortOrder; const filterHD = settings.filterHD || false; const filter4K = settings.filter4K || false; const filterSubtitle = settings.filterSubtitle || false; const filterUncensored = settings.filterUncensored || false; const SORT_LABELS = isJavBus$1 ? {date: "发布日期", size: "文件大小"} : { date: "发布日期", size: "文件大小", files: "文件数量" }; const SORT_HINTS = isJavBus$1 ? {date: "从新到旧", size: "从大到小"} : { date: "从新到旧", size: "从大到小", files: "从少到多" }; const FILTER_ITEMS = [ {key: "HD", label: "高清", checked: filterHD}, {key: "4K", label: "4K", checked: filter4K}, {key: "Subtitle", label: "字幕", checked: filterSubtitle}, {key: "Uncensored", label: "无码", checked: filterUncensored} ]; const currentSortOrder = [...sortOrder]; const currentSortEnabled = {...sortEnabled}; const currentFilters = { HD: filterHD, "4K": filter4K, Subtitle: filterSubtitle, Uncensored: filterUncensored }; const buildSortItemsHTML = () => { return currentSortOrder.map((key) => { const checked = currentSortEnabled[key] ? "checked" : ""; return `
    ⋮⋮
    `; }).join(""); }; const buildFilterItemsHTML = () => { return FILTER_ITEMS.map((item) => { const checked = currentFilters[item.key] ? "checked" : ""; return ``; }).join(""); }; const panelHTML = `
    磁力排序与过滤 ×
    排序优先级(拖动调整顺序,勾选启用)
    ${buildSortItemsHTML()}
    过滤条件(可多选,并集关系)
    ${buildFilterItemsHTML()}
    `; const wrapper = document.createElement("div"); wrapper.innerHTML = panelHTML; document.body.appendChild(wrapper); const panel = document.getElementById("magnet-sort-filter-panel"); const overlay = document.getElementById("msf-overlay"); const closePanel = () => { panel.remove(); overlay.remove(); }; overlay.addEventListener("click", closePanel); document.getElementById("msf-close-btn").addEventListener("click", closePanel); document.getElementById("msf-cancel-btn").addEventListener("click", closePanel); const rebuildSortList = () => { const sortList = document.getElementById("msf-sort-list"); sortList.innerHTML = buildSortItemsHTML(); bindSortEvents(); }; const bindSortEvents = () => { const sortItems = document.querySelectorAll("#msf-sort-list .msf-sort-item"); sortItems.forEach((item) => { item.addEventListener("dragstart", (e) => { e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("text/plain", item.dataset.sortKey); item.classList.add("msf-dragging"); }); item.addEventListener("dragend", () => { item.classList.remove("msf-dragging"); document.querySelectorAll("#msf-sort-list .msf-sort-item").forEach((el) => el.classList.remove("msf-drag-over")); }); item.addEventListener("dragover", (e) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; item.classList.add("msf-drag-over"); }); item.addEventListener("dragleave", () => { item.classList.remove("msf-drag-over"); }); item.addEventListener("drop", (e) => { e.preventDefault(); item.classList.remove("msf-drag-over"); const fromKey = e.dataTransfer.getData("text/plain"); const toKey = item.dataset.sortKey; if (fromKey === toKey) return; const fromIdx = currentSortOrder.indexOf(fromKey); const toIdx = currentSortOrder.indexOf(toKey); currentSortOrder.splice(fromIdx, 1); currentSortOrder.splice(toIdx, 0, fromKey); rebuildSortList(); }); const checkbox = item.querySelector(".msf-sort-checkbox"); checkbox.addEventListener("change", () => { currentSortEnabled[item.dataset.sortKey] = checkbox.checked; }); }); }; bindSortEvents(); document.querySelectorAll(".msf-filter-checkbox").forEach((cb) => { cb.addEventListener("change", () => { currentFilters[cb.dataset.filterKey] = cb.checked; }); }); document.getElementById("msf-confirm-btn").addEventListener("click", async () => { try { const enabledSortOrder = currentSortOrder.filter((key) => currentSortEnabled[key]); const newSettings = { sortOrder: currentSortOrder, sortEnabled: currentSortEnabled, filterHD: currentFilters.HD, filter4K: currentFilters["4K"], filterSubtitle: currentFilters.Subtitle, filterUncensored: currentFilters.Uncensored }; await storageManager.saveSettingItem("magnetSortAndFilter", JSON.stringify(newSettings)); const hasAnyFilter = currentFilters.HD || currentFilters["4K"] || currentFilters.Subtitle || currentFilters.Uncensored; const hasAnySort = enabledSortOrder.length > 0; if (hasAnyFilter || hasAnySort) { await highlightMagnetPlugin.applySortAndFilter({ filterHD: currentFilters.HD, filter4K: currentFilters["4K"], filterSubtitle: currentFilters.Subtitle, filterUncensored: currentFilters.Uncensored, sortOrder: enabledSortOrder }); $("#magnets-span").text("取消磁力过滤"); await storageManager.saveSettingItem("enableMagnetsFilter", YES$1); } else { highlightMagnetPlugin.showAll(); $("#magnets-span").text("排序与过滤"); await storageManager.saveSettingItem("enableMagnetsFilter", NO$1); } } catch (e) { console.error("[磁力排序] 应用失败:", e); } closePanel(); }); } async showStatus(carNum2) { const $filterBtn = $("#filterBtn span"), $favoriteBtn = $("#favoriteBtn span"), $hasDownBtn = $("#hasDownBtn span"), $hasWatchBtn = $("#hasWatchBtn span"), hotKeyDisplay = (hotKey) => hotKey ? `(${hotKey})` : ""; $filterBtn.text(`🚫 屏蔽 ${hotKeyDisplay(this.filterHotKey)}`); $favoriteBtn.text(`⭐ 收藏 ${hotKeyDisplay(this.favoriteHotKey)}`); $hasDownBtn.text(`📥️ 已下载 ${hotKeyDisplay(this.hasDownHotKey)}`); $hasWatchBtn.text(`🔍 已观看 ${hotKeyDisplay(this.hasWatchHotKey)}`); const car = await storageManager.getCar(carNum2); if (car) switch (car.status) { case Status_FILTER$1: $filterBtn.text(`🚫 已屏蔽 ${hotKeyDisplay(this.filterHotKey)}`); break; case Status_FAVORITE$1: $favoriteBtn.text(`⭐ 已收藏 ${hotKeyDisplay(this.favoriteHotKey)}`); break; case Status_HAS_DOWN$1: $hasDownBtn.text(`📥️ 已标记下载 ${hotKeyDisplay(this.hasDownHotKey)}`); break; case Status_HAS_WATCH$1: $hasWatchBtn.text(`🔍 已标记观看 ${hotKeyDisplay(this.hasWatchHotKey)}`); } } async favoriteOne() { let pageInfo = this.getPageInfo(); await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_FAVORITE$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); } async hasDownOne() { let pageInfo = this.getPageInfo(); await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_HAS_DOWN$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); } async hasWatchOne() { let pageInfo = this.getPageInfo(); await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_HAS_WATCH$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); } /* searchXunLeiSubtitle(carNum2) { let loadObj = loading(); gmHttp.get(`https://api-shoulei-ssl.xunlei.com/oracle/subtitle?gcid=&cid=&name=${carNum2}`).then((res => { let dataList = res.data; dataList && 0 !== dataList.length ? layer.open({ type: 1, title: "迅雷字幕", content: '\n
    \n
    \n
    \n ', scrollbar: !1, area: utils.getResponsiveArea([ "60%", "70%" ]), anim: -1, success: (layero, index) => { new Tabulator("#xunlei-table-container", { layout: "fitColumns", placeholder: "暂无数据", virtualDom: !0, data: dataList, responsiveLayout: "collapse", responsiveLayoutCollapse: !0, columnDefaults: { headerHozAlign: "center", hozAlign: "center" }, columns: [ { title: "文件名", field: "name", headerSort: !1, responsive: 0 }, { title: "类型", field: "ext", headerSort: !1, responsive: 0 }, { title: "操作", responsive: 0, headerSort: !1, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); onRendered((() => { const previewButton = cell.getElement().querySelector(".a-primary"), downButton = cell.getElement().querySelector(".a-success"); previewButton && previewButton.addEventListener("click", (async e => { let url = item.url, name2 = carNum2 + "." + item.ext; this.previewSubtitle(url, name2); })); downButton && downButton.addEventListener("click", (async e => { let url = item.url, name2 = carNum2 + "." + item.ext, content = await gmHttp.get(url); utils.download(content, name2); })); })); return '\n 预览\n 下载\n '; } } ], locale: "zh-cn", langs: { "zh-cn": { pagination: { first: "首页", first_title: "首页", last: "尾页", last_title: "尾页", prev: "上一页", prev_title: "上一页", next: "下一页", next_title: "下一页", all: "所有", page_size: "每页行数" } } } }); utils.setupEscClose(index); } }) : show.error("迅雷中找不到相关字幕!"); })).catch((e => { console.error(e); show.error(e); })).finally((() => { loadObj.close(); })); } */ async filterOne(event, noAlert) { event && event.preventDefault(); let pageInfo = this.getPageInfo(); if (noAlert) { await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_FILTER$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); layer.closeAll(); this.answerCount = 1; } else utils.q(event, `是否屏蔽${pageInfo.carNum}?`, (async () => { await storageManager.saveCar({ carNum: pageInfo.carNum, url: pageInfo.url, names: pageInfo.actress, actionType: Status_FILTER$1, publishTime: pageInfo.publishTime }); this.showStatus(pageInfo.carNum).then(); window.refresh(); utils.closePage(); }), (() => { this.answerCount = 1; })); } speedVideo() { if ($("#preview-video").is(":visible")) { const videoEl = document.getElementById("preview-video"); if (videoEl) { videoEl.muted = false; videoEl.controls = false; if (videoEl.currentTime + 5 < videoEl.duration) videoEl.currentTime += 5; else { show.info("预览视频结束, 已回到开头"); videoEl.currentTime = 1; } } return; } const iframe = $('iframe[id^="layui-layer-iframe"]'); if (iframe.length > 0) { iframe[0].contentWindow.postMessage("speedVideo", "*"); return; } let $videoPlayBtn = $(".preview-video-container"); if ($videoPlayBtn.length > 0) { $videoPlayBtn[0].click(); const videoEl = document.getElementById("preview-video"); if (videoEl) { videoEl.currentTime += 5; videoEl.muted = false; } } else $("#javTrailersBtn").click(); } hideVideoControls() { $(document).on("mouseenter", "#preview-video", (function () { $(this).prop("controls", true); })); } async bindHotkey() { const handlers = {}; this.filterHotKey && (handlers[this.filterHotKey] = () => { this.answerCount >= 2 ? this.filterOne(null, true) : this.filterOne(null); this.answerCount++; }); this.favoriteHotKey && (handlers[this.favoriteHotKey] = () => this.favoriteOne(null)); this.hasDownHotKey && (handlers[this.hasDownHotKey] = () => this.hasDownOne()); this.hasWatchHotKey && (handlers[this.hasWatchHotKey] = () => this.hasWatchOne()); this.speedVideoHotKey && (handlers[this.speedVideoHotKey] = () => this.speedVideo()); const registerHotkey = (key, handler) => { HotkeyManager.registerHotkey(key, ((event) => { const activeElement = document.activeElement; "INPUT" === activeElement.tagName || "TEXTAREA" === activeElement.tagName || activeElement.isContentEditable || (window.isDetailPage ? handler() : ((message) => { const childIframe = $(".layui-layer-content iframe"); if (0 === childIframe.length) return false; childIframe[0].contentWindow.postMessage(message, "*"); })(key)); })); }; window.isDetailPage && window.addEventListener("message", ((event) => { handlers[event.data] && handlers[event.data](); })); Object.entries(handlers).forEach((([key, handler]) => { registerHotkey(key, handler); })); } async previewSubtitle(url, name2) { if (!url) { console.error("未提供文件URL"); return; } const fileExt = url.split(".").pop().toLowerCase(); if ("ass" === fileExt || "srt" === fileExt) try { let resText = await gmHttp.get(url), title = "字幕预览"; "ass" === fileExt ? title = "ASS字幕预览 - " + name2 : "srt" === fileExt && (title = "SRT字幕预览 - " + name2); const lines = resText.split("\n"); let numberedContent = ""; const maxLineNumberLength = String(lines.length).length; lines.forEach(((line, index) => { const paddedLineNumber = String(index + 1).padStart(maxLineNumberLength, " "); numberedContent += `${paddedLineNumber}. ${line} `; })); const finalContent = numberedContent; layer.open({ type: 1, title, area: ["80%", "80%"], scrollbar: false, content: `
    ${finalContent}
    `, btn: ["下载", "关闭"], btn1: function (index, layero, that) { utils.download(resText, name2); return false; } }); } catch (error) { show.error(`预览失败: ${error.message}`); console.error("预览字幕文件出错:", error); } else show.error("仅支持预览ASS和SRT字幕文件"); } } class BlacklistPlugin extends BasePlugin { getName() { return "BlacklistPlugin"; } async addBlacklist(event) { let tempEvent = { clientX: event.clientX, clientY: event.clientY + 80 }; const hasAddBlacklist = $("#addBlacklistBtn span").text().includes("已加入"); let dataInfo, title; if (currentHref$1.includes("/tags")) { const urlObj = new URL(currentHref$1); urlObj.searchParams.delete("page"); const checkTag = $("#jhs-check-tag").text().trim(); dataInfo = { starId: "no-" + checkTag, name: "虚拟演员-" + checkTag, allName: ["虚拟演员"], role: "虚拟演员", movieType: "虚拟演员", blacklistUrl: urlObj.toString() }; title = `是否将分类 ${checkTag} 加入到黑名单中?`; hasAddBlacklist && (title = `分类 ${checkTag} 已在黑名单中, 是否从当前页开始追加屏蔽?`); } else { dataInfo = this.getActressPageInfo(); title = `是否将该演员 ${dataInfo.name} 加入到黑名单中?`; hasAddBlacklist && (title = `演员 ${dataInfo.name} 已在黑名单中, 是否从当前页开始追加屏蔽?`); } const {starId, name: name2, allName, role, movieType, blacklistUrl} = dataInfo; currentHref$1.includes("page") && !currentHref$1.includes("page=1") && (title += "
    注意: 当前页面非第一页, 屏蔽数据将从此页面开始"); if (isJavBus$1) { const afterStarParts = currentHref$1.split("/star/")[1].split("/"); if (afterStarParts.length > 1) { parseInt(afterStarParts[1]) > 1 && (title += "
    注意: 当前页面非第一页, 屏蔽数据将从此页面开始"); } } utils.q(tempEvent, title, (async () => { const taskPlugin = this.getBean("TaskPlugin"); navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { if (lock) { this.loadObj = loading(); try { await storageManager.addBlacklistItem({ starId, name: name2, allName, role, movieType, url: blacklistUrl }); await this.filterActorVideo(name2, starId); const toast = show.ok(`屏蔽结束,是否跳转到最后一页: ${this.lastPageLink || "无"}`, { duration: -1, close: true, onClick: () => { toast.closeShow(); this.lastPageLink && (window.location.href = this.lastPageLink); } }); } catch (e) { clog.error(e); const toast = show.error("发生错误, 是否填转到解析失败的那一页? (点击并跳转)", { duration: -1, close: true, onClick: () => { toast.closeShow(); window.location.href = this.nextPageLink; } }); } finally { this.loadObj.close(); } } else show.error("当前有定时任务在后台执行中, 无法发起此操作"); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); } async resetBtnTip() { const taskPlugin = this.getBean("TaskPlugin"), lastCheckBlacklistTimeStr = localStorage.getItem(taskPlugin.lastCheckBlacklistTimeKey) || "无", checkBlacklist_intervalTime = await storageManager.getSetting("checkBlacklist_intervalTime", 12); this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime", 8760); $("#checkBlacklistBtn").attr("data-tip", `上次检测时间: ${lastCheckBlacklistTimeStr}; 检测间隔时间: ${checkBlacklist_intervalTime}小时`); } async openBlacklistDialog() { const taskPlugin = this.getBean("TaskPlugin"), settingObj = await storageManager.getSetting(); let html2 = `
    重置
    `; layer.open({ type: 1, title: "演员黑名单", content: html2, scrollbar: false, area: utils.getResponsiveArea(["80%", "90%"]), anim: -1, success: async (layero) => { await this.loadTableData(); $(".layui-layer-content").on("click", "#cleanQueryBtn", (async (event) => { $("#searchValue").val(""); $("#dataType").val(""); $("#statusType").val(""); await this.reloadTable(); })).on("focusout keydown", "#searchValue", (async (event) => { if ("focusout" === event.type || "Enter" === event.key) { "Enter" === event.key && event.preventDefault(); if ("keydown" === event.type && "Enter" !== event.key) return; $("#dataType").val(""); await this.reloadTable(); } })).on("change", "#dataType", (async () => { $("#searchValue").val(""); await this.reloadTable(); })).on("change", "#statusType", (async () => { await this.reloadTable(); })).on("change", "#urlType", (async () => { await this.reloadTable(); })).on("click", "#toSetting", (() => { this.getBean("SettingPlugin").openSettingDialog("task-panel", (() => { $("#setting-blacklist").css({ border: "1px solid #f40" }); })); })).on("click", "#clearKeywordBlacklist", (async () => { await this.clearKeywordBlacklist(); })).on("click", ".open-url", ((event) => { event.preventDefault(); const $el = $(event.currentTarget), url = $el.attr("data-url"), name2 = $el.attr("data-name"); utils.openPage(url, name2, true, event); })).on("click", "#checkBlacklistBtn", ((event) => { utils.q({ clientX: event.clientX, clientY: event.clientY + 20 }, "是否手动检测黑名单?", (() => { navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: true }, (async (lock) => { if (lock) { await taskPlugin.loadConfig(); await taskPlugin.checkBlacklist(true); } else show.error("当前有定时任务在后台执行中, 无法发起手动任务"); })).catch(((error) => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); })); }, end: () => { if (this.tableObj) { this.tableObj.destroy(); this.tableObj = null; } window.refresh(); } }); } async reloadTable() { if (!this.tableObj) return; const tableData = await this.getTableData(); this.tableObj.setData(tableData); } async getTableData() { const taskPlugin = this.getBean("TaskPlugin"), blacklist = await storageManager.getBlacklist(), blacklistCarList = await storageManager.getBlacklistCarList(), searchValue = $("#searchValue").val(), statusType = $("#statusType").val(), $dataType = $("#dataType"), dataType = $dataType.val(), urlType = $("#urlType").val(), blacklistCount = blacklist.length; let actorCount = 0, actressCount = 0, noStarCount = 0; const filteredBlacklist = blacklist.map(((item) => { "actor" === item.role ? actorCount++ : "actress" === item.role ? actressCount++ : "虚拟演员" === item.role && noStarCount++; let isUnCheck = false; item.lastPublishTime && (isUnCheck = !taskPlugin.isUnnecessaryCheck(item.lastPublishTime, this.checkBlacklist_ruleTime)); return { ...item, isUnCheck }; })).filter(((item) => !(searchValue && !item.name.includes(searchValue)) && (("normal" !== statusType || !item.isUnCheck) && (!("stop" === statusType && !item.isUnCheck) && (dataType ? item.role === dataType : !("hasT" === urlType && !item.url.includes("t=") && !item.url.includes("/tags")) && ("noT" !== urlType || !item.url.includes("t=") && !item.url.includes("/tags"))))))); $dataType.html(` `); $dataType.val(dataType); const carListMap = /* @__PURE__ */ new Map(); for (const car of blacklistCarList) { const starId = car.starId; carListMap.has(starId) || carListMap.set(starId, []); carListMap.get(starId).push(car); } const finalResult = filteredBlacklist.map(((item) => { const starId = item.starId, carList = carListMap.get(starId) || []; return { ...item, carList, count: carList.length }; })); this.currentCarCount = finalResult.reduce(((accumulator, currentObject) => accumulator + (currentObject.count || 0)), 0); return finalResult; } async loadTableData() { this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime") || 8760; const tableData = await this.getTableData(); this.tableObj = new Tabulator("#table-container", { layout: "fitColumns", placeholder: "暂无数据", virtualDom: true, data: tableData, pagination: true, paginationMode: "local", paginationSize: 20, paginationSizeSelector: [20, 50, 100, 1e3, 99999], paginationCounter: (pageSize, currentRow, currentPage, totalRows, totalPages) => `演员: ${totalRows}    番号总数: ${this.currentCarCount} `, responsiveLayout: "collapse", responsiveLayoutCollapse: true, columnDefaults: { headerHozAlign: "center", hozAlign: "center" }, index: "starId", columns: [{ title: "演员", field: "name", sorter: "string", minWidth: 100, responsive: 0, headerSort: false, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); return `${item.name}`; } }, { title: "性别角色", field: "role", sorter: "string", width: 120, responsive: 5, formatter: (cell, formatterParams, onRendered) => { const role = cell.getData().role; let content = role; "actor" === role ? content = "男演员" : "actress" === role && (content = "女演员"); return content; } }, { title: "影视类别", field: "movieType", sorter: "string", width: 120, responsive: 5, formatter: (cell, formatterParams, onRendered) => { const movieType = cell.getData().movieType; let content = movieType; "censored" === movieType ? content = "有码" : "uncensored" === movieType && (content = "无码"); return content; } }, { title: "屏蔽类型", field: "url", sorter: "string", minWidth: 120, responsive: 4, visible: isJavDb$1, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); if ("虚拟演员" === item.role) { const nameSplitList = item.name.split("-"); return `${nameSplitList.length > 0 ? nameSplitList[1] : item.name}`; } const tParam = utils.getUrlParam(item.url, "t"); if (!tParam) return "所有分类"; { const tParamList = tParam.toString().split(","); let selectTypeStr = ""; tParamList.forEach(((i) => { let value = categoryMap[i]; selectTypeStr += value ? value + " " : "未知类别:" + i + " "; })); selectTypeStr = selectTypeStr.trim(); if (selectTypeStr) return `${selectTypeStr}`; } } }, { title: "番号数量", field: "count", sorter: "number", width: 170, responsive: 1 }, { title: "创建时间", field: "createTime", sorter: "string", width: 170, responsive: 5 }, { title: "最后发行时间", field: "lastPublishTime", sorter: "string", width: 170, responsive: 1 }, { title: "状态", field: "isUnCheck", sorter: "string", width: 120, responsive: 1, formatter: (cell, formatterParams, onRendered) => { let dataTip = "", content = "正常检测"; if (cell.getData().isUnCheck) { dataTip = `停更${this.checkBlacklist_ruleTime / 24 / 365}年以上, 下轮任务不再进行检测`; content = "停止检测"; } return `${content}`; } }, { title: "操作", sorter: "string", cssClass: "action-cell-dropdown", minWidth: 150, responsive: 0, headerSort: false, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); onRendered((() => { var _a2, _b; null == (_a2 = cell.getElement().querySelector(".delete-btn")) || _a2.addEventListener("click", ((e) => { const name2 = item.name, starId = item.starId; name2 ? starId ? utils.q(e, `是否移除对 ${name2} 的屏蔽?`, (async () => { await storageManager.removeBlacklistCarList(starId); await storageManager.deleteBlacklistItem(starId); show.info("操作成功"); this.reloadTable().then(); })) : show.error("获取starId失败") : show.error("获取名称失败"); })); null == (_b = cell.getElement().querySelector(".keyword-btn")) || _b.addEventListener("click", ((e) => { const prefixMap = item.carList.reduce(((dataMap, carItem) => { const prefix = carItem.carNum.split("-")[0] + "-"; dataMap[prefix] = (dataMap[prefix] || 0) + 1; return dataMap; }), {}), sortedPrefixList = Object.entries(prefixMap).map((([prefix, count]) => ({ prefix, count }))).sort(((a, b) => b.count - a.count)); console.log(sortedPrefixList); })); })); return '\n \n ✂️ 删除 \n '; } }], initialSort: [{ column: "createTime", dir: "desc" }], locale: "zh-cn", langs: { "zh-cn": { pagination: { first: "首页", first_title: "首页", last: "尾页", last_title: "尾页", prev: "上一页", prev_title: "上一页", next: "下一页", next_title: "下一页", all: "所有", page_size: "每页行数" } } } }); } getCurrentStarUrl() { let urlWithoutSortType = window.location.href.replace(/([&?])sort_type=[^&]+(&|$)/, "$1"); urlWithoutSortType = urlWithoutSortType.replace(/[&?]$/, ""); urlWithoutSortType = urlWithoutSortType.replace(/\?&/, "?"); let urlWithoutPageNumber = urlWithoutSortType; urlWithoutPageNumber = urlWithoutPageNumber.replace(/([&?])page=\d+(&|$)/, "$1"); urlWithoutPageNumber = urlWithoutPageNumber.replace(/[&?]$/, ""); urlWithoutPageNumber = urlWithoutPageNumber.replace(/\?&/, "?"); urlWithoutPageNumber = urlWithoutPageNumber.replace(/\/(\d+)(?:\/(\d+))?(\?|$)/, ((match, id, page, suffix) => void 0 !== page ? `/${id}${suffix}` : match)); return urlWithoutPageNumber; } parseUrlId(url) { if (!url) throw new Error("url未传入"); return new URL(url).pathname.split("/").filter(((segment) => "" !== segment.trim())).pop(); } async filterAllVideo(actorName, $dom) { let movieList, nextPageLink; if ($dom) { isJavBus$1 && $dom.find(".avatar-box").length > 0 && $dom.find(".avatar-box").parent().remove(); movieList = $dom.find(this.getSelector().requestDomItemSelector); nextPageLink = $dom.find(this.getSelector().nextPageSelector).attr("href"); } else { movieList = $(this.getSelector().itemSelector); nextPageLink = $(this.getSelector().nextPageSelector).attr("href"); } if (nextPageLink && 0 === movieList.length) { show.error("解析列表失败"); throw new Error("解析列表失败"); } for (const element of movieList) { const $item = $(element), {carNum: carNum2, url, publishTime} = this.getBoxCarInfo($item); if (url && carNum2) try { if (await storageManager.getCar(carNum2)) continue; await storageManager.saveCar({ carNum: carNum2, url, names: actorName, actionType: Status_FILTER$1, publishTime }); clog.log("屏蔽演员番号", actorName, carNum2); } catch (error) { console.error(`保存失败 [${carNum2}]:`, error); } } if (nextPageLink) { show.info("请不要关闭窗口, 正在解析下一页:" + nextPageLink); await new Promise(((resolve) => setTimeout(resolve, 500))); const html2 = await gmHttp.get(nextPageLink), parser = new DOMParser(), next$dom = $(parser.parseFromString(html2, "text/html")); await this.filterAllVideo(actorName, next$dom); } else { show.ok("执行结束!"); window.refresh(); } } async filterActorVideo(actorName, starId, $dom) { let {nextPageLink} = await this.parseAndSaveFilterInfo($dom, actorName, starId); this.nextPageLink = nextPageLink; if (nextPageLink) { this.lastPageLink = nextPageLink; show.info("请不要关闭窗口, 正在解析下一页:" + nextPageLink); let next$dom; const pageNum = utils.getUrlParam(nextPageLink, "page") || 0, beyond60Plugin = this.getBean("Beyond60Plugin"); if (isJavDb$1 && beyond60Plugin && pageNum > 60) { let {html: html2, nextUrl, hasMore} = await beyond60Plugin.handleBeyond60(nextPageLink), mergeHtml = `
    ${html2}
    ${nextUrl ? `` : ""} `; next$dom = utils.htmlTo$dom(mergeHtml); } else { clog.log("正在请求下一页内容:", nextPageLink); const html2 = await gmHttp.get(nextPageLink); next$dom = utils.htmlTo$dom(html2); } await this.filterActorVideo(actorName, starId, next$dom); } else { show.ok("执行结束!"); window.refresh(); } } async parseAndSaveFilterInfo($dom, actorName, starId) { let movieList, nextPageLink; if ($dom) { let tempIsJavBus = false, selectorType = "javdb"; if ($dom.text().includes("javbus")) { tempIsJavBus = true; selectorType = "javbus"; } tempIsJavBus && $dom.find(".avatar-box").length > 0 && $dom.find(".avatar-box").parent().remove(); movieList = $dom.find(this.getSelector(selectorType).requestDomItemSelector); nextPageLink = $dom.find(this.getSelector(selectorType).nextPageSelector).attr("href"); } else { movieList = $(this.getSelector().itemSelector); nextPageLink = $(this.getSelector().nextPageSelector).attr("href"); } if (nextPageLink && 0 === movieList.length) return { nextPageLink: null, lastPublishTime: null }; const filterKeywordList = await storageManager.getTitleFilterKeyword(); let carDataList = [], lastPublishTime = null; for (const element of movieList) { const $item = $(element), {carNum: carNum2, url, publishTime, title} = this.getBoxCarInfo($item); lastPublishTime || (lastPublishTime = publishTime); filterKeywordList.find(((kw) => title.includes(kw) || carNum2.startsWith(kw))) || url && carNum2 && carDataList.push({ carNum: carNum2, url, names: actorName, actionType: Status_FILTER$1, starId, publishTime }); } await storageManager.batchSaveBlacklistCarList(carDataList); return { nextPageLink, lastPublishTime }; } async clearKeywordBlacklist() { let filterKeywordList = await storageManager.getTitleFilterKeyword(); const processedKeywordsSet = /* @__PURE__ */ new Set(); filterKeywordList.forEach(((item) => { /^[a-z]{2,}-/i.test(item) && processedKeywordsSet.add(item); })); if (0 === processedKeywordsSet.size) { show.info("没有需要清理的关键词数据"); return; } const carList = await storageManager.getCarList(), blacklistCarList = await storageManager.getBlacklistCarList(), carNumSet = new Set(carList.map(((car) => car.carNum))), keywords = Array.from(processedKeywordsSet), waitRemoveCarNumList = [], removableCarData = []; for (const car of blacklistCarList) { const carNum2 = car.carNum; let matched = false; for (const keyword of keywords) if (carNum2.startsWith(keyword)) { removableCarData.push({ carNum: carNum2, names: car.names, matchedKeyword: keyword }); waitRemoveCarNumList.push(carNum2); matched = true; break; } if (!matched && carNumSet.has(carNum2)) { removableCarData.push({ carNum: carNum2, names: car.names, matchedKeyword: "已在鉴定记录中" }); waitRemoveCarNumList.push(carNum2); } } const carNumCount = waitRemoveCarNumList.length; 0 !== carNumCount ? layer.open({ type: 1, title: `确认清理黑名单数据 (共 ${carNumCount} 个番号)`, area: ["80%", "70%"], anim: -1, content: `

    以下匹配到 ${carNumCount} 个黑名单番号存在于屏蔽关键词中, 是否移除?

    `, btn: ["确定清理", "取消"], success: function (layero, index) { new Tabulator("#wait-remove-table", { data: removableCarData, virtualDom: true, layout: "fitColumns", pagination: true, paginationMode: "local", paginationSize: 50, paginationSizeSelector: [50, 100, 1e3, 99999], columns: [{ title: "演员", field: "names" }, { title: "番号", field: "carNum" }, { title: "匹配关键词", field: "matchedKeyword" }], locale: "zh-cn", initialSort: [{ column: "names", dir: "asc" }], langs: { "zh-cn": { pagination: { first: "首页", first_title: "首页", last: "尾页", last_title: "尾页", prev: "上一页", prev_title: "上一页", next: "下一页", next_title: "下一页", all: "所有", page_size: "每页行数" } } } }); }, yes: async (index) => { const removedCount = await storageManager.batchRemoveBlacklistCars(waitRemoveCarNumList); if (0 === removedCount) show.error("移除失败"); else { show.ok(`🎉 清理完成!已移除${removedCount}个相关黑名单番号数据`); await this.reloadTable(); layer.close(index); } }, btn2: function (index) { layer.close(index); } }) : show.info("没有需要清理的黑名单番号数据"); } } class ListPagePlugin extends BasePlugin { constructor() { super(...arguments); __publicField2(this, "currentPageFilterCount", 0); __publicField2(this, "currentPageFavoriteCount", 0); __publicField2(this, "currentPageHasDownCount", 0); __publicField2(this, "currentPageHasWatchCount", 0); __publicField2(this, "currentPageKeywordFilterCount", 0); __publicField2(this, "currentPageActorFilterCount", 0); __publicField2(this, "currentPageWaitCheckCount", 0); __publicField2(this, "currentPageTotalCount", 0); __publicField2(this, "cache", localStorage.getItem("jhs_translate") ? JSON.parse(localStorage.getItem("jhs_translate")) : {}); __publicField2(this, "writeQueue", Promise.resolve()); } getName() { return "ListPagePlugin"; } async handle() { this.cleanRepeatId(); this.replaceHdImg(); this.addJumpPageControl(); this.fixBusTitleBox(); await this.doFilter(); this.bindClick().then(); this.bindListPageHotKey().then(); this.rememberTagExpand(); $(this.getSelector().itemSelector + " a").attr("target", "_blank"); this.checkDom(); } rememberTagExpand() { if (!window.location.href.includes("actors")) return; const $expandButton = $(".tag-expand"); if (0 === $expandButton.length) return; const isExpanded = "true" === localStorage.getItem("jhs_tag_expand"), $actorTagsContent = $(".actor-tags .content"); isExpanded && $actorTagsContent.hasClass("collapse") && $expandButton[0].click(); $expandButton.on("click", (function () { const newState = !$actorTagsContent.hasClass("collapse"); localStorage.setItem("jhs_tag_expand", newState.toString()); })); } checkDom() { if (!window.isListPage) return; const selector = this.getSelector(), targetNode = document.querySelector(selector.boxSelector); if (!targetNode) { console.error("没有找到容器节点!"); return; } const observer = new MutationObserver((async (mutations) => { observer.disconnect(); try { this.replaceHdImg(); this.addJumpPageControl(); this.fixBusTitleBox(); this.getBean("CoverButtonPlugin").addSvgBtn().then(); await this.doFilter(); await this.getBean("ListPageButtonPlugin").sortItems(); $(this.getSelector().itemSelector + " a").attr("target", "_blank"); this.getBean("AutoPagePlugin").checkLoad(); } finally { observer.observe(targetNode, config); } })), config = { childList: true, subtree: false }; observer.observe(targetNode, config); } fixBusTitleBox() { if (!isJavBus$1) return; $(this.getSelector().itemSelector).toArray().forEach(((ele) => { var _a2; let $box2 = $(ele); if ($box2.find(".avatar-box").length > 0) return; const title = (null == (_a2 = $box2.find("img").attr("title")) ? void 0 : _a2.trim()) || ""; $box2.find(".photo-info span:first").contents().first().wrap(`${title}`); $box2.find("br").remove(); })); } cleanRepeatId() { if (!isJavBus$1) return; $("#waterfall_h").removeAttr("id").attr("id", "no-page"); const $waterfalls = $('[id="waterfall"]'); 0 !== $waterfalls.length && $waterfalls.each((function () { const $current = $(this); if (!$current.hasClass("masonry")) { $current.children().insertAfter($current); $current.remove(); } })); } async doFilter() { if (!window.isListPage) return; let movieList = $(this.getSelector().itemSelector).toArray(); if (movieList.length) { await this.filterMovieList(movieList); await this.getBean("WangPan115MatchPlugin").matchMovieList(movieList); isJavBus$1 && await this.getBean("BusImgPlugin").logImageHeightsByRow(); } } async filterMovieList(movieList) { utils.time("累计耗费时间"); utils.time("读取数据耗时"); const [carList, filterKeywordList, blacklist, blacklistCarList, settingObj] = await Promise.all([storageManager.getCarList(), storageManager.getTitleFilterKeyword(), storageManager.getBlacklist(), storageManager.getBlacklistCarList(), storageManager.getSetting()]), t1 = utils.time("读取数据耗时"), carNumSets = carList.reduce(((dataMap, item) => { const statusKey = item.status; dataMap.hasOwnProperty(statusKey) && dataMap[statusKey].add(item.carNum); return dataMap; }), { [Status_FILTER$1]: /* @__PURE__ */ new Set(), [Status_FAVORITE$1]: /* @__PURE__ */ new Set(), [Status_HAS_DOWN$1]: /* @__PURE__ */ new Set(), [Status_HAS_WATCH$1]: /* @__PURE__ */ new Set() }); utils.time("组装数据耗时"); const starIdToRoleMap = new Map(blacklist.map(((item) => [item.starId, item.role]))), { actorCarNumToNameMap, actressCarNumToNameMap } = blacklistCarList.reduce(((dataMap, carItem) => { const role = starIdToRoleMap.get(carItem.starId); if (!role) { clog.error("黑名单数据源丢失演员信息", carItem); return dataMap; } const targetMap = "actor" === role ? dataMap.actorCarNumToNameMap : dataMap.actressCarNumToNameMap; targetMap.has(carItem.carNum) || targetMap.set(carItem.carNum, carItem.names); return dataMap; }), { actorCarNumToNameMap: /* @__PURE__ */ new Map(), actressCarNumToNameMap: /* @__PURE__ */ new Map() }), t2 = utils.time("组装数据耗时"), settings = { showFilterItem: (null == settingObj ? void 0 : settingObj.showFilterItem) ?? NO$1, showFilterActorItem: (null == settingObj ? void 0 : settingObj.showFilterActorItem) ?? NO$1, showFilterKeywordItem: (null == settingObj ? void 0 : settingObj.showFilterKeywordItem) ?? NO$1, showFavoriteItem: (null == settingObj ? void 0 : settingObj.showFavoriteItem) ?? YES$1, showHasDownItem: (null == settingObj ? void 0 : settingObj.showHasDownItem) ?? YES$1, showHasWatchItem: (null == settingObj ? void 0 : settingObj.showHasWatchItem) ?? YES$1, showAllItem: (null == settingObj ? void 0 : settingObj.showAllItem) ?? NO$1, tagPosition: (null == settingObj ? void 0 : settingObj.tagPosition) || "rightTop", movieShowType: (null == settingObj ? void 0 : settingObj.movieShowType) || "hide" }; this.currentPageFilterCount = 0; this.currentPageFavoriteCount = 0; this.currentPageHasDownCount = 0; this.currentPageHasWatchCount = 0; this.currentPageKeywordFilterCount = 0; this.currentPageActorFilterCount = 0; this.currentPageWaitCheckCount = 0; this.currentPageTotalCount = 0; utils.time("处理页面耗时"); await Promise.all(movieList.map((async (ele) => { let $box2 = $(ele); if (isJavBus$1 && $box2.find(".avatar-box").length > 0) return; const boxInfo = this.getBoxCarInfo($box2); if (!boxInfo) return; const {carNum: carNum2, title} = boxInfo, { filter, favorite, hasDown, hasWatch } = carNumSets, isFavorite = favorite.has(carNum2), isHasDown = hasDown.has(carNum2), isHasWatch = hasWatch.has(carNum2), isFiltered = filter.has(carNum2), isFilterActorMale = actorCarNumToNameMap.has(carNum2), isFilterActorFemale = actressCarNumToNameMap.has(carNum2), isFilterActor = isFilterActorMale || isFilterActorFemale, foundKeyword = filterKeywordList.find(((kw) => title.includes(kw) || carNum2.startsWith(kw))), isFilterKeyword = !!foundKeyword; if (!isSearchPage) { let shouldHide = settings.showFavoriteItem === NO$1 && isFavorite || settings.showHasDownItem === NO$1 && isHasDown || settings.showHasWatchItem === NO$1 && isHasWatch || settings.showFilterItem === NO$1 && isFiltered && !(isFavorite || isHasDown || isHasWatch) || settings.showFilterActorItem === NO$1 && isFilterActor || settings.showFilterKeywordItem === NO$1 && isFilterKeyword; if ($box2.attr("data-movieShowType") !== settings.movieShowType) { $box2.css("border", ""); $box2.children().css("visibility", ""); $box2.removeAttr("data-hide"); $box2.show(); } const isCurrentlyHidden = $box2.attr("data-hide") === YES$1; settings.showAllItem === YES$1 && (shouldHide = false); if (shouldHide !== isCurrentlyHidden) { shouldHide ? $box2.attr("data-hide", YES$1) : $box2.removeAttr("data-hide"); if ("hide" === settings.movieShowType) shouldHide ? $box2.hide() : $box2.show(); else { if ("visibility" !== settings.movieShowType) throw new Error("movieShowType值有误:" + settings.movieShowType); { const $content = $box2.children(), borderStyle = shouldHide ? "1px solid rgb(192 176 176)" : "none", visibilityValue = shouldHide ? "hidden" : "visible"; $box2.css("border", borderStyle); $content.css("visibility", visibilityValue); } } $box2.attr("data-movieShowType") !== settings.movieShowType && $box2.attr("data-movieShowType", settings.movieShowType); } } let tag = TAG_STYLES.IS_WAIT_CHECK, filterReason = null; if (isFiltered) tag = TAG_STYLES.IS_FILTERED; else if (isFavorite) tag = TAG_STYLES.IS_FAVORITE; else if (isHasDown) tag = TAG_STYLES.IS_HAS_DOWN; else if (isHasWatch) tag = TAG_STYLES.IS_HAS_WATCH; else if (isFilterKeyword) { tag = TAG_STYLES.IS_KEYWORD_FILTER; filterReason = foundKeyword || "未知"; } else if (isFilterActorMale) { tag = TAG_STYLES.IS_ACTOR_FILTER; filterReason = actorCarNumToNameMap.get(carNum2) || ""; } else if (isFilterActorFemale) { tag = TAG_STYLES.IS_ACTRESS_FILTER; filterReason = actressCarNumToNameMap.get(carNum2) || ""; } filterReason || (filterReason = tag.reasonType); tag.isCounted && this[tag.countKey]++; this.currentPageTotalCount++; $box2.find(".status-tag").remove(); const tagPositionCss = "rightTop" === settings.tagPosition ? "right: 0; top:5px;" : "left: 0; top:5px;"; if (tag.text) { const tagHtml = isJavDb$1 ? ` ${tag.text} ` : ` ${tag.text} `; isJavDb$1 && $box2.find(".tags").append(tagHtml); if (isJavBus$1) { const $itemTag = $box2.find(".item-tag"); $itemTag.length ? $itemTag.append(tagHtml) : $box2.find(".photo-info > span > div").append(tagHtml); } } await this.translate($box2); }))); const t3 = utils.time("处理页面耗时"), t4 = utils.time("累计耗费时间"); $("#waitDownBtn span").text(`打开已收藏 (${carNumSets.favorite.size})`); clog.log(`
    ${t1} ${t2}
    ${t3} ${t4}
    项目 数量 项目 数量
    屏蔽单番号 ${this.currentPageFilterCount} 收藏 ${this.currentPageFavoriteCount}
    屏蔽演员 ${this.currentPageActorFilterCount} 已下载 ${this.currentPageHasDownCount}
    屏蔽关键词 ${this.currentPageKeywordFilterCount} 已观看 ${this.currentPageHasWatchCount}
    待鉴定 ${this.currentPageWaitCheckCount}
    总数 ${this.currentPageTotalCount}
    `); } async bindClick() { let selector = this.getSelector(); $(selector.boxSelector).on("click", ".item img", (async (event) => { event.preventDefault(); event.stopPropagation(); if ($(event.target).closest("div.meta-buttons").length) return; const $box2 = $(event.target).closest(".item"), {carNum: carNum2, aHref} = this.getBoxCarInfo($box2); let dialogOpenDetail = await storageManager.getSetting("dialogOpenDetail", YES$1); if (carNum2.includes("FC2-")) { let movieId = this.parseMovieId(aHref); this.getBean("Fc2Plugin").openFc2Dialog(movieId, carNum2, aHref); } else if (dialogOpenDetail === YES$1) { utils.openPage(aHref, carNum2, true, event); this.$currentImage = null; } else window.open(aHref); })); $(selector.boxSelector).on("click", ".item video", (async (event) => { const video = event.currentTarget; video.paused ? video.play().catch(((e) => console.error("播放失败:", e))) : video.pause(); event.preventDefault(); event.stopPropagation(); })); $(selector.boxSelector).on("click", ".item .video-title", (async (event) => { if ($(event.target).closest('[class^="jhs-match-"]').length) return; const $box2 = $(event.currentTarget).closest(".item"), { carNum: carNum2, aHref } = this.getBoxCarInfo($box2); if (carNum2.includes("FC2-")) { event.preventDefault(); let movieId = this.parseMovieId(aHref); this.getBean("Fc2Plugin").openFc2Dialog(movieId, carNum2, aHref); } })); $(selector.boxSelector).on("contextmenu", ".item img, .item video", (async (event) => { event.preventDefault(); const $box2 = $(event.target).closest(".item"), { carNum: carNum2, url, publishTime } = this.getBoxCarInfo($box2); let $actor = isJavDb$1 ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10"), actorName = ""; $actor.length && (actorName = $actor.text().trim().split(",")[0].replace("(無碼)", "")); utils.q(event, `是否屏蔽番号 ${carNum2}?`, (async () => { setTimeout((async () => { actorName || (actorName = await this.parseActressName(url)); await storageManager.saveCar({ carNum: carNum2, url, names: actorName, actionType: Status_FILTER$1, publishTime }); window.refresh(); show.ok("操作成功"); })); })); })); } async parseActressName(url) { let actorName = null; if (await storageManager.getSetting("enableSaveActressCarInfo", NO$1) === YES$1) { clog.debug("鉴定补录演员信息-已启用, 开始解析详情页"); clog.debug("开始解析演员详情页", url); const html2 = await gmHttp.get(url), $dom = utils.htmlTo$dom(html2); isJavDb$1 ? actorName = $dom.find(".female").prev().map(((i, el) => $(el).text())).get().join(" ") : isJavBus$1 && (actorName = $dom.find('span[onmouseover*="star_"] a').map(((i, el) => $(el).text())).get().join(" ")); clog.debug("解析到名称:", actorName); } return actorName; } async bindListPageHotKey() { this.$currentImage = null; $(document).on("mouseenter", this.getSelector().coverImgSelector, ((e) => { this.$currentImage = $(e.currentTarget); })).on("mouseleave", this.getSelector().coverImgSelector, (() => { this.$currentImage = null; })); let settingObj = await storageManager.getSetting(); this.filterHotKey = settingObj.filterHotKey; this.favoriteHotKey = settingObj.favoriteHotKey; this.hasDownHotKey = settingObj.hasDownHotKey; this.hasWatchHotKey = settingObj.hasWatchHotKey; this.enableImageHotKey = settingObj.enableImageHotKey || NO$1; this.clogHotKey = settingObj.clogHotKey; this.foldCategoryHotKey = settingObj.foldCategoryHotKey; this.showFilterItemHotKey = settingObj.showFilterItemHotKey; this.showFilterActorItemHotKey = settingObj.showFilterActorItemHotKey; this.showFilterKeywordItemHotKey = settingObj.showFilterKeywordItemHotKey; this.showFavoriteItemHotKey = settingObj.showFavoriteItemHotKey; this.showHasDownItemHotKey = settingObj.showHasDownItemHotKey; this.showHasWatchItemHotKey = settingObj.showHasWatchItemHotKey; this.showAllItemHotKey = settingObj.showAllItemHotKey; const HOTKEY_REGISTRY = { showFilterItemHotKey: "showFilterItem", showFilterActorItemHotKey: "showFilterActorItem", showFilterKeywordItemHotKey: "showFilterKeywordItem", showFavoriteItemHotKey: "showFavoriteItem", showHasDownItemHotKey: "showHasDownItem", showHasWatchItemHotKey: "showHasWatchItem", showAllItemHotKey: "showAllItem" }, registerToggleHotkey = (hotkeyName, settingKey) => { const hotkeyString = this[hotkeyName]; hotkeyString && HotkeyManager.registerHotkey(hotkeyString, (async (event) => { const newVal = await storageManager.getSetting(settingKey) === YES$1 ? NO$1 : YES$1; await storageManager.saveSettingItem(settingKey, newVal); window.refresh(); })); }; for (const hotkeyName in HOTKEY_REGISTRY) if (HOTKEY_REGISTRY.hasOwnProperty(hotkeyName)) { registerToggleHotkey(hotkeyName, HOTKEY_REGISTRY[hotkeyName]); } if (this.enableImageHotKey === NO$1) return; const handleAction = async (boxInfo, status) => { setTimeout((async () => { let actorName = await this.parseActressName(boxInfo.url); await storageManager.saveCar({ carNum: boxInfo.carNum, url: boxInfo.url, names: actorName, actionType: status, publishTime: boxInfo.publishTime }); window.refresh(); show.ok("操作成功"); })); }, handlers = {}; this.filterHotKey && (handlers[this.filterHotKey] = (boxInfo) => { handleAction(boxInfo, Status_FILTER$1); }); this.favoriteHotKey && (handlers[this.favoriteHotKey] = (boxInfo) => { handleAction(boxInfo, Status_FAVORITE$1); }); this.hasDownHotKey && (handlers[this.hasDownHotKey] = (boxInfo) => { handleAction(boxInfo, Status_HAS_DOWN$1); }); this.hasWatchHotKey && (handlers[this.hasWatchHotKey] = (boxInfo) => { handleAction(boxInfo, Status_HAS_WATCH$1); }); this.clogHotKey && HotkeyManager.registerHotkey(this.clogHotKey, ((event) => { clog.toggleExpandCollapsed(); })); this.foldCategoryHotKey && HotkeyManager.registerHotkey(this.foldCategoryHotKey, ((event) => { const $btn = $("#foldCategoryBtn"); $btn.length && $btn[0].click(); })); const registerImageHotkey = (key, handler) => { HotkeyManager.registerHotkey(key, ((event) => { const activeElement = document.activeElement; if (!("INPUT" === activeElement.tagName || "TEXTAREA" === activeElement.tagName || activeElement.isContentEditable) && this.$currentImage) { const $box2 = this.$currentImage.closest(".item"), boxInfo = this.getBoxCarInfo($box2); handler(boxInfo); } })); }; Object.entries(handlers).forEach((([key, handler]) => { registerImageHotkey(key, handler); })); } replaceHdImg(coverImgNodeList) { coverImgNodeList && "string" == typeof coverImgNodeList.jquery && (coverImgNodeList = coverImgNodeList.toArray()); coverImgNodeList || (coverImgNodeList = document.querySelectorAll(this.getSelector().coverImgSelector)); isJavDb$1 && coverImgNodeList.forEach(((img) => { img.src = img.src.replace("thumbs", "covers"); img.title = ""; })); if (isJavBus$1) { const THUMB_PATH_REGEX = /\/(imgs|pics)\/(thumb|thumbs)\//, IMG_EXT_REGEX = /(\.jpg|\.jpeg|\.png)$/i, replaceWithHd = (img) => { if (img.src && THUMB_PATH_REGEX.test(img.src) && "true" !== img.dataset.hdReplaced) { img.src = img.src.replace(THUMB_PATH_REGEX, "/$1/cover/").replace(IMG_EXT_REGEX, "_b$1"); img.dataset.hdReplaced = "true"; img.dataset.title = img.title; img.title = ""; } }, DMM_THUMB_REGEX = /ps(\.jpg|\.jpeg|\.png)$/i, replaceWithHdForDMM = (img) => { if (img.src && DMM_THUMB_REGEX.test(img.src) && "true" !== img.dataset.hdReplaced) { img.src = img.src.replace(DMM_THUMB_REGEX, "pl$1"); img.dataset.hdReplaced = "true"; img.dataset.title = img.title; img.title = ""; } }; coverImgNodeList.forEach(((img) => { replaceWithHd(img); replaceWithHdForDMM(img); })); } storageManager.getSetting("hoverBigImg", NO$1).then(((hoverBigImg) => { hoverBigImg === YES$1 && (window.imageHoverPreviewObj ? window.imageHoverPreviewObj.bindEvents() : window.imageHoverPreviewObj = new ImageHoverPreview({ selector: this.getSelector().coverImgSelector })); })); } async translate($box2) { if (await storageManager.getSetting("translateTitle", YES$1) !== YES$1) return; let content, carNum2, $title = $box2.find(".video-title"); if (isJavDb$1) { content = $title.contents().filter(((i, node) => 3 === node.nodeType && "" !== node.textContent.trim())).text().trim(); carNum2 = $box2.find(".video-title strong").text().trim(); } else { content = $box2.find("img").attr("data-title").trim(); carNum2 = $box2.find("a").attr("href").split("/").filter(Boolean).pop().trim(); } if (this.cache[carNum2]) { let _this = this; $title.attr("data-original-title", content); $title.contents().each((function () { 3 === this.nodeType && "" !== this.textContent.trim() && (this.textContent = " " + _this.cache[carNum2] + " "); })); $title.attr("title", _this.cache[carNum2]); } else translateText(content).then(((result) => { $title.attr("data-original-title", content); if (isJavDb$1) { $title.contents().each((function () { 3 !== this.nodeType || "" === this.textContent.trim() || this.textContent.includes(carNum2) || (this.textContent = " " + result + " "); })); $title.attr("title", result); } else $title.text(result); this.writeQueue = this.writeQueue.then((() => { this.cache[carNum2] = result; localStorage.setItem("jhs_translate", JSON.stringify(this.cache)); })); })).catch(((error) => { console.error("翻译失败:", error); })); } async revertTranslation() { $(this.getSelector().itemSelector).toArray().forEach(((ele) => { let $box2 = $(ele); const originalContent = $box2.find(".video-title").attr("data-original-title") || $box2.find(".box").attr("title") || $box2.find(".video-title").attr("title") || $box2.find("img").attr("data-title"); let carNum2; isJavDb$1 && (carNum2 = $box2.find(".video-title strong").text().trim()); const $title = $box2.find(".video-title"); $title.contents().each((function () { 3 !== this.nodeType || "" === this.textContent.trim() || this.textContent.includes(carNum2) || (this.textContent = " " + originalContent + " "); })); $title.removeAttr("title"); })); } addJumpPageControl() { if ($("#gemini-jump-page-control").length > 0) return; if (0 === $(".pagination-link.is-current").length) return; const currentPageNum = utils.getUrlParam(currentHref$1, "page") || 1, $input = $("", { type: "number", id: "jumpPageInput", placeholder: "页码", min: "1", style: "width: 60px; margin-left: 10px; padding: 10px; border: 1px solid #ccc; font-size: 14px;", value: currentPageNum + 1 }), $button = $("