☰

JHS-enhance

Jav-鉴黄师 增强脚本。列表页:作品状态标签、一键筛选、新作品检测、演员黑名单过滤;详情页:磁力链接高亮、DMM 多画质预览视频、标题翻译、整合字幕搜索(迅雷+SubTitleCat)并支持 115 直传、多源预览图(javfree/projectjav/javstore)含来源切换;数据:115 网盘目录匹配与多目录选择、云盘备份/恢复、跨 Tab 同步;其他:自动翻页、分类折叠、Top250、以图识图、热门榜单、评论查看、相关清单。支持 JavDB / JavBus / JavSee / SeeJav / FC2 / JavTrailers

您需要先安裝使用者腳本管理器擴展,如 Tampermonkey、Greasemonkey 或 Violentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 Tampermonkey或 Violentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 Tampermonkey或 Userscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         JHS-enhance
// @namespace    JHS-enhance
// @version      3.3.17
// @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/[email protected]/dist/jquery.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/layer.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/js/md5.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/src/toastify.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/localforage.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/viewer.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/qrcode.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/system.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/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 = `
<style>
  .top-bar {
    z-index: 12345689 !important;
  }

  ${detailPageCss$1}

  .masonry {
    height: 100% !important;
    width: 100% !important;
    padding: 0 15px !important;
  }
  .masonry {
    display: grid;
    column-gap: 10px;
    row-gap: 10px;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: start;
  }
  .masonry .item {
    top: initial !important;
    left: initial !important;
    float: none !important;
    background-color: #c4b1b1;
    position: relative !important;
  }
  .masonry .item:hover {
    box-shadow: 0 .5em 1em -.125em rgba(10, 10, 10, .1), 0 0 0 1px #485fc7;
  }
  .masonry .movie-box {
    width: 100% !important;
    height: 100% !important;
    margin: 0 !important;
    overflow: inherit !important;
  }
  .masonry .movie-box .photo-frame {
    height: auto !important;
    margin: 0 !important;
    position: relative;
  }
  .masonry .movie-box img {
    max-height: 500px;
    height: 100% !important;
    object-fit: contain;
    object-position: top;
  }
  .masonry .movie-box img:hover {
    transform: scale(1.04);
    transition: transform 0.3s;
  }
  .masonry .photo-info span {
    display: inline-block;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .photo-frame .mheyzo,
  .photo-frame .mcaribbeancom2 {
    margin-left: 0 !important;
  }
  .avatar-box {
    width: 100% !important;
    display: flex !important;
    margin: 0 !important;
  }
  .avatar-box .photo-info {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 30px;
    flex-direction: row;
    background-color: #fff !important;
  }
  footer {
    display: none !important;
  }
  .video-title {
    white-space: normal !important;
    height: 75px;
    display: -webkit-box !important;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
  }
</style>
`;
      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 = `
<style>
  ${detailPageCss}

  .navbar {
    z-index: 12345679 !important;
    padding: 0 0;
  }

  .navbar-link:not(.is-arrowless) {
    padding-right: 33px;
  }

  .sub-header,
  #footer,
  .app-desktop-banner,
  div[data-controller="movie-tab"] .tabs,
  h3.main-title,
  div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(3),
  div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(2),
  div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(1),
  .top-meta,
  .float-buttons {
    display: none !important;
  }

  div.tabs.no-bottom,
  .tabs ul {
    border-bottom: none !important;
  }

  .movie-list .item {
    position: relative !important;
  }

  .video-title {
    white-space: normal !important;
    height: 80px;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
  }

  .main-tabs, .tabs {
    overflow-x: hidden;
    flex-wrap: wrap;
    justify-content: flex-start;
  }

  .main-tabs ul, .tabs ul {
    flex-wrap: wrap;
    flex-grow: 0;
  }

  .toolbar {
    display: flex;
  }
</style>
`;
      const mainCss = `
<style>
  /* 全局通用样式 */
  .fr-btn {
    float: right;
    margin-left: 4px !important;
  }

  .menu-box {
    position: fixed;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    z-index: 1000;
    gap: 6px;
  }

  .menu-btn {
    display: inline-block;
    min-width: 80px;
    padding: 7px 12px;
    border-radius: 4px;
    color: white !important;
    text-decoration: none;
    font-weight: bold;
    font-size: 12px;
    text-align: center;
    cursor: pointer;
    transition: all 0.3s ease;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
    border: none;
    line-height: 1.3;
    margin: 0;
  }

  .menu-btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
    opacity: 0.9;
  }

  .menu-btn:active {
    transform: translateY(0);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  }

  .do-hide {
    display: none !important;
  }

  .main-tab-btn {
    border-bottom: none !important;
    border-radius: 3px !important;
    height: 30px;
    margin-left: 5px !important;
  }

  .jhs-icon {
    width: 16px;
    height: 16px;
    vertical-align: middle;
    margin-right: 5px;
  }

  .tool-box .jhs-icon {
    width: 1.5rem;
    height: 1.5rem;
  }

  .tabulator {
    margin: 0 !important;
  }

  .tabulator .tabulator-row .action-cell-dropdown {
    overflow: visible !important;
  }

  .tabulator .tabulator-row.tabulator-selectable:hover {
    cursor: default !important;
  }

  .tabulator .tabulator-col.tabulator-sortable[aria-sort="ascending"] .tabulator-arrow {
    border-bottom-color: #337ab7 !important;
  }

  .tabulator .tabulator-col.tabulator-sortable[aria-sort="descending"] .tabulator-arrow {
    border-top-color: #337ab7 !important;
  }

  .tabulator-responsive-collapse {
    border-top: none !important;
  }

  .tabulator-responsive-collapse table {
    margin-left: 50px !important;
  }

  .tabulator-cell {
    height: auto !important;
  }

  .tabulator .tabulator-cell {
    white-space: normal !important;
    text-overflow: clip !important;
  }

  .tabulator-tableholder {
    overflow-x: hidden !important;
  }

  .jhs-scrollbar::-webkit-scrollbar,
  .content-panel::-webkit-scrollbar,
  .tabulator-tableholder::-webkit-scrollbar,
  .has-navbar-fixed-top::-webkit-scrollbar,
  .layui-layer-content::-webkit-scrollbar {
    width: 6px;
    height: 6px;
  }

  .jhs-scrollbar::-webkit-scrollbar-track,
  .content-panel::-webkit-scrollbar-track,
  .tabulator-tableholder::-webkit-scrollbar-track,
  .has-navbar-fixed-top::-webkit-scrollbar-track,
  .layui-layer-content::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 10px;
  }

  .jhs-scrollbar::-webkit-scrollbar-thumb,
  .content-panel::-webkit-scrollbar-thumb,
  .tabulator-tableholder::-webkit-scrollbar-thumb,
  .has-navbar-fixed-top::-webkit-scrollbar-thumb,
  .layui-layer-content::-webkit-scrollbar-thumb {
    background: #888;
    border-radius: 10px;
  }

  .jhs-scrollbar::-webkit-scrollbar-thumb:hover,
  .content-panel::-webkit-scrollbar-thumb:hover,
  .tabulator-tableholder::-webkit-scrollbar-thumb:hover,
  .has-navbar-fixed-top::-webkit-scrollbar-thumb:hover,
  .layui-layer-content::-webkit-scrollbar-thumb:hover {
    background: #555;
  }

  .jhs-setting-panel {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
    z-index: 999999;
    width: 600px;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
  }

  .jhs-setting-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid #e2e8f0;
  }

  .jhs-setting-header h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: #1e293b;
  }

  .jhs-setting-body {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
  }

  .jhs-setting-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 16px 20px;
    border-top: 1px solid #e2e8f0;
  }

  .jhs-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.3);
    z-index: 999998;
  }

  .jhs-preview-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    z-index: 2147483647;
    display: flex;
    overflow: auto;
    cursor: zoom-out;
    backdrop-filter: blur(5px);
  }
  .jhs-preview-img {
    border-radius: 4px;
    margin: auto;
    cursor: zoom-in;
    max-width: 95vw;
    max-height: 95vh;
    object-fit: contain;
    display: block;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
  }
  .jhs-preview-img.zoomed {
    max-width: none;
    max-height: none;
    cursor: zoom-out;
  }
  .jhs-preview-toolbar {
    position: fixed;
    top: 20px;
    right: 20px;
    display: flex;
    gap: 8px;
    z-index: 2147483648;
    background: #fff;
    padding: 8px 12px;
    border-radius: 8px;
    border: 1px solid #e5e7eb;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  }
  .jhs-preview-btn {
    padding: 7px 16px;
    background: #f3f4f6;
    color: #374151;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s ease;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    white-space: nowrap;
  }
  .jhs-preview-btn:hover {
    background: #e5e7eb;
    border-color: #9ca3af;
  }
  .jhs-preview-btn.active {
    color: #fff;
    background: #2ecc71;
    border-color: #2ecc71;
  }
  .jhs-loading-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.7);
    z-index: 2147483649;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 18px;
    font-family: -apple-system, BlinkMacSystemFont, sans-serif;
  }
  .jhs-loading-spinner {
    text-align: center;
  }
  .jhs-loading-spinner .spinner {
    width: 40px;
    height: 40px;
    margin: 0 auto 12px;
    border: 3px solid rgba(255, 255, 255, 0.3);
    border-top-color: #fff;
    border-radius: 50%;
    animation: jhs-spin 0.6s linear infinite;
  }
  @keyframes jhs-spin {
    to { transform: rotate(360deg); }
  }
  .jhs-preview-toast {
    position: fixed;
    top: 80px;
    left: 50%;
    transform: translate(-50%, -20px);
    z-index: 2147483650;
    padding: 10px 24px;
    border-radius: 8px;
    font-size: 14px;
    font-weight: 600;
    font-family: -apple-system, BlinkMacSystemFont, sans-serif;
    color: #fff;
    opacity: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
    pointer-events: none;
  }
  .jhs-preview-toast.show {
    opacity: 1;
    transform: translate(-50%, 0);
  }
  .jhs-preview-toast-error {
    background: rgba(220, 38, 38, 0.9);
    box-shadow: 0 4px 16px rgba(220, 38, 38, 0.4);
  }
  .jhs-preview-toast-info {
    background: rgba(59, 130, 246, 0.9);
    box-shadow: 0 4px 16px rgba(59, 130, 246, 0.4);
  }

  .magnet-4k-highlight {
    background-color: #fff5f5 !important;
  }
  .magnet-4k-highlight .name {
    color: #f40 !important;
  }

  #magnets-content .item[data-rank] {
    position: relative !important;
  }
  #magnets-content .item[data-rank] .buttons {
    position: absolute !important;
    bottom: 0;
    right: 0;
    margin: 0 !important;
    display: flex;
    gap: 4px;
    align-items: center;
  }
  #magnets-content .item[data-rank] .name {
    display: block;
    padding-right: 260px;
  }

  .msf-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.3);
    z-index: 999997;
  }
  .msf-panel {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
    z-index: 999998;
    width: 380px;
    display: flex;
    flex-direction: column;
  }
  .msf-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 18px;
    border-bottom: 1px solid #e5e7eb;
    background: #f9fafb;
    border-radius: 10px 10px 0 0;
  }
  .msf-title {
    font-size: 15px;
    font-weight: 600;
    color: #1e293b;
  }
  .msf-close {
    font-size: 22px;
    color: #9ca3af;
    cursor: pointer;
    padding: 0 6px;
    line-height: 1;
    transition: color 0.2s;
  }
  .msf-close:hover {
    color: #ef4444;
  }
  .msf-body {
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 16px;
  }
  .msf-section {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .msf-section-title {
    font-size: 13px;
    font-weight: 600;
    color: #6b7280;
  }
  .msf-sort-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .msf-sort-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 6px;
    cursor: default;
    transition: all 0.2s;
    user-select: none;
  }
  .msf-sort-item:hover {
    border-color: #cbd5e1;
    background: #f1f5f9;
  }
  .msf-sort-item.msf-dragging {
    opacity: 0.5;
    border-color: #3b82f6;
    background: #eff6ff;
  }
  .msf-sort-item.msf-drag-over {
    border-color: #3b82f6;
    background: #eff6ff;
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
  }
  .msf-drag-handle {
    color: #cbd5e1;
    font-size: 16px;
    cursor: grab;
    line-height: 1;
    letter-spacing: -2px;
    transition: color 0.2s;
  }
  .msf-drag-handle:active {
    cursor: grabbing;
  }
  .msf-sort-item:hover .msf-drag-handle {
    color: #94a3b8;
  }
  .msf-sort-label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: #374151;
    cursor: pointer;
    flex: 1;
  }
  .msf-sort-checkbox {
    margin: 0;
    cursor: pointer;
    width: 16px;
    height: 16px;
  }
  .msf-sort-hint {
    font-size: 12px;
    color: #9ca3af;
    margin-left: 4px;
  }
  .msf-filter-list {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
  }
  .msf-filter-label {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 6px;
    font-size: 14px;
    color: #374151;
    cursor: pointer;
    transition: all 0.2s;
    user-select: none;
  }
  .msf-filter-label:hover {
    border-color: #cbd5e1;
    background: #f1f5f9;
  }
  .msf-filter-checkbox {
    margin: 0;
    cursor: pointer;
    width: 16px;
    height: 16px;
  }
  .msf-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 14px 18px;
    border-top: 1px solid #e5e7eb;
  }
  .msf-btn {
    padding: 8px 22px;
    border: none;
    border-radius: 6px;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s;
  }
  .msf-btn-cancel {
    background: #f3f4f6;
    color: #374151;
  }
  .msf-btn-cancel:hover {
    background: #e5e7eb;
  }
  .msf-btn-confirm {
    background: #3b82f6;
    color: #fff;
  }
  .msf-btn-confirm:hover {
    background: #2563eb;
  }
</style>
`;
      const subtitleModalCss = `
<style>
  .pdb-sub-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 999999;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .pdb-sub-modal {
    background: #fff;
    border-radius: 10px;
    width: 85vw;
    max-width: 1100px;
    height: 85vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    box-shadow: 0 8px 40px rgba(0, 0, 0, 0.2);
  }
  .pdb-sub-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 20px;
    border-bottom: 1px solid #e5e7eb;
    background: #f9fafb;
    flex-shrink: 0;
  }
  .pdb-sub-search-wrap {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
  }
  .pdb-sub-title {
    font-weight: 600;
    font-size: 14px;
    color: #374151;
    white-space: nowrap;
  }
  .pdb-sub-input {
    flex: 1;
    max-width: 400px;
    padding: 8px 14px;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    font-size: 14px;
    outline: none;
    transition: border-color 0.2s;
  }
  .pdb-sub-input:focus {
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
  }
  .pdb-sub-btn {
    padding: 8px 18px;
    background: #3b82f6;
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 14px;
    cursor: pointer;
    transition: background 0.2s;
    white-space: nowrap;
  }
  .pdb-sub-btn:hover {
    background: #2563eb;
  }
  .pdb-sub-115-wrap {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: 16px;
    flex-shrink: 1;
    min-width: 0;
    max-width: 360px;
  }
  .pdb-sub-checkbox-group {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    min-width: 0;
  }
  .pdb-sub-checkbox-label {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 13px;
    color: #374151;
    cursor: pointer;
    white-space: nowrap;
  }
  .pdb-sub-checkbox {
    margin: 0;
    cursor: pointer;
  }
  .pdb-sub-close {
    font-size: 26px;
    color: #9ca3af;
    cursor: pointer;
    padding: 0 8px;
    line-height: 1;
    transition: color 0.2s;
  }
  .pdb-sub-close:hover {
    color: #ef4444;
  }
  .pdb-sub-body {
    display: flex;
    flex: 1;
    overflow: hidden;
  }
  .pdb-sub-content {
    flex: 1;
    overflow-y: auto;
    padding: 12px;
  }
  .pdb-sub-preview-wrap {
    width: 45%;
    display: flex;
    flex-direction: column;
    border-left: 1px solid #e5e7eb;
    background: #1e1e1e;
  }
  .pdb-sub-preview-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 14px;
    border-bottom: 1px solid #333;
    color: #d1d5db;
    font-size: 13px;
    flex-shrink: 0;
  }
  .pdb-sub-preview-status {
    font-size: 12px;
    color: #6b7280;
  }
  .pdb-sub-textarea {
    flex: 1;
    background: #1e1e1e;
    color: #2ecc71;
    border: none;
    padding: 14px;
    font-family: Consolas, Monaco, monospace;
    font-size: 13px;
    line-height: 1.5;
    resize: none;
    outline: none;
    overflow-y: auto;
  }
  .pdb-sub-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
  }
  .pdb-sub-table thead {
    position: sticky;
    top: 0;
    z-index: 1;
  }
  .pdb-sub-th {
    padding: 10px 12px;
    background: #f3f4f6;
    color: #374151;
    font-weight: 600;
    text-align: left;
    border-bottom: 2px solid #e5e7eb;
    white-space: nowrap;
  }
  .pdb-sub-tr {
    transition: background 0.15s;
  }
  .pdb-sub-tr:hover {
    background: #f9fafb;
  }
  .pdb-sub-tr.is-previewing {
    background: #eff6ff;
    outline: 2px solid #3b82f6;
    outline-offset: -2px;
  }
  .pdb-sub-td,
  .pdb-sub-td-lang,
  .pdb-sub-td-ext,
  .pdb-sub-td-provider {
    padding: 9px 12px;
    border-bottom: 1px solid #f3f4f6;
    vertical-align: middle;
  }
  .pdb-sub-td {
    word-break: break-all;
  }
  .pdb-sub-td-lang,
  .pdb-sub-td-ext {
    text-align: center;
    white-space: nowrap;
  }
  .pdb-sub-td-actions {
    padding: 9px 12px;
    border-bottom: 1px solid #f3f4f6;
    text-align: center;
    white-space: nowrap;
  }
  .pdb-sub-provider {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 600;
  }
  .pdb-sub-provider--xunlei {
    background: #dbeafe;
    color: #1d4ed8;
  }
  .pdb-sub-provider--subtitlecat {
    background: #fef3c7;
    color: #92400e;
  }
  .pdb-sub-action-btn {
    padding: 4px 12px;
    border: none;
    border-radius: 4px;
    font-size: 12px;
    cursor: pointer;
    margin: 0 2px;
    transition: opacity 0.2s;
  }
  .pdb-sub-btn-preview {
    background: #e0f2fe;
    color: #0369a1;
  }
  .pdb-sub-btn-preview:hover {
    background: #bae6fd;
  }
  .pdb-sub-btn-download {
    background: #dcfce7;
    color: #166534;
  }
  .pdb-sub-btn-download:hover {
    background: #bbf7d0;
  }
  .pdb-sub-btn-upload {
    background: #ede9fe;
    color: #5b21b6;
  }
  .pdb-sub-btn-upload:hover {
    background: #ddd6fe;
  }
  .pdb-sub-msg {
    text-align: center;
    padding: 40px 20px;
    color: #6b7280;
    font-size: 14px;
  }
</style>
`;
      function insertStyle(css) {
        if (css) if (css.includes("<style>")) document.head.insertAdjacentHTML("beforeend", css);
        else {
          const style = document.createElement("style");
          style.textContent = css;
          document.head.appendChild(style);
        }
      }
      isJavBus$1 && insertStyle(javBusStyle);
      isJavDb$1 && insertStyle(javdbStyle);
      insertStyle(
        "\n<style>\n    .a-normal, /* 白色 */\n    .a-primary, /* 浅蓝色 */\n    .a-success, /* 浅绿色 */\n    .a-danger, /* 浅粉色 */\n    .a-warning, /* 浅橙色 */\n    .a-info /* 灰色 */\n    {\n        display: inline-flex;\n        align-items: center;\n        justify-content: center;\n        padding: 6px 14px;\n        margin-right: 10px;\n        border-radius: 6px;\n        text-decoration: none;\n        font-size: 13px;\n        font-weight: 500;\n        transition: all 0.2s ease;\n        cursor: pointer;\n        border: 1px solid rgba(0, 0, 0, 0.08);\n        white-space: nowrap;\n    }\n    \n    .a-primary {\n        background: #e0f2fe;\n        color: #0369a1;\n        border-color: #bae6fd;\n    }\n    \n    .a-primary:hover {\n        background: #bae6fd;\n    }\n    \n    .a-success {\n        background: #dcfce7;\n        color: #166534;\n        border-color: #bbf7d0;\n    }\n    \n    .a-success:hover {\n        background: #bbf7d0;\n    }\n    \n    .a-danger {\n        background: #fee2e2;\n        color: #b91c1c;\n        border-color: #fecaca;\n    }\n    \n    .a-danger:hover {\n        background: #fecaca;\n    }\n    \n    .a-warning {\n        background: #ffedd5;\n        color: #9a3412;\n        border-color: #fed7aa;\n    }\n    \n    .a-warning:hover {\n        background: #fed7aa;\n    }\n    \n    .a-info {\n        background: #e2e8f0;\n        color: #334155;\n        border-color: #cbd5e1;\n    }\n    \n    .a-info:hover {\n        background: #cbd5e1;\n    }\n    \n    .a-normal {\n        background: transparent;\n        color: #64748b;\n        border-color: #cbd5e1;\n    }\n    \n    .a-normal:hover {\n        background: #f8fafc;\n    }\n</style>\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(`屏蔽演员番号: <span style="color: #f40">${carData.names} ${carData.carNum}</span>`);
              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(`<span style="color: #f40">补全女优头像: ${name2}</span>`);
                resultCount++;
              }
              if (!favoriteActresses.actressType && actressType) {
                favoriteActresses.actressType = actressType;
                clog.log(`<span style="color: #f40">补全女优类别: ${name2} ${actressType}</span>`);
                resultCount++;
              }
              if (favoriteActresses.name.includes(uncensoredText)) {
                favoriteActresses.name = name2;
                favoriteActresses.allName = allName;
                clog.log(`<span style="color: #f40">更正女优名字: ${name2} ${allName}</span>`);
                resultCount++;
              }
              continue;
            }
            const nowStr = utils.getNowStr();
            favoriteActressesInfoList.push({
              starId,
              name: name2,
              allName,
              avatar,
              lastCheckTime,
              lastPublishTime,
              createDate: nowStr,
              updateDate: nowStr,
              actressType
            });
            clog.log(`<span style="color: #f40">同步JavDB已收藏的演员: ${name2}</span>`);
            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 += `
            <div class="item" id="${movie.id}">
                <a href="/v/${movie.id}" class="box" title="${movie.origin_title}">
                    <div class="cover ">
                        <img loading="lazy" src="${movie.cover_url.replace("https://tp.spfcas.com/rhe951l4q", "https://c0.jdbstatic.com")}" alt="">
                    </div>
                    <div class="video-title"><strong>${movie.number}</strong> ${movie.origin_title}</div>
                    <div class="score" id="score_${movie.id}">
                    </div>
                    <div class="meta">
                        ${movie.release_date}
                    </div>
                    <div class="tags has-addons">
                       ${movie.has_cnsub ? '<span class="tag is-warning">含中字磁鏈</span>' : movie.magnets_count > 0 ? '<span class="tag is-success">含磁鏈</span>' : '<span class="tag is-info">无磁鏈</span>'}
                       ${movie.new_magnets ? '<span class="tag is-info">今日新種</span>' : ""}
                    </div>
                </a>
            </div>
        `;
          }));
          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("<style>") && (css = "<style>" + css + "</style>");
              $("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", '<svg t="1760926954860" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4947" width="200" height="200"><path d="M511.099222 365.825763c-80.7786 0-146.26579 65.482515-146.26579 146.259556 0 80.7786 65.48719 146.259556 146.26579 146.259556 80.777041 0 146.259556-65.480957 146.259556-146.259556C657.358779 431.308278 591.876263 365.825763 511.099222 365.825763L511.099222 365.825763zM511.099222 585.215097c-40.391637 0-73.136012-32.742816-73.136012-73.129778 0-40.391637 32.742816-73.129778 73.136012-73.129778 40.386962 0 73.129778 32.738141 73.129778 73.129778C584.229 552.472281 551.486184 585.215097 511.099222 585.215097L511.099222 585.215097zM511.099222 585.215097M900.893017 568.24369l-26.451395-15.268032c3.065451-27.021784 3.138697-54.472139 0.077922-81.822754l26.373473-15.225955c69.953678-40.391637 93.920921-129.844512 53.533959-199.799749-40.390079-69.95212-129.839837-93.925596-199.799749-53.533959l-26.373473 15.225955c-22.153219-16.330888-45.963059-29.99217-70.896534-40.843585l0-30.545416c0-80.777041-65.48719-146.259556-146.26579-146.259556-80.7786 0-146.259556 65.482515-146.259556 146.259556l0 30.515806c-12.377127 5.421811-24.587501 11.55583-36.562551 18.473743-11.97505 6.917913-23.396854 14.420242-34.277879 22.432179l-26.431136-15.258682c-69.958353-40.391637-159.406553-16.424395-199.79819 53.533959C27.378272 326.082437 51.343956 415.535311 121.299193 455.922273l26.449837 15.275825c-3.063892 27.020226-3.137139 54.465905-0.077922 81.822754l-26.373473 15.224397c-69.953678 40.391637-93.920921 129.841395-53.533959 199.799749 40.391637 69.95212 129.839837 93.920921 199.79819 53.533959l26.375032-15.224397c22.153219 16.32933 45.963059 29.984378 70.896534 40.843585l0 30.537624c0 80.7786 65.48719 146.26579 146.26579 146.26579 80.777041 0 146.259556-65.48719 146.259556-146.26579l0-30.515806c12.377127-5.415577 24.587501-11.55583 36.567226-18.467509 11.97505-6.917913 23.398412-14.420242 34.277879-22.432179l26.423343 15.258682c69.959912 40.391637 159.408111 16.418162 199.799749-53.533959C994.813938 698.085085 970.848254 608.635327 900.893017 568.24369L900.893017 568.24369zM891.096666 731.474653c-20.198936 34.982294-64.923035 46.962019-99.900654 26.770875l-63.331869-36.567226 0 0 0 0-7.988562-4.611422c-18.134004 18.450366-39.024886 34.787489-62.516805 48.353705-23.49971 13.559983-48.091888 23.482568-73.129778 29.964118l0 9.222846 0 0 0 65.828489 0 7.301289c0 40.391637-32.742816 73.136012-73.136012 73.136012-40.386962 0-73.129778-32.742816-73.129778-73.136012l0-7.402588 0-65.72719 0 0 0-9.300768c-50.682014-13.090892-97.855981-39.682547-135.652816-78.232109l-7.983886 4.606747 0 0-63.331869 36.567226c-34.977618 20.191144-79.706394 8.206743-99.900654-26.770875-20.192702-34.977618-8.206743-79.701718 26.770875-99.899095l6.341291-3.657657 0 0 64.972905-37.516316c-14.487254-52.005129-13.929333-106.151555 0.073247-156.593569l-8.057133-4.650384 0 0-63.331869-36.567226c-34.982294-20.192702-46.963578-64.923035-26.770875-99.900654 20.192702-34.97606 64.923035-46.962019 99.900654-26.763083l6.324148 3.649866 0 0 64.996282 37.528784c18.132445-18.450366 39.024886-34.790606 62.516805-48.353705 23.493477-13.559983 48.085654-23.485685 73.129778-29.964118l0-9.229079L437.960093 153.739276l0-7.309082c0-40.385404 32.742816-73.129778 73.129778-73.129778 40.391637 0 73.129778 32.744375 73.129778 73.129778l0 7.404147 0 65.72719 0 9.307001c50.686689 13.086217 97.862215 39.684106 135.657491 78.232109l48.487732-27.997368 22.828023-13.176607c34.977618-20.192702 79.701718-8.212977 99.89442 26.763083 20.198936 34.982294 8.212977 79.706394-26.764641 99.900654l-30.822819 17.79738-32.50905 18.769847 0 0 0 0-7.983886 4.605189c14.488813 52.009805 13.929333 106.159347-0.077922 156.599803l64.979139 37.511641 0 0 6.414537 3.701294C899.303409 651.772936 911.289368 696.498594 891.096666 731.474653L891.096666 731.474653zM891.096666 731.474653M197.330785 324.240361c-1.932465 3.232203-3.824411 6.497135-5.649343 9.785442L197.330785 324.240361 197.330785 324.240361zM197.330785 324.240361M830.515443 690.133926l-5.655577 9.804144C826.793889 696.699632 828.685835 693.433143 830.515443 690.133926L830.515443 690.133926zM830.515443 690.133926M505.297151 146.430195l11.304921 0C512.835324 146.369416 509.067017 146.374091 505.297151 146.430195L505.297151 146.430195zM505.297151 146.430195M516.898176 877.740444l-11.31583 0C509.350653 877.796547 513.125193 877.796547 516.898176 877.740444L516.898176 877.740444zM516.898176 877.740444" fill="#272636" p-id="4948"></path></svg>');
          __publicField2(this, "editSvg", '<svg t="1760920692801" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3545" width="200" height="200"><path d="M1013.929675 128.26571a143.759824 143.759824 0 0 1 10.44409 53.858738 84.576649 84.576649 0 0 1-5.836403 30.308339 92.870485 92.870485 0 0 1-18.635533 29.284408 1314.726599 1314.726599 0 0 1-24.983901 24.574329c-7.372299 7.06512-13.82306 13.311095-19.249891 18.737926-6.143582 6.143582-12.082378 11.672806-17.406817 16.382886L720.266444 82.598415c9.317766-8.601015 20.478607-18.942712 33.277737-31.02509s23.448006-21.604931 31.946628-28.67005a102.085858 102.085858 0 0 1 68.193763-22.731255c11.263234 0.307179 22.116896 2.047861 32.560985 5.222045 10.546483 3.071791 19.659463 6.655547 27.441334 10.546483 16.280493 8.601015 34.301667 23.550399 54.063524 45.052936 19.864249 21.502538 35.120812 43.82422 46.076867 67.272226z m-907.20231 570.943576l32.560986-33.38013c17.099637-17.509209 38.397389-39.216533 64.098041-64.917186l84.986221-85.395793 94.303987-94.815953 250.350976-251.477299L850.817567 389.163169 600.46659 640.640468l-93.177663 94.815953c-31.02509 30.410732-58.978389 58.364031-83.859898 83.655111-24.779115 25.29108-45.360116 46.17926-61.743001 62.562146a504.797674 504.797674 0 0 1-55.804206 50.274981c-10.239304 7.884264-20.581 14.130239-31.537055 18.737926a507.152714 507.152714 0 0 1-47.715156 19.86425 1609.311367 1609.311367 0 0 1-131.063087 42.185931c-20.478607 5.426831-35.837563 8.908194-45.974474 10.546483-20.88818 2.35504-34.813633-0.819144-41.981145-9.42016-6.860333-8.601015-8.805801-22.93604-5.73401-43.312254a396.261054 396.261054 0 0 1 11.058448-47.305584c5.836403-20.683394 12.082378-42.185931 18.635532-64.40522 6.553154-22.219289 13.003916-42.697897 19.249891-61.435822 6.143582-18.635533 11.263234-31.537055 15.15417-38.602176 4.607687-10.853662 9.829732-20.785787 15.666135-29.796373a192.49891 192.49891 0 0 1 25.086294-29.796374z" fill="#FF9500" p-id="3546"></path></svg>');
          __publicField2(this, "deleteSvg", '<svg t="1760921450746" class="jhs-icon icon" viewBox="0 0 1194 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4530" width="200" height="200"><path d="M761.086847 36.028779s309.754321-147.538628 424.952209 231.50509c2.047962 6.570546 71.337359 253.862013-220.838618 415.139055-12.970429 7.167869-267.515096 145.746661-370.339877 341.327076 0 0-90.963666-205.649563-393.379455-351.566888-6.399883-3.071944-304.549083-156.583796-163.751664-487.2444 3.669266-8.533177 163.666333-336.20717 466.423449-99.411511l24.575549 27.391498L387.931021 324.279495l237.648977 159.570408-109.139333 145.746661L625.579998 849.069874l-30.719437-205.820227 166.226286-169.81022-216.486698-168.103585L761.086847 36.028779z" fill="#F4382E" p-id="4531"></path></svg>');
          __publicField2(this, "checkSvg", '<svg t="1760921633527" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5603" width="200" height="200"><path d="M924.928 544A413.76 413.76 0 0 1 544 924.736v3.264h-64v-3.2A413.696 413.696 0 0 1 99.072 544H96v-64h3.072A413.696 413.696 0 0 1 480 99.2V96h64v3.2a413.76 413.76 0 0 1 380.928 380.8h3.072v64h-3.072z m-64-64A350.016 350.016 0 0 0 544 163.2V288h-64V163.2A350.016 350.016 0 0 0 163.072 480H288v64H163.072A350.016 350.016 0 0 0 480 860.8V736h64v124.8a350.016 350.016 0 0 0 316.928-316.8H736v-64h124.928zM512 544a32 32 0 1 1 32-32 32 32 0 0 1-32 32z" fill="#333333" p-id="5604"></path></svg>');
          __publicField2(this, "actressSvg", '<svg t="1760926744637" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1948" width="200" height="200"><path d="M265.950168 668.467036V209.809493A209.809493 209.809493 0 0 1 475.759661 0h40.949536A209.809493 209.809493 0 0 1 726.564189 209.809493v440.435" p-id="1949"></path><path d="M916.558657 825.861124a193.463804 193.463804 0 0 0-137.442564-155.83573l-186.001889-45.795231-10.487631-124.293214H424.106373L412.231008 624.025416l-170.623063 44.44162a193.452429 193.452429 0 0 0-133.666108 154.698244L76.410695 1023.192384h871.189985z" fill="#FFE7D9" p-id="1950"></path><path d="M668.472724 265.682859c68.431223-29.187919 96.140409 100.349111 5.20969 151.774902z" fill="#FFCFB5" p-id="1951"></path><path d="M676.378259 334.421203c1.137487-99.814492-38.674561-172.158671-38.674561-172.15867l-59.740822 11.920865a493.805894 493.805894 0 0 1-80.761583 9.099896 493.669396 493.669396 0 0 1-80.761583-9.099896l-59.683948-11.88674s-39.812048 72.344179-38.776934 172.15867l-1.080613 92.05683c5.209691 56.271486 92.4777 121.381247 195.022161 119.163147 61.196805 0.034125 165.59537-51.573665 165.59537-119.197272z" fill="#FFE7D9" p-id="1952"></path><path d="M322.198905 274.703131c-68.419848-29.187919-96.140409 100.349111-5.209691 151.774902z" fill="#FFCFB5" p-id="1953"></path><path d="M297.390311 812.461526H742.034014a38.458438 38.458438 0 0 1 38.458438 38.458439V1020.325917H258.931873V850.90859a38.458438 38.458438 0 0 1 38.458438-38.447064z" fill="#FFD527" p-id="1954"></path><path d="M690.539973 92.284327c-20.645391 84.287793-275.613121 235.323328-424.589805 117.525166l104.955934-95.548915 139.399042-64.529643z" p-id="1955"></path><path d="M285.321573 383.708519h33.624119v177.118114h-33.624119zM675.855015 383.708519h33.624118v177.118114h-33.624118z" fill="#FFD527" p-id="1956"></path></svg>');
          __publicField2(this, "newSvg", '<svg t="1760926857487" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3954" width="200" height="200"><path d="M508.330667 733.994667c-11.008-7.338667-13.44-17.109333-7.338667-29.333334 28.117333-37.888 41.557333-98.986667 40.341333-183.317333v-165.013333c0-14.656 7.338667-23.210667 21.994667-25.664 37.888-1.216 82.496-5.504 133.845333-12.842667 13.44-2.432 21.376 3.072 23.829334 16.512 1.216 12.224-4.266667 19.562667-16.512 21.994667a1787.093333 1787.093333 0 0 1-113.664 11.008c-6.101333 0-9.173333 3.669333-9.173334 10.986666v84.330667h135.68c12.224 1.237333 18.944 7.957333 20.16 20.181333-1.216 10.986667-7.936 17.109333-20.16 18.346667h-36.672v223.658667c-1.216 12.202667-7.936 18.944-20.16 20.16-11.008-1.216-17.109333-7.957333-18.346666-20.16V501.162667h-60.48v18.346666c1.216 92.885333-13.44 161.92-44.010667 207.146667-6.101333 12.224-15.893333 14.677333-29.333333 7.338667z m-131.989334-282.325334c-1.237333 0-2.453333 0.618667-3.669333 1.834667h45.824a522.666667 522.666667 0 0 0 16.512-31.168c7.317333-12.224 12.224-20.778667 14.656-25.664 6.122667-11.008 15.274667-14.677333 27.52-11.008 9.770667 6.122667 12.202667 14.058667 7.317333 23.829333-4.906667 9.792-13.44 24.448-25.664 44.010667h49.493334c9.770667 1.216 15.274667 6.72 16.512 16.490667-1.237333 11.008-6.741333 17.109333-16.512 18.346666h-82.496a12.437333 12.437333 0 0 1 3.669333 9.173334v38.485333h69.653333c9.792 1.216 15.296 6.72 16.512 16.490667-1.216 11.008-6.72 17.130667-16.512 18.346666h-69.653333v108.16c0 34.218667-15.274667 51.946667-45.845333 53.162667h-16.490667a195.157333 195.157333 0 0 1-20.16 1.834667c-12.224 0-19.562667-6.72-22.016-20.16 1.237333-12.224 7.338667-18.944 18.346667-20.16 2.432 0 6.101333 0.597333 10.986666 1.834666h11.008c15.893333 0 23.829333-8.554667 23.829334-25.685333v-98.986667H314.026667c-11.008-1.216-17.109333-7.338667-18.346667-18.346666 1.237333-9.770667 7.338667-15.274667 18.346667-16.490667h75.157333V497.493333c0-3.669333 1.216-6.72 3.669333-9.173333h-89.813333c-11.029333-1.216-17.130667-7.317333-18.346667-18.325333 1.216-9.770667 7.317333-15.274667 18.346667-16.490667h56.810667c-3.669333-1.216-6.72-4.266667-9.173334-9.173333-1.216-1.216-3.050667-4.266667-5.482666-9.173334a758.336 758.336 0 0 0-14.677334-23.829333c-4.885333-9.770667-3.050667-17.706667 5.504-23.829333 11.008-3.669333 19.562667-1.216 25.664 7.338666 2.453333 2.432 6.122667 7.338667 11.008 14.656 6.101333 8.554667 9.770667 14.08 10.986667 16.512 4.906667 9.770667 2.453333 18.346667-7.317333 25.664z m-60.501333-71.509333c-9.792-1.216-15.274667-7.317333-16.512-18.346667 1.237333-9.749333 6.72-15.253333 16.512-16.490666h75.157333c-3.669333-12.202667-7.338667-21.973333-10.986666-29.333334-1.237333-12.202667 3.648-19.541333 14.656-21.973333 12.224-2.453333 21.397333 1.216 27.52 10.986667 0 1.216 0.597333 3.669333 1.813333 7.338666 4.906667 15.872 9.173333 26.88 12.842667 32.981334h60.48c11.008 1.237333 17.130667 6.741333 18.346666 16.512-1.216 11.008-7.338667 17.109333-18.346666 18.346666h-181.482667z m-14.677333 311.68c-8.533333-6.122667-10.986667-14.08-7.338667-23.829333a1659.648 1659.648 0 0 0 33.002667-66.005334c4.906667-9.792 12.224-12.842667 22.016-9.173333 9.770667 4.906667 13.44 12.224 10.986666 21.994667-3.669333 6.122667-9.173333 17.728-16.490666 34.837333-8.554667 15.893333-14.677333 27.52-18.346667 34.837333-4.885333 8.554667-12.821333 11.008-23.829333 7.338667z m201.664-25.664c-9.770667 4.885333-18.346667 2.432-25.664-7.338667a1138.56 1138.56 0 0 1-27.498667-44.010666c-4.885333-8.533333-3.050667-16.490667 5.504-23.829334 9.770667-3.669333 18.346667-1.216 25.664 7.338667l14.677333 21.994667c6.101333 9.770667 10.389333 17.109333 12.821334 21.994666 4.906667 8.554667 3.050667 16.512-5.504 23.850667z" fill="#333333" p-id="3955"></path><path d="M675.328 117.717333A425.429333 425.429333 0 0 0 512 85.333333C276.352 85.333333 85.333333 276.352 85.333333 512s191.018667 426.666667 426.666667 426.666667 426.666667-191.018667 426.666667-426.666667c0-56.746667-11.093333-112-32.384-163.328a21.333333 21.333333 0 0 0-39.402667 16.341333A382.762667 382.762667 0 0 1 896 512c0 212.074667-171.925333 384-384 384S128 724.074667 128 512 299.925333 128 512 128c51.114667 0 100.8 9.984 146.986667 29.12a21.333333 21.333333 0 0 0 16.341333-39.402667z" fill="#333333" p-id="3956"></path></svg>');
          __publicField2(this, "refreshSvg", '<svg t="1760926993643" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5942" width="200" height="200"><path d="M511.966722 0a511.966722 511.966722 0 1 0 179.828311 32.445891l-22.46254 59.964102A447.970882 447.970882 0 1 1 511.966722 63.99584a31.99792 31.99792 0 0 0 0-63.99584z" fill="#333333" p-id="5943"></path><path d="M649.2378 9.151405A30.909991 30.909991 0 0 1 671.316364 0h193.267438a31.99792 31.99792 0 0 1 31.357962 31.99792c0 17.662852-13.759106 31.99792-31.357962 31.99792H703.954243v160.629559a31.99792 31.99792 0 0 1-31.99792 31.357962 31.485953 31.485953 0 0 1-31.99792-31.357962V31.357962c0-8.511447 3.647763-16.318939 9.343392-21.950573z" fill="#333333" p-id="5944"></path></svg>');
          __publicField2(this, "blacklistSvg", '<svg t="1761386375897" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1936" width="200" height="200"><path d="M513.199827 65.667605c-246.537999 0-446.399933 199.861934-446.399933 446.399933 0 246.553349 199.861934 446.399933 446.399933 446.399933 246.553349 0 446.399933-199.846584 446.399933-446.399933C959.599759 265.529539 759.753175 65.667605 513.199827 65.667605zM513.199827 894.697075c-211.320916 0-382.629537-171.322947-382.629537-382.628514 0-94.183056 34.029024-180.417069 90.461291-247.080352l165.389818 165.389818c4.320399 39.651069 26.816762 73.840752 58.981323 94.068446-72.189136 27.369348-123.517151 97.156784-123.517151 178.936345l337.541643 0 100.846826 100.846826C693.608709 860.664981 607.375719 894.697075 513.199827 894.697075zM805.362956 759.14175 697.264982 651.0448c-16.556071-58.332547-60.10082-105.306394-116.275213-126.601396 35.888372-22.570042 59.752896-62.511729 59.752896-108.032482 0-70.436212-57.108672-127.542838-127.542838-127.542838-48.218188 0-90.184999 26.765597-111.865787 66.245773L266.120498 219.900316c66.663282-56.432267 152.897296-90.461291 247.079328-90.461291 211.304544 0 382.628514 171.308621 382.628514 382.629537C895.82834 606.244454 861.796246 692.476421 805.362956 759.14175z" fill="#272636" p-id="1937"></path></svg>');
          __publicField2(this, "removeSvg", '<svg t="1761958343616" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1939" width="200" height="200"><path d="M405.312 736a32 32 0 0 1-32-32V448a32 32 0 0 1 64 0v256a32 32 0 0 1-32 32zM650.688 448a32 32 0 0 0-64 0v256a32 32 0 1 0 64 0V448z" fill="#333333" p-id="1940"></path><path d="M514.816 53.312h-2.752c-21.504 0-39.808 0-55.04 1.408a116.672 116.672 0 0 0-45.568 12.352c-5.76 3.008-11.2 6.528-16.32 10.496a116.608 116.608 0 0 0-30.144 36.352c-7.552 13.248-15.168 29.888-24.128 49.536l-17.856 39.232H128a32 32 0 0 0 0 64h32V832A160 160 0 0 0 320 992h384a160 160 0 0 0 160-160V266.688h32a32 32 0 0 0 0-64h-190.912l-20.992-43.264c-9.152-18.944-16.896-35.008-24.576-47.744a116.608 116.608 0 0 0-30.208-35.072 117.376 117.376 0 0 0-16.064-10.112 116.608 116.608 0 0 0-44.736-11.84c-14.784-1.28-32.64-1.28-53.696-1.28zM800 266.688V832a96 96 0 0 1-96 96H320A96 96 0 0 1 224 832V266.688h576z m-166.016-64h-240.64l5.184-11.456c9.664-21.184 16.064-35.2 22.016-45.568a54.144 54.144 0 0 1 13.568-17.28 53.312 53.312 0 0 1 7.424-4.8 54.144 54.144 0 0 1 21.312-5.12c11.968-1.088 27.328-1.152 50.624-1.152 22.72 0 37.76 0 49.344 1.088 11.072 0.96 16.704 2.752 20.928 4.928 2.56 1.28 4.992 2.88 7.36 4.608 3.776 2.816 7.808 7.168 13.504 16.64 6.016 10.048 12.608 23.488 22.528 43.968l6.848 14.08z" fill="#333333" p-id="1941"></path></svg>');
          __publicField2(this, "copySvg", '<svg t="1749017229420" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9184" width="200" height="200"><path d="M512 74.666667C270.933333 74.666667 74.666667 270.933333 74.666667 512S270.933333 949.333333 512 949.333333 949.333333 753.066667 949.333333 512 753.066667 74.666667 512 74.666667z m0 810.666666c-204.8 0-373.333333-168.533333-373.333333-373.333333S307.2 138.666667 512 138.666667 885.333333 307.2 885.333333 512 716.8 885.333333 512 885.333333z" fill="#666666" p-id="9185"></path><path d="M512 512m-42.666667 0a42.666667 42.666667 0 1 0 85.333334 0 42.666667 42.666667 0 1 0-85.333334 0Z" fill="#666666" p-id="9186"></path><path d="M341.333333 512m-42.666666 0a42.666667 42.666667 0 1 0 85.333333 0 42.666667 42.666667 0 1 0-85.333333 0Z" fill="#666666" p-id="9187"></path><path d="M682.666667 512m-42.666667 0a42.666667 42.666667 0 1 0 85.333333 0 42.666667 42.666667 0 1 0-85.333333 0Z" fill="#666666" p-id="9188"></path></svg>');
          __publicField2(this, "titleSvg", '<svg t="1747553289744" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7507" width="200" height="200"><path d="M959.8 150.8c0-2.3-1.9-4.2-4.2-4.2H253.3c-2.3 0-4.2 1.9-4.2 4.2v115.9c0 2.3 1.9 4.2 4.2 4.2h702.3c2.3 0 4.2-1.9 4.2-4.2V150.8z" fill="" p-id="7508"></path><path d="M126.4 208.8m-62.2 0a62.2 62.2 0 1 0 124.4 0 62.2 62.2 0 1 0-124.4 0Z" fill="" p-id="7509"></path><path d="M851.5 453.7c0-2.1-1.8-3.9-3.9-3.9H252.9c-2.1 0-3.9 1.7-3.9 3.9v116.6c0 2.1 1.7 3.9 3.9 3.9h594.7c2.1 0 3.9-1.7 3.9-3.9V453.7z" fill="" p-id="7510"></path><path d="M126.4 512m-62.2 0a62.2 62.2 0 1 0 124.4 0 62.2 62.2 0 1 0-124.4 0Z" fill="" p-id="7511"></path><path d="M851.5 756.9c0-2.1-1.8-3.9-3.9-3.9H252.9c-2.1 0-3.9 1.8-3.9 3.9v116.6c0 2.1 1.7 3.9 3.9 3.9h594.7c2.1 0 3.9-1.7 3.9-3.9V756.9z" fill="" p-id="7512"></path><path d="M126.4 815.2m-62.2 0a62.2 62.2 0 1 0 124.4 0 62.2 62.2 0 1 0-124.4 0Z" fill="" p-id="7513"></path></svg>');
          __publicField2(this, "carNumSvg", '<svg t="1747552574854" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3539" width="200" height="200"><path d="M920.337035 447.804932c-6.067182-6.067182-10.918677-11.643178-16.985859-17.71036l48.536436-30.334889-42.469254-109.207238-121.340579 12.134365c-6.067182-6.067182-6.067182-12.134365-12.134365-18.201547-12.134365-12.134365-18.201547-24.267706-24.267706-30.334889-24.26873-36.402071-30.334889-42.469254-54.603619-42.469254H339.116511c-18.201547 0-24.267706 6.067182-54.603619 42.469254-6.067182 6.067182-12.134365 18.201547-24.267706 30.334889 0 0-6.067182 6.067182-12.134365 18.201547l-115.27442-12.134365-48.536436 109.207238 51.090608 24.378223c-6.067182 6.067182-30.334889 34.660404-30.334889 34.660405l-15.542998 22.280446-12.282744 17.018605c-6.067182 12.134365-5.064342 10.868535-5.064342 29.070082v224.480635c0 36.402071 18.201547 60.670801 54.603618 60.670801h115.273397c36.402071 0 54.603619-24.267706 54.603619-54.603619v-18.201547h424.693562v18.201547c0 30.334889 18.201547 54.603619 54.603618 54.603619h115.273397c36.402071 0 60.670801-24.267706 60.670801-60.670801V539.300786c0-42.469254 0.685615-46.662763-11.44875-64.863287-4.731768-6.744611-11.94403-16.196891-20.101827-26.632567z m-35.186383-78.381161l-30.334889 18.201547-12.134365-12.134365c-6.067182-8.899694-12.134365-12.134365-12.134365-18.201547l42.469254-6.067183 12.134365 18.201548z m-533.899776-97.072873h339.755054l78.871325 103.140055H272.378527l78.872349-103.140055zM175.305655 357.290429h36.402071c-6.067182 6.067182-6.067182 12.134365-12.134365 18.201547l-18.201547 6.067183-18.201547-12.134365 12.135388-12.134365z m667.375743 394.35765h-54.603619V678.843936H242.043638v72.804143H132.837424V527.167444c0-12.134365-0.041956-20.662599 1.216711-23.556508 1.258667-2.89391 9.955746-16.924461 21.193695-29.173437l35.722596-38.276768h639.576607l21.917172 20.938891c6.067182 6.067182 21.847587 21.366633 25.712615 28.732392 7.621585 9.996678 6.973832 10.999518 13.041014 23.133883v242.682182h-48.536436zM242.043638 533.234627h133.474944v60.670801H242.043638v-60.670801z m412.559197 0h133.474944v60.670801H654.602835v-60.670801z" p-id="3540"></path></svg>');
          __publicField2(this, "downSvg", '<svg t="1747552626242" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4551" width="200" height="200"><path d="M641.6 660l-8.64-64 32-4.32a211.2 211.2 0 0 0-26.72-420.32 215.36 215.36 0 0 0-213.12 192 94.56 94.56 0 0 0 0 11.52v41.28h-64V384v-7.04a153.12 153.12 0 0 1 0-19.52A279.84 279.84 0 0 1 636.16 108H640A275.2 275.2 0 0 1 673.28 656z" fill="#333333" p-id="4552"></path><path d="M490.4 446.24l-7.52-39.84a182.4 182.4 0 0 1 107.52-162.88l29.12-13.28L646.08 288l-29.12 13.28a117.92 117.92 0 0 0-70.08 101.28l6.24 30.4zM392.96 652.32h-78.72A202.24 202.24 0 0 1 256 256l30.72-9.12 18.24 61.28-30.72 9.12a138.24 138.24 0 0 0 39.68 270.72h78.72zM479.2 512h64v320h-64z" fill="#333333" p-id="4553"></path><path d="M510.4 908l-156.32-147.68 43.84-46.4 112.48 106.08 112.8-106.08 43.84 46.56-156.64 147.52z" fill="#333333" p-id="4554"></path></svg>');
          __publicField2(this, "handleSvg", '<svg t="1749106236917" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2628" width="200" height="200"><path d="M838 989.48a32 32 0 0 1-22.5-9.22L519.3 687.6 207.48 980.8a32 32 0 0 1-54-23.32V136.52A98.54 98.54 0 0 1 252 38.1h519.6A98.52 98.52 0 0 1 870 136.52v820.96a32 32 0 0 1-32 32zM252 102.1a34.46 34.46 0 0 0-34.42 34.42v746.96L498 619.84a32 32 0 0 1 44.42 0.56L806 880.88V136.52a34.46 34.46 0 0 0-34.4-34.42z" p-id="2629"></path><path d="M648 604.92a28 28 0 0 1-16.46-5.34l-112.84-82-112.84 82a28 28 0 0 1-43.08-31.32l43.1-132.64-112.84-82a28 28 0 0 1 16.46-50.66h139.48L492 170.34a28 28 0 0 1 53.26 0l43.1 132.64h139.48a28 28 0 0 1 16.46 50.66l-112.84 82 43.1 132.64A28 28 0 0 1 648 604.92z m-129.3-150a27.86 27.86 0 0 1 16.46 5.36l59.58 43.28-22.76-70a28 28 0 0 1 10.02-31.28l59.58-43.3H568a28 28 0 0 1-26.64-19.34l-22.76-70-22.76 70a28 28 0 0 1-26.62 19.34h-73.64l59.58 43.3a28 28 0 0 1 10.16 31.3l-22.76 70 59.58-43.28a28 28 0 0 1 16.46-5.32z" p-id="2630"></path></svg>');
          __publicField2(this, "siteSvg", '<svg t="1749107903569" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12439" width="200" height="200"><path d="M882.758621 133.674884C882.758621 59.84828 822.91034 0 749.083736 0 675.25715 0 615.40887 59.84828 615.40887 133.674884 615.40887 163.358402 625.152318 191.656395 642.813352 214.773283L670.872117 193.336726 648.314739 166.170836 253.911693 493.666092 276.469054 520.831982 302.371681 496.834595C277.256669 469.725608 241.995388 453.990153 204.295574 453.990153 130.46897 453.990153 70.62069 513.838433 70.62069 587.66502 70.62069 661.491624 130.46897 721.339904 204.295574 721.339904 255.555319 721.339904 301.619094 692.208675 324.036714 647.136344L276.646223 663.002394 706.082022 877.440106 721.856794 845.849335 690.37312 829.861888C680.932829 848.452414 675.940882 869.068818 675.940882 890.325116 675.940882 964.15172 735.789162 1024 809.615766 1024 883.442353 1024 943.290633 964.15172 943.290633 890.325116 943.290633 874.050807 940.36533 858.125365 934.723584 843.16446L868.645076 868.0826C871.294817 875.109252 872.669943 882.595452 872.669943 890.325116 872.669943 925.14899 844.439623 953.37931 809.615766 953.37931 774.791892 953.37931 746.561571 925.14899 746.561571 890.325116 746.561571 880.245089 748.902894 870.575616 753.340487 861.836782L769.436089 830.140063 737.631567 814.258564 308.195769 599.820853 276.554929 584.02108 260.805279 615.686903C250.212352 636.984797 228.494795 650.719214 204.295574 650.719214 169.4717 650.719214 141.241379 622.488894 141.241379 587.66502 141.241379 552.841163 169.4717 524.610842 204.295574 524.610842 222.12269 524.610842 238.680594 531.99985 250.566444 544.829369L273.29589 569.363385 299.026432 547.997855 693.429478 220.502616 719.514606 198.84265 698.930882 171.900169C690.596687 160.991373 686.029559 147.727007 686.029559 133.674884 686.029559 98.85101 714.25988 70.62069 749.083736 70.62069 783.90761 70.62069 812.137931 98.85101 812.137931 133.674884 812.137931 148.208022 807.249885 161.899255 798.379608 172.996785L853.543883 217.089695C872.331935 193.584128 882.758621 164.379366 882.758621 133.674884ZM749.083736 196.729062C729.149334 196.729062 710.818745 187.460449 698.930882 171.900169L642.813352 214.773283C667.922573 247.639305 706.904064 267.349751 749.083736 267.349751 790.225902 267.349751 828.357809 248.599782 853.543883 217.089695L798.379608 172.996785C786.455411 187.915034 768.530291 196.729062 749.083736 196.729062ZM337.970441 587.66502C337.970441 553.551854 325.093782 521.360666 302.371681 496.834595L250.566444 544.829369C261.309069 556.424898 267.349751 571.526356 267.349751 587.66502 267.349751 597.565263 265.091478 607.069184 260.805279 615.686903L324.036714 647.136344C333.156105 628.801148 337.970441 608.540036 337.970441 587.66502ZM809.615766 756.650249C758.753986 756.650249 712.986006 785.330865 690.37312 829.861888L753.340487 861.836782C764.027215 840.791658 785.603302 827.270938 809.615766 827.270938 836.08553 827.270938 859.461862 843.730308 868.645076 868.0826L934.723584 843.16446C915.252259 791.529949 865.714547 756.650249 809.615766 756.650249Z" fill="#389BFF" p-id="12440"></path></svg>');
          __publicField2(this, "videoSvg", '<svg t="1749003664455" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1952" width="200" height="200"><path d="M825.6 153.6H198.4C124.5 153.6 64 214.1 64 288v448c0 73.9 60.5 134.4 134.4 134.4h627.2c73.9 0 134.4-60.5 134.4-134.4V288c0-73.9-60.5-134.4-134.4-134.4z m-138.2 44.8l112 112H706l-112-112h93.4z m-156.8 0l112 112H526.7l-112-112h115.9z m-179.2 0l112 112H347.5l-112-112h115.9zM108.8 288c0-41.4 28.4-76.1 66.7-86.3l108.7 108.7H108.8V288z m806.4 448c0 49.4-40.2 89.6-89.6 89.6H198.4c-49.4 0-89.6-40.2-89.6-89.6V355.2h806.4V736z m0-425.6h-52.5l-112-112h74.9c49.4 0 89.6 40.2 89.6 89.6v22.4z" p-id="1953"></path><path d="M454 687.2l149.3-77.6c27.5-13.8 27.5-53 0-66.8L468 472.2c-31.2-15.6-68 7.1-68 42v139.6c0 27.8 29.2 45.8 54 33.4zM444.8 512l134.4 67.2-134.4 67.2V512z" p-id="1954"></path></svg>');
          __publicField2(this, "screenSvg", '<svg t="1750691468062" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2693" width="200" height="200"><path d="M288 160a64 64 0 0 0-64 64v576a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64v-576a64 64 0 0 0-64-64h-448m0-64h448a128 128 0 0 1 128 128v576a128 128 0 0 1-128 128h-448a128 128 0 0 1-128-128v-576a128 128 0 0 1 128-128z" fill="#4078FD" p-id="2694"></path><path d="M416 352m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" fill="#FE9C23" p-id="2695"></path><path d="M352 732.448a32 32 0 0 1-32-32v-160a32 32 0 0 1 44.224-29.568l130.112 53.632 153.952-169.984a32 32 0 0 1 55.712 21.472v284.448a32 32 0 0 1-32 32z m0-32h320z" fill="#4078FD" opacity=".2" p-id="2696"></path><path d="M672 416l-169.088 186.656-150.912-62.208v160h320V416m0-32a32 32 0 0 1 32 32v284.448a32 32 0 0 1-32 32h-320a32 32 0 0 1-32-32v-160a32 32 0 0 1 44.192-29.6l130.112 53.632 153.984-169.984a32 32 0 0 1 23.712-10.496z" fill="#4078FD" p-id="2697"></path></svg>');
          __publicField2(this, "recoveryVideoSvg", '<svg t="1749003779161" class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8204" width="200" height="200"><path d="M938.666667 553.92V768c0 64.8-52.533333 117.333333-117.333334 117.333333H202.666667c-64.8 0-117.333333-52.533333-117.333334-117.333333V256c0-64.8 52.533333-117.333333 117.333334-117.333333h618.666666c64.8 0 117.333333 52.533333 117.333334 117.333333v297.92z m-64-74.624V256a53.333333 53.333333 0 0 0-53.333334-53.333333H202.666667a53.333333 53.333333 0 0 0-53.333334 53.333333v344.48A290.090667 290.090667 0 0 1 192 597.333333a286.88 286.88 0 0 1 183.296 65.845334C427.029333 528.384 556.906667 437.333333 704 437.333333c65.706667 0 126.997333 16.778667 170.666667 41.962667z m0 82.24c-5.333333-8.32-21.130667-21.653333-43.648-32.917333C796.768 511.488 753.045333 501.333333 704 501.333333c-121.770667 0-229.130667 76.266667-270.432 188.693334-2.730667 7.445333-7.402667 20.32-13.994667 38.581333-7.68 21.301333-34.453333 28.106667-51.370666 13.056-16.437333-14.634667-28.554667-25.066667-36.138667-31.146667A222.890667 222.890667 0 0 0 192 661.333333c-14.464 0-28.725333 1.365333-42.666667 4.053334V768a53.333333 53.333333 0 0 0 53.333334 53.333333h618.666666a53.333333 53.333333 0 0 0 53.333334-53.333333V561.525333zM320 480a96 96 0 1 1 0-192 96 96 0 0 1 0 192z m0-64a32 32 0 1 0 0-64 32 32 0 0 0 0 64z" fill="#000000" p-id="8205"></path></svg>');
          __publicField2(this, "markDeleteSvg", '<svg class="jhs-icon icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64z m0 832c-212.1 0-384-171.9-384-384s171.9-384 384-384 384 171.9 384 384-171.9 384-384 384z"></path><path d="M717.3 338.7L685.3 306.7 512 480 338.7 306.7 306.7 338.7 480 512 306.7 685.3 338.7 717.3 512 544 685.3 717.3 717.3 685.3 544 512z"></path></svg>');
        }
        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 || "&nbsp;");
              });
            } else {
              titleElement.value = def.title || "&nbsp;";
            }
          } else {
            if (def.field) {
              this.langBind("columns|" + def.field, (text) => {
                this._formatColumnHeaderTitle(titleHolderElement, text || (def.title || "&nbsp;"));
              });
            } else {
              this._formatColumnHeaderTitle(titleHolderElement, def.title || "&nbsp;");
            }
          }
          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 = "<div class='tabulator-data-tree-control-collapse'></div>";
            }
            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 = "<div class='tabulator-data-tree-control-expand'></div>";
            }
            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 = '<polygon fill="#488CE9" stroke="#014AAE" stroke-width="37.6152" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="259.216,29.942 330.27,173.919 489.16,197.007 374.185,309.08 401.33,467.31 259.216,392.612 117.104,467.31 144.25,309.08 29.274,197.007 188.165,173.919 "/>';
            } else {
              if (self.table.browser == "ie") {
                star3.setAttribute("class", "tabulator-star-inactive");
              } else {
                star3.classList.replace("tabulator-star-active", "tabulator-star-inactive");
              }
              star3.innerHTML = '<polygon fill="#010155" stroke="#686868" stroke-width="37.6152" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="259.216,29.942 330.27,173.919 489.16,197.007 374.185,309.08 401.33,467.31 259.216,392.612 117.104,467.31 144.25,309.08 29.274,197.007 188.165,173.919 "/>';
            }
          });
        }
        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 "&nbsp;";
        }
      }
      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 : '<svg enable-background="new 0 0 24 24" height="14" width="14" viewBox="0 0 24 24" xml:space="preserve" ><path class="tabulator-tick" clip-rule="evenodd" d="M21.652,3.211c-0.293-0.295-0.77-0.295-1.061,0L9.41,14.34  c-0.293,0.297-0.771,0.297-1.062,0L3.449,9.351C3.304,9.203,3.114,9.13,2.923,9.129C2.73,9.128,2.534,9.201,2.387,9.351  l-2.165,1.946C0.078,11.445,0,11.63,0,11.823c0,0.194,0.078,0.397,0.223,0.544l4.94,5.184c0.292,0.296,0.771,0.776,1.062,1.07  l2.124,2.141c0.292,0.293,0.769,0.293,1.062,0l14.366-14.34c0.293-0.294,0.293-0.777,0-1.071L21.652,3.211z" fill-rule="evenodd"/></svg>', cross = typeof formatterParams.crossElement !== "undefined" ? formatterParams.crossElement : '<svg enable-background="new 0 0 24 24" height="14" width="14"  viewBox="0 0 24 24" xml:space="preserve" ><path class="tabulator-cross" d="M22.245,4.015c0.313,0.313,0.313,0.826,0,1.139l-6.276,6.27c-0.313,0.312-0.313,0.826,0,1.14l6.273,6.272  c0.313,0.313,0.313,0.826,0,1.14l-2.285,2.277c-0.314,0.312-0.828,0.312-1.142,0l-6.271-6.271c-0.313-0.313-0.828-0.313-1.141,0  l-6.276,6.267c-0.313,0.313-0.828,0.313-1.141,0l-2.282-2.28c-0.313-0.313-0.313-0.826,0-1.14l6.278-6.269  c0.313-0.312,0.313-0.826,0-1.14L1.709,5.147c-0.314-0.313-0.314-0.827,0-1.14l2.284-2.278C4.308,1.417,4.821,1.417,5.135,1.73  L11.405,8c0.314,0.314,0.828,0.314,1.141,0.001l6.276-6.267c0.312-0.312,0.826-0.312,1.141,0L22.245,4.015z"/></svg>';
        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 = '<polygon fill="#FFEA00" stroke="#C1AB60" stroke-width="37.6152" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="259.216,29.942 330.27,173.919 489.16,197.007 374.185,309.08 401.33,467.31 259.216,392.612 117.104,467.31 144.25,309.08 29.274,197.007 188.165,173.919 "/>', starInactive = '<polygon fill="#D2D2D2" stroke="#686868" stroke-width="37.6152" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="259.216,29.942 330.27,173.919 489.16,197.007 374.185,309.08 401.33,467.31 259.216,392.612 117.104,467.31 144.25,309.08 29.274,197.007 188.165,173.919 "/>';
        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 '<svg enable-background="new 0 0 24 24" height="14" width="14" viewBox="0 0 24 24" xml:space="preserve" ><path fill="#2DC214" clip-rule="evenodd" d="M21.652,3.211c-0.293-0.295-0.77-0.295-1.061,0L9.41,14.34  c-0.293,0.297-0.771,0.297-1.062,0L3.449,9.351C3.304,9.203,3.114,9.13,2.923,9.129C2.73,9.128,2.534,9.201,2.387,9.351  l-2.165,1.946C0.078,11.445,0,11.63,0,11.823c0,0.194,0.078,0.397,0.223,0.544l4.94,5.184c0.292,0.296,0.771,0.776,1.062,1.07  l2.124,2.141c0.292,0.293,0.769,0.293,1.062,0l14.366-14.34c0.293-0.294,0.293-0.777,0-1.071L21.652,3.211z" fill-rule="evenodd"/></svg>';
      }
      function buttonCross(cell, formatterParams, onRendered) {
        return '<svg enable-background="new 0 0 24 24" height="14" width="14" viewBox="0 0 24 24" xml:space="preserve" ><path fill="#CE1515" d="M22.245,4.015c0.313,0.313,0.313,0.826,0,1.139l-6.276,6.27c-0.313,0.312-0.313,0.826,0,1.14l6.273,6.272  c0.313,0.313,0.313,0.826,0,1.14l-2.285,2.277c-0.314,0.312-0.828,0.312-1.142,0l-6.271-6.271c-0.313-0.313-0.828-0.313-1.141,0  l-6.276,6.267c-0.313,0.313-0.828,0.313-1.141,0l-2.282-2.28c-0.313-0.313-0.313-0.826,0-1.14l6.278-6.269  c0.313-0.312,0.313-0.826,0-1.14L1.709,5.147c-0.314-0.313-0.314-0.827,0-1.14l2.284-2.278C4.308,1.417,4.821,1.417,5.135,1.73  L11.405,8c0.314,0.314,0.828,0.314,1.141,0.001l6.276-6.267c0.312-0.312,0.826-0.312,1.141,0L22.245,4.015z"/></svg>';
      }
      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 "<div class='tabulator-row-handle-box'><div class='tabulator-row-handle-bar'></div><div class='tabulator-row-handle-bar'></div><div class='tabulator-row-handle-bar'></div></div>";
      }
      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 = {
              "&": "&amp;",
              "<": "&lt;",
              ">": "&gt;",
              '"': "&quot;",
              "'": "&#39;",
              "/": "&#x2F;",
              "`": "&#x60;",
              "=": "&#x3D;"
            };
            return String(value).replace(/[&<>"'`=/]/g, function(s) {
              return entityMap[s];
            });
          } else {
            return value;
          }
        }
        emptyToSpace(value) {
          return value === null || typeof value === "undefined" || value === "" ? "&nbsp;" : 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) + "<span>(" + count + " " + (count === 1 ? langValue : lang.groups.items) + ")</span>";
              };
            });
            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 = "&vellip;";
          }
          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 = "&vellip;";
          }
          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 = `<svg class='tabulator-responsive-collapse-toggle-open' viewbox="0 0 24 24">
  <line x1="7" y1="12" x2="17" y2="12" fill="none" stroke-width="3" stroke-linecap="round" />
  <line y1="7" x1="12" y2="17" x2="12" fill="none" stroke-width="3" stroke-linecap="round" />
</svg>

<svg class='tabulator-responsive-collapse-toggle-close' viewbox="0 0 24 24">
  <line x1="7" y1="12" x2="17" y2="12"  fill="none" stroke-width="3" stroke-linecap="round" />
</svg>`;
        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", "<div class='tabulator-arrow'></div>");
          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('<span class="site-tag" style="top:-15px">多结果</span>');
                $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            <style>\n                .jhs-scroll {\n                    text-align: center;\n                    padding-top: 20px;\n                    font-size: 14px;\n                }\n                .jhs-scroll.waterfall-loading { color: #000; }\n                .jhs-scroll.waterfall-error { color: #f44336; cursor: pointer; }\n                .jhs-scroll.waterfall-no-more { color: #4CAF50; }\n            </style>\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"
          }, '<?xml version="1.0"?>\n                <d:propfind xmlns:d="DAV:">\n                    <d:prop>\n                        <d:displayname />\n                        <d:getcontentlength />\n                        <d:creationdate />\n                        <d:getlastmodified />\n                        <d:iscollection />\n                    </d:prop>\n                </d:propfind>\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 `
            <style>
                ${containerWidthCss}
                .nav-btn::after {
                    content:none !important;
                }
                
                #cache-data-display pre {
                    font-family: Consolas, Monaco, 'Andale Mono', monospace;
                    white-space: pre-wrap;
                    word-wrap: break-word;
                    line-height: 1.5;
                    color: #333;
                    border: 1px solid #ddd;
                }
                
                .cache-item {
                    transition: all 0.2s ease;
                }
                .cache-item:hover {
                    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
                    transform: translateY(-2px);
                }

                .tooltip-icon {
                    display: inline-block;
                    width: 16px;
                    height: 16px;
                    line-height: 16px;
                    text-align: center;
                    border-radius: 50%;
                    background-color: #ccc;
                    color: white;
                    font-size: 12px;
                    margin-right: 5px;
                    cursor: help;
                }
                .setting-item {
                    display: flex;
                    align-items: baseline;
                    justify-content: space-between;
                    margin-bottom: 3px;
                    padding: 3px;
                    /*border: 1px solid #ddd;
                    border-radius: 5px;*/
                }
                .simple-setting .setting-item{
                    align-items:center;
                }
                .setting-label {
                    font-size: 14px;
                    min-width: 160px;
                    font-weight: bold;
                    margin-right: 10px;
                }
                .form-content{
                    max-width: 160px;
                    min-width: 160px;
                }
                .form-content * {
                    width: 100%;
                    padding: 5px;
                    margin-right: 10px;
                    text-align: center;
                }
                
                .keyword-label {
                    display: inline-flex;
                    align-items: center;
                    padding: 4px 8px;
                    border-radius: 4px;
                    font-size: 14px;
                    position: relative;
                    margin-left: 8px;
                    margin-bottom: 5px;
                }
                .keyword-remove {
                    margin-left: 6px;
                    cursor: pointer;
                    font-size: 12px;
                    line-height: 1;
                }
                .keyword-input {
                    padding: 6px 12px;
                    border: 1px solid #ccc;
                    border-radius: 4px;
                    font-size: 14px;
                    float:right;
                }
                .add-tag-btn {
                    padding: 6px 12px;
                    background-color: #e2e8f0;
                    color: #334155;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 14px;
                    margin-left: 8px;
                    float:right;
                }
                .add-tag-btn:hover {
                    background-color: #cbd5e1;
                }
                .tag-box {
                    margin-top:15px;
                }
                
                
                #saveBtn,#moreBtn,#helpBtn,#clean-all {
                    padding: 8px 20px;
                    background-color: #4CAF50;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 16px;
                    margin-top: 10px;
                }
                #saveBtn:hover {
                    background-color: #45a049;
                }
                #moreBtn {
                    background-color: #5cb85c;
                    color: white;
                }
                #moreBtn:hover {
                    background-color: #4cae4c;
                }
                #helpBtn {
                    background-color: #e67e22;
                    color: white;
                }
                #helpBtn:hover {
                    background-color: #d35400;
                }
                .simple-setting, .mini-simple-setting {
                    display: none;
                    background: rgba(255,255,255,1); 
                    position: absolute;
                    top: ${isJavDb$1 ? "35px" : "25px"};
                    right: ${isJavDb$1 ? "-300%" : "0"};
                    z-index: 1000;
                    border: 1px solid #ddd;
                    border-radius: 4px;
                    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
                    padding: 0;
                    margin-top: 5px; /* 稍微拉开一点距离 */
                    color: #333;
                }
                
                .mini-switch {
                  appearance: none;
                  -webkit-appearance: none;
                  width: 40px;
                  height: 20px;
                  background: #e0e0e0;
                  border-radius: 20px;
                  position: relative;
                  cursor: pointer;
                  outline: none;
                  /*transition: all 0.2s ease;*/
                }
                
                .mini-switch:checked {
                  background: #4CAF50;
                }
                
                .mini-switch::before {
                  content: "";
                  position: absolute;
                  width: 16px;
                  height: 16px;
                  border-radius: 50%;
                  background: white;
                  top: 2px;
                  left: 2px;
                  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
                  /*transition: all 0.2s ease;*/
                }
                
                .mini-switch:checked::before {
                  left: calc(100% - 18px);
                }
                
                .side-menu-item {
                    padding: 12px 12px;
                    cursor: pointer;
                    color: #333;
                    border-left: 3px solid transparent;
                    transition: all 0.2s;
                    display: flex;
                    gap: 5px;
                }
                
                .side-menu-item .icon {
                     height: 24px; 
                     width: 24px;
                }
                
                .side-menu-item:hover {
                    background-color: #e9e9e9;
                }
                
                .side-menu-item.active {
                    background-color: #e0e0e0;
                    border-left: 3px solid #5d87c2;
                    font-weight: bold;
                }
                
                .content-panel {
                    display: none;
                    margin-top:20px;
                    padding: 0 10px 10px 0;
                    height: 100%;
                    overflow-x: hidden;
                    overflow-y: auto;
                }
                
                .content-panel.active {
                    display: block;
                }
                
                input[type="checkbox"]:disabled {
                    opacity: 0.6; 
                    cursor: default !important;
                }
            </style>
        `;
        }
        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('<div class="navbar-item has-dropdown is-hoverable setting-box" style="position:relative;">\n                    <a id="setting-btn" class="navbar-link nav-btn" style="color: #ff8400 !important;padding-right:15px !important;">\n                        设置\n                    </a>\n                    <div class="simple-setting"></div>\n                </div>');
            utils.loopDetector((() => $("#miniHistoryBtn").length > 0), (() => {
              $(".miniHistoryBtnBox").before('\n                    <div class="navbar-item mini-setting-box" style="position:relative;margin-left: auto;">\n                        <a id="mini-setting-btn" class="navbar-link nav-btn" style="color: #ff8400 !important;padding-left:0 !important;padding-right:0 !important;">\n                            设置\n                        </a>\n                        <div class="mini-simple-setting"></div>\n                    </div>\n                ');
              handleResize2();
            }));
            $(window).resize(handleResize2);
          }
          if (isJavBus$1) {
            utils.loopDetector((() => $("#waitCheckBtn").length), (() => {
              $("#waitCheckBtn").parent().append('\n                    <div id="top-right-box" style="position: relative; display: flex; flex-grow: 1;justify-content: flex-end;z-index: 12345679 !important;">\n                        <div class="setting-box">\n                            <a id="setting-btn" class="menu-btn main-tab-btn" style="background-color:#6e685e !important;">\n                                <span>设置</span>\n                            </a>\n                            <div class="simple-setting"></div>\n                        </div>\n                    </div>\n               ');
            }), 1, 1e4, false);
            window.isDetailPage && $("h3").before('\n                    <div class="container-fluid" style="margin-top:20px">\n                        <div id="top-right-box" style="position: relative; display: flex; flex-grow: 1;justify-content: flex-end;z-index: 12345679 !important;">\n                            <div class="setting-box">\n                                <a id="setting-btn" class="menu-btn main-tab-btn" style="background-color:#6e685e !important;">\n                                    <span>设置</span>\n                                </a>\n                                <div class="simple-setting"></div>\n                            </div>\n                        </div>\n                    </div>\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) => `
            <div class="cache-item" style="border: 1px solid #eee; border-radius: 8px; padding: 12px;">
                <div style="font-weight: bold; margin-bottom: 8px;">${item.text}</div>
                <div style="display: flex; gap: 8px;">
                    <a class="menu-btn clean-btn" data-key="${item.key}" style="background-color:#448cc2; flex:1; text-align:center;" title="${item.title}">
                        <span>清理</span>
                    </a>
                    <a class="menu-btn view-btn" data-key="${item.key}" style="background-color:#b2bec0; flex:1; text-align:center;" >
                        <span>查看</span>
                    </a>
                </div>
            </div>
        `)).join("");
          let videoQualityHtml = "";
          qualityOptions.forEach(((option) => {
            option.canSelect && (videoQualityHtml += `<option value="${option.quality}">${option.text}</option>`);
          }));
          const coverButtonPlugin = this.getBean("CoverButtonPlugin");
          let settingHtml = `
            <div style="display: flex; height: 100%;">
                <div style="width: 140px; flex-shrink: 0; padding: 15px 0; background: #f5f5f5; border-right: 1px solid #ddd;">
                    <div class="side-menu-item ${"backup-panel" === defaultActivePanel ? "active" : ""}" data-panel="backup-panel">💾 数据备份</div>
                    <div class="side-menu-item ${"base-panel" === defaultActivePanel ? "active" : ""}" data-panel="base-panel">⚙️ 基础配置</div>
                    <div class="side-menu-item ${"filter-panel" === defaultActivePanel ? "active" : ""}" data-panel="filter-panel">🚫 屏蔽配置</div>
                    <div class="side-menu-item ${"task-panel" === defaultActivePanel ? "active" : ""}" data-panel="task-panel">📋 定时任务</div>
                    <div class="side-menu-item ${"offline-panel" === defaultActivePanel ? "active" : ""}" data-panel="offline-panel">📂 离线配置</div>
                    <div class="side-menu-item ${"domain-panel" === defaultActivePanel ? "active" : ""}" data-panel="domain-panel" title="第三方视频资源域名配置">🌐 外部网站</div>
                    <div class="side-menu-item ${"mark-delete-panel" === defaultActivePanel ? "active" : ""}" data-panel="mark-delete-panel">🗑️ 标记删除</div>
                    <div class="side-menu-item ${"hotkey-panel" === defaultActivePanel ? "active" : ""}" data-panel="hotkey-panel">⌨️ 快捷键配置</div>
                    <div class="side-menu-item ${"cache-panel" === defaultActivePanel ? "active" : ""}" data-panel="cache-panel">🧹 清理缓存</div>
                    <div class="side-menu-item ${"tip-author-panel" === defaultActivePanel ? "active" : ""}" data-panel="tip-author-panel">💵 打赏作者</div>
                </div>
        
                <div style="flex: 1; display: flex; flex-direction: column; height: 100%; ">
                    <div style="flex: 1; margin: 0 10px; padding-bottom: 20px;overflow: hidden">
                    
                        <!-- 阿里云盘面板 -->
                        <div id="backup-panel" class="content-panel" style="display: ${"backup-panel" === defaultActivePanel ? "block" : "none"};">
                            <div style="margin-bottom: 20px">
                                <a id="importBtn" class="menu-btn" style="background-color:#d25a88"><span>导入数据</span></a>
                                <a id="exportBtn" class="menu-btn" style="background-color:#85d0a3"><span>导出数据</span></a>
                                <a id="getRefreshTokenBtn" class="menu-btn fr-btn" style="background-color:#c4a35e"><span>获取refresh_token</span></a>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label">阿里云盘备份</span>
                                <div>
                                    <a id="backupListBtn" class="menu-btn" style="background-color:#5d87c2"><span>查看备份</span></a>
                                    <a id="backupBtn" class="menu-btn" style="background-color:#64bb69"><span>备份数据</span></a>
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">refresh_token:</span>
                                <div class="form-content">
                                    <input id="refresh_token">
                                </div>
                            </div>
                            
                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>
                            
                            <div class="setting-item">
                                <span class="setting-label">WebDav备份</span>
                                <div>
                                    <a id="webdavBackupListBtn" class="menu-btn" style="background-color:#5d87c2"><span>查看备份</span></a>
                                    <a id="webdavBackupBtn" class="menu-btn" style="background-color:#64bb69"><span>备份数据</span></a>
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">服务地址:</span>
                                <div class="form-content">
                                    <input id="webDavUrl">
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">用户名:</span>
                                <div class="form-content">
                                    <input id="webDavUsername">
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">密码:</span>
                                <div class="form-content">
                                    <input id="webDavPassword">
                                </div>
                            </div>
                        </div>
                        
                        
                        <!-- 基础设置面板 -->
                        <div id="base-panel" class="content-panel" style="display: ${"base-panel" === defaultActivePanel ? "block" : "none"};">
                            <div class="setting-item">
                                <span class="setting-label">已鉴定标签展示位置:</span>
                                <div class="form-content">
                                    <select id="tagPosition">
                                        <option value="rightTop">右上</option>
                                        <option value="leftTop">左上</option>
                                    </select>
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label">已鉴定内容处理方式:</span>
                                <div class="form-content">
                                    <select id="movieShowType">
                                        <option value="hide">隐藏</option>
                                        <option value="visibility">透明</option>
                                    </select>
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label" style="display:flex; align-items:center; gap:5px">
                                    鉴定补录演员信息 <span data-tip="在列表页进行鉴定是获取不到演员名称的, 开启后, 额外解析详情页补录演员名称, 因发请求解析费时, 会被以往慢1秒左右">❓</span>
                                </span>
                                <div class="form-content">
                                    <input type="checkbox" id="enableSaveActressCarInfo" class="mini-switch">
                                </div>
                            </div>
                            
                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>
                            
                            <div class="setting-item" style="margin-top:10px">
                                <span class="setting-label">
                                    列表页功能按钮
                                </span>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label">按钮-打开待鉴定:</span>
                                <div class="form-content">
                                    <input type="checkbox" id="showWaitCheckBtn" class="mini-switch">
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label">按钮-打开已收藏:</span>
                                <div class="form-content">
                                    <input type="checkbox" id="showWaitDownBtn" class="mini-switch">
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label">打开待鉴定|已收藏 窗口数:</span>
                                <div class="form-content">
                                    <input type="number" id="waitCheckCount" min="1" max="20" style="width: 100%;">
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label">随机打开已收藏:</span>
                                <div class="form-content">
                                    <input type="checkbox" id="randomOpenWaitDown" class="mini-switch">
                                </div>
                            </div>
                            
                            
                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>
                            
                            <div class="setting-item" style="margin-top:10px">
                                <span class="setting-label">
                                    封面快捷按钮
                                </span>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label" style="display:flex; align-items:center; gap:5px">
                                    ${coverButtonPlugin.screenSvg}长缩略图:
                                </span>
                                <div class="form-content">
                                    <input type="checkbox" id="enableScreenSvg" class="mini-switch">
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label" style="display:flex; align-items:center; gap:5px">
                                    ${coverButtonPlugin.videoSvg}预览视频:
                                </span>
                                <div class="form-content">
                                    <input type="checkbox" id="enableVideoSvg" class="mini-switch">
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label" style="display:flex; align-items:center; gap:5px">
                                    ${coverButtonPlugin.handleSvg}鉴定按钮:
                                </span>
                                <div class="form-content">
                                    <input type="checkbox" id="enableHandleSvg" class="mini-switch">
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label" style="display:flex; align-items:center; gap:5px">
                                    ${coverButtonPlugin.siteSvg}第三方跳转:
                                </span>
                                <div class="form-content">
                                    <input type="checkbox" id="enableSiteSvg" class="mini-switch">
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label" style="display:flex; align-items:center; gap:5px">
                                    ${coverButtonPlugin.copySvg}复制按钮:
                                </span>
                                <div class="form-content">
                                    <input type="checkbox" id="enableCopySvg" class="mini-switch">
                                </div>
                            </div>
                            
                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>

                            <div class="setting-item">
                                <span class="setting-label">预览视频默认画质:</span>
                                <div class="form-content">
                                    <select id="videoQuality">
                                        ${videoQualityHtml}
                                    </select>
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label">评论区条数:</span>
                                <div class="form-content">
                                    <select id="reviewCount">
                                        <option value="10">10条</option>
                                        <option value="20">20条</option>
                                        <option value="30">30条</option>
                                        <option value="40">40条</option>
                                        <option value="50">50条</option>
                                    </select>
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label">
                                    高亮已收藏演员 <span data-tip="详情页, 对已收藏的演员进行边框高亮提醒">❓</span>
                                </span>
                                <div class="form-content">
                                    <input type="checkbox" id="enableFavoriteActresses" class="mini-switch">
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span id="highlightedTagLabel" class="setting-label">
                                    分类标签|高亮演员-边框样式:
                                </span>
                                <div class="form-content" style="display: flex; align-items: center;">
                                    <input type="number" id="highlightedTagNumber" min="0" max="20">
                                    <input type="color" id="highlightedTagColor">
                                </div>
                            </div>

                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>
                            
                            <div class="setting-item">
                                <span class="setting-label">请求超时时间(毫秒):</span>
                                <div class="form-content">
                                    <input type="number" id="httpTimeout" min="1000" max="10000" style="width: 100%;">
                                </div>
                            </div>
                            
                            <div class="setting-item">
                                <span class="setting-label">请求失败重试次数:</span>
                                <div class="form-content">
                                    <input type="number" id="httpRetryCount" min="0" max="10" style="width: 100%;">
                                </div>
                            </div>
                            
                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>
                            
                            <div class="setting-item">
                                <span class="setting-label">
                                    启用控制台日志:
                                </span>
                                <div class="form-content">
                                    <select id="enableClog">
                                        <option value="no">禁用</option>
                                        <option value="yes">开启</option>
                                    </select>
                                </div>
                            </div>

                            <div class="setting-item">
                                <span class="setting-label">日志最大行数:</span>
                                <div class="form-content">
                                    <input type="number" id="clogMsgCount" min="100" max="3000" style="width: 100%;">
                                </div>
                            </div>
                        </div>
                        
                        <!-- 定时任务 -->
                        <div id="task-panel" class="content-panel" style="display: ${"task-panel" === defaultActivePanel ? "block" : "none"};">
                        
                            <div class="setting-item">
                                <span class="setting-label">请求并发数量:</span>
                                <div class="form-content">
                                    <input type="number" id="checkConcurrencyCount" min="2" max="5" style="width: 100%;">
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">请求间隔时间(毫秒):</span>
                                <div class="form-content">
                                    <input type="number" id="checkRequestSleep" min="0" max="3000" style="width: 100%;">
                                </div>
                            </div>
                        
                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>
                        
                            <div id="setting-blacklist" style="border: 1px solid #ccc; padding: 10px; margin-bottom: 15px;">
                                <span style="font-size: 14px; font-weight: bold; padding:3px">自动检测屏蔽黑名单演员</span>
                                <div class="setting-item">
                                    <span class="setting-label">
                                        任务开关: <span data-tip="变更后, 刷新页面生效">❓</span> 
                                    </span>
                                    <div class="form-content">
                                        <select id="enableCheckBlacklist">
                                            <option value="no">禁用</option>
                                            <option value="yes">开启</option>
                                        </select>
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">任务间隔时间:</span>
                                    <div class="form-content">
                                         <select id="checkBlacklist_intervalTime">
                                            <option value="2">每2小时</option>
                                            <option value="3">每3小时</option>
                                            <option value="6">每6小时</option>
                                            <option value="12">每12小时</option>
                                            <option value="24">每24小时</option>
                                        </select>
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">检测规则:</span>
                                    <div class="form-content">
                                         <select id="checkBlacklist_ruleTime">
                                            <option value="0">全部检测</option>
                                            <option value="8760">不检测停更1年以上</option>
                                            <option value="17520">不检测停更2年以上</option>
                                            <option value="26280">不检测停更3年以上</option>
                                        </select>
                                    </div>
                                </div>
                            </div>
                        
                            <div id="setting-checkFavoriteActress" style="border: 1px solid #ccc; padding: 10px; margin-bottom: 15px;" class="${isJavDb$1 ? "" : "do-hide"}">
                                <span style="font-size: 14px; font-weight: bold; padding:3px">自动同步已收藏的演员</span>
                                <div class="setting-item">
                                    <span class="setting-label">
                                        任务开关: <span data-tip="变更后, 刷新页面生效">❓</span> 
                                    </span>
                                    <div class="form-content">
                                        <select id="enableCheckFavoriteActress">
                                            <option value="no">禁用</option>
                                            <option value="yes">开启</option>
                                        </select>
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">任务间隔时间:</span>
                                    <div class="form-content">
                                         <select id="checkFavoriteActress_IntervalTime">
                                            <option value="12">每12小时</option>
                                            <option value="24">每24小时</option>
                                        </select>
                                    </div>
                                </div>
                            </div>
                        
                            <div id="setting-checkNewVideo" style="border: 1px solid #ccc; padding: 10px; margin-bottom: 15px;" class="${isJavDb$1 ? "" : "do-hide"}">
                                <span style="font-size: 14px; font-weight: bold; padding:3px">自动检测已收藏演员的最新作品</span>
                                <div class="setting-item">
                                    <span class="setting-label">
                                        任务开关: <span data-tip="变更后, 刷新页面生效">❓</span> 
                                    </span>
                                    <div class="form-content">
                                        <select id="enableCheckNewVideo">
                                            <option value="no">禁用</option>
                                            <option value="yes">开启</option>
                                        </select>
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">任务间隔时间:</span>
                                    <div class="form-content">
                                         <select id="checkNewVideo_intervalTime">
                                            <option value="2">每2小时</option>
                                            <option value="3">每3小时</option>
                                            <option value="6">每6小时</option>
                                            <option value="12">每12小时</option>
                                            <option value="24">每24小时</option>
                                        </select>
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">检测规则:</span>
                                    <div class="form-content">
                                         <select id="checkNewVideo_ruleTime">
                                            <option value="0">全部检测</option>
                                            <option value="8760">不检测停更1年以上</option>
                                            <option value="17520">不检测停更2年以上</option>
                                            <option value="26280">不检测停更3年以上</option>
                                        </select>
                                    </div>
                                </div>
                            </div>
                        </div>               
         
                        <!-- 离线配置面板 -->
                        <div id="offline-panel" class="content-panel" style="display: ${"offline-panel" === defaultActivePanel ? "block" : "none"};">
                            <div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 15px;">
                                <span style="font-size: 14px; font-weight: bold; padding:3px">115离线文件夹命名</span>
                                <div class="setting-item">
                                    <span class="setting-label">
                                        启用自定义格式:
                                    </span>
                                    <div class="form-content" style="text-align: right;">
                                        <input type="checkbox" id="enable115OfflineFormat" class="mini-switch">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">命名格式:</span>
                                    <div class="form-content" style="max-width: 600px;">
                                        <input id="offlineFolderFormat" placeholder="例: \${code}" style="width: 100%;">
                                    </div>
                                </div>
                                <div style="margin-top: 10px; padding: 8px; background: #f8f8f8; border-radius: 4px; font-size: 12px; color: #666; line-height: 1.8;">
                                    <div style="font-weight: bold; margin-bottom: 4px;">可用变量(\${} 形式):</div>
                                    <div><code>code</code> - 完整番号(如 ABC-123)</div>
                                    <div><code>title</code> - 作品标题</div>
                                    <div><code>maker</code> - 片商</div>
                                    <div><code>actor</code> - 演员(多演员只取第一个)</div>
                                    <div><code>date</code> - 当前日期(YYYY-MM-DD)</div>
                                    <div style="margin-top: 6px; color: #999;">例: <code>\${actor}/\${code}</code> = 演员目录下的番号子目录</div>
                                    <div style="color: #e67e22;">支持 / 分隔的多级目录,离线文件下载到最末级子目录</div>
                                </div>
                            </div>
                        </div>

                        <!-- 域名设置面板 -->
                        <div id="domain-panel" class="content-panel" style="display: ${"domain-panel" === defaultActivePanel ? "block" : "none"};">
                            <div class="setting-item">
                                <span class="setting-label">域名 - MissAv:</span>
                                <div class="form-content">
                                    <input id="missAvUrl">
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">域名 - Jable:</span>
                                <div class="form-content">
                                    <input id="jableUrl">
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">域名 - JavTrailer:</span>
                                <div class="form-content">
                                    <input id="javTrailersUrl">
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">域名 - 123Av:</span>
                                <div class="form-content">
                                    <input id="av123Url">
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">域名 - JavDb:</span>
                                <div class="form-content">
                                    <input id="javDbUrl">
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">域名 - JavBus:</span>
                                <div class="form-content">
                                    <input id="javBusUrl">
                                </div>
                            </div>
                            <div class="setting-item">
                                <span class="setting-label">域名 - SupJav:</span>
                                <div class="form-content">
                                    <input id="supJavUrl">
                                </div>
                            </div>           
                        </div>
                         
                         <!-- 标记删除 -->
                        <div id="mark-delete-panel" class="content-panel" style="display: ${"mark-delete-panel" === defaultActivePanel ? "block" : "none"};">
                            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
                                <span style="font-size: 14px; font-weight: bold;">标记删除列表</span>
                                <button id="batchDeleteBtn" class="menu-btn" style="background-color:#e74c3c; color:white;">
                                    <span>一键清除</span>
                                </button>
                            </div>
                            <div style="margin-bottom: 10px;">
                                <label style="cursor: pointer;">
                                    <input type="checkbox" id="markDeleteSelectAll" checked> <b>全选</b>
                                </label>
                                <span id="markDeleteCount" style="margin-left: 10px; color: #888; font-size: 13px;"></span>
                            </div>
                            <div id="markDeleteList" style="max-height: 450px; overflow-y: auto; border: 1px solid #e0e0e0; border-radius: 4px;">
                                <div style="text-align: center; padding: 40px; color: #999;">加载中...</div>
                            </div>
                            <p style="color: #e74c3c; font-size: 12px; margin-top: 10px; text-align: left;">⚠ 作品删除目前暂无法删除115下载记录,如果需要重新下载,请去115云下载记录里删除或清空记录</p>
                        </div>
                         
                         <!-- 快捷键 -->
                        <div id="hotkey-panel" class="content-panel" style="display: ${"hotkey-panel" === defaultActivePanel ? "block" : "none"};">
                            <p style="color: #c62222; font-size: 14px;font-weight: bold;margin-bottom: 10px;">快捷键修改后, 刷新页面生效</p>
                            <div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 15px;">
                                <div class="setting-item">
                                    <span class="setting-label">🚫 屏蔽:</span>
                                    <div class="form-content">
                                        <input id="filterHotKey" placeholder="录入快捷键" data-default-hotkey="a">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">⭐ 收藏:</span>
                                    <div class="form-content">
                                        <input id="favoriteHotKey" placeholder="录入快捷键" data-default-hotkey="s">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">📥️ 已下载:</span>
                                    <div class="form-content">
                                        <input id="hasDownHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">🔍 已观看:</span>
                                    <div class="form-content">
                                        <input id="hasWatchHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                                
                                <div class="setting-item">
                                    <span class="setting-label">
                                        <span data-tip="列表页,鼠标放置图片上时可使用快捷键">❓ </span> 对视频列表页启用快捷键:
                                    </span>
                                    <div class="form-content">
                                        <input type="checkbox" id="enableImageHotKey" class="mini-switch">
                                    </div>
                                </div>
                            </div>
                            
                            <div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 15px;">
                                <div class="setting-item">
                                    <span class="setting-label">⏩ 快进:</span>
                                    <div class="form-content">
                                        <input id="speedVideoHotKey" placeholder="录入快捷键" data-default-hotkey="z">
                                    </div>
                                </div>
                                
                                <div class="setting-item">
                                    <span class="setting-label">▲ 折叠:</span>
                                    <div class="form-content">
                                        <input id="foldCategoryHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                                
                                <div class="setting-item">
                                    <span class="setting-label">💻 控制台:</span>
                                    <div class="form-content">
                                        <input id="clogHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                            </div>


                            <div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 15px;">
                                <span style="font-size: 14px; font-weight: bold; padding:3px">显示已鉴定内容</span>
                                <div class="setting-item">
                                    <span class="setting-label">屏蔽单番号:</span>
                                    <div class="form-content">
                                        <input id="showFilterItemHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">屏蔽演员:</span>
                                    <div class="form-content">
                                        <input id="showFilterActorItemHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">屏蔽关键词:</span>
                                    <div class="form-content">
                                        <input id="showFilterKeywordItemHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">收藏:</span>
                                    <div class="form-content">
                                        <input id="showFavoriteItemHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">已下载:</span>
                                    <div class="form-content">
                                        <input id="showHasDownItemHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">已观看:</span>
                                    <div class="form-content">
                                        <input id="showHasWatchItemHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>
                                <div class="setting-item">
                                    <span class="setting-label">显示所有:</span>
                                    <div class="form-content">
                                        <input id="showAllItemHotKey" placeholder="录入快捷键">
                                    </div>
                                </div>                                
                            </div>

                        </div>
                        
                        <!-- 屏蔽设置面板 -->
                        <div id="filter-panel" class="content-panel" style="display: ${"filter-panel" === defaultActivePanel ? "block" : "none"};">
                            <div class="setting-item">
                                <span class="setting-label">
                                     启用划词屏蔽 <span data-tip="视频详情页中, 标题或评论区选中文字, 按右键可快捷加入屏蔽词">❓ </span>
                                </span>
                                <div style="display: flex">
                                    <input type="checkbox" id="enableTitleSelectFilter" class="mini-switch">
                                </div>
                            </div>
                            
                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>
                            
                            <div id="reviewKeywordContainer">
                                <div class="setting-item">
                                    <span class="setting-label">评论区屏蔽词:</span>
                                    <div style="display: flex">
                                        <input type="text" class="keyword-input" placeholder="添加屏蔽词">
                                        <button class="add-tag-btn">添加</button>
                                    </div>
                                </div>
                                <div class="tag-box"> </div>
                            </div>
                            
                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>
                            
                            <div id="filterKeywordContainer">
                                <div class="setting-item">
                                    <span class="setting-label">视频标题屏蔽词:</span>
                                    <div style="display: flex">
                                        <input type="text" class="keyword-input" placeholder="添加屏蔽词">
                                        <button class="add-tag-btn">添加</button>
                                    </div>
                                </div>
                                <div class="tag-box"> </div>
                            </div>
                        </div>
                        <div id="cache-panel" class="content-panel" style="display: ${"cache-panel" === defaultActivePanel ? "block" : "none"};">
                            <h1 style="text-align:center;font-size: 20px;font-weight: bold">以下操作, 不会对核心数据造成影响</h1>
                            <br/>               
                            <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-top: 20px;">
                                ${buttonsHTML}
                            </div>    
                            <div id="cache-data-display" style="margin-top: 20px; display: none;">
                                <pre style="background: #f5f5f5; padding: 10px; border-radius: 5px; max-height: 400px; overflow: auto;"></pre>
                            </div>
                        </div>                        
                        <div id="tip-author-panel" class="content-panel" style="display: ${"tip-author-panel" === defaultActivePanel ? "block" : "none"};">
                            <p style="color: #666; font-size: 0.9em;">如果JHS-enhance给您带来了便捷和价值,请考虑给予一点支持,您的鼓励是我持续创作的最大动力!感谢您的慷慨支持!</p>
                            <div>
                                <div style="display: flex; justify-content: space-around; align-items: flex-start; margin-bottom: 20px; flex-wrap: wrap;">
                                    <div style="text-align: center; margin: 10px; flex: 1 1 30%; min-width: 150px;">
                                        <img src="https://i.imgur.com/ynGK6he.jpeg" alt="SOL二维码" style="width: 350px; height: 350px; border: 1px solid #ddd; padding: 5px; display: block; margin: 0 auto 5px;">
                                        <p>SOL</p>
                                        <input type="text" readonly value="5xvRp1jhWkCasT1H7Nh8G5eAuvdCFD5s2Xux2w3rJ8RG" onclick="this.select();document.execCommand('copy');alert('地址已复制!');" 
                                            style="width: 90%; padding: 5px; margin-top: 5px; border: 1px solid #a99087; background-color: #fff; text-align: center; font-size: 0.8em; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                                        <p style="font-size: 0.75em; color: #5a504c; margin-top: 4px;">点击地址可复制</p>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <div style="flex-shrink: 0; padding: 15px 20px; text-align: right; border-top: 1px solid #eee; background: white;">   
                        <button id="saveBtn">保存设置</button>
                        <button id="clean-all" style="display: none">♾️ 清理全部缓存</button>
                    </div>
                </div>
            </div>
        `;
          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 `
             <div class="jhs-scrollbar" style="margin-top:20px;max-height:90vh; overflow-y:auto;">
                <div style="margin: 0 10px;">
                    <div class="setting-item">
                        <span class="setting-label">
                            显示已鉴定内容:
                        </span>
                        <div class="form-content" style="display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;">
                            <span style="display:inline-block; width: 100px; font-size:13px; font-weight:bold; text-align: left">屏蔽单番号${showFilterItemHotKey ? `(${showFilterItemHotKey})` : ""}: </span><input type="checkbox" id="showFilterItem" class="mini-switch"><br/>
                            <span style="display:inline-block; width: 100px; font-size:13px; font-weight:bold; text-align: left">屏蔽演员${showFilterActorItemHotKey ? `(${showFilterActorItemHotKey})` : ""}: </span><input type="checkbox" id="showFilterActorItem" class="mini-switch"><br/>
                            <span style="display:inline-block; width: 100px; font-size:13px; font-weight:bold; text-align: left">屏蔽关键词${showFilterKeywordItemHotKey ? `(${showFilterKeywordItemHotKey})` : ""}: </span><input type="checkbox" id="showFilterKeywordItem" class="mini-switch"><br/>
                            <span style="display:inline-block; width: 100px; font-size:13px; font-weight:bold; text-align: left">收藏${showFavoriteItemHotKey ? `(${showFavoriteItemHotKey})` : ""}: </span><input type="checkbox" id="showFavoriteItem" class="mini-switch"><br/>
                            <span style="display:inline-block; width: 100px; font-size:13px; font-weight:bold; text-align: left">已下载${showHasDownItemHotKey ? `(${showHasDownItemHotKey})` : ""}: </span><input type="checkbox" id="showHasDownItem" class="mini-switch"><br/>
                            <span style="display:inline-block; width: 100px; font-size:13px; font-weight:bold; text-align: left">已观看${showHasWatchItemHotKey ? `(${showHasWatchItemHotKey})` : ""}: </span><input type="checkbox" id="showHasWatchItem" class="mini-switch"><br/>
                        </div>
                    </div>
                    <div class="setting-item">
                        <span class="setting-label">
                            <span data-tip="快速显示所有已鉴定内容,减少对以上开关的频繁操作">❓ </span> 显示所有${showAllItemHotKey ? `(${showAllItemHotKey})` : ""}:
                        </span>
                        <div class="form-content" style="display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;">
                            <input type="checkbox" id="showAllItem" class="mini-switch">
                        </div>
                    </div>
                    
                    <hr style="border: 0; height: 1px; margin:10px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>
                    
                    <div class="setting-item">
                        <span class="setting-label">
                            <span data-tip="点击封面的打开方式,弹窗|新窗口">❓ </span>弹窗方式打开页面:
                        </span>
                        <div class="form-content" style="text-align: right;">
                             <input type="checkbox" id="dialogOpenDetail" class="mini-switch">
                        </div>
                    </div>      
                    
                    <div class="setting-item">
                        <span class="setting-label">鉴定后立即关闭页面:</span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="needClosePage" class="mini-switch">
                        </div>
                    </div>
                    
                    <hr style="border: 0; height: 1px; margin:10px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>

                    <div class="setting-item">
                        <span class="setting-label">
                             <span data-tip="使用瀑布流模式, 排序方式将调整为默认">❓ </span>瀑布流模式:
                        </span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="autoPage" class="mini-switch">
                        </div>
                    </div>
       
                    <div class="setting-item">
                        <span class="setting-label">启用标题翻译:</span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="translateTitle" class="mini-switch">
                        </div>
                    </div>
                    
                    <div class="setting-item">
                        <span class="setting-label">启用悬浮大图:</span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="hoverBigImg" class="mini-switch">
                        </div>
                    </div>
                    
                    <div class="setting-item">
                        <span class="setting-label">
                            <span data-tip="匹配到不规则文件名时自动重命名为标准番号格式,如 mdvr00317-3.mp4 → MDVR-317-3.mp4">❓ </span>不规则重命名:<span style="font-size:11px;color:#999;white-space:nowrap;">(刷新详情页生效,有延迟)</span></span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="enable115AutoRename" class="mini-switch">
                        </div>
                    </div>
                    <div class="setting-item" id="autoRenameScopeRow" style="display:none;">
                        <span class="setting-label">重命名范围:</span>
                        <div class="form-content" style="text-align: right;">
                            <select id="autoRenameScope" style="padding: 2px 4px;">
                                <option value="vr_only">仅VR</option>
                                <option value="all">全部</option>
                            </select>
                        </div>
                    </div>
                    
                                        
                    <div class="setting-item">
                        <span class="setting-label">启用115视频匹配: </span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="enable115Match" class="mini-switch">
                        </div>
                    </div>
                    
                    <hr style="border: 0; height: 1px; margin:10px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>

                    ${isJavDb$1 ? '\n                    <div class="setting-item">\n                        <span class="setting-label">\n                            <span data-tip="详情页是否展示女优年龄、三围等信息">❓ </span>加载女优信息:\n                        </span>\n                        <div class="form-content" style="text-align: right;">\n                            <input type="checkbox" id="enableLoadActressInfo" class="mini-switch">\n                        </div>\n                    </div>' : ""}
                    
                    <div class="setting-item">
                        <span class="setting-label">
                            <span data-tip="详情页第三方资源检测,如missAv,123AV">❓ </span>加载第三方视频资源:
                        </span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="enableLoadOtherSite" class="mini-switch">
                        </div>
                    </div>
                    
                    <div class="setting-item">
                        <span class="setting-label">
                            <span data-tip="详情页图片区首列位置加载长缩略图">❓ </span>加载长缩略图:
                        </span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="enableLoadScreenShot" class="mini-switch">
                        </div>
                    </div>
                    
                     <div class="setting-item">
                        <span class="setting-label">
                            <span data-tip="详情页解析更多更高画质的预览视频">❓ </span>更高画质预览视频:
                        </span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="enableLoadPreviewVideo" class="mini-switch">
                        </div>
                    </div>

                    <hr style="border: 0; height: 1px; margin:10px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>

                    <div class="setting-item">
                        <span class="setting-label">
                            <span data-tip="列数6以上,建议开启竖图">❓ </span>竖图模式:
                        </span>
                        <div class="form-content" style="text-align: right;">
                            <input type="checkbox" id="enableVerticalModel" class="mini-switch">
                        </div>
                    </div>
                                    
                    <div class="setting-item">
                        <span class="setting-label">页面列数: <span id="showContainerColumns"></span></span>
                        <div class="form-content">
                            <input type="range" id="containerColumns" min="2" max="10" step="1" style="padding:5px 0">
                        </div>
                    </div>
                    
                    <div class="setting-item">
                        <span class="setting-label">页面宽度: <span id="showContainerWidth"></span></span>
                        <div class="form-content">
                            <input type="range" id="containerWidth" min="0" max="30" step="1" style="padding:5px 0">
                        </div>
                    </div>
                </div>
                <div style="padding: 0 20px 15px; text-align: right; border-top: 1px solid #eee;">   
                    <button id="helpBtn" style="float:left;">常见问题</button>
                    <button id="moreBtn">更多设置</button>
                </div>
            </div>
        `;
        }
        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: `
<style>
    .help-container {
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        color: #333;
        padding: 15px;
        max-height: 100%;
        overflow-y: auto;
    }
    
    .help-section {
        margin-bottom: 25px;
    }
    
    .help-section summary {
        font-size: 18px;
        color: #3498db;
        margin-bottom: 12px;
        cursor: pointer;
    }
    
    .help-content {
        background-color: #f9f9f9;
        border-radius: 5px;
        padding: 15px;
        border-left: 4px solid #3498db;
    }
    
    .help-content p {
        line-height: 1.6;
        margin-bottom: 10px;
    }
    .help-section img {
        max-width: 100%;
        height: auto;
        border: 1px solid #ddd;
        border-radius: 4px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }

</style>

<div class="help-container">
    <h1 style="font-size: 22px; margin-bottom: 20px; color: #2c3e50; border-bottom: 1px solid #eee; padding-bottom: 10px;">使用说明</h1>
    
    <details class="help-section">
        <summary>1. 无法查看预览视频,提示分流?</summary>
        <div class="help-content">
            <p>JavDB限制日本IP的访问,而预览视频来自DMM,需要日本IP才能访问。</p>
            <p>这样会导致二者无法同时使用,需要对其一进行代理转发。</p>
            <p>将 cc3001.dmm.co.jp 及 dmm.co 分流到日本ip。</p>
            <p><a href="https://youtu.be/wQUK8z_YeU4?t=121" target="_blank">Clash Verge分流规则设置 </a> (如果你是别的代理软件,自行搜索如何分流)</p>
        </div>
    </details>
    
    <details class="help-section">
        <summary>2. 如何屏蔽某一系列的番号?</summary>
        <div class="help-content">
            <p>方法一:设置中-添加视频标题关键词,如: VENX-</p>
            <p>方法二:进入详情页,选中标题文字,右键可加入</p>
            <img src="https://i.imgur.com/lVnhK5A.png" alt="进入详情页,选中标题,进行右键"/>
        </div>
    </details>

    <details class="help-section">
        <summary>3. 屏蔽某演员,如何只屏蔽单体影片?</summary>
        <div class="help-content">
            <p>屏蔽演员前,先筛选分类,再点屏蔽</p>
            <img src="https://imgur.com/Ue7eCAi.png" alt="屏蔽演员前,先筛选分类,再点屏蔽"/>
        </div>
    </details>
    
    <details class="help-section">
        <summary>4. 如何多浏览器同时登录115网盘?</summary>
        <div class="help-content">
            <p>① 访问115登录页, 选择JHS-扫码面板, 并扫码登录</p>
            <img src="https://imgur.com/XbaisWD.png" alt=""/>
        </div>
        <div class="help-content">
            <p>② 进入网盘后, 右下角悬浮按钮, 复制Cookie</p>
            <img src="https://imgur.com/GvzJ2Gy.png" alt=""/>
        </div>
        <div class="help-content">
            <p>③ 打开另一个浏览器(需装JHS脚本), 进入登录页面, 选择JHS-扫码面板, 输入Cookie并回车</p>
            <img src="https://imgur.com/FX08qdO.png" alt=""/>
        </div>
    </details>
</div>
`,
              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;
                }
            `;
            $("<style>").attr("id", "verticalImgStyle").text(verticalStyle).appendTo("head");
          } else {
            const horizontalStyle = "\n                .cover {\n                    min-height:auto !important;\n                    padding-top: 67% !important;\n                }\n                .cover img {\n                    object-fit: contain !important;\n                    object-position: 50% 50% !important\n                }\n                \n                /* bus的 */\n                 .masonry .movie-box img {\n                    min-height:auto !important;\n                    object-fit: contain !important;\n                    object-position: top;\n                }\n            ";
            $("<style>").attr("id", "verticalImgStyle").text(horizontalStyle).appendTo("head");
          }
          isJavBus$1 && this.getBean("BusImgPlugin").logImageHeightsByRow();
        }
        bindClick() {
          const self = this;
          $(".side-menu-item").on("click", (function() {
            $(".side-menu-item").removeClass("active");
            $(this).addClass("active");
            $(".content-panel").hide();
            const panelId = $(this).data("panel");
            $("#" + panelId).show();
            if ("cache-panel" === panelId || "mark-delete-panel" === panelId) {
              $("#saveBtn").hide();
              "cache-panel" === panelId ? $("#clean-all").show() : $("#clean-all").hide();
            } else {
              $("#saveBtn").show();
              $("#clean-all").hide();
            }
            if ("mark-delete-panel" === panelId) {
              self.loadMarkDeleteList();
            }
          }));
          $("#importBtn").on("click", ((event) => this.importData(event)));
          $("#exportBtn").on("click", ((event) => this.exportData(event)));
          $("#backupBtn").on("click", ((event) => this.backupData(event)));
          $("#backupListBtn").on("click", ((event) => this.backupListBtn(event)));
          $("#webdavBackupBtn").on("click", ((event) => this.backupDataByWebDav(event)));
          $("#webdavBackupListBtn").on("click", ((event) => this.backupListBtnByWebDav(event)));
          $("#getRefreshTokenBtn").on("click", ((event) => layer.alert("即将跳转阿里云盘, 请登录后, 点击最右侧悬浮按钮获取refresh_token", {
            yes: function(index, layero, that) {
              window.open("https://www.aliyundrive.com/drive/home");
              layer.close(index);
            }
          })));
          $("#saveBtn").on("click", (() => this.saveForm()));
          $(".clean-btn").on("click", ((event) => {
            const key = $(event.currentTarget).data("key"), cacheItem = this.cacheItems.find(((item) => item.key === key));
            localStorage.removeItem(key);
            show.ok(`${cacheItem.text} 清理成功`);
            $("#cache-data-display").hide();
            "jhs_dmm_video" === key && localStorage.removeItem("jhs_other_site_dmm");
          }));
          $("#clean-all").on("click", (() => {
            this.cacheItems.forEach(((item) => localStorage.removeItem(item.key)));
            show.ok("全部缓存已清理");
            $("#cache-data-display").hide();
            localStorage.removeItem("jhs_other_site_dmm");
          }));
          $(".view-btn").on("click", ((event) => {
            const key = $(event.currentTarget).data("key"), data = localStorage.getItem(key), displayDiv = $("#cache-data-display"), pre = displayDiv.find("pre");
            displayDiv.show();
            if (data) try {
              const parsedData = JSON.parse(data);
              pre.text(JSON.stringify(parsedData, null, 2));
            } catch {
              pre.text(data);
            }
            else pre.text("无数据");
          }));
          const $widthInput = $("#highlightedTagNumber"), $colorPicker = $("#highlightedTagColor"), $previewBox = $("#highlightedTagLabel");
          function updateBorder() {
            const currentWidth = $widthInput.val(), currentColor = $colorPicker.val();
            $previewBox.css("border", `${currentWidth}px solid ${currentColor}`);
          }
          $widthInput.on("input", updateBorder);
          $colorPicker.on("input", updateBorder);
          $("#markDeleteSelectAll").on("change", (function() {
            const checked = $(this).prop("checked");
            $(".mark-delete-item-cb").prop("checked", checked);
          }));
          $("#batchDeleteBtn").on("click", (async () => {
            const markedList = await storageManager.forage.getItem("markedDeleteList") || [];
            if (markedList.length === 0) {
              show.error("没有标记删除的作品");
              return;
            }
            const checkedItems = [];
            $(".mark-delete-item-cb:checked").each((function() {
              const index = parseInt($(this).data("index"));
              checkedItems.push(markedList[index]);
            }));
            if (checkedItems.length === 0) {
              show.error("请至少选择一个作品");
              return;
            }
            utils.q(null, `确认一键删除 ${checkedItems.length} 个作品?此操作不可撤销!`, (async () => {
              let loadObj = loading();
              try {
                const matchPlugin = this.getBean("WangPan115MatchPlugin");
                const result = await matchPlugin.batchDeleteMarked(checkedItems, ((current, total, item, status) => {
                  loadObj.close();
                  loadObj = loading();
                  $(`#markDeleteProgress`).text(`正在删除: ${current}/${total} - ${item.carNum} ${status}`);
                }));
                loadObj.close();
                const remaining = markedList.filter((item) => !checkedItems.some((ci) => ci.carNum === item.carNum));
                await storageManager.forage.setItem("markedDeleteList", remaining);
                show.ok(`删除完成: 成功 ${result.deletedCount} 个,失败 ${result.failedCount} 个`);
                this.loadMarkDeleteList();
              } catch (error) {
                loadObj.close();
                console.error("批量删除失败:", error);
                show.error(`批量删除失败: ${error.message || "网络错误"}`);
              }
            }));
          }));
          $(document).on("click", ".mark-delete-remove-btn", (async (event) => {
            event.preventDefault();
            const $btn = $(event.currentTarget);
            const index = parseInt($btn.data("index"));
            const markedList = await storageManager.forage.getItem("markedDeleteList") || [];
            if (index >= 0 && index < markedList.length) {
              const item = markedList[index];
              utils.q(null, `确认从标记列表中移除 "${item.title}"?`, (async () => {
                markedList.splice(index, 1);
                await storageManager.forage.setItem("markedDeleteList", markedList);
                show.ok(`已移除: ${item.title}`);
                const self2 = this;
                $(`.markDeleteSvg`).each((function() {
                  const $markBtn = $(this);
                  const $box2 = $markBtn.closest(".item");
                  const { carNum: carNum2 } = self2.getBoxCarInfo($box2);
                  if (carNum2 === item.carNum) {
                    $markBtn.removeClass("marked");
                    $markBtn.attr("title", "标记删除");
                  }
                }));
                this.loadMarkDeleteList();
              }));
            }
          }));
        }
        async saveForm() {
          let settingObj = await storageManager.getSetting();
          settingObj.videoQuality = $("#videoQuality").val();
          settingObj.reviewCount = $("#reviewCount").val();
          settingObj.tagPosition = $("#tagPosition").val();
          settingObj.movieShowType = $("#movieShowType").val();
          settingObj.waitCheckCount = $("#waitCheckCount").val();
          settingObj.refresh_token = $("#refresh_token").val();
          settingObj.highlightedTagNumber = $("#highlightedTagNumber").val();
          settingObj.highlightedTagColor = $("#highlightedTagColor").val();
          settingObj.showWaitCheckBtn = $("#showWaitCheckBtn").is(":checked") ? YES$1 : NO$1;
          settingObj.showWaitCheckBtn === YES$1 ? $("#waitCheckBtn").show() : $("#waitCheckBtn").hide();
          settingObj.showWaitDownBtn = $("#showWaitDownBtn").is(":checked") ? YES$1 : NO$1;
          settingObj.showWaitDownBtn === YES$1 ? $("#waitDownBtn").show() : $("#waitDownBtn").hide();
          settingObj.randomOpenWaitDown = $("#randomOpenWaitDown").is(":checked") ? YES$1 : NO$1;
          settingObj.checkConcurrencyCount = $("#checkConcurrencyCount").val();
          settingObj.checkRequestSleep = $("#checkRequestSleep").val();
          settingObj.enableCheckBlacklist = $("#enableCheckBlacklist").val();
          settingObj.checkBlacklist_intervalTime = $("#checkBlacklist_intervalTime").val();
          settingObj.checkBlacklist_ruleTime = $("#checkBlacklist_ruleTime").val();
          settingObj.enableCheckFavoriteActress = $("#enableCheckFavoriteActress").val();
          settingObj.checkFavoriteActress_IntervalTime = $("#checkFavoriteActress_IntervalTime").val();
          settingObj.enableCheckNewVideo = $("#enableCheckNewVideo").val();
          settingObj.checkNewVideo_intervalTime = $("#checkNewVideo_intervalTime").val();
          settingObj.checkNewVideo_ruleTime = $("#checkNewVideo_ruleTime").val();
          settingObj.httpTimeout = $("#httpTimeout").val();
          settingObj.httpRetryCount = $("#httpRetryCount").val();
          settingObj.enableClog = $("#enableClog").val();
          settingObj.enableClog === YES$1 ? clog.show() : clog.hide();
          settingObj.clogMsgCount = $("#clogMsgCount").val();
          settingObj.webDavUrl = $("#webDavUrl").val();
          settingObj.webDavUsername = $("#webDavUsername").val();
          settingObj.webDavPassword = $("#webDavPassword").val();
          settingObj.missAvUrl = $("#missAvUrl").val().replace(/\/$/, "");
          settingObj.jableUrl = $("#jableUrl").val().replace(/\/$/, "");
          settingObj.javTrailersUrl = $("#javTrailersUrl").val().replace(/\/$/, "");
          settingObj.av123Url = $("#av123Url").val().replace(/\/$/, "");
          settingObj.javDbUrl = $("#javDbUrl").val().replace(/\/$/, "");
          settingObj.javBusUrl = $("#javBusUrl").val().replace(/\/$/, "");
          settingObj.supJavUrl = $("#supJavUrl").val().replace(/\/$/, "");
          settingObj.enableTitleSelectFilter = $("#enableTitleSelectFilter").is(":checked") ? YES$1 : NO$1;
          settingObj.enableFavoriteActresses = $("#enableFavoriteActresses").is(":checked") ? YES$1 : NO$1;
          settingObj.enableSaveActressCarInfo = $("#enableSaveActressCarInfo").is(":checked") ? YES$1 : NO$1;
          settingObj.enableScreenSvg = $("#enableScreenSvg").is(":checked") ? YES$1 : NO$1;
          settingObj.enableVideoSvg = $("#enableVideoSvg").is(":checked") ? YES$1 : NO$1;
          settingObj.enableHandleSvg = $("#enableHandleSvg").is(":checked") ? YES$1 : NO$1;
          settingObj.enableSiteSvg = $("#enableSiteSvg").is(":checked") ? YES$1 : NO$1;
          settingObj.enableCopySvg = $("#enableCopySvg").is(":checked") ? YES$1 : NO$1;
          $("#hotkey-panel [id]").map(((i, el) => el.id)).get().forEach(((containerId) => {
            settingObj[containerId] = $(`#${containerId}`).val();
          }));
          settingObj.enableImageHotKey = $("#enableImageHotKey").is(":checked") ? YES$1 : NO$1;
          settingObj.enable115OfflineFormat = $("#enable115OfflineFormat").is(":checked") ? YES$1 : NO$1;
          settingObj.offlineFolderFormat = $("#offlineFolderFormat").val();
          await storageManager.saveSetting(settingObj);
          let reviewKeywordList = [];
          $("#reviewKeywordContainer .keyword-label").toArray().forEach(((item) => {
            let keyword = $(item).text().replace("×", "").replace(/[\r\n]+/g, " ").replace(/\s{2,}/g, " ").trim();
            reviewKeywordList.push(keyword);
          }));
          await storageManager.saveReviewFilterKeyword(reviewKeywordList);
          let filterKeywordList = [];
          $("#filterKeywordContainer .keyword-label").toArray().forEach(((item) => {
            let keyword = $(item).text().replace("×", "").replace(/[\r\n]+/g, " ").replace(/\s{2,}/g, " ").trim();
            filterKeywordList.push(keyword);
          }));
          await storageManager.saveTitleFilterKeyword(filterKeywordList);
          show.ok("保存成功");
          window.refresh();
          const newVideoPlugin = this.getBean("NewVideoPlugin");
          newVideoPlugin && newVideoPlugin.resetBtnTip();
          this.getBean("BlacklistPlugin").resetBtnTip();
          this.getBean("BlacklistPlugin").reloadTable();
        }
        async loadMarkDeleteList() {
          const markedList = await storageManager.forage.getItem("markedDeleteList") || [];
          const $list = $("#markDeleteList");
          const $count = $("#markDeleteCount");
          $count.text(`共 ${markedList.length} 个作品`);
          if (markedList.length === 0) {
            $list.html('<div style="text-align: center; padding: 40px; color: #999;">暂无标记删除的作品</div>');
            return;
          }
          let html2 = "";
          for (let i = 0; i < markedList.length; i++) {
            const item = markedList[i];
            const truncatedTitle = item.title && item.title.length > 30 ? item.title.substring(0, 30) + "..." : item.title;
            const detailUrl = item.url ? item.url.startsWith("http") ? item.url : window.location.origin + item.url : "#";
            html2 += `<div style="display: flex; align-items: center; padding: 8px 12px; border-bottom: 1px solid #eee;">
                <input type="checkbox" class="mark-delete-item-cb" data-index="${i}" checked style="margin-right: 10px; flex-shrink: 0;">
                <span style="flex: 1; min-width: 0;">
                    <div style="font-weight: bold; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${item.carNum}</div>
                    <a href="${detailUrl}" target="_blank" title="${item.title}" style="font-size: 12px; color: #2980b9; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; text-decoration: none;">${truncatedTitle}</a>
                    <div style="font-size: 11px; color: #999;">${item.publishTime || "-"} | 评分: ${item.score || "-"}</div>
                </span>
                <a class="mark-delete-remove-btn" data-index="${i}" style="color: #e74c3c; cursor: pointer; margin-left: 8px; flex-shrink: 0; font-size: 12px;" title="从列表移除">✕</a>
            </div>`;
          }
          html2 += `<div id="markDeleteProgress" style="text-align: center; padding: 8px; color: #e74c3c; font-size: 13px; font-weight: bold;"></div>`;
          $list.html(html2);
          $("#markDeleteSelectAll").prop("checked", true);
        }
        addLabelTag(containerId, keyword) {
          const $tagBox = $(`${containerId} .tag-box`);
          let $label, color2 = "#333";
          if (/^[a-z]{2,}-/i.test(keyword) && isJavDb$1) {
            color2 = "#3477ad";
            $label = $(`
                <a class="keyword-label" data-keyword="${keyword}" style="background-color: #cbd5e1; color: ${color2}" href="/video_codes/${keyword.replace("-", "")}" target="_blank">
                    ${keyword}
                    <span class="keyword-remove">×</span>
                </a>
            `);
          } else $label = $(`
                <div class="keyword-label" data-keyword="${keyword}" style="background-color: #cbd5e1; color: ${color2}">
                    ${keyword}
                    <span class="keyword-remove">×</span>
                </div>
            `);
          $label.find(".keyword-remove").click(((event) => {
            event.stopPropagation();
            event.preventDefault();
            const $targetEl = $(event.currentTarget);
            const dataKeyword = $targetEl.closest(".keyword-label").attr("data-keyword").split(" ")[0];
            utils.q(event, `是否移除屏蔽词  ${dataKeyword}?`, (async () => {
              $targetEl.parent().remove();
            }));
          }));
          $tagBox.append($label);
        }
        addKeyword(event, containerId) {
          let $keywordInput = $(`${containerId} .keyword-input`);
          const keyword = $keywordInput.val().trim();
          if (keyword) {
            this.addLabelTag(containerId, keyword);
            $keywordInput.val("");
          }
        }
        importData() {
          try {
            const input2 = document.createElement("input");
            input2.type = "file";
            input2.accept = ".json";
            input2.onchange = (e) => {
              const file = e.target.files[0];
              if (!file) return;
              const reader = new FileReader();
              reader.onload = (event) => {
                try {
                  const content = event.target.result.toString(), updateJsonData = JSON.parse(content);
                  layer.confirm("确定是否要覆盖导入?", {
                    icon: 3,
                    title: "确认覆盖",
                    btn: ["确定", "取消"]
                  }, (async function(index) {
                    await storageManager.importData(updateJsonData);
                    show.ok("数据导入成功");
                    layer.close(index);
                    location.reload();
                  }));
                } catch (err) {
                  console.error(err);
                  show.error("导入失败:文件内容不是有效的JSON格式 " + err);
                }
              };
              reader.onerror = () => {
                show.error("读取文件时出错");
              };
              reader.readAsText(file);
            };
            document.body.appendChild(input2);
            input2.click();
            setTimeout((() => document.body.removeChild(input2)), 1e3);
          } catch (err) {
            console.error(err);
            show.error("导入数据时出错: " + err.message);
          }
        }
        async backupData(event) {
          let loadObj = loading();
          try {
            const refresh_token = await storageManager.getSetting("refresh_token");
            if (!refresh_token) {
              show.error("请填写refresh_token并保存后, 再试此功能");
              return;
            }
            show.info("正在整理数据...");
            let fileName = utils.getNowStr("_", "_") + ".json", uploadContent = JSON.stringify(await storageManager.exportData());
            uploadContent = simpleEncrypt(uploadContent);
            const aliyunApi = new AliyunApi(refresh_token);
            await aliyunApi.backup(this.folderName, fileName, uploadContent);
            show.ok("备份完成");
          } catch (e) {
            console.error(e);
            show.error(e.toString());
          } finally {
            loadObj.close();
          }
        }
        async backupListBtn(event) {
          const refresh_token = await storageManager.getSetting("refresh_token");
          if (!refresh_token) {
            show.error("请填写refresh_token并保存后, 再试此功能");
            return;
          }
          let loadObj = loading();
          try {
            const aliyunApi = new AliyunApi(refresh_token), fileList = await aliyunApi.getBackupList(this.folderName);
            this.openFileListDialog(fileList, aliyunApi, "阿里云盘");
          } catch (e) {
            console.error(e);
            show.error(`发生错误: ${e ? e.message : e}`);
          } finally {
            loadObj.close();
          }
        }
        async backupDataByWebDav(event) {
          const settingObj = await storageManager.getSetting(), webDavUrl = settingObj.webDavUrl;
          if (!webDavUrl) {
            show.error("请填写webDav服务地址并保存后, 再试此功能");
            return;
          }
          const webDavUsername = settingObj.webDavUsername;
          if (!webDavUsername) {
            show.error("请填写webDav用户名并保存后, 再试此功能");
            return;
          }
          const webDavPassword = settingObj.webDavPassword;
          if (!webDavPassword) {
            show.error("请填写webDav密码并保存后, 再试此功能");
            return;
          }
          let fileName = utils.getNowStr("_", "_") + ".json", uploadContent = JSON.stringify(await storageManager.exportData());
          uploadContent = simpleEncrypt(uploadContent);
          let loadObj = loading();
          try {
            const webDavApi = new WebDavApi(webDavUrl, webDavUsername, webDavPassword);
            await webDavApi.backup(this.folderName, fileName, uploadContent);
            show.ok("备份完成");
          } catch (e) {
            console.error(e);
            show.error(e.toString());
          } finally {
            loadObj.close();
          }
        }
        async backupListBtnByWebDav(event) {
          const settingObj = await storageManager.getSetting(), webDavUrl = settingObj.webDavUrl;
          if (!webDavUrl) {
            show.error("请填写webDav服务地址并保存后, 再试此功能");
            return;
          }
          const webDavUsername = settingObj.webDavUsername;
          if (!webDavUsername) {
            show.error("请填写webDav用户名并保存后, 再试此功能");
            return;
          }
          const webDavPassword = settingObj.webDavPassword;
          if (!webDavPassword) {
            show.error("请填写webDav密码并保存后, 再试此功能");
            return;
          }
          let loadObj = loading();
          try {
            const webDavApi = new WebDavApi(webDavUrl, webDavUsername, webDavPassword), fileList = await webDavApi.getBackupList(this.folderName);
            this.openFileListDialog(fileList, webDavApi, "WebDav");
          } catch (e) {
            console.error(e);
            show.error(`发生错误: ${e ? e.message : e}`);
          } finally {
            loadObj.close();
          }
        }
        openFileListDialog(fileList, api, apiType) {
          layer.open({
            type: 1,
            title: apiType + "备份文件",
            content: '\n                <div style="height: 100%;overflow:hidden;"> \n                    <div id="table-container" style="height: calc(100%);"></div>\n                </div>\n            ',
            area: ["800px", "70%"],
            anim: -1,
            success: (layero) => {
              const tableObj = new Tabulator("#table-container", {
                layout: "fitColumns",
                placeholder: "暂无数据",
                virtualDom: true,
                data: fileList,
                responsiveLayout: "collapse",
                responsiveLayoutCollapse: true,
                columnDefaults: {
                  headerHozAlign: "center",
                  hozAlign: "center"
                },
                columns: [{
                  title: "文件名",
                  field: "name",
                  width: 200,
                  headerSort: false,
                  responsive: 0
                }, {
                  title: "文件大小",
                  field: "size",
                  responsive: 1,
                  headerSort: false,
                  formatter: (cell, formatterParams, onRendered) => {
                    const units = ["B", "KB", "MB", "GB", "TB", "PB"];
                    let unitIndex = 0, adjustedSize = cell.getData().size;
                    for (; adjustedSize >= 1024 && unitIndex < units.length - 1; ) {
                      adjustedSize /= 1024;
                      unitIndex++;
                    }
                    return `${adjustedSize % 1 == 0 ? adjustedSize.toFixed(0) : adjustedSize.toFixed(2)} ${units[unitIndex]}`;
                  }
                }, {
                  title: "备份日期",
                  field: "createTime",
                  responsive: 2,
                  headerSort: false,
                  formatter: (cell, formatterParams, onRendered) => {
                    const item = cell.getData();
                    return `${utils.getNowStr("-", ":", item.createTime)}`;
                  }
                }, {
                  title: "操作",
                  minWidth: 250,
                  responsive: 0,
                  headerSort: false,
                  formatter: (cell, formatterParams, onRendered) => {
                    const item = cell.getData();
                    onRendered((() => {
                      const deleteButton = cell.getElement().querySelector(".a-danger"), downButton = cell.getElement().querySelector(".a-primary"), importButton = cell.getElement().querySelector(".a-success");
                      deleteButton && deleteButton.addEventListener("click", ((e) => {
                        utils.q(e, `是否删除 ${item.name} ?`, (async (index) => {
                          let loadObj = loading();
                          try {
                            await api.deleteFile(item.fileId);
                            let newFileList = await api.getBackupList(this.folderName);
                            tableObj.replaceData(newFileList);
                            "阿里云盘" === apiType ? utils.alert(e, "已移至回收站, 请到阿里云盘回收站二次删除") : utils.alert(e, "删除成功");
                          } catch (e2) {
                            console.error(e2);
                            show.error(`发生错误: ${e2 ? e2.message : e2}`);
                          } finally {
                            loadObj.close();
                          }
                        }));
                      }));
                      downButton && downButton.addEventListener("click", (async (e) => {
                        let loadObj = loading();
                        try {
                          if ("阿里云盘" === apiType) {
                            show.info("获取下载地址...");
                            const url = await api.getDownloadUrl(item.fileId);
                            show.info("获取文件内容...");
                            let content = simpleDecrypt(await gmHttp.downloadFileInChunks(url, {
                              Referer: "https://www.aliyundrive.com/"
                            }));
                            utils.download(content, item.name);
                          } else {
                            const content = simpleDecrypt(await api.getFileContent(item.fileId));
                            utils.download(content, item.name);
                          }
                        } catch (e2) {
                          clog.error(e2);
                          show.error("下载失败: " + e2);
                        } finally {
                          loadObj.close();
                        }
                      }));
                      importButton && importButton.addEventListener("click", (async (e) => {
                        layer.confirm(`是否将该云备份数据 ${item.name} 导入?`, {
                          icon: 3,
                          title: "提示",
                          btn: ["确定", "取消"]
                        }, (async (index) => {
                          layer.close(index);
                          let loadObj = loading();
                          try {
                            let respData;
                            if ("阿里云盘" === apiType) {
                              show.info("获取下载地址...");
                              const downUrl = await api.getDownloadUrl(item.fileId);
                              show.info("获取文件内容...");
                              respData = await gmHttp.downloadFileInChunks(downUrl, {
                                Referer: "https://www.aliyundrive.com/"
                              });
                            } else respData = await api.getFileContent(item.fileId);
                            show.info("解密文件内容...");
                            const content = simpleDecrypt(respData);
                            show.info("解密完成, 开始导入...");
                            const updateJsonData = JSON.parse(content);
                            await storageManager.importData(updateJsonData);
                            show.ok("导入成功!");
                            window.location.reload();
                          } catch (err) {
                            console.error(err);
                            show.error(err);
                          } finally {
                            loadObj.close();
                          }
                        }));
                      }));
                    }));
                    return '\n                                    <a class="a-danger">删除</a>\n                                    <a class="a-primary">下载</a>\n                                    <a class="a-success">导入</a>\n                                ';
                  }
                }],
                locale: "zh-cn",
                langs: {
                  "zh-cn": {
                    pagination: {
                      first: "首页",
                      first_title: "首页",
                      last: "尾页",
                      last_title: "尾页",
                      prev: "上一页",
                      prev_title: "上一页",
                      next: "下一页",
                      next_title: "下一页",
                      all: "所有",
                      page_size: "每页行数"
                    }
                  }
                }
              });
            }
          });
        }
        async exportData(event) {
          try {
            const backupData = JSON.stringify(await storageManager.exportData()), fileName = `${utils.getNowStr("_", "_")}.json`;
            utils.download(backupData, fileName);
            show.ok("数据导出成功");
          } catch (err) {
            console.error(err);
            show.error("导出数据时出错: " + err.message);
          }
        }
      }
      class TaskPlugin extends BasePlugin {
        constructor() {
          super(...arguments);
          __publicField2(this, "singleTaskKey", "checkNewActressActorFilterCar");
          __publicField2(this, "taskConfig", null);
          __publicField2(this, "storageQueue", new AsyncQueue());
          __publicField2(this, "lastCheckFavoriteActressTimeKey", "jhs_time_checkFavoriteActress");
          __publicField2(this, "lastCheckBlacklistTimeKey", "jhs_time_checkBlacklist");
          __publicField2(this, "lastCheckNewVideoTimeKey", "jhs_time_checkNewVideo");
        }
        getName() {
          return "TaskPlugin";
        }
        async limitConcurrency(taskParamList, checkConcurrencyCount, checkRequestSleep, taskFn) {
          this.showIsRun();
          const activePromises = [], totalItems = taskParamList.length;
          let processedCount = 0;
          for (const taskParam of taskParamList) {
            const p = taskFn(taskParam).finally((() => {
              activePromises.splice(activePromises.indexOf(p), 1);
            }));
            activePromises.push(p);
            processedCount++;
            if (activePromises.length >= checkConcurrencyCount) {
              const remainingToStart = totalItems - processedCount;
              clog.debug(`剩余任务数: <span style="color: #f40">${remainingToStart}</span>`);
              await Promise.race(activePromises);
              await utils.sleep(checkRequestSleep);
            }
          }
          await Promise.all(activePromises);
        }
        isUnnecessaryCheck(lastCheckTimeStr, checkIntervalTime) {
          if (!checkIntervalTime) throw new Error("未传入checkIntervalTime");
          checkIntervalTime = parseInt(checkIntervalTime);
          return utils.getHourDifference(new Date(lastCheckTimeStr), /* @__PURE__ */ new Date()) < checkIntervalTime;
        }
        handle() {
          this.doTask().then();
        }
        showIsRun() {
          show.info("正在执行检测任务中, 请勿关闭当前窗口", {
            duration: 3e3
          });
        }
        async doTask() {
          if (window.isListPage) {
            await this.loadConfig();
            this.javDbUrl = await this.getBean("OtherSitePlugin").getJavDbUrl();
            return navigator.locks.request(this.singleTaskKey, {
              ifAvailable: true
            }, (async (lock) => {
              if (lock) {
                if (window.isListPage) {
                  this.taskConfig.enableCheckBlacklist === YES$1 ? await this.checkBlacklist() : clog.warn("自动检测屏蔽黑名单-禁用");
                  if (!isJavBus$1) {
                    if (this.taskConfig.enableCheckFavoriteActress === YES$1) {
                      const lastCheckFavoriteActressTimeStr = localStorage.getItem(this.lastCheckFavoriteActressTimeKey), checkFavoriteActress_IntervalTime = this.taskConfig.checkFavoriteActress_IntervalTime, isUnCheck = lastCheckFavoriteActressTimeStr && this.isUnnecessaryCheck(lastCheckFavoriteActressTimeStr, checkFavoriteActress_IntervalTime), isLogin = $('a[href*="/users/profile"]').length > 0;
                      isUnCheck && clog.debug(`检测同步演员, 上次检测时间: ${lastCheckFavoriteActressTimeStr} 检测间隔时间: ${checkFavoriteActress_IntervalTime}小时 未到时间`);
                      !isUnCheck && isLogin && await this.checkFavoriteActress();
                    } else clog.warn("自动同步已收藏的演员-禁用");
                    this.taskConfig.enableCheckNewVideo === YES$1 ? await this.checkNewVideo() : clog.warn("自动检测已收藏演员的最新作品-禁用");
                  }
                }
              } else clog.debug("争夺任务锁失败, 跳过执行");
            })).catch(((error) => {
              console.error("锁任务出现错误:", error);
              clog.error("锁任务出现错误:", error);
            })).finally((() => {
              setTimeout((() => {
                this.doTask();
              }), 3e5);
            }));
          }
        }
        async loadConfig() {
          const settingObj = await storageManager.getSetting();
          this.taskConfig = {
            checkConcurrencyCount: settingObj.checkConcurrencyCount ? Number(settingObj.checkConcurrencyCount) : 2,
            checkRequestSleep: settingObj.checkRequestSleep ? Number(settingObj.checkRequestSleep) : 100,
            enableCheckBlacklist: settingObj.enableCheckBlacklist || YES$1,
            checkBlacklist_intervalTime: settingObj.checkBlacklist_intervalTime ? Number(settingObj.checkBlacklist_intervalTime) : 12,
            checkBlacklist_ruleTime: settingObj.checkBlacklist_ruleTime ? Number(settingObj.checkBlacklist_ruleTime) : 8760,
            enableCheckFavoriteActress: settingObj.enableCheckFavoriteActress || YES$1,
            checkFavoriteActress_IntervalTime: settingObj.checkFavoriteActress_IntervalTime ? Number(settingObj.checkFavoriteActress_IntervalTime) : 24,
            enableCheckNewVideo: settingObj.enableCheckNewVideo || YES$1,
            checkNewVideo_intervalTime: settingObj.checkNewVideo_intervalTime ? Number(settingObj.checkNewVideo_intervalTime) : 12,
            checkNewVideo_ruleTime: settingObj.checkNewVideo_ruleTime ? Number(settingObj.checkNewVideo_ruleTime) : 8760
          };
        }
        async checkBlacklist(isManual) {
          let blacklist = await storageManager.getBlacklist();
          if (0 === blacklist.length) return;
          blacklist = blacklist.sort(((a, b) => a.createTime < b.createTime ? 1 : a.createTime > b.createTime ? -1 : 0));
          const checkConcurrencyCount = this.taskConfig.checkConcurrencyCount, checkRequestSleep = this.taskConfig.checkRequestSleep, checkBlacklist_intervalTime = this.taskConfig.checkBlacklist_intervalTime, checkBlacklist_ruleTime = this.taskConfig.checkBlacklist_ruleTime, lastCheckBlacklistTimeStr = localStorage.getItem(this.lastCheckBlacklistTimeKey);
          if (!isManual && lastCheckBlacklistTimeStr && this.isUnnecessaryCheck(lastCheckBlacklistTimeStr, checkBlacklist_intervalTime)) {
            clog.debug(`检测黑名单, 上次检测时间: ${lastCheckBlacklistTimeStr} 检测间隔时间: ${checkBlacklist_intervalTime}小时 未到时间`);
            return;
          }
          const taskParamList = [], msgList = [];
          for (const blacklistItem of blacklist) {
            let name2 = blacklistItem.name, checkTime = blacklistItem.checkTime, lastPublishTime = blacklistItem.lastPublishTime, url = blacklistItem.url;
            if (new URL(window.location.href).hostname === new URL(url).hostname) {
              if (isManual || !checkTime || !this.isUnnecessaryCheck(checkTime, checkBlacklist_intervalTime)) if (!lastPublishTime || 0 === checkBlacklist_ruleTime || this.isUnnecessaryCheck(lastPublishTime, checkBlacklist_ruleTime)) taskParamList.push(blacklistItem);
              else {
                let msg = `检测黑名单: ${name2} ${lastPublishTime} 停更超过${checkBlacklist_ruleTime / 24 / 365}年,跳过检测`;
                msgList.push(msg);
                $("#checkBlacklistMsg").text(msg);
              }
            } else clog.log("黑名单地址非同域名,跳过", url);
          }
          if (0 === taskParamList.length) return;
          msgList.forEach(((msg) => {
            clog.log(msg);
          }));
          clog.log(`<span style='color: #f40'>检测屏蔽黑名单, 总任务数: ${taskParamList.length}, 并发限制:${checkConcurrencyCount}, 请求间隔时间:${checkRequestSleep}ms</span>`);
          const blacklistPlugin = this.getBean("BlacklistPlugin");
          await this.limitConcurrency(taskParamList, checkConcurrencyCount, checkRequestSleep, (async (task) => {
            let { starId, name: name2, url } = task;
            try {
              clog.log("正在检屏黑名单演员:", name2, url);
              $("#checkBlacklistMsg").text(`正在检屏黑名单演员: ${name2} ${url}`);
              const html2 = await gmHttp.get(url), $dom = utils.htmlTo$dom(html2);
              this.storageQueue.addTask((async () => {
                let { lastPublishTime } = await blacklistPlugin.parseAndSaveFilterInfo($dom, name2, starId);
                await storageManager.updateBlacklistItem({
                  starId,
                  name: name2,
                  checkTime: utils.getNowStr(),
                  lastPublishTime
                });
              }));
            } catch (e) {
              $("#checkBlacklistMsg").text(`检测屏蔽演员信息, 发生错误: ${url}`);
              clog.error("检测屏蔽演员信息, 发生错误:", url, e);
              show.error("检测屏蔽演员信息, 发生错误:" + e, "bottom", "right");
            }
          }));
          await this.storageQueue.waitAllFinished();
          const updateLastCheckBlacklistTimeStr = utils.getNowStr();
          localStorage.setItem(this.lastCheckBlacklistTimeKey, updateLastCheckBlacklistTimeStr);
          clog.log('<span style="color: #f40">-------- END 检测屏蔽黑名单 END --------</span>');
          $("#checkBlacklistMsg").text("检测屏蔽黑名单, 结束");
          this.getBean("BlacklistPlugin").resetBtnTip().then();
        }
        async checkFavoriteActress() {
          const checkUrl = `${this.javDbUrl}/users/collection_actors`, actorInfoList = [];
          await this.scrapeActorInfo(checkUrl, actorInfoList);
          clog.log("所有演员信息已收集, 总计数量:", actorInfoList.length);
          $("#checkNewVideoMsg").text("同步完成");
          if (actorInfoList.length > 0) {
            await storageManager.addFavoriteActressList(actorInfoList);
            localStorage.setItem(this.lastCheckFavoriteActressTimeKey, utils.getNowStr());
            this.getBean("NewVideoPlugin").resetBtnTip().then();
          }
        }
        async scrapeActorInfo(currentUrl, actorInfoList) {
          clog.log(`正在抓取页面: ${currentUrl}`);
          $("#checkNewVideoMsg").text(`正在解析已收藏的演员: ${currentUrl}`);
          try {
            const html2 = await gmHttp.get(currentUrl), $dom = utils.htmlTo$dom(html2);
            $dom.find("#actors .actor-box a").each(((i, element) => {
              const $a = $(element), title = $a.attr("title"), href = $a.attr("href");
              if (title && href) {
                const allName = title.split(",").map(((name3) => name3.trim())).filter(((name3) => name3.length > 0)), name2 = allName[0] || "", segments = new URL(href, this.javDbUrl).pathname.split("/").filter(((s) => s.length > 0));
                let starId = "";
                segments.length > 0 && (starId = segments[segments.length - 1]);
                let actressType = "censored";
                const avatar = $a.find("img").attr("src"), $infoSpan = $a.find(".info");
                $infoSpan.length && $infoSpan.text().trim().includes("無碼") && (actressType = "uncensored");
                actorInfoList.push({
                  starId,
                  name: name2,
                  allName,
                  avatar,
                  actressType,
                  lastCheckTime: null,
                  lastUpdateTime: null
                });
              }
            }));
            const nextRelativeUrl = $dom.find(".pagination-next").attr("href");
            if (nextRelativeUrl) {
              const nextAbsoluteUrl = new URL(nextRelativeUrl, this.javDbUrl).href;
              await this.scrapeActorInfo(nextAbsoluteUrl, actorInfoList);
            }
          } catch (error) {
            clog.error(`抓取 ${currentUrl} 时发生错误:`, error);
          }
        }
        async checkNewVideo(isManual) {
          const dataActressInfoList = await storageManager.getFavoriteActressList(), actressInfoList = utils.genericSort(dataActressInfoList, [{
            key: (item) => {
              var _a2;
              return (null == (_a2 = item.newVideoList) ? void 0 : _a2.length) ?? 0;
            },
            order: "desc"
          }, {
            key: "lastPublishTime",
            order: "desc"
          }]), checkConcurrencyCount = this.taskConfig.checkConcurrencyCount, checkRequestSleep = this.taskConfig.checkRequestSleep, checkNewVideo_intervalTime = this.taskConfig.checkNewVideo_intervalTime, checkNewVideo_ruleTime = this.taskConfig.checkNewVideo_ruleTime, lastCheckNewVideoTimeStr = localStorage.getItem(this.lastCheckNewVideoTimeKey);
          if (!isManual && lastCheckNewVideoTimeStr && this.isUnnecessaryCheck(lastCheckNewVideoTimeStr, checkNewVideo_intervalTime)) {
            clog.debug(`检测新作品, 上次检测时间: ${lastCheckNewVideoTimeStr} 检测间隔时间: ${checkNewVideo_intervalTime}小时 未到时间`);
            return;
          }
          const taskParamList = [], msgList = [];
          for (const actress of actressInfoList) {
            const { lastCheckTime, lastPublishTime, name: name2 } = actress;
            !isManual && lastCheckTime && this.isUnnecessaryCheck(lastCheckTime, checkNewVideo_intervalTime) || (!lastPublishTime || 0 === checkNewVideo_ruleTime || this.isUnnecessaryCheck(lastPublishTime, checkNewVideo_ruleTime) ? taskParamList.push(actress) : msgList.push(`检测新作品: ${name2} ${lastPublishTime} 停更超过${checkNewVideo_ruleTime / 24 / 365}年,跳过检测`));
          }
          if (0 === taskParamList.length) return;
          msgList.forEach(((msg) => {
            clog.log(msg);
          }));
          clog.log(`<span style='color: #f40'>检测最新作品, 总任务数: ${taskParamList.length}, 并发限制:${checkConcurrencyCount}, 请求间隔时间:${checkRequestSleep}ms</span>`);
          const filterKeywordList = await storageManager.getTitleFilterKeyword(), filterActorActressCarList = await storageManager.getBlacklistCarList(), filterActorActressCarNumList = new Set(filterActorActressCarList.map(((car) => car.carNum)));
          await this.limitConcurrency(taskParamList, checkConcurrencyCount, checkRequestSleep, (async (task) => {
            const { lastCheckTime, name: name2, starId } = task;
            let url = `${this.javDbUrl}/actors/${starId}?t=d`;
            try {
              clog.log("正在检测最新作品, 演员:", name2, url);
              $("#checkNewVideoMsg").text(`正在检测最新作品, 演员: ${name2}`);
              const html2 = await gmHttp.get(url), $dom = utils.htmlTo$dom(html2);
              this.storageQueue.addTask((async () => {
                await this.parsePage($dom, starId, name2, filterKeywordList, filterActorActressCarNumList);
              }));
            } catch (e) {
              clog.error("检测屏蔽演员信息, 发生错误:", url, e);
              console.error("检测屏蔽演员信息, 发生错误:", url, e);
              show.error("检测屏蔽演员信息, 发生错误:" + e, "bottom", "right");
            }
          }));
          await this.storageQueue.waitAllFinished();
          localStorage.setItem(this.lastCheckNewVideoTimeKey, utils.getNowStr());
          clog.log('<span style="color: #f40">检测最新作品---结束</span>');
          $("#checkNewVideoMsg").text("检测完毕");
          const newVideoPlugin = this.getBean("NewVideoPlugin");
          newVideoPlugin.loadData();
          newVideoPlugin.resetBtnTip().then();
        }
        async parsePage($dom, starId, name2, filterKeywordList, filterActorActressCarNumList) {
          let movieList, nextPageLink, 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");
          if (nextPageLink && 0 === movieList.length) {
            clog.error("新作品检测-解析列表失败");
            show.error("新作品检测-解析列表失败");
            throw new Error("新作品检测-解析列表失败");
          }
          let carNumList = [], lastPublishTime = null;
          for (const element of movieList) {
            const $item = $(element), { carNum: carNum2, url, title, publishTime } = this.getBoxCarInfo($item);
            if (!carNum2) continue;
            if (!filterKeywordList.find(((kw) => title.includes(kw) || carNum2.startsWith(kw))) && !filterActorActressCarNumList.has(carNum2)) {
              lastPublishTime || (lastPublishTime = publishTime);
              carNumList.push(carNum2);
            }
          }
          const carList = await storageManager.getCarList(), storageCarNumList = new Set(carList.map(((car) => car.carNum))), nonExistingCarNumList = carNumList.filter(((carNum2) => !storageCarNumList.has(carNum2)));
          nonExistingCarNumList.length > 0 && clog.log(`<span style='color: #f40'>检测出新作品, ${name2}, 共${nonExistingCarNumList.length}部</span>`);
          await storageManager.updateFavoriteActress({
            starId,
            lastCheckTime: utils.getNowStr(),
            newVideoList: nonExistingCarNumList,
            lastPublishTime
          });
        }
        async checkOneNewVideo(actress) {
          const filterKeywordList = await storageManager.getTitleFilterKeyword(), filterActorActressCarList = await storageManager.getBlacklistCarList(), filterActorActressCarNumList = new Set(filterActorActressCarList.map(((car) => car.carNum))), { lastCheckTime, name: name2, starId } = actress;
          let url = `${this.javDbUrl}/actors/${starId}?t=d`;
          const $checkNewVideoMsg = $("#checkNewVideoMsg");
          try {
            clog.log("正在检测最新作品, 演员:", name2, url);
            $checkNewVideoMsg.text(`正在检测最新作品, 演员: ${name2}`);
            const html2 = await gmHttp.get(url), $dom = utils.htmlTo$dom(html2);
            await this.parsePage($dom, starId, name2, filterKeywordList, filterActorActressCarNumList);
            clog.log('<span style="color: #f40">检测最新作品---结束</span>');
            $checkNewVideoMsg.text("检测完毕");
            this.getBean("NewVideoPlugin").loadData();
          } catch (e) {
            clog.error("检测屏蔽演员信息, 发生错误:", url, e);
            show.error("检测屏蔽演员信息, 发生错误:" + e, "bottom", "right");
            $checkNewVideoMsg.text(`检测屏蔽演员信息, 发生错误: ${url}`);
          }
        }
      }
      const CDN_SOURCES = [{
        name: "jsDelivr (全球CDN)",
        json: "https://cdn.jsdelivr.net/gh/gfriends/gfriends/Filetree.json",
        base: "https://cdn.jsdelivr.net/gh/gfriends/gfriends/Content/"
      }, {
        name: "GitHub Raw (备用)",
        json: "https://raw.githubusercontent.com/gfriends/gfriends/master/Filetree.json",
        base: "https://raw.githubusercontent.com/gfriends/gfriends/master/Content/"
      }];
      let currentCdnIndex$1 = parseInt(localStorage.getItem("jhs_img_cdn_index") || "0", 10);
      (currentCdnIndex$1 >= CDN_SOURCES.length || currentCdnIndex$1 < 0) && (currentCdnIndex$1 = 0);
      window.G_FRIENDS_JSON_URL = CDN_SOURCES[currentCdnIndex$1].json;
      window.window.CDN_BASE_URL = CDN_SOURCES[currentCdnIndex$1].base;
      const STORE_NAME = "filetreeStore", dbHelper = {
        db: null,
        async open() {
          return this.db ? this.db : new Promise(((resolve, reject) => {
            const request = indexedDB.open("GfriendsAvatarDB", 1);
            request.onupgradeneeded = (event) => {
              this.db = event.target.result;
              this.db.objectStoreNames.contains(STORE_NAME) || this.db.createObjectStore(STORE_NAME);
            };
            request.onsuccess = (event) => {
              this.db = event.target.result;
              resolve(this.db);
            };
            request.onerror = (event) => {
              console.error("IndexedDB open error:", event.target.errorCode);
              reject(new Error("Failed to open IndexedDB"));
            };
          }));
        },
        async get(key) {
          await this.open();
          return new Promise(((resolve) => {
            const request = this.db.transaction([STORE_NAME], "readonly").objectStore(STORE_NAME).get(key);
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => resolve(null);
          }));
        },
        async set(key, value) {
          await this.open();
          return new Promise(((resolve, reject) => {
            const request = this.db.transaction([STORE_NAME], "readwrite").objectStore(STORE_NAME).put(value, key);
            request.onsuccess = () => resolve();
            request.onerror = (event) => {
              console.error("IndexedDB set error:", event.target.errorCode);
              reject(new Error("Failed to write to IndexedDB"));
            };
          }));
        }
      };
      window.window.G_FRIENDS_DATA_CACHE = null;
      window.window.G_FRIENDS_AVATAR_MAP = null;
      class NewVideoPlugin extends BasePlugin {
        constructor() {
          super(...arguments);
          __publicField2(this, "currentPage", 1);
          __publicField2(this, "pageSize", 30);
        }
        getName() {
          return "NewVideoPlugin";
        }
        async initCss() {
          return "\n            <style>\n                #actress-card-container {\n                    display: grid;\n                    grid-template-columns: repeat(auto-fill, minmax(243px, 1fr)); /* 响应式3-5列 */\n                    gap: 20px;\n                    padding-bottom: 20px;\n                    padding-right: 10px;\n                    background: #f9f9f9;\n                    border-radius: 5px;\n                    overflow-y: auto;\n                }\n                .actress-card {\n                    background: #fff;\n                    border: 1px solid #e0e0e0;\n                    border-radius: 8px;\n                    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08);\n                    padding: 15px;\n                    text-align: center;\n                    display: flex;\n                    flex-direction: column;\n                    justify-content: space-between;\n                    position: relative;\n                    overflow: hidden;\n                }\n                .actress-card:hover {\n                    box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15);\n                }\n                .actress-card-name {\n                    font-size: 1.2em;\n                    font-weight: bold;\n                    color: #007bff;\n                    margin-top: 10px;\n                }\n                .actress-card-allname {\n                    font-size: 0.9em;\n                    color: #999;\n                    margin-top: 5px;\n                    height: 30px; /* 保证高度一致性 */\n                    overflow: hidden;\n                    white-space: nowrap;      /* 防止文字换行 */\n                    text-overflow: ellipsis;  /* 当文本溢出时,显示省略号 */\n                }\n                .actress-card-avatar {\n                    width: 100px;\n                    height: 100px;\n                    border-radius: 50%;\n                    object-fit: contain;\n                    margin: 0 auto;\n                    border: 4px solid #f0f0f0;\n                    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n                }\n                \n                .card-tag {\n                    position: absolute;\n                    top: 15px; /* 调整标签距离顶部的距离 */\n                    right: -50px; /* 调整标签距离右侧的距离,负值让它移到外面一点 */\n                    \n                    width: 150px; /* 标签的宽度,影响斜角长度 */\n                    padding: 5px 0; /* 上下内边距 */\n                    text-align: center;\n                    \n                    background-color: #ff4757; /* 标签颜色 */\n                    color: white; /* 文字颜色 */\n                    font-size: 14px;\n                    font-weight: bold;\n                    z-index: 10; /* 确保标签在其他内容之上 */\n                \n                    /* 3. 核心:旋转标签,使其倾斜 */\n                    transform: rotate(45deg); /* 45度斜角 */\n                    \n                    /* 可选:添加一些阴影或边框效果 */\n                    box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n                }\n                \n                .card-new-count-tag {\n                    position: absolute;\n                    top: 5px;\n                    text-align: center;\n                    font-size: 14px;\n                    font-weight: bold;\n                    z-index: 10;\n                }\n                \n                #actress-pagination {\n                    padding-top: 10px;\n                    text-align: center;\n                    border-top: 1px solid #ddd;\n                }\n                @media (max-width: 600px) {\n                    .page-number-btn {\n                        display: none !important;\n                    }\n                }\n                \n                \n                .card-btn {\n                    width: 44px;\n                    height: 44px;\n                    border-radius: 50%;\n                    display: flex;\n                    justify-content: center;\n                    align-items: center;\n                    text-decoration: none;\n                    border: none;\n                    cursor: pointer;\n                    background: linear-gradient(145deg, #e0e0e0 0%, #f7f7f7 100%);\n                    box-shadow: 8px 8px 16px rgba(0, 0, 0, 0.08),\n                                -8px -8px 16px rgba(255, 255, 255, 1.0);\n                    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n                }\n                \n                .card-btn svg,\n                .card-btn svg path {\n                    transition: fill 0.3s ease;\n                }\n                \n                .card-btn:hover {\n                    box-shadow: inset 5px 5px 10px rgba(0, 0, 0, 0.1),\n                                inset -5px -5px 10px rgba(255, 255, 255, 0.9);\n                    transform: scale(0.97);\n                    background: #e0e0e0;\n                }\n                \n                .btn-check-actress svg path {\n                    fill: #4CAF50;\n                }\n                .btn-check-actress:hover svg path {\n                    fill: #388E3C;\n                }\n                \n                .btn-edit-actress svg path {\n                    fill: #FFC107;\n                }\n                .btn-edit-actress:hover svg path {\n                    fill: #FFB300;\n                }\n                \n                .btn-delete-actress svg path {\n                    fill: #F44336;\n                }\n                .btn-delete-actress:hover svg path {\n                    fill: #D32F2F;\n                }\n            </style>\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 = `
            <div class="newVideoToolBox" style="display: flex; flex-direction: column; height: 100%; overflow: hidden; padding:10px">
                <div style="margin-bottom: 15px;display: flex; justify-content: space-between;">
                    <div>
                        <a class="a-danger" id="checkFavoriteActress" data-tip="上次自动同步时间: ${lastCheckTimeStr}; 检测间隔时间: ${checkFavoriteActress_IntervalTime}小时">${this.actressSvg} &nbsp;&nbsp; 手动同步演员</a>
                        <a class="a-warning" id="checkNewVideo" data-tip="上次检测时间: ${lastCheckNewVideoTimeStr}; 检测间隔时间: ${checkNewVideo_intervalTime}小时">${this.newSvg} &nbsp;&nbsp; 手动检测最新作品</a>
                        <a class="a-info" id="toSetting">${this.settingSvg} &nbsp;&nbsp; 配置</a>
                        <span id="checkNewVideoMsg"></span>
                    </div>
                    <div style="display: flex; align-items: flex-start;">
                        <select id="paramActressType" style="text-align: center; height: 100%; min-width: 150px; border: 1px solid #ddd; margin-right: 10px">
                            <option value="all" selected>所有</option>
                            <option value="uncensored">无码</option>
                            <option value="censored">有码</option>
                            <option value="">未知</option>
                        </select>
                        
                        <a class="a-normal" id="reLoad">${this.refreshSvg} &nbsp;&nbsp; 刷新</a>
                    </div>

                </div>
                <div id="actress-card-container" class="jhs-scrollbar"></div>
                <div id="actress-pagination"></div>
            </div>
        `;
          layer.open({
            type: 1,
            title: '<span style="padding: 0 10px;" data-tip="数据来源: 女优页面首页,含磁链分类">新作品检测 ❓</span>',
            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 `
                <div class="actress-card" data-starId="${data.starId}" style="${isUnCheck ? "background: #d4cece;" : ""} min-height: 370px;">
                    <a href="${detailLink}" target="_blank" style="text-decoration: none; color: inherit; display: block; flex-grow: 1;">
                        <img src="${data.avatar || "https://c0.jdbstatic.com/images/actor_unknow.jpg"}" alt="${allNamesStr}" class="actress-card-avatar">
                    </a>

                    <div>
                        <a href="${detailLink}" target="_blank" style="text-decoration: none; color: inherit; display: block; flex-grow: 1;">
                            <div class="actress-card-name">${data.name}</div>
                        </a>
                        <div class="actress-card-allname" title="${allNamesStr}">${allNamesStr}</div>
                    </div>
                    
                    <div style="font-size: 0.8em; margin-top: 5px;">
                         <span>上次检测: ${data.lastCheckTime || ""}</span>
                    </div>
                    <div style="font-size: 0.8em; margin-top: 5px;">
                         <span>最后发行作品: ${data.lastPublishTime || ""}</span>
                    </div>

                    <div style="font-size: 0.7em; color: #cc4444; margin-top: 5px; min-height: 18px">
                         <span>${isUnCheck ? "停更" + checkNewVideo_ruleTime / 24 / 365 + "年以上, 下轮任务不再进行检测" : ""}</span>
                    </div>
                    
                    <div style="font-size: 0.8em; margin-top: 5px; color: #3765c5; min-height: 10px">
                         <span>${data.remark || ""}</span>
                    </div>
                    
                    <div style="margin-top: 10px;display: flex; justify-content:center; gap: 10px;">
                        <a title="编辑" class="card-btn btn-edit-actress" style="${cardUnnecessaryBtnCss}" data-starId="${data.starId}">${this.editSvg}</a>
                        <a title="取消收藏" class="card-btn btn-delete-actress" style="${cardUnnecessaryBtnCss}" data-starId="${data.starId}">${this.deleteSvg}</a>
                        <a title="重新检测该演员" class="card-btn btn-check-actress" style="${cardUnnecessaryBtnCss}" data-starId="${data.starId}">${this.checkSvg}</a>
                    </div>
                    
                    <div class="card-tag" style="background-color:${actressTypeBgc}">${actressTypeText}</div>
                    <div class="card-new-count-tag" data-tip="最新作品数量: ${(null == (_a2 = data.newVideoList) ? void 0 : _a2.length) || 0}"
                        style="${(null == (_b = data.newVideoList) ? void 0 : _b.length) > 0 ? "color: #4CAF50;" : ""}"> 
                        🔔 ${(null == (_c = data.newVideoList) ? void 0 : _c.length) || 0} 
                    </div>
                </div>
            `;
          })).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 = `
            <div style="padding: 20px;">
                <div style="margin-bottom: 15px; text-align: center;">
                    <img id="edit-avatar-preview" src="${initialAvatar}" alt="Avatar Preview" 
                         style="width: 100px; height: 100px; border-radius: 50%; object-fit: cover; margin-bottom: 10px; border: 2px solid #ddd;">
                    <div style="text-align: left">
                        <label style="display: block; margin-bottom: 5px; font-weight: bold;">头像链接:</label>
                        <input type="text" id="edit-actress-avatar" value="${initialAvatar}" 
                               style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;">
                       <div style="display: flex; gap: 5px; margin-top: 5px;">
                            <button type="button" id="search-avatar-btn" 
                                style="flex-grow: 1; padding: 8px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;">
                                搜索头像
                            </button>
                            <button type="button" id="select-cdn-btn" 
                                style="width: 100px; padding: 8px; background-color: #6c757d; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">
                                选择 CDN 源
                            </button>
                        </div>
                    </div>
                </div>
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">主名称:</label>
                    <input type="text" id="edit-actress-name" value="${initialName}" 
                           style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;">
                </div>
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">所有别名(用逗号隔开):</label>
                    <textarea id="edit-actress-allname" style="${textareaStyle}">${initialAllName}</textarea>
                </div>
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">演员类别:</label>
                    <select id="actressType" style="width: 100%; padding: 10px; border: 1px solid #ddd;">
                        <option value="" ${"" === initActressType ? "selected" : ""}>未知</option>
                        <option value="censored" ${"censored" === initActressType ? "selected" : ""}>有码</option>
                        <option value="uncensored" ${"uncensored" === initActressType ? "selected" : ""}>无码</option>
                    </select>
                </div>
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">最新作品(用逗号隔开):</label>
                    <textarea id="edit-actress-newvideolist" style="${textareaStyle}">${initialNewVideoList}</textarea>
                </div>
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">备注:</label>
                   <textarea id="edit-remark" style="${textareaStyle}">${initialRemark}</textarea>
                </div>
            </div>
        `;
          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) => `
        <div style="margin-bottom: 10px;">
            <input type="radio" id="cdn-${index2}" name="cdn-source" value="${index2}" ${index2 === initialIndex ? "checked" : ""} style="margin-right: 10px;">
            <label for="cdn-${index2}">${source.name} ${source.json.includes("jsdelivr") ? "(推荐)" : ""}</label>
        </div>
    `)).join(""), cdnSelectHtml = `
        <div style="padding: 20px;">
            <p style="margin-bottom: 15px; font-weight: bold; color: #333;">请选择头像数据源 (当前: ${CDN_SOURCES[initialIndex].name}):</p>
            ${radioOptions}
            <p style="margin-top: 20px; color: #555; font-size: 12px;">切换源会清除本地缓存的数据,并在下次搜索时重新加载。</p>
        </div>
    `;
                  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 = '<span style="color: #666;">共 0 条记录</span>';
            $actressPagination.html(paginationHtml);
            return;
          }
          currentPage > 1 && totalPages > 5 && (paginationHtml += '<button class="pagination-btn" data-page="1" style="padding: 8px 12px; margin: 0 5px; background: #f0f0f0; border: 1px solid #ddd; border-radius: 4px; cursor: pointer;">首页</button>');
          currentPage > 1 && (paginationHtml += `<button class="pagination-btn" data-page="${currentPage - 1}" style="padding: 8px 12px; margin: 0 5px; background: #f0f0f0; border: 1px solid #ddd; border-radius: 4px; cursor: pointer;">上一页</button>`);
          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 += `<button class="pagination-btn page-number-btn ${i === currentPage ? "active" : ""}" data-page="${i}" style="padding: 8px 12px; margin: 0 3px; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; ${i === currentPage ? "background: #007bff; color: white; border-color: #007bff;" : ""}">${i}</button>`;
          }
          currentPage < totalPages && (paginationHtml += `<button class="pagination-btn" data-page="${currentPage + 1}" style="padding: 8px 12px; margin: 0 5px; background: #f0f0f0; border: 1px solid #ddd; border-radius: 4px; cursor: pointer;">下一页</button>`);
          currentPage < totalPages && totalPages > 5 && (paginationHtml += `<button class="pagination-btn" data-page="${totalPages}" style="padding: 8px 12px; margin: 0 5px; background: #f0f0f0; border: 1px solid #ddd; border-radius: 4px; cursor: pointer;">尾页</button>`);
          paginationHtml += `<span style="margin-left: 20px; color: #666;">共 ${totalCount} 条记录 (第 ${currentPage}/${totalPages} 页)</span>`;
          $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) => `
        <div id="wrapper-${index}" class="gfriends-image-item-wrapper">
            <img alt="" src="${url}" data-url="${url}" class="gfriends-selectable-img" data-wrapper-id="wrapper-${index}" >
            <div class="gfriends-size-tag" data-size-for="wrapper-${index}">...</div> 
        </div>
    `)).join(""), imageListHtml = `
        <style>
            /* 保持上一个回答的美化样式 */
            #gfriends-image-list-container { padding: 15px; height: 100%; box-sizing: border-box; background-color: #f8f9fa; }
            #gfriends-prompt { color: #555; font-weight: 500; border-bottom: 1px solid #eee; padding-bottom: 10px; }
            #gfriends-image-list { display: flex; flex-wrap: wrap; gap: 15px; justify-content: center; }
            .gfriends-image-item-wrapper {
                width: 160px; height: 225px; /* 增加高度以容纳尺寸标签 */
                overflow: hidden; border-radius: 6px;
                box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); transition: transform 0.2s ease, box-shadow 0.2s ease;
                cursor: pointer; position: relative; 
                padding-bottom: 25px; /* 为尺寸标签留出空间 */
            }
            .gfriends-selectable-img {
                width: 100%; height: 200px; /* 固定图片高度 */
                object-fit: cover; border: 3px solid transparent; 
                border-radius: 6px; transition: border 0.2s ease;
            }
            .gfriends-image-item-wrapper:hover {
                transform: translateY(-4px) scale(1.02);
                box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
            }
            .gfriends-selectable-img.is-selected {
                border-color: #ff6347;
                box-shadow: 0 0 0 3px #ff6347;
            }
            /* 新增:尺寸标签样式 */
            .gfriends-size-tag {
                position: absolute;
                bottom: 0; /* 定位到图片容器底部 */
                left: 0;
                right: 0;
                height: 25px;
                line-height: 25px;
                text-align: center;
                background-color: rgba(0, 0, 0, 0.7); /* 半透明背景 */
                color: #fff;
                font-size: 11px;
                font-weight: bold;
                border-bottom-left-radius: 6px;
                border-bottom-right-radius: 6px;
                user-select: none;
            }
        </style>
        
        <div id="gfriends-image-list-container">
            <p id="gfriends-prompt" style="text-align: center; font-size: 15px; margin-bottom: 15px;">
                点击图片即可选择(初始共 ${imageLinks.length} 张)
            </p>
            <div style="overflow-y: auto; height: calc(100% - 40px);">
                <div id="gfriends-image-list">
                    ${imageItems}
                </div>
            </div>
        </div>
    `;
          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            <style>\n                /* 下拉菜单容器(相对定位) */\n                .sub-btns {\n                    position: relative;\n                    display: inline-block;\n                }\n                \n                /* 下拉菜单内容(默认隐藏) */\n                .sub-btns-menu {\n                    display: none;\n                    position: absolute;\n                    right: 80px;\n                    top:-10px;\n                    background: white;\n                    padding:10px;\n                    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n                    z-index: 100;\n                    border-radius: 4px;\n                    overflow: hidden;\n                }\n                \n                \n                /* 点击后显示菜单(JS 控制) */\n                .sub-btns-menu.show {\n                    display: flex !important;\n                    flex-direction: column;\n                }\n                \n                .table-link-param {\n                    cursor: pointer;\n                }\n            </style\n        ";
        }
        handleResize() {
          if ($(".navbar-search").is(":hidden")) {
            $(".historyBtnBox").show();
            $(".miniHistoryBtnBox").hide();
          } else {
            $(".historyBtnBox").hide();
            $(".miniHistoryBtnBox").show();
          }
        }
        handle() {
          if (isJavDb$1) {
            $(".navbar-end").prepend('<div class="navbar-item has-sub-btns is-hoverable historyBtnBox">\n                    <a id="historyBtn" class="navbar-link nav-btn" style="color: #aade66 !important;padding-right:15px !important;">\n                        鉴定记录\n                    </a>\n                </div>');
            $(".navbar-search").css("margin-left", "0").before('\n                <div class="navbar-item miniHistoryBtnBox">\n                    <a id="miniHistoryBtn" class="navbar-link nav-btn" style="color: #aade66 !important;padding-left:0 !important;padding-right:0 !important;">\n                        鉴定记录\n                    </a>\n                </div>\n            ');
            this.handleResize();
            $(window).resize((() => {
              this.handleResize();
            }));
            $("#historyBtn,#miniHistoryBtn").on("click", ((event) => this.openHistory()));
          }
          isJavBus$1 && utils.loopDetector((() => $("#setting-btn").length), (() => {
            $("#top-right-box").append('\n                    <a id="historyBtn" class="menu-btn main-tab-btn" style="background-color:#b68625 !important;">\n                        鉴定记录\n                    </a>\n               ');
            $("#historyBtn,#miniHistoryBtn").on("click", ((event) => this.openHistory()));
          }), 1, 1e4, false);
          this.bindClick();
        }
        openHistory() {
          layer.open({
            type: 1,
            title: "鉴定记录",
            content: '\n            <div style="padding: 10px 20px; height: 100%;overflow:hidden;"> \n                 <div id="filterBox" style="display: flex;gap: 5px;">\n                    <select id="dataType" style="text-align: center;min-width: 150px;">\n                        <option value="all" selected>所有</option>\n                        <option value="filter">🚫 已屏蔽</option>\n                        <option value="favorite">⭐ 已收藏</option>\n                        <option value="hasDown">📥️ 已下载</option>\n                        <option value="hasWatch">🔍 已观看</option>\n                    </select>\n                    <input id="searchCarNum" type="text" placeholder="搜索番号|演员" style="padding: 4px 5px;">\n                    <a id="clearSearchbtn" class="a-info" style="margin-left: 0">重置</a>\n                </div>\n                <div id="allSelectBox" style="margin-top: 8px;display: none">\n                    <a class="menu-btn multiple-history-deleteBtn" style="background-color:#8c8080; color:white; margin-bottom: 5px;"> <span>✂️ 移除</span> </a>\n                    <a class="menu-btn multiple-history-hasWatchBtn" style="background-color:#d7a80c;margin-bottom: 5px">🔍 已观看</a>\n                    <a class="menu-btn multiple-history-hasDownBtn" style="background-color:#7bc73b;margin-bottom: 5px">📥️ 已下载</a>\n                    <a class="menu-btn multiple-history-favoriteBtn" style="background-color:#25b1dc;margin-bottom: 5px">⭐ 收藏</a>\n                    <a class="menu-btn multiple-history-filterBtn" style="background-color:#de3333;margin-bottom: 5px">🚫 屏蔽</a>\n                </div>\n                <div id="table-container" style="margin-top:20px !important; height: calc(100% - 50px); overflow-x:hidden;"></div>\n            </div>\n        ',
            scrollbar: false,
            shadeClose: true,
            area: utils.getResponsiveArea(["70%", "90%"]),
            anim: -1,
            success: async (layero) => {
              await this.loadTableData();
              $(".layui-layer-content").on("click", "#clearSearchbtn", (async (event) => {
                $("#searchCarNum").val("");
                $("#dataType").val("all");
                await this.reloadTable();
                $("#allSelectBox").hide();
              })).on("focusout keydown", "#searchCarNum", (async (event) => {
                if ("focusout" === event.type || "Enter" === event.key) {
                  "Enter" === event.key && event.preventDefault();
                  if ("keydown" === event.type && "Enter" !== event.key) return;
                  await this.reloadTable();
                }
              })).on("click", ".table-link-param", (async (event) => {
                let $targetEl = $(event.currentTarget);
                $("#searchCarNum").val($targetEl.text());
                await this.reloadTable();
              })).on("change", "#dataType", (async () => {
                await this.reloadTable();
              }));
            },
            end: () => {
              if (this.tableObj) {
                this.tableObj.destroy();
                this.tableObj = null;
              }
              window.refresh();
            }
          });
        }
        async reloadTable() {
          this.tableObj.deselectRow();
          this.tableObj.setPage(1);
        }
        bindClick() {
          document.addEventListener("click", (function(e) {
            if (e.target.closest(".sub-btns-toggle")) {
              const menu = e.target.closest(".sub-btns").querySelector(".sub-btns-menu");
              document.querySelectorAll(".sub-btns-menu.show").forEach(((openMenu) => {
                openMenu !== menu && openMenu.classList.remove("show");
              }));
              menu.classList.toggle("show");
            } else document.querySelectorAll(".sub-btns-menu.show").forEach(((menu) => {
              menu.classList.remove("show");
            }));
          }));
          $(document).on("click", ".history-deleteBtn, .history-filterBtn, .history-favoriteBtn, .history-hasDownBtn, .history-hasWatchBtn, .history-detailBtn", ((event) => {
            event.preventDefault();
            event.stopPropagation();
            const $btn = $(event.currentTarget), $menuBox = $btn.closest(".action-btns"), carNum2 = $menuBox.attr("data-car-num"), aHref = $menuBox.attr("data-href"), handleAction = async (status) => {
              await storageManager.saveCar({
                carNum: carNum2,
                url: aHref,
                names: null,
                actionType: status
              });
              window.refresh();
              await this.reloadTable();
            };
            $btn.hasClass("history-filterBtn") ? utils.q(event, `是否屏蔽${carNum2}?`, (() => handleAction(Status_FILTER$1))) : $btn.hasClass("history-favoriteBtn") ? handleAction(Status_FAVORITE$1).then() : $btn.hasClass("history-hasDownBtn") ? handleAction(Status_HAS_DOWN$1).then() : $btn.hasClass("history-hasWatchBtn") ? handleAction(Status_HAS_WATCH$1).then() : $btn.hasClass("history-deleteBtn") ? this.handleDelete(event, carNum2) : $btn.hasClass("history-detailBtn") && this.handleClickDetail(event, {
              carNum: carNum2,
              url: aHref
            }).then();
          }));
          $(document).on("click", ".multiple-history-deleteBtn, .multiple-history-filterBtn, .multiple-history-favoriteBtn, .multiple-history-hasDownBtn, .multiple-history-hasWatchBtn", ((event) => {
            event.preventDefault();
            event.stopPropagation();
            const $btn = $(event.currentTarget);
            let selectedRows = this.tableObj.getSelectedData(), handleText = "", handleStatus = "";
            if ($btn.hasClass("multiple-history-filterBtn")) {
              handleText = "屏蔽";
              handleStatus = Status_FILTER$1;
            } else if ($btn.hasClass("multiple-history-favoriteBtn")) {
              handleText = "收藏";
              handleStatus = Status_FAVORITE$1;
            } else if ($btn.hasClass("multiple-history-hasDownBtn")) {
              handleText = "已下载";
              handleStatus = Status_HAS_DOWN$1;
            } else if ($btn.hasClass("multiple-history-hasWatchBtn")) {
              handleText = "已观看";
              handleStatus = Status_HAS_WATCH$1;
            } else if ($btn.hasClass("multiple-history-deleteBtn")) {
              handleText = "移除";
              handleStatus = "delete";
            }
            utils.q(event, `当前已勾选${selectedRows.length}条数据, 是否全标记为 ${handleText}?`, (async () => {
              let loadObj = loading();
              try {
                if ("delete" === handleStatus) {
                  const deleteCarNumList = selectedRows.map(((row) => row.carNum)), removedCount = await storageManager.batchRemoveCars(deleteCarNumList);
                  removedCount > 0 ? show.ok(`已成功删除 ${removedCount} 个番号`) : false === removedCount && show.error("提供的番号中没有一个存在于列表中。");
                } else {
                  const carRecords = JSON.parse(JSON.stringify(selectedRows));
                  carRecords.forEach(((record) => {
                    record.actionType = handleStatus;
                  }));
                  await storageManager.saveCarList(carRecords);
                  show.ok("操作成功");
                }
                this.tableObj.deselectRow();
                this.reloadTable().then();
              } catch (e) {
                console.error(e);
              } finally {
                loadObj.close();
              }
            }));
          }));
        }
        async getDataList(page, size, sort) {
          let dataList = await storageManager.getCarList();
          this.allCount = dataList.length;
          this.filterCount = 0;
          this.favoriteCount = 0;
          this.hasDownCount = 0;
          this.hasWatchCount = 0;
          dataList.forEach(((item) => {
            switch (item.status) {
              case Status_FILTER$1:
                this.filterCount++;
                break;
              case Status_FAVORITE$1:
                this.favoriteCount++;
                break;
              case Status_HAS_DOWN$1:
                this.hasDownCount++;
                break;
              case Status_HAS_WATCH$1:
                this.hasWatchCount++;
            }
          }));
          $('#dataType option[value="all"]').text(`所有 (${this.allCount})`);
          $('#dataType option[value="filter"]').text(`🚫 已屏蔽 (${this.filterCount})`);
          $('#dataType option[value="favorite"]').text(`⭐ 已收藏 (${this.favoriteCount})`);
          $('#dataType option[value="hasDown"]').text(`📥️ 已下载 (${this.hasDownCount})`);
          $('#dataType option[value="hasWatch"]').text(`🔍 已观看 (${this.hasWatchCount})`);
          const dataType = $("#dataType").val();
          let filterDataList = "all" === dataType ? dataList : dataList.filter(((item) => item.status === dataType));
          const searchCarNum = $("#searchCarNum").val().trim();
          if (searchCarNum) {
            let tempCarNum = searchCarNum.toLowerCase().replace("-c", "").replace("-uc", "").replace("-4k", "");
            filterDataList = filterDataList.filter(((item) => {
              const result1 = item.carNum.toLowerCase().includes(tempCarNum);
              const result2 = (item.names ? item.names : "").toLowerCase().includes(tempCarNum);
              return result1 || result2;
            }));
          }
          if (sort && sort.length > 0) {
            const sorter = sort[0], field = sorter.field, dir = sorter.dir;
            filterDataList.sort(((a, b) => {
              const valA = a[field], valB = b[field], isValANullish = null == valA || "" === valA, isValBNullish = null == valB || "" === valB;
              return isValANullish && !isValBNullish ? 1 : !isValANullish && isValBNullish ? -1 : isValANullish && isValBNullish ? 0 : valA < valB ? "asc" === dir ? -1 : 1 : valA > valB ? "asc" === dir ? 1 : -1 : 0;
            }));
          }
          const totalCount = filterDataList.length, maxPage = Math.ceil(totalCount / size), startIndex = (page - 1) * size, endIndex = startIndex + size;
          filterDataList = filterDataList.slice(startIndex, endIndex);
          return {
            maxPage,
            dataList: filterDataList,
            totalCount
          };
        }
        async loadTableData() {
          this.tableObj = new Tabulator("#table-container", {
            layout: "fitColumns",
            placeholder: "暂无数据",
            virtualDom: true,
            pagination: true,
            paginationMode: "remote",
            sortMode: "remote",
            ajaxURL: "queryRealm",
            dataLoader: false,
            ajaxRequestFunc: async (url, ajaxConfig, params) => {
              const page = (null == params ? void 0 : params.page) || 1, size = (null == params ? void 0 : params.size) || 50, sort = (null == params ? void 0 : params.sort) || [];
              return await this.getDataList(page, size, sort);
            },
            dataReceiveParams: {
              last_page: "maxPage",
              last_row: "totalCount",
              data: "dataList"
            },
            paginationSize: 50,
            paginationSizeSelector: [50, 100, 1e3, 99999],
            paginationCounter: (pageSize, currentRow, currentPage, totalRows, totalPages) => `共 ${totalRows} 条记录`,
            responsiveLayout: "collapse",
            responsiveLayoutCollapse: true,
            columnDefaults: {
              headerHozAlign: "center",
              hozAlign: "center"
            },
            selectableRowsPersistence: false,
            index: "carNum",
            columns: [{
              formatter: "rowSelection",
              titleFormatter: "rowSelection",
              hozAlign: "center",
              headerSort: false,
              responsive: 0,
              width: 40,
              titleFormatterParams: {
                rowRange: "active"
              },
              cellClick: (e, cell) => {
                cell.getRow().toggleSelect();
              }
            }, {
              title: "番号",
              field: "carNum",
              width: 120,
              sorter: "string",
              responsive: 0,
              formatter: (cell, formatterParams, onRendered) => {
                const carNumString = cell.getData().carNum, hyphenIndex = carNumString.indexOf("-");
                if (-1 === hyphenIndex) return carNumString;
                return `<a class="table-link-param">${carNumString.substring(0, hyphenIndex + 1)}</a>${carNumString.substring(hyphenIndex + 1)}`;
              }
            }, {
              title: "演员",
              field: "names",
              minWidth: 200,
              sorter: "string",
              responsive: 5,
              headerSort: true,
              formatter: (cell, formatterParams, onRendered) => (cell.getData().names || "").split(" ").filter(((name2) => "" !== name2.trim())).map(((name2) => `<a class="table-link-param">${name2}</a>`)).join(" ")
            }, {
              title: "创建时间",
              field: "createDate",
              width: 170,
              sorter: "string",
              responsive: 4
            }, {
              title: "修改时间",
              field: "updateDate",
              width: 170,
              sorter: "string",
              responsive: 4
            }, {
              title: "发行时间",
              field: "publishTime",
              width: 170,
              sorter: "string",
              responsive: 4
            }, {
              title: "来源",
              field: "url",
              width: 80,
              sorter: "string",
              responsive: 5,
              hozAlign: "left",
              formatter: (cell, formatterParams, onRendered) => {
                let url = cell.getData().url;
                return url ? url.includes("javdb") ? '<span style="color:#d34f9e">Javdb</span>' : url.includes("javbus") ? '<span style="color:#eaa813">JavBus</span>' : url.includes("123av") ? '<span style="color:#eaa813">123Av</span>' : `<span style="color:#050505">${url}</span>` : "";
              }
            }, {
              title: "状态",
              field: "status",
              width: 100,
              sorter: "string",
              responsive: 1,
              headerSort: false,
              formatter: (cell, formatterParams, onRendered) => {
                const statusValue = cell.getData().status;
                let color2 = "", text = "";
                switch (statusValue) {
                  case "filter":
                    color2 = "#de3333";
                    text = "🚫 屏蔽";
                    break;
                  case "favorite":
                    color2 = "#25b1dc";
                    text = "⭐ 收藏";
                    break;
                  case "hasDown":
                    color2 = "#7bc73b";
                    text = "📥️ 已下载";
                    break;
                  case "hasWatch":
                    color2 = "#d7a80c";
                    text = "🔍 已观看";
                    break;
                  default:
                    text = statusValue;
                }
                return `<span style="color:${color2}">${text}</span>`;
              }
            }, {
              title: "备注",
              field: "remark",
              width: 100,
              sorter: "string",
              responsive: 6
            }, {
              title: "操作",
              sorter: "string",
              minWidth: 150,
              cssClass: "action-cell-dropdown",
              responsive: 0,
              headerSort: false,
              formatter: (cell, formatterParams, onRendered) => {
                const item = cell.getData();
                onRendered((() => {
                  var _a2;
                  null == (_a2 = cell.getElement().querySelector(".history-editBtn")) || _a2.addEventListener("click", ((e) => {
                    this.editRecord(item);
                  }));
                }));
                return `
                            <div class="action-btns" style="display: flex; gap: 5px;justify-content:center" data-car-num="${item.carNum}" data-href="${item.url ? item.url : ""}">
                                <div class="sub-btns">
                                    <a class="menu-btn sub-btns-toggle" style="background-color:#c59d36; color:white; margin-bottom: 5px;">
                                        <span>✏️ 变更</span>
                                    </a>
                                    <div class="sub-btns-menu">
                                        <a class="menu-btn history-editBtn" style="background-color:#007bff; color:white; margin-bottom: 5px;"> <span>✏️ 编辑</span> </a>
                                        <a class="menu-btn history-deleteBtn" style="background-color:#8c8080; color:white; margin-bottom: 5px;"> <span>✂️ 移除</span> </a>
                                        <a class="menu-btn history-hasWatchBtn" style="background-color:#d7a80c;margin-bottom: 5px">🔍 已观看</a>
                                        <a class="menu-btn history-hasDownBtn" style="background-color:#7bc73b;margin-bottom: 5px">📥️ 已下载</a>
                                        <a class="menu-btn history-favoriteBtn" style="background-color:#25b1dc;margin-bottom: 5px">⭐ 收藏</a>
                                        <a class="menu-btn history-filterBtn" style="background-color:#de3333;margin-bottom: 5px">🚫 屏蔽</a>
                                    </div>
                                </div>
                                
                                <a class="menu-btn history-detailBtn" style="background-color:#3397de; color:white; margin-bottom: 5px;"> <span>📄 详情页</span> </a>
                                
                            </div>
                        `;
              }
            }],
            initialSort: [{
              column: "updateDate",
              dir: "desc"
            }],
            locale: "zh-cn",
            langs: {
              "zh-cn": {
                pagination: {
                  first: "首页",
                  first_title: "首页",
                  last: "尾页",
                  last_title: "尾页",
                  prev: "上一页",
                  prev_title: "上一页",
                  next: "下一页",
                  next_title: "下一页",
                  all: "所有",
                  page_size: "每页行数"
                }
              }
            }
          });
          this.tableObj.on("rowSelectionChanged", ((data, rows2, selected, deselected) => {
            const $allSelectBox = $("#allSelectBox"), $filterBox = $("#filterBox");
            if (data && data.length > 0) {
              $filterBox.hide();
              $allSelectBox.show();
            } else {
              $filterBox.show();
              $allSelectBox.hide();
            }
          }));
          this.tableObj.on("rowDblClick", (function(e, row) {
            row.toggleSelect();
          }));
          this.tableObj.on("tableBuilt", (async () => {
          }));
        }
        handleDelete(event, carNum2) {
          utils.q(event, `是否移除${carNum2}?`, (async () => {
            await storageManager.removeCar(carNum2);
            this.reloadTable().then();
          }));
        }
        async handleClickDetail(event, data) {
          if (isJavDb$1) if (data.carNum.includes("FC2-")) {
            const movieId = this.parseMovieId(data.url);
            this.getBean("Fc2Plugin").openFc2Dialog(movieId, data.carNum, data.url);
          } else {
            if (!data.url) {
              window.open("/search?q=" + data.carNum, "_blank");
              return;
            }
            utils.openPage(data.url, data.carNum, false, event);
          }
          if (isJavBus$1) {
            let url = data.url;
            if (url.includes("javdb")) if (data.carNum.includes("FC2-")) {
              const movieId = this.parseMovieId(url);
              await this.getBean("Fc2Plugin").openFc2Page(movieId, data.carNum, url);
            } else window.open(url, "_blank");
            else utils.openPage(data.url, data.carNum, false, event);
          }
        }
        async editRecord(item) {
          const initialCarNum = item.carNum, initialNames = item.names || "", initialUrl = item.url || "", initialStatus = item.status, initialRemark = item.remark || "", textareaStyle = "width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; min-height: 60px; overflow-y: hidden;", inputStyle = "width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;", editFormHtml = `
            <div style="padding: 20px;">
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">番号:</label>
                    <input type="text" id="edit-carNum" value="${initialCarNum}" style="${inputStyle} background-color: #f0f0f0;" readonly>
                </div>
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">演员 (用空格隔开):</label>
                    <textarea id="edit-names" style="${textareaStyle}">${initialNames}</textarea>
                </div>
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">状态:</label>
                    <select id="edit-status" style="width: 100%; padding: 10px; border: 1px solid #ddd;">
                        <option value="" ${"" === initialStatus ? "selected" : ""}>-- 请选择 --</option>
                        ${[{
      value: Status_FILTER$1,
      text: "🚫 屏蔽"
    }, {
      value: Status_FAVORITE$1,
      text: "⭐ 收藏"
    }, {
      value: Status_HAS_DOWN$1,
      text: "📥️ 已下载"
    }, {
      value: Status_HAS_WATCH$1,
      text: "🔍 已观看"
    }].map(((option) => `
                            <option value="${option.value}" ${initialStatus === option.value ? "selected" : ""}>${option.text}</option>
                        `)).join("")}
                    </select>
                </div>
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">链接:</label>
                    <input type="text" id="edit-url" value="${initialUrl}" style="${inputStyle}">
                </div>
                
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px; font-weight: bold;">备注:</label>
                    <textarea id="edit-remark" style="${textareaStyle}">${initialRemark}</textarea>
                </div>
            </div>
        `;
          layer.open({
            type: 1,
            title: `编辑记录: ${initialCarNum}`,
            area: ["500px", "650px"],
            content: editFormHtml,
            btn: ["保存", "取消"],
            success: (layero, index) => {
              const autoResizeTextarea = ($textarea) => {
                $textarea.css("height", "auto");
                $textarea.css("height", $textarea[0].scrollHeight + 15 + "px");
              }, $namesTextarea = $("#edit-names");
              $namesTextarea.on("input", (function() {
                autoResizeTextarea($(this));
              }));
              autoResizeTextarea($namesTextarea);
              const $remarkTextarea = $("#edit-remark");
              $remarkTextarea.on("input", (function() {
                autoResizeTextarea($(this));
              }));
              autoResizeTextarea($remarkTextarea);
            },
            yes: async (index) => {
              const newNames = $("#edit-names").val().trim(), newStatus = $("#edit-status").val(), newUrl = $("#edit-url").val().trim(), newRemark = $("#edit-remark").val().trim(), updatedRecord = {
                ...item,
                names: newNames,
                actionType: newStatus,
                url: newUrl,
                remark: newRemark
              };
              await storageManager.updateCarInfo(updatedRecord);
              this.tableObj.setData();
              layer.close(index);
            }
          });
        }
      }
      const _unsafeWindow$1 = /* @__PURE__ */ (() => typeof unsafeWindow !== "undefined" ? unsafeWindow : window)();
      class DetailPagePlugin extends BasePlugin {
        getName() {
          return "DetailPagePlugin";
        }
        constructor() {
          super();
        }
        handle() {
          if (window.isDetailPage) {
            $(".video-meta-panel a").each((function() {
              const href = $(this).attr("href");
              href && (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("/")) && $(this).attr("target", "_blank");
            }));
            this.handleFancyBox();
          }
        }
        handleFancyBox() {
          document.addEventListener("click", (function(e) {
            if (e.target.closest(".fancybox-button--thumbs")) {
              const isVisible = !$(".fancybox-thumbs").is(":hidden");
              localStorage.setItem("jhs_fancyboxThumbs", isVisible.toString());
              _unsafeWindow$1.$.fancybox.defaults.thumbs.autoStart = isVisible;
            }
          }));
          if (void 0 !== _unsafeWindow$1.$.fancybox) {
            const savedState = localStorage.getItem("jhs_fancyboxThumbs");
            _unsafeWindow$1.$.fancybox.defaults.thumbs.autoStart = "true" === savedState;
          }
        }
      }
      class PreviewVideoPlugin extends BasePlugin {
        getName() {
          return "PreviewVideoPlugin";
        }
        async initCss() {
          return "\n            .video-control-btn {\n                min-width:120px;\n                padding: 7px 12px;\n                font-size: 12px;\n                background: rgba(0,0,0,0.7);\n                color: white;\n                border: none;\n                border-radius: 4px;\n                cursor: pointer;\n            }\n            .video-control-btn.active {\n                background-color: #1890ff;\n                color: white;\n                font-weight: bold;\n                border: 2px solid #096dd9;\n            }\n        ";
        }
        async handle() {
          if (!window.isDetailPage) return;
          let settingObj = await storageManager.getSetting();
          this.filterHotKey = settingObj.filterHotKey;
          this.favoriteHotKey = settingObj.favoriteHotKey;
          this.speedVideoHotKey = settingObj.speedVideoHotKey;
          let $preview = $(".preview-video-container");
          $preview.on("click", ((event) => {
            utils.loopDetector((() => $(".fancybox-content #preview-video").length > 0), (() => {
              this.handleVideo().then();
            }));
          }));
          await storageManager.getSetting("enableLoadPreviewVideo", YES$1) !== YES$1 || currentHref$1.includes("autoPlay=1") || this.initDmm().then();
          let href = window.location.href;
          (href.includes("gallery-1") || href.includes("gallery-2")) && utils.loopDetector((() => $(".fancybox-content #preview-video").length > 0), (() => {
            $(".fancybox-content #preview-video").length > 0 && this.handleVideo().then();
          }));
          href.includes("autoPlay=1") && $preview.length > 0 && $preview[0].click();
        }
        async initDmm() {
          try {
            const dmmVideoMap = await getDmmVideo(this.getPageInfo().carNum, false);
            if (!dmmVideoMap) return;
            let settingDefaultVideoQuality = await storageManager.getSetting("videoQuality");
            clog.debug("解析其它画质预览视频", "设置-期望画质", settingDefaultVideoQuality);
            const defaultVideoUrl = dmmVideoMap[selectDefaultQuality(Object.keys(dmmVideoMap), settingDefaultVideoQuality)];
            clog.log("切换其它画质预览视频: ", defaultVideoUrl);
            const $previewVideoEL = $("#preview-video"), videoEl = $previewVideoEL.length ? $previewVideoEL[0] : null, isVideoElementHidden = !videoEl || utils.isHidden($previewVideoEL);
            if ($previewVideoEL.length) {
              if (videoEl) {
                const currentTime = videoEl.currentTime;
                $previewVideoEL.attr("src", defaultVideoUrl);
                if (!isVideoElementHidden) {
                  clog.debug("播放器已手动打开, 变更进度条");
                  videoEl.currentTime = currentTime;
                  videoEl.play();
                }
              }
            } else {
              clog.debug("JavDB没有视频播放元素, 开始创建...");
              const videoCoverSrc = $(".column-video-cover img").attr("src");
              $(".preview-images").prepend(`
                    <a class="preview-video-container" data-fancybox="gallery" href="#preview-video">
                        <span>预告片</span>
                        <img src="${videoCoverSrc}" class="video-cover" style="width: 150px; height: auto;" alt="">
                    </a>
                `);
              $(".preview-video-container").on("click", ((event) => {
                utils.loopDetector((() => $(".fancybox-content #preview-video").length > 0), (async () => {
                  await this.handleVideo();
                }));
              }));
            }
          } catch (error) {
            clog.error("预加载dmm失败:", error);
          }
        }
        async handleVideo() {
          if (await storageManager.getSetting("enableLoadPreviewVideo", YES$1) === NO$1) return;
          const $videoEl = $("#preview-video");
          if (!$videoEl.length) return;
          const $videoContainer = $videoEl.parent();
          $videoContainer.css("position", "relative");
          const videoEl = $videoEl[0];
          let carNum2 = this.getPageInfo().carNum;
          const jhs_videoMuted = localStorage.getItem("jhs_videoMuted");
          if (jhs_videoMuted) videoEl.muted = "yes" === jhs_videoMuted;
          videoEl.addEventListener("volumechange", (function() {
            localStorage.setItem("jhs_videoMuted", videoEl.muted ? "yes" : "no");
          }));
          const CACHE_KEY2 = "jhs_dmm_video";
          let dmmErrorRetryCount = 0;
          const attachVideoErrorHandler = () => {
            videoEl.addEventListener("error", async function onVideoError(e) {
              if (dmmErrorRetryCount >= 2) return;
              dmmErrorRetryCount++;
              clog.warn(`预览视频加载失败(第${dmmErrorRetryCount}次),清除缓存后重试...`);
              const cachedData = localStorage.getItem(CACHE_KEY2) ? JSON.parse(localStorage.getItem(CACHE_KEY2)) : {};
              delete cachedData[carNum2];
              localStorage.setItem(CACHE_KEY2, JSON.stringify(cachedData));
              const freshMap = await getDmmVideo(carNum2);
              if (!freshMap) {
                show.error("预览视频获取失败,请稍后重试");
                return;
              }
              const storedQuality = await storageManager.getSetting("videoQuality");
              const quality = selectDefaultQuality(Object.keys(freshMap), storedQuality);
              const freshUrl = freshMap[quality];
              const savedTime = videoEl.currentTime;
              $videoEl.attr("src", freshUrl);
              videoEl.load();
              if (savedTime > 0 && Number.isFinite(savedTime)) {
                videoEl.currentTime = savedTime;
              }
              videoEl.play();
            }, { once: false });
          };
          attachVideoErrorHandler();
          videoEl.play();
          const dmmVideoMap = await getDmmVideo(carNum2);
          let $bottomToolbar = $("<div></div>").attr("id", "video-bottom-toolbar").css({
            display: "flex",
            gap: "5px",
            "align-items": "center",
            "flex-wrap": "wrap"
          }), $qualityButtonGroup = $("<div></div>").css({
            display: "flex",
            gap: "5px",
            "align-items": "center"
          }), defaultVideoQuality = null;
          if (dmmVideoMap) {
            let storedQuality = await storageManager.getSetting("videoQuality");
            defaultVideoQuality = selectDefaultQuality(Object.keys(dmmVideoMap), storedQuality);
            let defaultVideoUrl = dmmVideoMap[defaultVideoQuality];
            if ($videoEl.attr("src") !== defaultVideoUrl) {
              $videoEl.attr("src", defaultVideoUrl);
              videoEl.load();
              videoEl.play();
            }
            qualityOptions.forEach(((option) => {
              let dmmVideoUrl = dmmVideoMap[option.quality];
              if (dmmVideoUrl) {
                const isActive = defaultVideoQuality === option.quality;
                let qualityButton = $(`
                    <button class="video-control-btn${isActive ? " active" : ""}" 
                            id="${option.id}" 
                            data-quality="${option.quality}"
                            data-video-src="${dmmVideoUrl}"
                            style="min-width: 40px; border: 1px solid #ccc; background-color: ${isActive ? "#007bff" : "#fff"}; color: ${isActive ? "white" : "black"};">
                        ${option.text}
                    </button>
                `);
                $qualityButtonGroup.append(qualityButton);
              }
            }));
          }
          $bottomToolbar.append($qualityButtonGroup);
          let $functionButtonGroup = $("<div></div>").css({
            display: "flex",
            gap: "5px",
            "align-items": "center",
            "margin-left": "auto"
          }), filterButton = $(`<button class="menu-btn" id="video-filterBtn" style="min-width: 120px; background-color:#de3333;">屏蔽 ${this.filterHotKey ? "(" + this.filterHotKey + ")" : ""}</button>`);
          $functionButtonGroup.append(filterButton);
          let favoriteButton = $(`<button class="menu-btn" id="video-favoriteBtn" style="min-width: 120px; background-color:#25b1dc;">收藏 ${this.favoriteHotKey ? "(" + this.favoriteHotKey + ")" : ""}</button>`);
          $functionButtonGroup.append(favoriteButton);
          let speedButton = $(`<button class="menu-btn" id="speed-btn" style="min-width: 120px; background-color:#76b45d;">快进 ${this.speedVideoHotKey ? "(" + this.speedVideoHotKey + ")" : ""}</button>`);
          $functionButtonGroup.append(speedButton);
          $bottomToolbar.append($functionButtonGroup);
          $videoContainer.append($bottomToolbar);
          $bottomToolbar.on("click", ".video-control-btn", (async (e) => {
            const $button = $(e.currentTarget), videoSrc = $button.data("video-src");
            if (!$button.hasClass("active")) try {
              const currentTime = videoEl.currentTime;
              $videoEl.attr("src", videoSrc);
              videoEl.load();
              if (currentTime > 0 && Number.isFinite(currentTime)) {
                videoEl.currentTime = currentTime;
              }
              await videoEl.play();
              $bottomToolbar.find(".video-control-btn").removeClass("active").css({
                "background-color": "#fff",
                color: "black"
              });
              $button.addClass("active").css({
                "background-color": "#007bff",
                color: "white"
              });
            } catch (error) {
              console.error("切换画质失败:", error);
            }
          }));
          $("#speed-btn").on("click", (() => {
            this.getBean("DetailPageButtonPlugin").speedVideo();
          }));
          utils.rightClick(document.body, "#speed-btn", ((event) => {
            this.getBean("DetailPageButtonPlugin").filterOne(event);
          }));
          $("#video-filterBtn").on("click", ((event) => {
            this.getBean("DetailPageButtonPlugin").filterOne(event);
          }));
          $("#video-favoriteBtn").on("click", ((event) => {
            this.getBean("DetailPageButtonPlugin").favoriteOne(event);
          }));
        }
      }
      const _HotkeyManager = class _HotkeyManager2 {
        constructor() {
          if (new.target === _HotkeyManager2) throw new Error("HotkeyManager cannot be instantiated.");
        }
        static registerHotkey(hotkeyString, callback, keyupCallback = null) {
          if (Array.isArray(hotkeyString)) {
            let id_list = [];
            hotkeyString.forEach(((hotkey) => {
              if (!this.isHotkeyFormat(hotkey)) throw new Error("快捷键格式错误");
              let id = this.recordHotkey(hotkey, callback, keyupCallback);
              id_list.push(id);
            }));
            return id_list;
          }
          if (!this.isHotkeyFormat(hotkeyString)) throw new Error("快捷键格式错误");
          return this.recordHotkey(hotkeyString, callback, keyupCallback);
        }
        static recordHotkey(hotkeyString, callback, keyupCallback) {
          let id = Math.random().toString(36).substr(2);
          this.registerHotKeyMap.set(id, {
            hotkeyString,
            callback,
            keyupCallback
          });
          return id;
        }
        static unregisterHotkey(id) {
          this.registerHotKeyMap.has(id) && this.registerHotKeyMap.delete(id);
        }
        static isHotkeyFormat(hotkeyString) {
          return hotkeyString.toLowerCase().split("+").map(((k) => k.trim())).every(((k) => ["ctrl", "shift", "alt"].includes(k) || 1 === k.length));
        }
        static judgeHotkey(hotkeyString, event) {
          const keyList = hotkeyString.toLowerCase().split("+").map(((k) => k.trim())), ctrl = keyList.includes("ctrl"), shift = keyList.includes("shift"), alt = keyList.includes("alt"), key = keyList.find(((k) => "ctrl" !== k && "shift" !== k && "alt" !== k));
          return (this.isMac ? event.metaKey : event.ctrlKey) === ctrl && event.shiftKey === shift && event.altKey === alt && event.key.toLowerCase() === key;
        }
      };
      __publicField2(_HotkeyManager, "isMac", 0 === navigator.platform.indexOf("Mac"));
      __publicField2(_HotkeyManager, "registerHotKeyMap", /* @__PURE__ */ new Map());
      __publicField2(_HotkeyManager, "handleKeydown", ((event) => {
        for (const [id, data] of _HotkeyManager.registerHotKeyMap) {
          let hotkeyString = data.hotkeyString, callback = data.callback;
          _HotkeyManager.judgeHotkey(hotkeyString, event) && callback(event);
        }
      }));
      __publicField2(_HotkeyManager, "handleKeyup", ((event) => {
        for (const [id, data] of _HotkeyManager.registerHotKeyMap) {
          let hotkeyString = data.hotkeyString, keyupCallback = data.keyupCallback;
          keyupCallback && (_HotkeyManager.judgeHotkey(hotkeyString, event) && keyupCallback(event));
        }
      }));
      let HotkeyManager = _HotkeyManager;
      document.addEventListener("keydown", ((event) => {
        HotkeyManager.handleKeydown(event);
      }));
      document.addEventListener("keyup", ((event) => {
        HotkeyManager.handleKeyup(event);
      }));
      class JavTrailersPlugin extends BasePlugin {
        getName() {
          return "JavTrailersPlugin";
        }
        constructor() {
          super();
          this.hasBand = false;
        }
        handle() {
          let href = window.location.href;
          if (!href.includes("handle=1")) return;
          if ($("h1:contains('Page not found')").length) {
            console.log("番号无法匹配, 跳搜索");
            let keyword = href.split("?")[0].split("video/")[1].toLowerCase().replace("00", "-");
            window.location.href = "/search/" + encodeURIComponent(keyword) + window.location.search;
            return;
          }
          let findList = $(".videos-list .video-link").toArray();
          if (findList.length) {
            const keyword = href.split("?")[0].split("search/")[1].toLowerCase(), matchedLink = findList.find(((el) => $(el).find(".vid-title").text().toLowerCase().includes(keyword)));
            if (matchedLink) {
              window.location.href = $(matchedLink).attr("href") + window.location.search;
              return;
            }
          }
          this.handlePlayJavTrailers();
          $("#videoPlayerContainer").on("click", (() => {
            this.handlePlayJavTrailers();
          }));
          window.addEventListener("message", ((event) => {
            let videoEl = document.getElementById("vjs_video_3_html5_api");
            videoEl && (videoEl.currentTime += 5);
          }));
          const urlParams = new URLSearchParams(window.location.search), filterHotKey = urlParams.get("filterHotKey"), favoriteHotKey = urlParams.get("favoriteHotKey"), speedVideoHotKey = urlParams.get("speedVideoHotKey");
          filterHotKey && HotkeyManager.registerHotkey(filterHotKey, (() => window.parent.postMessage(filterHotKey, "*")));
          favoriteHotKey && HotkeyManager.registerHotkey(favoriteHotKey, (() => window.parent.postMessage(favoriteHotKey, "*")));
          speedVideoHotKey && HotkeyManager.registerHotkey(speedVideoHotKey, (() => {
            const videoEl = document.getElementById("vjs_video_3_html5_api");
            videoEl && (videoEl.currentTime += 5);
          }));
        }
        handlePlayJavTrailers() {
          if (!this.hasBand) {
            utils.loopDetector((() => 0 !== $("#vjs_video_3_html5_api").length), (() => {
              setTimeout((() => {
                this.hasBand = true;
                let videoEl = document.getElementById("vjs_video_3_html5_api");
                console.log(videoEl);
                videoEl.play();
                videoEl.currentTime = 5;
                videoEl.addEventListener("timeupdate", (function() {
                  videoEl.currentTime >= 14 && videoEl.currentTime < 16 && (videoEl.currentTime += 2);
                }));
                $("#vjs_video_3_html5_api").css({
                  position: "fixed",
                  width: "100vw",
                  height: "100vh",
                  objectFit: "cover",
                  zIndex: "999999999"
                });
                $(".vjs-control-bar").css({
                  position: "fixed",
                  bottom: "20px",
                  zIndex: "999999999"
                });
              }), 100);
            }));
            utils.loopDetector((() => $("#vjs_video_3 canvas").length > 0), (() => {
              0 !== $("#vjs_video_3 canvas").length && $("#vjs_video_3 canvas").css({
                position: "fixed",
                width: "100vw",
                height: "100vh",
                objectFit: "cover",
                top: "0",
                right: "0",
                zIndex: "999999998"
              });
            }));
          }
        }
      }
      class SubTitleCatPlugin extends BasePlugin {
        getName() {
          return "SubTitleCatPlugin";
        }
        handle() {
          $(".t-banner-inner").hide();
          $("#navbar").hide();
          let keyword = new URLSearchParams(window.location.search).get("search").toLowerCase(), findList = $(".sub-table tr td a").toArray(), visibleCount = 0;
          findList.forEach(((el) => {
            let item = $(el);
            item.text().toLowerCase().includes(keyword) ? visibleCount++ : item.parent().parent().hide();
          }));
          0 === visibleCount && show.error("该番号无字幕!");
          const $secTitle = $(".sec-title"), newHTML = $secTitle.html().replace(/^\d+/, visibleCount);
          $secTitle.html(newHTML);
        }
      }
      class JavPackSubtitle {
        static escapeHtml(value = "") {
          return String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
        }
        static sanitizeName(value = "") {
          return String(value).replace(/[\\/:*?"<>|]/g, "").replace(/\s+/g, " ").trim();
        }
        static isSubtitleFile(file = {}) {
          return /\.(srt|ass|ssa|vtt|sub)$/i.test(file.n || "");
        }
        static buildDefaultKeyword(details = {}) {
          return this.sanitizeName(details.code || "") || this.sanitizeName([details.code, details.title].filter(Boolean).join(" ")) || document.title;
        }
        static buildSearchKeywords(details = {}) {
          return [this.buildDefaultKeyword(details)].filter(Boolean);
        }
        static buildSubtitleBaseName(details = {}) {
          var _a2;
          if ((_a2 = window.JavPackMatch115Console) == null ? void 0 : _a2.buildRename) {
            return window.JavPackMatch115Console.buildRename(details, [{ n: `${details.code || "subtitle"}.mp4` }]);
          }
          return this.sanitizeName([details.code, details.title].filter(Boolean).join(" ")) || this.buildDefaultKeyword(details);
        }
        static buildSubtitleFilename(details = {}, item = {}) {
          const ext = this.sanitizeName(item.ext || "srt").replace(/^\.+/, "") || "srt";
          return `${this.buildSubtitleBaseName(details)}.${ext}`;
        }
        static clearPreviewCache() {
          this.previewCache.clear();
        }
        static async checkSubInCloud(req115, cid) {
          if (!req115 || !cid) return false;
          const res = await req115.filesAll(cid);
          return ((res == null ? void 0 : res.data) || []).some((file) => this.isSubtitleFile(file));
        }
        static getReq115() {
          return window.Req115 || null;
        }
        static getTargetCid() {
          var _a2, _b;
          return ((_a2 = document.querySelector(".x-match-cont .zymatch-item [data-cid]")) == null ? void 0 : _a2.dataset.cid) || ((_b = document.querySelector(".x-match-cont .zymatch-item")) == null ? void 0 : _b.dataset.cid) || "";
        }
        static async find115Cid(code) {
          if (!code) return "";
          const req115 = this.getReq115();
          if (!req115) return "";
          try {
            const { data } = await req115.filesSearchAllFolders(code);
            if (!(data == null ? void 0 : data.length)) return "";
            const exact = data.find((f) => f.n === code);
            if (exact) return exact.cid;
            const upper = data.find((f) => f.n.toUpperCase() === code.toUpperCase());
            if (upper) return upper.cid;
            const contains = data.find((f) => f.n.toUpperCase().includes(code.toUpperCase()));
            return (contains == null ? void 0 : contains.cid) || "";
          } catch (e) {
            console.warn("[JavPackSubtitle] 115搜索目录失败:", e.message);
            return "";
          }
        }
        static async preload115Matches(code) {
          this.cached115Matches = [];
          this.current115Cids = [];
          if (!code) return;
          const req115 = this.getReq115();
          if (!req115) return;
          try {
            const { data } = await req115.filesSearchAllFolders(code);
            if (!(data == null ? void 0 : data.length)) return;
            const codeUpper = code.toUpperCase();
            const matches = data.filter((f) => f.n.toUpperCase().includes(codeUpper)).map((f) => ({ cid: f.cid, name: f.n })).sort((a, b) => {
              const aExact = a.name.toUpperCase() === codeUpper;
              const bExact = b.name.toUpperCase() === codeUpper;
              if (aExact && !bExact) return -1;
              if (!aExact && bExact) return 1;
              return a.name.localeCompare(b.name);
            });
            this.cached115Matches = matches;
            this.current115Cids = matches.length === 1 ? [matches[0].cid] : matches.map((m) => m.cid);
            this.last115Code = code;
          } catch (e) {
            console.warn("[JavPackSubtitle] 115预加载目录失败:", e.message);
          }
        }
        static ensureDetailButton({ details, getTargetCid, getSubtitleEntries } = {}) {
          const panel = document.querySelector(".movie-panel-info");
          const buttons = panel == null ? void 0 : panel.querySelector(".panel-block:last-child .buttons");
          if (!buttons || document.getElementById(this.BTN_ID)) return;
          const btn = document.createElement("button");
          btn.id = this.BTN_ID;
          btn.type = "button";
          btn.className = "button is-small is-info x-subtitle-search";
          btn.textContent = "字幕搜索";
          const refreshSubtitleFileTip = () => {
            const groups = /* @__PURE__ */ new Map();
            ((getSubtitleEntries == null ? void 0 : getSubtitleEntries()) || []).forEach(({ directory, files = [] }) => {
              const path = String(directory || "").trim();
              if (!path) return;
              if (!groups.has(path)) groups.set(path, /* @__PURE__ */ new Set());
              files.map((file) => String((file == null ? void 0 : file.n) || (file == null ? void 0 : file.name) || file || "").trim()).filter(Boolean).forEach((name2) => groups.get(path).add(name2));
            });
            btn.title = groups.size ? ["已有字幕文件:", ...[...groups].flatMap(([path, files]) => [
              `目录:${path}`,
              ...[...files].map((name2) => `  - ${name2}`)
            ])].join("\n") : "暂无已匹配的字幕文件";
            btn.textContent = groups.size ? "已有字幕" : "字幕搜索";
            btn.classList.toggle("is-success", Boolean(groups.size));
            btn.classList.toggle("is-info", !groups.size);
          };
          btn.addEventListener("mouseenter", refreshSubtitleFileTip);
          btn.addEventListener("focus", refreshSubtitleFileTip);
          btn.addEventListener("click", (e) => {
            e.preventDefault();
            e.stopPropagation();
            this.openSearchModal({ details, getTargetCid });
          });
          buttons.appendChild(btn);
          refreshSubtitleFileTip();
          const matchRoot = document.querySelector(".x-match-cont");
          if (matchRoot) new MutationObserver(refreshSubtitleFileTip).observe(matchRoot, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ["data-has-subtitle", "data-subtitle-files"]
          });
        }
        static modalTemplate(defaultKw) {
          return `
      <div class="pdb-sub-modal">
        <div class="pdb-sub-header">
          <div class="pdb-sub-search-wrap">
            <span class="pdb-sub-title">字幕检索:</span>
            <input type="text" id="sub-search-input" value="${this.escapeHtml(defaultKw)}" class="pdb-sub-input" placeholder="输入检索词..." />
            <button id="sub-search-btn" class="pdb-sub-btn">重新搜索</button>
          </div>
          <div class="pdb-sub-115-wrap" id="sub-115-wrap" style="display:none;">
            <span class="pdb-sub-title">115目录:</span>
            <div id="sub-115-select" class="pdb-sub-checkbox-group"></div>
          </div>
          <span class="pdb-sub-close" id="sub-close-btn">&times;</span>
        </div>
        <div class="pdb-sub-body">
          <div class="pdb-sub-content"></div>
          <div class="pdb-sub-preview-wrap">
            <div class="pdb-sub-preview-header">
              <span>字幕内容预览</span><span id="preview-status" class="pdb-sub-preview-status">暂无预览</span>
            </div>
            <textarea class="pdb-sub-textarea" readonly></textarea>
          </div>
        </div>
      </div>
    `;
        }
        static openSearchModal({ details = {}, getTargetCid } = {}) {
          var _a2;
          const defaultKw = this.buildDefaultKeyword(details);
          this.currentDetails = details;
          (_a2 = document.getElementById(this.MODAL_ID)) == null ? void 0 : _a2.remove();
          const overlay = document.createElement("div");
          overlay.id = this.MODAL_ID;
          overlay.className = "pdb-sub-overlay";
          overlay.innerHTML = this.modalTemplate(defaultKw);
          document.body.appendChild(overlay);
          const contentWrap = overlay.querySelector(".pdb-sub-content");
          const previewBox = overlay.querySelector(".pdb-sub-textarea");
          const statusNode = overlay.querySelector("#preview-status");
          const input2 = overlay.querySelector("#sub-search-input");
          const dirWrap = overlay.querySelector("#sub-115-wrap");
          const dirSelect = overlay.querySelector("#sub-115-select");
          const refresh115DirUI = () => {
            if (!this.cached115Matches.length) {
              dirWrap.style.display = "none";
              return;
            }
            dirWrap.style.display = "";
            dirSelect.innerHTML = this.cached115Matches.map((m) => {
              const checked = this.current115Cids.includes(m.cid) ? " checked" : "";
              const displayName = m.name.length > 26 ? m.name.slice(0, 26) + "..." : m.name;
              return `<label class="pdb-sub-checkbox-label"><input type="checkbox" class="pdb-sub-checkbox" value="${m.cid}"${checked}><span title="${this.escapeHtml(m.name)}">${this.escapeHtml(displayName)}</span></label>`;
            }).join("");
          };
          dirSelect.addEventListener("change", () => {
            this.current115Cids = Array.from(dirSelect.querySelectorAll("input:checked")).map((cb) => cb.value);
          });
          if (this.last115Code === details.code && this.cached115Matches.length) {
            refresh115DirUI();
          } else {
            this.preload115Matches(details.code).then(refresh115DirUI);
          }
          const closeModal = () => {
            this.clearPreviewCache();
            previewBox.value = "";
            overlay.remove();
          };
          overlay.querySelector("#sub-close-btn").addEventListener("click", closeModal);
          overlay.addEventListener("click", (e) => {
            if (e.target === overlay) closeModal();
          });
          const performSearch = async (kw) => {
            if (!kw) return;
            contentWrap.innerHTML = '<div class="pdb-sub-msg">正在搜索字幕来源,请稍候...</div>';
            previewBox.value = "";
            statusNode.textContent = "暂无预览";
            try {
              const results = await Promise.allSettled([
                this.searchXunlei(kw),
                this.searchSubtitleCat(kw)
              ]);
              const dataList = results.flatMap((result) => result.status === "fulfilled" ? result.value : []);
              const chineseResults = this.sortResults(dataList, kw, details);
              if (!chineseResults.length) {
                contentWrap.innerHTML = '<div class="pdb-sub-msg">未找到简中或繁中字幕,请尝试使用完整番号搜索</div>';
                return;
              }
              this.renderTable({
                container: contentWrap,
                dataList: chineseResults,
                previewBox,
                statusNode,
                overlay,
                details,
                getTargetCid,
                kw
              });
              if (kw.toUpperCase() !== (details.code || "").toUpperCase()) {
                this.preload115Matches(kw).then(refresh115DirUI);
              }
            } catch (e) {
              console.error("字幕搜索失败:", e);
              contentWrap.innerHTML = '<div class="pdb-sub-msg">搜索失败,请稍后重试</div>';
            }
          };
          overlay.querySelector("#sub-search-btn").addEventListener("click", () => performSearch(input2.value.trim()));
          input2.addEventListener("keypress", (e) => {
            if (e.key === "Enter") performSearch(input2.value.trim());
          });
          performSearch(defaultKw);
        }
        static requestText(url) {
          return window.gmHttp.get(url);
        }
        static async searchXunlei(kw) {
          const root = await this.requestText(`https://api-shoulei-ssl.xunlei.com/oracle/subtitle?name=${encodeURIComponent(kw)}`);
          if (!root || root.code !== 0 || !Array.isArray(root.data)) return [];
          const results = root.data.slice(0, 20).map((item) => this.normalizeXunleiResult(item));
          const labeledResults = await Promise.all(results.map((item) => this.labelXunleiChineseSubtitle(item)));
          return labeledResults.filter(Boolean);
        }
        static normalizeXunleiResult(item = {}) {
          const rawLanguages = item.languages ?? item.Languages ?? item.langs ?? item.Langs ?? item.language ?? item.Language ?? [];
          return {
            ...item,
            name: item.name || item.Name || item.extra_name || item.Title || "",
            languages: Array.isArray(rawLanguages) ? rawLanguages : rawLanguages ? [String(rawLanguages)] : [],
            ext: item.ext || item.Ext || item.format || item.Format || "srt",
            url: item.url || item.Url || "",
            provider: "迅雷"
          };
        }
        static async labelXunleiChineseSubtitle(item) {
          if (this.isWantedChineseSubtitle(item)) return item;
          if (!item.url) return null;
          try {
            const language = this.detectChineseSubtitleLanguage(this.decodeSubtitle(await this.fetchBinaryCached(item.url)));
            return language ? { ...item, languages: [language] } : null;
          } catch (_) {
            return null;
          }
        }
        static detectChineseSubtitleLanguage(text = "") {
          const sample = String(text).slice(0, 12e3);
          if (!/[\u3400-\u9fff]/.test(sample)) return "";
          const simplifiedCount = (sample.match(/[为与个么开关东风头后发这边过还进时样气应实话说问题现经体万台农国门书车云]/g) || []).length;
          const traditionalCount = (sample.match(/[為與個麼開關東風頭後發這邊過還進時樣氣應實話說問題現經體萬臺農國門書車雲]/g) || []).length;
          if (simplifiedCount === 0 && traditionalCount === 0) return "";
          return traditionalCount > simplifiedCount ? "繁中" : "简中";
        }
        static async searchSubtitleCat(kw) {
          const searchUrl = `${this.SUBTITLECAT_ORIGIN}/index.php?search=${encodeURIComponent(kw)}`;
          const searchHtml = await this.requestText(searchUrl);
          const entries = this.parseSubtitleCatSearch(searchHtml).slice(0, 20);
          const languageGroups = await Promise.all(entries.map(async (entry) => {
            try {
              return this.parseSubtitleCatLanguages(await this.requestText(entry.detailUrl), entry);
            } catch (_) {
              return [];
            }
          }));
          return languageGroups.flat();
        }
        static parseSubtitleCatSearch(html2) {
          const doc = new DOMParser().parseFromString(html2, "text/html");
          return [...doc.querySelectorAll("table.sub-table tbody tr")].flatMap((row) => {
            var _a2;
            const link2 = row.querySelector('a[href*="subs/"]');
            const name2 = (_a2 = link2 == null ? void 0 : link2.textContent) == null ? void 0 : _a2.trim();
            if (!link2 || !name2) return [];
            return [{
              name: name2,
              detailUrl: new URL(link2.getAttribute("href"), this.SUBTITLECAT_ORIGIN).href
            }];
          });
        }
        static parseSubtitleCatLanguages(html2, entry) {
          const doc = new DOMParser().parseFromString(html2, "text/html");
          return [...doc.querySelectorAll(".sub-single")].flatMap((node) => {
            var _a2;
            const languageCode = ((_a2 = node.querySelector("img.flag")) == null ? void 0 : _a2.getAttribute("alt")) || "";
            const language = this.SUBTITLECAT_CHINESE_LANGUAGES.get(languageCode);
            const link2 = node.querySelector("a.green-link[href$='.srt']");
            if (!language || !link2) return [];
            return [{
              name: entry.name,
              languageCode,
              languages: [language],
              ext: "srt",
              provider: "SubtitleCat",
              url: new URL(link2.getAttribute("href"), this.SUBTITLECAT_ORIGIN).href
            }];
          });
        }
        static isWantedChineseSubtitle(item = {}) {
          if (item.provider === "SubtitleCat") return this.SUBTITLECAT_CHINESE_LANGUAGES.has(item.languageCode);
          const language = Array.isArray(item.languages) ? item.languages.join(" ") : String(item.languages || "");
          const name2 = `${item.name || ""} ${item.extra_name || ""}`;
          return /(zh[-_]?cn|zh[-_]?tw|chs|cht|chinese\s*(simplified|traditional)|简体|繁体|繁體|简中|繁中)/i.test(`${language} ${name2}`);
        }
        static sortResults(dataList, kw = "", details = {}) {
          const kwClean = kw.toLowerCase().replace(/[-_.\s]/g, "");
          const tokens = kw.toLowerCase().split(/[-_.\s]+/).filter((word) => word.length > 1);
          const codeClean = String(details.code || kw).toLowerCase().replace(/[-_.\s]/g, "");
          return [...dataList].filter((item) => this.isWantedChineseSubtitle(item)).sort((a, b) => this.scoreResult(b, kwClean, tokens, codeClean) - this.scoreResult(a, kwClean, tokens, codeClean));
        }
        static scoreResult(item, kwClean, tokens, codeClean = "") {
          const name2 = (item.name || item.extra_name || "").toLowerCase();
          const compactName = name2.replace(/[-_.\s]/g, "");
          const lang = (item.languages && item.languages[0] || "").toLowerCase();
          let score = 0;
          if (codeClean && compactName.includes(codeClean)) score += 800;
          tokens.forEach((token) => {
            if (name2.includes(token)) score += 50;
          });
          if (kwClean && compactName.includes(kwClean)) score += 500;
          if (/(zh|cn|chs|cht|chinese|简中|繁中)/i.test(lang) || /(zh|cn|chs|cht|chinese|简中|繁中)/i.test(name2)) score += 100;
          if (item.ext === "srt" || item.ext === "ass") score += 20;
          return score;
        }
        static renderTable({ container, dataList, previewBox, statusNode, overlay, details, getTargetCid, kw = "" }) {
          const words = kw.split(/[-_.\s]+/).filter((word) => word.length > 1);
          const highlightRegex = words.length ? new RegExp(`(${words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})`, "gi") : null;
          container.innerHTML = `
      <table class="pdb-sub-table">
        <thead>
          <tr>
            <th class="pdb-sub-th" style="width: 90px;">来源</th>
            <th class="pdb-sub-th">原始字幕名称</th>
            <th class="pdb-sub-th" style="width: 80px;">语言</th>
            <th class="pdb-sub-th" style="width: 60px;">格式</th>
            <th class="pdb-sub-th" style="width: 170px; text-align:center;">操作</th>
          </tr>
        </thead>
        <tbody>
          ${dataList.map((item, index) => this.renderRow(item, index, highlightRegex)).join("")}
        </tbody>
      </table>
    `;
          container.querySelectorAll(".sub-action-btn").forEach((btn) => {
            btn.addEventListener("click", () => this.handleAction({
              btn,
              item: dataList[btn.dataset.idx],
              previewBox,
              statusNode,
              overlay,
              details,
              getTargetCid
            }));
          });
        }
        static renderRow(item, index, highlightRegex) {
          var _a2;
          const lang = ((_a2 = item.languages) == null ? void 0 : _a2[0]) || "未知";
          const name2 = item.name || item.extra_name || "未知字幕";
          const provider = item.provider || "迅雷";
          const providerClass = provider === "SubtitleCat" ? "pdb-sub-provider--subtitlecat" : "pdb-sub-provider--xunlei";
          const displayName = highlightRegex ? this.escapeHtml(name2).replace(highlightRegex, '<span style="color:#e74c3c; font-weight:bold;">$1</span>') : this.escapeHtml(name2);
          return `
      <tr class="pdb-sub-tr">
        <td class="pdb-sub-td-provider"><span class="pdb-sub-provider ${providerClass}">${this.escapeHtml(provider)}</span></td>
        <td class="pdb-sub-td">${displayName}</td>
        <td class="pdb-sub-td-lang">${this.escapeHtml(lang)}</td>
        <td class="pdb-sub-td-ext">${this.escapeHtml(item.ext || "srt")}</td>
        <td class="pdb-sub-td-actions">
          <button class="sub-action-btn pdb-sub-action-btn pdb-sub-btn-preview" data-action="preview" data-idx="${index}">预览</button>
          <button class="sub-action-btn pdb-sub-action-btn pdb-sub-btn-download" data-action="download" data-idx="${index}">下载</button>
          <button class="sub-action-btn pdb-sub-action-btn pdb-sub-btn-upload" data-action="upload" data-idx="${index}">115直传</button>
        </td>
      </tr>
    `;
        }
        static async handleAction({ btn, item, previewBox, statusNode, overlay, details, getTargetCid }) {
          const action = btn.dataset.action;
          const oldText = btn.textContent;
          const url = item.url;
          if (!url) return alert("无效的字幕下载直链");
          const filename = this.buildSubtitleFilename(details, item);
          btn.textContent = "获取中...";
          btn.style.opacity = "0.6";
          try {
            const buffer = await this.fetchBinaryCached(url);
            const text = this.decodeSubtitle(buffer);
            if (text.includes("<?xml") && text.includes("<Code>NoSuchKey</Code>")) throw new Error("该字幕在迅雷云端已失效丢失 (NoSuchKey)");
            if (action === "preview") {
              previewBox.value = text;
              statusNode.textContent = filename;
              this.setActivePreviewRow(overlay, btn);
            } else if (action === "download") {
              this.downloadBuffer(buffer, filename);
            } else if (action === "upload") {
              const jdbCid = (getTargetCid == null ? void 0 : getTargetCid()) || this.getTargetCid();
              let cids = jdbCid ? [jdbCid] : [...this.current115Cids];
              const req115 = this.getReq115();
              if (!cids.length || !req115) throw new Error("未在 115 找到匹配目录,请确认作品已下载到 115");
              btn.textContent = "直传中...";
              for (const cid of cids) {
                await this.uploadSubtitle({ req115, cid, filename, buffer });
              }
              show.ok(`字幕已上传到 115(${cids.length}个目录)`);
              const detailBtn = document.getElementById(this.BTN_ID);
              if (detailBtn) {
                detailBtn.classList.add("is-success");
                detailBtn.textContent = "已有字幕";
              }
              window.dispatchEvent(new CustomEvent("JavDB_SubtitleUploaded", {
                detail: {
                  code: details.code || "",
                  cid: String(cids[0]),
                  subtitle: { n: filename, s: buffer.byteLength }
                }
              }));
              overlay.remove();
              this.clearPreviewCache();
            }
          } catch (err) {
            alert(`执行中止: ${err.message}`);
          } finally {
            btn.textContent = oldText;
            btn.style.opacity = "1";
          }
        }
        static setActivePreviewRow(overlay, btn) {
          overlay.querySelectorAll(".pdb-sub-tr.is-previewing").forEach((row2) => {
            row2.classList.remove("is-previewing");
            row2.removeAttribute("aria-current");
          });
          const row = btn.closest(".pdb-sub-tr");
          if (!row) return;
          row.classList.add("is-previewing");
          row.setAttribute("aria-current", "true");
        }
        static decodeSubtitle(buffer) {
          let text = new TextDecoder("utf-8").decode(buffer);
          if ((text.match(/\uFFFD/g) || []).length > 3) text = new TextDecoder("gbk").decode(buffer);
          return text;
        }
        static fetchBinaryCached(url) {
          if (this.previewCache.has(url)) return Promise.resolve(this.previewCache.get(url));
          return this.fetchBinary(url).then((buffer) => {
            this.previewCache.set(url, buffer);
            return buffer;
          });
        }
        static fetchBinary(url) {
          return new Promise((resolve, reject) => {
            _GM_xmlhttpRequest({
              method: "GET",
              url,
              responseType: "arraybuffer",
              onload: (res) => {
                if (res.status === 200 && res.response) resolve(res.response);
                else reject(new Error(`字幕流获取失败,HTTP_CODE: ${res.status}`));
              },
              onerror: () => reject(new Error("跨域网络请求被阻断"))
            });
          });
        }
        static downloadBuffer(buffer, filename) {
          const blob = new Blob([buffer], { type: "application/octet-stream" });
          const link2 = document.createElement("a");
          link2.href = URL.createObjectURL(blob);
          link2.download = filename;
          link2.click();
          URL.revokeObjectURL(link2.href);
        }
        static uploadSubtitle(args) {
          return args.req115.queueMutation("上传字幕", () => this.uploadSubtitleNow(args));
        }
        static async uploadSubtitleNow({ req115, cid, filename, buffer }) {
          const blob = new Blob([buffer], { type: "application/octet-stream" });
          const file = new File([blob], filename, { type: "application/octet-stream" });
          const initRes = await req115.sampleInitUpload({ filename, filesize: file.size, cid });
          if (!initRes || !initRes.host && initRes.status !== 2 && initRes.statuscode !== 0) {
            throw new Error((initRes == null ? void 0 : initRes.error_msg) || "获取115上传安全凭证被拦截");
          }
          if (!initRes.host) return initRes;
          let uploadRes = null;
          for (let retry = 0; retry < 3; retry++) {
            uploadRes = await req115.upload({ ...initRes, filename, file });
            if (uploadRes && uploadRes.state !== false) return uploadRes;
            await new Promise((resolve) => setTimeout(resolve, 1500));
          }
          throw new Error((uploadRes == null ? void 0 : uploadRes.error_msg) || (uploadRes == null ? void 0 : uploadRes.error) || "115 服务器拒绝接收回调");
        }
      }
      __publicField(JavPackSubtitle, "BTN_ID", "x-subtitle-search-btn");
      __publicField(JavPackSubtitle, "MODAL_ID", "x-subtitle-modal");
      __publicField(JavPackSubtitle, "previewCache", /* @__PURE__ */ new Map());
      __publicField(JavPackSubtitle, "cached115Matches", []);
      __publicField(JavPackSubtitle, "current115Cids", []);
      __publicField(JavPackSubtitle, "last115Code", "");
      __publicField(JavPackSubtitle, "SUBTITLECAT_ORIGIN", "https://www.subtitlecat.com");
      __publicField(JavPackSubtitle, "SUBTITLECAT_CHINESE_LANGUAGES", /* @__PURE__ */ new Map([
        ["zh-CN", "简中"],
        ["zh-TW", "繁中"]
      ]));
      window.JavPackSubtitle = JavPackSubtitle;
      class Req {
        static _serializeToFormData(formData, data, parentKey = "") {
          Object.entries(data).forEach(([key, val]) => {
            const fullKey = parentKey ? `${parentKey}[${key}]` : key;
            if (val === void 0) return;
            if (val !== null && typeof val === "object" && !(val instanceof Blob || val instanceof Date)) {
              this._serializeToFormData(formData, val, fullKey);
            } else {
              formData.append(fullKey, val instanceof Date ? val.toISOString() : val);
            }
          });
        }
        static request(config) {
          let options;
          if (typeof config === "string") {
            options = { url: config };
          } else if (config !== null && typeof config === "object") {
            options = { ...config };
          } else {
            return Promise.reject(new TypeError("Request config must be a string or an object"));
          }
          if (!options.url) return Promise.reject(new Error("Request URL is required"));
          options.method = (options.method || this.DEFAULT_METHOD).toUpperCase();
          options.timeout || (options.timeout = this.DEFAULT_TIMEOUT);
          const { params, method, data, signal } = options;
          try {
            const urlInstance = new URL(options.url, location.origin);
            if (params) {
              const cleanParams = this._isPlainObject(params) ? Object.fromEntries(Object.entries(params).filter(([_, v]) => v != null)) : params;
              new URLSearchParams(cleanParams).forEach((v, k) => urlInstance.searchParams.set(k, v));
            }
            options.url = urlInstance.toString();
          } catch {
            return Promise.reject(new Error(`Invalid Request URL: ${options.url}`));
          }
          if (method === "POST") {
            options.responseType ?? (options.responseType = this.DEFAULT_POST_RESPONSE_TYPE);
            if (this._isPlainObject(data) && !this._isRawBody(data)) {
              const formData = new FormData();
              this._serializeToFormData(formData, data);
              options.data = formData;
            }
          } else if (method === "GET") {
            options.responseType ?? (options.responseType = options.url.split(/[?#]/)[0].endsWith(".json") ? "json" : this.DEFAULT_GET_RESPONSE_TYPE);
          }
          return new Promise((resolve, reject) => {
            if (signal == null ? void 0 : signal.aborted) return reject(new DOMException("Aborted", "AbortError"));
            const { url: targetUrl } = options;
            let xhrHandler;
            const handleAbort = () => xhrHandler == null ? void 0 : xhrHandler.abort();
            const finalize = (settleFn, ...args) => {
              signal == null ? void 0 : signal.removeEventListener("abort", handleAbort);
              settleFn(...args);
            };
            xhrHandler = _GM_xmlhttpRequest({
              onabort: () => reject(new DOMException("Aborted", "AbortError")),
              ontimeout: () => finalize(reject, new Error(`Timeout: ${targetUrl}`)),
              onerror: () => finalize(reject, new Error(`Network Error: ${targetUrl}`)),
              onload: ({
                status,
                finalUrl,
                response
              }) => status >= 400 ? finalize(reject, new Error(`HTTP ${status}: ${targetUrl}`)) : finalize(resolve, method === "HEAD" ? finalUrl : response),
              ...options,
              params: void 0
            });
            signal == null ? void 0 : signal.addEventListener("abort", handleAbort, { once: true });
          });
        }
        static async tasks(initial, steps, signal) {
          let ctx = initial;
          for (const step of steps) {
            if (signal == null ? void 0 : signal.aborted) throw new DOMException("Aborted", "AbortError");
            if (typeof ctx === "string" || (ctx == null ? void 0 : ctx.url)) ctx = await this.request(typeof ctx === "string" ? {
              url: ctx,
              signal
            } : { ...ctx, signal });
            ctx = await step(ctx);
          }
          return ctx;
        }
      }
      __publicField(Req, "DEFAULT_GET_RESPONSE_TYPE", "document");
      __publicField(Req, "DEFAULT_POST_RESPONSE_TYPE", "json");
      __publicField(Req, "DEFAULT_TIMEOUT", 3e4);
      __publicField(Req, "DEFAULT_METHOD", "GET");
      __publicField(Req, "_isRawBody", (v) => v instanceof FormData || v instanceof Blob || v instanceof ArrayBuffer || v instanceof URLSearchParams);
      __publicField(Req, "_isPlainObject", (v) => Object.prototype.toString.call(v) === "[object Object]");
      class Drive115 extends Req {
        static files(cid = "0", params = {}) {
          return this.request({
            url: "https://webapi.115.com/files",
            params: { cid, ...params }
          });
        }
        static filesSearch(search_value, params = {}) {
          return this.request({
            url: "https://webapi.115.com/files/search",
            params: { search_value, ...params }
          });
        }
        static lixianTaskLists(page = 1) {
          return this.request({
            url: "https://115.com/web/lixian/",
            params: { ct: "lixian", ac: "task_lists", page }
          });
        }
        static labelList() {
          return this.request({
            url: "https://webapi.115.com/label/list",
            params: { keyword: "", limit: 11150 }
          });
        }
        /**
         * @connect 115vod.com
         */
        static filesVideo(pickcode) {
          return this.request({
            url: "https://115vod.com/webapi/files/video",
            params: { pickcode, local: 1 }
          });
        }
        static post(details) {
          return this.request({ method: "POST", ...details });
        }
        static filesAdd(cname, pid) {
          return this.post({
            url: "https://webapi.115.com/files/add",
            data: { cname, pid }
          });
        }
        static lixianAddTaskUrl(url, wp_path_id) {
          return this.post({
            url: "https://115.com/web/lixian/",
            params: { ct: "lixian", ac: "add_task_url" },
            data: { url, wp_path_id }
          });
        }
        /**
         * Bulk delete offline tasks and source files
         * @param {string[]} hash Array of info_hashes
         */
        static lixianTaskDel(hash) {
          return this.post({
            url: "https://115.com/web/lixian/",
            params: { ct: "lixian", ac: "task_del" },
            data: { hash }
          });
        }
        /**
         * Bulk delete files
         * @param {string[]} fid Array of file IDs
         * @param {string} pid Parent folder ID
         */
        static rbDelete(fid, pid = "") {
          return this.post({
            url: "https://webapi.115.com/rb/delete",
            data: { fid, pid, ignore_warn: 1 }
          });
        }
        /**
         * Batch move files
         * @param {string[]} fid Array of file IDs
         * @param {string} pid Destination folder ID
         */
        static filesMove(fid, pid) {
          return this.post({
            url: "https://webapi.115.com/files/move",
            data: { fid, pid, move_proid: "" }
          });
        }
        /**
         * Bulk label files
         * @param {string} file_ids fid1,fid2,fid3...
         * @param {string} file_label label_id1,label_id2,label_id3...
         */
        static filesBatchLabel(file_ids, file_label, action = "add") {
          return this.post({
            url: "https://webapi.115.com/files/batch_label",
            data: { file_ids, file_label, action }
          });
        }
        /**
         * Bulk rename files
         * @param {object} files_new_name { [fid]: rename }
         */
        static filesBatchRename(files_new_name) {
          return this.post({
            url: "https://webapi.115.com/files/batch_rename",
            data: { files_new_name }
          });
        }
        static sampleInitUpload({ filename, filesize, cid }) {
          return this.post({
            url: "https://uplb.115.com/3.0/sampleinitupload.php",
            data: { filename, filesize, target: `U_1_${cid}` }
          });
        }
        /**
         * @connect aliyuncs.com
         */
        static upload({
          host: url,
          filename: name2,
          object: key,
          policy,
          accessid: OSSAccessKeyId,
          callback,
          signature,
          file
        }) {
          return this.post({
            url,
            data: {
              name: name2,
              key,
              policy,
              OSSAccessKeyId,
              success_action_status: "200",
              callback,
              signature,
              file
            }
          });
        }
        static filesEdit(fid, fid_cover) {
          return this.post({
            url: "https://webapi.115.com/files/edit",
            data: { fid, fid_cover }
          });
        }
      }
      __publicField(Drive115, "DEFAULT_GET_RESPONSE_TYPE", "json");
      class Req115 extends Drive115 {
        static async filesAll(cid, params = {}) {
          const res = await this.files(cid, params);
          const { count, page_size, data } = res;
          return count > page_size && data.length ? this.files(cid, { ...params, limit: count }) : res;
        }
        static filesAllVideos(cid, params = {}) {
          return this.filesAll(cid, { ...params, type: 4 });
        }
        static filesAllSRTs(cid, params = {}) {
          return this.filesAll(cid, { ...params, suffix: "srt" });
        }
        static async filesSearchAll(search_value, params = {}) {
          const res = await this.filesSearch(search_value, params);
          const { count, page_size, data } = res;
          return count > page_size && data.length ? this.filesSearch(search_value, { ...params, limit: count }) : res;
        }
        static filesSearchAllVideos(search_value, params = {}) {
          return this.filesSearchAll(search_value, { ...params, type: 4 });
        }
        static filesSearchAllFolders(search_value, params = {}) {
          return this.filesSearchAll(search_value, { ...params, fc: 1 });
        }
        static async handleDir(routes) {
          var _a2;
          if (routes.length === 1 && /^\d{5,}$/.test(routes[0])) return routes[0];
          let cid;
          const routesStr = routes.join("/");
          const cachedCid = localStorage.getItem(routesStr);
          if (cachedCid) {
            const res = await this.files(cachedCid);
            if ((_a2 = res == null ? void 0 : res.path) == null ? void 0 : _a2.length) {
              const path = res.path.slice(1).map((p) => p.name);
              if (path.join("/") === routesStr) cid = cachedCid;
            }
          }
          if (!cid) {
            cid = "0";
            for (const route of routes) {
              const { data } = await this.filesSearchAllFolders(route, { cid });
              let folder = data.find((folder2) => folder2.n === route);
              if (!folder) folder = await this.filesAdd(route, cid);
              cid = folder == null ? void 0 : folder.cid;
              if (!cid) break;
            }
          }
          const month = (/* @__PURE__ */ new Date()).getMonth().toString();
          if (localStorage.getItem("115_CD") !== month) {
            localStorage.clear();
            localStorage.setItem("115_CD", month);
          }
          if (cid) localStorage.setItem(routesStr, cid);
          return cid;
        }
        static async handleVerify(info_hash, { regex, codes }, { max, filter }) {
          const sleep = () => {
            return new Promise((r) => {
              setTimeout(r, 1e3);
            });
          };
          let file_id = "";
          let videos = [];
          for (let index = 0; index < max; index++) {
            if (index) await sleep();
            const { tasks } = await this.lixianTaskLists();
            const task = tasks.find((task2) => task2.info_hash === info_hash);
            if (!task || task.status === -1) break;
            file_id = task.file_id;
            if (file_id) break;
          }
          if (!file_id) return { file_id, videos };
          for (let index = 0; index < max; index++) {
            if (index) await sleep();
            const { data } = await this.filesAllVideos(file_id);
            videos = data.filter((item) => regex.test(item.n));
            if (videos.length) break;
          }
          if (!videos.length) {
            const { tasks } = await this.lixianTaskLists();
            const task = tasks.find((task2) => task2.info_hash === info_hash);
            if (task.status === 2) {
              const { data } = await this.filesAllVideos(file_id);
              codes = codes.map((code) => code.toUpperCase());
              videos = data.filter((item) => {
                const name2 = item.n.toUpperCase();
                return codes.some((code) => name2.includes(code));
              });
            }
          }
          return { videos: videos.filter(filter), file_id };
        }
        static async handleClean(keepFiles, cid) {
          const needMove = keepFiles.filter((file) => file.cid !== cid).map((file) => file.fid);
          if (needMove.length) await this.filesMove(needMove, cid);
          const { data } = await this.filesAll(cid);
          const needRemove = data.filter((item) => !keepFiles.some((file) => file.fid === item.fid)).map((item) => item.fid ?? item.cid);
          if (needRemove.length) return this.rbDelete(needRemove, cid);
        }
        static async handleTags(files, tags) {
          var _a2;
          const { data } = await this.labelList();
          if (!((_a2 = data == null ? void 0 : data.list) == null ? void 0 : _a2.length)) return;
          const { list: list2 } = data;
          const labels = [];
          tags.forEach((tag) => {
            const item = list2.find((item2) => item2.name === tag);
            if (item) labels.push(item.id);
          });
          if (labels.length) return this.filesBatchLabel(files.map((it) => it.fid).toString(), labels.toString());
        }
        static handleRename(files, cid, { rename, renameTxt, zh, crack }) {
          rename = rename.replaceAll("$zh", zh ? renameTxt.zh : "");
          rename = rename.replaceAll("$crack", crack ? renameTxt.crack : "");
          rename = rename.split("$sep").filter(Boolean).join(renameTxt.sep);
          rename = rename.trim();
          const renameObj = { [cid]: rename };
          if (files.length === 1) {
            const { fid, ico } = files[0];
            renameObj[fid] = `${rename}.${ico}`;
            return this.filesBatchRename(renameObj);
          }
          const icoMap = files.reduce((acc, { ico, ...item }) => {
            acc[ico] ?? (acc[ico] = []);
            acc[ico].push(item);
            return acc;
          }, {});
          const noTxt = renameTxt.no;
          for (const [ico, items] of Object.entries(icoMap)) {
            if (items.length === 1) {
              renameObj[items[0].fid] = `${rename}.${ico}`;
              continue;
            }
            items.toSorted((a, b) => a.n.localeCompare(b.n)).forEach(({ fid }, idx) => {
              const no = noTxt.replaceAll(`\${no}`, `${idx + 1}`.padStart(2, "0"));
              renameObj[fid] = `${rename}${no}.${ico}`;
            });
          }
          return this.filesBatchRename(renameObj);
        }
        static async handleCover(url, cid, filename) {
          const file = await this.request({ url, timeout: 6e4, responseType: "blob" });
          if (!file) return;
          const res = await this.sampleInitUpload({ cid, filename, filesize: file.size });
          if (res == null ? void 0 : res.host) return this.upload({ ...res, filename, file });
        }
        static async handleOffline({ dir, regex, codes, verifyOptions, code, rename, renameTxt, tags, clean, cover }, magnets) {
          const res = { status: "error", msg: `获取目录失败: ${dir.join("/")}` };
          const cid = await this.handleDir(dir);
          if (!cid) return res;
          for (let index = 0, { length } = magnets; index < length; index++) {
            const { url, zh, crack } = magnets[index];
            const { state, error_msg, errcode, info_hash } = await this.lixianAddTaskUrl(url, cid);
            if (!state) {
              res.msg = error_msg;
              res.status = "error";
              res.currIdx = index;
              if (errcode === 10008) continue;
              if (errcode === 911) res.status = "warn";
              break;
            }
            const { videos, file_id } = await this.handleVerify(info_hash, { regex, codes }, verifyOptions);
            if (!videos.length) {
              if (verifyOptions.clean) this.lixianTaskDel([info_hash]);
              if (file_id && clean) this.rbDelete([file_id], cid);
              res.msg = `${code} 离线验证失败`;
              res.status = "error";
              continue;
            }
            const { data: srts = [] } = await this.filesAllSRTs(file_id);
            const files = [...videos, ...srts];
            if (clean) await this.handleClean(files, file_id);
            if (tags.length) this.handleTags(videos, tags);
            if (rename) this.handleRename(files, file_id, { rename, renameTxt, zh: zh || srts.length, crack });
            if (cover) {
              try {
                const { data } = await this.handleCover(cover, file_id, `${code}.cover.jpg`);
                if (data == null ? void 0 : data.file_id) this.filesEdit(file_id, data.file_id);
              } catch (err) {
                console.warn("[Req115.handleCover]", err == null ? void 0 : err.message);
              }
            }
            res.msg = `${code} 离线任务成功`;
            res.status = "success";
            break;
          }
          return res;
        }
        static async queueMutation(name2, fn) {
          return fn();
        }
      }
      window.Req115 = Req115;
      class Fc2Plugin extends BasePlugin {
        getName() {
          return "Fc2Plugin";
        }
        async initCss() {
          return "\n            <style>\n                /* 弹层样式 */\n                .movie-detail-layer .layui-layer-title {\n                    font-size: 18px;\n                    color: #333;\n                    background: #f8f8f8;\n                }\n                \n                \n                /* 容器样式 */\n                .movie-detail-container {\n                    margin: 40px;\n                    height: 100%;\n                    background: #fff;\n                }\n                \n                .movie-poster-container {\n                    flex: 0 0 60%;\n                    padding: 15px;\n                }\n                \n                .right-box {\n                    flex: 1;\n                    padding: 20px;\n                    overflow-y: auto;\n                }\n                \n                /* 预告片iframe */\n                .movie-trailer {\n                    width: 100%;\n                    height: 100%;\n                    min-height: 400px;\n                    background: #000;\n                    border-radius: 4px;\n                }\n                \n                /* 电影信息样式 */\n                .movie-title {\n                    font-size: 24px;\n                    margin-bottom: 15px;\n                    color: #333;\n                }\n                \n                .movie-meta {\n                    margin-bottom: 20px;\n                    color: #666;\n                }\n                \n                .movie-meta span {\n                    margin-right: 15px;\n                }\n                \n                /* 演员列表 */\n                .actor-list {\n                    display: flex;\n                    flex-wrap: wrap;\n                    gap: 8px;\n                    margin-top: 10px;\n                }\n                \n                .actor-tag {\n                    padding: 4px 12px;\n                    background: #f0f0f0;\n                    border-radius: 15px;\n                    font-size: 12px;\n                    color: #555;\n                }\n                \n                /* 图片列表 */\n                .image-list {\n                    display: flex;\n                    flex-wrap: wrap;\n                    gap: 10px;\n                    margin-top: 10px;\n                }\n                \n                .movie-image-thumb {\n                    width: 120px;\n                    height: 80px;\n                    object-fit: cover;\n                    border-radius: 4px;\n                    cursor: pointer;\n                    transition: transform 0.3s;\n                }\n                \n                .movie-image-thumb:hover {\n                    transform: scale(1.05);\n                }\n                \n                /* 加载中和错误状态 */\n                .search-loading, .movie-error {\n                    padding: 40px;\n                    text-align: center;\n                    color: #999;\n                }\n                \n                .movie-error {\n                    color: #f56c6c;\n                }\n                \n                .fancybox-container{\n                    z-index:99999999\n                 }\n                 \n                 \n                 /* 错误提示样式 */\n                .movie-not-found, .movie-error {\n                    text-align: center;\n                    padding: 30px;\n                    color: #666;\n                }\n                \n                .movie-not-found h3, .movie-error h3 {\n                    color: #f56c6c;\n                    margin: 15px 0;\n                }\n                \n                .icon-warning, .icon-error {\n                    font-size: 50px;\n                    color: #e6a23c;\n                }\n                \n                .icon-error {\n                    color: #f56c6c;\n                }\n                \n                .fc2-movie-panel-info .panel-block {\n                    padding: 0 !important;\n                }\n            </style>\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            <div class="movie-detail-container">\n                <!--<div class="movie-poster-container">\n                    <iframe class="movie-trailer" frameborder="0" allowfullscreen scrolling="no"></iframe>\n                </div>-->\n               <!-- <div class="right-box">-->\n                    <div class="movie-info-container">\n                        <div class="search-loading">加载中...</div>\n                    </div>\n                    \n                    <div class="movie-panel-info fc2-movie-panel-info" style="margin-top:20px"><strong>第三方资源: </strong></div>\n                    \n                    <div style="margin: 30px 0">\n                        <a id="filterBtn" class="menu-btn" style="background-color:#de3333"><span>🚫 屏蔽</span></a>\n                        <a id="favoriteBtn" class="menu-btn" style="background-color:#25b1dc"><span>⭐ 收藏</span></a>\n                        <a id="hasDownBtn" class="menu-btn" style="background-color:#7bc73b"><span>📥️ 已下载</span></a>\n                        <a id="hasWatchBtn" class="menu-btn" style="background-color:#d7a80c;"><span>🔍 已观看</span></a>\n                        \n                        <a id="subtitleSearchBtn" class="menu-btn fr-btn" style="background:linear-gradient(to left, #375f7c, #2196F3)">\n                            <span>字幕搜索</span>\n                        </a>\n                        <!--\n                        <a id="search-subtitle-btn" class="menu-btn fr-btn" style="background:linear-gradient(to bottom, #8d5656, rgb(196,159,91))">\n                            <span>字幕 (SubTitleCat)</span>\n                        </a>\n                        <a id="xunLeiSubtitleBtn" class="menu-btn fr-btn" style="background:linear-gradient(to left, #375f7c, #2196F3)">\n                            <span>字幕 (迅雷)</span>\n                        </a>\n                        -->\n                        <a id="magnetSearchBtn" class="menu-btn fr-btn" style="width: 120px; background: linear-gradient(to right, rgb(245,140,1), rgb(84,161,29)); color: white; text-align: center; padding: 8px 0;">\n                            <span>磁力搜索</span>\n                        </a>\n                    </div>\n                    <div class="message video-panel" style="margin-top:20px">\n                        <div id="magnets-content" class="magnet-links" style="margin: 0 0.75rem">\n                            <div class="search-loading">加载中...</div>\n                        </div>\n                    </div>\n                    <div id="reviews-content">\n                    </div>\n                    <div id="related-content">\n                    </div>\n                    <span id="data-actress" style="display: none"></span>\n                <!--</div>-->\n            </div>\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: '<div id="magnetHubBox"></div>',
                  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 += `<span class="actor-tag"><a href="/actors/${actor.id}" target="_blank">${actor.name}</a></span>`;
                0 === actor.gender && (actress += actor.name + " ");
              }
              $("#data-actress").text(actress);
            } else actorsHtml = '<span class="no-data">暂无演员信息</span>';
            let imagesHtml = "";
            imagesHtml = Array.isArray(imgList) && imgList.length > 0 ? imgList.map(((img, index) => `
                <a href="${img}" data-fancybox="movie-gallery" data-caption="剧照 ${index + 1}">
                    <img src="${img}" class="movie-image-thumb"  alt=""/>
                </a>
            `)).join("") : '<div class="no-data">暂无剧照</div>';
            $(".movie-info-container").html(`
                <h3 class="movie-title"><strong class="current-title">${res.title || "无标题"}</strong></h3>
                <div class="movie-meta">
                    <span><strong>番号: </strong>${res.carNum || "未知"}</span>
                    <span><strong>年份: </strong>${res.releaseDate || "未知"}</span>
                    <span><strong>评分: </strong>${res.score || "无"}</span>
                    <span><strong>时长: </strong>${res.duration + " m" || "无"}</span>
                </div>
                <div class="movie-meta">
                    <span>
                        <strong>站点: </strong>
                        <a href="https://fc2ppvdb.com/articles/${res.carNum.replace("FC2-", "")}" target="_blank">fc2ppvdb</a>
                        <a style="margin-left: 5px;" href="https://adult.contents.fc2.com/article/${res.carNum.replace("FC2-", "")}/" target="_blank">fc2电子市场</a>
                    </span>
                </div>
                <div class="movie-actors">
                    <div class="actor-list"><strong>主演: </strong>${actorsHtml}</div>
                </div>
                <div class="movie-gallery" style="margin-top:10px">
                    <strong>剧照: </strong>
                    <div class="image-list">${imagesHtml}</div>
                </div>
                <div id="data-releaseDate" style="display: none">${res.releaseDate || ""}</div>
            `);
            this.getBean("TranslatePlugin").translate(res.carNum, false).then();
          })).catch(((err) => {
            console.error(err);
            $(".movie-info-container").html(`
                <div class="movie-error">加载失败: ${err.message}</div>
            `);
          }));
        }
        handleLongImg(carNum2) {
          utils.loopDetector((() => $(".movie-gallery .image-list").length > 0), (async () => {
            $(".movie-gallery .image-list").prepend(' <a class="tile-item screen-container" style="overflow:hidden;max-height: 150px;max-width:150px; text-align:center;"><div style="margin-top: 50px;color: #000;cursor: auto">正在加载缩略图</div></a> ');
            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 += `
                        <div class="item columns is-desktop ${oddClass}">
                            <div class="magnet-name column is-four-fifths">
                                <a href="magnet:?xt=urn:btih:${magnet.hash}" title="右鍵點擊並選擇「複製鏈接地址」">
                                    <span class="name">${magnet.name}</span>
                                    <br>
                                    <span class="meta">
                                        ${(magnet.size / 1024).toFixed(2)}GB, ${magnet.files_count}個文件 
                                     </span>
                                    <br>
                                    <div class="tags">
                                        ${magnet.hd ? '<span class="tag is-primary is-small is-light">高清</span>' : ""}
                                        ${magnet.cnsub ? '<span class="tag is-warning is-small is-light">字幕</span>' : ""}
                                    </div>
                                </a>
                            </div>
                            <div class="buttons column">
                                <button class="button is-info is-small copy-to-clipboard" data-clipboard-text="magnet:?xt=urn:btih:${magnet.hash}" type="button">&nbsp;複製&nbsp;</button>
                            </div>
                            <div class="date column"><span class="time">${magnet.created_at}</span></div>
                        </div>
                    `;
            }
            else magnetsHtml = '<span class="no-data">暂无磁力信息</span>';
            $("#magnets-content").html(magnetsHtml);
            $(".buttons button[data-clipboard-text*='magnet:']").each(((i, el) => {
              $(el).parent().append($("<button>").text("115离线下载").addClass("button is-info is-small").click((async (event) => {
                event.stopPropagation();
                event.preventDefault();
                let loadObj = loading();
                try {
                  await this.getBean("WangPan115TaskPlugin").handleAddTask($(el).attr("data-clipboard-text"));
                } catch (e) {
                  show.error("发生错误:" + e);
                  console.error(e);
                } finally {
                  loadObj.close();
                }
              })));
            }));
          })).catch(((err) => {
            console.error(err);
            $("#magnets-content").html(`
                <div class="movie-error">加载失败: ${err.message}</div>
            `);
          }));
        }
        async openFc2Page(movieId, carNum2, url) {
          const otherSitePlugin = this.getBean("OtherSitePlugin");
          let javDbUrl = await otherSitePlugin.getJavDbUrl();
          window.open(`${javDbUrl}/users/collection_codes?movieId=${movieId}&carNum=${carNum2}&url=${url}`);
        }
      }
      class HighlightMagnetPlugin extends BasePlugin {
        getName() {
          return "HighlightMagnetPlugin";
        }
        parseDateFromAttr(dateStr) {
          if (!dateStr) return 0;
          if (dateStr.length === 8) {
            const y = parseInt(dateStr.substring(0, 4), 10);
            const m = parseInt(dateStr.substring(4, 6), 10) - 1;
            const d2 = parseInt(dateStr.substring(6, 8), 10);
            const date2 = new Date(y, m, d2);
            return isNaN(date2.getTime()) ? 0 : date2.getTime();
          }
          const d = new Date(dateStr);
          return isNaN(d.getTime()) ? 0 : d.getTime();
        }
        parseSizeToBytes(sizeText) {
          if (!sizeText) return 0;
          const s = sizeText.trim().toUpperCase();
          const num = parseFloat(s);
          if (isNaN(num)) return 0;
          if (s.includes("TB")) return num * 1024 * 1024 * 1024 * 1024;
          if (s.includes("GB")) return num * 1024 * 1024 * 1024;
          if (s.includes("MB")) return num * 1024 * 1024;
          if (s.includes("KB")) return num * 1024;
          return num;
        }
        getRowInfo(row) {
          const $row = $(row);
          const nameText = $row.find(".name").text().toLowerCase();
          const tags = [];
          $row.find(".tags .tag").each((_, tag) => tags.push($(tag).text().toLowerCase()));
          const sizeMB = parseInt($row.attr("data-size"), 10) || 0;
          const files = parseInt($row.attr("data-files"), 10) || 0;
          const dateStr = $row.attr("data-date") || "";
          return {
            row,
            $row,
            nameText,
            date: this.parseDateFromAttr(dateStr),
            dateStr,
            size: sizeMB * 1024 * 1024,
            files,
            tags,
            is4K: nameText.includes("4k") || nameText.includes("[4k]") || tags.includes("4k"),
            isHD: nameText.includes("高清") || tags.includes("高清"),
            isSubtitle: nameText.includes("字幕") || nameText.includes("-c") || nameText.includes(".chs") || nameText.includes(".cht") || nameText.includes(".chi") || tags.includes("字幕"),
            isUncensored: nameText.includes("无码") || nameText.includes("無碼") || nameText.includes("-u") || nameText.includes("-uc") || tags.includes("无码") || tags.includes("無碼")
          };
        }
        getRowInfoForBus(row) {
          const $row = $(row);
          const $nameCell = $row.find("td:first-child");
          const nameText = ($nameCell.find("a:first-child").text() || "").toLowerCase().trim();
          const sizeText = ($row.find("td:nth-child(2)").text() || "").trim();
          const dateText = ($row.find("td:nth-child(3)").text() || "").trim();
          const hasHDButton = $nameCell.find("a.btn-primary").text().includes("高清");
          return {
            row,
            $row,
            nameText,
            date: this.parseDateFromAttr(dateText),
            dateStr: dateText,
            size: this.parseSizeToBytes(sizeText),
            files: 0,
            tags: [],
            is4K: nameText.includes("4k") || nameText.includes("[4k]"),
            isHD: hasHDButton || nameText.includes("高清"),
            isSubtitle: nameText.includes("字幕") || nameText.includes("-c") || nameText.includes(".chs") || nameText.includes(".cht") || nameText.includes(".chi"),
            isUncensored: nameText.includes("无码") || nameText.includes("無碼") || nameText.includes("-u") || nameText.includes("-uc")
          };
        }
        async applySortAndFilter(settings) {
          if (isJavBus$1) {
            return this.applySortAndFilterForBus(settings);
          }
          if (!isJavDb$1) return;
          const $container = $("#magnets-content");
          const $rows = $container.find(".item[data-rank]");
          if (0 === $rows.length) return;
          const rowInfos = $rows.toArray().map((row) => this.getRowInfo(row));
          const { filterHD, filter4K, filterSubtitle, filterUncensored, sortOrder } = settings;
          const anyFilterEnabled = filterHD || filter4K || filterSubtitle || filterUncensored;
          let visibleInfos = rowInfos;
          if (anyFilterEnabled) {
            visibleInfos = rowInfos.filter((info) => {
              if (filterHD && info.isHD) return true;
              if (filter4K && info.is4K) return true;
              if (filterSubtitle && info.isSubtitle) return true;
              if (filterUncensored && info.isUncensored) return true;
              return false;
            });
          }
          if (sortOrder && sortOrder.length > 0) {
            visibleInfos.sort((a, b) => {
              for (const key of sortOrder) {
                let cmp = 0;
                switch (key) {
                  case "date":
                    cmp = b.date - a.date;
                    break;
                  case "size":
                    cmp = b.size - a.size;
                    break;
                  case "files":
                    cmp = a.files - b.files;
                    break;
                }
                if (0 !== cmp) return cmp;
              }
              return 0;
            });
          }
          const container = document.getElementById("magnets-content");
          if (!container) return;
          const hiddenInfos = rowInfos.filter((info) => !visibleInfos.includes(info));
          const reviewsContainer = document.getElementById("reviewsContainer");
          const insertRef = reviewsContainer && reviewsContainer.parentNode === container ? reviewsContainer.previousElementSibling || reviewsContainer : null;
          const fragment = document.createDocumentFragment();
          visibleInfos.forEach((info) => fragment.appendChild(info.row));
          hiddenInfos.forEach((info) => fragment.appendChild(info.row));
          if (insertRef) {
            container.insertBefore(fragment, insertRef);
          } else {
            container.appendChild(fragment);
          }
          visibleInfos.forEach((info) => {
            if (info.is4K) {
              info.$row.addClass("magnet-4k-highlight");
            }
          });
          hiddenInfos.forEach((info) => $(info.row).hide());
        }
        applySortAndFilterForBus(settings) {
          const $table = $("#magnet-table");
          if (!$table.length) return;
          const allRows = $table.find("tr").toArray();
          const headerRow = allRows.find((r) => $(r).find("td").length === 0 || $(r).css("font-weight") === "bold" || $(r).css("fontWeight") === "bold" || $(r).css("font-weight") === "700");
          const dataRows = headerRow ? allRows.filter((r) => r !== headerRow) : allRows.slice(1);
          if (0 === dataRows.length) return;
          const rowInfos = dataRows.map((row) => this.getRowInfoForBus(row));
          const { filterHD, filter4K, filterSubtitle, filterUncensored, sortOrder } = settings;
          const anyFilterEnabled = filterHD || filter4K || filterSubtitle || filterUncensored;
          let visibleInfos = rowInfos;
          if (anyFilterEnabled) {
            visibleInfos = rowInfos.filter((info) => {
              if (filterHD && info.isHD) return true;
              if (filter4K && info.is4K) return true;
              if (filterSubtitle && info.isSubtitle) return true;
              if (filterUncensored && info.isUncensored) return true;
              return false;
            });
          }
          if (sortOrder && sortOrder.length > 0) {
            visibleInfos.sort((a, b) => {
              for (const key of sortOrder) {
                let cmp = 0;
                switch (key) {
                  case "date":
                    cmp = b.date - a.date;
                    break;
                  case "size":
                    cmp = b.size - a.size;
                    break;
                }
                if (0 !== cmp) return cmp;
              }
              return 0;
            });
          }
          const hiddenInfos = rowInfos.filter((info) => !visibleInfos.includes(info));
          const fragment = document.createDocumentFragment();
          visibleInfos.forEach((info) => fragment.appendChild(info.row));
          hiddenInfos.forEach((info) => fragment.appendChild(info.row));
          const insertAnchor = headerRow || $table.find("tr").first()[0];
          if (insertAnchor && insertAnchor.nextSibling) {
            $(insertAnchor).after(fragment);
          } else {
            $table.append(fragment);
          }
          visibleInfos.forEach((info) => {
            if (info.is4K) {
              info.$row.addClass("magnet-4k-highlight");
            }
          });
          hiddenInfos.forEach((info) => $(info.row).hide());
        }
        showAll() {
          if (isJavDb$1) {
            $("#magnets-content .item[data-rank]").toArray().forEach(((el) => {
              $(el).show();
              $(el).removeClass("magnet-4k-highlight");
            }));
          }
          if (isJavBus$1) {
            $("#magnet-table tr").toArray().forEach(((el) => {
              $(el).show();
              $(el).removeClass("magnet-4k-highlight");
            }));
          }
        }
        handleBus() {
          isJavBus$1 && window.isDetailPage && utils.loopDetector((() => $("#magnet-table td a").length > 0), (() => {
            const rows2 = $("#magnet-table tr"), QUALITY_KEYWORDS = ["4k", "-c", "-u", "-uc"];
            let hasHighQuality = false;
            rows2.each(((_, row) => {
              const $row = $(row), $firstTd = $row.find("td:first-child"), $firstLink = $firstTd.find("a:first-child"), $secondLink = $firstTd.find("a:nth-child(2)"), linkText = $firstLink.text().toLowerCase();
              linkText.includes("4k") && $firstLink.css("color", "#f40");
              if (QUALITY_KEYWORDS.some(((keyword) => linkText.includes(keyword))) || $secondLink.length && $secondLink.text().includes("字幕")) {
                hasHighQuality = true;
                $row.addClass("high-quality");
              }
            }));
            hasHighQuality ? rows2.each(((_, row) => {
              const $row = $(row);
              $row.hasClass("high-quality") || $row.hide();
            })) : $("#enable-magnets-filter").addClass("do-hide");
          }));
        }
      }
      class FoldCategoryPlugin extends BasePlugin {
        getName() {
          return "FoldCategoryPlugin";
        }
        async initCss() {
          const settingObj = await storageManager.getSetting();
          return `
            <style>
                #tags a.tag, .tags a.tag {
                    position:relative;
                }
                .highlight-btn {
                    position: absolute;
                    top: -10px;
                    right: -10px;
                    background-color: #4CAF50;
                    color: white;
                    border: none;
                    border-radius: 50%;
                    width: 24px;
                    height: 24px;
                    font-size: 14px;
                    line-height: 24px;
                    text-align: center;
                    cursor: pointer;
                    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
                    display: none;
                    z-index: 999;
                }
                /* 当父元素被高亮时,按钮变为其他颜色 */
                .highlighted .highlight-btn {
                    background-color: #FF5722;
                }
                /* 高亮状态下的标签样式 */
                .highlighted {
                    /* 浅黄色 */
                    border: ${settingObj.highlightedTagNumber || 1}px solid ${settingObj.highlightedTagColor || "#ce2222"} !important;
                }
            </style>
        `;
        }
        async handle() {
          if (window.isListPage && !currentHref$1.includes("advanced_search")) {
            this.highlightTag();
            utils.loopDetector((() => $("#waitCheckBtn").length), (() => {
              this.createFoldBtn();
            }), 1, 1e4, true);
            $("#tags .tag-category .tag-expand").each(((index, el) => {
              $(el).parent().hasClass("collapse") && el.click();
            }));
          }
        }
        highlightTag() {
          (async () => {
            const tags = await storageManager.getHighlightedTags();
            tags && tags.forEach(((text) => {
              $(`#tags a.tag:contains(${text})`).addClass("highlighted");
              $(`.tags a.tag:contains(${text})`).addClass("highlighted");
            }));
          })().then();
          $("#tags a.tag, .tags a.tag").hover((function() {
            const $tag = $(this), button = $('<button class="highlight-btn" title="高亮显示">★</button>');
            $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(`
            <div style="display: flex;align-items: center;flex-grow:1;justify-content: flex-end;">
                <div>已选分类: <span id="jhs-check-tag">${checkTagStr}</span></div>
                <a class="menu-btn  main-tab-btn" id="foldCategoryBtn" style="background-color:#d23e60 !important;">
                    <span></span>
                    ${foldCategoryHotKey ? ` (${foldCategoryHotKey})` : ""}
                    <i style="margin-left: 10px"></i>
                </a>

            </div>
        `);
          let $section = $("h2.section-title");
          if ($section.length > 0) {
            $section.append('\n                <div id="foldCategoryBtn">\n                    <a class="menu-btn" style="background-color:#d23e60 !important;margin-left: 20px;border-bottom:none !important;border-radius:3px;">\n                        <span></span>\n                        <i style="margin-left: 10px"></i>\n                    </a>\n                </div>\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            <style>\n                .info-tag {\n                    background-color: #ecf5ff;\n                    display: inline-block;\n                    height: 32px;\n                    padding: 0 10px;\n                    line-height: 30px;\n                    font-size: 12px;\n                    color: #409eff;\n                    border: 1px solid #d9ecff;\n                    border-radius: 4px;\n                    box-sizing: border-box;\n                    white-space: nowrap;\n                }\n            </style>\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 ? `
                    <div class="panel-block actress-info">
                        <strong>${name2}:</strong>
                        <a href="${result.url}" style="margin-left: 5px" target="_blank">
                            <span class="info-tag">${result.birthday} ${result.age}</span>
                            <span class="info-tag">${result.height} ${result.weight}</span>
                            <span class="info-tag">${result.threeSizeText} ${result.braSize}</span>
                        </a>
                    </div>
                ` : `<div class="panel-block actress-info"><a href="${this.apiUrl + name2}" target="_blank"><strong>${name2}:</strong></a></div> `;
            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 = '<div class="actress-info" style="font-size: 17px; font-weight: normal; margin-top: 5px;">无此相关演员信息</div>';
          result && (contentHtml = `
                <a class="actress-info" href="${result.url}" target="_blank">
                    <div style="font-size: 17px; font-weight: normal; margin-top: 5px;">
                        <div style="display: flex; margin-bottom: 10px;">
                            <span style="width: 300px;">出生日期: ${result.birthday}</span>
                            <span style="width: 200px;">年龄: ${result.age}</span>
                            <span style="width: 200px;">身高: ${result.height}</span>
                        </div>
                        <div style="display: flex; margin-bottom: 10px;">
                            <span style="width: 300px;">体重: ${result.weight}</span>
                            <span style="width: 200px;">三围: ${result.threeSizeText}</span>
                            <span style="width: 200px;">罩杯: ${result.braSize}</span>
                        </div>
                    </div>
                </a>
            `);
          $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('<a class="a-success" id="refresh-token-btn" style="position:fixed; right: 0; top:50%;z-index:99999">获取refresh_token</a>');
          $("#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('<div class="tool-box" style="margin-top: 10px"></div>');
          this.$contentBox.append('<div class="movie-list h cols-4 vcols-8" style="margin-top: 10px"></div>');
        }
        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 = `
            <div class="button-group" style="margin-top:18px">
                <div class="buttons has-addons" id="conditionBox">
                    <a style="padding:18px 18px !important;" class="button is-small ${"daily" === period ? "is-info" : ""}" href="/rankings/playback?period=daily">日榜</a>
                    <a style="padding:18px 18px !important;" class="button is-small ${"weekly" === period ? "is-info" : ""}" href="/rankings/playback?period=weekly">周榜</a>
                    <a style="padding:18px 18px !important;" class="button is-small ${"monthly" === period ? "is-info" : ""}" href="/rankings/playback?period=monthly">月榜</a>
                </div>
            </div>
        `;
          this.$contentBox.append(conditionHtml);
        }
        getStarRating(score) {
          let stars = "";
          const fullStars = Math.floor(score);
          for (let i = 0; i < fullStars; i++) stars += '<i class="icon-star"></i>';
          for (let i = 0; i < 5 - fullStars; i++) stars += '<i class="icon-star gray"></i>';
          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 = `
                        <span class="value">
                            <span class="score-stars">${this.getStarRating(score)}</span> 
                            &nbsp; ${score}分,由${watchedCount}人評價
                        </span>
                    `;
              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('<a href="/rankings/top"><span>Top250</span></a>');
          $('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('<div class="tool-box" style="margin-top: 10px"></div>');
          this.$contentBox.append('<div class="movie-list h cols-4 vcols-8" style="margin-top: 10px"></div>');
          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 += `<li><a class="pagination-link ${page === i ? "is-current" : ""}" data-page="${i}">${i}</a></li>`;
            }
            return `
                <nav class="pagination">
                    <a class="pagination-previous ${page <= 1 ? "do-hide" : ""}" data-page="${page - 1}">上一頁</a>
                    <a class="pagination-next ${isNextDisabled ? "do-hide" : ""}" data-page="${page + 1}">下一頁</a>
                    
                    <ul class="pagination-list">
                        ${paginationListHTML}
                    </ul>
                </nav>
            `;
          })(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(`<h3>${message}</h3>`);
              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 += `
                <a style="padding:18px 18px !important;" 
                   class="button is-small ${type_value === year.toString() ? "is-info" : ""}" 
                   href="/rankings/top?t=y${year}&has_cnsub=${this.has_cnsub}">
                  ${year}
                </a>
            `;
          let conditionHtml = `
            <div class="button-group">
                <div class="buttons has-addons" id="conditionBox" style="margin-bottom: 0!important;">
                    <a style="padding:18px 18px !important;" class="button is-small ${"all" === type ? "is-info" : ""}" href="/rankings/top?has_cnsub=${this.has_cnsub}">全部</a>
                    <a style="padding:18px 18px !important;" class="button is-small ${"0" === type_value ? "is-info" : ""}" href="/rankings/top?t=0&has_cnsub=${this.has_cnsub}">有码</a>
                    <a style="padding:18px 18px !important;" class="button is-small ${"1" === type_value ? "is-info" : ""}" href="/rankings/top?t=1&has_cnsub=${this.has_cnsub}">无码</a>
                    <a style="padding:18px 18px !important;" class="button is-small ${"2" === type_value ? "is-info" : ""}" href="/rankings/top?t=2&has_cnsub=${this.has_cnsub}">欧美</a>
                    <a style="padding:18px 18px !important;" class="button is-small ${"3" === type_value ? "is-info" : ""}" href="/rankings/top?t=3&has_cnsub=${this.has_cnsub}">Fc2</a>
                    
                    <a style="padding:18px 18px !important;margin-left: 50px" class="button is-small ${"1" === this.has_cnsub ? "is-info" : ""}" data-cnsub-value="1">含中字磁鏈</a>
                    <a style="padding:18px 18px !important;" class="button is-small ${"0" === this.has_cnsub ? "is-info" : ""}" data-cnsub-value="0">无字幕</a>
                    <a style="padding:18px 18px !important;" class="button is-small" data-cnsub-value="">重置</a>
                </div>
                
                <div class="buttons has-addons" id="conditionBox">
                    ${yearHtml}
                </div>
            </div>
        `;
          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: `
                <div style="padding: 30px; font-family: 'Helvetica Neue', Arial, sans-serif;">
                    <div style="margin-bottom: 25px;">
                        <input type="text" id="username" name="username" 
                            style="width: 100%; padding: 12px 15px; border: 1px solid #e0e0e0; border-radius: 4px; 
                                   box-sizing: border-box; transition: all 0.3s; font-size: 14px;
                                   background: #f9f9f9; color: #333;"
                            placeholder="用户名 | 邮箱"
                            onfocus="this.style.borderColor='#4a8bfc'; this.style.background='#fff'"
                            onblur="this.style.borderColor='#e0e0e0'; this.style.background='#f9f9f9'">
                    </div>
                    
                    <div style="margin-bottom: 15px;">
                        <input type="password" id="password" name="password" 
                            style="width: 100%; padding: 12px 15px; border: 1px solid #e0e0e0; border-radius: 4px; 
                                   box-sizing: border-box; transition: all 0.3s; font-size: 14px;
                                   background: #f9f9f9; color: #333;"
                            placeholder="密码"
                            onfocus="this.style.borderColor='#4a8bfc'; this.style.background='#fff'"
                            onblur="this.style.borderColor='#e0e0e0'; this.style.background='#f9f9f9'">
                    </div>
                    
                    <button id="loginBtn" 
                            style="width: 100%; padding: 12px; background: #4a8bfc; color: white; 
                                   border: none; border-radius: 4px; font-size: 15px; cursor: pointer;
                                   transition: background 0.3s;"
                            onmouseover="this.style.background='#3a7be0'"
                            onmouseout="this.style.background='#4a8bfc'">
                        登录
                    </button>
                </div>
            `,
            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            <div class="navbar-menu" id="search-box">\n                <div class="navbar-start" style="display: flex; align-items: center; gap: 5px;">\n                    <select id="search-type" style="padding: 8px 12px; border: 1px solid #555; border-radius: 4px; background-color: #333; color: #eee; font-size: 14px; outline: none;">\n                        <option value="all">影片</option>\n                        <option value="actor">演員</option>\n                        <option value="series">系列</option>\n                        <option value="maker">片商</option>\n                        <option value="director">導演</option>\n                        <option value="code">番號</option>\n                        <option value="list">清單</option>\n                    </select>\n                    <input id="search-keyword" type="text" placeholder="輸入影片番號,演員名等關鍵字進行檢索" style="padding: 8px 12px; border: 1px solid #555; border-radius: 4px; flex-grow: 1; font-size: 14px; background-color: #333; color: #eee; outline: none;">\n                    <a href="/advanced_search?noFold=1" title="進階檢索" style="padding: 6px 12px; background-color: #444; border-radius: 4px; text-decoration: none; color: #ddd; font-size: 14px; border: 1px solid #555;"><span>...</span></a>\n                    <a id="search-img-btn" style="padding: 6px 16px; background-color: #444; color: #fff; border-radius: 4px; text-decoration: none; font-weight: 500; cursor: pointer; border: 1px solid #555;">识图</a>\n                    <a id="search-btn" style="padding: 6px 16px; background-color: #444; color: #fff; border-radius: 4px; text-decoration: none; font-weight: 500; cursor: pointer; border: 1px solid #555;">檢索</a>\n                </div>\n            </div>\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            <div class="navbar-item has-dropdown is-hoverable">\n                <a class="navbar-link">其它</a>\n                <div class="navbar-dropdown is-boxed">\n                  <a class="navbar-item" href="/feedbacks/new" target="_blank" >反饋</a>\n                  <a class="navbar-item" rel="nofollow noopener" target="_blank" href="https://theporndude.com/zh">ThePornDude</a>\n                </div>\n              </div>\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                <div id="bus-preview-modal" class="bus-preview-modal">\n                    <div class="bus-preview-modal-content">\n                        </div>\n                </div>\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 = $(`
            <a class="preview-video-container sample-box" style="cursor: pointer">
                <div class="photo-frame" style="position:relative;">
                    <img src="${firstImageSrc}" class="video-cover" alt="">
                    <div class="play-icon" style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); 
                                color:white; font-size:40px; text-shadow:0 0 10px rgba(0,0,0,0.5);">
                        ▶
                    </div>
                </div>
            </a>`);
          $("#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(`
            <div class="video-player-wrapper">
                <video id="preview-video" controls playsinline>
                    <source src="${defaultVideoUrl}" />
                </video>
            </div>
            <div class="video-control-box">
                </div>
        `);
          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 += `
                    <button class="video-control-btn${isActive ? " active" : ""}" 
                            data-quality="${option.quality}"
                            data-video-src="${dmmVideoUrl}">
                        ${option.text}
                    </button>
                `;
            }
          }));
          $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/[email protected]/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 = $(`<a class="site-btn" style="min-width: auto; padding: 0 5px; margin-left: 6px; margin-bottom: 0; background-color: #1890ff; vertical-align: middle; cursor: pointer; font-size: 11px;"><span>视频预览</span></a>`);
          $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(`
                <div id="jhs-javopen-video-modal" class="jhs-javopen-video-modal">
                    <div class="jhs-javopen-video-modal-inner">
                        <div class="jhs-javopen-video-wrapper">
                            <video id="jhs-javopen-video" controls playsinline></video>
                        </div>
                        <div class="jhs-javopen-quality-bar"></div>
                    </div>
                </div>
            `);
            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('<div class="jhs-javopen-loading">视频预览加载中...</div>');
          $qualityBar.empty();
          $modal.addClass("is-open");
          let videoMap = null;
          try {
            videoMap = await getTrailer(carNum2);
          } catch (err) {
            $wrapper.html(`<div class="jhs-javopen-loading">视频预览请求失败: ${err.message}</div>`);
            return;
          }
          if (!videoMap || Object.keys(videoMap).length === 0) {
            $wrapper.html('<div class="jhs-javopen-loading">未找到可用的视频源</div>');
            return;
          }
          const qualityList = Object.keys(videoMap);
          const defaultQuality = this._selectDefaultQuality(qualityList, "720p");
          const defaultUrl = videoMap[defaultQuality];
          $wrapper.html(`<video id="jhs-javopen-video" controls playsinline>
            <source src="${defaultUrl}" />
        </video>`);
          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 += `<button class="jhs-javopen-quality-btn${option.quality === defaultQuality ? " active" : ""}"
                    data-quality="${option.quality}" data-video-src="${url}">${option.text}</button>`;
            }
          });
          $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            <style>\n                #upload-area {\n                    border: 2px dashed #85af68;\n                    border-radius: 8px;\n                    padding: 40px;\n                    text-align: center;\n                    margin-bottom: 20px;\n                    transition: all 0.3s;\n                    background-color: #f9f9f9;\n                }\n                #upload-area:hover {\n                    border-color: #76b947;\n                    background-color: #f0f0f0;\n                }\n                /* 拖拽进入 */\n                #upload-area.highlight {\n                    border-color: #2196F3;\n                    background-color: #e3f2fd;\n                }\n                \n                \n                #select-image-btn {\n                    background-color: #4CAF50;\n                    color: white;\n                    border: none;\n                    padding: 10px 20px;\n                    border-radius: 4px;\n                    cursor: pointer;\n                    font-size: 16px;\n                    transition: background-color 0.3s;\n                }\n                #select-image-btn:hover {\n                    background-color: #45a049;\n                }\n                \n                \n                #handle-btn, #cancel-btn {\n                    padding: 8px 16px;\n                    border-radius: 4px;\n                    cursor: pointer;\n                    font-size: 14px;\n                    border: none;\n                    transition: opacity 0.3s;\n                }\n                #handle-btn {\n                    background-color: #2196F3;\n                    color: white;\n                }\n                #handle-btn:hover {\n                    opacity: 0.9;\n                }\n                #cancel-btn {\n                    background-color: #f44336;\n                    color: white;\n                }\n                #cancel-btn:hover {\n                    opacity: 0.9;\n                }\n                \n                .search-img-site-btns-container {\n                    display: flex;\n                    flex-wrap: wrap;\n                    gap: 10px;\n                    margin-top: 15px;\n                }\n                .search-img-site-btn {\n                    display: flex;\n                    align-items: center;\n                    padding: 8px 12px;\n                    background-color: #f5f5f5;\n                    border-radius: 4px;\n                    text-decoration: none;\n                    color: #333;\n                    transition: all 0.2s;\n                    font-size: 14px;\n                    border: 1px solid #ddd;\n                }\n                .search-img-site-btn:hover {\n                    background-color: #e0e0e0;\n                    transform: translateY(-2px);\n                    box-shadow: 0 2px 5px rgba(0,0,0,0.1);\n                }\n                .search-img-site-btn img {\n                    width: 16px;\n                    height: 16px;\n                    margin-right: 6px;\n                }\n                .search-img-site-btn span {\n                    white-space: nowrap;\n                }\n            </style>\n        ";
        }
        open(onOpenFun) {
          layer.open({
            type: 1,
            title: "以图识图",
            content: '\n            <div style="padding: 20px">\n                <div id="upload-area">\n                    <div style="color: #555;margin-bottom: 15px;">\n                        <p>拖拽图片到此处 或 点击按钮选择图片</p>\n                        <p>也可以直接 Ctrl+V 粘贴图片或 图片URL</p>\n                    </div>\n                    <button id="select-image-btn">选择图片</button>\n                    <input type="file" style="display: none" id="image-file" accept="image/*">\n                </div>\n                \n                <div id="url-input-container" style="margin-top: 15px;display: none;">\n                    <input type="text" id="image-url" placeholder="粘贴图片URL地址..." style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">\n                </div>\n                \n                <div id="preview-area" style="margin-bottom: 20px; text-align: center; display: none;">\n                    <img id="preview-image" alt="" src="" style="max-width: 100%; max-height: 300px; border-radius: 4px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);">\n                    <div style="margin-top: 15px; display: flex; justify-content: center; gap: 10px;" id="action-btns">\n                        <button id="handle-btn">搜索图片</button>\n                        <button id="cancel-btn">取消</button>\n                    </div>\n                    \n                    <div id="search-results" style="display: none;">\n                        <p style="margin: 20px auto">请选择识图网站:<a id="openAll" style="cursor: pointer">全部打开</a></p>\n                        <div class="search-img-site-btns-container" id="search-img-site-btns-container"></div>\n                    </div>\n                </div>\n                \n            </div>\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(`
                        <a href="${siteUrl}" class="search-img-site-btn" target="_blank" title="${site.name}">
                        <input type="checkbox" 
                               class="site-checkbox" 
                               data-site-name="${site.name}" 
                               style="margin-right: 5px"
                               ${isChecked ? "checked" : ""}>
                            <img src="${site.ico}" alt="${site.name}">
                            <span>${site.name}</span>
                        </a>
                    `);
                  }));
                  $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            <button class="btn btn-default" style="color: #0d9488" id="search-img-btn">识图</button>\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(`
            <div style="display: flex; align-items: center; margin: 16px 0; color: #666; font-size: 14px;">
                <span style="flex: 1; height: 1px; background: linear-gradient(to right, transparent, #999, transparent);"></span>
                <span style="padding: 0 10px;">相关清单</span>
                <a id="relatedFold" style="margin-left: 8px; color: #1890ff; text-decoration: none; display: flex; align-items: center;">
                    <span class="toggle-text">${enableLoadRelated === YES$1 ? "折叠" : "展开"}</span>
                    <span class="toggle-icon" style="margin-left: 4px;">${enableLoadRelated === YES$1 ? "▲" : "▼"}</span>
                </a>
                <span style="flex: 1; height: 1px; background: linear-gradient(to right, transparent, #999, transparent);"></span>
            </div>
        `);
            $("#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('<div id="relatedContainer"></div>');
            $magnets.append('<div id="relatedFooter"></div>');
            enableLoadRelated === YES$1 && await this.fetchAndDisplayRelateds(movieId);
          } else show.error("未传入movieId");
        }
        async fetchAndDisplayRelateds(movieId) {
          const $relatedContainer = $("#relatedContainer"), $relatedFooter = $("#relatedFooter");
          $relatedContainer.append('<div id="relatedLoading" style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">获取清单中...</div>');
          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                <button id="loadMoreRelateds" style="width:100%; background-color: #e1f5fe; border:none; padding:10px; margin-top:10px; cursor:pointer; color:#0277bd; font-weight:bold; border-radius:4px;">\n                    加载更多清单\n                </button>\n                <div id="relatedEnd" style="display:none; text-align:center; padding:10px; color:#666; margin-top:10px;">已加载全部清单</div>\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('<div style="text-align:center; padding:10px; color:#666; margin-top:10px;">已加载全部清单</div>');
          } else $relatedContainer.append('<div style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">无清单</div>');
          else {
            $relatedContainer.append('\n                <div style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">\n                    获取清单失败\n                    <a id="retryFetchRelateds" href="javascript:;" style="margin-left: 10px; color: #1890ff; text-decoration: none;">重试</a>\n                </div>\n            ');
            $("#retryFetchRelateds").on("click", (async () => {
              $("#retryFetchRelateds").parent().remove();
              await this.fetchAndDisplayRelateds(movieId);
            }));
          }
        }
        displayRelateds(dataList, $container) {
          dataList.length && dataList.forEach(((item) => {
            let commentHtml = `
                <div class="item columns is-desktop" style="display:block;margin-top:6px;background-color:#ffffff;padding:10px;margin-left: -10px;word-break: break-word;position:relative;">
                   <span style="position:absolute;top:5px;right:10px;color:#999;font-size:12px;">#${this.floorIndex++}</span>
                   <span style="position:absolute;bottom:5px;right:10px;color:#999;font-size:12px;">创建时间: ${item.createTime}</span>
                   <p><a href="/lists/${item.relatedId}" target="_blank" style="color:#2e8abb">${item.name}</a></p>
                   <p style="margin-top: 5px;">视频个数: ${item.movieCount}</p>
                   <p style="margin-top: 5px;">收藏次数: ${item.collectionCount} 被查看次数: ${item.viewCount}</p>
                </div>
            `;
            $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('<a class="a-primary" id="wantWatchBtn" style="padding:10px;">导入至 JHS</a>');
            $("#wantWatchBtn").on("click", ((event) => {
              this.type = Status_FAVORITE$1;
              this.importWantWatchVideos(event, "是否将 想看的影片 导入到 JHS-收藏?");
            }));
          }
          if (window.location.href.includes("/watched_videos")) {
            $("h3").append('<a class="a-success" id="wantWatchBtn" style="padding:10px;">导入至 JHS</a>');
            $("#wantWatchBtn").on("click", ((event) => {
              this.type = Status_HAS_DOWN$1;
              this.importWantWatchVideos(event, "是否将 看过的影片 导入到 JHS-已下载?");
            }));
          }
        }
        importWantWatchVideos(event, title) {
          utils.q(null, `${title} <br/> <span style='color: #f40'>执行此功能前请记得备份数据</span>`, (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 `
            <style>
                .magnet-container {
                    margin: 12px auto;
                    width: 100%;
                    font-family: Arial, sans-serif;
                }
                .magnet-tabs {
                    display: flex;
                    border-bottom: 1px solid #ddd;
                    margin-bottom: 0;
                    justify-content: space-between;
                    align-items: center;
                }
                .magnet-tab {
                    padding: 4px 10px;
                    cursor: pointer;
                    border: 1px solid transparent;
                    border-bottom: none;
                    margin-right: 3px;
                    background: #f5f5f5;
                    border-radius: 5px 5px 0 0;
                    font-size: 12px;
                    transition: background .16s;
                }
                .magnet-tab.active {
                    background: #fff;
                    border-color: #ddd;
                    border-bottom: 1px solid #fff;
                    margin-bottom: -1px;
                    font-weight: bold;
                }
                .magnet-tab:hover:not(.active) {
                    background: #e9e9e9;
                }
                .magnet-controls {
                    display: flex;
                    align-items: center;
                    gap: 6px;
                    padding: 6px 10px;
                    background: #fafafa;
                    border-bottom: 1px solid #eee;
                }
                .magnet-sort-select {
                    height: 24px;
                    padding: 1px 18px 1px 5px;
                    border: 1px solid #ccc;
                    border-radius: 4px;
                    font-size: 11px;
                    color: #333;
                    background: #fff;
                    cursor: pointer;
                }
                .magnet-sort-select:disabled {
                    opacity: .5;
                    cursor: not-allowed;
                }
                .magnet-results {
                    min-height: 160px;
                }
                .magnet-table {
                    width: 100%;
                    border-collapse: collapse;
                    table-layout: fixed;
                    font-size: 12px;
                }
                .magnet-table th {
                    padding: 4px;
                    background: #f8f8f8;
                    border-bottom: 2px solid #e0e0e0;
                    font-weight: 600;
                    color: #555;
                    font-size: 11px;
                    text-align: left;
                }
                .magnet-table th.col-name { max-width: 0; }
                .magnet-table th.col-size { width: 60px; text-align: center; }
                .magnet-table th.col-date { width: 75px; text-align: center; }
                .magnet-table th.col-actions { width: 170px; text-align: center; }
                .magnet-table td {
                    padding: 5px 4px;
                    border-bottom: 1px solid #eee;
                    vertical-align: middle;
                }
                .magnet-table tr:hover td {
                    background-color: #f9f9f9;
                }
                .magnet-table td.col-size { text-align: center; white-space: nowrap; }
                .magnet-table td.col-date { text-align: center; white-space: nowrap; font-size: 11px; color: #888; }
                .magnet-table td.col-actions { text-align: center; white-space: nowrap; }
                .magnet-name-cell {
                    display: flex;
                    align-items: center;
                    gap: 4px;
                    min-width: 0;
                    overflow: hidden;
                }
                .magnet-name-cell > a {
                    flex: 1 1 auto;
                    min-width: 0;
                    display: block;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                    color: #243b67;
                    font-weight: 650;
                    text-decoration: none;
                    line-height: 1.4;
                }
                .magnet-name-cell > a:hover {
                    color: #1d4ed8;
                    text-decoration: underline;
                }
                .magnet-badge {
                    display: inline-flex;
                    align-items: center;
                    flex-shrink: 0;
                    padding: 1px 4px;
                    border-radius: 3px;
                    font-size: 10px;
                    font-weight: 800;
                    line-height: 1.4;
                    color: #fff;
                }
                .magnet-badge.mg-subtitle { background: #16a34a; }
                .magnet-badge.mg-four-k { background: #2563eb; }
                .magnet-badge.mg-cracked { background: #be123c; }
                .magnet-loading {
                    text-align: center;
                    padding: 14px;
                    color: #666;
                    font-size: 12px;
                }
                .magnet-error {
                    color: #f44336;
                    padding: 8px;
                    font-size: 12px;
                }
                .magnet-empty {
                    text-align: center;
                    padding: 14px;
                    color: #999;
                    font-size: 12px;
                }
                .magnet-empty a {
                    color: #e74c3c;
                    font-weight: bold;
                    cursor: pointer;
                }
                .magnet-hub-btn {
                    display: inline-block;
                    min-width: 36px;
                    padding: 4px 8px;
                    border-radius: 4px;
                    color: white;
                    font-weight: bold;
                    font-size: 11px;
                    text-align: center;
                    cursor: pointer;
                    transition: all 0.2s ease;
                    box-shadow: 0 2px 4px rgba(0,0,0,.12);
                    text-shadow: 0 1px 1px rgba(0,0,0,.15);
                    border: none;
                    line-height: 1.3;
                    margin-left: 3px;
                }
                .magnet-hub-btn:hover {
                    transform: translateY(-1px);
                    box-shadow: 0 3px 6px rgba(0,0,0,.18);
                    opacity: .92;
                }
                .magnet-hub-btn:active {
                    transform: translateY(0);
                    box-shadow: 0 1px 2px rgba(0,0,0,.1);
                }
                .magnet-hub-btn.copied {
                    background-color: #66bb6a;
                }
                .magnet-hub-btn.copy-btn {
                    background-color: #8b5cf6;
                }
                .magnet-hub-btn.check-btn {
                    background-color: #e11d48;
                }
                .magnet-hub-btn.down-btn {
                    background-color: #14b8a6;
                }
                .whatslink-overlay {
                    position: fixed;
                    inset: 0;
                    z-index: 2147483700;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    padding: 22px;
                    background: rgba(15,23,42,.66);
                    backdrop-filter: blur(8px);
                }
                .whatslink-modal {
                    position: relative;
                    width: min(1140px, calc(100vw - 36px));
                    max-width: 1140px;
                    max-height: calc(100vh - 36px);
                    background: transparent;
                    border: 0;
                    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
                }
                .whatslink-gallery-scene {
                    display: grid;
                    grid-template-rows: minmax(0, 1fr) auto auto;
                    row-gap: 3px;
                    width: 100%;
                    align-items: center;
                    justify-items: center;
                }
                .whatslink-gallery-visual {
                    position: relative;
                    z-index: 1;
                    width: 100%;
                    display: grid;
                    place-items: center;
                    overflow: hidden;
                }
                .whatslink-gallery-hero {
                    width: 100%;
                    height: 100%;
                    min-height: 0;
                    object-fit: contain;
                    display: block;
                }
                .whatslink-gallery-close {
                    position: absolute;
                    top: 12px;
                    right: 14px;
                    z-index: 4;
                    width: 32px;
                    height: 32px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    border: 0;
                    border-radius: 50%;
                    color: #fff;
                    background: rgba(42,45,49,.76);
                    cursor: pointer;
                    padding: 0 0 2px;
                    font-family: Arial, sans-serif;
                    font-size: 23px;
                    line-height: 1;
                }
                .whatslink-gallery-close:hover { background: rgba(22,25,28,.92); }
                .whatslink-gallery-arrow {
                    position: absolute;
                    top: 50%;
                    z-index: 3;
                    width: 34px;
                    height: 34px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    transform: translateY(-50%);
                    border: 0;
                    border-radius: 50%;
                    color: #fff;
                    background: rgba(38,42,45,.76);
                    cursor: pointer;
                    padding: 0 0 3px;
                    font-family: Arial, sans-serif;
                    font-size: 27px;
                    line-height: 1;
                }
                .whatslink-gallery-arrow:hover { background: rgba(22,25,28,.92); }
                .whatslink-gallery-prev { left: 16px; }
                .whatslink-gallery-next { right: 16px; }
                .whatslink-gallery-info {
                    position: relative;
                    z-index: 3;
                    display: flex;
                    align-items: center;
                    gap: 8px;
                    padding: 4px 10px;
                    border-radius: 4px;
                    color: #fff;
                    background: rgba(38,40,42,.68);
                    font: 11px ui-monospace, SFMono-Regular, Consolas, monospace;
                    white-space: nowrap;
                }
                .whatslink-gallery-info span + span::before { content: " · "; margin-right: 8px; color: rgba(255,255,255,.42); }
                .whatslink-gallery-thumbs {
                    position: relative;
                    z-index: 3;
                    display: flex;
                    justify-content: center;
                    gap: 3px;
                    max-width: 100%;
                    padding: 3px;
                    border-radius: 4px;
                    background: rgba(29,31,34,.68);
                }
                .whatslink-gallery-thumb {
                    width: 88px;
                    height: 50px;
                    padding: 0;
                    overflow: hidden;
                    border: 2px solid transparent;
                    border-radius: 3px;
                    background: transparent;
                    cursor: pointer;
                }
                .whatslink-gallery-thumb.active { border-color: #e3a05b; }
                .whatslink-gallery-thumb img { width: 100%; height: 100%; display: block; object-fit: cover; }
                .whatslink-gallery-empty {
                    position: absolute;
                    z-index: 2;
                    display: none;
                    width: min(420px, 72%);
                    text-align: center;
                    color: rgba(255,255,255,.74);
                }
                .whatslink-modal.no-shots .whatslink-gallery-empty { display: block; }
                .whatslink-modal.no-shots .whatslink-gallery-visual { height: min(460px, calc(100vh - 100px)); }
                .whatslink-modal.no-shots .whatslink-gallery-hero,
                .whatslink-modal.no-shots .whatslink-gallery-arrow,
                .whatslink-modal.no-shots .whatslink-gallery-thumbs { display: none; }
                .whatslink-gallery-empty-icon {
                    width: 56px;
                    height: 56px;
                    margin: 0 auto 14px;
                    display: grid;
                    place-items: center;
                    border: 1px solid rgba(255,255,255,.24);
                    border-radius: 50%;
                    color: #e3a05b;
                    font-size: 24px;
                }
                .whatslink-gallery-empty-title { margin-bottom: 7px; color: #fff; font-size: 17px; font-weight: 800; }
                .whatslink-gallery-empty-text { margin: 0; font-size: 13px; line-height: 1.6; }
                .whatslink-loading { padding: 28px; text-align: center; color: #475569; font-size: 14px; }
            </style>
        `;
        }
        createMagnetHub(keyword) {
          keyword = keyword.replace("FC2-", "");
          const $container = $('<div class="magnet-container"></div>');
          const $tabs = $('<div class="magnet-tabs"></div>');
          const key = "jhs_magnetHub_selectedEngine";
          const savedEngineId = localStorage.getItem(key);
          let defaultEngineIndex = 0;
          const $tabBox = $('<div style="display: flex; align-items: center;"></div>');
          this.searchEngines.forEach(((engine, index) => {
            const $tab = $(`<div class="magnet-tab" data-engine="${engine.id}">${engine.name}</div>`);
            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 = $(`<select class="magnet-sort-select" disabled style="margin-left:auto;margin-right:4px;">
            <option value="size" ${sortMode === "size" ? "selected" : ""}>按大小</option>
            <option value="newest" ${sortMode === "newest" ? "selected" : ""}>按最新</option>
            <option value="oldest" ${sortMode === "oldest" ? "selected" : ""}>按最旧</option>
        </select>`);
          $tabBox.append($sortSelect);
          $tabs.append($tabBox);
          $tabs.append(`<a style="margin-right: 20px;margin-top:3px" id="targetBox" href="${this.currentEngine.targetPage.replace("{keyword}", encodeURIComponent(keyword))}" target="_blank">原网页</a>`);
          $container.append($tabs);
          const $resultsContainer = $('<div class="magnet-results"></div>');
          $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(`<div class="magnet-loading">正在从 ${engine.name} 搜索 "${keyword}"...</div>`);
          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(`<div class="magnet-error">${message} <a id="retrySearch" href="#">🔄 刷新</a></div>`);
          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 = $(`<table class="magnet-table">
                <thead><tr>
                    <th class="col-name">名称</th>
                    <th class="col-size">大小</th>
                    <th class="col-date">日期</th>
                    <th class="col-actions">操作</th>
                </tr></thead>
                <tbody></tbody>
            </table>`);
            const $tbody = $table.find("tbody");
            results.forEach(((result) => {
              const quality = classifyQuality(result.title);
              let badges = "";
              if (quality.is4K) badges += '<span class="magnet-badge mg-four-k">4K</span>';
              if (quality.isChinese) badges += '<span class="magnet-badge mg-subtitle">中字</span>';
              if (quality.isCracked) badges += '<span class="magnet-badge mg-cracked">破解</span>';
              const displayDate = formatMagnetDate(result.date);
              const dnCode = extractCode(result.title);
              const $row = $(`<tr>
                    <td>
                        <div class="magnet-name-cell">
                            ${badges}
                            <a href="${result.magnet}" title="${result.title}">${result.title}</a>
                        </div>
                    </td>
                    <td class="col-size">${result.size || "未知"}</td>
                    <td class="col-date">${displayDate || "未知"}</td>
                    <td class="col-actions">
                        <button class="magnet-hub-btn copy-btn" data-magnet="${result.magnet}" data-dncode="${dnCode || ""}">复制</button>
                        <button class="magnet-hub-btn check-btn" data-magnet="${result.magnet}">验车</button>
                        <button class="magnet-hub-btn down-btn down-115" data-magnet="${result.magnet}">115离线</button>
                    </td>
                </tr>`);
              $tbody.append($row);
            }));
            $container.append($table);
          } else {
            $container.append(`<div class="magnet-empty">无搜索结果 <a id="retrySearch" href="#">🔄 刷新</a></div>`);
            $("#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 = '<div class="whatslink-modal no-shots"><div class="whatslink-loading">正在验车...</div></div>';
          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 = `<div class="whatslink-gallery-scene">
            <div class="whatslink-gallery-visual">
                <button class="whatslink-gallery-close" type="button" aria-label="关闭">×</button>
                <button class="whatslink-gallery-arrow whatslink-gallery-prev" type="button" aria-label="上一张">‹</button>
                <img class="whatslink-gallery-hero" alt="截图预览">
                <button class="whatslink-gallery-arrow whatslink-gallery-next" type="button" aria-label="下一张">›</button>
                <div class="whatslink-gallery-empty">
                    <div class="whatslink-gallery-empty-icon">?</div>
                    <div class="whatslink-gallery-empty-title">暂无截图</div>
                    <p class="whatslink-gallery-empty-text">当前资源没有可展示的截图,可以结合资源名称和文件数量判断。</p>
                </div>
            </div>
            <div class="whatslink-gallery-info">
                <span class="whatslink-gallery-name"></span>
                <span class="whatslink-gallery-count"></span>
                <span class="whatslink-gallery-index"></span>
            </div>
            <div class="whatslink-gallery-thumbs"></div>
        </div>`;
          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 = `<img src="${url}" alt="截图${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("(?<!\\w)(\\d{6}[-_]\\d{2,3})(?!\\w)"),
          /([A-Z]{1,2})(\d{3,4})/i
        ];
        for (const re of patterns) {
          const m = text.match(re);
          if (m) return m[0].toUpperCase();
        }
        return null;
      }
      function randomString(length) {
        const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        let result = "";
        for (let i = 0; i < length; i++) result += chars.charAt(Math.floor(Math.random() * chars.length));
        return result;
      }
      function RerenderSort($container, sortMode) {
        const data = $container.data("_magnetData");
        if (!data || !data.length) return;
        const results = [...data].sort((a, b) => {
          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 = $(`<table class="magnet-table">
        <thead><tr>
            <th class="col-name">名称</th>
            <th class="col-size">大小</th>
            <th class="col-date">日期</th>
            <th class="col-actions">操作</th>
        </tr></thead>
        <tbody></tbody>
    </table>`);
        const $tbody = $table.find("tbody");
        results.forEach(((result) => {
          const quality = classifyQuality(result.title);
          let badges = "";
          if (quality.is4K) badges += '<span class="magnet-badge mg-four-k">4K</span>';
          if (quality.isChinese) badges += '<span class="magnet-badge mg-subtitle">中字</span>';
          if (quality.isCracked) badges += '<span class="magnet-badge mg-cracked">破解</span>';
          const displayDate = formatMagnetDate(result.date);
          const dnCode = extractCode(result.title);
          const $row = $(`<tr>
            <td>
                <div class="magnet-name-cell">
                    ${badges}
                    <a href="${result.magnet}" title="${result.title}">${result.title}</a>
                </div>
            </td>
            <td class="col-size">${result.size || "未知"}</td>
            <td class="col-date">${displayDate || "未知"}</td>
            <td class="col-actions">
                <button class="magnet-hub-btn copy-btn" data-magnet="${result.magnet}" data-dncode="${dnCode || ""}">复制</button>
                <button class="magnet-hub-btn check-btn" data-magnet="${result.magnet}">验车</button>
                <button class="magnet-hub-btn down-btn down-115" data-magnet="${result.magnet}">115</button>
            </td>
        </tr>`);
          $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 = '<div class="jhs-loading-spinner"><div class="spinner"></div><div>正在加载预览图...</div></div>';
          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(
            '<a class="tile-item screen-container" style="overflow:hidden;max-height:215px;text-align:center;"><div style="margin-top:50px;color:#000;cursor:auto">正在加载缩略图</div></a>'
          );
          isJavBus$1 && $("#sample-waterfall .sample-box:first").after(
            '<a class="sample-box screen-container" style="overflow:hidden;height:110px;text-align:center;"><div style="margin-top:30px;color:#000;cursor:auto">正在加载缩略图</div></a>'
          );
          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(
            `<img src="${imgUrl}" alt="${title}" loading="lazy" style="width:100%;">`
          );
          isJavBus$1 && $(".screen-container").html(
            `<div class="photo-frame"><img src="${imgUrl}" style="height:inherit;width:100%;" title="${title}" alt="${title}"></div>`
          );
          $(".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(
            `<div style="${differentCss};cursor:auto;color:#000;">获取缩略图失败</div><br/><a href='#' class='retry-link'>点击重试</a> 或 <a class="check-link" href='https://javstore.net/search?q=${encodeURIComponent(carNum2)}' target='_blank'>前往确认</a>`
          ).off("click", ".retry-link").off("click", ".check-link").on("click", ".retry-link", async (e) => {
            e.stopPropagation();
            e.preventDefault();
            $(".screen-container").html(
              `<div style="${differentCss};cursor:auto;color:#000;">正在重新加载...</div>`
            );
            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");
            }
            $("<button>").text("验车").addClass("button is-small").attr("style", "background-color:#2b6cb0 !important;color:#fff !important;border:none !important;cursor:pointer;border-radius:4px;font-size:12px;padding:2px 8px;").click(((event) => {
              event.stopPropagation();
              event.preventDefault();
              this.getBean("MagnetHubPlugin").checkWhatslink(magnet);
            })).appendTo($copyBtn.parent());
            $("<button>").text("115离线下载").addClass("button is-small").attr("style", "background-color:#4a90e2 !important;color:#fff !important;border:none !important;cursor:pointer;border-radius:4px;font-size:12px;padding:2px 8px;").click((async (event) => {
              event.stopPropagation();
              event.preventDefault();
              let loadObj = loading();
              try {
                await this.handleAddTask(magnet);
              } catch (e) {
                show.error("发生错误:" + e);
                console.error(e);
              } finally {
                loadObj.close();
              }
            })).appendTo($copyBtn.parent());
          }));
          isJavBus$1 && window.isDetailPage && utils.loopDetector((() => $("#magnet-table td a").length > 0), (() => {
            this.bus115Down();
          }));
        }
        async bus115Down() {
          const $headerRow = $("#magnet-table tr").first();
          $("#magnet-table tr").each(((i, row) => {
            if (row === $headerRow[0]) return;
            const magnetLink = $(row).find("td:nth-child(1) a").attr("href");
            if (magnetLink && magnetLink.includes("magnet:")) {
              const actionCell = $("<td>").addClass("action-cell");
              $("<button>").text("验车").addClass("button is-small").attr("style", "background-color:#2b6cb0 !important;color:#fff !important;border:none !important;margin-right:4px;cursor:pointer;border-radius:4px;font-size:12px;padding:2px 8px;").click(((event) => {
                event.stopPropagation();
                event.preventDefault();
                this.getBean("MagnetHubPlugin").checkWhatslink(magnetLink);
              })).appendTo(actionCell);
              $("<button>").text("115离线下载").addClass("button is-small").attr("style", "background-color:#4a90e2 !important;color:#fff !important;border:none !important;cursor:pointer;border-radius:4px;font-size:12px;padding:2px 8px;").click((async (event) => {
                event.stopPropagation();
                event.preventDefault();
                let loadObj = loading();
                try {
                  await this.handleAddTask(magnetLink);
                } catch (e) {
                  show.error("发生错误:" + e);
                  console.error(e);
                } finally {
                  loadObj.close();
                }
              })).appendTo(actionCell);
              $(row).append(actionCell);
            }
          }));
          if ($headerRow.find("td, th").last().text().trim() !== "操作") {
            $headerRow.append($("<td>").text("操作"));
          }
        }
        static sanitizeDirName(str) {
          if (!str) return "";
          return str.replace(/[\\/:*?"<>|]/g, "").replace(/\s+/g, " ").trim();
        }
        static parseOfflineDirTemplate(str, params) {
          if (!str || !params) return str;
          return str.replace(/\$\{(\w+)\}/g, (match, key) => {
            if (!params.hasOwnProperty(key)) return match;
            const value = params[key] || "";
            return WangPan115TaskPlugin.sanitizeDirName(String(value));
          });
        }
        static async ensureDir(dirName, pid = 0) {
          const result = await gmHttp.postFileFormData("https://webapi.115.com/files/add", {
            pid,
            cname: dirName
          });
          if (result && result.file_id) return result.file_id;
          const searchRes = await searchFiles(dirName, 0, 1, { fc: 1 });
          if (searchRes && searchRes.data && searchRes.data.length > 0) {
            const match = searchRes.data.find((d) => d.pid === pid || d.pid === String(pid));
            if (match) return match.fid || match.file_id;
          }
          return null;
        }
        static async registerDirAsDownPath(fileId, dirName) {
          await gmHttp.postFileFormData("https://webapi.115.com/offine/downpath", {
            file_id: fileId
          });
          const list2 = await getDownPathList();
          const created = list2 ? list2.find((d) => d.file_id === fileId || String(d.file_id) === String(fileId)) : null;
          return created ? created.id : null;
        }
        static async createMultiLevelDownPath(templatePath) {
          const segments = templatePath.split("/").filter((s) => s.trim());
          if (segments.length === 0) return null;
          let pid = 0;
          let fileId = null;
          for (const seg of segments) {
            fileId = await WangPan115TaskPlugin.ensureDir(seg.trim(), pid);
            if (!fileId) {
              console.error("[115离线] 创建目录失败:", seg);
              return null;
            }
            pid = fileId;
          }
          const lastDirName = segments[segments.length - 1].trim();
          const downPathId = await WangPan115TaskPlugin.registerDirAsDownPath(fileId, lastDirName);
          console.log("[115离线] 多级目录创建完成:", templatePath, "→ downPathId=" + downPathId);
          return downPathId;
        }
        getPageInfoForOffline() {
          const baseInfo = this.getPageInfo();
          const carNum2 = baseInfo.carNum || "";
          let title = "", maker = "", actor = "";
          if (isJavBus$1) {
            title = $("h3").first().text().trim();
            maker = $('span.header:contains("製作商:")').parent("p").text().replace("製作商:", "").trim() || $('span.header:contains("制作商:")').parent("p").text().replace("制作商:", "").trim();
            const actorEl = $('span.genre a[href*="/star/"]').first();
            actor = actorEl.length ? actorEl.text().trim() : "";
          } else {
            title = $(".current-title").first().text().trim() || $("h2.title.is-4 .current-title").first().text().trim() || $(".movie-title").first().text().trim();
            maker = $('strong:contains("片商:")').parent().find(".value").text().trim() || $('strong:contains("片商:")').parent().find(".value").text().trim();
            actor = $(".actor-female").first().text().trim() || $('strong:contains("演員:"), strong:contains("演员:")').parent().find(".value a").first().text().trim() || "";
          }
          const now = /* @__PURE__ */ new Date();
          if (carNum2 && title) {
            const upperTitle = title.toUpperCase();
            const upperCode = carNum2.toUpperCase();
            if (upperTitle.startsWith(upperCode)) {
              title = title.substring(carNum2.length).replace(/^[\s\-_]+/, "").trim();
            }
          }
          console.log("[115离线] 页面信息提取:", { code: carNum2, title, maker, actor });
          return {
            code: carNum2,
            title,
            maker,
            actor,
            date: utils.formatDate(now).split(" ")[0]
          };
        }
        async getSavePathId(nyName) {
          const enableCustom = await storageManager.getSetting("enable115OfflineFormat", NO);
          if (enableCustom === YES) {
            const template = await storageManager.getSetting("offlineFolderFormat", "");
            if (template && template.includes("${")) {
              const pageInfo = this.getPageInfoForOffline();
              return WangPan115TaskPlugin.parseOfflineDirTemplate(template, pageInfo);
            }
          }
          let savePath115 = await storageManager.getSetting("savePath115", "云下载");
          nyName && (savePath115 = savePath115.replaceAll("{ny}", nyName));
          savePath115 = savePath115.replaceAll("{date}", utils.formatDate(/* @__PURE__ */ new Date()));
          return savePath115;
        }
        async handleAddTask(magnetLink, savePath) {
          const singInfo = await (async () => {
            const res = await gmHttp.get("https://115.com/?ct=offline&ac=space&_=" + (/* @__PURE__ */ new Date()).getTime());
            return "object" == typeof res ? res : null;
          })();
          if (!singInfo) {
            show.error("未登录115网盘", {
              close: true,
              duration: -1,
              callback: async () => {
                window.open("https://115.com");
              }
            });
            return;
          }
          const sign = singInfo.sign, time2 = singInfo.time, userId = this.getUserId();
          let wp_path_id = savePath || "";
          if (!wp_path_id) {
            const fullPath = await this.getSavePathId();
            console.log("[115离线] 目录模板解析结果:", fullPath);
            if (fullPath) {
              if (fullPath.includes("/")) {
                console.log("[115离线] 检测到多级目录,逐级创建:", fullPath);
                const newId = await WangPan115TaskPlugin.createMultiLevelDownPath(fullPath);
                if (newId) wp_path_id = newId;
              } else {
                const downPathList = await getDownPathList();
                if (downPathList && downPathList.length > 0) {
                  const matched = downPathList.find(((d) => d.name === fullPath));
                  if (matched) {
                    wp_path_id = matched.id;
                    console.log('[115离线] 目录匹配结果: 匹配到 "' + matched.name + '" -> id=' + matched.id);
                  } else {
                    console.log('[115离线] 目录匹配结果: 未匹配到 "' + fullPath + '",自动创建并注册');
                    const newId = await WangPan115TaskPlugin.createMultiLevelDownPath(fullPath);
                    if (newId) wp_path_id = newId;
                  }
                }
              }
            }
          }
          const result = await (async (magnet, wp_path_id2 = "", uid, sign2, time3) => {
            const data = {
              url: encodeURIComponent(magnet),
              wp_path_id: wp_path_id2,
              uid,
              sign: sign2,
              time: time3
            };
            return await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=add_task_url", data);
          })(magnetLink, wp_path_id, userId, sign, time2);
          console.log("离线下载返回值:", result);
          let infoHash = result.info_hash, fileId = await this.getFileId(userId, sign, time2, infoHash), openUrl = "https://115.com/?tab=offline&mode=wangpan";
          fileId && (openUrl = `https://115.com/?cid=${fileId}&offset=0&mode=wangpan`);
          let title = "添加成功, 是否前往查看?";
          false === result.state && (title = result.error_msg + " 是否前往查看?");
          utils.q(null, title, (async () => {
            let fileId2 = await this.getFileId(userId, sign, time2, infoHash);
            fileId2 && (openUrl = `https://115.com/?cid=${fileId2}&offset=0&mode=wangpan`);
            window.open(openUrl);
          }));
        }
        async getUserId() {
          let downPathList = await getDownPathList();
          if (downPathList && downPathList.length > 0) return downPathList[0].id;
          {
            show.info("没有默认离线目录, 正在创建中...");
            const dirId = (await (async (dirName, pid = 0) => {
              const data = {
                pid,
                cname: dirName
              };
              return await gmHttp.postFileFormData("https://webapi.115.com/files/add", data);
            })("云下载")).file_id;
            await (async (dirId2) => {
              const data = {
                file_id: dirId2
              };
              return await gmHttp.postFileFormData("https://webapi.115.com/offine/downpath", data);
            })(dirId);
            show.info("创建完成, 开始执行离线下载");
            downPathList = await getDownPathList();
            if (downPathList && downPathList.length > 0) return downPathList[0].id;
            throw new Error("获取115用户Id失败");
          }
        }
        async getFileId(userId, sign, time2, infoHash) {
          const taskList = await (async (uid, sign2, time3) => {
            const data = {
              page: 1,
              uid,
              sign: sign2,
              time: time3
            };
            return (await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=task_lists", data)).tasks;
          })(userId, sign, time2);
          console.log("云离线列表:", taskList);
          let fileId = null;
          for (let i = 0; i < taskList.length; i++) {
            let task = taskList[i];
            if (task.info_hash === infoHash) {
              fileId = task.file_id;
              break;
            }
          }
          return fileId;
        }
        //TODO: 暂时没有删除离线任务
        // async deleteOfflineTasksByKeyword(keyword) {
        //     try {
        //         const singInfo = await (async () => {
        //             const res = await gmHttp.get("https://115.com/?ct=offline&ac=space&_=" + (new Date).getTime());
        //             return "object" == typeof res ? res : null;
        //         })();
        //         if (!singInfo) {
        //             console.error("删除离线任务: 未登录115网盘");
        //             return;
        //         }
        //         const {sign: sign, time: time} = singInfo, uid = singInfo.uid || (() => {
        //             const match = document.cookie.match(/UID=(\d+)_/);
        //             return match ? match[1] : null;
        //         })() || await this.getUserId();
        //         const taskListRes = await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=task_lists", {
        //             page: 1,
        //             uid: uid,
        //             sign: sign,
        //             time: time
        //         });
        //         const tasks = (null == taskListRes ? void 0 : taskListRes.tasks) || [];
        //         const keyword2 = keyword.toLowerCase().replace("fc2-", "");
        //         let deletedCount = 0;
        //         for (const task of tasks) {
        //             if (task.name && task.name.toLowerCase().includes(keyword2)) {
        //                 console.log(`删除离线任务: ${task.name} (hash: ${task.info_hash})`);
        //                 await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=task_del", {
        //                     "hash[0]": task.info_hash,
        //                     flag: 1,
        //                     uid: uid,
        //                     sign: sign,
        //                     time: time
        //                 });
        //                 deletedCount++;
        //             }
        //         }
        //         deletedCount > 0 && console.log(`共删除 ${deletedCount} 个离线任务`);
        //     } catch (error) {
        //         console.error("删除离线任务失败:", error);
        //     }
        // }
      }
      class WangPan115Plugin extends BasePlugin {
        constructor() {
          super(...arguments);
          __publicField2(this, "JHS_115_COOKIE", "jhs_115_cookie");
          __publicField2(this, "JHS_115_MAX_AGE", "jhs_115_max_age");
        }
        getName() {
          return "WangPan115Plugin";
        }
        async initCss() {
          return "\n            <style>\n                .login-box .ltab-office {\n                    border: 1px solid #DEE4EE;\n                }\n                \n                .change-bg::before {\n                    background-color:#F9FAFB !important;\n                }\n                \n                .site-login-wrap {\n                    height: auto;\n                }\n                \n                #jhs-cookie-panel {\n                    width: 200px;\n                    position: fixed;\n                    bottom: 20px;\n                    right: 20px;\n                    z-index: 10000;\n                    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n                    cursor: pointer;\n                    background-color: #FFFFFF;\n                    color: #333333;\n                    padding: 0;\n                    border-radius: 6px;\n                    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n                    transition: all 0.3s ease;\n                    border: 1px solid #E0E0E0;\n                }\n    \n                #jhs-cookie-panel.expanded {\n                    padding: 0;\n                    border-radius: 8px;\n                    background-color: #FFFFFF;\n                    color: #333333;\n                    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);\n                }\n    \n                #jhs-cookie-header {\n                    padding: 10px 15px;\n                    background-color: #0078D4;\n                    color: white;\n                    border-radius: 6px 6px 0 0;\n                    display: flex;\n                    justify-content: space-between;\n                    align-items: center;\n                    font-weight: 600;\n                }\n                \n                #jhs-cookie-panel:not(.expanded) #jhs-cookie-header {\n                    border-radius: 6px;\n                    padding: 8px 15px;\n                }\n    \n                #jhs-cookie-content {\n                    max-height: 0;\n                    overflow: hidden;\n                    transition: max-height 0.3s ease-out;\n                    padding: 0 15px;\n                }\n    \n                #jhs-cookie-panel.expanded #jhs-cookie-content {\n                    max-height: 250px;\n                    padding: 15px;\n                }\n    \n                #jhs-cookie-value {\n                    max-height: 100px;\n                    overflow-y: auto;\n                    white-space: pre-wrap;\n                    word-break: break-all;\n                    margin-bottom: 15px;\n                    padding: 10px;\n                    border: 1px solid #CCCCCC;\n                    background-color: #F8F8F8;\n                    font-size: 12px;\n                    border-radius: 4px;\n                    color: #555;\n                }\n    \n                #jhs-copy-btn {\n                    background-color: #10B981;\n                    color: white;\n                    border: none;\n                    padding: 8px 15px;\n                    text-align: center;\n                    text-decoration: none;\n                    display: inline-block;\n                    font-size: 14px;\n                    margin: 0;\n                    cursor: pointer;\n                    border-radius: 4px;\n                    width: 100%;\n                    font-weight: 600;\n                    transition: background-color 0.2s ease;\n                }\n                \n                #jhs-copy-btn:hover {\n                    background-color: #059669;\n                }\n            </style>\n        ";
        }
        async handle() {
          if (!currentHref$1.includes("&ac=userfile") && currentHref$1.includes("115")) {
            utils.loopDetector((() => $("#js-login-box").length > 0), (() => {
              if (0 !== $("#js-login-box").length) {
                this.reLogin();
                this.hookPage();
                this.bindClick();
              }
            }), 20, 4e3, true);
            this.createCookiePanel();
          }
        }
        reLogin() {
          utils.loopDetector((() => $(".login-finished").length > 0), (() => {
            if ($(".login-finished").length > 0 || 0 === $("#js-login-box").length) return;
            const jhs_115_cookie = localStorage.getItem(this.JHS_115_COOKIE), jhs_115_max_age = localStorage.getItem(this.JHS_115_MAX_AGE);
            document.cookie.includes("SEID") || null === jhs_115_cookie || utils.q(null, "检测到上次登录已有缓存cookie, 是否使用并登录?", (() => {
              utils.addCookie(jhs_115_cookie, {
                maxAge: parseInt(jhs_115_max_age),
                domain: ".115.com"
              });
              window.location.href = "https://115.com/?cid=0&offset=0&mode=wangpan";
            }));
          }), 20, 1500, true);
        }
        hookPage() {
          const $cookieTab = $('<a id="jhs-cookie"><s>🔰 JHS-扫码</s></a>');
          $(".ltab-office").after($cookieTab);
          const $cookieScene = $(`
            <div id="jhs_cookie_box" style="display: none; padding: 0 20px; max-width: 300px; margin: auto;">
                <div style="margin-bottom: 15px; text-align: center;">
                    <span style="font-size: 18px; font-weight: bold; color: #333; display: block; margin-bottom: 10px;"> 使用115App扫码登录 </span>
                    <div style="text-align: left;">
                        <select id="login-115-type" style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid #ddd; font-size: 14px; box-sizing: border-box; background-color: white;">
                            <option value="" style="color: #999;">请选择登录方式</option>
                            <option value="wechatmini">微信小程序</option>
                            <option value="alipaymini">支付宝小程序</option>
                        </select>
                    </div>
                </div>
                
                <div style="text-align: left;">
                    <select id="cookie-expiry-select" style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid #ddd; font-size: 14px; box-sizing: border-box; background-color: white;">
                        ${[{
      label: "有效期: 会话 (关闭浏览器)",
      value: 0
    }, {
      label: "有效期: 1 天",
      value: 86400
    }, {
      label: "有效期: 7 天",
      value: 604800
    }, {
      label: "有效期: 30 天",
      value: 2592e3,
      default: true
    }, {
      label: "有效期: 60 天",
      value: 5184e3
    }, {
      label: "有效期: 180 天",
      value: 15552e3
    }].map(((c) => `<option value="${c.value}"  ${c.default ? "selected" : ""} > ${c.label} </option>`)).join("")}
                    </select>
                </div>
                
                <div id="qrcode-box" style="display: none; justify-content:center; min-height: 100px; border: 1px dashed #aaa; padding: 15px; text-align: center; margin-top: 15px; border-radius: 4px; background-color: #fff; line-height: 70px; color: #666;">
                    二维码占位区域
                </div>
                
                                
                <div style="margin-bottom: 15px; text-align: center; margin-top:50px">
                    <span style="font-size: 18px; font-weight: bold; color: #333; display: block; margin-bottom: 10px;">已有Cookie? 在此输入并回车</span>
                    <div style="text-align: left;">
                        <input type="text" id="cookie-str-input" style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid #ddd; font-size: 14px; box-sizing: border-box; background-color: white;">
                    </div>
                     <div style="text-align: center;margin-top:5px">
                        <a class="a-primary" id="submit-cookie-btn">提交</a>
                    </div>
                </div>
            </div>
        `);
          $("#js-login_box").find(".login-footer").before($cookieScene);
        }
        bindClick() {
          $("#jhs-cookie").on("click", (() => {
            const finishedEl = document.querySelector('[lg_rel="finished"]');
            if (finishedEl) finishedEl.style.display = "none";
            else {
              document.querySelector('[lg_rel="qrcode"]').style.display = "none";
              document.querySelector(".login-footer").style.display = "none";
              document.querySelector(".list-other-login").style.display = "none";
            }
            document.querySelectorAll("#js-login_way > *").forEach(((tab) => {
              tab.classList.remove("current");
            }));
            document.querySelector("#jhs_cookie_box").style.display = "block";
            $("#jhs-cookie").css("background", "#fff");
            $(".ltab-cloud").addClass("change-bg");
          }));
          $(".ltab-cloud").on("click", (() => {
            document.querySelector("#jhs_cookie_box").style.display = "none";
            const finishedEl = document.querySelector('[lg_rel="finished"]');
            if (finishedEl) finishedEl.style.display = "flex";
            else {
              document.querySelector('[lg_rel="qrcode"]').style.display = "block";
              document.querySelector(".login-footer").style.display = "block";
              document.querySelector(".list-other-login").style.display = "block";
            }
            $("#jhs-cookie").css("background", "#F9FAFB");
            $(".ltab-cloud").removeClass("change-bg");
          }));
          let loginTimeout = null;
          $("#login-115-type").on("change", (async (event) => {
            let login115Type = $("#login-115-type").val();
            if (!login115Type) return;
            const loginInfo = (await (async (loginType) => {
              let url = `https://qrcodeapi.115.com/api/1.0/${loginType}/1.0/token/`;
              return await gmHttp.get(url);
            })(login115Type)).data, qrcode = loginInfo.qrcode, sign = loginInfo.sign, time2 = loginInfo.time, uid = loginInfo.uid;
            console.log("生成二维码:", loginInfo);
            const $qrcodeBox = $("#qrcode-box");
            $qrcodeBox.css("display", "flex");
            $qrcodeBox.html("");
            new QRCode($qrcodeBox[0], {
              text: qrcode,
              width: 150,
              height: 150,
              correctLevel: QRCode.CorrectLevel.H
            });
            loginTimeout && clearTimeout(loginTimeout);
            const checkLoginRecursive = async () => {
              try {
                const loginResult = await (async (uid2, time3, sign2) => {
                  let url = `https://qrcodeapi.115.com/get/status/?uid=${uid2}&time=${time3}&sign=${sign2}`;
                  return await gmHttp.get(url);
                })(uid, time2, sign);
                console.log("已扫码, 正在获取结果:", loginResult);
                let data = loginResult.data, msg = data.msg, status = data.status;
                if (msg) {
                  console.log(msg);
                  show.info(msg);
                }
                if (2 === status) {
                  show.ok("扫码登录成功");
                  const checkResult = await (async (loginType, uid2) => {
                    const data2 = {
                      app: loginType,
                      account: uid2
                    }, url = `https://passportapi.115.com/app/1.0/${loginType}/1.0/login/qrcode/`;
                    return await gmHttp.postFileFormData(url, data2);
                  })(login115Type, uid);
                  console.log("扫码登录成功:", checkResult);
                  if (checkResult.data && checkResult.data.cookie) {
                    const cookie = checkResult.data.cookie, cookieStr = `UID=${cookie.UID}; CID=${cookie.CID}; SEID=${cookie.SEID}; KID=${cookie.KID}`;
                    console.log("解析出cookie:", cookieStr);
                    localStorage.setItem(this.JHS_115_COOKIE, cookieStr);
                    localStorage.setItem(this.JHS_115_MAX_AGE, $("#cookie-expiry-select").val());
                    window.location.href = "https://115.com/?cid=0&offset=0&mode=wangpan";
                  }
                  return;
                }
                loginTimeout = setTimeout(checkLoginRecursive, 500);
              } catch (error) {
                console.error("登录检查失败:", error);
              }
            };
            await checkLoginRecursive();
          }));
          const handleCookie = () => {
            const cookieStr = cookieInput.value, expirySelect = document.getElementById("cookie-expiry-select");
            let maxAge = parseInt(expirySelect.value);
            utils.addCookie(cookieStr, {
              maxAge,
              domain: ".115.com"
            });
            window.location.href = "https://115.com/?cid=0&offset=0&mode=wangpan";
          }, cookieInput = document.getElementById("cookie-str-input");
          cookieInput.addEventListener("keydown", (function(event) {
            if ("Enter" === event.key) {
              event.preventDefault();
              handleCookie();
            }
          }));
          $("#submit-cookie-btn").on("click", (() => {
            handleCookie();
          }));
        }
        showMessage(message) {
          const toast = document.createElement("div");
          toast.textContent = message;
          toast.style.cssText = "\n            position: fixed;\n            top: 20px;\n            right: 20px;\n            background-color: #333;\n            color: white;\n            padding: 10px 20px;\n            border-radius: 5px;\n            z-index: 20000;\n            opacity: 0;\n            transition: opacity 0.5s ease-in-out;\n        ";
          document.body.appendChild(toast);
          setTimeout((() => {
            toast.style.opacity = "1";
          }), 10);
          setTimeout((() => {
            toast.style.opacity = "0";
            setTimeout((() => toast.remove()), 500);
          }), 3e3);
        }
        createCookiePanel() {
          const cookieValue = localStorage.getItem(this.JHS_115_COOKIE);
          if (!cookieValue) return;
          const panel = document.createElement("div");
          panel.id = "jhs-cookie-panel";
          panel.innerHTML = `
            <div id="jhs-cookie-header">
                <span>JHS-115-Cookie</span>
                <span id="jhs-toggle-icon">▼</span>
            </div>
            <div id="jhs-cookie-content">
                <div id="jhs-cookie-value">${cookieValue}</div>
                <button id="jhs-copy-btn">复制 Cookie</button>
            </div>
        `;
          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            <style>\n                [class^='jhs-match-'] {\n                    padding: 1px 2px;\n                    margin-left: 0;\n                    margin-right: 5px;\n                }\n                \n                .jhs-match-detail {\n                    display: inline-block;\n                    width: 50%;\n                    z-index: 1000;\n                    background: #fff;\n                    border: 1px solid #ddd;\n                    border-radius: 4px;\n                    padding: 10px;\n                    overflow-y: auto;\n                }\n                .jhs-match-detail.isListPage{\n                    position: absolute;\n                    box-shadow: 0 2px 10px rgba(0,0,0,0.2);\n                }\n                .jhs-match-detail table {\n                    width: 100%;\n                    border-collapse: collapse;\n                }\n                .jhs-match-detail th, .jhs-match-detail td {\n                    padding: 4px 8px;\n                    border: 1px solid #eee;\n                    text-align: left;\n                }\n                .jhs-match-detail th {\n                    background-color: #f5f5f5;\n                }\n                .jhs-match-detail tr:hover {\n                    background-color: #f9f9f9;\n                }\n            </style>\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(`<a class='jhs-match-btn' title="匹配中...">匹配中...</a>`);
            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            <div class="jhs-match-detail jhs-115-match-detail" id="115-match-table">\n                <table>\n                    <thead>\n                        <tr style="text-align: center">\n                            <th colspan="4">115匹配</th>\n                        </tr>\n                        <tr>\n                            <th>名称</th>\n                            <th>大小</th>\n                            <th>时间</th>\n                            <th>播放</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                    </tbody>\n                </table>\n            </div>\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(`<tr><td colspan="4">
                     <a class='jhs-match-no-login-btn a-info'
                        data-keyword="${carNum2}"
                        title="未登录115网盘">未登录</a>
                 </td></tr>`);
            else if (matchList.length > 0) {
              const rowsHtml = matchList.map(((match) => `
                <tr>
                    <td>${match.name}</td>
                    <td>${this.formatSize(match.size)}</td>
                    <td>${match.createTime}</td>
                    <td>
                        <a href="https://115vod.com/?pickcode=${match.videoId}&share_id=0"
                           target="_blank"
                           class="a-success"
                           title="播放">播放</a>
                    </td>
                </tr>
            `)).join("");
              $tbody.append(rowsHtml);
              $detail.css("position", "relative");
              $detail.append(`<span class="jhs-detail-delete-btn" 
                    data-match='${JSON.stringify(matchList)}'
                    style="position: absolute; top: 8px; right: 12px; cursor: pointer; color: #e74c3c; font-size: 16px; font-weight: bold; z-index: 1;" 
                    title="删除115作品">🗑️ 删除</span>`);
            } else $tbody.append(`<tr><td colspan="4">
                     <a class='jhs-match-detail-error-btn a-info'
                        data-keyword="${carNum2}"
                        title="未匹配,点击重试">未匹配</a>
                 </td></tr>`);
          } catch (error) {
            $tbody.append(`<tr><td colspan="4">
                 <a class="a-danger jhs-match-detail-error-btn" title="${error.message || "加载失败"}">加载失败,请重试</a>
             </td></tr>`);
            console.error("加载文件列表时发生错误:", error);
          }
          if (isJavDb$1) if ($("#all-match-box").length) $("#all-match-box").append($detail);
          else {
            $("#tabs-container").before("<div style='display: flex' id='all-match-box'></div>");
            $("#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 = $(`
            <div class="jhs-match-detail isListPage">
                <table>
                    <thead>
                        <tr>
                            <th>名称</th>
                            <th>大小</th>
                            <th>时间</th>
                            <th>播放</th>
                        </tr>
                    </thead>
                    <tbody>
                        ${matches.map(((match) => `
                            <tr>
                                <td>${match.name}</td>
                                <td>${this.formatSize(match.size)}</td>
                                <td>${match.createTime}</td>
                                <td>
                                    <a href="https://115vod.com/?pickcode=${match.videoId}&share_id=0" 
                                       target="_blank" 
                                       class="a-success"
                                       title="播放">播放</a>
                                </td>
                            </tr>
                        `)).join("")}
                    </tbody>
                </table>
            </div>
        `);
          $("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(`<a class='jhs-match-btn' title="匹配中...">匹配中...</a>`);
          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(`<a class='jhs-match-btn a-success' 
                   data-keyword="${carNum2}"
                   data-match='${JSON.stringify(matchList)}'
                   title="点击查看匹配详情">匹配${matchList.length}个</a>`);
            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(`<a class='jhs-match-error-btn a-info' data-keyword="${carNum2}" 
                  title="点击重新尝试匹配">未匹配</a>`);
            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 = '<div style="padding: 15px; max-height: 400px; overflow-y: auto;">';
          html2 += `<div style="margin-bottom: 12px; font-size: 14px;">作品 <b>${carNum2}</b> 匹配到 <b>${matchList.length}</b> 个文件夹(可删除 <b>${safeMatchList.length}</b> 个):</div>`;
          if (unsafeCount > 0) {
            html2 += `<div style="margin-bottom: 10px; padding: 6px 10px; background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; font-size: 12px; color: #856404;">⚠ 已自动排除 ${unsafeCount} 个目录名不含番号的项目,防止误删</div>`;
          }
          html2 += '<div style="margin-bottom: 10px;"><label style="cursor: pointer;"><input type="checkbox" class="jhs-delete-select-all" checked> <b>全选</b></label></div>';
          for (let i = 0; i < safeMatchList.length; i++) {
            const m = safeMatchList[i];
            html2 += `<div style="margin-bottom: 6px; padding: 8px 10px; border: 1px solid #e0e0e0; border-radius: 4px; background: #fafafa;">
                <label style="display: flex; align-items: flex-start; cursor: pointer; width: 100%;">
                    <input type="checkbox" class="jhs-delete-folder-cb" data-index="${i}" checked style="margin-top: 3px; flex-shrink: 0;">
                    <span style="margin-left: 8px; flex: 1;">
                        <div style="word-break: break-all;">${m.name}</div>
                        <div style="font-size: 12px; color: #888; margin-top: 2px;">${formatSize(m.size)} | ${m.createTime}</div>
                    </span>
                </label>
            </div>`;
          }
          html2 += "</div>";
          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(`<a class='jhs-match-error-btn' data-keyword="${carNum2}" 
              title="匹配失败,点击重试">匹配失败</a>`);
          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(`<a class='jhs-match-no-login-btn a-info' 
                   data-keyword="${carNum2}" 
                   title="未登录115网盘">未登录</a>`);
          else if (matchList.length > 0) {
            const title = 1 === matchList.length ? "点击直接播放" : `点击查看${matchList.length}个匹配结果`;
            $box2.find(".video-title").prepend(`<a class='jhs-match-btn a-success' 
                       data-keyword="${carNum2}"
                       data-match='${JSON.stringify(matchList)}'
                       title="${title}">匹配${matchList.length}个</a>`);
            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(`<a class='jhs-match-error-btn a-info' 
                   data-keyword="${carNum2}" 
                   title="未匹配,点击重试">未匹配</a>`);
        }
        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(`(?<![a-z])${pattern}(?!\\d)`, "i")
        };
      };
      class FavoriteActressesPlugin extends BasePlugin {
        getName() {
          return "FavoriteActressesPlugin";
        }
        async initCss() {
          const settingObj = await storageManager.getSetting();
          const tagNumber = settingObj.highlightedTagNumber || 1;
          const tagColor = settingObj.highlightedTagColor || "#ce2222";
          return `
            <style>
                .highlighted {
                    border: ${tagNumber}px solid ${tagColor} !important;
                }
            </style>
        `;
        }
        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 = '<div class="column actor-avatar"> <div class="image"> <span class="avatar"></span> </div> </div>';
              $(".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(`
                <div style="display: flex;align-items: center; ${otherCss} ">
                    <a id="waitCheckBtn" class="menu-btn main-tab-btn" data-tip="打开未鉴定的列表项, 并自动播放视频" style="background-color:#56c938 !important; ${showWaitCheckBtn === NO$1 ? "display: none" : ""}"><span>打开待鉴定</span></a>
                    <a id="waitDownBtn" class="menu-btn main-tab-btn" style="background-color:#2caac0 !important; ${showWaitDownBtn === NO$1 ? "display: none" : ""}"><span>打开已收藏</span></a>
                    ${isStarPage ? `
                     <a id="addBlacklistBtn" class="menu-btn main-tab-btn" style="background-color:${addBlacklistBtnColor} !important;" data-tip="将演员加入黑名单, 后续有作品更新也会纳入屏蔽中"><span>${addBlacklistBtnText}</span></a>
                     <a id="filterAllVideo" class="menu-btn main-tab-btn" style="background-color:#e8ab39 !important;margin-right: 30px!important;" data-tip="一键屏蔽已选分类的视频列表至鉴定记录中"><span>一键屏蔽所有作品</span></a>
                    ` : ""}
                    ${currentHref$1.includes("/tags") ? `
                      <a id="addBlacklistBtn" class="menu-btn main-tab-btn" style="background-color:${addBlacklistBtnColor} !important;" data-tip="将演员加入黑名单, 后续有作品更新也会纳入屏蔽中"><span>${addBlacklistBtnText}</span></a>
                    ` : ""}
                </div>
                <div style="display: flex;align-items: center;">
                    <a id="one-time-sort-btn" class="menu-btn main-tab-btn" style="background-color:#8783ab !important;">一次性排序: 默认</a>
                    <a id="newVideoBtn" class="menu-btn main-tab-btn" style="background-color:#2c6cc0 !important;"><span>新作品检测 (<span id="newVideoCount">0</span>)</span></a>
                    <a id="blacklistBtn" class="menu-btn main-tab-btn" style="background-color:#34393f !important;"><span>演员黑名单</span></a>
                    ${isSearchPage || isFc2Page2 ? "" : `<a id="sort-toggle-btn" class="menu-btn main-tab-btn" style="background-color:#8783ab !important;"> ${sortText} </a>`}
                </div>
            `);
          }
          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(`
                <div style="margin: 10px; display: flex;">
                    <a id="waitCheckBtn" class="menu-btn main-tab-btn" style="background-color:#56c938 !important; ${showWaitCheckBtn === NO$1 ? "display: none" : ""}"><span>打开待鉴定</span></a>
                    <a id="waitDownBtn" class="menu-btn main-tab-btn" style="background-color:#2caac0 !important; ${showWaitDownBtn === NO$1 ? "display: none" : ""}"><span>打开已收藏</span></a>
                    
                    ${isStarPage ? `    
                        <a id="addBlacklistBtn" class="menu-btn main-tab-btn" style="background-color:${addBlacklistBtnColor} !important;" data-tip="将演员加入黑名单, 后续有作品更新也会纳入屏蔽中"><span>${addBlacklistBtnText}</span></a>
                        <a id="filterAllVideo" class="menu-btn main-tab-btn" style="background-color:#e8ab39 !important;" data-tip="一键屏蔽已选分类的视频列表至鉴定记录中"><span>一键屏蔽所有作品</span></a>
                    ` : '<a id="blacklistBtn" class="menu-btn main-tab-btn" style="background-color:#34393f !important;"><span>演员黑名单</span></a>'}
                </div>
            `);
          }
          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            <style> \n                .translated-title {\n                    margin-top: 8px; \n                    padding: 12px; \n                    border-radius: 5px; \n                    border-left: 4px solid rgb(76, 175, 80);\n                    background: linear-gradient(135deg, rgb(255, 255, 255) 0%, rgb(245, 245, 245) 100%); \n                    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08);\n                    font-size: 20px;\n                }\n            </style>\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('<div class="translated-title">翻译中...</div>');
          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 + "&nbsp;&nbsp;&nbsp;" + cache[carNum2] : cache[carNum2]) : translateText(originalText, "ja", "zh-CN").then(((translatedText) => {
            $loadingElement.html(showCarNum ? carNum2 + "&nbsp;&nbsp;&nbsp;" + translatedText : translatedText);
          })).catch(((error) => {
            console.error("翻译失败:", error);
            $loadingElement.replaceWith(`<div class="translated-title" style="color: red;">翻译失败: ${error.message}</div>`);
          }));
        }
      }
      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            <style>\n                .site-btn {\n                    position: relative !important;\n                    min-width: 80px;\n                    display: inline-block;\n                    padding: 5px 10px;\n                    color: white !important;\n                    background-color:#938585;\n                    text-decoration: none;\n                    border-radius: 4px;\n                    text-align: center;\n                    margin-bottom: 5px;\n                }\n                .site-btn:hover {\n                    color: white;\n                    transform: translateY(-2px);\n                    box-shadow: 0 2px 5px rgba(0,0,0,0.1);\n                }\n                .site-tag {\n                    position: absolute; \n                    top: -15px; \n                    right: 0; \n                    background-color: #ffc107; \n                    color: #333; \n                    font-size: 12px; \n                    padding: 2px 6px; \n                    border-radius: 4px;\n                }\n            </style>\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 = `
            <div id="otherSiteBox" class="panel-block" style="${isJavDb$1 ? "margin-top:8px;font-size:13px" : "margin-top:10px;font-size:13px"}; user-select: none; ">
                <div style="display: flex;gap: 5px;flex-wrap: wrap">
                    ${this.siteConfigs.map(((config) => {
      config.sourceCarNum = sourceCarNum;
      if (config.condition && false === config.condition(config.sourceCarNum)) return "";
      return `<a target="_blank" class="site-btn" style="${enabledSites.includes(config.id) ? "" : "display:none"}" id="${config.id}"><span>${config.id.replace("Btn", "")}</span></a>`;
    })).join("")}
                    <a id="settingSiteBtn" class="site-btn"><span>跳转设置</span></a>
                </div>
            </div>
            
            <div id="settingsArea" class="panel-block"  style="display: none; margin-top:10px; margin-bottom: 10px; user-select: none; ">
                <div id="siteCheckboxes" style="display: flex;gap: 5px;flex-wrap: wrap">
                </div>
            </div>
        `;
          $(".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('<span class="site-tag" style="top:-15px">多结果</span>');
                $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 += '<span class="site-tag" style="top:-15px">多结果</span>';
              $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 `
                <div style="margin-right: 15px; display: flex; align-items: ${isJavDb$1 ? "center" : "flex-start"};">
                    <input type="checkbox" id="checkbox-${config.id}" data-site-id="${config.id}" ${isEnabled ? "checked" : ""} style="margin-right: 8px; cursor: pointer;">
                    <label for="checkbox-${config.id}" style="color: #333; font-weight: 500; cursor: pointer;">${config.id.replace("Btn", "")}</label>
                </div>
            `;
          })).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(`
            <div style="display: flex; align-items: center; margin: 16px 0; color: #666; font-size: 14px;">
                <span style="flex: 1; height: 1px; background: linear-gradient(to right, transparent, #999, transparent);"></span>
                <span style="padding: 0 10px;" data-tip="想要发表评论? 滑上去, 点击上面的按钮-看过">❓ 评论区</span>
                <a id="reviewsFold" style="margin-left: 8px; color: #1890ff; text-decoration: none; display: flex; align-items: center;">
                    <span class="toggle-text">${enableLoadReview === YES$1 ? "折叠" : "展开"}</span>
                    <span class="toggle-icon" style="margin-left: 4px;">${enableLoadReview === YES$1 ? "▲" : "▼"}</span>
                </a>
                <span style="flex: 1; height: 1px; background: linear-gradient(to right, transparent, #999, transparent);"></span>
            </div>
        `);
          $("#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('<div id="reviewsContainer"></div>');
          $magnets.append('<div id="reviewsFooter"></div>');
          enableLoadReview === YES$1 && await this.fetchAndDisplayReviews(movieId);
        }
        async fetchAndDisplayReviews(movieId) {
          const $reviewsContainer = $("#reviewsContainer"), $reviewsFooter = $("#reviewsFooter");
          $reviewsContainer.append('<div id="reviewsLoading" style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">获取评论中...</div>');
          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                <div style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">\n                    获取评论失败\n                    <a id="retryFetchReviews" href="javascript:;" style="margin-left: 10px; color: #1890ff; text-decoration: none;">重试</a>\n                </div>\n            ');
            $("#retryFetchReviews").on("click", (async () => {
              $("#retryFetchReviews").parent().remove();
              await this.fetchAndDisplayReviews(movieId);
            }));
            return;
          }
          if (0 === dataList.length) {
            $reviewsContainer.append('<div style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">无评论</div>');
            return;
          }
          const reviewKeywordList = await storageManager.getReviewFilterKeywordList();
          this.displayReviews(dataList, $reviewsContainer, reviewKeywordList);
          if (dataList.length === reviewCount) {
            $reviewsFooter.html('\n                <button id="loadMoreReviews" style="width:100%; background-color: #e1f5fe; border:none; padding:10px; margin-top:10px; cursor:pointer; color:#0277bd; font-weight:bold; border-radius:4px;">\n                    加载更多评论\n                </button>\n                <div id="reviewsEnd" style="display:none; text-align:center; padding:10px; color:#666; margin-top:10px;">已加载全部评论</div>\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('<div style="text-align:center; padding:10px; color:#666; margin-top:10px;">已加载全部评论</div>');
        }
        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('<i class="icon-star"></i>').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://") ? `
                            <span style="word-break: break-all;background: #e0f2fe;color: #0369a1;">${match}</span>
                            <button class="button is-info down-115" data-magnet="${match}" style="font-size: 11px">115离线下载</button>
                        ` : match.startsWith("magnet:") ? `
                            <a href="${match}" class="a-primary" style="padding:0; word-break: break-all; white-space: pre-wrap;" target="_blank" rel="noopener noreferrer">${match}</a>
                            <button class="button is-info down-115" data-magnet="${match}" style="font-size: 11px">115离线下载</button>
                        ` : match.startsWith("http://") || match.startsWith("https://") ? `
                            <a href="${match}" class="a-primary" style="padding:0; word-break: break-all; white-space: pre-wrap;" target="_blank" rel="noopener noreferrer">${match}</a>
                        ` : match)), commentHtml = `
                <div class="item columns is-desktop" style="display:block;margin-top:6px;background-color:#ffffff;padding:10px;margin-left: -10px;word-break: break-word;position:relative;">
                    <span style="position:absolute;top:5px;right:10px;color:#999;font-size:12px;">#${this.floorIndex++}楼</span>
                    ${item.username} &nbsp;&nbsp; <span class="score-stars">${starsHtml}</span> 
                    <span class="time">${utils.formatDate(item.created_at)}</span> 
                    &nbsp;&nbsp; 点赞:${item.likes_count}
                    <p class="review-content" style="margin-top: 5px;"> ${content} </p>
                </div>
            `;
              $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 `
            <style>
                .box .tags {
                    justify-content: space-between;
                }
                .tool-box span{
                    opacity:.6
                }
                .tool-box span:hover{
                    opacity:1
                }
                ${isJavBus$1 ? ".tool-box .icon, .setting-label .icon{ height: 24px; width: 24px; }" : ""}
                .tool-box svg path {
                  fill: blue;
                }
                [data-theme="dark"] .tool-box svg path {
                  fill: white;
                }
                .tool-box .delete115Svg svg path {
                  fill: #e57373 !important;
                }
                [data-theme="dark"] .tool-box .delete115Svg svg path {
                  fill: #ef9a9a !important;
                }
                .tool-box .markDeleteSvg svg path {
                  fill: #81c784 !important;
                }
                [data-theme="dark"] .tool-box .markDeleteSvg svg path {
                  fill: #66bb6a !important;
                }
                .tool-box .markDeleteSvg.marked svg path {
                  fill: #d84315 !important;
                }
                [data-theme="dark"] .tool-box .markDeleteSvg.marked svg path {
                  fill: #e64a19 !important;
                }
                
                
                /* 鼠标移入时的弹性动画 */
                .elastic-in {
                    animation: elasticIn 0.2s ease-out forwards;  /* 动画名称 | 时长 | 缓动函数 | 保持最终状态 */
                }
                
                /* 鼠标移出时的弹性动画 */
                .elastic-out {
                    animation: elasticOut 0.2s ease-in forwards;
                }
                /* 弹性进入动画(像果冻弹入) */
                @keyframes elasticIn {
                    0% {
                        opacity: 0;
                        transform: scale(0.8);  /* 起始状态:80% 大小 */
                    }
                    50% {
                        opacity: 1;
                        transform: scale(1.1);  /* 弹到 110%(超调一点) */
                    }
                    70% {
                        transform: scale(0.95); /* 回弹到 95%(模拟弹性阻尼) */
                    }
                    100% {
                        opacity: 1;
                        transform: scale(1);    /* 最终恢复正常大小 */
                    }
                }
                /* 弹性离开动画(像果冻弹出) */
                @keyframes elasticOut {
                    0% {
                        opacity: 1;
                        transform: scale(1);    /* 起始状态:正常大小 */
                    }
                    30% {
                        transform: scale(1.05); /* 先弹大一点(105%) */
                    }
                    100% {
                        opacity: 0;
                        transform: scale(0.8);  /* 最终缩小并消失 */
                    }
                }
                
                
                .loading {
                    opacity: 0.7;
                    filter: blur(1px);
                }
                .loading-spinner {
                    position: absolute;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    width: 40px;
                    height: 40px;
                    border: 3px solid rgba(255,255,255,.3);
                    border-radius: 50%;
                    border-top-color: #fff;
                    animation: spin 1s ease-in-out infinite;
                    z-index: 20;
                }
                @keyframes spin {
                    to { transform: translate(-50%, -50%) rotate(360deg); }
                }
            </style>
        `;
        }
        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(`
                    <div class="tool-box" style="margin-left: auto; display: flex; align-items: center">
                        <span class="markDeleteSvg" title="标记删除" style="margin-right: 15px; display: none;">${this.markDeleteSvg}</span>
                        <span class="delete115Svg" title="删除115作品" style="margin-right: 15px; display: none;">${this.removeSvg}</span>
                        <span class="screenSvg" title="长缩略图" style="margin-right: 15px;">${this.screenSvg}</span>
                        
                        <span class="videoSvg" title="播放视频" style="margin-right: 15px;">${this.videoSvg}</span>
                        
                        <div class="more-tools-container handleSvg" style="position: relative; margin-right: 15px;">
                            <div title="鉴定处理" style="padding: 5px; margin: -5px;opacity:.3">${this.handleSvg}</div>
                            
                            <div class="more-tools" style=" position: absolute; bottom: 33px; right: -30px; display: none;
                                background-color: rgba(255, 255, 255, 0);z-index: 10;">
                                <a class="menu-btn hasWatchBtn" style="background-color:#d7a80c;color:white !important;margin-bottom: 5px"><span style="opacity: 1;">🔍 已观看</span></a>
                                <a class="menu-btn hasDownBtn" style="background-color:#7bc73b; color:white !important;margin-bottom: 5px"><span style="opacity: 1;">📥️ 已下载</span></a>
                                <a class="menu-btn favoriteBtn" style="background-color:#25b1dc; color:white !important;margin-bottom: 5px"><span style="opacity: 1;">⭐ 收藏</span></a>
                                <a class="menu-btn filterBtn" style="background-color:#de3333;   color:white !important;margin-bottom: 5px"><span style="opacity: 1;">🚫 屏蔽</span></a>
                            </div>
                        </div>
                        
                        <div class="more-tools-container siteSvg"  style="position: relative; margin-right: 15px;">
                            <div title="第三方网站" style="padding: 5px; margin: -5px;opacity:.3">${this.siteSvg}</div>
                            
                             <div class="more-tools" style=" position: absolute; bottom: 33px; right: -30px; display: none;
                                background-color: rgba(255, 255, 255, 0);z-index: 10;">
                                <a class="site-btn site-jable" style="color:white !important;margin-bottom: 5px;background-color:#71bb59;">
                                    <span style="opacity: 1;">Jable</span>
                                </a>
                                <a class="site-btn site-miss-av" style="color:white !important;margin-bottom: 5px;background-color:#71bb59;">
                                    <span style="opacity: 1;">MissAv</span>
                                </a>
                                <a class="site-btn site-123-av" style="color:white !important;margin-bottom: 5px;background-color:#71bb59;">
                                    <span style="opacity: 1;">123Av</span>
                                </a>
                            </div>
                        </div>
                        
                        <div class="more-tools-container copySvg" style="position: relative; margin-right: 15px;">
                            <div title="复制按钮" style="padding: 5px; margin: -5px;opacity:.3">${this.copySvg}</div>
                            
                            <div class="more-tools" style="
                                position: absolute;
                                bottom: 20px;
                                right: -10px;
                                display: none;
                                background: white;
                                box-shadow: 0 2px 8px rgba(0,0,0,0.15);
                                border-radius: 20px;
                                padding: 10px 0;
                                margin-bottom: 15px;
                                z-index: 10;
                            ">
                                <span class="carNumSvg" title="复制番号" style="padding: 5px 10px; white-space: nowrap;">${this.carNumSvg}</span>
                                <span class="titleSvg" title="复制标题" style="padding: 5px 10px; white-space: nowrap;">${this.titleSvg}</span>
                                <span class="downSvg" title="下载封面" style="padding: 5px 10px; white-space: nowrap;">${this.downSvg}</span>
                            </div>
                        </div>
                    </div>
                `);
              if (isJavBus$1) {
                if ($box2.find(".avatar-box").length > 0) return;
                $box2.find(".photo-info").append(`
                    <div class="tool-box" style="display: flex; align-items: center;justify-content: flex-end">
                        <span class="markDeleteSvg" title="标记删除" style="margin-right: 15px; display: none;">${this.markDeleteSvg}</span>
                        <span class="delete115Svg" title="删除115作品" style="margin-right: 15px; display: none;">${this.removeSvg}</span>
                        <span class="screenSvg" title="长缩略图" style="margin-right: 15px;">${this.screenSvg}</span>

                        <span class="videoSvg" title="播放视频" style="margin-right: 15px;">${this.videoSvg}</span>
                        
                        <div class="more-tools-container handleSvg" style="position: relative; margin-right: 15px;">
                            <div title="鉴定处理" style="padding: 5px; margin: -5px;opacity:.3">${this.handleSvg}</div>
                            
                            <div class="more-tools" style=" position: absolute; bottom: 33px; right: -30px; display: none;
                                background-color: rgba(255, 255, 255, 0);z-index: 10;">
                                <a class="menu-btn hasWatchBtn" style="background-color:#d7a80c;color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline; color:white !important">🔍 已观看</span></a>
                                <a class="menu-btn hasDownBtn" style="background-color:#7bc73b; color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline; color:white !important">📥️ 已下载</span></a>
                                <a class="menu-btn favoriteBtn" style="background-color:#25b1dc; color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline; color:white !important">⭐ 收藏</span></a>
                                <a class="menu-btn filterBtn" style="background-color:#de3333;   color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline; color:white !important">🚫 屏蔽</span></a>
                            </div>
                        </div>
                        
                        <div class="more-tools-container siteSvg" style="position: relative; margin-right: 15px;">
                            <div title="第三方网站" style="padding: 5px; margin: -5px;opacity:.3">${this.siteSvg}</div>
                            
                             <div class="more-tools" style=" position: absolute; bottom: 33px; right: -30px; display: none;
                                background-color: rgba(255, 255, 255, 0);z-index: 10;">
                                <a class="site-btn site-jable" style="color:white;margin-bottom: 5px;background-color:#71bb59;">
                                    <span style="opacity: 1;display: inline; color:white !important">Jable</span>
                                </a>
                                <a class="site-btn site-miss-av" style="color:white;margin-bottom: 5px;background-color:#71bb59;">
                                    <span style="opacity: 1;display: inline; color:white !important">MissAv</span>
                                </a>
                                <a class="site-btn site-123-av" style="color:white;margin-bottom: 5px;background-color:#71bb59;">
                                    <span style="opacity: 1;display: inline; color:white !important">123Av</span>
                                </a>
                            </div>
                        </div>
                      
                        <div class="more-tools-container copySvg" style="position: relative;">
                            <div title="复制按钮" style="padding: 5px; margin: -5px;opacity:.3">${this.copySvg}</div>
                            
                            <div class="more-tools" style="
                                max-width: 44px;
                                position: absolute;
                                bottom: 20px;
                                right: -10px;
                                display: none;
                                background: white;
                                box-shadow: 0 2px 8px rgba(0,0,0,0.15);
                                border-radius: 20px;
                                padding: 10px 0;
                                margin-bottom: 15px;
                                z-index: 10;
                                text-align: center;
                            ">
                                <span class="carNumSvg" title="复制番号" style="padding: 5px 10px; white-space: nowrap;display: inline">${this.carNumSvg}</span>
                                <span class="titleSvg" title="复制标题"  style="padding: 5px 10px; white-space: nowrap;display: inline">${this.titleSvg}</span>
                                <span class="downSvg" title="下载封面"   style="padding: 5px 10px; white-space: nowrap;display: inline">${this.downSvg}</span>
                            </div>
                        </div>
                    </div>
                `);
              }
            }
          }));
          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('<div class="loading-spinner"></div>');
          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 = `
            <div style="display: flex; justify-content: center; align-items: center; position: absolute; top:0; left:0; height: 100%; width: 100%; z-index: 10; overflow: hidden">
                <video 
                    src="${videoUrl}" 
                    poster="${poster}" 
                    id="${id}" 
                    controls 
                    loop 
                    muted 
                    playsinline
                    style="max-height: 100%; max-width: 100%; object-fit: contain"
                ></video>
            </div>
        `;
          isJavBus$1 && (videoHtml = `
                <div>
                    <video 
                        src="${videoUrl}" 
                        poster="${poster}" 
                        id="${id}" 
                        controls 
                        loop 
                        muted 
                        playsinline
                        style="max-height: 100%; max-width: 100%; object-fit: contain"
                    ></video>
                </div>
            `);
          $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('<a class="navbar-item" href="/advanced_search?type=100&released_start=2099-09">123Av-Fc2</a>');
          $('.tabs li:contains("FC2")').after('<li><a href="/advanced_search?type=100&released_start=2099-09"><span>123Av-Fc2</span></a></li>');
          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            <div style="margin-left: 100px; width: 400px;">\n                <input id="search-123av-keyword" type="text" placeholder="搜索123Av Fc2ppv内容" style="padding: 4px 5px;margin-right: 0">\n                <a id="search-123av-btn" class="a-primary" style="margin-left: 0">搜索</a>\n                <a id="clear-123av-btn" class="a-info" style="margin-left: 0">重置</a>\n            </div>\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('<div class="tool-box" style="margin-top: 10px"></div>');
          this.$contentBox.append('<div class="movie-list h cols-4 vcols-8" style="margin-top: 10px"></div>');
          this.$contentBox.append('<div class="page-box"></div>');
          $(".tool-box").append('\n            <div class="button-group">\n                <div class="buttons has-addons" id="conditionBox">\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="release_date">发布日期</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="recent_update">最近更新</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="trending">热门</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_viewed_today">今天最多观看</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_viewed_week">本周最多观看</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_viewed_month">本月最多观看</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_viewed">最多观看</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_favourited">最受欢迎</a>\n                </div>\n            </div>\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            <nav class="pagination">\n                <a class="pagination-previous">上一页</a>\n                <ul class="pagination-list"></ul>\n                <a class="pagination-next">下一页</a>\n            </nav>\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('<li><a class="pagination-link" data-page="1">1</a></li>');
            startPage > 2 && $paginationList.append('<li><span class="pagination-ellipsis">…</span></li>');
          }
          for (let i = startPage; i <= endPage; i++) {
            const activeClass = i === this.currentPage ? " is-current" : "";
            $paginationList.append(`<li><a class="pagination-link${activeClass}" data-page="${i}">${i}</a></li>`);
          }
          if (endPage < this.maxPage) {
            endPage < this.maxPage - 1 && $paginationList.append('<li><span class="pagination-ellipsis">…</span></li>');
            $paginationList.append(`<li><a class="pagination-link" data-page="${this.maxPage}">${this.maxPage}</a></li>`);
          }
        }
        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 = '<div class="movie-panel-info fc2-movie-panel-info" style="margin-top:20px"><strong>第三方站点: </strong></div>');
          let pageHtml = `
            <div class="movie-detail-container">
               <!-- <div class="movie-poster-container">
                    <iframe class="movie-trailer" frameborder="0" allowfullscreen scrolling="no"></iframe>
                </div>
                <div class="right-box">-->
                    <div class="movie-info-container">
                        <div class="search-loading">加载中...</div>
                    </div>
                    
                    ${otherHtml}
                    
                    <div style="margin: 10px 0">
                        <a id="filterBtn" class="menu-btn" style="background-color:#de3333"><span>🚫 屏蔽</span></a>
                        <a id="favoriteBtn" class="menu-btn" style="background-color:#25b1dc"><span>⭐ 收藏</span></a>
                        <a id="hasDownBtn" class="menu-btn" style="background-color:#7bc73b"><span>📥️ 已下载</span></a>
                        <a id="hasWatchBtn" class="menu-btn" style="background-color:#d7a80c;"><span>🔍 已观看</span></a>
                        
                        <a id="subtitleSearchBtn" class="menu-btn fr-btn" style="background:linear-gradient(to left, #375f7c, #2196F3)">
                            <span>字幕搜索</span>
                        </a>
                        <!--
                        <a id="search-subtitle-btn" class="menu-btn fr-btn" style="background:linear-gradient(to bottom, #8d5656, rgb(196,159,91))">
                            <span>字幕 (SubTitleCat)</span>
                        </a>
                        <a id="xunLeiSubtitleBtn" class="menu-btn fr-btn" style="background:linear-gradient(to left, #375f7c, #2196F3)">
                            <span>字幕 (迅雷)</span>
                        </a>
                        -->
                    </div>
                    <div class="message video-panel" style="margin-top:20px">
                        <div id="magnets-content" class="magnet-links">
                        </div>
                    </div>
                    <div id="reviews-content">
                    </div>
                    <div id="related-content">
                    </div>
                    <span id="data-actress" style="display: none"></span>
               <!-- </div>-->
            </div>
        `;
          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(`
                    <h3 class="movie-title" style="margin-bottom: 10px"><strong class="current-title">${title || "无标题"}</strong></h3>
                    <div class="movie-meta" style="margin-bottom: 10px">
                        <span><strong>番号: </strong>${carNum2 || "未知"}</span>
                        <span><strong>年份: </strong>${publishDate || "未知"}</span>
                        <span>
                            <strong>站点: </strong>
                            <a href="https://fc2ppvdb.com/articles/${carNum2.replace("FC2-", "")}" target="_blank">fc2ppvdb</a>
                            <a style="margin-left: 5px;" href="https://adult.contents.fc2.com/article/${carNum2.replace("FC2-", "")}/" target="_blank">fc2电子市场</a>
                        </span>
                    </div>
                    <div class="movie-actors" style="margin-bottom: 10px">
                        <div class="actor-list"><strong>主演: </strong></div>
                    </div>
                    <div class="movie-seller" style="margin-bottom: 10px">
                        <span><strong>販売者: </strong></span>
                    </div>
                    <div class="movie-gallery" style="margin-bottom: 10px">
                        <strong>剧照: </strong>
                        <div class="image-list"></div>
                    </div>
                    
                    <div id="data-publishTime" style="display: none">${publishDate || ""}</div>

                `);
            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(' <a class="tile-item screen-container" style="overflow:hidden;max-height: 150px;max-width:150px; text-align:center;"><div style="margin-top: 50px;color: #000;cursor: auto">正在加载缩略图</div></a> ');
            const imgUrl = await this.getBean("ScreenShotPlugin").getScreenshot(carNum2);
            if (imgUrl) {
              $(".screen-container").html(`<img src="${imgUrl}" alt="" loading="lazy" style="width: 100%;">`);
              $(".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 = "<strong>主演: </strong>";
          if ($actress.length > 0) {
            let actress = "";
            $actress.each(((index, ele) => {
              let $actor = $(ele), name2 = $actor.text(), actressHref = $actor.attr("href");
              actorsHtml += `<span class="actor-tag"><a href="https://fc2ppvdb.com${actressHref}" target="_blank">${name2}</a></span>`;
              actress += name2 + " ";
            }));
            $("#data-actress").text(actress);
          } else actorsHtml += "<span>暂无演员信息</span>";
          $(".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(`<span><strong>販売者: </strong><a href="https://fc2ppvdb.com${sellerHref}" target="_blank">${name2}</a></span>`);
            }
          }
        }
        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) => `
                <a href="${img}" data-fancybox="movie-gallery" data-caption="剧照 ${index + 1}">
                    <img src="${img}" class="movie-image-thumb"  alt=""/>
                </a>
            `)).join("") : $(".movie-gallery").html("<h4>剧照: 暂无剧照</h4>");
          $(".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 += `
                <div class="item">
                    <a href="${movie.href}" class="box" title="${movie.title}">
                        <div class="cover ">
                            <img loading="lazy" src="${movie.imgSrc.replace("/s360", "")}" alt="">
                        </div>
                        <div class="video-title"><strong>${movie.carNum}</strong> ${movie.title}</div>
                        <div class="score">
                        </div>
                        <div class="meta">
                        </div>
                        <div class="tags has-addons">
                        </div>
                    </a>
                </div>
            `;
          }));
          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            <div style="margin: 10px auto; display: flex; justify-content: space-between; align-items: center; flex-wrap:wrap;gap: 20px;">\n                <div style="display: flex; gap: 10px; flex-wrap:wrap;">\n                    <a id="filterBtn" class="menu-btn" style="width: 120px; background-color:#de3333; color: white; text-align: center; padding: 8px 0;">\n                        <span>🚫 屏蔽</span>\n                    </a>\n                    <a id="favoriteBtn" class="menu-btn" style="width: 120px; background-color:#25b1dc; color: white; text-align: center; padding: 8px 0;">\n                        <span>⭐ 收藏</span>\n                    </a>\n                    <a id="hasDownBtn" class="menu-btn" style="width: 120px; background-color:#7bc73b; color: white; text-align: center; padding: 8px 0;">\n                        <span>📥️ 已下载</span>\n                    </a>\n                    <a id="hasWatchBtn" class="menu-btn" style="width: 120px; background-color:#d7a80c; color: white; text-align: center; padding: 8px 0;">\n                        <span>🔍 已观看</span>\n                    </a>\n                </div>\n        \n                <div style="display: flex; gap: 10px; flex-wrap:wrap;">\n                    <a id="downloadCoverBtn" class="menu-btn" style="width: 140px; background-color: #9b59b6; color: white; text-align: center; padding: 8px 0;">\n                        <span>下载高清封面</span>\n                    </a>\n                    <a id="enable-magnets-filter" class="menu-btn" style="width: 140px; background-color: #c2bd4c; color: white; text-align: center; padding: 8px 0;">\n                        <span id="magnets-span">排序与过滤</span>\n                    </a>\n                    <a id="magnetSearchBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to right, rgb(245,140,1), rgb(84,161,29)); color: white; text-align: center; padding: 8px 0;">\n                        <span>磁力搜索</span>\n                    </a>\n                    <a id="subtitleSearchBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to left, #375f7c, #2196F3); color: white; text-align: center; padding: 8px 0;">\n                        <span>字幕搜索</span>\n                    </a>\n                    <!--\n                    <a id="xunLeiSubtitleBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to left, #375f7c, #2196F3); color: white; text-align: center; padding: 8px 0;">\n                        <span>字幕 (迅雷)</span>\n                    </a>\n                    <a id="search-subtitle-btn" class="menu-btn" style="width: 160px; background: linear-gradient(to bottom, #8d5656, rgb(196,159,91)); color: white; text-align: center; padding: 8px 0;">\n                        <span>字幕 (SubTitleCat)</span>\n                    </a>\n                    -->\n                </div>\n            </div>\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: '<div id="magnetHubBox"></div>',
              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 `<div class="msf-sort-item" draggable="true" data-sort-key="${key}">
                    <span class="msf-drag-handle">⋮⋮</span>
                    <label class="msf-sort-label">
                        <input type="checkbox" class="msf-sort-checkbox" data-sort-key="${key}" ${checked}>
                        ${SORT_LABELS[key]} <span class="msf-sort-hint">${SORT_HINTS[key]}</span>
                    </label>
                </div>`;
            }).join("");
          };
          const buildFilterItemsHTML = () => {
            return FILTER_ITEMS.map((item) => {
              const checked = currentFilters[item.key] ? "checked" : "";
              return `<label class="msf-filter-label">
                    <input type="checkbox" class="msf-filter-checkbox" data-filter-key="${item.key}" ${checked}>
                    ${item.label}
                </label>`;
            }).join("");
          };
          const panelHTML = `
            <div class="msf-overlay" id="msf-overlay"></div>
            <div class="msf-panel" id="magnet-sort-filter-panel">
                <div class="msf-header">
                    <span class="msf-title">磁力排序与过滤</span>
                    <span class="msf-close" id="msf-close-btn">&times;</span>
                </div>
                <div class="msf-body">
                    <div class="msf-section">
                        <div class="msf-section-title">排序优先级(拖动调整顺序,勾选启用)</div>
                        <div class="msf-sort-list" id="msf-sort-list">
                            ${buildSortItemsHTML()}
                        </div>
                    </div>
                    <div class="msf-section">
                        <div class="msf-section-title">过滤条件(可多选,并集关系)</div>
                        <div class="msf-filter-list">
                            ${buildFilterItemsHTML()}
                        </div>
                    </div>
                </div>
                <div class="msf-footer">
                    <button class="msf-btn msf-btn-cancel" id="msf-cancel-btn">取消</button>
                    <button class="msf-btn msf-btn-confirm" id="msf-confirm-btn">确认</button>
                </div>
            </div>
        `;
          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                    <div style="height: 100%;overflow:hidden;"> \n                        <div id="xunlei-table-container" style="height: 100%;padding-bottom: 20px"></div>\n                    </div>\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                                        <a class="a-primary">预览</a>\n                                        <a class="a-success">下载</a>\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 += `<span style="color:#AAA;">${paddedLineNumber}. </span>${line}
`;
            }));
            const finalContent = numberedContent;
            layer.open({
              type: 1,
              title,
              area: ["80%", "80%"],
              scrollbar: false,
              content: `<div style="padding:15px 5px;background:#1E1E1E;color:#FFF;font-family:Consolas,Monaco,monospace;white-space:pre-wrap;overflow:auto;height:100%;">${finalContent}</div>`,
              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 = `是否将分类 <span style="color: #f40">${checkTag}</span> 加入到黑名单中?`;
            hasAddBlacklist && (title = `分类 <span style="color: #f40">${checkTag}</span> 已在黑名单中, 是否从当前页开始追加屏蔽?`);
          } else {
            dataInfo = this.getActressPageInfo();
            title = `是否将该演员 <span style="color: #f40">${dataInfo.name}</span> 加入到黑名单中?`;
            hasAddBlacklist && (title = `演员 <span style="color: #f40">${dataInfo.name}</span> 已在黑名单中, 是否从当前页开始追加屏蔽?`);
          }
          const { starId, name: name2, allName, role, movieType, blacklistUrl } = dataInfo;
          currentHref$1.includes("page") && !currentHref$1.includes("page=1") && (title += "<br/> 注意: 当前页面非第一页, 屏蔽数据将从此页面开始");
          if (isJavBus$1) {
            const afterStarParts = currentHref$1.split("/star/")[1].split("/");
            if (afterStarParts.length > 1) {
              parseInt(afterStarParts[1]) > 1 && (title += "<br/> 注意: 当前页面非第一页, 屏蔽数据将从此页面开始");
            }
          }
          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 = `
            <div style="padding: 10px 20px; height: 100%;overflow:hidden;"> 
                 <div style="display: flex;justify-content: space-between;">
                    <div style="display: flex; gap:5px">
                        <a id="checkBlacklistBtn" class="a-danger" data-tip="上次检测时间: ${localStorage.getItem(taskPlugin.lastCheckBlacklistTimeKey) || "无"}; 检测间隔时间: ${settingObj.checkBlacklist_intervalTime}小时">${this.blacklistSvg} &nbsp;手动检测黑名单</a>
                        <a class="a-warning" id="clearKeywordBlacklist" data-tip="检测黑名单番号数据列表, 是否包含标题关键词">${this.removeSvg} &nbsp;&nbsp; 清理数据</a>
                        <a class="a-info" id="toSetting">${this.settingSvg} &nbsp;&nbsp; 配置</a>
                    </div>
                    <div style="display: flex; gap:5px">
                        <select id="dataType" style="text-align: center;min-width: 150px;">
                            <option value="" selected>所有</option>
                            <option value="actor">男演员</option>
                            <option value="actress">女演员</option>
                            <option value="虚拟演员">虚拟演员</option>
                        </select>
                        <select id="statusType" style="text-align: center;min-width: 150px;">
                            <option value="" selected>--检测状态--</option>
                            <option value="normal">正常检测</option>
                            <option value="stop">停止检测</option>
                        </select>
                        <select id="urlType" data-tip="在演员页屏蔽时,是否选择了分类" style="text-align: center;min-width: 150px; ${isJavDb$1 ? "" : "display: none;"}">
                            <option value="" selected>--屏蔽类型--</option>
                            <option value="hasT">按所选分类屏蔽</option>
                            <option value="noT">所有分类</option>
                        </select>
                        <input id="searchValue" type="text" placeholder="搜索演员" style="padding: 4px 5px;">
                        <a id="cleanQueryBtn" class="a-info" style="margin-left: 0">重置</a>
                    </div>

                 </div>
                 <div id="table-container" style="margin-top:20px !important; height: calc(100% - 50px);"></div>
            </div>
        `;
          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(`
            <option value="">所有 (${blacklistCount})</option>
            <option value="actor">男演员 (${actorCount})</option>
            <option value="actress">女演员 (${actressCount})</option>
            <option value="虚拟演员">虚拟演员 (${noStarCount})</option>
        `);
          $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} &nbsp;&nbsp;&nbsp;番号总数: ${this.currentCarCount}  <span id="checkBlacklistMsg" style="margin-left: 10px"></span>`,
            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 `<a class="open-url" data-url="${item.url}" href="${item.url}" data-name="${item.name}" target="_blank">${item.name}</a>`;
              }
            }, {
              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 `<span style="color:#cc4444">${nameSplitList.length > 0 ? nameSplitList[1] : item.name}</span>`;
                }
                const tParam = utils.getUrlParam(item.url, "t");
                if (!tParam) return "<span>所有分类</span>";
                {
                  const tParamList = tParam.toString().split(",");
                  let selectTypeStr = "";
                  tParamList.forEach(((i) => {
                    let value = categoryMap[i];
                    selectTypeStr += value ? value + " " : "未知类别:" + i + " ";
                  }));
                  selectTypeStr = selectTypeStr.trim();
                  if (selectTypeStr) return `<span style="color:#cc4444">${selectTypeStr}</span>`;
                }
              }
            }, {
              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 `<span data-tip="${dataTip}" style="${dataTip ? "color: #cc4444;" : ""}">${content}</span>`;
              }
            }, {
              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                           <!-- <a class="a-normal keyword-btn"> <span>提取屏蔽词</span> </a>-->\n                            <a class="a-danger delete-btn"> <span>✂️ 删除</span> </a>\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 = `
                    <div class ='movie-list'>${html2}</div>
                    ${nextUrl ? `<a class="pagination-next" href="${nextUrl}"></a>` : ""}
                `;
              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: `
                <div style="height: 100%;overflow:hidden;">
                    <div style="padding: 10px; overflow-y: auto;">
                        <p>以下匹配到 <strong style="color: red;">${carNumCount}</strong> 个黑名单番号存在于屏蔽关键词中, 是否移除?</p>
                    </div>
                    <div id="wait-remove-table"  style="height: calc(100% - 70px);"></div>
                </div>
            `,
            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(`<span class="video-title" title="${title}">${title}</span>`);
            $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 ? `<span class="tag is-success status-tag" data-tip="${filterReason}" title=""
                        style="margin-right: 5px; border-radius:10px; position:absolute; 
                        z-index:10; background-color: ${tag.color} !important; ${tagPositionCss}">
                        ${tag.text}
                    </span>` : `<a class="a-primary status-tag" data-tip="${filterReason}"  title=""
                        style="margin-right: 5px; padding: 0 5px; color: #fff !important; border-radius:10px; position:absolute; 
                        z-index:10; background-color: ${tag.color} !important; ${tagPositionCss}">
                        <span class="tag" style="color:#fff !important;">${tag.text}</span>
                    </a>`;
              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(`
            <table class="countTable" style='border-collapse: collapse; width: 100%'>
                <tr>
                    <td colspan="2" style='padding: 3px; border: 1px solid #ccc;'>${t1}</td>
                    <td colspan="2" style='padding: 3px; border: 1px solid #ccc;'>${t2}</td>
                </tr>
                
                <tr>
                    <td colspan="2" style='padding: 3px; border: 1px solid #ccc;'>${t3}</td>
                    <td colspan="2" style='padding: 3px; border: 1px solid #ccc;'>${t4}</td>
                </tr>
                <tr>
                    <td style='padding: 3px; border: 1px solid #ccc; font-weight: bold;'>项目</td>
                    <td style='padding: 3px; border: 1px solid #ccc; font-weight: bold;'>数量</td>
                    <td style='padding: 3px; border: 1px solid #ccc; font-weight: bold;'>项目</td>
                    <td style='padding: 3px; border: 1px solid #ccc; font-weight: bold;'>数量</td>
                </tr>
                
                <tr>
                    <td style='padding: 3px; border: 1px solid #ccc;'>屏蔽单番号</td>
                    <td style='padding: 3px; border: 1px solid #ccc;'><strong>${this.currentPageFilterCount}</strong></td>
                     <td style='padding: 3px; border: 1px solid #ccc;'>收藏</td>
                    <td style='padding: 3px; border: 1px solid #ccc;'><strong>${this.currentPageFavoriteCount}</strong></td>
                </tr>
                
                <tr>
                    <td style='padding: 3px; border: 1px solid #ccc;'>屏蔽演员</td>
                    <td style='padding: 3px; border: 1px solid #ccc;'><strong>${this.currentPageActorFilterCount}</strong></td>
                    <td style='padding: 3px; border: 1px solid #ccc;'>已下载</td>
                    <td style='padding: 3px; border: 1px solid #ccc;'><strong>${this.currentPageHasDownCount}</strong></td>
                </tr>
                
                <tr>
                    <td style='padding: 3px; border: 1px solid #ccc;'>屏蔽关键词</td>
                    <td style='padding: 3px; border: 1px solid #ccc;'><strong>${this.currentPageKeywordFilterCount}</strong></td>
                    <td style='padding: 3px; border: 1px solid #ccc;'>已观看</td>
                    <td style='padding: 3px; border: 1px solid #ccc;'><strong>${this.currentPageHasWatchCount}</strong></td>
                </tr>
                
                <tr>
                    <td style='padding: 3px; border: 1px solid #ccc;'>待鉴定</td>
                    <td style='padding: 3px; border: 1px solid #ccc;'><strong>${this.currentPageWaitCheckCount}</strong></td>
                    <td style='padding: 3px; border: 1px solid #ccc;'></td>
                    <td style='padding: 3px; border: 1px solid #ccc;'></td>
                </tr>
        
                <tr>
                    <td style='padding: 3px; border: 1px solid #ccc;'><strong>总数</strong></td>
                    <td style='padding: 3px; border: 1px solid #ccc;'><strong>${this.currentPageTotalCount}</strong></td>
                </tr>
            </table>
        `);
        }
        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 = $("<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 = $("<button>", {
            text: "跳转",
            style: "margin-left: 5px; padding: 9px 8px; cursor: pointer; border: 1px solid #ccc; background-color: #f0f0f0; font-size: 14px;"
          }), $jumpLi = $("<li>", {
            id: "gemini-jump-page-control"
          }).append($input).append($button);
          $(".pagination-list").append($jumpLi);
          const jumpToPage = () => {
            const pageNumber = parseInt($input.val(), 10);
            if (isNaN(pageNumber) || pageNumber < 1) {
              $input.focus();
              return;
            }
            const newUrl = new URL(window.location.href);
            newUrl.searchParams.set("page", pageNumber.toString());
            window.location.href = newUrl.toString();
          };
          $button.on("click", jumpToPage);
          $input.on("keypress", (function(e) {
            if (13 === e.which) {
              jumpToPage();
              e.preventDefault();
            }
          }));
        }
      }
      window.Tabulator = TabulatorFull;
      window.javDbApi = javDbApi;
      const _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow !== "undefined" ? unsafeWindow : window)();
      _unsafeWindow.utils = window.utils = new Utils();
      _unsafeWindow.gmHttp = window.gmHttp = new GmHttp();
      _unsafeWindow.storageManager = window.storageManager = new StorageManager();
      const senderChannel = new BroadcastChannel("jhs-channel");
      window.refresh = function() {
        senderChannel.postMessage({
          type: "refresh"
        });
      };
      window.clean_cacheBlacklistCarList = function() {
        storageManager.cacheBlacklistCarList && (storageManager.cacheBlacklistCarList = null);
        senderChannel.postMessage({
          type: "clean_cacheBlacklistCarList"
        });
      };
      window.clean_cacheSettingObj = function() {
        storageManager.cacheSettingObj && (storageManager.cacheSettingObj = null);
        senderChannel.postMessage({
          type: "clean_cacheSettingObj"
        });
      };
      window.clean_cacheFavoriteActressList = function() {
        storageManager.cacheFavoriteActressList && (storageManager.cacheFavoriteActressList = null);
        senderChannel.postMessage({
          type: "clean_cacheFavoriteActressList"
        });
      };
      window.clean_cacheCarList = function() {
        storageManager.cacheCarList && (storageManager.cacheCarList = null);
        senderChannel.postMessage({
          type: "clean_cacheCarList"
        });
      };
      new BroadcastChannel("jhs-channel").addEventListener("message", (async (event) => {
        let dataType = event.data.type;
        if ("refresh" === dataType) {
          const listPagePlugin = window.pluginManager.getBean("ListPagePlugin");
          await listPagePlugin.doFilter();
          const historyPlugin = window.pluginManager.getBean("HistoryPlugin");
          historyPlugin.tableObj && historyPlugin.tableObj.setData();
          const newVideoPlugin = window.pluginManager.getBean("NewVideoPlugin");
          if (newVideoPlugin) {
            newVideoPlugin.showNewVideoCount().then();
            newVideoPlugin.loadData();
          }
        } else "clean_cacheBlacklistCarList" === dataType ? storageManager.cacheBlacklistCarList && (storageManager.cacheBlacklistCarList = null) : "clean_cacheSettingObj" === dataType ? storageManager.cacheSettingObj && (storageManager.cacheSettingObj = null) : "clean_cacheFavoriteActressList" === dataType ? storageManager.cacheFavoriteActressList && (storageManager.cacheFavoriteActressList = null) : "clean_cacheCarList" === dataType && storageManager.cacheCarList && (storageManager.cacheCarList = null);
      }));
      !(function() {
        document.head.insertAdjacentHTML("beforeend", '\n        <style>\n            .loading-container {\n                position: fixed;\n                top: 0;\n                left: 0;\n                width: 100%;\n                height: 100%;\n                display: flex;\n                flex-direction: column; /* 垂直排列 */\n                justify-content: center;\n                align-items: center;\n                background-color: rgba(0, 0, 0, 0.1);\n                z-index: 99999999;\n            }\n    \n            .loading-animation {\n                position: relative;\n                width: 60px;\n                height: 12px;\n                background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);\n                border-radius: 6px;\n                animation: loading-animate 1.8s ease-in-out infinite;\n                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n                margin-bottom: 30px; /* 和按钮之间留出间距 */\n            }\n    \n            .loading-animation:before,\n            .loading-animation:after {\n                position: absolute;\n                display: block;\n                content: "";\n                animation: loading-animate 1.8s ease-in-out infinite;\n                height: 12px;\n                border-radius: 6px;\n                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n            }\n    \n            .loading-animation:before {\n                top: -20px;\n                left: 10px;\n                width: 40px;\n                background: linear-gradient(90deg, #ff758c 0%, #ff7eb3 100%);\n            }\n    \n            .loading-animation:after {\n                bottom: -20px;\n                width: 35px;\n                background: linear-gradient(90deg, #ff9a9e 0%, #fad0c4 100%);\n            }\n            \n            /* 新增按钮样式 */\n            .loading-close-btn {\n                padding: 8px 15px;\n                border: none;\n                border-radius: 4px;\n                background-color: #f44336; /* 红色背景 */\n                color: white;\n                cursor: pointer;\n                font-size: 14px;\n                opacity: 0; /* 默认隐藏 */\n                transition: opacity 0.3s ease; /* 渐变效果 */\n            }\n\n            .loading-close-btn.visible {\n                opacity: 1; /* 3秒后显示 */\n            }\n    \n            @keyframes loading-animate {\n                0% {\n                    transform: translateX(40px);\n                }\n                50% {\n                    transform: translateX(-30px);\n                }\n                100% {\n                    transform: translateX(40px);\n                }\n            }\n        </style>\n    ');
        _unsafeWindow.loading = window.loading = function() {
          const container = document.createElement("div");
          container.className = "loading-container";
          const animation = document.createElement("div");
          animation.className = "loading-animation";
          const closeBtn = document.createElement("button");
          closeBtn.className = "loading-close-btn";
          closeBtn.textContent = "关闭";
          closeBtn.addEventListener("click", (() => {
            close();
          }));
          setTimeout((() => {
            closeBtn.classList.add("visible");
          }), 3e3);
          container.appendChild(animation);
          container.appendChild(closeBtn);
          document.body.appendChild(container);
          const close = () => {
            container && container.parentNode && container.parentNode.removeChild(container);
          };
          return {
            close
          };
        };
      })();
      !(function() {
        const showMessage = (msg, type, gravityOrOptions, positionOrOptions, options) => {
          let finalOptions;
          if ("object" == typeof gravityOrOptions) finalOptions = gravityOrOptions;
          else {
            finalOptions = "object" == typeof positionOrOptions ? positionOrOptions : options || {};
            finalOptions.gravity = gravityOrOptions || "top";
            finalOptions.position = "string" == typeof positionOrOptions ? positionOrOptions : "center";
          }
          finalOptions.gravity && "center" !== finalOptions.gravity || (finalOptions.offset = {
            y: "calc(50vh - 150px)"
          });
          const colors_infoStart = "#60A5FA", colors_infoEnd = "#93C5FD", colors_successStart = "#10B981", colors_successEnd = "#6EE7B7", colors_errorStart = "#EF4444", colors_errorEnd = "#FCA5A5", commonStyles = {
            borderRadius: "12px",
            color: "white",
            padding: "12px 16px",
            boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
            minWidth: "150px",
            textAlign: "center",
            zIndex: 999999999
          }, defaultConfig2 = {
            text: msg,
            duration: 1e3,
            close: false,
            gravity: "top",
            position: "center",
            style: {
              info: {
                ...commonStyles,
                background: `linear-gradient(to right, ${colors_infoStart}, ${colors_infoEnd})`
              },
              success: {
                ...commonStyles,
                background: `linear-gradient(to right, ${colors_successStart}, ${colors_successEnd})`
              },
              error: {
                ...commonStyles,
                background: `linear-gradient(to right, ${colors_errorStart}, ${colors_errorEnd})`
              }
            }[type],
            stopOnFocus: true,
            oldestFirst: false,
            ...finalOptions
          };
          -1 === defaultConfig2.duration && (defaultConfig2.close = true);
          const toast = Toastify(defaultConfig2);
          toast.showToast();
          toast.closeShow = () => {
            toast.toastElement.remove();
          };
          return toast;
        };
        _unsafeWindow.show = window.show = {
          ok: (msg, gravityOrOptions = "center", positionOrOptions, options) => showMessage(msg, "success", gravityOrOptions, positionOrOptions, options),
          error: (msg, gravityOrOptions = "center", positionOrOptions, options) => showMessage(msg, "error", gravityOrOptions, positionOrOptions, options),
          info: (msg, gravityOrOptions = "center", positionOrOptions, options) => showMessage(msg, "info", gravityOrOptions, positionOrOptions, options)
        };
      })();
      !(function() {
        document.head.insertAdjacentHTML("beforeend", "\n        <style>\n            .viewer-canvas {\n                overflow: auto !important;\n            }\n            \n            .viewer-close {\n                background: rgba(255,0,0,0.6) !important;\n            }\n            .viewer-close:hover {\n                background: rgba(255,0,0,0.8) !important;\n            }\n        </style>\n    ");
        function smartToggleBodyScroll2(waitTime = 10) {
          setTimeout((() => {
            const openLayerCount = document.querySelectorAll(".layui-layer-shade").length;
            document.documentElement.style.overflow = openLayerCount > 0 ? "hidden" : "";
          }), waitTime);
        }
        window.showImageViewer = function(imgUrlOrContainer, altText = "") {
          let $container = null, isTempContainer = false;
          if ("string" == typeof imgUrlOrContainer || imgUrlOrContainer instanceof String) {
            $container = $('<div class="temporary-container" style="display:none;">').append(`<img src="${imgUrlOrContainer}" alt="${altText}">`).appendTo("body");
            isTempContainer = true;
          } else $container = $(imgUrlOrContainer);
          const options = {
            zIndex: 999999990,
            navbar: false,
            zoomOnWheel: false,
            zoomRatio: 0.1,
            toggleOnDblclick: false,
            toolbar: {
              zoomIn: 1,
              zoomOut: 1,
              reset: 1,
              rotateLeft: 0,
              rotateRight: 0,
              flipHorizontal: 0,
              flipVertical: 0
            },
            title: false,
            keyboard: false,
            viewed() {
              viewerInstance.zoomTo(1.4);
              let left = (viewerInstance.viewerData.width - viewerInstance.imageData.width) / 2;
              viewerInstance.moveTo(left, 0);
            },
            shown() {
              isTempContainer && $container.remove();
              document.documentElement.style.overflow = "hidden";
              document.body.style.overflow = "hidden";
              viewerInstance.handleKeydown = function(e) {
                if ("Escape" === e.key || " " === e.key) {
                  e.preventDefault();
                  e.stopPropagation();
                  viewerInstance.destroy();
                  document.removeEventListener("keydown", viewerInstance.handleKeydown);
                  document.documentElement.style.overflow = "";
                  document.body.style.overflow = "";
                  smartToggleBodyScroll2();
                }
              };
              document.addEventListener("keydown", viewerInstance.handleKeydown);
            },
            hidden() {
              viewerInstance && viewerInstance.handleKeydown && document.removeEventListener("keydown", viewerInstance.handleKeydown);
              viewerInstance.destroy();
              document.documentElement.style.overflow = "";
              document.body.style.overflow = "";
              smartToggleBodyScroll2();
            }
          }, viewerInstance = new Viewer($container[0], options);
          viewerInstance.show();
        };
      })();
      window.ImageHoverPreview = class {
        constructor(options = {}) {
          this.config = {
            selector: ".hover-preview",
            dataAttribute: "data-full",
            maxWidth: 1e3,
            maxHeight: 1e3,
            offsetX: 20,
            offsetY: 20,
            zIndex: 9999999999,
            transition: 0.2,
            autoAdjustPosition: true,
            ...options
          };
          this.preview = null;
          this.currentTarget = null;
          this.timer = null;
          this.imgElement = null;
          this.boundElements = /* @__PURE__ */ new WeakSet();
          this.init();
        }
        init() {
          this.injectStyles();
          this.createPreviewElement();
          this.bindEvents();
        }
        injectStyles() {
          const css = `
                <style>
                    .image-hover-preview {
                        position: fixed;
                        display: none;
                        z-index: ${this.config.zIndex};
                        border-radius: 4px;
                        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
                        overflow: hidden;
                        pointer-events: none;
                        opacity: 0;
                        transition: opacity ${this.config.transition}s ease;
                        background-color: #fff;
                    }
                    
                    .image-hover-preview.active {
                        opacity: 1;
                    }
                    
                    .image-hover-preview img {
                        max-width: ${this.config.maxWidth + "px"};
                        max-height: ${this.config.maxHeight + "px"};
                        display: block;
                        object-fit: contain;
                    }
                    
                    .image-hover-preview::after {
                        content: '';
                        position: absolute;
                        top: 0;
                        left: 0;
                        right: 0;
                        bottom: 0;
                        background: rgba(0, 0, 0, 0.03);
                        pointer-events: none;
                    }
                    
                    .image-hover-preview.loading::before {
                        content: '加载中...';
                        position: absolute;
                        top: 50%;
                        left: 50%;
                        transform: translate(-50%, -50%);
                        color: #666;
                        font-size: 14px;
                    }
                </style>
            `;
          document.head.insertAdjacentHTML("beforeend", css);
        }
        createPreviewElement() {
          this.preview = document.createElement("div");
          this.preview.className = "image-hover-preview";
          document.body.appendChild(this.preview);
        }
        bindEvents() {
          document.querySelectorAll(this.config.selector).forEach(((el) => {
            if (!this.boundElements.has(el)) {
              el.addEventListener("mouseenter", ((e) => this.handleMouseEnter(e)));
              el.addEventListener("mouseleave", ((e) => this.handleMouseLeave(e)));
              el.addEventListener("mousemove", ((e) => this.handleMouseMove(e)));
              this.boundElements.add(el);
            }
          }));
        }
        handleMouseEnter(e) {
          clearTimeout(this.timer);
          this.currentTarget = e.currentTarget;
          const imgUrl = this.currentTarget.getAttribute(this.config.dataAttribute) || this.currentTarget.src;
          if (!imgUrl) return;
          this.preview.innerHTML = "";
          this.preview.classList.add("loading");
          this.preview.style.display = "block";
          this.preview.classList.remove("active");
          const img = new Image();
          img.onload = () => {
            this.preview.classList.remove("loading");
            this.preview.innerHTML = `<img src="${imgUrl}" alt="预览图">`;
            this.imgElement = this.preview.querySelector("img");
            const { width, height } = this.calculateImageSize(img);
            this.preview.style.width = `${width}px`;
            this.preview.style.height = `${height}px`;
            this.preview.offsetHeight;
            this.preview.classList.add("active");
            this.handleMouseMove(e);
          };
          img.onerror = () => {
            this.preview.classList.remove("loading");
            this.preview.innerHTML = '<div style="padding:10px;color:#f00;">图片加载失败</div>';
          };
          img.src = imgUrl;
        }
        calculateImageSize(img) {
          let width = img.naturalWidth, height = img.naturalHeight;
          if (width > this.config.maxWidth || height > this.config.maxHeight) {
            const ratio = Math.min(this.config.maxWidth / width, this.config.maxHeight / height);
            width *= ratio;
            height *= ratio;
          }
          return {
            width,
            height
          };
        }
        handleMouseMove(e) {
          if (!this.currentTarget || !this.preview.classList.contains("active")) return;
          let { offsetX, offsetY } = this.config, left = e.clientX + offsetX, top = e.clientY + offsetY;
          if (this.config.autoAdjustPosition) {
            const previewWidth = this.preview.offsetWidth, previewHeight = this.preview.offsetHeight;
            left + previewWidth > window.innerWidth && (left = e.clientX - previewWidth - offsetX);
            top + previewHeight > window.innerHeight && (top = e.clientY - previewHeight - offsetY);
            left = Math.max(0, left);
            top = Math.max(0, top);
          }
          this.preview.style.left = `${left}px`;
          this.preview.style.top = `${top}px`;
        }
        handleMouseLeave() {
          this.preview.classList.remove("active");
          this.preview.style.display = "none";
          this.currentTarget = null;
          this.imgElement = null;
        }
        destroy() {
          document.querySelectorAll(this.config.selector).forEach(((el) => {
            if (this.boundElements.has(el)) {
              el.removeEventListener("mouseenter", this.handleMouseEnter);
              el.removeEventListener("mouseleave", this.handleMouseLeave);
              el.removeEventListener("mousemove", this.handleMouseMove);
              this.boundElements.delete(el);
            }
          }));
          this.preview && this.preview.parentNode && this.preview.parentNode.removeChild(this.preview);
        }
      };
      !(async function() {
        document.head.insertAdjacentHTML("beforeend", `
        <style>
            .console-logger-container {
                position: fixed;
                bottom: 0;
                right: 0;
                z-index: 99999999;
                font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                display: flex;
                flex-direction: column; 
                align-items: flex-end;
                width: fit-content;
            }

            .console-logger-toggle {
                width: 40px;
                height: 30px;
                background: #2c3e50;
                border-radius: 120px 10px 0 0;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
                transition: all 0.3s ease;
                color: white;
                font-size: 16px;
            }

            .console-logger-toggle:hover {
                background: #34495e;
            }

            .console-logger-toggle::after {
                content: '▼';
                transition: transform 0.3s ease;
            }

            .console-logger-toggle.collapsed::after {
                content: '▲';
            }

            .console-logger-window {
                width: 400px;
                height: 400px;
                background: white;
                border-radius: 10px 0 10px 10px;
                box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
                display: flex;
                flex-direction: column;
                overflow: hidden;
                transform: translateY(0);
                opacity: 1;
                /* 简化过渡属性 */
                transition: width 0.3s ease, height 0.3s ease, opacity 0.3s ease, transform 0.3s ease;
            }

            .console-logger-window.maximized {
                width: 600px !important;
                height: 85vh !important;
                border-radius: 10px 0 0 10px; /* 调整圆角以匹配右下角 */
            }

            .console-logger-window.collapsed {
                height: 0 !important;
                min-height: 0 !important; 
                opacity: 0;
            }

            .console-logger-header {
                background: #2c3e50;
                color: white;
                padding: 12px 15px;
                display: flex;
                justify-content: space-between;
                align-items: center;
                flex-shrink: 0;
            }

            .console-logger-title {
                font-weight: 600;
                font-size: 16px;
            }

            .console-logger-controls {
                display: flex;
                gap: 10px;
            }

            .console-logger-controls button {
                background: transparent;
                border: 1px solid rgba(255, 255, 255, 0.3);
                padding: 5px 10px;
                font-size: 12px;
                color: white;
                border-radius: 4px;
                cursor: pointer;
                transition: background 0.3s;
            }

            .console-logger-controls button:hover {
                background: rgba(255, 255, 255, 0.1);
            }

            /* 新增的按钮样式 */
            .console-logger-maximize-toggle {
                line-height: 1;
                font-size: 14px !important; /* 使箭头看起来更大 */
                padding: 5px 8px !important;
            }
            .console-logger-maximize-toggle::before {
                content: '⇱'; /* Unicode symbol for maximized */
            }
            .console-logger-maximize-toggle.active::before {
                content: '⇲'; /* Unicode symbol for minimized */
            }


            .console-logger-filters {
                display: flex;
                align-items: center;
                gap: 5px;
                padding: 10px;
                background: #f8f9fa;
                border-bottom: 1px solid #e9ecef;
                flex-shrink: 0;
                overflow-x: hidden; 
            }

            /* 新增: 过滤器按钮组的容器,负责滚动 */
            .console-logger-filter-group {
                display: flex;
                gap: 5px;
                overflow-x: auto; /* 允许过滤器按钮滚动 */
                flex-grow: 1; /* 占据剩余空间 */
                padding-right: 10px; /* 避免滚动条影响按钮 */
            }

            .console-logger-filter {
                padding: 5px 10px;
                font-size: 12px;
                border-radius: 15px;
                background: #ecf0f1;
                color: #7f8c8d;
                border: 1px solid #ddd;
                cursor: pointer;
                transition: all 0.3s;
                white-space: nowrap;
                flex-shrink: 0; /* 确保不被压缩 */
            }

            .console-logger-filter.active {
                background: #3498db;
                color: white;
                border-color: #3498db;
            }

            /* 新增: 滚动到底部按钮的样式 (位于 filtersContainer 内部右侧) */
            .console-logger-scroll-to-bottom {
                background: #3498db;
                border: none;
                padding: 5px 10px;
                font-size: 12px;
                color: white;
                border-radius: 4px;
                cursor: pointer;
                transition: background 0.3s;
                line-height: 1;
                height: fit-content;
                white-space: nowrap;
                margin-left: auto; /* 将按钮推到最右侧 */
                flex-shrink: 0; /* 确保不被压缩 */
            }

            .console-logger-scroll-to-bottom:hover {
                background: #2980b9;
            }


            .console-logger-content {
                flex: 1;
                overflow-y: auto;
                padding: 10px;
                background: #ffffff;
                word-wrap: break-word;
                text-align: left;
            }

            .console-logger-entry {
                padding: 8px 10px;
                margin-bottom: 3px;
                border-radius: 4px;
                font-size: 12px;
                line-height: 1.4;
                /*animation: consoleFadeIn 0.3s ease;*/
                border-left: 3px solid transparent;
            }

            @keyframes consoleFadeIn {
                from { opacity: 0; transform: translateY(5px); }
                to { opacity: 1; transform: translateY(0); }
            }

            .console-logger-timestamp {
                color: #7f8c8d;
                font-size: 11px;
                margin-right: 2px;
            }

            @media (max-width: 768px) {
                .console-logger-container {
                    right: 10px;
                    bottom: 10px;
                }

                .console-logger-window {
                    width: calc(100vw - 20px);
                    height: 300px;
                }
            }
            
            .console-logger-message[data-type="json"] {
                white-space: pre-wrap; 
            }
        </style>
    `);
        const groupTypes = {
          base: {
            label: "信息",
            background: "#e8f4fd",
            borderLeftColor: "#3498db"
          },
          warn: {
            label: "警告",
            background: "#fef9e7",
            borderLeftColor: "#f39c12"
          },
          error: {
            label: "错误",
            background: "#fdedec",
            borderLeftColor: "#e74c3c"
          },
          debug: {
            label: "调试",
            background: "#f4f6f6",
            borderLeftColor: "#95a5a6"
          }
        }, filterMap = {
          base: ["base", "warn", "error"],
          warn: ["warn"],
          error: ["error"],
          debug: ["base", "warn", "error", "debug"]
        }, maxEntries = await storageManager.getSetting("clogMsgCount", 2e3);
        class CLog {
          constructor() {
            const storedFilter = localStorage.getItem("jhs_clog_filter");
            this.currentFilter = storedFilter && groupTypes[storedFilter] ? storedFilter : "base";
            this.logs = [];
            this.isInitialized = false;
            this.userScrolledUp = false;
          }
          tryInitialize() {
            if ("loading" === document.readyState) return false;
            if (this.isInitialized) return true;
            this.init();
            this.isInitialized = true;
            return true;
          }
          init() {
            this.createContainer();
            this.bindEvents();
            this.checkInitialMaximizeState();
            this.checkInitialCollapseState();
          }
          createContainer() {
            this.container = document.createElement("div");
            this.container.className = "console-logger-container";
            this.container.style.display = "none";
            this.toggleBtn = document.createElement("div");
            this.toggleBtn.className = "console-logger-toggle collapsed";
            this.container.appendChild(this.toggleBtn);
            this.window = document.createElement("div");
            this.window.className = "console-logger-window collapsed";
            const header = document.createElement("div");
            header.className = "console-logger-header";
            const title = document.createElement("div");
            title.className = "console-logger-title";
            title.textContent = "JHS V3.3.6";
            const controls = document.createElement("div");
            controls.className = "console-logger-controls";
            this.maximizeBtn = document.createElement("button");
            this.maximizeBtn.textContent = "";
            this.maximizeBtn.classList.add("console-logger-maximize-toggle");
            controls.appendChild(this.maximizeBtn);
            const clearBtn = document.createElement("button");
            clearBtn.textContent = "清空";
            clearBtn.addEventListener("click", (() => this.clear()));
            controls.appendChild(clearBtn);
            header.appendChild(title);
            header.appendChild(controls);
            this.filtersContainer = document.createElement("div");
            this.filtersContainer.className = "console-logger-filters";
            this.filterButtonGroup = document.createElement("div");
            this.filterButtonGroup.className = "console-logger-filter-group";
            this.filtersContainer.appendChild(this.filterButtonGroup);
            this.scrollToBottomBtn = document.createElement("button");
            this.scrollToBottomBtn.className = "console-logger-scroll-to-bottom";
            this.scrollToBottomBtn.textContent = "到底部";
            this.filtersContainer.appendChild(this.scrollToBottomBtn);
            this.content = document.createElement("div");
            this.content.className = "console-logger-content jhs-scrollbar";
            this.window.appendChild(header);
            this.window.appendChild(this.filtersContainer);
            this.window.appendChild(this.content);
            this.container.appendChild(this.window);
            document.body.appendChild(this.container);
            Object.keys(groupTypes).forEach(((type) => {
              const filterBtn = document.createElement("div");
              filterBtn.className = "console-logger-filter";
              type === this.currentFilter && filterBtn.classList.add("active");
              filterBtn.textContent = groupTypes[type].label;
              filterBtn.dataset.type = type;
              filterBtn.addEventListener("click", (() => this.setFilter(type)));
              this.filterButtonGroup.appendChild(filterBtn);
            }));
          }
          bindEvents() {
            this.toggleBtn.addEventListener("click", (() => {
              this.toggleExpandCollapsed();
            }));
            this.maximizeBtn.addEventListener("click", (() => this.toggleMaximize()));
            this.scrollToBottomBtn.addEventListener("click", (() => {
              this.content.scrollTop = this.content.scrollHeight;
              this.userScrolledUp = false;
            }));
            this.content.addEventListener("scroll", (() => {
              const isAtBottom = this.content.scrollHeight - this.content.clientHeight <= this.content.scrollTop + 5;
              this.userScrolledUp = !isAtBottom;
            }));
            this.content.addEventListener("wheel", ((e) => {
              const isAtTop = 0 === this.content.scrollTop, isAtBottom = this.content.scrollHeight - this.content.clientHeight <= this.content.scrollTop + 1;
              if (isAtTop && e.deltaY < 0 || isAtBottom && e.deltaY > 0) {
                e.preventDefault();
                e.stopPropagation();
              }
            }), {
              passive: false
            });
          }
          toggleExpandCollapsed() {
            const isCollapsed = this.window.classList.toggle("collapsed");
            this.toggleBtn.classList.toggle("collapsed");
            if (isCollapsed) localStorage.setItem("jhs_clog_expand", "no");
            else {
              localStorage.setItem("jhs_clog_expand", "yes");
              this.reRenderAllLogs();
            }
          }
          checkInitialCollapseState() {
            const expandStatus = localStorage.getItem("jhs_clog_expand");
            if (expandStatus && "no" !== expandStatus) {
              this.window.classList.toggle("collapsed");
              this.toggleBtn.classList.toggle("collapsed");
              setTimeout((() => {
                this.content.scrollTop = this.content.scrollHeight;
              }), 0);
            } else {
              this.window.classList.add("collapsed");
              this.toggleBtn.classList.add("collapsed");
            }
          }
          checkInitialMaximizeState() {
            if ("maximized" === localStorage.getItem("jhs_clog_maximize")) {
              this.window.classList.add("maximized");
              this.maximizeBtn.classList.add("active");
            }
          }
          toggleMaximize() {
            const isMaximized = this.window.classList.toggle("maximized");
            this.maximizeBtn.classList.toggle("active", isMaximized);
            isMaximized ? localStorage.setItem("jhs_clog_maximize", "maximized") : localStorage.setItem("jhs_clog_maximize", "minimized");
            this.window.classList.contains("collapsed") || (this.content.scrollTop = this.content.scrollHeight);
          }
          addLog(primaryContent, type = "base", ...optionalParams) {
            const initialized = this.tryInitialize();
            let logType, extraParams = [];
            if (groupTypes[type]) {
              logType = type;
              extraParams = optionalParams;
            } else {
              logType = "base";
              extraParams = [type, ...optionalParams];
            }
            logType = groupTypes[logType] ? logType : "base";
            const allContents = [primaryContent, ...extraParams];
            let messageType = "msg";
            const processedParts = [];
            allContents.forEach(((content) => {
              if ("[object Error]" === Object.prototype.toString.call(content)) processedParts.push(String(content));
              else if ("object" == typeof content && null !== content) try {
                processedParts.push("<br/>" + JSON.stringify(content, null, 2));
                messageType = "json";
              } catch (e) {
                processedParts.push(String(content));
                messageType = "msg";
              }
              else processedParts.push(String(content));
            }));
            let formattedMessage = processedParts.join("  ");
            formattedMessage = formattedMessage.replace(/(?:(?:https?|ftp):\/\/|www\.|(?:\/\/))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]/gi, ((url) => {
              const isFullProtocol = url.startsWith("http") || url.startsWith("ftp"), isProtocolRelative = url.startsWith("//"), isWww = url.startsWith("www.");
              let fullUrl = url;
              isProtocolRelative ? fullUrl = `http:${url}` : !isFullProtocol && isWww && (fullUrl = `http://${url}`);
              return `<a href="${fullUrl}" target="_blank">${url}</a>`;
            }));
            const logEntry = {
              message: formattedMessage,
              messageType,
              type: logType,
              timestamp: /* @__PURE__ */ new Date(),
              id: Date.now() + Math.random()
            };
            this.logs.push(logEntry);
            if (this.logs.length > maxEntries) {
              const firstLog = this.logs[0];
              if (initialized) {
                const elementToRemove = this.content.querySelector(`.console-logger-entry[data-id="${firstLog.id}"]`);
                if (elementToRemove) {
                  this.logs.shift();
                  this.content.removeChild(elementToRemove);
                }
              }
            }
            initialized && this.renderLog(logEntry);
          }
          log(...message) {
            const [mainMessage, ...optionalParams] = message;
            setTimeout((() => {
              this.addLog(mainMessage, "base", ...optionalParams);
            }), 0);
          }
          error(...message) {
            const [mainMessage, ...optionalParams] = message;
            console.error(...message);
            setTimeout((() => {
              this.addLog(mainMessage, "error", ...optionalParams);
            }), 0);
          }
          warn(...message) {
            const [mainMessage, ...optionalParams] = message;
            setTimeout((() => {
              this.addLog(mainMessage, "warn", ...optionalParams);
            }), 0);
          }
          debug(...message) {
            const [mainMessage, ...optionalParams] = message;
            setTimeout((() => {
              this.addLog(mainMessage, "debug", ...optionalParams);
            }), 0);
          }
          renderLog(logEntry) {
            if ("none" === this.container.style.display) return;
            if (this.window.classList.contains("collapsed")) return;
            if (!(filterMap[this.currentFilter] || []).includes(logEntry.type)) return;
            const entryEl = this._createLogElement(logEntry);
            this.content.appendChild(entryEl);
            this.window.classList.contains("collapsed") || this.userScrolledUp || (this.content.scrollTop = this.content.scrollHeight);
          }
          reRenderAllLogs() {
            "none" !== this.container.style.display && (this.window.classList.contains("collapsed") || setTimeout((() => {
              this.content.innerHTML = "";
              if (0 === this.logs.length) return;
              const allowedTypes = filterMap[this.currentFilter] || [], fragment = document.createDocumentFragment();
              this.logs.forEach(((logEntry) => {
                if (allowedTypes.includes(logEntry.type)) {
                  const entryEl = this._createLogElement(logEntry);
                  fragment.appendChild(entryEl);
                }
              }));
              this.content.appendChild(fragment);
              this.content.scrollTop = this.content.scrollHeight;
            }), 0));
          }
          _createLogElement(logEntry) {
            const entryEl = document.createElement("div");
            entryEl.className = "console-logger-entry";
            entryEl.dataset.type = logEntry.type;
            entryEl.dataset.id = logEntry.id;
            const config = groupTypes[logEntry.type] || groupTypes.base;
            entryEl.style.borderLeft = "3px solid " + config.borderLeftColor;
            entryEl.style.background = config.background;
            const timeStr = (logEntry.timestamp instanceof Date ? logEntry.timestamp : new Date(logEntry.timestamp)).toTimeString().split(" ")[0];
            entryEl.innerHTML = `
                <span class="console-logger-timestamp">[${timeStr}]</span>
                <span class="console-logger-message" data-type="${logEntry.messageType}">${logEntry.message}</span>
            `;
            return entryEl;
          }
          setFilter(type) {
            if (this.currentFilter === type) return;
            this.currentFilter = type;
            localStorage.setItem("jhs_clog_filter", type);
            this.filterButtonGroup.querySelectorAll(".console-logger-filter").forEach(((btn) => {
              btn.dataset.type === type ? btn.classList.add("active") : btn.classList.remove("active");
            }));
            this.reRenderAllLogs();
          }
          clear() {
            this.logs = [];
            this.content.innerHTML = "";
          }
          show() {
            if (this.isInitialized && this.container) {
              this.container.style.display = "";
              this.reRenderAllLogs();
            } else if (this.tryInitialize() && this.container) {
              this.container.style.display = "";
              this.reRenderAllLogs();
            }
          }
          hide() {
            this.isInitialized && this.container && (this.container.style.display = "none");
          }
          lowZIndex() {
            this.isInitialized && this.container && (this.container.style.zIndex = "12345678");
          }
          highZIndex() {
            this.isInitialized && this.container && (this.container.style.zIndex = "999999999");
          }
        }
        if (isJavBus$1 || isJavDb$1) {
          try {
            _unsafeWindow.parent.clog && "function" == typeof _unsafeWindow.parent.clog.log ? window.clog = _unsafeWindow.clog = _unsafeWindow.parent.clog : window.clog = _unsafeWindow.clog = new CLog();
          } catch (e) {
            console.error("创建日志控制台出现异常", e);
            window.clog = _unsafeWindow.clog = new CLog();
          }
          !(function() {
            const clog2 = window.clog || console;
            window.addEventListener("error", (function(errorEvent) {
              const filename = errorEvent.filename, message = errorEvent.message;
              filename.includes("javdb") || filename.includes("javbus") || clog2.error(`[全局 Error 异常捕获] ${message} 来源: ${filename}`);
            }));
            window.addEventListener("unhandledrejection", (function(event) {
              const reason = event.reason, message = (null == reason ? void 0 : reason.message) ?? "";
              if (message.includes("play()")) return;
              if (message.includes("The element has no supported sources")) {
                show.error("播放失败, 请检查是否已对节点分流?");
                clog2.error("播放失败, 请检查是否已对节点分流?");
                return;
              }
              if (message.includes("<span>1005</span>") && message.includes("fc2ppvdb")) return;
              const errorMessage = `[全局 Promise 异常捕获] ${reason.message || reason}`;
              clog2.error(errorMessage, reason);
              event.preventDefault();
            }));
          })();
          document.addEventListener("mousedown", ((event) => {
            const clog2 = window.clog;
            if (!clog2.isInitialized || !clog2.container) return;
            const target = event.target, whitelistSelector = [".console-logger-container", ".layui-layer-shade", ".loading-container"].join(",");
            target.closest(whitelistSelector) ? clog2.highZIndex() : clog2.lowZIndex();
          }));
        }
      })();
      !(function() {
        document.head.insertAdjacentHTML("beforeend", "\n        <style>\n            .js-tooltip {\n                /* 通用样式 */\n                position: fixed;\n                padding: 8px 12px; \n                border-radius: 6px; \n                white-space: normal;\n                max-width: 600px; \n                \n                pointer-events: none;\n                font-size: 14px;\n                line-height: 1.5;\n                z-index: 9999999999;\n                \n                background: #F0FDF4; \n                color: #166534;      \n                border: none; \n                box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3); \n                \n                display: none; \n            }\n            .js-tooltip.is-active {\n                display: block !important;\n            }\n\n        </style>\n    ");
        function positionTooltip(target, content, preferredPosition) {
          const el = (function(content2) {
            const el2 = document.createElement("div");
            el2.classList.add("js-tooltip");
            const contentEl = document.createElement("div");
            contentEl.innerHTML = content2;
            el2.appendChild(contentEl);
            document.body.appendChild(el2);
            return el2;
          })(content);
          el.style.display = "block";
          const targetRect = target.getBoundingClientRect(), tooltipRect = el.getBoundingClientRect();
          el.style.display = "none";
          const viewportWidth = window.innerWidth, viewportHeight = window.innerHeight;
          let finalLeft, finalTop, finalPosition = preferredPosition;
          const fitsVertical = (y) => y >= 8 && y + tooltipRect.height <= viewportHeight - 8, fitsHorizontal = (x) => x >= 8 && x + tooltipRect.width <= viewportWidth - 8, centerHorizontal = targetRect.left + targetRect.width / 2 - tooltipRect.width / 2, centerVertical = targetRect.top + targetRect.height / 2 - tooltipRect.height / 2;
          switch (preferredPosition) {
            case "top":
              finalTop = targetRect.top - tooltipRect.height - 0;
              if (finalTop < 8 && fitsVertical(targetRect.bottom + 0)) {
                finalTop = targetRect.bottom + 0;
                finalPosition = "bottom";
              }
              break;
            case "bottom":
              finalTop = targetRect.bottom + 0;
              if (finalTop + tooltipRect.height > viewportHeight - 8 && fitsVertical(targetRect.top - tooltipRect.height - 0)) {
                finalTop = targetRect.top - tooltipRect.height - 0;
                finalPosition = "top";
              }
              break;
            case "left":
              finalLeft = targetRect.left - tooltipRect.width - 0;
              if (finalLeft < 8 && fitsHorizontal(targetRect.right + 0)) {
                finalLeft = targetRect.right + 0;
                finalPosition = "right";
              }
              break;
            case "right":
              finalLeft = targetRect.right + 0;
              if (finalLeft + tooltipRect.width > viewportWidth - 8 && fitsHorizontal(targetRect.left - tooltipRect.width - 0)) {
                finalLeft = targetRect.left - tooltipRect.width - 0;
                finalPosition = "left";
              }
          }
          const isHorizontal = "left" === finalPosition || "right" === finalPosition;
          if ("top" === finalPosition || "bottom" === finalPosition) {
            finalLeft = centerHorizontal;
            finalLeft < 8 ? finalLeft = 8 : finalLeft + tooltipRect.width > viewportWidth - 8 && (finalLeft = viewportWidth - tooltipRect.width - 8);
          } else if (isHorizontal) {
            finalTop = centerVertical;
            finalTop < 8 ? finalTop = 8 : finalTop + tooltipRect.height > viewportHeight - 8 && (finalTop = viewportHeight - tooltipRect.height - 8);
          }
          el.style.left = `${finalLeft}px`;
          el.style.top = `${finalTop}px`;
          el.classList.add("is-active");
          target.tooltipElement = el;
        }
        const selector = "[data-tip-top], [data-tip-bottom], [data-tip-left], [data-tip-right], [data-tip]";
        document.addEventListener("mouseover", ((event) => {
          const target = event.target.closest(selector);
          if (target && !target.tooltipElement) {
            let tipContent, preferredPosition = "top";
            if (target.hasAttribute("data-tip-bottom")) {
              tipContent = target.getAttribute("data-tip-bottom");
              preferredPosition = "bottom";
            } else if (target.hasAttribute("data-tip-left")) {
              tipContent = target.getAttribute("data-tip-left");
              preferredPosition = "left";
            } else if (target.hasAttribute("data-tip-right")) {
              tipContent = target.getAttribute("data-tip-right");
              preferredPosition = "right";
            } else if (target.hasAttribute("data-tip-top")) {
              tipContent = target.getAttribute("data-tip-top");
              preferredPosition = "top";
            } else if (target.hasAttribute("data-tip")) {
              tipContent = target.getAttribute("data-tip");
              preferredPosition = "top";
            }
            if (!tipContent) return;
            target.hoverTimeout = setTimeout((() => {
              target.matches(":hover") && !target.tooltipElement && positionTooltip(target, tipContent, preferredPosition);
            }), 50);
          }
        }));
        document.addEventListener("mouseout", ((event) => {
          const target = event.target.closest(selector);
          if (target) {
            if (target.hoverTimeout) {
              clearTimeout(target.hoverTimeout);
              target.hoverTimeout = null;
            }
            if (!target.contains(event.relatedTarget) && target.tooltipElement) {
              (el = target.tooltipElement) && el.parentNode && el.remove();
              target.tooltipElement = null;
            }
          }
          var el;
        }));
      })();
      if (typeof layer !== "undefined") {
        const originalLayerClose = layer.close;
        layer.close = function(index) {
          const result = originalLayerClose.call(this, index);
          !(function(waitTime = 10) {
            setTimeout((() => {
              const openLayerCount = document.querySelectorAll(".layui-layer-shade").length;
              document.documentElement.style.overflow = openLayerCount > 0 ? "hidden" : "";
            }), waitTime);
          })();
          return result;
        };
        const originalLayerOpen = layer.open;
        layer.open = function(options) {
          const originalSuccess = (options = options || {}).success;
          options.success = function(layero, index) {
            "function" == typeof originalSuccess && originalSuccess.call(this, layero, index);
            utils.setupEscClose(index);
          };
          return originalLayerOpen.call(this, options);
        };
      }
      utils.importResource("https://cdn.jsdelivr.net/npm/[email protected]/layer.min.css");
      utils.importResource("https://cdn.jsdelivr.net/npm/[email protected]/src/toastify.min.css");
      utils.importResource("https://cdn.jsdelivr.net/npm/[email protected]/dist/viewer.min.css");
      utils.importResource("https://cdn.jsdelivr.net/npm/[email protected]/dist/css/tabulator_semanticui.min.css");
      const pluginManager = (function() {
        const pluginManager2 = new PluginManager();
        _unsafeWindow.pluginManager = window.pluginManager = pluginManager2;
        let hostname = window.location.hostname;
        if (isJavDb$1) {
          pluginManager2.register(ListPagePlugin);
          pluginManager2.register(AutoPagePlugin);
          pluginManager2.register(Fc2Plugin);
          pluginManager2.register(FoldCategoryPlugin);
          pluginManager2.register(ListPageButtonPlugin);
          pluginManager2.register(HistoryPlugin);
          pluginManager2.register(SettingPlugin);
          pluginManager2.register(NavBarPlugin);
          pluginManager2.register(HitShowPlugin);
          pluginManager2.register(TOP250Plugin);
          pluginManager2.register(CoverButtonPlugin);
          pluginManager2.register(ImageRecognitionPlugin);
          pluginManager2.register(Fc2By123AvPlugin);
          pluginManager2.register(WangPan115MatchPlugin);
          pluginManager2.register(DetailPagePlugin);
          pluginManager2.register(ReviewPlugin);
          pluginManager2.register(RelatedPlugin);
          pluginManager2.register(DetailPageButtonPlugin);
          pluginManager2.register(HighlightMagnetPlugin);
          pluginManager2.register(PreviewVideoPlugin);
          pluginManager2.register(JavopenPreviewVideoPlugin);
          pluginManager2.register(FilterTitleKeywordPlugin);
          pluginManager2.register(ActressInfoPlugin);
          pluginManager2.register(OtherSitePlugin);
          pluginManager2.register(WangPan115TaskPlugin);
          pluginManager2.register(TranslatePlugin);
          pluginManager2.register(WantAndWatchedVideosPlugin);
          pluginManager2.register(MagnetHubPlugin);
          pluginManager2.register(ScreenShotPlugin);
          pluginManager2.register(BlacklistPlugin);
          pluginManager2.register(FavoriteActressesPlugin);
          pluginManager2.register(NewVideoPlugin);
          pluginManager2.register(TaskPlugin);
        }
        if (isJavBus$1) {
          pluginManager2.register(ListPagePlugin);
          pluginManager2.register(ListPageButtonPlugin);
          pluginManager2.register(SettingPlugin);
          pluginManager2.register(HistoryPlugin);
          pluginManager2.register(AutoPagePlugin);
          pluginManager2.register(ImageRecognitionPlugin);
          pluginManager2.register(BusNavBarPlugin);
          pluginManager2.register(CoverButtonPlugin);
          pluginManager2.register(WangPan115MatchPlugin);
          pluginManager2.register(BusImgPlugin);
          pluginManager2.register(BusDetailPagePlugin);
          pluginManager2.register(DetailPageButtonPlugin);
          pluginManager2.register(ReviewPlugin);
          pluginManager2.register(FilterTitleKeywordPlugin);
          pluginManager2.register(HighlightMagnetPlugin);
          pluginManager2.register(BusPreviewVideoPlugin);
          pluginManager2.register(JavopenPreviewVideoPlugin);
          pluginManager2.register(MagnetHubPlugin);
          pluginManager2.register(ScreenShotPlugin);
          pluginManager2.register(OtherSitePlugin);
          pluginManager2.register(WangPan115TaskPlugin);
          pluginManager2.register(TranslatePlugin);
          pluginManager2.register(BlacklistPlugin);
          pluginManager2.register(FavoriteActressesPlugin);
          pluginManager2.register(FoldCategoryPlugin);
          pluginManager2.register(TaskPlugin);
        }
        hostname.includes("javtrailers") && pluginManager2.register(JavTrailersPlugin);
        hostname.includes("subtitlecat") && pluginManager2.register(SubTitleCatPlugin);
        (hostname.includes("aliyundrive") || hostname.includes("alipan")) && pluginManager2.register(AliyunPanPlugin);
        hostname.includes("115.com") && pluginManager2.register(WangPan115Plugin);
        return pluginManager2;
      })();
      pluginManager.processCss().then();
      !(async function() {
        window.isDetailPage = (function() {
          let href = window.location.href;
          return isJavDb$1 ? href.split("?")[0].includes("/v/") : !!isJavBus$1 && $("#magnet-table").length > 0;
        })();
        window.isListPage = (function() {
          let href = window.location.href;
          return isJavDb$1 ? $(".movie-list").length > 0 || href.includes("advanced_search") : !!isJavBus$1 && $(".masonry > div .item").length > 0;
        })();
        window.isFc2Page = (function() {
          let href = window.location.href;
          return href.includes("advanced_search?type=3") || href.includes("advanced_search?type=100");
        })();
        isJavDb$1 && /(^|;)\s*locale\s*=\s*en\s*($|;)/i.test(document.cookie) && show.error("请切换到中文语言下才可正常使用本脚本", {
          duration: -1
        });
        pluginManager.processPlugins().then();
      })();

    })
  };
}));

System.register("./layer-npqWxmx4-BZXOsKge.js", [], (function (exports, module) {
  'use strict';
  return {
    execute: (function () {

      function _mergeNamespaces$1(n, m) {
        for (var i = 0; i < m.length; i++) {
          const e = m[i];
          if (typeof e !== "string" && !Array.isArray(e)) {
            for (const k in e) {
              if (k !== "default" && !(k in n)) {
                const d = Object.getOwnPropertyDescriptor(e, k);
                if (d) {
                  Object.defineProperty(n, k, d.get ? d : {
                    enumerable: true,
                    get: () => e[k]
                  });
                }
              }
            }
          }
        }
        return Object.freeze(Object.defineProperty(n, Symbol.toStringTag, { value: "Module" }));
      }
      function getDefaultExportFromCjs(x) {
        return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
      }
      var layer_1;
      var hasRequiredLayer;
      function requireLayer() {
        if (hasRequiredLayer) return layer_1;
        hasRequiredLayer = 1;
        var isLayui = window.layui && layui.define, $, win, ready = {
          getPath: (function() {
            var js = document.scripts, script = js[js.length - 1], jsPath = script.src;
            if (script.getAttribute("merge")) return;
            return jsPath.substring(0, jsPath.lastIndexOf("/") + 1);
          })(),
          config: {},
          end: {},
          minIndex: 0,
          minLeft: [],
          btn: ["&#x786E;&#x5B9A;", "&#x53D6;&#x6D88;"],
          //五种原始层模式
          type: ["dialog", "page", "iframe", "loading", "tips"]
        };
        var layer2 = {
          v: "3.0.1",
          ie: (function() {
            var agent = navigator.userAgent.toLowerCase();
            return !!window.ActiveXObject || "ActiveXObject" in window ? (agent.match(/msie\s(\d+)/) || [])[1] || "11" : false;
          })(),
          index: window.layer && window.layer.v ? 1e5 : 0,
          path: ready.getPath,
          config: function(options, fn) {
            options = options || {};
            layer2.cache = ready.config = $.extend({}, ready.config, options);
            layer2.path = ready.config.path || layer2.path;
            typeof options.extend === "string" && (options.extend = [options.extend]);
            if (ready.config.path) layer2.ready();
            if (!options.extend) return this;
            isLayui ? layui.addcss("modules/layer/" + options.extend) : layer2.link("skin/" + options.extend);
            return this;
          },
          //载入CSS配件
          link: function(href, fn, cssname) {
            if (!layer2.path) return;
            var head = $("head")[0], link = document.createElement("link");
            if (typeof fn === "string") cssname = fn;
            var app = (cssname || href).replace(/\.|\//g, "");
            var id = "layuicss-" + app, timeout = 0;
            link.rel = "stylesheet";
            link.href = layer2.path + href;
            link.id = id;
            if (!$("#" + id)[0]) {
              head.appendChild(link);
            }
            if (typeof fn !== "function") return;
            (function poll() {
              if (++timeout > 8 * 1e3 / 100) {
                return window.console && console.error("layer.css: Invalid");
              }
              parseInt($("#" + id).css("width")) === 1989 ? fn() : setTimeout(poll, 100);
            })();
          },
          ready: function(callback) {
            var cssname = "skinlayercss", ver = "1110";
            isLayui ? layui.addcss("modules/layer/default/layer.css?v=" + layer2.v + ver, callback, cssname) : layer2.link("skin/default/layer.css?v=" + layer2.v + ver, callback, cssname);
            return this;
          },
          //各种快捷引用
          alert: function(content, options, yes) {
            var type = typeof options === "function";
            if (type) yes = options;
            return layer2.open($.extend({
              content,
              yes
            }, type ? {} : options));
          },
          confirm: function(content, options, yes, cancel) {
            var type = typeof options === "function";
            if (type) {
              cancel = yes;
              yes = options;
            }
            return layer2.open($.extend({
              content,
              btn: ready.btn,
              yes,
              btn2: cancel
            }, type ? {} : options));
          },
          msg: function(content, options, end) {
            var type = typeof options === "function", rskin = ready.config.skin;
            var skin2 = (rskin ? rskin + " " + rskin + "-msg" : "") || "layui-layer-msg";
            var anim = doms.anim.length - 1;
            if (type) end = options;
            return layer2.open($.extend({
              content,
              time: 3e3,
              shade: false,
              skin: skin2,
              title: false,
              closeBtn: false,
              btn: false,
              resize: false,
              end
            }, type && !ready.config.skin ? {
              skin: skin2 + " layui-layer-hui",
              anim
            } : (function() {
              options = options || {};
              if (options.icon === -1 || options.icon === void 0 && !ready.config.skin) {
                options.skin = skin2 + " " + (options.skin || "layui-layer-hui");
              }
              return options;
            })()));
          },
          load: function(icon, options) {
            return layer2.open($.extend({
              type: 3,
              icon: icon || 0,
              resize: false,
              shade: 0.01
            }, options));
          },
          tips: function(content, follow, options) {
            return layer2.open($.extend({
              type: 4,
              content: [content, follow],
              closeBtn: false,
              time: 3e3,
              shade: false,
              resize: false,
              fixed: false,
              maxWidth: 210
            }, options));
          }
        };
        var Class = function(setings) {
          var that = this;
          that.index = ++layer2.index;
          that.config = $.extend({}, that.config, ready.config, setings);
          document.body ? that.creat() : setTimeout(function() {
            that.creat();
          }, 50);
        };
        Class.pt = Class.prototype;
        var doms = ["layui-layer", ".layui-layer-title", ".layui-layer-main", ".layui-layer-dialog", "layui-layer-iframe", "layui-layer-content", "layui-layer-btn", "layui-layer-close"];
        doms.anim = ["layer-anim", "layer-anim-01", "layer-anim-02", "layer-anim-03", "layer-anim-04", "layer-anim-05", "layer-anim-06"];
        Class.pt.config = {
          type: 0,
          shade: 0.3,
          fixed: true,
          move: doms[1],
          title: "&#x4FE1;&#x606F;",
          offset: "auto",
          area: "auto",
          closeBtn: 1,
          time: 0,
          //0表示不自动关闭
          zIndex: 19891014,
          maxWidth: 360,
          anim: 0,
          icon: -1,
          moveType: 1,
          resize: true,
          scrollbar: true,
          //是否允许浏览器滚动条
          tips: 2
        };
        Class.pt.vessel = function(conType, callback) {
          var that = this, times = that.index, config = that.config;
          var zIndex = config.zIndex + times, titype = typeof config.title === "object";
          var ismax = config.maxmin && (config.type === 1 || config.type === 2);
          var titleHTML = config.title ? '<div class="layui-layer-title" style="' + (titype ? config.title[1] : "") + '">' + (titype ? config.title[0] : config.title) + "</div>" : "";
          config.zIndex = zIndex;
          callback([
            //遮罩
            config.shade ? '<div class="layui-layer-shade" id="layui-layer-shade' + times + '" times="' + times + '" style="' + ("z-index:" + (zIndex - 1) + "; background-color:" + (config.shade[1] || "#000") + "; opacity:" + (config.shade[0] || config.shade) + "; filter:alpha(opacity=" + (config.shade[0] * 100 || config.shade * 100) + ");") + '"></div>' : "",
            //主体
            '<div class="' + doms[0] + (" layui-layer-" + ready.type[config.type]) + ((config.type == 0 || config.type == 2) && !config.shade ? " layui-layer-border" : "") + " " + (config.skin || "") + '" id="' + doms[0] + times + '" type="' + ready.type[config.type] + '" times="' + times + '" showtime="' + config.time + '" conType="' + (conType ? "object" : "string") + '" style="z-index: ' + zIndex + "; width:" + config.area[0] + ";height:" + config.area[1] + (config.fixed ? "" : ";position:absolute;") + '">' + (conType && config.type != 2 ? "" : titleHTML) + '<div id="' + (config.id || "") + '" class="layui-layer-content' + (config.type == 0 && config.icon !== -1 ? " layui-layer-padding" : "") + (config.type == 3 ? " layui-layer-loading" + config.icon : "") + '">' + (config.type == 0 && config.icon !== -1 ? '<i class="layui-layer-ico layui-layer-ico' + config.icon + '"></i>' : "") + (config.type == 1 && conType ? "" : config.content || "") + '</div><span class="layui-layer-setwin">' + (function() {
              var closebtn = ismax ? '<a class="layui-layer-min" href="javascript:;"><cite></cite></a><a class="layui-layer-ico layui-layer-max" href="javascript:;"></a>' : "";
              config.closeBtn && (closebtn += '<a class="layui-layer-ico ' + doms[7] + " " + doms[7] + (config.title ? config.closeBtn : config.type == 4 ? "1" : "2") + '" href="javascript:;"></a>');
              return closebtn;
            })() + "</span>" + (config.btn ? (function() {
              var button = "";
              typeof config.btn === "string" && (config.btn = [config.btn]);
              for (var i = 0, len = config.btn.length; i < len; i++) {
                button += '<a class="' + doms[6] + i + '">' + config.btn[i] + "</a>";
              }
              return '<div class="' + doms[6] + " layui-layer-btn-" + (config.btnAlign || "") + '">' + button + "</div>";
            })() : "") + (config.resize ? '<span class="layui-layer-resize"></span>' : "") + "</div>"
          ], titleHTML, $('<div class="layui-layer-move"></div>'));
          return that;
        };
        Class.pt.creat = function() {
          var that = this, config = that.config, times = that.index, content = config.content, conType = typeof content === "object", body = $("body");
          if ($("#" + config.id)[0]) return;
          if (typeof config.area === "string") {
            config.area = config.area === "auto" ? ["", ""] : [config.area, ""];
          }
          if (config.shift) {
            config.anim = config.shift;
          }
          if (layer2.ie == 6) {
            config.fixed = false;
          }
          switch (config.type) {
            case 0:
              config.btn = "btn" in config ? config.btn : ready.btn[0];
              layer2.closeAll("dialog");
              break;
            case 2:
              var content = config.content = conType ? config.content : [config.content || "http://layer.layui.com", "auto"];
              config.content = '<iframe scrolling="' + (config.content[1] || "auto") + '" allowtransparency="true" id="' + doms[4] + times + '" name="' + doms[4] + times + `" onload="this.className='';" class="layui-layer-load" frameborder="0" src="` + config.content[0] + '"></iframe>';
              break;
            case 3:
              delete config.title;
              delete config.closeBtn;
              config.icon === -1 && config.icon === 0;
              layer2.closeAll("loading");
              break;
            case 4:
              conType || (config.content = [config.content, "body"]);
              config.follow = config.content[1];
              config.content = config.content[0] + '<i class="layui-layer-TipsG"></i>';
              delete config.title;
              config.tips = typeof config.tips === "object" ? config.tips : [config.tips, true];
              config.tipsMore || layer2.closeAll("tips");
              break;
          }
          that.vessel(conType, function(html, titleHTML, moveElem) {
            body.append(html[0]);
            conType ? (function() {
              config.type == 2 || config.type == 4 ? (function() {
                $("body").append(html[1]);
              })() : (function() {
                if (!content.parents("." + doms[0])[0]) {
                  content.data("display", content.css("display")).show().addClass("layui-layer-wrap").wrap(html[1]);
                  $("#" + doms[0] + times).find("." + doms[5]).before(titleHTML);
                }
              })();
            })() : body.append(html[1]);
            $(".layui-layer-move")[0] || body.append(ready.moveElem = moveElem);
            that.layero = $("#" + doms[0] + times);
            config.scrollbar || doms.html.css("overflow", "hidden").attr("layer-full", times);
          }).auto(times);
          config.type == 2 && layer2.ie == 6 && that.layero.find("iframe").attr("src", content[0]);
          config.type == 4 ? that.tips() : that.offset();
          if (config.fixed) {
            win.on("resize", function() {
              that.offset();
              (/^\d+%$/.test(config.area[0]) || /^\d+%$/.test(config.area[1])) && that.auto(times);
              config.type == 4 && that.tips();
            });
          }
          config.time <= 0 || setTimeout(function() {
            layer2.close(that.index);
          }, config.time);
          that.move().callback();
          if (doms.anim[config.anim]) {
            that.layero.addClass(doms.anim[config.anim]).data("anim", true);
          }
        };
        Class.pt.auto = function(index) {
          var that = this, config = that.config, layero = $("#" + doms[0] + index);
          if (config.area[0] === "" && config.maxWidth > 0) {
            if (layer2.ie && layer2.ie < 8 && config.btn) {
              layero.width(layero.innerWidth());
            }
            layero.outerWidth() > config.maxWidth && layero.width(config.maxWidth);
          }
          var area = [layero.innerWidth(), layero.innerHeight()];
          var titHeight = layero.find(doms[1]).outerHeight() || 0;
          var btnHeight = layero.find("." + doms[6]).outerHeight() || 0;
          function setHeight(elem) {
            elem = layero.find(elem);
            elem.height(area[1] - titHeight - btnHeight - 2 * (parseFloat(elem.css("padding")) | 0));
          }
          switch (config.type) {
            case 2:
              setHeight("iframe");
              break;
            default:
              if (config.area[1] === "") {
                if (config.fixed && area[1] >= win.height()) {
                  area[1] = win.height();
                  setHeight("." + doms[5]);
                }
              } else {
                setHeight("." + doms[5]);
              }
              break;
          }
          return that;
        };
        Class.pt.offset = function() {
          var that = this, config = that.config, layero = that.layero;
          var area = [layero.outerWidth(), layero.outerHeight()];
          var type = typeof config.offset === "object";
          that.offsetTop = (win.height() - area[1]) / 2;
          that.offsetLeft = (win.width() - area[0]) / 2;
          if (type) {
            that.offsetTop = config.offset[0];
            that.offsetLeft = config.offset[1] || that.offsetLeft;
          } else if (config.offset !== "auto") {
            if (config.offset === "t") {
              that.offsetTop = 0;
            } else if (config.offset === "r") {
              that.offsetLeft = win.width() - area[0];
            } else if (config.offset === "b") {
              that.offsetTop = win.height() - area[1];
            } else if (config.offset === "l") {
              that.offsetLeft = 0;
            } else if (config.offset === "lt") {
              that.offsetTop = 0;
              that.offsetLeft = 0;
            } else if (config.offset === "lb") {
              that.offsetTop = win.height() - area[1];
              that.offsetLeft = 0;
            } else if (config.offset === "rt") {
              that.offsetTop = 0;
              that.offsetLeft = win.width() - area[0];
            } else if (config.offset === "rb") {
              that.offsetTop = win.height() - area[1];
              that.offsetLeft = win.width() - area[0];
            } else {
              that.offsetTop = config.offset;
            }
          }
          if (!config.fixed) {
            that.offsetTop = /%$/.test(that.offsetTop) ? win.height() * parseFloat(that.offsetTop) / 100 : parseFloat(that.offsetTop);
            that.offsetLeft = /%$/.test(that.offsetLeft) ? win.width() * parseFloat(that.offsetLeft) / 100 : parseFloat(that.offsetLeft);
            that.offsetTop += win.scrollTop();
            that.offsetLeft += win.scrollLeft();
          }
          if (layero.attr("minLeft")) {
            that.offsetTop = win.height() - (layero.find(doms[1]).outerHeight() || 0);
            that.offsetLeft = layero.css("left");
          }
          layero.css({ top: that.offsetTop, left: that.offsetLeft });
        };
        Class.pt.tips = function() {
          var that = this, config = that.config, layero = that.layero;
          var layArea = [layero.outerWidth(), layero.outerHeight()], follow = $(config.follow);
          if (!follow[0]) follow = $("body");
          var goal = {
            width: follow.outerWidth(),
            height: follow.outerHeight(),
            top: follow.offset().top,
            left: follow.offset().left
          }, tipsG = layero.find(".layui-layer-TipsG");
          var guide = config.tips[0];
          config.tips[1] || tipsG.remove();
          goal.autoLeft = function() {
            if (goal.left + layArea[0] - win.width() > 0) {
              goal.tipLeft = goal.left + goal.width - layArea[0];
              tipsG.css({ right: 12, left: "auto" });
            } else {
              goal.tipLeft = goal.left;
            }
          };
          goal.where = [function() {
            goal.autoLeft();
            goal.tipTop = goal.top - layArea[1] - 10;
            tipsG.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color", config.tips[1]);
          }, function() {
            goal.tipLeft = goal.left + goal.width + 10;
            goal.tipTop = goal.top;
            tipsG.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color", config.tips[1]);
          }, function() {
            goal.autoLeft();
            goal.tipTop = goal.top + goal.height + 10;
            tipsG.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color", config.tips[1]);
          }, function() {
            goal.tipLeft = goal.left - layArea[0] - 10;
            goal.tipTop = goal.top;
            tipsG.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color", config.tips[1]);
          }];
          goal.where[guide - 1]();
          if (guide === 1) {
            goal.top - (win.scrollTop() + layArea[1] + 8 * 2) < 0 && goal.where[2]();
          } else if (guide === 2) {
            win.width() - (goal.left + goal.width + layArea[0] + 8 * 2) > 0 || goal.where[3]();
          } else if (guide === 3) {
            goal.top - win.scrollTop() + goal.height + layArea[1] + 8 * 2 - win.height() > 0 && goal.where[0]();
          } else if (guide === 4) {
            layArea[0] + 8 * 2 - goal.left > 0 && goal.where[1]();
          }
          layero.find("." + doms[5]).css({
            "background-color": config.tips[1],
            "padding-right": config.closeBtn ? "30px" : ""
          });
          layero.css({
            left: goal.tipLeft - (config.fixed ? win.scrollLeft() : 0),
            top: goal.tipTop - (config.fixed ? win.scrollTop() : 0)
          });
        };
        Class.pt.move = function() {
          var that = this, config = that.config, _DOC = $(document), layero = that.layero, moveElem = layero.find(config.move), resizeElem = layero.find(".layui-layer-resize"), dict = {};
          if (config.move) {
            moveElem.css("cursor", "move");
          }
          moveElem.on("mousedown", function(e) {
            e.preventDefault();
            if (config.move) {
              dict.moveStart = true;
              dict.offset = [
                e.clientX - parseFloat(layero.css("left")),
                e.clientY - parseFloat(layero.css("top"))
              ];
              ready.moveElem.css("cursor", "move").show();
            }
          });
          resizeElem.on("mousedown", function(e) {
            e.preventDefault();
            dict.resizeStart = true;
            dict.offset = [e.clientX, e.clientY];
            dict.area = [
              layero.outerWidth(),
              layero.outerHeight()
            ];
            ready.moveElem.css("cursor", "se-resize").show();
          });
          _DOC.on("mousemove", function(e) {
            if (dict.moveStart) {
              var X = e.clientX - dict.offset[0], Y = e.clientY - dict.offset[1], fixed = layero.css("position") === "fixed";
              e.preventDefault();
              dict.stX = fixed ? 0 : win.scrollLeft();
              dict.stY = fixed ? 0 : win.scrollTop();
              if (!config.moveOut) {
                var setRig = win.width() - layero.outerWidth() + dict.stX, setBot = win.height() - layero.outerHeight() + dict.stY;
                X < dict.stX && (X = dict.stX);
                X > setRig && (X = setRig);
                Y < dict.stY && (Y = dict.stY);
                Y > setBot && (Y = setBot);
              }
              layero.css({
                left: X,
                top: Y
              });
            }
            if (config.resize && dict.resizeStart) {
              var X = e.clientX - dict.offset[0], Y = e.clientY - dict.offset[1];
              e.preventDefault();
              layer2.style(that.index, {
                width: dict.area[0] + X,
                height: dict.area[1] + Y
              });
              dict.isResize = true;
            }
          }).on("mouseup", function(e) {
            if (dict.moveStart) {
              delete dict.moveStart;
              ready.moveElem.hide();
              config.moveEnd && config.moveEnd();
            }
            if (dict.resizeStart) {
              delete dict.resizeStart;
              ready.moveElem.hide();
            }
          });
          return that;
        };
        Class.pt.callback = function() {
          var that = this, layero = that.layero, config = that.config;
          that.openLayer();
          if (config.success) {
            if (config.type == 2) {
              layero.find("iframe").on("load", function() {
                config.success(layero, that.index);
              });
            } else {
              config.success(layero, that.index);
            }
          }
          layer2.ie == 6 && that.IE6(layero);
          layero.find("." + doms[6]).children("a").on("click", function() {
            var index = $(this).index();
            if (index === 0) {
              if (config.yes) {
                config.yes(that.index, layero);
              } else if (config["btn1"]) {
                config["btn1"](that.index, layero);
              } else {
                layer2.close(that.index);
              }
            } else {
              var close = config["btn" + (index + 1)] && config["btn" + (index + 1)](that.index, layero);
              close === false || layer2.close(that.index);
            }
          });
          function cancel() {
            var close = config.cancel && config.cancel(that.index, layero);
            close === false || layer2.close(that.index);
          }
          layero.find("." + doms[7]).on("click", cancel);
          if (config.shadeClose) {
            $("#layui-layer-shade" + that.index).on("click", function() {
              layer2.close(that.index);
            });
          }
          layero.find(".layui-layer-min").on("click", function() {
            var min = config.min && config.min(layero);
            min === false || layer2.min(that.index, config);
          });
          layero.find(".layui-layer-max").on("click", function() {
            if ($(this).hasClass("layui-layer-maxmin")) {
              layer2.restore(that.index);
              config.restore && config.restore(layero);
            } else {
              layer2.full(that.index, config);
              setTimeout(function() {
                config.full && config.full(layero);
              }, 100);
            }
          });
          config.end && (ready.end[that.index] = config.end);
        };
        ready.reselect = function() {
          $.each($("select"), function(index, value) {
            var sthis = $(this);
            if (!sthis.parents("." + doms[0])[0]) {
              sthis.attr("layer") == 1 && $("." + doms[0]).length < 1 && sthis.removeAttr("layer").show();
            }
            sthis = null;
          });
        };
        Class.pt.IE6 = function(layero) {
          $("select").each(function(index, value) {
            var sthis = $(this);
            if (!sthis.parents("." + doms[0])[0]) {
              sthis.css("display") === "none" || sthis.attr({ "layer": "1" }).hide();
            }
            sthis = null;
          });
        };
        Class.pt.openLayer = function() {
          var that = this;
          layer2.zIndex = that.config.zIndex;
          layer2.setTop = function(layero) {
            var setZindex = function() {
              layer2.zIndex++;
              layero.css("z-index", layer2.zIndex + 1);
            };
            layer2.zIndex = parseInt(layero[0].style.zIndex);
            layero.on("mousedown", setZindex);
            return layer2.zIndex;
          };
        };
        ready.record = function(layero) {
          var area = [
            layero.width(),
            layero.height(),
            layero.position().top,
            layero.position().left + parseFloat(layero.css("margin-left"))
          ];
          layero.find(".layui-layer-max").addClass("layui-layer-maxmin");
          layero.attr({ area });
        };
        ready.rescollbar = function(index) {
          if (doms.html.attr("layer-full") == index) {
            if (doms.html[0].style.removeProperty) {
              doms.html[0].style.removeProperty("overflow");
            } else {
              doms.html[0].style.removeAttribute("overflow");
            }
            doms.html.removeAttr("layer-full");
          }
        };
        window.layer = layer2;
        layer2.getChildFrame = function(selector, index) {
          index = index || $("." + doms[4]).attr("times");
          return $("#" + doms[0] + index).find("iframe").contents().find(selector);
        };
        layer2.getFrameIndex = function(name) {
          return $("#" + name).parents("." + doms[4]).attr("times");
        };
        layer2.iframeAuto = function(index) {
          if (!index) return;
          var heg = layer2.getChildFrame("html", index).outerHeight();
          var layero = $("#" + doms[0] + index);
          var titHeight = layero.find(doms[1]).outerHeight() || 0;
          var btnHeight = layero.find("." + doms[6]).outerHeight() || 0;
          layero.css({ height: heg + titHeight + btnHeight });
          layero.find("iframe").css({ height: heg });
        };
        layer2.iframeSrc = function(index, url) {
          $("#" + doms[0] + index).find("iframe").attr("src", url);
        };
        layer2.style = function(index, options, limit) {
          var layero = $("#" + doms[0] + index), contElem = layero.find(".layui-layer-content"), type = layero.attr("type"), titHeight = layero.find(doms[1]).outerHeight() || 0, btnHeight = layero.find("." + doms[6]).outerHeight() || 0;
          layero.attr("minLeft");
          if (type === ready.type[3] || type === ready.type[4]) {
            return;
          }
          if (!limit) {
            if (parseFloat(options.width) <= 260) {
              options.width = 260;
            }
            if (parseFloat(options.height) - titHeight - btnHeight <= 64) {
              options.height = 64 + titHeight + btnHeight;
            }
          }
          layero.css(options);
          btnHeight = layero.find("." + doms[6]).outerHeight();
          if (type === ready.type[2]) {
            layero.find("iframe").css({
              height: parseFloat(options.height) - titHeight - btnHeight
            });
          } else {
            contElem.css({
              height: parseFloat(options.height) - titHeight - btnHeight - parseFloat(contElem.css("padding-top")) - parseFloat(contElem.css("padding-bottom"))
            });
          }
        };
        layer2.min = function(index, options) {
          var layero = $("#" + doms[0] + index), titHeight = layero.find(doms[1]).outerHeight() || 0, left = layero.attr("minLeft") || 181 * ready.minIndex + "px", position = layero.css("position");
          ready.record(layero);
          if (ready.minLeft[0]) {
            left = ready.minLeft[0];
            ready.minLeft.shift();
          }
          layero.attr("position", position);
          layer2.style(index, {
            width: 180,
            height: titHeight,
            left,
            top: win.height() - titHeight,
            position: "fixed",
            overflow: "hidden"
          }, true);
          layero.find(".layui-layer-min").hide();
          layero.attr("type") === "page" && layero.find(doms[4]).hide();
          ready.rescollbar(index);
          if (!layero.attr("minLeft")) {
            ready.minIndex++;
          }
          layero.attr("minLeft", left);
        };
        layer2.restore = function(index) {
          var layero = $("#" + doms[0] + index), area = layero.attr("area").split(",");
          layero.attr("type");
          layer2.style(index, {
            width: parseFloat(area[0]),
            height: parseFloat(area[1]),
            top: parseFloat(area[2]),
            left: parseFloat(area[3]),
            position: layero.attr("position"),
            overflow: "visible"
          }, true);
          layero.find(".layui-layer-max").removeClass("layui-layer-maxmin");
          layero.find(".layui-layer-min").show();
          layero.attr("type") === "page" && layero.find(doms[4]).show();
          ready.rescollbar(index);
        };
        layer2.full = function(index) {
          var layero = $("#" + doms[0] + index), timer;
          ready.record(layero);
          if (!doms.html.attr("layer-full")) {
            doms.html.css("overflow", "hidden").attr("layer-full", index);
          }
          clearTimeout(timer);
          timer = setTimeout(function() {
            var isfix = layero.css("position") === "fixed";
            layer2.style(index, {
              top: isfix ? 0 : win.scrollTop(),
              left: isfix ? 0 : win.scrollLeft(),
              width: win.width(),
              height: win.height()
            }, true);
            layero.find(".layui-layer-min").hide();
          }, 100);
        };
        layer2.title = function(name, index) {
          var title = $("#" + doms[0] + (index || layer2.index)).find(doms[1]);
          title.html(name);
        };
        layer2.close = function(index) {
          var layero = $("#" + doms[0] + index), type = layero.attr("type"), closeAnim = "layer-anim-close";
          if (!layero[0]) return;
          var WRAP = "layui-layer-wrap", remove = function() {
            if (type === ready.type[1] && layero.attr("conType") === "object") {
              layero.children(":not(." + doms[5] + ")").remove();
              var wrap = layero.find("." + WRAP);
              for (var i = 0; i < 2; i++) {
                wrap.unwrap();
              }
              wrap.css("display", wrap.data("display")).removeClass(WRAP);
            } else {
              if (type === ready.type[2]) {
                try {
                  var iframe = $("#" + doms[4] + index)[0];
                  iframe.contentWindow.document.write("");
                  iframe.contentWindow.close();
                  layero.find("." + doms[5])[0].removeChild(iframe);
                } catch (e) {
                }
              }
              layero[0].innerHTML = "";
              layero.remove();
            }
            typeof ready.end[index] === "function" && ready.end[index]();
            delete ready.end[index];
          };
          if (layero.data("anim")) {
            layero.addClass(closeAnim);
          }
          $("#layui-layer-moves, #layui-layer-shade" + index).remove();
          layer2.ie == 6 && ready.reselect();
          ready.rescollbar(index);
          if (layero.attr("minLeft")) {
            ready.minIndex--;
            ready.minLeft.push(layero.attr("minLeft"));
          }
          setTimeout(function() {
            remove();
          }, layer2.ie && layer2.ie < 10 || !layero.data("anim") ? 0 : 200);
        };
        layer2.closeAll = function(type) {
          $.each($("." + doms[0]), function() {
            var othis = $(this);
            var is = type ? othis.attr("type") === type : 1;
            is && layer2.close(othis.attr("times"));
            is = null;
          });
        };
        var cache = layer2.cache || {}, skin = function(type) {
          return cache.skin ? " " + cache.skin + " " + cache.skin + "-" + type : "";
        };
        layer2.prompt = function(options, yes) {
          var style = "";
          options = options || {};
          if (typeof options === "function") yes = options;
          if (options.area) {
            var area = options.area;
            style = 'style="width: ' + area[0] + "; height: " + area[1] + ';"';
            delete options.area;
          }
          var prompt, content = options.formType == 2 ? '<textarea class="layui-layer-input"' + style + ">" + (options.value || "") + "</textarea>" : (function() {
            return '<input type="' + (options.formType == 1 ? "password" : "text") + '" class="layui-layer-input" value="' + (options.value || "") + '">';
          })();
          return layer2.open($.extend({
            type: 1,
            btn: ["&#x786E;&#x5B9A;", "&#x53D6;&#x6D88;"],
            content,
            skin: "layui-layer-prompt" + skin("prompt"),
            maxWidth: win.width(),
            success: function(layero) {
              prompt = layero.find(".layui-layer-input");
              prompt.focus();
            },
            resize: false,
            yes: function(index) {
              var value = prompt.val();
              if (value === "") {
                prompt.focus();
              } else if (value.length > (options.maxlength || 500)) {
                layer2.tips("&#x6700;&#x591A;&#x8F93;&#x5165;" + (options.maxlength || 500) + "&#x4E2A;&#x5B57;&#x6570;", prompt, { tips: 1 });
              } else {
                yes && yes(value, index, prompt);
              }
            }
          }, options));
        };
        layer2.tab = function(options) {
          options = options || {};
          var tab = options.tab || {};
          return layer2.open($.extend({
            type: 1,
            skin: "layui-layer-tab" + skin("tab"),
            resize: false,
            title: (function() {
              var len = tab.length, ii = 1, str = "";
              if (len > 0) {
                str = '<span class="layui-layer-tabnow">' + tab[0].title + "</span>";
                for (; ii < len; ii++) {
                  str += "<span>" + tab[ii].title + "</span>";
                }
              }
              return str;
            })(),
            content: '<ul class="layui-layer-tabmain">' + (function() {
              var len = tab.length, ii = 1, str = "";
              if (len > 0) {
                str = '<li class="layui-layer-tabli xubox_tab_layer">' + (tab[0].content || "no content") + "</li>";
                for (; ii < len; ii++) {
                  str += '<li class="layui-layer-tabli">' + (tab[ii].content || "no  content") + "</li>";
                }
              }
              return str;
            })() + "</ul>",
            success: function(layero) {
              var btn = layero.find(".layui-layer-title").children();
              var main = layero.find(".layui-layer-tabmain").children();
              btn.on("mousedown", function(e) {
                e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
                var othis = $(this), index = othis.index();
                othis.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow");
                main.eq(index).show().siblings().hide();
                typeof options.change === "function" && options.change(index);
              });
            }
          }, options));
        };
        layer2.photos = function(options, loop, key) {
          var dict = {};
          options = options || {};
          if (!options.photos) return;
          var type = options.photos.constructor === Object;
          var photos = type ? options.photos : {}, data = photos.data || [];
          var start = photos.start || 0;
          dict.imgIndex = (start | 0) + 1;
          options.img = options.img || "img";
          if (!type) {
            var parent = $(options.photos), pushData = function() {
              data = [];
              parent.find(options.img).each(function(index) {
                var othis = $(this);
                othis.attr("layer-index", index);
                data.push({
                  alt: othis.attr("alt"),
                  pid: othis.attr("layer-pid"),
                  src: othis.attr("layer-src") || othis.attr("src"),
                  thumb: othis.attr("src")
                });
              });
            };
            pushData();
            if (data.length === 0) return;
            loop || parent.on("click", options.img, function() {
              var othis = $(this), index = othis.attr("layer-index");
              layer2.photos($.extend(options, {
                photos: {
                  start: index,
                  data,
                  tab: options.tab
                },
                full: options.full
              }), true);
              pushData();
            });
            if (!loop) return;
          } else if (data.length === 0) {
            return layer2.msg("&#x6CA1;&#x6709;&#x56FE;&#x7247;");
          }
          dict.imgprev = function(key2) {
            dict.imgIndex--;
            if (dict.imgIndex < 1) {
              dict.imgIndex = data.length;
            }
            dict.tabimg(key2);
          };
          dict.imgnext = function(key2, errorMsg) {
            dict.imgIndex++;
            if (dict.imgIndex > data.length) {
              dict.imgIndex = 1;
              if (errorMsg) {
                return;
              }
            }
            dict.tabimg(key2);
          };
          dict.keyup = function(event) {
            if (!dict.end) {
              var code = event.keyCode;
              event.preventDefault();
              if (code === 37) {
                dict.imgprev(true);
              } else if (code === 39) {
                dict.imgnext(true);
              } else if (code === 27) {
                layer2.close(dict.index);
              }
            }
          };
          dict.tabimg = function(key2) {
            if (data.length <= 1) return;
            photos.start = dict.imgIndex - 1;
            layer2.close(dict.index);
            layer2.photos(options, true, key2);
          };
          dict.event = function() {
            dict.bigimg.hover(function() {
              dict.imgsee.show();
            }, function() {
              dict.imgsee.hide();
            });
            dict.bigimg.find(".layui-layer-imgprev").on("click", function(event) {
              event.preventDefault();
              dict.imgprev();
            });
            dict.bigimg.find(".layui-layer-imgnext").on("click", function(event) {
              event.preventDefault();
              dict.imgnext();
            });
            $(document).on("keyup", dict.keyup);
          };
          function loadImage(url, callback, error) {
            var img = new Image();
            img.src = url;
            if (img.complete) {
              return callback(img);
            }
            img.onload = function() {
              img.onload = null;
              callback(img);
            };
            img.onerror = function(e) {
              img.onerror = null;
              error(e);
            };
          }
          dict.loadi = layer2.load(1, {
            shade: "shade" in options ? false : 0.9,
            scrollbar: false
          });
          loadImage(data[start].src, function(img) {
            layer2.close(dict.loadi);
            dict.index = layer2.open($.extend({
              type: 1,
              area: (function() {
                var imgarea = [img.width, img.height];
                var winarea = [$(window).width() - 100, $(window).height() - 100];
                if (!options.full && (imgarea[0] > winarea[0] || imgarea[1] > winarea[1])) {
                  var wh = [imgarea[0] / winarea[0], imgarea[1] / winarea[1]];
                  if (wh[0] > wh[1]) {
                    imgarea[0] = imgarea[0] / wh[0];
                    imgarea[1] = imgarea[1] / wh[0];
                  } else if (wh[0] < wh[1]) {
                    imgarea[0] = imgarea[0] / wh[1];
                    imgarea[1] = imgarea[1] / wh[1];
                  }
                }
                return [imgarea[0] + "px", imgarea[1] + "px"];
              })(),
              title: false,
              shade: 0.9,
              shadeClose: true,
              closeBtn: false,
              move: ".layui-layer-phimg img",
              moveType: 1,
              scrollbar: false,
              moveOut: true,
              anim: Math.random() * 5 | 0,
              skin: "layui-layer-photos" + skin("photos"),
              content: '<div class="layui-layer-phimg"><img src="' + data[start].src + '" alt="' + (data[start].alt || "") + '" layer-pid="' + data[start].pid + '"><div class="layui-layer-imgsee">' + (data.length > 1 ? '<span class="layui-layer-imguide"><a href="javascript:;" class="layui-layer-iconext layui-layer-imgprev"></a><a href="javascript:;" class="layui-layer-iconext layui-layer-imgnext"></a></span>' : "") + '<div class="layui-layer-imgbar" style="display:' + (key ? "block" : "") + '"><span class="layui-layer-imgtit"><a href="javascript:;">' + (data[start].alt || "") + "</a><em>" + dict.imgIndex + "/" + data.length + "</em></span></div></div></div>",
              success: function(layero, index) {
                dict.bigimg = layero.find(".layui-layer-phimg");
                dict.imgsee = layero.find(".layui-layer-imguide,.layui-layer-imgbar");
                dict.event(layero);
                options.tab && options.tab(data[start], layero);
              },
              end: function() {
                dict.end = true;
                $(document).off("keyup", dict.keyup);
              }
            }, options));
          }, function() {
            layer2.close(dict.loadi);
            layer2.msg("&#x5F53;&#x524D;&#x56FE;&#x7247;&#x5730;&#x5740;&#x5F02;&#x5E38;<br>&#x662F;&#x5426;&#x7EE7;&#x7EED;&#x67E5;&#x770B;&#x4E0B;&#x4E00;&#x5F20;&#xFF1F;", {
              time: 3e4,
              btn: ["&#x4E0B;&#x4E00;&#x5F20;", "&#x4E0D;&#x770B;&#x4E86;"],
              yes: function() {
                data.length > 1 && dict.imgnext(true, true);
              }
            });
          });
        };
        ready.run = function(_$) {
          $ = _$;
          win = $(window);
          doms.html = $("html");
          layer2.open = function(deliver) {
            var o = new Class(deliver);
            return o.index;
          };
        };
        ready.run(jQuery);
        layer_1 = layer2;
        return layer_1;
      }
      var layerExports = requireLayer();
      const layer = /* @__PURE__ */ getDefaultExportFromCjs(layerExports);
      const layer$1 = exports("l", /* @__PURE__ */ _mergeNamespaces$1({
        __proto__: null,
        default: layer
      }, [layerExports]));

    })
  };
}));

System.import("./__entry.js", "./");